Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
util.c
Go to the documentation of this file.
1 /* Audacious - Cross-platform multimedia player
2  * Copyright (C) 2005-2011 Audacious development team
3  *
4  * Based on BMP:
5  * Copyright (C) 2003-2004 BMP development team.
6  *
7  * Based on XMMS:
8  * Copyright (C) 1998-2003 XMMS development team.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; under version 3 of the License.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <http://www.gnu.org/licenses>.
21  *
22  * The Audacious team does not consider modular code linking to
23  * Audacious or using our public API to be a derived work.
24  */
25 
26 #include <dirent.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 
30 #ifdef _WIN32
31 #include <windows.h>
32 #endif
33 
34 #ifdef __APPLE__
35 #include <mach-o/dyld.h>
36 #endif
37 
38 #include <glib.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <ctype.h>
42 
43 #include <errno.h>
44 
45 #include <libaudcore/audstrings.h>
46 
47 #include "config.h"
48 #include "debug.h"
49 #include "i18n.h"
50 #include "misc.h"
51 #include "plugins.h"
52 #include "util.h"
53 
54 bool_t dir_foreach (const char * path, DirForeachFunc func, void * user)
55 {
56  DIR * dir = opendir (path);
57  if (! dir)
58  return FALSE;
59 
60  struct dirent * entry;
61  while ((entry = readdir (dir)))
62  {
63  if (entry->d_name[0] == '.')
64  continue;
65 
66  char * full = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s", path, entry->d_name);
67  bool_t stop = func (full, entry->d_name, user);
68  g_free (full);
69 
70  if (stop)
71  break;
72  }
73 
74  closedir (dir);
75  return TRUE;
76 }
77 
78 char * construct_uri (const char * string, const char * playlist_name)
79 {
80  /* URI */
81  if (strstr (string, "://"))
82  return strdup (string);
83 
84  /* absolute filename (assumed UTF-8) */
85 #ifdef _WIN32
86  if (string[0] && string[1] == ':' && string[2] == '\\')
87 #else
88  if (string[0] == '/')
89 #endif
90  return filename_to_uri (string);
91 
92  /* relative filename (assumed UTF-8) */
93  const char * slash = strrchr (playlist_name, '/');
94  if (! slash)
95  return NULL;
96 
97  int pathlen = slash + 1 - playlist_name;
98  char buf[pathlen + 3 * strlen (string) + 1];
99  memcpy (buf, playlist_name, pathlen);
100  str_encode_percent (string, -1, buf + pathlen);
101  return strdup (buf);
102 }
103 
104 /* local files -- not URI's */
105 int file_get_mtime (const char * filename)
106 {
107  struct stat info;
108 
109  if (stat (filename, & info))
110  return -1;
111 
112  return info.st_mtime;
113 }
114 
115 void
116 make_directory(const char * path, mode_t mode)
117 {
118  if (g_mkdir_with_parents(path, mode) == 0)
119  return;
120 
121  g_printerr(_("Could not create directory (%s): %s\n"), path,
122  g_strerror(errno));
123 }
124 
125 char * write_temp_file (void * data, int64_t len)
126 {
127  char * name = g_strdup_printf ("%s/audacious-temp-XXXXXX", g_get_tmp_dir ());
128 
129  int handle = g_mkstemp (name);
130  if (handle < 0)
131  {
132  fprintf (stderr, "Error creating temporary file: %s\n", strerror (errno));
133  g_free (name);
134  return NULL;
135  }
136 
137  while (len)
138  {
139  int64_t written = write (handle, data, len);
140  if (written < 0)
141  {
142  fprintf (stderr, "Error writing %s: %s\n", name, strerror (errno));
143  close (handle);
144  g_free (name);
145  return NULL;
146  }
147 
148  data = (char *) data + written;
149  len -= written;
150  }
151 
152  if (close (handle) < 0)
153  {
154  fprintf (stderr, "Error closing %s: %s\n", name, strerror (errno));
155  g_free (name);
156  return NULL;
157  }
158 
159  return name;
160 }
161 
162 char * get_path_to_self (void)
163 {
164 #if defined _WIN32 || defined HAVE_PROC_SELF_EXE
165  int size = 256;
166  char * buf = g_malloc (size);
167 
168  while (1)
169  {
170  int len;
171 
172 #ifdef _WIN32
173  if (! (len = GetModuleFileName (NULL, buf, size)))
174  {
175  fprintf (stderr, "GetModuleFileName failed.\n");
176  g_free (buf);
177  return NULL;
178  }
179 #else
180  if ((len = readlink ("/proc/self/exe", buf, size)) < 0)
181  {
182  fprintf (stderr, "Cannot access /proc/self/exe: %s.\n", strerror (errno));
183  g_free (buf);
184  return NULL;
185  }
186 #endif
187 
188  if (len < size)
189  {
190  buf[len] = 0;
191  return buf;
192  }
193 
194  size += size;
195  buf = g_realloc (buf, size);
196  }
197 #else
198  return NULL;
199 #endif
200 }
201 
202 /* Strips various common top-level folders from a URI. The string passed will
203  * not be modified, but the string returned will share the same memory.
204  * Examples:
205  * "file:///home/john/folder/file.mp3" -> "folder/file.mp3"
206  * "file:///folder/file.mp3" -> "folder/file.mp3" */
207 
208 static char * skip_top_folders (char * name)
209 {
210  static char * home;
211  static int len;
212 
213  if (! home)
214  {
215  home = filename_to_uri (g_get_home_dir ());
216  len = strlen (home);
217 
218  if (len > 0 && home[len - 1] == '/')
219  len --;
220  }
221 
222 #ifdef _WIN32
223  if (! g_ascii_strncasecmp (name, home, len) && name[len] == '/')
224 #else
225  if (! strncmp (name, home, len) && name[len] == '/')
226 #endif
227  return name + len + 1;
228 
229  if (! strncmp (name, "file:///", 8))
230  return name + 8;
231 
232  return name;
233 }
234 
235 /* Divides a URI into the base name, the lowest folder, and the
236  * second lowest folder. The string passed will be modified, and the strings
237  * returned will use the same memory. May return NULL for <first> and <second>.
238  * Examples:
239  * "a/b/c/d/e.mp3" -> "e", "d", "c"
240  * "d/e.mp3" -> "e", "d", NULL
241  * "e.mp3" -> "e", NULL, NULL */
242 
243 static void split_filename (char * name, char * * base, char * * first,
244  char * * second)
245 {
246  * first = * second = NULL;
247 
248  char * c;
249 
250  if ((c = strrchr (name, '/')))
251  {
252  * base = c + 1;
253  * c = 0;
254  }
255  else
256  {
257  * base = name;
258  goto DONE;
259  }
260 
261  if ((c = strrchr (name, '/')))
262  {
263  * first = c + 1;
264  * c = 0;
265  }
266  else
267  {
268  * first = name;
269  goto DONE;
270  }
271 
272  if ((c = strrchr (name, '/')))
273  * second = c + 1;
274  else
275  * second = name;
276 
277 DONE:
278  if ((c = strrchr (* base, '.')))
279  * c = 0;
280 }
281 
282 /* Separates the domain name from an internet URI. The string passed will be
283  * modified, and the string returned will share the same memory. May return
284  * NULL. Examples:
285  * "http://some.domain.org/folder/file.mp3" -> "some.domain.org"
286  * "http://some.stream.fm:8000" -> "some.stream.fm" */
287 
288 static char * stream_name (char * name)
289 {
290  if (! strncmp (name, "http://", 7))
291  name += 7;
292  else if (! strncmp (name, "https://", 8))
293  name += 8;
294  else if (! strncmp (name, "mms://", 6))
295  name += 6;
296  else
297  return NULL;
298 
299  char * c;
300 
301  if ((c = strchr (name, '/')))
302  * c = 0;
303  if ((c = strchr (name, ':')))
304  * c = 0;
305  if ((c = strchr (name, '?')))
306  * c = 0;
307 
308  return name;
309 }
310 
311 static char * get_nonblank_field (const Tuple * tuple, int field)
312 {
313  char * str = tuple ? tuple_get_str (tuple, field, NULL) : NULL;
314 
315  if (str && ! str[0])
316  {
317  str_unref (str);
318  str = NULL;
319  }
320 
321  return str;
322 }
323 
324 static char * str_get_decoded (char * str)
325 {
326  if (! str)
327  return NULL;
328 
329  str_decode_percent (str, -1, str);
330  return str_get (str);
331 }
332 
333 /* Derives best guesses of title, artist, and album from a file name (URI) and
334  * tuple (which may be NULL). The returned strings are stringpooled or NULL. */
335 
336 void describe_song (const char * name, const Tuple * tuple, char * * _title,
337  char * * _artist, char * * _album)
338 {
339  /* Common folder names to skip */
340  static const char * const skip[] = {"music"};
341 
342  char * title = get_nonblank_field (tuple, FIELD_TITLE);
343  char * artist = get_nonblank_field (tuple, FIELD_ARTIST);
344  char * album = get_nonblank_field (tuple, FIELD_ALBUM);
345 
346  if (title && artist && album)
347  {
348 DONE:
349  * _title = title;
350  * _artist = artist;
351  * _album = album;
352  return;
353  }
354 
355  char buf[strlen (name) + 1];
356  memcpy (buf, name, sizeof buf);
357 
358  if (! strncmp (buf, "file:///", 8))
359  {
360  char * base, * first, * second;
361  split_filename (skip_top_folders (buf), & base, & first, & second);
362 
363  if (! title)
364  title = str_get_decoded (base);
365 
366  for (int i = 0; i < G_N_ELEMENTS (skip); i ++)
367  {
368  if (first && ! g_ascii_strcasecmp (first, skip[i]))
369  first = NULL;
370  if (second && ! g_ascii_strcasecmp (second, skip[i]))
371  second = NULL;
372  }
373 
374  if (first)
375  {
376  if (second && ! artist && ! album)
377  {
378  artist = str_get_decoded (second);
379  album = str_get_decoded (first);
380  }
381  else if (! artist)
382  artist = str_get_decoded (first);
383  else if (! album)
384  album = str_get_decoded (first);
385  }
386  }
387  else
388  {
389  if (! title)
390  {
391  title = str_get_decoded (stream_name (buf));
392 
393  if (! title)
394  title = str_get_decoded (buf);
395  }
396  else if (! artist)
397  artist = str_get_decoded (stream_name (buf));
398  else if (! album)
399  album = str_get_decoded (stream_name (buf));
400  }
401 
402  goto DONE;
403 }