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 <limits.h>
00027 #include <unistd.h>
00028 
00029 #ifdef _WIN32
00030 #include <windows.h>
00031 #endif
00032 
00033 #ifdef HAVE_CONFIG_H
00034 #  include "config.h"
00035 #endif
00036 
00037 #include <glib.h>
00038 #include <stdlib.h>
00039 #include <string.h>
00040 #include <ctype.h>
00041 
00042 #include <errno.h>
00043 
00044 #ifdef HAVE_FTS_H
00045 #  include <sys/types.h>
00046 #  include <sys/stat.h>
00047 #  include <fts.h>
00048 #endif
00049 
00050 #include <libaudcore/audstrings.h>
00051 #include <libaudcore/stringpool.h>
00052 
00053 #include "audconfig.h"
00054 #include "debug.h"
00055 #include "i18n.h"
00056 #include "misc.h"
00057 #include "plugins.h"
00058 #include "util.h"
00059 
00060 gboolean
00061 dir_foreach(const gchar * path, DirForeachFunc function,
00062             gpointer user_data, GError ** error)
00063 {
00064     GError *error_out = NULL;
00065     GDir *dir;
00066     const gchar *entry;
00067     gchar *entry_fullpath;
00068 
00069     if (!(dir = g_dir_open(path, 0, &error_out))) {
00070         g_propagate_error(error, error_out);
00071         return FALSE;
00072     }
00073 
00074     while ((entry = g_dir_read_name(dir))) {
00075         entry_fullpath = g_build_filename(path, entry, NULL);
00076 
00077         if ((*function) (entry_fullpath, entry, user_data)) {
00078             g_free(entry_fullpath);
00079             break;
00080         }
00081 
00082         g_free(entry_fullpath);
00083     }
00084 
00085     g_dir_close(dir);
00086 
00087     return TRUE;
00088 }
00089 
00098 gchar*
00099 util_get_localdir(void)
00100 {
00101   gchar *datadir;
00102   gchar *tmp;
00103 
00104   if ( (tmp = getenv("XDG_CONFIG_HOME")) == NULL )
00105     datadir = g_build_filename( g_get_home_dir() , ".config" , "audacious" ,  NULL );
00106   else
00107     datadir = g_build_filename( tmp , "audacious" , NULL );
00108 
00109   return datadir;
00110 }
00111 
00112 
00113 gchar * construct_uri (const gchar * string, const gchar * playlist_name)
00114 {
00115     gchar *filename = g_strdup(string);
00116     gchar *uri = NULL;
00117 
00118     /* try to translate dos path */
00119     convert_dos_path(filename); /* in place replacement */
00120 
00121     // make full path uri here
00122     // case 1: filename is raw full path or uri
00123     if (filename[0] == '/' || strstr(filename, "://")) {
00124         uri = g_filename_to_uri(filename, NULL, NULL);
00125         if(!uri)
00126             uri = g_strdup(filename);
00127     }
00128     // case 2: filename is not raw full path nor uri
00129     // make full path by replacing last part of playlist path with filename.
00130     else
00131     {
00132         const gchar * slash = strrchr (playlist_name, '/');
00133         if (slash)
00134             uri = g_strdup_printf ("%.*s/%s", (gint) (slash - playlist_name),
00135              playlist_name, filename);
00136     }
00137 
00138     g_free (filename);
00139     return uri;
00140 }
00141 
00142 /* local files -- not URI's */
00143 gint file_get_mtime (const gchar * filename)
00144 {
00145     struct stat info;
00146 
00147     if (stat (filename, & info))
00148         return -1;
00149 
00150     return info.st_mtime;
00151 }
00152 
00153 void
00154 make_directory(const gchar * path, mode_t mode)
00155 {
00156     if (g_mkdir_with_parents(path, mode) == 0)
00157         return;
00158 
00159     g_printerr(_("Could not create directory (%s): %s\n"), path,
00160                g_strerror(errno));
00161 }
00162 
00163 gchar * get_path_to_self (void)
00164 {
00165     gchar buf[PATH_MAX];
00166     gint len;
00167 
00168 #ifdef _WIN32
00169     if (! (len = GetModuleFileName (NULL, buf, sizeof buf)) || len == sizeof buf)
00170     {
00171         fprintf (stderr, "GetModuleFileName failed.\n");
00172         return NULL;
00173     }
00174 #else
00175     if ((len = readlink ("/proc/self/exe", buf, sizeof buf)) < 0)
00176     {
00177         fprintf (stderr, "Cannot access /proc/self/exe: %s.\n", strerror (errno));
00178         return NULL;
00179     }
00180 #endif
00181 
00182     return g_strndup (buf, len);
00183 }
00184 
00185 #define URL_HISTORY_MAX_SIZE 30
00186 
00187 void
00188 util_add_url_history_entry(const gchar * url)
00189 {
00190     if (g_list_find_custom(cfg.url_history, url, (GCompareFunc) strcasecmp))
00191         return;
00192 
00193     cfg.url_history = g_list_prepend(cfg.url_history, g_strdup(url));
00194 
00195     while (g_list_length(cfg.url_history) > URL_HISTORY_MAX_SIZE) {
00196         GList *node = g_list_last(cfg.url_history);
00197         g_free(node->data);
00198         cfg.url_history = g_list_delete_link(cfg.url_history, node);
00199     }
00200 }
00201 
00202 /* Strips various common top-level folders from a file name (not URI).  The
00203  * string passed will not be modified, but the string returned will share the
00204  * same memory.  Examples:
00205  *     "/home/john/folder/file.mp3"    -> "folder/file.mp3"
00206  *     "/folder/file.mp3"              -> "folder/file.mp3"
00207  *     "C:\Users\John\folder\file.mp3" -> "folder\file.mp3"
00208  *     "E:\folder\file.mp3"            -> "folder\file.mp3" */
00209 
00210 static gchar * skip_top_folders (gchar * name)
00211 {
00212     const gchar * home = getenv ("HOME");
00213     if (! home)
00214         goto NO_HOME;
00215 
00216     gint len = strlen (home);
00217     if (len > 0 && home[len - 1] == G_DIR_SEPARATOR)
00218         len --;
00219 
00220 #ifdef _WIN32
00221     if (! strncasecmp (name, home, len) && name[len] == '\\')
00222 #else
00223     if (! strncmp (name, home, len) && name[len] == '/')
00224 #endif
00225         return name + len + 1;
00226 
00227 NO_HOME:
00228 #ifdef _WIN32
00229     return (name[0] && name[1] == ':' && name[2] == '\\') ? name + 3 : name;
00230 #else
00231     return (name[0] == '/') ? name + 1 : name;
00232 #endif
00233 }
00234 
00235 /* Divides a file name (not URI) into the base name, the lowest folder, and the
00236  * second lowest folder.  The string passed will be modified, and the strings
00237  * returned will use the same memory.  May return NULL for <first> and <second>.
00238  * Examples:
00239  *     "a/b/c/d/e.mp3" -> "e", "d",  "c"
00240  *     "d/e.mp3"       -> "e", "d",  NULL
00241  *     "e.mp3"         -> "e", NULL, NULL */
00242 
00243 static void split_filename (gchar * name, gchar * * base, gchar * * first,
00244  gchar * * second)
00245 {
00246     * first = * second = NULL;
00247 
00248     gchar * c;
00249 
00250     if ((c = strrchr (name, G_DIR_SEPARATOR)))
00251     {
00252         * base = c + 1;
00253         * c = 0;
00254     }
00255     else
00256     {
00257         * base = name;
00258         goto DONE;
00259     }
00260 
00261     if ((c = strrchr (name, G_DIR_SEPARATOR)))
00262     {
00263         * first = c + 1;
00264         * c = 0;
00265     }
00266     else
00267     {
00268         * first = name;
00269         goto DONE;
00270     }
00271 
00272     if ((c = strrchr (name, G_DIR_SEPARATOR)))
00273         * second = c + 1;
00274     else
00275         * second = name;
00276 
00277 DONE:
00278     if ((c = strrchr (* base, '.')))
00279         * c = 0;
00280 }
00281 
00282 /* Separates the domain name from an internet URI.  The string passed will be
00283  * modified, and the string returned will share the same memory.  May return
00284  * NULL.  Examples:
00285  *     "http://some.domain.org/folder/file.mp3" -> "some.domain.org"
00286  *     "http://some.stream.fm:8000"             -> "some.stream.fm" */
00287 
00288 static gchar * stream_name (gchar * name)
00289 {
00290     if (! strncmp (name, "http://", 7))
00291         name += 7;
00292     else if (! strncmp (name, "https://", 8))
00293         name += 8;
00294     else if (! strncmp (name, "mms://", 6))
00295         name += 6;
00296     else
00297         return NULL;
00298 
00299     gchar * c;
00300 
00301     if ((c = strchr (name, '/')))
00302         * c = 0;
00303     if ((c = strchr (name, ':')))
00304         * c = 0;
00305     if ((c = strchr (name, '?')))
00306         * c = 0;
00307 
00308     return name;
00309 }
00310 
00311 /* Derives best guesses of title, artist, and album from a file name (URI) and
00312  * tuple.  The returned strings are stringpooled or NULL. */
00313 
00314 void describe_song (const gchar * name, const Tuple * tuple, gchar * * _title,
00315  gchar * * _artist, gchar * * _album)
00316 {
00317     /* Common folder names to skip */
00318     static const gchar * const skip[] = {"music"};
00319 
00320     const gchar * title = tuple_get_string (tuple, FIELD_TITLE, NULL);
00321     const gchar * artist = tuple_get_string (tuple, FIELD_ARTIST, NULL);
00322     const gchar * album = tuple_get_string (tuple, FIELD_ALBUM, NULL);
00323 
00324     if (title && ! title[0])
00325         title = NULL;
00326     if (artist && ! artist[0])
00327         artist = NULL;
00328     if (album && ! album[0])
00329         album = NULL;
00330 
00331     gchar * copy = NULL;
00332 
00333     if (title && artist && album)
00334         goto DONE;
00335 
00336     copy = uri_to_display (name);
00337 
00338     if (! strncmp (name, "file://", 7))
00339     {
00340         gchar * base, * first, * second;
00341         split_filename (skip_top_folders (copy), & base, & first,
00342          & second);
00343 
00344         if (! title)
00345             title = base;
00346 
00347         for (gint i = 0; i < G_N_ELEMENTS (skip); i ++)
00348         {
00349             if (first && ! strcasecmp (first, skip[i]))
00350                 first = NULL;
00351             if (second && ! strcasecmp (second, skip[i]))
00352                 second = NULL;
00353         }
00354 
00355         if (first)
00356         {
00357             if (second && ! artist && ! album)
00358             {
00359                 artist = second;
00360                 album = first;
00361             }
00362             else if (! artist)
00363                 artist = first;
00364             else if (! album)
00365                 album = first;
00366         }
00367     }
00368     else
00369     {
00370         if (! title)
00371             title = stream_name (copy);
00372         else if (! artist)
00373             artist = stream_name (copy);
00374         else if (! album)
00375             album = stream_name (copy);
00376     }
00377 
00378 DONE:
00379     * _title = title ? stringpool_get ((gchar *) title, FALSE) : NULL;
00380     * _artist = artist ? stringpool_get ((gchar *) artist, FALSE) : NULL;
00381     * _album = album ? stringpool_get ((gchar *) album, FALSE) : NULL;
00382 
00383     g_free (copy);
00384 }