Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
util.c
Go to the documentation of this file.
00001 /*  Audacious - Cross-platform multimedia player
00002  *  Copyright (C) 2005-2011  Audacious development team
00003  *
00004  *  Based on BMP:
00005  *  Copyright (C) 2003-2004  BMP development team.
00006  *
00007  *  Based on XMMS:
00008  *  Copyright (C) 1998-2003  XMMS development team.
00009  *
00010  *  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; under version 3 of the License.
00013  *
00014  *  This program is distributed in the hope that it will be useful,
00015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  *  GNU General Public License for more details.
00018  *
00019  *  You should have received a copy of the GNU General Public License
00020  *  along with this program.  If not, see <http://www.gnu.org/licenses>.
00021  *
00022  *  The Audacious team does not consider modular code linking to
00023  *  Audacious or using our public API to be a derived work.
00024  */
00025 
00026 #include <dirent.h>
00027 #include <limits.h>
00028 #include <unistd.h>
00029 
00030 #ifdef _WIN32
00031 #include <windows.h>
00032 #endif
00033 
00034 #ifdef HAVE_CONFIG_H
00035 #  include "config.h"
00036 #endif
00037 
00038 #include <glib.h>
00039 #include <stdlib.h>
00040 #include <string.h>
00041 #include <ctype.h>
00042 
00043 #include <errno.h>
00044 
00045 #ifdef HAVE_FTS_H
00046 #  include <sys/types.h>
00047 #  include <sys/stat.h>
00048 #  include <fts.h>
00049 #endif
00050 
00051 #include <libaudcore/audstrings.h>
00052 #include <libaudcore/stringpool.h>
00053 
00054 #include "audconfig.h"
00055 #include "debug.h"
00056 #include "i18n.h"
00057 #include "misc.h"
00058 #include "plugins.h"
00059 #include "util.h"
00060 
00061 gboolean dir_foreach (const gchar * path, DirForeachFunc func, void * user)
00062 {
00063     DIR * dir = opendir (path);
00064     if (! dir)
00065         return FALSE;
00066     
00067     gchar full[PATH_MAX];
00068     gint len = snprintf (full, sizeof full, "%s" G_DIR_SEPARATOR_S, path);
00069     
00070     struct dirent * entry;
00071     while ((entry = readdir (dir)))
00072     {
00073         if (entry->d_name[0] == '.')
00074             continue;
00075         
00076         snprintf (full + len, sizeof full - len, "%s", entry->d_name);
00077 
00078         if (func (full, entry->d_name, user))
00079             break;
00080     }
00081 
00082     closedir (dir);
00083     return TRUE;
00084 }
00085 
00094 gchar*
00095 util_get_localdir(void)
00096 {
00097   gchar *datadir;
00098   gchar *tmp;
00099 
00100   if ( (tmp = getenv("XDG_CONFIG_HOME")) == NULL )
00101     datadir = g_build_filename( g_get_home_dir() , ".config" , "audacious" ,  NULL );
00102   else
00103     datadir = g_build_filename( tmp , "audacious" , NULL );
00104 
00105   return datadir;
00106 }
00107 
00108 
00109 gchar * construct_uri (const gchar * string, const gchar * playlist_name)
00110 {
00111     gchar *filename = g_strdup(string);
00112     gchar *uri = NULL;
00113 
00114     /* try to translate dos path */
00115     convert_dos_path(filename); /* in place replacement */
00116 
00117     // make full path uri here
00118     // case 1: filename is raw full path or uri
00119     if (filename[0] == '/' || strstr(filename, "://")) {
00120         uri = g_filename_to_uri(filename, NULL, NULL);
00121         if(!uri)
00122             uri = g_strdup(filename);
00123     }
00124     // case 2: filename is not raw full path nor uri
00125     // make full path by replacing last part of playlist path with filename.
00126     else
00127     {
00128         const gchar * slash = strrchr (playlist_name, '/');
00129         if (slash)
00130             uri = g_strdup_printf ("%.*s/%s", (gint) (slash - playlist_name),
00131              playlist_name, filename);
00132     }
00133 
00134     g_free (filename);
00135     return uri;
00136 }
00137 
00138 /* local files -- not URI's */
00139 gint file_get_mtime (const gchar * filename)
00140 {
00141     struct stat info;
00142 
00143     if (stat (filename, & info))
00144         return -1;
00145 
00146     return info.st_mtime;
00147 }
00148 
00149 void
00150 make_directory(const gchar * path, mode_t mode)
00151 {
00152     if (g_mkdir_with_parents(path, mode) == 0)
00153         return;
00154 
00155     g_printerr(_("Could not create directory (%s): %s\n"), path,
00156                g_strerror(errno));
00157 }
00158 
00159 gchar * get_path_to_self (void)
00160 {
00161     gchar buf[PATH_MAX];
00162     gint len;
00163 
00164 #ifdef _WIN32
00165     if (! (len = GetModuleFileName (NULL, buf, sizeof buf)) || len == sizeof buf)
00166     {
00167         fprintf (stderr, "GetModuleFileName failed.\n");
00168         return NULL;
00169     }
00170 #else
00171     if ((len = readlink ("/proc/self/exe", buf, sizeof buf)) < 0)
00172     {
00173         fprintf (stderr, "Cannot access /proc/self/exe: %s.\n", strerror (errno));
00174         return NULL;
00175     }
00176 #endif
00177 
00178     return g_strndup (buf, len);
00179 }
00180 
00181 #define URL_HISTORY_MAX_SIZE 30
00182 
00183 void
00184 util_add_url_history_entry(const gchar * url)
00185 {
00186     if (g_list_find_custom(cfg.url_history, url, (GCompareFunc) strcasecmp))
00187         return;
00188 
00189     cfg.url_history = g_list_prepend(cfg.url_history, g_strdup(url));
00190 
00191     while (g_list_length(cfg.url_history) > URL_HISTORY_MAX_SIZE) {
00192         GList *node = g_list_last(cfg.url_history);
00193         g_free(node->data);
00194         cfg.url_history = g_list_delete_link(cfg.url_history, node);
00195     }
00196 }
00197 
00198 /* Strips various common top-level folders from a file name (not URI).  The
00199  * string passed will not be modified, but the string returned will share the
00200  * same memory.  Examples:
00201  *     "/home/john/folder/file.mp3"    -> "folder/file.mp3"
00202  *     "/folder/file.mp3"              -> "folder/file.mp3"
00203  *     "C:\Users\John\folder\file.mp3" -> "folder\file.mp3"
00204  *     "E:\folder\file.mp3"            -> "folder\file.mp3" */
00205 
00206 static gchar * skip_top_folders (gchar * name)
00207 {
00208     const gchar * home = getenv ("HOME");
00209     if (! home)
00210         goto NO_HOME;
00211 
00212     gint len = strlen (home);
00213     if (len > 0 && home[len - 1] == G_DIR_SEPARATOR)
00214         len --;
00215 
00216 #ifdef _WIN32
00217     if (! strncasecmp (name, home, len) && name[len] == '\\')
00218 #else
00219     if (! strncmp (name, home, len) && name[len] == '/')
00220 #endif
00221         return name + len + 1;
00222 
00223 NO_HOME:
00224 #ifdef _WIN32
00225     return (name[0] && name[1] == ':' && name[2] == '\\') ? name + 3 : name;
00226 #else
00227     return (name[0] == '/') ? name + 1 : name;
00228 #endif
00229 }
00230 
00231 /* Divides a file name (not URI) into the base name, the lowest folder, and the
00232  * second lowest folder.  The string passed will be modified, and the strings
00233  * returned will use the same memory.  May return NULL for <first> and <second>.
00234  * Examples:
00235  *     "a/b/c/d/e.mp3" -> "e", "d",  "c"
00236  *     "d/e.mp3"       -> "e", "d",  NULL
00237  *     "e.mp3"         -> "e", NULL, NULL */
00238 
00239 static void split_filename (gchar * name, gchar * * base, gchar * * first,
00240  gchar * * second)
00241 {
00242     * first = * second = NULL;
00243 
00244     gchar * c;
00245 
00246     if ((c = strrchr (name, G_DIR_SEPARATOR)))
00247     {
00248         * base = c + 1;
00249         * c = 0;
00250     }
00251     else
00252     {
00253         * base = name;
00254         goto DONE;
00255     }
00256 
00257     if ((c = strrchr (name, G_DIR_SEPARATOR)))
00258     {
00259         * first = c + 1;
00260         * c = 0;
00261     }
00262     else
00263     {
00264         * first = name;
00265         goto DONE;
00266     }
00267 
00268     if ((c = strrchr (name, G_DIR_SEPARATOR)))
00269         * second = c + 1;
00270     else
00271         * second = name;
00272 
00273 DONE:
00274     if ((c = strrchr (* base, '.')))
00275         * c = 0;
00276 }
00277 
00278 /* Separates the domain name from an internet URI.  The string passed will be
00279  * modified, and the string returned will share the same memory.  May return
00280  * NULL.  Examples:
00281  *     "http://some.domain.org/folder/file.mp3" -> "some.domain.org"
00282  *     "http://some.stream.fm:8000"             -> "some.stream.fm" */
00283 
00284 static gchar * stream_name (gchar * name)
00285 {
00286     if (! strncmp (name, "http://", 7))
00287         name += 7;
00288     else if (! strncmp (name, "https://", 8))
00289         name += 8;
00290     else if (! strncmp (name, "mms://", 6))
00291         name += 6;
00292     else
00293         return NULL;
00294 
00295     gchar * c;
00296 
00297     if ((c = strchr (name, '/')))
00298         * c = 0;
00299     if ((c = strchr (name, ':')))
00300         * c = 0;
00301     if ((c = strchr (name, '?')))
00302         * c = 0;
00303 
00304     return name;
00305 }
00306 
00307 /* Derives best guesses of title, artist, and album from a file name (URI) and
00308  * tuple.  The returned strings are stringpooled or NULL. */
00309 
00310 void describe_song (const gchar * name, const Tuple * tuple, gchar * * _title,
00311  gchar * * _artist, gchar * * _album)
00312 {
00313     /* Common folder names to skip */
00314     static const gchar * const skip[] = {"music"};
00315 
00316     const gchar * title = tuple_get_string (tuple, FIELD_TITLE, NULL);
00317     const gchar * artist = tuple_get_string (tuple, FIELD_ARTIST, NULL);
00318     const gchar * album = tuple_get_string (tuple, FIELD_ALBUM, NULL);
00319 
00320     if (title && ! title[0])
00321         title = NULL;
00322     if (artist && ! artist[0])
00323         artist = NULL;
00324     if (album && ! album[0])
00325         album = NULL;
00326 
00327     gchar * copy = NULL;
00328 
00329     if (title && artist && album)
00330         goto DONE;
00331 
00332     copy = uri_to_display (name);
00333 
00334     if (! strncmp (name, "file://", 7))
00335     {
00336         gchar * base, * first, * second;
00337         split_filename (skip_top_folders (copy), & base, & first,
00338          & second);
00339 
00340         if (! title)
00341             title = base;
00342 
00343         for (gint i = 0; i < G_N_ELEMENTS (skip); i ++)
00344         {
00345             if (first && ! strcasecmp (first, skip[i]))
00346                 first = NULL;
00347             if (second && ! strcasecmp (second, skip[i]))
00348                 second = NULL;
00349         }
00350 
00351         if (first)
00352         {
00353             if (second && ! artist && ! album)
00354             {
00355                 artist = second;
00356                 album = first;
00357             }
00358             else if (! artist)
00359                 artist = first;
00360             else if (! album)
00361                 album = first;
00362         }
00363     }
00364     else
00365     {
00366         if (! title)
00367             title = stream_name (copy);
00368         else if (! artist)
00369             artist = stream_name (copy);
00370         else if (! album)
00371             album = stream_name (copy);
00372     }
00373 
00374 DONE:
00375     * _title = title ? stringpool_get ((gchar *) title, FALSE) : NULL;
00376     * _artist = artist ? stringpool_get ((gchar *) artist, FALSE) : NULL;
00377     * _album = album ? stringpool_get ((gchar *) album, FALSE) : NULL;
00378 
00379     g_free (copy);
00380 }