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 <sys/stat.h>
00028 #include <unistd.h>
00029 
00030 #ifdef _WIN32
00031 #include <windows.h>
00032 #endif
00033 
00034 #ifdef __APPLE__
00035 #include <mach-o/dyld.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 #include <libaudcore/audstrings.h>
00046 
00047 #include "config.h"
00048 #include "debug.h"
00049 #include "i18n.h"
00050 #include "misc.h"
00051 #include "plugins.h"
00052 #include "util.h"
00053 
00054 bool_t dir_foreach (const char * path, DirForeachFunc func, void * user)
00055 {
00056     DIR * dir = opendir (path);
00057     if (! dir)
00058         return FALSE;
00059 
00060     struct dirent * entry;
00061     while ((entry = readdir (dir)))
00062     {
00063         if (entry->d_name[0] == '.')
00064             continue;
00065 
00066         char * full = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s", path, entry->d_name);
00067         bool_t stop = func (full, entry->d_name, user);
00068         g_free (full);
00069 
00070         if (stop)
00071             break;
00072     }
00073 
00074     closedir (dir);
00075     return TRUE;
00076 }
00077 
00078 char * construct_uri (const char * string, const char * playlist_name)
00079 {
00080     /* URI */
00081     if (strstr (string, "://"))
00082         return strdup (string);
00083 
00084     /* absolute filename (assumed UTF-8) */
00085 #ifdef _WIN32
00086     if (string[0] && string[1] == ':' && string[2] == '\\')
00087 #else
00088     if (string[0] == '/')
00089 #endif
00090         return filename_to_uri (string);
00091 
00092     /* relative filename (assumed UTF-8) */
00093     const char * slash = strrchr (playlist_name, '/');
00094     if (! slash)
00095         return NULL;
00096 
00097     int pathlen = slash + 1 - playlist_name;
00098     char buf[pathlen + 3 * strlen (string) + 1];
00099     memcpy (buf, playlist_name, pathlen);
00100     str_encode_percent (string, -1, buf + pathlen);
00101     return strdup (buf);
00102 }
00103 
00104 /* local files -- not URI's */
00105 int file_get_mtime (const char * filename)
00106 {
00107     struct stat info;
00108 
00109     if (stat (filename, & info))
00110         return -1;
00111 
00112     return info.st_mtime;
00113 }
00114 
00115 void
00116 make_directory(const char * path, mode_t mode)
00117 {
00118     if (g_mkdir_with_parents(path, mode) == 0)
00119         return;
00120 
00121     g_printerr(_("Could not create directory (%s): %s\n"), path,
00122                g_strerror(errno));
00123 }
00124 
00125 char * write_temp_file (void * data, int64_t len)
00126 {
00127     char * name = g_strdup_printf ("%s/audacious-temp-XXXXXX", g_get_tmp_dir ());
00128 
00129     int handle = g_mkstemp (name);
00130     if (handle < 0)
00131     {
00132         fprintf (stderr, "Error creating temporary file: %s\n", strerror (errno));
00133         g_free (name);
00134         return NULL;
00135     }
00136 
00137     while (len)
00138     {
00139         int64_t written = write (handle, data, len);
00140         if (written < 0)
00141         {
00142             fprintf (stderr, "Error writing %s: %s\n", name, strerror (errno));
00143             close (handle);
00144             g_free (name);
00145             return NULL;
00146         }
00147 
00148         data = (char *) data + written;
00149         len -= written;
00150     }
00151 
00152     if (close (handle) < 0)
00153     {
00154         fprintf (stderr, "Error closing %s: %s\n", name, strerror (errno));
00155         g_free (name);
00156         return NULL;
00157     }
00158 
00159     return name;
00160 }
00161 
00162 char * get_path_to_self (void)
00163 {
00164 #if defined _WIN32 || defined HAVE_PROC_SELF_EXE
00165     int size = 256;
00166     char * buf = g_malloc (size);
00167 
00168     while (1)
00169     {
00170         int len;
00171 
00172 #ifdef _WIN32
00173         if (! (len = GetModuleFileName (NULL, buf, size)))
00174         {
00175             fprintf (stderr, "GetModuleFileName failed.\n");
00176             g_free (buf);
00177             return NULL;
00178         }
00179 #else
00180         if ((len = readlink ("/proc/self/exe", buf, size)) < 0)
00181         {
00182             fprintf (stderr, "Cannot access /proc/self/exe: %s.\n", strerror (errno));
00183             g_free (buf);
00184             return NULL;
00185         }
00186 #endif
00187 
00188         if (len < size)
00189         {
00190             buf[len] = 0;
00191             return buf;
00192         }
00193 
00194         size += size;
00195         buf = g_realloc (buf, size);
00196     }
00197 #else
00198     return NULL;
00199 #endif
00200 }
00201 
00202 /* Strips various common top-level folders from a URI.  The string passed will
00203  * not be modified, but the string returned will share the same memory.
00204  * Examples:
00205  *     "file:///home/john/folder/file.mp3" -> "folder/file.mp3"
00206  *     "file:///folder/file.mp3"           -> "folder/file.mp3" */
00207 
00208 static char * skip_top_folders (char * name)
00209 {
00210     static char * home;
00211     static int len;
00212 
00213     if (! home)
00214     {
00215         home = filename_to_uri (g_get_home_dir ());
00216         len = strlen (home);
00217 
00218         if (len > 0 && home[len - 1] == '/')
00219             len --;
00220     }
00221 
00222 #ifdef _WIN32
00223     if (! g_ascii_strncasecmp (name, home, len) && name[len] == '/')
00224 #else
00225     if (! strncmp (name, home, len) && name[len] == '/')
00226 #endif
00227         return name + len + 1;
00228 
00229     if (! strncmp (name, "file:///", 8))
00230         return name + 8;
00231 
00232     return name;
00233 }
00234 
00235 /* Divides a 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 (char * name, char * * base, char * * first,
00244  char * * second)
00245 {
00246     * first = * second = NULL;
00247 
00248     char * c;
00249 
00250     if ((c = strrchr (name, '/')))
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, '/')))
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, '/')))
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 char * stream_name (char * 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     char * 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 static char * get_nonblank_field (const Tuple * tuple, int field)
00312 {
00313     char * str = tuple ? tuple_get_str (tuple, field, NULL) : NULL;
00314 
00315     if (str && ! str[0])
00316     {
00317         str_unref (str);
00318         str = NULL;
00319     }
00320 
00321     return str;
00322 }
00323 
00324 static char * str_get_decoded (char * str)
00325 {
00326     if (! str)
00327         return NULL;
00328 
00329     str_decode_percent (str, -1, str);
00330     return str_get (str);
00331 }
00332 
00333 /* Derives best guesses of title, artist, and album from a file name (URI) and
00334  * tuple (which may be NULL).  The returned strings are stringpooled or NULL. */
00335 
00336 void describe_song (const char * name, const Tuple * tuple, char * * _title,
00337  char * * _artist, char * * _album)
00338 {
00339     /* Common folder names to skip */
00340     static const char * const skip[] = {"music"};
00341 
00342     char * title = get_nonblank_field (tuple, FIELD_TITLE);
00343     char * artist = get_nonblank_field (tuple, FIELD_ARTIST);
00344     char * album = get_nonblank_field (tuple, FIELD_ALBUM);
00345 
00346     if (title && artist && album)
00347     {
00348 DONE:
00349         * _title = title;
00350         * _artist = artist;
00351         * _album = album;
00352         return;
00353     }
00354 
00355     char buf[strlen (name) + 1];
00356     memcpy (buf, name, sizeof buf);
00357 
00358     if (! strncmp (buf, "file:///", 8))
00359     {
00360         char * base, * first, * second;
00361         split_filename (skip_top_folders (buf), & base, & first, & second);
00362 
00363         if (! title)
00364             title = str_get_decoded (base);
00365 
00366         for (int i = 0; i < G_N_ELEMENTS (skip); i ++)
00367         {
00368             if (first && ! g_ascii_strcasecmp (first, skip[i]))
00369                 first = NULL;
00370             if (second && ! g_ascii_strcasecmp (second, skip[i]))
00371                 second = NULL;
00372         }
00373 
00374         if (first)
00375         {
00376             if (second && ! artist && ! album)
00377             {
00378                 artist = str_get_decoded (second);
00379                 album = str_get_decoded (first);
00380             }
00381             else if (! artist)
00382                 artist = str_get_decoded (first);
00383             else if (! album)
00384                 album = str_get_decoded (first);
00385         }
00386     }
00387     else
00388     {
00389         if (! title)
00390         {
00391             title = str_get_decoded (stream_name (buf));
00392 
00393             if (! title)
00394                 title = str_get_decoded (buf);
00395         }
00396         else if (! artist)
00397             artist = str_get_decoded (stream_name (buf));
00398         else if (! album)
00399             album = str_get_decoded (stream_name (buf));
00400     }
00401 
00402     goto DONE;
00403 }