Audacious
$Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
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 }