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