1 /* -*- coding: utf-8; -*-
2 |
3 |  Copyright (C) 2002-2007 Jorg Schuler <jcsjcs at users sourceforge net>
4 |  Part of the gtkpod project.
5 |
6 |  URL: http://www.gtkpod.org/
7 |  URL: http://gtkpod.sourceforge.net/
8 |
9 |  This program is free software; you can redistribute it and/or modify
10 |  it under the terms of the GNU General Public License as published by
11 |  the Free Software Foundation; either version 2 of the License, or
12 |  (at your option) any later version.
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, write to the Free Software
21 |  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 |
23 |  iTunes and iPod are trademarks of Apple
24 |
25 |  This product is not supported/written/published by Apple!
26 |
27 |  $Id$
28 */
29 
30 #ifdef HAVE_CONFIG_H
31 #  include <config.h>
32 #endif
33 
34 #include <errno.h>
35 #include <gtk/gtk.h>
36 #include <glib/gstdio.h>
37 #include <string.h>
38 #include <sys/stat.h>
39 #include <sys/wait.h>
40 #include <unistd.h>
41 #include "autodetection.h"
42 #include "charset.h"
43 #include "clientserver.h"
44 #include "file_convert.h"
45 #include "misc.h"
46 #include "mp4file.h"
47 #include "prefs.h"
48 #include "misc_track.h"
49 #include "display_photo.h"
50 #include "stock_icons.h"
51 
52 #define DEBUG_MISC 0
53 
54 
55 /* where to find the scripts */
56 const gchar *SCRIPTDIR = PACKAGE_DATA_DIR G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "scripts" G_DIR_SEPARATOR_S;
57 
58 
59 /*------------------------------------------------------------------*\
60  *                                                                  *
61  *             Miscellaneous                                        *
62  *                                                                  *
63 \*------------------------------------------------------------------*/
64 
utf8_strcasestr(const gchar * haystack,const gchar * needle)65 gchar *utf8_strcasestr(const gchar *haystack, const gchar *needle) {
66     g_return_val_if_fail (haystack != NULL, NULL);
67     g_return_val_if_fail (needle != NULL, NULL);
68 
69     return g_strrstr(haystack, needle);
70 }
71 
72 /* Calculate the time in ms passed since @old_time. @old_time is
73    updated with the current time if @update is TRUE*/
get_ms_since(GTimeVal * old_time,gboolean update)74 float get_ms_since (GTimeVal *old_time, gboolean update)
75 {
76     GTimeVal new_time;
77     float result;
78 
79     g_get_current_time (&new_time);
80     result = (new_time.tv_sec - old_time->tv_sec) * 1000 +
81 	(float)(new_time.tv_usec - old_time->tv_usec) / 1000;
82     if (update)
83     {
84 	old_time->tv_sec = new_time.tv_sec;
85 	old_time->tv_usec = new_time.tv_usec;
86     }
87     return result;
88 }
89 
90 /* parse a bunch of track pointers delimited by \n
91  * @s - address of the character string we're parsing (gets updated)
92  * @track - pointer the track pointer parsed from the string
93  * Returns FALSE when the string is empty, TRUE when the string can still be
94  *	parsed
95  */
96 gboolean
parse_tracks_from_string(gchar ** s,Track ** track)97 parse_tracks_from_string(gchar **s, Track **track)
98 {
99     g_return_val_if_fail (track, FALSE);
100     *track = NULL;
101     g_return_val_if_fail (s, FALSE);
102 
103     if(*s)
104     {
105 	gchar *str = *s;
106 	gchar *strp = strchr (str, '\n');
107 	int tokens;
108 
109 	if (strp == NULL)
110 	{
111 	    *track = NULL;
112 	    *s = NULL;
113 	    return FALSE;
114 	}
115 	tokens = sscanf (str, "%p", track);
116 	++strp;
117 	if (*strp) *s = strp;
118 	else       *s = NULL;
119 	if (tokens == 1) 	return TRUE;
120 	else                    return FALSE;
121     }
122     return FALSE;
123 }
124 
125 gboolean
parse_artwork_from_string(gchar ** s,Artwork ** artwork)126 parse_artwork_from_string(gchar **s, Artwork **artwork)
127 {
128     g_return_val_if_fail (artwork, FALSE);
129     *artwork = NULL;
130     g_return_val_if_fail (s, FALSE);
131 
132     if(*s)
133     {
134 	gchar *str = *s;
135 	gchar *strp = strchr (str, '\n');
136 	int tokens;
137 
138 	if (strp == NULL)
139 	{
140 	    *artwork = NULL;
141 	    *s = NULL;
142 	    return FALSE;
143 	}
144 	tokens = sscanf (str, "%p", artwork);
145 	++strp;
146 	if (*strp) *s = strp;
147 	else       *s = NULL;
148 	if (tokens == 1) 	return TRUE;
149 	else                    return FALSE;
150     }
151     return FALSE;
152 }
153 
154 
155 /***************************************************************************
156  * gtkpod.in,out calls
157  *
158  **************************************************************************/
159 
160 
161 /* tries to call "/bin/sh @script" with command line options */
do_script(const gchar * script,va_list args)162 static void do_script (const gchar *script, va_list args)
163 {
164     char *str;
165     char **argv;
166     GPtrArray *ptra = g_ptr_array_sized_new (10);
167 
168     /* prepend args with "sh" and the name of the script */
169     g_ptr_array_add (ptra, "sh");
170     g_ptr_array_add (ptra, (gpointer)script);
171     /* add remaining args */
172     while ((str = va_arg (args, char *)))
173     {
174 	g_ptr_array_add (ptra, str);
175     }
176     g_ptr_array_add (ptra, NULL);
177     argv = (char **)g_ptr_array_free (ptra, FALSE);
178 
179     if (script)
180     {
181 	pid_t pid, tpid;
182 	int status;
183 
184 	pid = fork ();
185 	switch (pid)
186 	{
187 	case 0: /* child */
188 	    execv("/bin/sh", argv);
189 	    exit(0);
190 	break;
191 	case -1: /* parent and error */
192 	break;
193 	default: /* parent -- let's wait for the child to terminate */
194 	    tpid = waitpid (pid, &status, 0);
195 	    /* we could evaluate tpid and status now */
196 	    break;
197 	}
198     }
199     g_free (argv);
200 }
201 
202 
203 /* tries to execute "/bin/sh ~/.gtkpod/@script" or
204  * "/bin/sh /etc/gtkpod/@script" if the former does not exist. This
205  * function accepts command line arguments that must be terminated by
206  * NULL. */
call_script(gchar * script,...)207 void call_script (gchar *script, ...)
208 {
209     gchar *cfgdir;
210     va_list args;
211     gchar *file;
212 
213     if (!script) return;
214 
215     cfgdir =  prefs_get_cfgdir ();
216     file = g_build_filename (cfgdir, script, NULL);
217 
218     va_start (args, script);
219     if (g_file_test (file, G_FILE_TEST_EXISTS))
220     {
221 	do_script (file, args);
222     }
223     else
224     {
225 	C_FREE (file);
226 	file = g_build_filename ("/etc/gtkpod/", script, NULL);
227 	if (g_file_test (file, G_FILE_TEST_EXISTS))
228 	{
229 	    do_script (file, args);
230 	}
231     }
232     va_end (args);
233 
234     g_free (file);
235     g_free (cfgdir);
236 }
237 
238 
239 
240 /* Create a NULL-terminated array of strings given in the command
241    line. The last argument must be NULL.
242 
243    As a special feature, the first argument is split up into
244    individual strings to allow the use of "convert-2mp3 -q <special
245    settings>". Set the first argument to NULL if you don't want this.
246 
247    You must free the returned array with g_strfreev() after use. */
build_argv_from_strings(const gchar * first_arg,...)248 gchar **build_argv_from_strings (const gchar *first_arg, ...)
249 {
250     gchar **argv;
251     va_list args;
252     const gchar *str;
253     GPtrArray *ptra = g_ptr_array_sized_new (20);
254 
255     if (first_arg)
256     {
257 	gchar **strings = g_strsplit (first_arg, " ", 0);
258 	gchar **strp = strings;
259 	while (*strp)
260 	{
261 	    if (**strp)
262 	    {   /* ignore empty strings */
263 		g_ptr_array_add (ptra, g_strdup(*strp));
264 	    }
265 	    ++strp;
266 	}
267 	g_strfreev (strings);
268     }
269 
270     va_start (args, first_arg);
271     do
272     {
273 	str = va_arg (args, const gchar *);
274 	g_ptr_array_add (ptra, g_strdup (str));
275     }
276     while (str);
277 
278     va_end (args);
279 
280     argv = (gchar **)g_ptr_array_free (ptra, FALSE);
281 
282     return argv;
283 }
284 
285 
286 
287 
288 /* compare @str1 and @str2 case-sensitively or case-insensitively
289  * depending on prefs settings */
compare_string(const gchar * str1,const gchar * str2)290 gint compare_string (const gchar *str1, const gchar *str2)
291 {
292     gint result;
293     gchar *sortkey1 = make_sortkey (str1);
294     gchar *sortkey2 = make_sortkey (str2);
295 
296     result = strcmp (sortkey1, sortkey2);
297 
298     g_free (sortkey1);
299     g_free (sortkey2);
300     return result;
301 }
302 
303 struct csfk
304 {
305     gint length;
306     gchar *key;
307 };
308 
309 static GList *csfk_list = NULL;
310 
311 
312 /* Returns the sortkey for an entry name.
313  *
314  * The sort key can be compared with other sort keys using strcmp and
315  * it will give the expected result, according to the user
316  * settings. Must be regenerated if the user settings change.
317  *
318  * The caller is responsible of freeing the returned key with g_free.
319  */
320 gchar *
make_sortkey(const gchar * name)321 make_sortkey (const gchar *name)
322 {
323     if (prefs_get_int("case_sensitive"))
324     {
325 	return g_utf8_collate_key (name, -1);
326     }
327     else
328     {
329 	gchar *casefolded = g_utf8_casefold (name, -1);
330 	gchar *key = g_utf8_collate_key (casefolded, -1);
331 	g_free (casefolded);
332 	return key;
333     }
334 }
335 
336 /* needs to be called everytime the sort_ign_strings in the prefs were
337    changed */
compare_string_fuzzy_generate_keys(void)338 void compare_string_fuzzy_generate_keys (void)
339 {
340     GList *gl;
341     GList *sort_ign_strings;
342     GList *current;
343 
344     /* remove old keys */
345     for (gl=csfk_list; gl; gl=gl->next)
346     {
347 	struct csfk *csfk = gl->data;
348 	g_return_if_fail (csfk);
349 	g_free (csfk->key);
350 	g_free (csfk);
351     }
352     g_list_free (csfk_list);
353     csfk_list = NULL;
354 
355     /* create new keys */
356     sort_ign_strings = prefs_get_list("sort_ign_string_");
357     current = sort_ign_strings;
358     while (current)
359     {
360 	gchar *str = current->data;
361 	struct csfk *csfk;
362 	gchar *tempStr;
363 
364 	current = g_list_next(current);
365 
366 	csfk = g_malloc (sizeof (struct csfk));
367 	tempStr = g_utf8_casefold (str, -1 );
368 	csfk->length = g_utf8_strlen (tempStr, -1 );
369 	csfk->key = g_utf8_collate_key (tempStr, -1 );
370 	g_free (tempStr);
371 
372 	csfk_list = g_list_prepend (csfk_list, csfk);
373     }
374     prefs_free_list(sort_ign_strings);
375 }
376 
377 /* Returns a pointer inside the name, possibly skiping a prefix from
378  * the list generated by compare_string_fuzzy_generate_keys.
379  */
380 const gchar *
fuzzy_skip_prefix(const gchar * name)381 fuzzy_skip_prefix (const gchar *name)
382 {
383     const gchar *result = name;
384     const GList *gl;
385     gchar *cleanStr;
386 
387     /* If the article collations keys have not been generated,
388      * do that first
389      */
390     if (!csfk_list)
391 	compare_string_fuzzy_generate_keys ();
392 
393     cleanStr = g_utf8_casefold (name, -1);
394 
395     for (gl=csfk_list; gl; gl=g_list_next(gl))
396     {
397 	struct csfk *csfk = gl->data;
398 	gchar *tempStr;
399 
400 	g_return_val_if_fail (csfk, 0);
401 
402     	tempStr = g_utf8_collate_key (cleanStr, csfk->length);
403 	if (strcmp (tempStr, csfk->key) == 0)
404 	{
405 	    /* Found article, bump pointers ahead appropriate distance
406 	     */
407 	    result += csfk->length;
408 	    g_free (tempStr);
409 	    break;
410 	}
411 	g_free (tempStr);
412     }
413 
414     g_free (cleanStr);
415 
416     return result;
417 }
418 
419 /* compare @str1 and @str2 case-sensitively or case-insensitively
420  * depending on prefs settings, and ignoring certain initial articles
421  * ("the", "le"/"la", etc) */
compare_string_fuzzy(const gchar * str1,const gchar * str2)422 gint compare_string_fuzzy (const gchar *str1, const gchar *str2)
423 {
424     return compare_string (fuzzy_skip_prefix (str1),
425 			   fuzzy_skip_prefix (str2));
426 }
427 
428 /* compare @str1 and @str2 case-insensitively */
compare_string_case_insensitive(const gchar * str1,const gchar * str2)429 gint compare_string_case_insensitive (const gchar *str1, const gchar *str2)
430 {
431     gchar *string1 = g_utf8_casefold (str1, -1);
432     gchar *string2 = g_utf8_casefold (str2, -1);
433     gint result = g_utf8_collate (string1, string2);
434     g_free (string1);
435     g_free (string2);
436     return result;
437 }
438 
439 /* todo: optionally ignore 'the', 'a,' etc. */
compare_string_start_case_insensitive(const gchar * haystack,const gchar * needle)440 gboolean compare_string_start_case_insensitive (const gchar *haystack, const gchar *needle)
441 {
442   gint cmp = 0;
443   gchar *nhaystack = g_utf8_normalize(haystack, -1, G_NORMALIZE_ALL);
444   gchar *lhaystack = g_utf8_casefold(nhaystack,-1);
445   gchar *nneedle = g_utf8_normalize(needle, -1, G_NORMALIZE_ALL);
446   gchar *lneedle = g_utf8_casefold(nneedle,-1);
447 
448 
449   cmp = strncmp(lhaystack, lneedle, strlen(lneedle));
450 
451   /*
452     printf("searched for %s , matching against %s with %d bytes. say=%d\n",
453     lneedle, lhaystack, strlen(lneedle), cmp);
454   */
455 
456   g_free(nhaystack);
457   g_free(lhaystack);
458   g_free(nneedle);
459   g_free(lneedle);
460   return cmp;
461 };
462 
463 
464 /* ------------------------------------------------------------
465 ------------------------------------------------------------------
466 --------                                                 ---------
467 --------                 UTF16 section                   ---------
468 --------                                                 ---------
469 ------------------------------------------------------------------
470    ------------------------------------------------------------ */
471 
472 /* Get length of utf16 string in number of characters (words) */
utf16_strlen(gunichar2 * utf16)473 guint32 utf16_strlen (gunichar2 *utf16)
474 {
475   guint32 i=0;
476   if (utf16)
477       while (utf16[i] != 0) ++i;
478   return i;
479 }
480 
481 /* duplicate a utf16 string */
utf16_strdup(gunichar2 * utf16)482 gunichar2 *utf16_strdup (gunichar2 *utf16)
483 {
484     guint32 len;
485     gunichar2 *new = NULL;
486 
487     if (utf16)
488     {
489 	len = utf16_strlen (utf16);
490 	new = g_malloc (sizeof (gunichar2) * (len+1));
491 	if (new) memcpy (new, utf16, sizeof (gunichar2) * (len+1));
492     }
493     return new;
494 }
495 
496 
497 
498 
499 /*------------------------------------------------------------------*\
500  *                                                                  *
501  *  Generic functions to handle options in pop-up requesters        *
502  *                                                                  *
503 \*------------------------------------------------------------------*/
504 
505 
506 
507 
508 /* Set the toggle button to active that is specified by @prefs_string
509    (integer value). If no parameter is set in the prefs, use
510    @dflt. The corresponding widget names are stored in an array
511    @widgets and are member of @win */
option_set_radio_button(GladeXML * win_xml,const gchar * prefs_string,const gchar ** widgets,gint dflt)512 void option_set_radio_button (GladeXML *win_xml,
513 			      const gchar *prefs_string,
514 			      const gchar **widgets,
515 			      gint dflt)
516 {
517     gint wnum, num=0;
518     GtkWidget *w;
519 
520     g_return_if_fail (win_xml && prefs_string && widgets);
521 
522     /* number of available widgets */
523     num=0;
524     while (widgets[num]) ++num;
525 
526     if (!prefs_get_int_value (prefs_string, &wnum))
527 	wnum = dflt;
528 
529     if ((wnum >= num) || (wnum < 0))
530     {
531 	fprintf (stderr, "Programming error: wnum > num (%d,%d,%s)\n",
532 		 wnum, num, prefs_string);
533 	/* set to reasonable default value */
534 	prefs_set_int (prefs_string, 0);
535 	wnum = 0;
536     }
537     w = gtkpod_xml_get_widget (win_xml, widgets[wnum]);
538     if (w)
539 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), TRUE);
540 }
541 
542 
543 /* Retrieve which toggle button was activated and store the state in
544  * the prefs */
option_get_radio_button(GladeXML * win_xml,const gchar * prefs_string,const gchar ** widgets)545 gint option_get_radio_button (GladeXML *win_xml,
546 			      const gchar *prefs_string,
547 			      const gchar **widgets)
548 {
549     gint i;
550 
551     g_return_val_if_fail (win_xml && prefs_string && widgets, 0);
552 
553     for (i=0; widgets[i]; ++i)
554     {
555 	GtkWidget *w = gtkpod_xml_get_widget (win_xml, widgets[i]);
556 	if (w)
557 	{
558 	    if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)))
559 		break;
560 	}
561     }
562     if (!widgets[i])
563     {
564 	fprintf (stderr, "Programming error: no active toggle button (%s)", prefs_string);
565 	/* set reasonable default */
566 	i=0;
567     }
568     prefs_set_int (prefs_string, i);
569     return i;
570 }
571 
572 
573 /* Set the current folder to what is stored in the prefs */
option_set_folder(GtkFileChooser * fc,const gchar * prefs_string)574 void option_set_folder (GtkFileChooser *fc, const gchar *prefs_string)
575 {
576     gchar *folder;
577 
578     g_return_if_fail (fc && prefs_string);
579 
580     prefs_get_string_value (prefs_string, &folder);
581     if (!folder)
582 	folder = g_strdup (g_get_home_dir ());
583     gtk_file_chooser_set_current_folder (fc, folder);
584     g_free (folder);
585 }
586 
587 
588 /* Retrieve the current folder and write it to the prefs */
589 /* If @value is != NULL, a copy of the folder is placed into
590    @value. It has to be g_free()d after use */
option_get_folder(GtkFileChooser * fc,const gchar * prefs_string,gchar ** value)591 void option_get_folder (GtkFileChooser *fc,
592 			const gchar *prefs_string,
593 			gchar **value)
594 {
595     gchar *folder;
596 
597     g_return_if_fail (fc && prefs_string);
598 
599     folder = gtk_file_chooser_get_current_folder (fc);
600     prefs_set_string (prefs_string, folder);
601 
602     if (value) *value = folder;
603     else       g_free (folder);
604 }
605 
606 
607 /* Set the current filename to what is stored in the prefs */
option_set_filename(GtkFileChooser * fc,const gchar * prefs_string)608 void option_set_filename (GtkFileChooser *fc, const gchar *prefs_string)
609 {
610     gchar *filename;
611 
612     g_return_if_fail (fc && prefs_string);
613 
614     prefs_get_string_value (prefs_string, &filename);
615     if (!filename)
616 	filename = g_strdup (g_get_home_dir ());
617     gtk_file_chooser_set_current_name (fc, filename);
618     g_free (filename);
619 }
620 
621 
622 /* Retrieve the current filename and write it to the prefs */
623 /* If @value is != NULL, a copy of the filename is placed into
624    @value. It has to be g_free()d after use */
option_get_filename(GtkFileChooser * fc,const gchar * prefs_string,gchar ** value)625 void option_get_filename (GtkFileChooser *fc,
626 			  const gchar *prefs_string,
627 			  gchar **value)
628 {
629     gchar *filename;
630 
631     g_return_if_fail (fc && prefs_string);
632 
633     filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(fc));
634     prefs_set_string (prefs_string, filename);
635 
636     if (value) *value = filename;
637     else       g_free (filename);
638 }
639 
640 
641 /* Set the string entry @name to the prefs value stored in @name or
642    to @default if @name is not yet defined. */
option_set_string(GladeXML * win_xml,const gchar * name,const gchar * dflt)643 void option_set_string (GladeXML *win_xml,
644 			const gchar *name,
645 			const gchar *dflt)
646 {
647     gchar *string;
648     GtkWidget *entry;
649 
650     g_return_if_fail (win_xml && name && dflt);
651 
652     prefs_get_string_value (name, &string);
653 
654     if (!string)
655 	string = g_strdup (dflt);
656 
657     entry = gtkpod_xml_get_widget (win_xml, name);
658 
659     if (entry)
660 	gtk_entry_set_text(GTK_ENTRY(entry), string);
661 
662     g_free (string);
663 }
664 
665 /* Retrieve the current content of the string entry @name and write it
666  * to the prefs (@name) */
667 /* If @value is != NULL, a copy of the string is placed into
668    @value. It has to be g_free()d after use */
option_get_string(GladeXML * win_xml,const gchar * name,gchar ** value)669 void option_get_string (GladeXML *win_xml,
670 			const gchar *name,
671 			gchar **value)
672 {
673     GtkWidget *entry;
674 
675     g_return_if_fail (win_xml && name);
676 
677     entry = gtkpod_xml_get_widget (win_xml, name);
678 
679     if (entry)
680     {
681 	const gchar *str = gtk_entry_get_text (GTK_ENTRY (entry));
682 	prefs_set_string (name, str);
683 	if (value) *value = g_strdup (str);
684     }
685 }
686 
687 
688 /* Set the state of toggle button @name to the prefs value stored in
689    @name or to @default if @name is not yet defined. */
option_set_toggle_button(GladeXML * win_xml,const gchar * name,gboolean dflt)690 void option_set_toggle_button (GladeXML *win_xml,
691 			       const gchar *name,
692 			       gboolean dflt)
693 {
694     gboolean active;
695     GtkWidget *button;
696 
697     g_return_if_fail (win_xml && name);
698 
699     if (!prefs_get_int_value (name, &active))
700 	active = dflt;
701 
702     button = gtkpod_xml_get_widget (win_xml, name);
703 
704     if (button)
705 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button),
706 				      active);
707 }
708 
709 /* Retrieve the current state of the toggle button @name and write it
710  * to the prefs (@name) */
711 /* Return value: the current state */
option_get_toggle_button(GladeXML * win_xml,const gchar * name)712 gboolean option_get_toggle_button (GladeXML *win_xml,
713 				   const gchar *name)
714 {
715     gboolean active = FALSE;
716     GtkWidget *button;
717 
718     g_return_val_if_fail (win_xml && name, active);
719 
720     button = gtkpod_xml_get_widget (win_xml, name);
721 
722     if (button)
723     {
724 	active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button));
725 	prefs_set_int (name, active);
726     }
727     return active;
728 }
729 
730 
731 
732 /*------------------------------------------------------------------*\
733  *                                                                  *
734  *  Functions to create string/filename from a template             *
735  *                                                                  *
736 \*------------------------------------------------------------------*/
737 
738 
739 
740 
741 /*
742 |  Copyright (C) 2004 Ero Carrera <ero at dkbza.org>
743 |
744 |  Placed under GPL in agreement with Ero Carrera. (JCS -- 12 March 2004)
745 */
746 
747 /**
748  * Check if supported char and return substitute.
749  */
check_char(gchar c)750 static gchar check_char(gchar c)
751 {
752     gint i;
753     static const gchar
754 	invalid[]={'"', '*', ':', '<', '>', '?', '\\', '|', '/', 0};
755     static const gchar
756 	replace[]={'_', '_', '-', '_', '_', '_', '-',  '-', '-', 0};
757     for(i=0; invalid[i]!=0; i++)
758 	if(c==invalid[i])  return replace[i];
759     return c;
760 }
761 
762 /**
763  * Process a path. It will substitute all the invalid characters.
764  * The changes are made within the original string. A pointer to the
765  * original string is returned.
766  */
fix_path(gchar * orig)767 static gchar *fix_path(gchar *orig)
768 {
769         if(orig)
770 	{
771 	    gchar *op = orig;
772 	    while (*op)
773 	    {
774 		*op = check_char(*op);
775 		++op;
776 	    }
777 	}
778 	return orig;
779 }
780 
781 /* End of code originally supplied by Ero Carrera */
782 
783 
784 /* Match a list of templates @p separated by ';' with the type of the
785    filename. E.g. '%s.mp3;%t.wav' will return '%s.mp3' if @track is an
786    mp3 file, or '%t.wav' if @track is a wav file. If no template can
787    be matched, an empty string is returned.
788 
789    String be freed after use.
790 */
select_template(Track * track,const gchar * p)791 static gchar *select_template (Track *track, const gchar *p)
792 {
793     gchar **templates, **tplp;
794     gchar *ext = NULL;
795     const gchar *tname;
796     gchar *result;
797     ExtraTrackData *etr;
798 
799     g_return_val_if_fail (track, strdup (""));
800     etr = track->userdata;
801     g_return_val_if_fail (etr, strdup (""));
802     if (etr->pc_path_locale && strlen(etr->pc_path_locale))
803 	tname = etr->pc_path_locale;
804     else
805 	tname = track->ipod_path;
806     if (!tname)
807     {   /* this should not happen... */
808 	gchar *buf = get_track_info (track, TRUE);
809 	gtkpod_warning (_("Could not process '%s' (no filename available)"),
810 			buf);
811 	g_free (buf);
812     }
813     ext = strrchr (tname, '.');        /* pointer to filename extension */
814 
815     templates = g_strsplit (p, ";", 0);
816     tplp = templates;
817     while (*tplp)
818     {
819 	if (strcmp (*tplp, "%o") == 0)
820 	{   /* this is only a valid extension if the original filename
821 	       is present */
822 	    if (etr->pc_path_locale && strlen(etr->pc_path_locale))  break;
823 	}
824 	else if (strrchr (*tplp, '.') == NULL)
825 	{   /* this template does not have an extension and therefore
826 	     * matches */
827 	    if (ext)
828 	    {   /* if we have an extension, add it */
829 		gchar *str = g_strdup_printf ("%s%s", *tplp, ext);
830 		g_free (*tplp);
831 		*tplp = str;
832 	    }
833 	    break;
834 	}
835 	else if (ext && (strlen (*tplp) >= strlen (ext)))
836 	{  /* this template is valid if the extensions match */
837 	    if (strcasecmp (&((*tplp)[strlen (*tplp) - strlen (ext)]),
838 			    ext) == 0)
839 		break;
840 	}
841 	++tplp;
842     }
843     result = g_strdup (*tplp);
844     g_strfreev (templates);
845     return result;
846 }
847 
848 
849 /* Return a string for @track built according to @template.
850 
851    @is_filename: if TRUE, remove potentially harmful characters.
852    @silent: don't print error messages (no gtk_*() calls -- thread
853    safe)
854 */
get_string_from_template(Track * track,const gchar * template,gboolean is_filename,gboolean silent)855 gchar *get_string_from_template (Track *track,
856 				 const gchar *template,
857 				 gboolean is_filename,
858 				 gboolean silent)
859 {
860     GString *result;
861     gchar *res_utf8;
862     const gchar *p;
863     gchar *basename = NULL;
864     gchar *basename_noext = NULL;
865     ExtraTrackData *etr;
866 
867     g_return_val_if_fail (track, NULL);
868     g_return_val_if_fail (template, NULL);
869     etr = track->userdata;
870     g_return_val_if_fail (etr, NULL);
871 
872     result = g_string_new ("");
873 
874     /* try to get the original filename */
875     if (etr->pc_path_utf8)
876 	basename = g_path_get_basename (etr->pc_path_utf8);
877     /* get original filename without extension */
878     if (basename)
879     {
880 	gchar *ptr;
881 	basename_noext = g_strdup (basename);
882 	ptr = strrchr (basename_noext, '.');
883 	if (ptr) *ptr = '\0';
884     }
885 
886     p=template;
887     while (*p != '\0') {
888 	if (*p == '%') {
889 	    const gchar* tmp = NULL;
890 	    gchar dummy[100];
891 	    Playlist *pl;
892 	    p++;
893 	    switch (*p) {
894 	    case 'o':
895 		if (basename)
896 		{
897 		    tmp = basename;
898 		}
899 		break;
900 	    case 'O':
901 		if (basename_noext)
902 		{
903 		    tmp = basename_noext;
904 		}
905 		break;
906 	    case 'p':
907 		pl = pm_get_selected_playlist ();
908 		if (pl)
909 		    tmp = pl->name;
910 		break;
911 	    case 'a':
912 		tmp = track_get_item (track, T_ARTIST);
913 		break;
914 	    case 'A':
915 		tmp = track_get_item (track, T_ALBUM);
916 		break;
917 	    case 't':
918 		tmp = track_get_item (track, T_TITLE);
919 		break;
920 	    case 'c':
921 		tmp = track_get_item (track, T_COMPOSER);
922 		break;
923 	    case 'g':
924 	    case 'G':
925 		tmp = track_get_item (track, T_GENRE);
926 		break;
927 	    case 'C':
928 		if (track->cds == 0)
929 		    sprintf (dummy, "%.2d", track->cd_nr);
930 		else if (track->cds < 10)
931 		    sprintf(dummy, "%.1d", track->cd_nr);
932 		else if (track->cds < 100)
933 		    sprintf (dummy, "%.2d", track->cd_nr);
934 		else if (track->cds < 1000)
935 		    sprintf (dummy, "%.3d", track->cd_nr);
936 		else
937 		    sprintf (dummy,"%.4d", track->cd_nr);
938 		tmp = dummy;
939 		break;
940 	    case 'T':
941 		if (track->tracks == 0)
942 		    sprintf (dummy, "%.2d", track->track_nr);
943 		else if (track->tracks < 10)
944 		    sprintf(dummy, "%.1d", track->track_nr);
945 		else if (track->tracks < 100)
946 		    sprintf (dummy, "%.2d", track->track_nr);
947 		else if (track->tracks < 1000)
948 		    sprintf (dummy, "%.3d", track->track_nr);
949 		else
950 		    sprintf (dummy,"%.4d", track->track_nr);
951 		tmp = dummy;
952 		break;
953 	    case 'Y':
954 		sprintf (dummy, "%4d", track->year);
955 		tmp = dummy;
956 		break;
957 	    case '%':
958 		tmp = "%";
959 		break;
960 	    default:
961 		if (!silent)
962 		{
963 		    gtkpod_warning (_("Unknown token '%%%c' in template '%s'"),
964 				    *p, template);
965 		}
966 		break;
967 	    }
968 	    if (tmp)
969 	    {
970 		gchar *tmpcp = g_strdup (tmp);
971 		if (is_filename)
972 		{
973 		    /* remove potentially illegal/harmful characters */
974 		    fix_path (tmpcp);
975 		    /* strip spaces to avoid problems with vfat */
976 		    g_strstrip (tmpcp);
977 		    /* append to current string */
978 		}
979 		result = g_string_append (result, tmpcp);
980 		tmp = NULL;
981 		g_free (tmpcp);
982 	    }
983 	}
984 	else
985 	    result = g_string_append_c (result, *p);
986 	p++;
987     }
988     /* get the utf8 version of the filename */
989     res_utf8 = g_string_free (result, FALSE);
990 
991     if (is_filename)
992     {   /* remove white space before the filename extension
993 	   (last '.') */
994 	gchar *ext = strrchr (res_utf8, '.');
995 	gchar *extst = NULL;
996 	if (ext)
997 	{
998 	    extst = g_strdup (ext);
999 	    *ext = '\0';
1000 	}
1001 	g_strstrip (res_utf8);
1002 	if (extst)
1003 	{
1004 	    /* The following strcat() is safe because g_strstrip()
1005 	       does not increase the original string size. Therefore
1006 	       the result of the strcat() call will not be longer than
1007 	       the original string. */
1008 	    strcat (res_utf8, extst);
1009 	    g_free (extst);
1010 	}
1011     }
1012 
1013     g_free (basename);
1014     g_free (basename_noext);
1015 
1016     return res_utf8;
1017 }
1018 
1019 
1020 
1021 /* Return a string for @track built according to @full_template.
1022    @full_template can contain several templates separated by ';',
1023    e.g. '%s.mp3;%t.wav'. The correct one is selected using
1024    select_template() defined above.
1025 
1026    If @is_filename is TRUE, potentially harmful characters are
1027    replaced in an attempt to create a valid filename.
1028 
1029    If @is_filename is FALSE, the extension (e.g. '.mp3' will be
1030    removed). */
get_string_from_full_template(Track * track,const gchar * full_template,gboolean is_filename)1031 gchar *get_string_from_full_template (Track *track,
1032 				      const gchar *full_template,
1033 				      gboolean is_filename)
1034 {
1035     gchar *res_utf8;
1036     gchar *template;
1037 
1038     g_return_val_if_fail (track, NULL);
1039     g_return_val_if_fail (full_template, NULL);
1040 
1041     template = select_template (track, full_template);
1042 
1043     if (!template)
1044     {
1045 	gchar *fn = get_file_name_from_source (track, SOURCE_PREFER_LOCAL);
1046 	gtkpod_warning (_("Template ('%s') does not match file type '%s'\n"), full_template, fn ? fn:"");
1047 	g_free (fn);
1048 	return NULL;
1049     }
1050 
1051     if (!is_filename)
1052     {   /* remove an extension, if present ('.???' or '.????'  at the
1053 	   end) */
1054 	gchar *pnt = strrchr (template, '.');
1055 	if (pnt)
1056 	{
1057 	    if (pnt == template+strlen(template)-3)
1058 		*pnt = 0;
1059 	    if (pnt == template+strlen(template)-4)
1060 		*pnt = 0;
1061 	}
1062     }
1063 
1064     res_utf8 = get_string_from_template (track, template, is_filename, FALSE);
1065 
1066     g_free (template);
1067 
1068     return res_utf8;
1069 }
1070 
1071 
1072 /**
1073  * which - run the shell command which, useful for querying default values
1074  * for executable,
1075  * @name - the executable we're trying to find the path for
1076  * Returns the path to the executable, NULL on not found
1077  */
which(const gchar * exe)1078 gchar *which (const gchar *exe)
1079 {
1080     FILE *fp = NULL;
1081     gchar *result = NULL;
1082     gchar buf[PATH_MAX];
1083     gchar *which_exec = NULL;
1084 
1085     g_return_val_if_fail (exe, NULL);
1086 
1087     memset(&buf[0], 0, PATH_MAX);
1088     which_exec = g_strdup_printf("which %s", exe);
1089     if((fp = popen(which_exec, "r")))
1090     {
1091         int read_bytes = 0;
1092         if((read_bytes = fread(buf, sizeof(gchar), PATH_MAX, fp)) > 0)
1093             result = g_strndup(buf, read_bytes-1);
1094         pclose(fp);
1095     }
1096     g_free(which_exec);
1097     return(result);
1098 }
1099 
1100 /**
1101  * Recursively make directories.
1102  *
1103  * @silent: don't print error messages via gtk (->thread safe)
1104  *
1105  * @return FALSE is this is not possible.
1106  */
mkdirhier(const gchar * dirname,gboolean silent)1107 gboolean mkdirhier(const gchar *dirname, gboolean silent)
1108 {
1109     gchar *dn, *p;
1110 
1111     g_return_val_if_fail (dirname && *dirname, FALSE);
1112 
1113     if (strncmp ("~/", dirname, 2) == 0)
1114 	 dn = g_build_filename (g_get_home_dir(), dirname+2, NULL);
1115     else dn = g_strdup (dirname);
1116 
1117     p = dn;
1118 
1119     do
1120     {
1121 	++p;
1122 	p = index (p, G_DIR_SEPARATOR);
1123 
1124 	if (p)   *p = '\0';
1125 
1126 	if (!g_file_test(dn, G_FILE_TEST_EXISTS))
1127 	{
1128 	    if (g_mkdir(dn, 0777) == -1)
1129 	    {
1130 		if (!silent)
1131 		{
1132 		    gtkpod_warning (_("Error creating %s: %s\n"),
1133 				    dn, g_strerror(errno));
1134 		}
1135 		g_free (dn);
1136 		return FALSE;
1137 	    }
1138 	}
1139 	if (p)   *p = G_DIR_SEPARATOR;
1140     } while (p);
1141 
1142     g_free (dn);
1143     return TRUE;
1144 }
1145 
1146 /**
1147  * Recursively make directories in the given filename.
1148  * @return FALSE is this is not possible.
1149  */
mkdirhierfile(const gchar * filename)1150 gboolean mkdirhierfile(const gchar *filename)
1151 {
1152     gboolean result;
1153     gchar *dirname = g_path_get_dirname (filename);
1154     result = mkdirhier (dirname, FALSE);
1155     g_free (dirname);
1156     return result;
1157 }
1158 
1159 
1160 /**
1161  * Convert "~/" to "/home/.../"
1162  *
1163  * g_free() return value when no longer needed.
1164  */
convert_filename(const gchar * filename)1165 gchar *convert_filename (const gchar *filename)
1166 {
1167   if (filename)
1168   {
1169     if (strncmp ("~/", filename, 2) == 0)
1170       return g_build_filename (g_get_home_dir(), filename+2, NULL);
1171     else
1172       return g_strdup (filename);
1173   }
1174 
1175   return NULL;
1176 }
1177 
1178 
1179 /**
1180  * get_size_of_directory
1181  *
1182  * Determine the total size in bytes of all files in @dir including
1183  * subdirectories. This function ignores errors in the sense that if a
1184  * directory or file cannot be accessed, a size of 0 is assumed.
1185  */
get_size_of_directory(const gchar * dir)1186 gint64 get_size_of_directory (const gchar *dir)
1187 {
1188     GDir *gdir;
1189     const gchar *fname;
1190     gint64 tsize = 0;
1191 
1192     g_return_val_if_fail (dir, 0);
1193 
1194     gdir = g_dir_open (dir, 0, NULL);
1195 
1196     /* Check for error */
1197     if (!gdir)
1198 	return 0;
1199 
1200     while ((fname = g_dir_read_name (gdir)))
1201     {
1202 	gchar *fullname = g_build_filename (dir, fname, NULL);
1203 	if (g_file_test (fullname, G_FILE_TEST_IS_DIR))
1204 	{
1205 	    tsize += get_size_of_directory (fullname);
1206 	}
1207 	else if (g_file_test (fullname, G_FILE_TEST_IS_REGULAR))
1208 	{
1209 	    struct stat statbuf;
1210 	    if (g_stat (fullname, &statbuf) == 0)
1211 	    {   /* OK, add size */
1212 		tsize += statbuf.st_size;
1213 	    }
1214 	}
1215 	g_free (fullname);
1216     }
1217 
1218     g_dir_close (gdir);
1219 
1220     return tsize;
1221 }
1222 
1223 
1224 
1225 /**
1226  * Wrapper for glade_xml_new() for cygwin compatibility issues
1227  *
1228  **/
gtkpod_xml_new(const gchar * xml_file,const gchar * name)1229 GladeXML *gtkpod_xml_new (const gchar *xml_file, const gchar *name)
1230 {
1231     GladeXML *xml;
1232 
1233 #ifdef ENABLE_NLS
1234 	xml = glade_xml_new (xml_file, name, GETTEXT_PACKAGE);
1235 #else
1236 	xml = glade_xml_new (xml_file, name, NULL);
1237 #endif
1238 
1239     if (!xml)
1240 		fprintf (stderr, "*** Programming error: Cannot create glade XML: '%s'\n",
1241 				 name);
1242 
1243     return xml;
1244 }
1245 
1246 
1247 /**
1248  * Wrapper for gtkpod_xml_get_widget() giving out a warning if widget
1249  * could not be found.
1250  *
1251  **/
gtkpod_xml_get_widget(GladeXML * xml,const gchar * name)1252 GtkWidget *gtkpod_xml_get_widget (GladeXML *xml, const gchar *name)
1253 {
1254     GtkWidget *w = glade_xml_get_widget (xml, name);
1255 
1256     if (!w)
1257 		fprintf (stderr, "*** Programming error: Widget not found: '%s'\n",
1258 				 name);
1259 
1260     return w;
1261 }
1262 
1263 /* ------------------------------------------------------------
1264  *
1265  *        Helper functions for pref keys
1266  *
1267  * ------------------------------------------------------------ */
1268 
1269 
1270 /**
1271  * Helper function to construct prefs key for itdb,
1272  * e.g. itdb_1_mountpoint...
1273  * gfree() after use
1274  *
1275  **/
get_itdb_prefs_key(gint index,const gchar * subkey)1276 gchar *get_itdb_prefs_key (gint index, const gchar *subkey)
1277 {
1278     g_return_val_if_fail (subkey, NULL);
1279 
1280     return g_strdup_printf ("itdb_%d_%s", index, subkey);
1281 }
1282 
1283 
1284 /**
1285  * Helper function to construct prefs key for playlists,
1286  * e.g. itdb_1_playlist_xxxxxxxxxxx_syncmode...
1287  *
1288  * gfree() after use
1289  **/
get_playlist_prefs_key(gint index,Playlist * pl,const gchar * subkey)1290 gchar *get_playlist_prefs_key (gint index,
1291 			       Playlist *pl, const gchar *subkey)
1292 {
1293     g_return_val_if_fail (pl, NULL);
1294     g_return_val_if_fail (subkey, NULL);
1295 
1296     return g_strdup_printf ("itdb_%d_playlist_%llu_%s",
1297 			    index, (unsigned long long)pl->id, subkey);
1298 }
1299 
1300 
1301 /**
1302  * Helper function to retrieve the index number of @itdb needed to
1303  * construct any keys (see above)
1304  **/
get_itdb_index(iTunesDB * itdb)1305 gint get_itdb_index (iTunesDB *itdb)
1306 {
1307 	struct itdbs_head *itdbs_head;
1308 
1309 	itdbs_head = gp_get_itdbs_head (gtkpod_window);
1310 	g_return_val_if_fail (itdbs_head, 0);
1311 
1312 	return g_list_index (itdbs_head->itdbs, itdb);
1313 }
1314 
1315 
1316 /**
1317  * Helper function to retrieve a string prefs entry for @itdb.
1318  *
1319  * gfree() after use
1320  **/
get_itdb_prefs_string(iTunesDB * itdb,const gchar * subkey)1321 gchar *get_itdb_prefs_string (iTunesDB *itdb, const gchar *subkey)
1322 {
1323     gchar *key, *value;
1324 
1325     g_return_val_if_fail (itdb, NULL);
1326     g_return_val_if_fail (subkey, NULL);
1327 
1328     key = get_itdb_prefs_key (get_itdb_index (itdb), subkey);
1329     value = prefs_get_string (key);
1330     g_free (key);
1331 
1332     return value;
1333 }
1334 
1335 
1336 /**
1337  * Helper function to retrieve a string prefs entry for @playlist.
1338  *
1339  **/
get_playlist_prefs_string(Playlist * playlist,const gchar * subkey)1340 gchar *get_playlist_prefs_string (Playlist *playlist, const gchar *subkey)
1341 {
1342     gchar *key, *value;
1343 
1344     g_return_val_if_fail (playlist, 0);
1345     g_return_val_if_fail (subkey, 0);
1346 
1347     key = get_playlist_prefs_key (get_itdb_index (playlist->itdb),
1348 				  playlist, subkey);
1349     value = prefs_get_string (key);
1350     g_free (key);
1351 
1352     return value;
1353 }
1354 
1355 /**
1356  * Helper function to retrieve an int prefs entry for @itdb.
1357  *
1358  **/
get_itdb_prefs_int(iTunesDB * itdb,const gchar * subkey)1359 gint get_itdb_prefs_int (iTunesDB *itdb, const gchar *subkey)
1360 {
1361     gchar *key;
1362     gint value;
1363 
1364     g_return_val_if_fail (itdb, 0);
1365     g_return_val_if_fail (subkey, 0);
1366 
1367     key = get_itdb_prefs_key (get_itdb_index (itdb), subkey);
1368     value = prefs_get_int (key);
1369     g_free (key);
1370 
1371     return value;
1372 }
1373 
1374 /**
1375  * Helper function to retrieve an int prefs entry for @playlist.
1376  *
1377  **/
get_playlist_prefs_int(Playlist * playlist,const gchar * subkey)1378 gint get_playlist_prefs_int (Playlist *playlist, const gchar *subkey)
1379 {
1380     gchar *key;
1381     gint value;
1382 
1383     g_return_val_if_fail (playlist, 0);
1384     g_return_val_if_fail (subkey, 0);
1385 
1386     key = get_playlist_prefs_key (get_itdb_index (playlist->itdb),
1387 				  playlist, subkey);
1388     value = prefs_get_int (key);
1389     g_free (key);
1390 
1391     return value;
1392 }
1393 
1394 /**
1395  * Helper function to retrieve a string prefs entry for @itdb.
1396  *
1397  * Returns TRUE if the key was actually set in the prefs.
1398  *
1399  * gfree() after use
1400  **/
get_itdb_prefs_string_value(iTunesDB * itdb,const gchar * subkey,gchar ** value)1401 gboolean get_itdb_prefs_string_value (iTunesDB *itdb, const gchar *subkey,
1402 				      gchar **value)
1403 {
1404     gchar *key;
1405     gboolean result;
1406 
1407     g_return_val_if_fail (itdb, FALSE);
1408     g_return_val_if_fail (subkey, FALSE);
1409 
1410     key = get_itdb_prefs_key (get_itdb_index (itdb), subkey);
1411     result = prefs_get_string_value (key, value);
1412     g_free (key);
1413 
1414     return result;
1415 }
1416 
1417 
1418 /**
1419  * Helper function to retrieve an in prefs entry for @itdb.
1420  *
1421  **/
get_itdb_prefs_int_value(iTunesDB * itdb,const gchar * subkey,gint * value)1422 gboolean get_itdb_prefs_int_value (iTunesDB *itdb, const gchar *subkey,
1423 				   gint *value)
1424 {
1425     gchar *key;
1426     gboolean result;
1427 
1428     g_return_val_if_fail (itdb, FALSE);
1429     g_return_val_if_fail (subkey, FALSE);
1430 
1431     key = get_itdb_prefs_key (get_itdb_index (itdb), subkey);
1432     result = prefs_get_int_value (key, value);
1433     g_free (key);
1434 
1435     return result;
1436 }
1437 
1438 
1439 /**
1440  * Helper function to set a string prefs entry for @itdb.
1441  *
1442  * gfree() after use
1443  **/
set_itdb_prefs_string(iTunesDB * itdb,const gchar * subkey,const gchar * value)1444 void set_itdb_prefs_string (iTunesDB *itdb,
1445 			    const gchar *subkey, const gchar *value)
1446 {
1447     gchar *key;
1448 
1449     g_return_if_fail (itdb);
1450     g_return_if_fail (subkey);
1451 
1452     key = get_itdb_prefs_key (get_itdb_index (itdb), subkey);
1453     prefs_set_string (key, value);
1454     g_free (key);
1455 }
1456 
1457 
1458 /**
1459  * Helper function to set a string prefs entry for @itdb.
1460  *
1461  * gfree() after use
1462  **/
set_itdb_index_prefs_string(gint index,const gchar * subkey,const gchar * value)1463 void set_itdb_index_prefs_string (gint index,
1464 				  const gchar *subkey, const gchar *value)
1465 {
1466     gchar *key;
1467 
1468     g_return_if_fail (subkey);
1469 
1470     key = get_itdb_prefs_key (index, subkey);
1471     prefs_set_string (key, value);
1472     g_free (key);
1473 }
1474 
1475 
1476 /**
1477  * Helper function to set an in prefs entry for @itdb.
1478  *
1479  **/
set_itdb_prefs_int(iTunesDB * itdb,const gchar * subkey,gint value)1480 void set_itdb_prefs_int (iTunesDB *itdb, const gchar *subkey, gint value)
1481 {
1482     gchar *key;
1483 
1484     g_return_if_fail (itdb);
1485     g_return_if_fail (subkey);
1486 
1487     key = get_itdb_prefs_key (get_itdb_index (itdb), subkey);
1488     prefs_set_int (key, value);
1489     g_free (key);
1490 }
1491 
1492 
1493 /**
1494  * Helper function to set an in prefs entry for @itdb.
1495  *
1496  **/
set_itdb_index_prefs_int(gint index,const gchar * subkey,gint value)1497 void set_itdb_index_prefs_int (gint index,
1498 			       const gchar *subkey, gint value)
1499 {
1500     gchar *key;
1501 
1502     g_return_if_fail (subkey);
1503 
1504     key = get_itdb_prefs_key (index, subkey);
1505     prefs_set_int (key, value);
1506     g_free (key);
1507 }
1508 
1509 /**
1510  * Helper function to remove all prefs strings for itdb @index and
1511  * move all subsequent itdb strings forward. You can call this even
1512  * after your have removed the itdb from itdbs_head.
1513  *
1514  **/
remove_itdb_index_prefs(gint index)1515 static void remove_itdb_index_prefs (gint index)
1516 {
1517     struct itdbs_head *itdbs_head;
1518     gchar *subkey;
1519     gint i, n;
1520 
1521     itdbs_head = gp_get_itdbs_head (gtkpod_window);
1522     g_return_if_fail (itdbs_head);
1523 
1524     n = g_list_length (itdbs_head->itdbs);
1525     subkey = get_itdb_prefs_key (index, "");
1526     prefs_flush_subkey (subkey);
1527     g_free (subkey);
1528 
1529     for (i=index; i<=n; ++i)
1530     {
1531 	gchar *from_key = get_itdb_prefs_key (i+1, "");
1532 	gchar *to_key = get_itdb_prefs_key (i, "");
1533 	prefs_rename_subkey (from_key, to_key);
1534 	g_free (from_key);
1535 	g_free (to_key);
1536     }
1537 }
1538 
1539 
1540 /**
1541  * Helper function to remove all prefs strings for @itdb and move all
1542  * subsequent itdb strings forward. Call this before removing the itdb
1543  * from itdbs_head.
1544  *
1545  **/
remove_itdb_prefs(iTunesDB * itdb)1546 void remove_itdb_prefs (iTunesDB *itdb)
1547 {
1548     g_return_if_fail (itdb);
1549 
1550     remove_itdb_index_prefs (get_itdb_index (itdb));
1551 }
1552 
1553 /* Save all itdb_<index>_* keys to the iPod
1554  * (<ControlDir>/gtkpod.prefs).
1555  *
1556  * Return value: TRUE on succes, FALSE on error
1557  */
save_ipod_index_prefs(gint index,const gchar * mountpoint)1558 gboolean save_ipod_index_prefs (gint index, const gchar *mountpoint)
1559 {
1560     TempPrefs *temp_prefs;
1561     gboolean result = FALSE;
1562     gchar *subkey, *dir;
1563 
1564     g_return_val_if_fail (mountpoint, FALSE);
1565 
1566     /* isolate all 'itdb_<index>_*' keys */
1567     subkey = get_itdb_prefs_key (index, "");
1568     temp_prefs = prefs_create_subset (subkey);
1569 
1570     /* rename to 'itdb_*' */
1571     temp_prefs_rename_subkey (temp_prefs, subkey, "itdb_");
1572 
1573     /* remove some keys */
1574     temp_prefs_remove_key (temp_prefs, "itdb_mountpoint");
1575     temp_prefs_remove_key (temp_prefs, "itdb_name");
1576     temp_prefs_remove_key (temp_prefs, "itdb_type");
1577 
1578     /* build filename path */
1579     dir = itdb_get_itunes_dir (mountpoint);
1580     if (dir)
1581     {
1582 	GError *error = NULL;
1583 	gchar *path = g_build_filename (dir, "gtkpod.prefs", NULL);
1584 	result = temp_prefs_save (temp_prefs, path, &error);
1585 	if (result == FALSE)
1586 	{
1587 	    gtkpod_warning (_("Writing preferences file '%s' failed (%s).\n\n"),
1588 			    path,
1589 			    error? error->message:_("unspecified error"));
1590 	    g_error_free (error);
1591 	}
1592 	g_free (path);
1593 	g_free (dir);
1594     }
1595     else
1596     {
1597 	gtkpod_warning (_("Writing preferences to the iPod (%s) failed: could not get path to Control Directory.\n\n"),
1598 			mountpoint);
1599     }
1600 
1601     temp_prefs_destroy (temp_prefs);
1602     g_free (subkey);
1603 
1604     return result;
1605 }
1606 
1607 
1608 
1609 /* Save all itdb_<index>_* keys to the iPod
1610  * (<ControlDir>/gtkpod.prefs).
1611  *
1612  * Return value: TRUE on succes, FALSE on error
1613  */
save_ipod_prefs(iTunesDB * itdb,const gchar * mountpoint)1614 gboolean save_ipod_prefs (iTunesDB *itdb, const gchar *mountpoint)
1615 {
1616     g_return_val_if_fail (itdb && mountpoint, FALSE);
1617     return save_ipod_index_prefs (get_itdb_index (itdb), mountpoint);
1618 }
1619 
1620 
1621 /* Load preferences file from the iPod and merge them into the general
1622  * prefs system.
1623  */
load_ipod_index_prefs(gint index,const gchar * mountpoint)1624 static void load_ipod_index_prefs (gint index, const gchar *mountpoint)
1625 {
1626     gchar *dir;
1627 
1628     g_return_if_fail (mountpoint);
1629 
1630     /* build filename path */
1631     dir = itdb_get_itunes_dir (mountpoint);
1632     if (dir)
1633     {
1634 	TempPrefs *temp_prefs;
1635 	GError *error = NULL;
1636 	gchar *path = g_build_filename (dir, "gtkpod.prefs", NULL);
1637 	temp_prefs = temp_prefs_load (path, &error);
1638 	g_free (path);
1639 	if (temp_prefs)
1640 	{
1641 	    gchar *subkey;
1642 	    subkey = get_itdb_prefs_key (index, "");
1643 	    /* rename 'itdb_*' to 'itdb_<index>_*' */
1644 	    temp_prefs_rename_subkey (temp_prefs, "itdb_", subkey);
1645 	    g_free (subkey);
1646 	    /* merge with real prefs */
1647 	    temp_prefs_apply (temp_prefs);
1648 	    /* destroy temp prefs */
1649 	    temp_prefs_destroy (temp_prefs);
1650 	}
1651 	else
1652 	{
1653 	    /* we ignore errors -- no need to be concerned about them */
1654 	    g_error_free (error);
1655 	}
1656 	g_free (dir);
1657     }
1658 }
1659 
1660 
1661 
1662 /* Load preferences file from the iPod and merge them into the general
1663  * prefs system.
1664  */
load_ipod_prefs(iTunesDB * itdb,const gchar * mountpoint)1665 void load_ipod_prefs (iTunesDB *itdb, const gchar *mountpoint)
1666 {
1667     g_return_if_fail (mountpoint);
1668     load_ipod_index_prefs (get_itdb_index (itdb), mountpoint);
1669 }
1670 
1671 
1672 
1673 
1674 /* retrieve offline mode from itdb (convenience function) */
get_offline(iTunesDB * itdb)1675 gboolean get_offline (iTunesDB *itdb)
1676 {
1677     ExtraiTunesDBData *eitdb;
1678 
1679     g_return_val_if_fail (itdb, FALSE);
1680     eitdb = itdb->userdata;
1681     g_return_val_if_fail (eitdb, FALSE);
1682 
1683     return eitdb->offline;
1684 }
1685 
1686 /* ----------------------------------------------------------------
1687  *
1688  * Main program init and shutdown
1689  *
1690  * ---------------------------------------------------------------- */
1691 
1692 /**
1693  * gtkpod_init
1694  *
1695  * initialize prefs and other services as well as display
1696  */
gtkpod_init(int argc,char * argv[])1697 void gtkpod_init (int argc, char *argv[])
1698 {
1699     gchar *progname;
1700 
1701     /* initialize xml_file: if gtkpod is called in the build directory
1702        (".../src/gtkpod") use the local gtkpod.glade (in the data
1703        directory), otherwise use
1704        "PACKAGE_DATA_DIR/PACKAGE/data/gtkpod.glade" */
1705 
1706     progname = g_find_program_in_path (argv[0]);
1707     if (progname)
1708     {
1709 	static const gchar *SEPsrcSEPgtkpod = G_DIR_SEPARATOR_S "src" G_DIR_SEPARATOR_S "gtkpod";
1710 
1711     if (!g_path_is_absolute (progname))
1712     {
1713         gchar *cur_dir = g_get_current_dir ();
1714         gchar *prog_absolute;
1715 
1716         if (g_str_has_prefix (progname, "." G_DIR_SEPARATOR_S))
1717             prog_absolute = g_build_filename (cur_dir,progname+2,NULL);
1718         else
1719             prog_absolute = g_build_filename (cur_dir,progname,NULL);
1720 
1721         g_free (progname);
1722         g_free (cur_dir);
1723         progname = prog_absolute;
1724     }
1725 
1726     if (g_str_has_suffix (progname, SEPsrcSEPgtkpod))
1727     {
1728         gchar *suffix = g_strrstr (progname, SEPsrcSEPgtkpod);
1729 
1730         if (suffix)
1731         {
1732             *suffix = 0;
1733             xml_file = g_build_filename (progname, "data", "gtkpod.glade", NULL);
1734         }
1735     }
1736 
1737     if (!xml_file)
1738     {
1739         gchar *prog_path = g_path_get_dirname (progname);
1740         gchar *cmake_file = g_build_filename (prog_path, "CMakeCache.txt", NULL);
1741 
1742         if (g_file_test (cmake_file, G_FILE_TEST_EXISTS))
1743         {
1744             gchar *source_root = g_path_get_dirname (prog_path);
1745             xml_file = g_build_filename(source_root, "data", "gtkpod.glade", NULL);
1746             g_free (source_root);
1747         }
1748 
1749         g_free (cmake_file);
1750         g_free (prog_path);
1751     }
1752 
1753 	g_free (progname);
1754 
1755     if (xml_file && !g_file_test (xml_file, G_FILE_TEST_EXISTS))
1756 	{
1757 	    g_free (xml_file);
1758 	    xml_file = NULL;
1759 	}
1760     }
1761     if (!xml_file)
1762 	xml_file = g_build_filename (PACKAGE_DATA_DIR, PACKAGE, "data", "gtkpod.glade", NULL);
1763     else
1764     {
1765 	printf ("Using local gtkpod.glade file since program was started from source directory:\n%s\n", xml_file);
1766     }
1767 
1768     /* Attempt to load libmp4v2 */
1769     mp4_init();
1770 
1771     /* Initialisation of libxml */
1772     LIBXML_TEST_VERSION;
1773 
1774     main_window_xml = gtkpod_xml_new (xml_file, "gtkpod");
1775 
1776     glade_xml_signal_autoconnect (main_window_xml);
1777 
1778     gtkpod_window = gtkpod_xml_get_widget (main_window_xml, "gtkpod");
1779 
1780     prefs_init (argc, argv);
1781 
1782     coverart_init (argv[0]);
1783     stockid_init (argv[0]);
1784 
1785     file_convert_init ();
1786 
1787     display_create ();
1788 
1789     gtk_widget_show (gtkpod_window);
1790 
1791     init_data (gtkpod_window);   /* setup base data, importing all local
1792 				  * repositories */
1793 
1794     /* stuff to be done before starting gtkpod */
1795     call_script ("gtkpod.in", NULL);
1796 
1797     autodetection_init ();
1798 
1799     server_setup ();   /* start server to accept playcount updates */
1800 }
1801 
1802 
1803 
1804 /**
1805  * gtkpod_shutdown
1806  *
1807  * free memory, shutdown services and call gtk_main_quit ()
1808  */
gtkpod_shutdown()1809 void gtkpod_shutdown ()
1810 {
1811     /* stop accepting requests for playcount updates */
1812     server_shutdown ();
1813 
1814 		/* Change the windows back to track view to ensure the
1815 		 * sorttab state is saved correctly/
1816 		 */
1817 		gphoto_change_to_photo_window (FALSE);
1818 
1819     /* Sort column order needs to be stored */
1820     tm_store_col_order();
1821 
1822     /* Update default sizes */
1823     display_update_default_sizes();
1824 
1825     /* shut down conversion infrastructure */
1826     file_convert_shutdown ();
1827 
1828     /* Save prefs */
1829     prefs_save ();
1830 
1831 /* FIXME: release memory in a clean way */
1832 #if 0
1833     remove_all_playlists ();  /* first remove playlists, then tracks!
1834 			       * (otherwise non-existing *tracks may
1835 			       * be accessed) */
1836     remove_all_tracks ();
1837 #endif
1838     display_cleanup ();
1839 
1840     prefs_shutdown ();
1841 
1842     xmlCleanupParser();
1843     xmlMemoryDump();
1844 
1845     mp4_close();
1846 
1847     call_script ("gtkpod.out", NULL);
1848     gtk_main_quit ();
1849 }
1850