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 URI. The string passed will
215  * not be modified, but the string returned will share the same memory.
216  * Examples:
217  * "file:///home/john/folder/file.mp3" -> "folder/file.mp3"
218  * "file:///folder/file.mp3" -> "folder/file.mp3" */
219 
220 static char * skip_top_folders (char * name)
221 {
222  static char * home;
223  static int len;
224 
225  if (! home)
226  {
227  home = filename_to_uri (g_get_home_dir ());
228  len = strlen (home);
229 
230  if (len > 0 && home[len - 1] == '/')
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  if (! strncmp (name, "file:///", 8))
242  return name + 8;
243 
244  return name;
245 }
246 
247 /* Divides a URI into the base name, the lowest folder, and the
248  * second lowest folder. The string passed will be modified, and the strings
249  * returned will use the same memory. May return NULL for <first> and <second>.
250  * Examples:
251  * "a/b/c/d/e.mp3" -> "e", "d", "c"
252  * "d/e.mp3" -> "e", "d", NULL
253  * "e.mp3" -> "e", NULL, NULL */
254 
255 static void split_filename (char * name, char * * base, char * * first,
256  char * * second)
257 {
258  * first = * second = NULL;
259 
260  char * c;
261 
262  if ((c = strrchr (name, '/')))
263  {
264  * base = c + 1;
265  * c = 0;
266  }
267  else
268  {
269  * base = name;
270  goto DONE;
271  }
272 
273  if ((c = strrchr (name, '/')))
274  {
275  * first = c + 1;
276  * c = 0;
277  }
278  else
279  {
280  * first = name;
281  goto DONE;
282  }
283 
284  if ((c = strrchr (name, '/')))
285  * second = c + 1;
286  else
287  * second = name;
288 
289 DONE:
290  if ((c = strrchr (* base, '.')))
291  * c = 0;
292 }
293 
294 /* Separates the domain name from an internet URI. The string passed will be
295  * modified, and the string returned will share the same memory. May return
296  * NULL. Examples:
297  * "http://some.domain.org/folder/file.mp3" -> "some.domain.org"
298  * "http://some.stream.fm:8000" -> "some.stream.fm" */
299 
300 static char * stream_name (char * name)
301 {
302  if (! strncmp (name, "http://", 7))
303  name += 7;
304  else if (! strncmp (name, "https://", 8))
305  name += 8;
306  else if (! strncmp (name, "mms://", 6))
307  name += 6;
308  else
309  return NULL;
310 
311  char * c;
312 
313  if ((c = strchr (name, '/')))
314  * c = 0;
315  if ((c = strchr (name, ':')))
316  * c = 0;
317  if ((c = strchr (name, '?')))
318  * c = 0;
319 
320  return name;
321 }
322 
323 static char * get_nonblank_field (const Tuple * tuple, int field)
324 {
325  char * str = tuple ? tuple_get_str (tuple, field, NULL) : NULL;
326 
327  if (str && ! str[0])
328  {
329  str_unref (str);
330  str = NULL;
331  }
332 
333  return str;
334 }
335 
336 static char * str_get_decoded (char * str)
337 {
338  if (! str)
339  return NULL;
340 
341  str_decode_percent (str, -1, str);
342  return str_get (str);
343 }
344 
345 /* Derives best guesses of title, artist, and album from a file name (URI) and
346  * tuple (which may be NULL). The returned strings are stringpooled or NULL. */
347 
348 void describe_song (const char * name, const Tuple * tuple, char * * _title,
349  char * * _artist, char * * _album)
350 {
351  /* Common folder names to skip */
352  static const char * const skip[] = {"music"};
353 
354  char * title = get_nonblank_field (tuple, FIELD_TITLE);
355  char * artist = get_nonblank_field (tuple, FIELD_ARTIST);
356  char * album = get_nonblank_field (tuple, FIELD_ALBUM);
357 
358  if (title && artist && album)
359  {
360 DONE:
361  * _title = title;
362  * _artist = artist;
363  * _album = album;
364  return;
365  }
366 
367  char buf[strlen (name) + 1];
368  memcpy (buf, name, sizeof buf);
369 
370  if (! strncmp (buf, "file:///", 8))
371  {
372  char * base, * first, * second;
373  split_filename (skip_top_folders (buf), & base, & first, & second);
374 
375  if (! title)
376  title = str_get_decoded (base);
377 
378  for (int i = 0; i < G_N_ELEMENTS (skip); i ++)
379  {
380  if (first && ! g_ascii_strcasecmp (first, skip[i]))
381  first = NULL;
382  if (second && ! g_ascii_strcasecmp (second, skip[i]))
383  second = NULL;
384  }
385 
386  if (first)
387  {
388  if (second && ! artist && ! album)
389  {
390  artist = str_get_decoded (second);
391  album = str_get_decoded (first);
392  }
393  else if (! artist)
394  artist = str_get_decoded (first);
395  else if (! album)
396  album = str_get_decoded (first);
397  }
398  }
399  else
400  {
401  if (! title)
402  {
403  title = str_get_decoded (stream_name (buf));
404 
405  if (! title)
406  title = str_get_decoded (buf);
407  }
408  else if (! artist)
409  artist = str_get_decoded (stream_name (buf));
410  else if (! album)
411  album = str_get_decoded (stream_name (buf));
412  }
413 
414  goto DONE;
415 }