1 /*
2  * Copyright (C) 2000-2019 the xine project
3  *
4  * This file is part of xine, a unix video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <dirent.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <errno.h>
34 
35 #include <X11/keysym.h>
36 #include "common.h"
37 
38 #define WINDOW_WIDTH            525
39 #define WINDOW_HEIGHT           270
40 
41 static struct {
42   apply_callback_t              callback;
43   void                         *user_data;
44 
45   xitk_window_t                *xwin;
46 
47   xitk_widget_list_t           *widget_list;
48 
49   xitk_widget_t                *mrl;
50   xitk_widget_t                *ident;
51   xitk_widget_t                *sub;
52   xitk_widget_t                *start;
53   xitk_widget_t                *end;
54   xitk_widget_t                *av_offset;
55   xitk_widget_t                *spu_offset;
56 
57   mediamark_t                 **mmk;
58 
59   int                           running;
60   int                           visible;
61   xitk_register_key_t           widget_key;
62 } mmkeditor;
63 
64 
65 typedef struct {
66   char                         *data;
67   char                        **lines;
68   int                           numl;
69 
70   int                           entries;
71   char                         *type;
72 } playlist_t;
73 
74 typedef mediamark_t **(*playlist_guess_func_t)(playlist_t *, const char *);
75 typedef mediamark_t **(*playlist_xml_guess_func_t)(playlist_t *, const char *, char *xml_content, xml_node_t *);
76 
_download_file(const char * filename,int * size)77 static char *_download_file(const char *filename, int *size) {
78   download_t  *download;
79   char        *buf = NULL;
80 
81   if((!filename) || (!strlen(filename))) {
82     fprintf(stderr, "%s(): Empty or NULL filename.\n", __XINE_FUNCTION__);
83     return NULL;
84   }
85 
86   download = (download_t *) calloc(1, sizeof(download_t));
87   download->buf    = NULL;
88   download->error  = NULL;
89   download->size   = 0;
90   download->status = 0;
91 
92   if((network_download(filename, download))) {
93     *size = download->size;
94     buf = (char *) malloc(*size + 1);
95     if (buf) {
96       memcpy(buf, download->buf, *size);
97       buf[*size] = 0;
98     }
99   }
100   else
101     xine_error("Unable to download '%s': %s", filename, download->error);
102 
103   SAFE_FREE(download->buf);
104   SAFE_FREE(download->error);
105   free(download);
106 
107   return buf;
108 }
109 
110 #if 0
111 static int _file_exist(char *filename) {
112   struct stat  st;
113 
114   if(filename && (stat(filename, &st) == 0))
115     return 1;
116 
117   return 0;
118 }
119 #endif
120 
mrl_look_like_playlist(const char * mrl)121 int mrl_look_like_playlist (const char *mrl) {
122   /* TJ. I dont know whether someone really needs to treat
123    * "foo/bar.m3under/the/table" as an m3u playlist.
124    * Lets keep this behaviour for now, but make sure that
125    * hls (.m3u8) goes to xine-lib verbatim. */
126   const char *extension = strrchr (mrl, '.');
127   if (extension) {
128     /* All known playlist ending */
129     if ((!strncasecmp (extension, ".asx", 4))  ||
130         (!strncasecmp (extension, ".smi", 4))  ||
131       /*(!strncasecmp (extension, ".smil", 5)) || caught by ".smi" */
132         (!strncasecmp (extension, ".pls", 4))  ||
133         (!strncasecmp (extension, ".sfv", 4))  ||
134         (!strncasecmp (extension, ".xml", 4))  ||
135         (!strncasecmp (extension, ".tox", 4))  ||
136         (!strncasecmp (extension, ".fxd", 4)))
137       return 1;
138     if ((!strncasecmp (extension, ".m3u", 4)) && (extension[4] != '8'))
139       return 1;
140   }
141   return 0;
142 }
143 
_read_file(const char * filename,int * size)144 static char *_read_file(const char *filename, int *size) {
145   struct stat  st;
146   char        *buf = NULL;
147   int          fd, bytes_read;
148 
149   if((!filename) || (!strlen(filename))) {
150     fprintf(stderr, "%s(): Empty or NULL filename.\n", __XINE_FUNCTION__);
151     return NULL;
152   }
153 
154   if (mrl_look_like_playlist (filename) && is_downloadable ((char *)filename))
155     return _download_file(filename, size);
156 
157   if(stat(filename, &st) < 0) {
158     fprintf(stderr, "%s(): Unable to stat() '%s' file: %s.\n",
159 	    __XINE_FUNCTION__, filename, strerror(errno));
160     return NULL;
161   }
162 
163   if((*size = st.st_size) == 0) {
164     fprintf(stderr, "%s(): File '%s' is empty.\n", __XINE_FUNCTION__, filename);
165     return NULL;
166   }
167 
168   if((fd = xine_open_cloexec(filename, O_RDONLY)) == -1) {
169     fprintf(stderr, "%s(): open(%s) failed: %s.\n", __XINE_FUNCTION__, filename, strerror(errno));
170     return NULL;
171   }
172 
173   if((buf = (char *) malloc(*size + 1)) == NULL) {
174     fprintf(stderr, "%s(): malloc() failed.\n", __XINE_FUNCTION__);
175     close(fd);
176     return NULL;
177   }
178 
179   if((bytes_read = read(fd, buf, *size)) != *size) {
180     fprintf(stderr, "%s(): read() return wrong size (%d / %d): %s.\n",
181 	    __XINE_FUNCTION__, bytes_read, *size, strerror(errno));
182     *size = bytes_read;
183   }
184 
185   close(fd);
186 
187   buf[*size] = '\0';
188 
189   return buf;
190 }
191 
mediamark_have_alternates(mediamark_t * mmk)192 int mediamark_have_alternates(mediamark_t *mmk) {
193   if(mmk && mmk->alternates)
194     return 1;
195 
196   return 0;
197 }
mediamark_free_alternates(mediamark_t * mmk)198 void mediamark_free_alternates(mediamark_t *mmk) {
199   if(mmk && mediamark_have_alternates(mmk)) {
200     alternate_t *alt = mmk->alternates;
201 
202     while(alt) {
203       alternate_t *c_alt = alt;
204 
205       free(alt->mrl);
206 
207       c_alt = alt;
208       alt   = alt->next;
209       free(c_alt);
210     }
211     mmk->alternates = NULL;
212     mmk->cur_alt    = NULL;
213   }
214 }
mediamark_get_first_alternate_mrl(mediamark_t * mmk)215 char *mediamark_get_first_alternate_mrl(mediamark_t *mmk) {
216   if(mmk && mmk->alternates) {
217     mmk->cur_alt = mmk->alternates;
218     return mmk->cur_alt->mrl;
219   }
220   return NULL;
221 }
mediamark_get_next_alternate_mrl(mediamark_t * mmk)222 char *mediamark_get_next_alternate_mrl(mediamark_t *mmk) {
223   if(mmk && mmk->cur_alt) {
224     if(mmk->cur_alt->next) {
225       mmk->cur_alt = mmk->cur_alt->next;
226       return mmk->cur_alt->mrl;
227     }
228   }
229   return NULL;
230 }
mediamark_get_current_alternate_mrl(mediamark_t * mmk)231 char *mediamark_get_current_alternate_mrl(mediamark_t *mmk) {
232   if(mmk && mmk->cur_alt)
233     return mmk->cur_alt->mrl;
234 
235   return NULL;
236 }
mediamark_append_alternate_mrl(mediamark_t * mmk,const char * mrl)237 void mediamark_append_alternate_mrl(mediamark_t *mmk, const char *mrl) {
238   if(mmk && mrl) {
239     alternate_t *alt;
240 
241     alt       = (alternate_t *) calloc(1, sizeof(alternate_t));
242     alt->mrl  = strdup(mrl);
243     alt->next = NULL;
244 
245     if(mmk->alternates) {
246       alternate_t *p_alt = mmk->alternates;
247 
248       while(p_alt->next)
249 	p_alt = p_alt->next;
250       p_alt->next = alt;
251     }
252     else
253       mmk->alternates = alt;
254 
255   }
256 }
mediamark_duplicate_alternates(mediamark_t * s_mmk,mediamark_t * d_mmk)257 void mediamark_duplicate_alternates(mediamark_t *s_mmk, mediamark_t *d_mmk) {
258   if(s_mmk && s_mmk->alternates && d_mmk) {
259     alternate_t *alt;
260 
261     if((alt = s_mmk->alternates)) {
262       alternate_t *c_alt, *p_alt = NULL, *t_alt = NULL;
263 
264       while(alt) {
265 
266 	c_alt       = (alternate_t *) calloc(1, sizeof(alternate_t));
267 	c_alt->mrl  = strdup(alt->mrl);
268 	c_alt->next = NULL;
269 
270 	if(!p_alt)
271 	  t_alt = p_alt = c_alt;
272 	else {
273 	  p_alt->next = c_alt;
274 	  p_alt       = c_alt;
275 	}
276 
277 	alt = alt->next;
278       }
279 
280       d_mmk->alternates = t_alt;
281       d_mmk->cur_alt    = NULL;
282 
283     }
284   }
285 }
mediamark_got_alternate(mediamark_t * mmk)286 int mediamark_got_alternate(mediamark_t *mmk) {
287   if(mmk && mmk->got_alternate)
288     return 1;
289   return 0;
290 }
mediamark_set_got_alternate(mediamark_t * mmk)291 void mediamark_set_got_alternate(mediamark_t *mmk) {
292   if(mmk)
293     mmk->got_alternate = 1;
294 }
mediamark_unset_got_alternate(mediamark_t * mmk)295 void mediamark_unset_got_alternate(mediamark_t *mmk) {
296   if(mmk)
297     mmk->got_alternate = 0;
298 }
mediamark_store_mmk(mediamark_t ** mmk,const char * mrl,const char * ident,const char * sub,int start,int end,int av_offset,int spu_offset)299 int mediamark_store_mmk(mediamark_t **mmk,
300 			const char *mrl, const char *ident, const char *sub,
301 			int start, int end, int av_offset, int spu_offset) {
302 
303   if(mmk && mrl) {
304 
305     (*mmk) = (mediamark_t *) calloc(1, sizeof(mediamark_t));
306     (*mmk)->mrl           = strdup(mrl);
307     (*mmk)->ident         = strdup((ident != NULL) ? ident : mrl);
308     (*mmk)->sub           = (sub != NULL) ? strdup(sub) : NULL;
309     (*mmk)->start         = start;
310     (*mmk)->end           = end;
311     (*mmk)->av_offset     = av_offset;
312     (*mmk)->spu_offset    = spu_offset;
313     (*mmk)->played        = 0;
314     (*mmk)->got_alternate = 0;
315     (*mmk)->cur_alt       = NULL;
316     (*mmk)->alternates    = NULL;
317 
318     return 1;
319   }
320 
321   return 0;
322 }
323 
mediamark_clone_mmk(mediamark_t * mmk)324 mediamark_t *mediamark_clone_mmk(mediamark_t *mmk) {
325   mediamark_t *cmmk = NULL;
326 
327   if(mmk && mmk->mrl) {
328     cmmk = (mediamark_t *) calloc(1, sizeof(mediamark_t));
329     cmmk->mrl           = strdup(mmk->mrl);
330     cmmk->ident         = (mmk->ident) ? strdup(mmk->ident) : NULL;
331     cmmk->sub           = (mmk->sub) ? strdup(mmk->sub) : NULL;
332     cmmk->start         = mmk->start;
333     cmmk->end           = mmk->end;
334     cmmk->av_offset     = mmk->av_offset;
335     cmmk->spu_offset    = mmk->spu_offset;
336     cmmk->played        = mmk->played;
337     cmmk->got_alternate = mmk->got_alternate;
338     mediamark_duplicate_alternates(mmk, cmmk);
339   }
340 
341   return cmmk;
342 }
343 
mediamark_free_mmk(mediamark_t ** mmk)344 int mediamark_free_mmk(mediamark_t **mmk) {
345   if((*mmk) != NULL) {
346     mediamark_free_alternates((*mmk));
347     SAFE_FREE((*mmk)->ident);
348     SAFE_FREE((*mmk)->mrl);
349     SAFE_FREE((*mmk)->sub);
350     SAFE_FREE((*mmk));
351     return 1;
352   }
353   return 0;
354 }
355 
356 /*
357  * Split lines from playlist->data
358  */
playlist_split_data(playlist_t * playlist)359 static int playlist_split_data(playlist_t *playlist) {
360 
361   if(playlist->data) {
362     char *buf, *pbuf, *p;
363 
364     /* strsep modify original string, avoid its corruption */
365     buf = strdup(playlist->data);
366     playlist->numl = 0;
367 
368     pbuf = buf;
369     while((p = xine_strsep(&pbuf, "\n")) != NULL) {
370       if(p && (p <= pbuf) && (strlen(p))) {
371 
372 	playlist->lines = (char **) realloc(playlist->lines, sizeof(char *) * (playlist->numl + 2));
373 
374 	while((*(p + strlen(p) - 1) == '\n') || (*(p + strlen(p) - 1) == '\r'))
375 	  *(p + strlen(p) - 1) = '\0';
376 
377 	playlist->lines[playlist->numl++] = strdup(p);
378 	playlist->lines[playlist->numl] = NULL;
379       }
380     }
381 
382     free(buf);
383   }
384 
385   return (playlist->numl > 0);
386 }
387 
set_pos_to_value(char ** p)388 static void set_pos_to_value(char **p) {
389 
390   ABORT_IF_NULL(*p);
391 
392   while(*(*p) != '\0' && *(*p) != '=' && *(*p) != ':' && *(*p) != '{') ++(*p);
393   while(*(*p) == '=' || *(*p) == ':' || *(*p) == ' ' || *(*p) == '\t') ++(*p);
394 }
395 
get_basedir(const char * filename)396 static char *get_basedir(const char *filename) {
397   char *path;
398   char *origin = NULL;
399 
400   path = strrchr(filename, '/');
401   if(path && (path > filename)) {
402     origin = (char *) malloc((path - filename) + 2);
403     snprintf(origin, (path-filename)+2, "%s", filename);
404   }
405 
406   return origin;
407 }
408 
is_mrl(const char * entry)409 static int is_mrl(const char *entry) {
410   char *path;
411 
412   path = strstr(entry, ":/");
413   if (path) {
414     while (entry < path) {
415       if (!isalnum(entry[0])) return 0;
416       entry++;
417     }
418     return 1;
419   }
420   return 0;
421 }
422 
concat_basedir(char * buffer,size_t size,const char * origin,const char * entry)423 static const char *concat_basedir(char *buffer, size_t size, const char *origin, const char *entry) {
424   if(origin && entry[0] != '/' && !is_mrl(entry)) {
425     snprintf(buffer, size, "%s%s", origin, entry);
426     return buffer;
427   } else {
428     return entry;
429   }
430 }
431 
432 /*
433  * Playlists guessing
434  */
guess_pls_playlist(playlist_t * playlist,const char * filename)435 static mediamark_t **guess_pls_playlist(playlist_t *playlist, const char *filename) {
436   mediamark_t **mmk = NULL;
437   char         *extension;
438 
439   if(filename) {
440     if(is_a_file((char *) filename) || is_downloadable((char *) filename)) {
441 
442       extension = strrchr(filename, '.');
443 
444       if((extension) && (!strncasecmp(extension, ".pls", 4))) {
445 	char *pls_content;
446 	int   size;
447 
448 	if((pls_content = _read_file(filename, &size)) != NULL) {
449 
450 	  playlist->data = pls_content;
451 
452 	  if(playlist_split_data(playlist)) {
453 	    int   valid_pls     = 0;
454 	    int   entries_pls   = 0;
455 	    int   found_nument  = 0;
456 	    int   stored_nument = 0;
457 	    int   pl_line       = 0;
458 	    int   linen         = 0;
459 	    int   count         = 0;
460 	    char *ln, *origin;
461 	    const char *store_mrl;
462 	    char  buffer[_PATH_MAX + _NAME_MAX + 2];
463 
464 	    origin = get_basedir(filename);
465 	    do {
466 
467 	      while((ln = playlist->lines[linen++]) != NULL) {
468 		if(ln) {
469 
470 		  if(valid_pls) {
471 
472 		    if(entries_pls) {
473 		      int   entry;
474 
475 		      if((!strncasecmp(ln, "file", 4)) && ((sscanf(ln + 4, "%d=", &entry)) == 1)) {
476 			char *mrl = strchr(ln, '=');
477 
478 			if(mrl)
479 			  mrl++;
480 
481 			if((entry && mrl) &&
482 			   ((entry) <= entries_pls) &&
483 			   (mmk && (!mmk[(entry - 1)]))) {
484 			  stored_nument++;
485 			  store_mrl = concat_basedir(buffer, sizeof(buffer), origin, mrl);
486 			  mediamark_store_mmk(&mmk[(entry - 1)], store_mrl, NULL, NULL, 0, -1, 0, 0);
487 			}
488 
489 		      }
490 
491 		    }
492 		    else {
493 		      if((!strncasecmp(ln, "numberofentries", 15))
494 			 && ((sscanf(ln + 15, "=%d", &entries_pls)) == 1)) {
495 
496 			if(!found_nument) {
497 			  if(entries_pls) {
498 			    playlist->entries = entries_pls;
499 			    mmk = (mediamark_t **) calloc((entries_pls + 1), sizeof(mediamark_t *));
500 			    found_nument = 1;
501 			  }
502 			}
503 
504 		      }
505 		    }
506 
507 		  }
508 		  else if((!strcasecmp(ln, "[playlist]"))) {
509 		    if(!valid_pls) {
510 		      valid_pls = 1;
511 		      pl_line = linen;
512 		    }
513 		  }
514 
515 		}
516 	      }
517 
518 	      count++;
519 	      linen = pl_line;
520 
521 	    } while((playlist->entries && !stored_nument) && (count < 2));
522 	    free(origin);
523 
524 	    if(mmk && valid_pls && entries_pls) {
525 	      int i;
526 
527 	      mmk[entries_pls] = NULL;
528 
529 	      /* Fill missing entries */
530 	      for(i = 0; i < entries_pls; i++) {
531 		if(!mmk[i])
532 		  mediamark_store_mmk(&mmk[i], _("!!Invalid entry!!"), NULL, NULL, 0, -1, 0, 0);
533 	      }
534 	      playlist->type = strdup("PLS");
535 	    }
536 
537 	    while(playlist->numl) {
538 	      free(playlist->lines[playlist->numl - 1]);
539 	      playlist->numl--;
540 	    }
541 
542 	    if(valid_pls && entries_pls) {
543 	      SAFE_FREE(playlist->lines);
544 	      free(pls_content);
545 	      return mmk;
546 	    }
547 	  }
548 	  SAFE_FREE(playlist->lines);
549 	  free(pls_content);
550 	}
551       }
552     }
553   }
554 
555   return NULL;
556 }
557 
guess_m3u_playlist(playlist_t * playlist,const char * filename)558 static mediamark_t **guess_m3u_playlist(playlist_t *playlist, const char *filename) {
559   mediamark_t **mmk = NULL;
560 
561   if(filename) {
562     if(is_a_file((char *) filename) || is_downloadable((char *) filename)) {
563       char *m3u_content;
564       int   size;
565 
566       if((m3u_content = _read_file(filename, &size)) != NULL) {
567 
568 	playlist->data = m3u_content;
569 
570 	if(playlist_split_data(playlist)) {
571 	  int   valid_m3u   = 0;
572 	  int   entries_m3u = 0;
573 	  char *title       = NULL;
574 	  char *origin;
575 	  int   linen = 0;
576 	  char *ln;
577 
578 	  origin = get_basedir(filename);
579 
580 	  while((ln = playlist->lines[linen++]) != NULL) {
581 	    if(ln) {
582 
583 	      if(valid_m3u) {
584 
585 		if(!strncmp(ln, "#EXTINF", 7)) {
586                   char *ptitle;
587 		  if((ptitle = strchr(ln, ',')) != NULL) {
588 		    ptitle++;
589 
590 		    SAFE_FREE(title);
591 
592 		    if(strlen(ptitle))
593 		      title = strdup(ptitle);
594 		  }
595 		}
596 		else if (ln[0] != '#') {
597 		  char  buffer[_PATH_MAX + _NAME_MAX + 2];
598 		  const char *entry;
599 
600 		  mmk = (mediamark_t **) realloc(mmk, sizeof(mediamark_t *) * (entries_m3u + 2));
601 		  entry = concat_basedir(buffer, sizeof(buffer), origin, ln);
602 
603 		  mediamark_store_mmk(&mmk[entries_m3u], entry, title, NULL, 0, -1, 0, 0);
604 		  playlist->entries = ++entries_m3u;
605 
606 		  SAFE_FREE(title);
607 		}
608 
609 	      }
610 	      else if((!strcasecmp(ln, "#EXTM3U")))
611 		valid_m3u = 1;
612 	    }
613 	  }
614 
615 	  if(valid_m3u && entries_m3u) {
616 	    mmk[entries_m3u] = NULL;
617 	    playlist->type = strdup("M3U");
618 	  }
619 
620 	  free(origin);
621           free(title);
622 
623 	  while(playlist->numl) {
624 	    free(playlist->lines[playlist->numl - 1]);
625 	    playlist->numl--;
626 	  }
627 	}
628 	SAFE_FREE(playlist->lines);
629 
630 	free(m3u_content);
631       }
632     }
633   }
634 
635   return mmk;
636 }
637 
guess_sfv_playlist(playlist_t * playlist,const char * filename)638 static mediamark_t **guess_sfv_playlist(playlist_t *playlist, const char *filename) {
639   mediamark_t **mmk = NULL;
640 
641   if(filename) {
642     if(is_a_file((char *) filename) || is_downloadable((char *) filename)) {
643       char  *extension;
644 
645       extension = strrchr(filename, '.');
646 
647       if((extension) && (!strncasecmp(extension, ".sfv", 4))) {
648 	char *sfv_content;
649 	int   size;
650 
651 	if((sfv_content = _read_file(filename, &size)) != NULL) {
652 
653 	  playlist->data = sfv_content;
654 
655 	  if(playlist_split_data(playlist)) {
656 	    int    valid_sfv = 0;
657 	    int    entries_sfv = 0;
658 	    char  *origin;
659 	    int    linen = 0;
660 	    char  *ln;
661 
662 	    origin = get_basedir(filename);
663 
664 	    while((ln = playlist->lines[linen++]) != NULL) {
665 
666 	      if(ln) {
667 
668 		if(valid_sfv) {
669 
670 		  if(strncmp(ln, ";", 1)) {
671 		    char            buffer[_PATH_MAX + _NAME_MAX + 2];
672 		    const char     *entry;
673 		    long long int   crc = 0;
674 		    char           *p;
675 
676 		    p = ln + strlen(ln) - 1;
677 		    if(p) {
678 
679 		      while(p && (p > ln) && (*p != ' '))
680 			p--;
681 
682 		      if(p > ln) {
683 			*p = '\0';
684 			p++;
685 		      }
686 
687 		      if(p && strlen(p)) {
688 			crc = strtoll(p, &p, 16);
689 
690 			if((errno == ERANGE) || (errno == EINVAL))
691 			  crc = 0;
692 
693 		      }
694 
695 		      if(crc > 0) {
696 
697 			mmk = (mediamark_t **) realloc(mmk, sizeof(mediamark_t *) * (entries_sfv + 2));
698 
699 		  	entry = concat_basedir(buffer, sizeof(buffer), origin, ln);
700 			mediamark_store_mmk(&mmk[entries_sfv], entry, NULL, NULL, 0, -1, 0, 0);
701 			playlist->entries = ++entries_sfv;
702 		      }
703 		    }
704 		  }
705 		}
706 		else if(strlen(ln) > 1) {
707 		  long int   size;
708 		  int        h, m, s;
709 		  int        Y, M, D;
710 		  char       fn[2];
711 		  char       mon[4];
712 
713 		  if(((sscanf(ln, ";%ld %d:%d.%d %d-%d-%d %1s", &size, &h, &m, &s, &Y, &M, &D, &fn[0])) == 8) ||
714 		     ((sscanf(ln, ";%ld %3s %d %d:%d:%d %d %1s", &size, &mon[0], &D, &h, &m, &s, &Y, &fn[0])) == 8))
715 		    valid_sfv = 1;
716 
717 		}
718 	      }
719 	    }
720 
721 	    if(valid_sfv && entries_sfv) {
722 	      mmk[entries_sfv] = NULL;
723 	      playlist->type = strdup("SFV");
724 	    }
725 
726 	    free(origin);
727 
728 	    while(playlist->numl) {
729 	      free(playlist->lines[playlist->numl - 1]);
730 	      playlist->numl--;
731 	    }
732 	  }
733 	  SAFE_FREE(playlist->lines);
734 	  free(sfv_content);
735 	}
736       }
737     }
738   }
739 
740   return mmk;
741 }
742 
guess_raw_playlist(playlist_t * playlist,const char * filename)743 static mediamark_t **guess_raw_playlist(playlist_t *playlist, const char *filename) {
744   mediamark_t **mmk = NULL;
745 
746   if(filename) {
747     if(is_a_file((char *) filename) || is_downloadable((char *) filename)) {
748       char *raw_content;
749       int   size;
750 
751       if((raw_content = _read_file(filename, &size)) != NULL) {
752 
753 	playlist->data = raw_content;
754 
755 	if(playlist_split_data(playlist)) {
756 	  char *origin;
757 	  int   entries_raw = 0;
758 	  int   linen = 0;
759 	  char *ln;
760 
761 	  origin = get_basedir(filename);
762 
763 	  while((ln = playlist->lines[linen++]) != NULL) {
764 	    if(ln) {
765 
766 	      if((strncmp(ln, ";", 1)) && (strncmp(ln, "#", 1))) {
767 		char        buffer[_PATH_MAX + _NAME_MAX + 2];
768 		const char *entry;
769 
770 		mmk = (mediamark_t **) realloc(mmk, sizeof(mediamark_t *) * (entries_raw + 2));
771 
772 	  	entry = concat_basedir(buffer, sizeof(buffer), origin, ln);
773 
774 		mediamark_store_mmk(&mmk[entries_raw], entry, NULL, NULL, 0, -1, 0, 0);
775 		playlist->entries = ++entries_raw;
776 	      }
777 	    }
778 	  }
779 
780 	  if(entries_raw) {
781 	    mmk[entries_raw] = NULL;
782 	    playlist->type = strdup("RAW");
783 	  }
784 
785 	  free(origin);
786 
787 	  while(playlist->numl) {
788 	    free(playlist->lines[playlist->numl - 1]);
789 	    playlist->numl--;
790 	  }
791 	}
792 	SAFE_FREE(playlist->lines);
793 	free(raw_content);
794       }
795     }
796   }
797 
798   return mmk;
799 }
800 
guess_toxine_playlist(playlist_t * playlist,const char * filename)801 static mediamark_t **guess_toxine_playlist(playlist_t *playlist, const char *filename) {
802   mediamark_t **mmk = NULL;
803 
804   if(filename) {
805     if(is_a_file((char *) filename) || is_downloadable((char *) filename)) {
806       int   entries_tox = 0;
807       char *tox_content;
808       int   size;
809 
810       if((tox_content = _read_file(filename, &size)) != NULL) {
811 
812 	playlist->data = tox_content;
813 
814 	if(playlist_split_data(playlist)) {
815 	  char    buffer[32768], path[_PATH_MAX + _NAME_MAX + 2];
816 	  char   *p, *pp, *origin;
817 	  int     start = 0;
818 	  int     linen = 0;
819 	  char   *ln;
820 
821 	  memset(&buffer, 0, sizeof(buffer));
822 	  p = buffer;
823 	  origin = get_basedir(filename);
824 
825 	  while((ln = playlist->lines[linen++]) != NULL) {
826 
827 	    if(!start) {
828 	      memset(&buffer, 0, sizeof(buffer));
829 	      p = buffer;
830 	    }
831 
832 	    if((ln) && (strlen(ln))) {
833 
834 	      if(strncmp(ln, "#", 1)) {
835 
836 		pp = ln;
837 
838 		while(*pp != '\0') {
839 
840 		  if(!strncasecmp(pp, "entry {", 7)) {
841 		    if(!start) {
842 		      start = 1;
843 		      pp += 7;
844 		      memset(&buffer, 0, sizeof(buffer));
845 		      p = buffer;
846 		    }
847 		    else {
848 		      memset(&buffer, 0, sizeof(buffer));
849 		      p = buffer;
850 		      goto __discard;
851 		    }
852 		  }
853 		  if((*pp == '}') && (*(pp + 1) == ';')) {
854 		    if(start) {
855 		      start = 0;
856 		      pp += 2;
857 
858 		      { /* OKAY, check found string */
859 			mediamark_t   mmkf;
860 			int           mmkf_members[7];
861 			char         *line, *pline;
862 			char         *m;
863 
864 			mmkf.ident      = NULL;
865 			mmkf.sub        = NULL;
866 			mmkf.start      = 0;
867 			mmkf.end        = -1;
868 			mmkf.av_offset  = 0;
869 			mmkf.spu_offset = 0;
870 			mmkf.mrl        = NULL;
871 
872 			mmkf_members[0] = 0;  /* ident */
873 			mmkf_members[1] = -1; /* mrl */
874 			mmkf_members[2] = 0;  /* start */
875 			mmkf_members[3] = 0;  /* end */
876 			mmkf_members[4] = 0;  /* av offset */
877 			mmkf_members[5] = 0;  /* spu offset */
878 			mmkf_members[6] = 0;  /* sub */
879 
880 			line = strdup(atoa(buffer));
881 
882 			pline = line;
883 			while((m = xine_strsep(&pline, ";")) != NULL) {
884 			  char *key;
885 			  const char *entry;
886 
887 			  key = atoa(m);
888 			  if(strlen(key)) {
889 
890 			    if(!strncasecmp(key, "identifier", 10)) {
891 			      if(mmkf_members[0] == 0) {
892 				mmkf_members[0] = 1;
893 				set_pos_to_value(&key);
894 				mmkf.ident = strdup(key);
895 			      }
896 			    }
897 			    else if(!strncasecmp(key, "subtitle", 8)) {
898 			      if(mmkf_members[6] == 0) {
899 				set_pos_to_value(&key);
900 				/* Workaround old toxine playlist version bug */
901 				if(strcmp(key, "(null)")) {
902 				  mmkf_members[6] = 1;
903 				  entry = concat_basedir(path, sizeof(path), origin, key);
904 				  mmkf.sub = strdup(entry);
905 				}
906 			      }
907 			    }
908 			    else if(!strncasecmp(key, "spu_offset", 10)) {
909 			      if(mmkf_members[5] == 0) {
910 				mmkf_members[5] = 1;
911 				set_pos_to_value(&key);
912 				mmkf.spu_offset = strtol(key, &key, 10);
913 			      }
914 			    }
915 			    else if(!strncasecmp(key, "av_offset", 9)) {
916 			      if(mmkf_members[4] == 0) {
917 				mmkf_members[4] = 1;
918 				set_pos_to_value(&key);
919 				mmkf.av_offset = strtol(key, &key, 10);
920 			      }
921 			    }
922 			    else if(!strncasecmp(key, "start", 5)) {
923 			      if(mmkf_members[2] == 0) {
924 				mmkf_members[2] = 1;
925 				set_pos_to_value(&key);
926 				mmkf.start = strtol(key, &key, 10);
927 			      }
928 			    }
929 			    else if(!strncasecmp(key, "end", 3)) {
930 			      if(mmkf_members[3] == 0) {
931 				mmkf_members[3] = 1;
932 				set_pos_to_value(&key);
933 				mmkf.end = strtol(key, &key, 10);
934 			      }
935 			    }
936 			    else if(!strncasecmp(key, "mrl", 3)) {
937 			      if(mmkf_members[1] == -1) {
938 				mmkf_members[1] = 1;
939 				set_pos_to_value(&key);
940 				entry = concat_basedir(path, sizeof(path), origin, key);
941 				mmkf.mrl = strdup(entry);
942 			      }
943 			    }
944 			  }
945 			}
946 
947 			if((mmkf_members[1] == 0) || (mmkf_members[1] == -1)) {
948 			  /*  printf("wow, no mrl found\n"); */
949 			  goto __discard;
950 			}
951 
952 			if(mmkf_members[0] == 0)
953 			  mmkf.ident = mmkf.mrl;
954 
955 			/*
956 			  STORE new mmk;
957 			*/
958 #if 0
959 			printf("DUMP mediamark entry:\n");
960 			printf("ident:     '%s'\n", mmkf.ident);
961 			printf("mrl:       '%s'\n", mmkf.mrl);
962 			printf("sub:       '%s'\n", mmkf.sub);
963 			printf("start:      %d\n", mmkf.start);
964 			printf("end:        %d\n", mmkf.end);
965 			printf("av_offset:  %d\n", mmkf.av_offset);
966 			printf("spu_offset: %d\n", mmkf.spu_offset);
967 #endif
968 
969 			mmk = (mediamark_t **) realloc(mmk, sizeof(mediamark_t *) * (entries_tox + 2));
970 
971 			mediamark_store_mmk(&mmk[entries_tox],
972 					    mmkf.mrl, mmkf.ident, mmkf.sub,
973 					    mmkf.start, mmkf.end, mmkf.av_offset, mmkf.spu_offset);
974 			playlist->entries = ++entries_tox;
975 
976 			free(line);
977 
978 			if(mmkf_members[0])
979 			  free(mmkf.ident);
980 
981 			SAFE_FREE(mmkf.sub);
982 
983 			free(mmkf.mrl);
984 		      }
985 
986 		    }
987 		    else {
988 		      memset(&buffer, 0, sizeof(buffer));
989 		      p = buffer;
990 		      goto __discard;
991 		    }
992 		  }
993 
994 		  if(*pp != '\0') {
995 		    /* buffer full? don't copy */
996 		    if (p - buffer < sizeof (buffer) - 1)
997 		      *p++ = *pp;
998 		    pp++;
999 		  }
1000 		}
1001 
1002 	      }
1003 	    }
1004 	  __discard: ;
1005 	  }
1006 
1007 	  free(origin);
1008 
1009 	  if(entries_tox) {
1010 	    mmk[entries_tox] = NULL;
1011 	    playlist->type = strdup("TOX");
1012 	  }
1013 
1014 	  while(playlist->numl) {
1015 	    free(playlist->lines[playlist->numl - 1]);
1016 	    playlist->numl--;
1017 	  }
1018 	}
1019 	SAFE_FREE(playlist->lines);
1020 	free(tox_content);
1021       }
1022     }
1023   }
1024   return mmk;
1025 }
1026 
1027 /*
1028  * XML based playlists
1029  */
xml_asx_playlist(playlist_t * playlist,const char * filename,char * xml_content,xml_node_t * xml_tree)1030 static mediamark_t **xml_asx_playlist(playlist_t *playlist, const char *filename, char *xml_content, xml_node_t *xml_tree) {
1031   mediamark_t **mmk = NULL;
1032 
1033   if(xml_tree) {
1034     xml_node_t      *asx_entry, *asx_ref;
1035     xml_property_t  *asx_prop;
1036     int              entries_asx = 0;
1037 
1038     if(!strcasecmp(xml_tree->name, "ASX")) {
1039 
1040       asx_prop = xml_tree->props;
1041 
1042       while((asx_prop) && (strcasecmp(asx_prop->name, "VERSION")))
1043 	asx_prop = asx_prop->next;
1044 
1045       if(asx_prop) {
1046 	int  version_major, version_minor = 0;
1047 
1048 	if((((sscanf(asx_prop->value, "%d.%d", &version_major, &version_minor)) == 2) ||
1049 	    ((sscanf(asx_prop->value, "%d", &version_major)) == 1)) &&
1050 	   ((version_major == 3) && (version_minor == 0))) {
1051 
1052 	__parse_anyway:
1053 	  asx_entry = xml_tree->child;
1054 	  while(asx_entry) {
1055 	    if((!strcasecmp(asx_entry->name, "ENTRY")) ||
1056 	       (!strcasecmp(asx_entry->name, "ENTRYREF"))) {
1057 	      char *title  = NULL;
1058 	      char *href   = NULL;
1059 	      char *author = NULL;
1060 	      char *sub    = NULL;
1061 
1062 	      asx_ref = asx_entry->child;
1063 	      while(asx_ref) {
1064 
1065 		if(!strcasecmp(asx_ref->name, "TITLE")) {
1066 
1067 		  if(!title)
1068 		    title = asx_ref->data;
1069 
1070 		}
1071 		else if(!strcasecmp(asx_ref->name, "AUTHOR")) {
1072 
1073 		  if(!author)
1074 		    author = asx_ref->data;
1075 
1076 		}
1077 		else if(!strcasecmp(asx_ref->name, "REF")) {
1078 
1079 		  for(asx_prop = asx_ref->props; asx_prop; asx_prop = asx_prop->next) {
1080 
1081 		    if(!strcasecmp(asx_prop->name, "HREF")) {
1082 
1083 		      if(!href)
1084 			href = asx_prop->value;
1085 		    }
1086 		    /* This is not part of the ASX specs */
1087 		    else if(!strcasecmp(asx_prop->name, "SUBTITLE")) {
1088 
1089 		      if(!sub)
1090 			sub = asx_prop->value;
1091 		    }
1092 
1093 		    if(href && sub)
1094 		      break;
1095 		  }
1096 		}
1097 
1098 		asx_ref = asx_ref->next;
1099 	      }
1100 
1101 	      if(href && strlen(href)) {
1102 		/* Use the _orig pointers to store the string before
1103 		   using atoa() that changes the pointer */
1104 		char *atitle     = NULL, *atitle_orig  = NULL;
1105 		char *aauthor    = NULL, *aauthor_orig = NULL;
1106 		char *real_title = NULL;
1107 		int   len        = 0;
1108 
1109 		if(title && strlen(title)) {
1110 		  atitle_orig = atitle = strdup(title);
1111 		  atitle = atoa(atitle);
1112 		  len = strlen(atitle);
1113 
1114 		  if(author && strlen(author)) {
1115 		    aauthor_orig = aauthor = strdup(author);
1116 		    aauthor = atoa(aauthor);
1117 		    len += strlen(aauthor) + 3;
1118 		  }
1119 
1120 		  len++;
1121 		}
1122 
1123 		if(atitle && strlen(atitle)) {
1124 		  real_title = (char *) malloc(len);
1125 		  strcpy(real_title, atitle);
1126 
1127 		  if(aauthor && strlen(aauthor))
1128 		    snprintf(real_title+strlen(real_title), len-strlen(real_title), " (%s)", aauthor);
1129 		}
1130 
1131 		mmk = (mediamark_t **) realloc(mmk, sizeof(mediamark_t *) * (entries_asx + 2));
1132 
1133 		mediamark_store_mmk(&mmk[entries_asx], href, real_title, sub, 0, -1, 0, 0);
1134 		playlist->entries = ++entries_asx;
1135 
1136 		SAFE_FREE(real_title);
1137 		SAFE_FREE(atitle_orig);
1138 		SAFE_FREE(aauthor_orig);
1139 	      }
1140 
1141 	      href = title = author = NULL;
1142 	    }
1143 	    asx_entry = asx_entry->next;
1144 	  }
1145 	}
1146 	else
1147 	  fprintf(stderr, "%s(): Wrong ASX version: %s\n", __XINE_FUNCTION__, asx_prop->value);
1148 
1149       }
1150       else {
1151 	fprintf(stderr, "%s(): Unable to find VERSION tag.\n", __XINE_FUNCTION__);
1152 	fprintf(stderr, "%s(): last chance: try to parse it anyway\n", __XINE_FUNCTION__);
1153 	goto __parse_anyway;
1154       }
1155 
1156     }
1157 #ifdef DEBUG
1158     else
1159       fprintf(stderr, "%s(): Unsupported XML type: '%s'.\n", __XINE_FUNCTION__, xml_tree->name);
1160 #endif
1161 
1162     /* Maybe it's 'ASF <url> */
1163     if(entries_asx == 0) {
1164 
1165       playlist->data = xml_content;
1166 
1167       if(playlist_split_data(playlist)) {
1168 	int    linen = 0;
1169 	char  *ln;
1170 
1171 	while((ln = playlist->lines[linen++]) != NULL) {
1172 
1173 	  if(!strncasecmp("ASF", ln, 3)) {
1174 	    char *p = ln + 3;
1175 
1176 	    while(p && ((*p == ' ') || (*p == '\t')))
1177 	      p++;
1178 
1179 	    if(p && strlen(p)) {
1180 	      mmk = (mediamark_t **) realloc(mmk, sizeof(mediamark_t *) * (entries_asx + 2));
1181 
1182 	      mediamark_store_mmk(&mmk[entries_asx], p, p, NULL, 0, -1, 0, 0);
1183 	      playlist->entries = ++entries_asx;
1184 	    }
1185 	  }
1186 	}
1187       }
1188       SAFE_FREE(playlist->lines);
1189     }
1190 
1191     if(entries_asx) {
1192       mmk[entries_asx] = NULL;
1193       playlist->type = strdup("ASX3");
1194       return mmk;
1195     }
1196   }
1197 
1198   return NULL;
1199 }
1200 
__gx_get_entries(playlist_t * playlist,mediamark_t *** mmk,int * entries,xml_node_t * entry)1201 static void __gx_get_entries(playlist_t *playlist, mediamark_t ***mmk, int *entries, xml_node_t *entry) {
1202   xml_property_t  *prop;
1203   xml_node_t      *ref;
1204   xml_node_t      *node = entry;
1205 
1206   while(node) {
1207     if(!strcasecmp(node->name, "SUB"))
1208       __gx_get_entries(playlist, mmk, entries, node->child);
1209     else if(!strcasecmp(node->name, "ENTRY")) {
1210       char *title  = NULL;
1211       char *href   = NULL;
1212       int   start  = 0;
1213 
1214       ref = node->child;
1215       while(ref) {
1216 
1217 	if(!strcasecmp(ref->name, "TITLE")) {
1218 
1219 	  if(!title)
1220 	    title = ref->data;
1221 
1222 	}
1223 	else if(!strcasecmp(ref->name, "REF")) {
1224 
1225 	  for(prop = ref->props; prop; prop = prop->next) {
1226 	    if(!strcasecmp(prop->name, "HREF")) {
1227 
1228 	      if(!href)
1229 		href = prop->value;
1230 	    }
1231 
1232 	    if(href)
1233 	      break;
1234 	  }
1235 	}
1236 	else if(!strcasecmp(ref->name, "TIME")) {
1237 
1238 	  for(prop = ref->props; prop; prop = prop->next) {
1239 	    if(!strcasecmp(prop->name, "START")) {
1240 
1241 	      if(prop->value && strlen(prop->value))
1242 		start = atoi(prop->value);
1243 
1244 	    }
1245 	  }
1246 	}
1247 
1248 	ref = ref->next;
1249       }
1250 
1251       if(href && strlen(href)) {
1252 	char *atitle = NULL;
1253 
1254 	if(title && strlen(title)) {
1255 	  atitle = strdup(title);
1256 	  atitle = atoa(atitle);
1257 	}
1258 
1259 	(*mmk) = (mediamark_t **) realloc((*mmk), sizeof(mediamark_t *) * (*entries + 2));
1260 
1261 	mediamark_store_mmk(&(*mmk)[*entries], href, (atitle && strlen(atitle)) ? atitle : NULL, NULL, start, -1, 0, 0);
1262 	playlist->entries = ++(*entries);
1263 
1264 	free(atitle);
1265       }
1266 
1267       href = title = NULL;
1268       start = 0;
1269     }
1270 
1271     node = node->next;
1272   }
1273 }
xml_gx_playlist(playlist_t * playlist,const char * filename,char * xml_content,xml_node_t * xml_tree)1274 static mediamark_t **xml_gx_playlist(playlist_t *playlist, const char *filename, char *xml_content, xml_node_t *xml_tree) {
1275   mediamark_t **mmk = NULL;
1276 
1277   if(xml_tree) {
1278     xml_node_t      *gx_entry;
1279     xml_property_t  *gx_prop;
1280     int              entries_gx = 0;
1281 
1282     if(!strcasecmp(xml_tree->name, "GXINEMM")) {
1283 
1284       gx_prop = xml_tree->props;
1285 
1286       while((gx_prop) && (strcasecmp(gx_prop->name, "VERSION")))
1287 	gx_prop = gx_prop->next;
1288 
1289       if(gx_prop) {
1290 	int  version_major;
1291 
1292 	if(((sscanf(gx_prop->value, "%d", &version_major)) == 1) && (version_major == 1)) {
1293 
1294 	  gx_entry = xml_tree->child;
1295 	  while(gx_entry) {
1296 
1297 	    if(!strcasecmp(gx_entry->name, "SUB")) {
1298 	      __gx_get_entries(playlist, &mmk, &entries_gx, gx_entry->child);
1299 	    }
1300 	    else if(!strcasecmp(gx_entry->name, "ENTRY"))
1301 	      __gx_get_entries(playlist, &mmk, &entries_gx, gx_entry);
1302 
1303 	    gx_entry = gx_entry->next;
1304 	  }
1305 	}
1306 	else
1307 	  fprintf(stderr, "%s(): Wrong GXINEMM version: %s\n", __XINE_FUNCTION__, gx_prop->value);
1308       }
1309       else
1310 	fprintf(stderr, "%s(): Unable to find VERSION tag.\n", __XINE_FUNCTION__);
1311     }
1312 #ifdef DEBUG
1313     else
1314       fprintf(stderr, "%s(): Unsupported XML type: '%s'.\n", __XINE_FUNCTION__, xml_tree->name);
1315 #endif
1316 
1317     if(entries_gx) {
1318       mmk[entries_gx] = NULL;
1319       playlist->type = strdup("GXMM");
1320       return mmk;
1321     }
1322   }
1323 
1324   return NULL;
1325 }
1326 
xml_noatun_playlist(playlist_t * playlist,const char * filename,char * xml_content,xml_node_t * xml_tree)1327 static mediamark_t **xml_noatun_playlist(playlist_t *playlist, const char *filename, char *xml_content, xml_node_t *xml_tree) {
1328   mediamark_t **mmk = NULL;
1329 
1330   if(xml_tree) {
1331     xml_node_t      *noa_entry;
1332     xml_property_t  *noa_prop;
1333     int              entries_noa = 0;
1334 
1335     if(!strcasecmp(xml_tree->name, "PLAYLIST")) {
1336       int found = 0;
1337 
1338       noa_prop = xml_tree->props;
1339 
1340       while(noa_prop) {
1341 	if((!strcasecmp(noa_prop->name, "CLIENT")) && (!strcasecmp(noa_prop->value, "NOATUN")))
1342 	  found++;
1343 	else if(!strcasecmp(noa_prop->name, "VERSION")) {
1344 	  int  version_major;
1345 
1346 	  if(((sscanf(noa_prop->value, "%d", &version_major)) == 1) && (version_major == 1))
1347 	    found++;
1348 	}
1349 
1350 	noa_prop = noa_prop->next;
1351       }
1352 
1353       if(found >= 2) {
1354 	noa_entry = xml_tree->child;
1355 
1356 	while(noa_entry) {
1357 
1358 	  if(!strcasecmp(noa_entry->name, "ITEM")) {
1359 	    char *real_title = NULL;
1360 	    char *title      = NULL;
1361 	    char *album      = NULL;
1362 	    char *artist     = NULL;
1363 	    char *url        = NULL;
1364 
1365 	    for(noa_prop = noa_entry->props; noa_prop; noa_prop = noa_prop->next) {
1366 	      if(!strcasecmp(noa_prop->name, "TITLE"))
1367 		title = noa_prop->value;
1368 	      else if(!strcasecmp(noa_prop->name, "ALBUM"))
1369 		album = noa_prop->value;
1370 	      else if(!strcasecmp(noa_prop->name, "ARTIST"))
1371 		artist = noa_prop->value;
1372 	      else if(!strcasecmp(noa_prop->name, "URL"))
1373 		url = noa_prop->value;
1374 	    }
1375 
1376 	    if(url) {
1377 	      int   titlen = 0;
1378 
1379 	      /*
1380 		title (artist - album)
1381 	      */
1382 	      if(title && (titlen = strlen(title))) {
1383 		int artlen = 0;
1384 		int alblen = 0;
1385 
1386 		if(artist && (artlen = strlen(artist)) && album && (alblen = strlen(album))) {
1387 		  int len = titlen + artlen + alblen + 7;
1388 
1389 		  real_title = (char *) malloc(len);
1390 		  sprintf(real_title, "%s (%s - %s)", title, artist, album);
1391 		}
1392 		else if(artist && (artlen = strlen(artist))) {
1393 		  int len = titlen + artlen + 4;
1394 
1395 		  real_title = (char *) malloc(len);
1396 		  sprintf(real_title, "%s (%s)", title, artist);
1397 		}
1398 		else if(album && (alblen = strlen(album))) {
1399 		  int len = titlen + alblen + 4;
1400 
1401 		  real_title = (char *) malloc(len);
1402 		  sprintf(real_title, "%s (%s)", title, album);
1403 		}
1404 		else
1405 		  real_title = strdup(title);
1406 
1407 	      }
1408 
1409 	      mmk = (mediamark_t **) realloc(mmk, sizeof(mediamark_t *) * (entries_noa + 2));
1410 
1411 	      mediamark_store_mmk(&mmk[entries_noa], url, real_title, NULL, 0, -1, 0, 0);
1412 	      playlist->entries = ++entries_noa;
1413 
1414 	      free(real_title);
1415 
1416 	    }
1417 	  }
1418 
1419 	  noa_entry = noa_entry->next;
1420 	}
1421       }
1422     }
1423 #ifdef DEBUG
1424     else
1425       fprintf(stderr, "%s(): Unsupported XML type: '%s'.\n", __XINE_FUNCTION__, xml_tree->name);
1426 #endif
1427 
1428     if(entries_noa) {
1429       mmk[entries_noa] = NULL;
1430       playlist->type = strdup("NOATUN");
1431       return mmk;
1432     }
1433   }
1434 
1435   return NULL;
1436 }
1437 
1438 /*
1439  * ********************************** SMIL BEGIN ***********************************
1440  */
1441 #undef DEBUG_SMIL
1442 
1443 #ifdef DEBUG_SMIL
1444 static int offset = 0;
1445 #define palign do { int i; for(i = 0; i < offset; i++) { printf(" "); } } while(0)
1446 #endif
1447 
1448 typedef struct smil_node_s smil_node_t;
1449 
1450 typedef struct {
1451   char   *anchor;
1452 
1453   int     repeat;
1454   int     begin;
1455   int     end;
1456 
1457   int     clip_begin;
1458   int     clip_end;
1459 
1460   int     duration;
1461 
1462 } smil_property_t;
1463 
1464 struct smil_node_s {
1465   mediamark_t      *mmk;
1466   smil_property_t   prop;
1467   smil_node_t      *next;
1468 };
1469 
1470 typedef struct {
1471   /* Header */
1472   char   *title;
1473   char   *author;
1474   char   *copyright;
1475   char   *year;
1476   char   *base;
1477   /* End Header */
1478 
1479   smil_node_t *first;
1480   smil_node_t *node;
1481 
1482 } smil_t;
1483 
1484 /* protos */
1485 static void smil_seq(smil_t *, smil_node_t **, xml_node_t *, smil_property_t *);
1486 static void smil_par(smil_t *, smil_node_t **, xml_node_t *, smil_property_t *);
1487 static void smil_switch(smil_t *, smil_node_t **, xml_node_t *, smil_property_t *);
1488 static void smil_repeat(int, smil_node_t *, smil_node_t **, smil_property_t *);
1489 
smil_init_smil_property(smil_property_t * sprop)1490 static void smil_init_smil_property(smil_property_t *sprop) {
1491   sprop->anchor     = NULL;
1492   sprop->repeat     = 1;
1493   sprop->begin      = 0;
1494   sprop->clip_begin = 0;
1495   sprop->end        = -1;
1496   sprop->clip_end   = -1;
1497   sprop->duration   = 0;
1498 }
smil_new_node(void)1499 static smil_node_t *smil_new_node(void) {
1500   smil_node_t *node;
1501 
1502   node       = (smil_node_t *) calloc(1, sizeof(smil_node_t));
1503   node->mmk  = NULL;
1504   node->next = NULL;
1505 
1506   smil_init_smil_property(&(node->prop));
1507 
1508   return node;
1509 }
smil_new_mediamark(void)1510 static mediamark_t *smil_new_mediamark(void) {
1511   mediamark_t *mmk;
1512 
1513   mmk             = (mediamark_t *) calloc(1, sizeof(mediamark_t));
1514   mmk->mrl        = NULL;
1515   mmk->ident      = NULL;
1516   mmk->sub        = NULL;
1517   mmk->start      = 0;
1518   mmk->end        = -1;
1519   mmk->played     = 0;
1520   mmk->alternates = NULL;
1521   mmk->cur_alt    = NULL;
1522 
1523   return mmk;
1524 }
smil_duplicate_mmk(mediamark_t * ommk)1525 static mediamark_t *smil_duplicate_mmk(mediamark_t *ommk) {
1526   mediamark_t *mmk = NULL;
1527 
1528   if(ommk) {
1529     mmk             = smil_new_mediamark();
1530     mmk->mrl        = strdup(ommk->mrl);
1531     mmk->ident      = strdup(ommk->ident ? ommk->ident : ommk->mrl);
1532     mmk->sub        = ommk->sub ? strdup(ommk->sub) : NULL;
1533     mmk->start      = ommk->start;
1534     mmk->end        = ommk->end;
1535     mmk->played     = ommk->played;
1536     mmk->alternates = NULL;
1537     mmk->cur_alt    = NULL;
1538   }
1539 
1540   return mmk;
1541 }
smil_free_properties(smil_property_t * smil_props)1542 static void smil_free_properties(smil_property_t *smil_props) {
1543   if(smil_props) {
1544     SAFE_FREE(smil_props->anchor);
1545   }
1546 }
1547 
1548 #ifdef DEBUG_SMIL
smil_dump_header(smil_t * smil)1549 static void smil_dump_header(smil_t *smil) {
1550   printf("title: %s\n", smil->title);
1551   printf("author: %s\n", smil->author);
1552   printf("copyright: %s\n", smil->copyright);
1553   printf("year: %s\n", smil->year);
1554   printf("base: %s\n", smil->base);
1555 }
1556 
smil_dump_tree(smil_node_t * node)1557 static void smil_dump_tree(smil_node_t *node) {
1558   if(node->mmk) {
1559     printf("mrl:   %s\n", node->mmk->mrl);
1560     printf(" ident: %s\n", node->mmk->ident);
1561     printf("  sub:   %s\n", node->mmk->sub);
1562     printf("   start: %d\n", node->mmk->start);
1563     printf("    end:   %d\n", node->mmk->end);
1564   }
1565 
1566   if(node->next)
1567     smil_dump_tree(node->next);
1568 }
1569 #endif
1570 
smil_get_prop_value(xml_property_t * props,const char * propname)1571 static char *smil_get_prop_value(xml_property_t *props, const char *propname) {
1572   xml_property_t  *prop;
1573 
1574   for(prop = props; prop; prop = prop->next) {
1575     if(!strcasecmp(prop->name, propname)) {
1576       return prop->value;
1577     }
1578   }
1579   return NULL;
1580 }
1581 
smil_get_time_in_seconds(const char * time_str)1582 static int smil_get_time_in_seconds(const char *time_str) {
1583   int    retval = 0;
1584   //int    hours, mins, secs, msecs;
1585   int    unit = 0; /* 1 = ms, 2 = s, 3 = min, 4 = h */
1586 
1587   if(strstr(time_str, "ms"))
1588     unit = 1;
1589   else if(strstr(time_str, "s"))
1590     unit = 2;
1591   else if(strstr(time_str, "min"))
1592     unit = 3;
1593   else if(strstr(time_str, "h"))
1594     unit = 4;
1595   else
1596     unit = 2;
1597 
1598   /* not reached
1599   if(unit == 0) {
1600     if((sscanf(time_str, "%d:%d:%d.%d", &hours, &mins, &secs, &msecs)) == 4)
1601       retval = (hours * 60 * 60) + (mins * 60) + secs + ((int) msecs / 1000);
1602     else if((sscanf(time_str, "%d:%d:%d", &hours, &mins, &secs)) == 3)
1603       retval = (hours * 60 * 60) + (mins * 60) + secs;
1604     else if((sscanf(time_str, "%d:%d.%d", &mins, &secs, &msecs)) == 3)
1605       retval = (mins * 60) + secs + ((int) msecs / 1000);
1606     else if((sscanf(time_str, "%d:%d", &mins, &secs)) == 2) {
1607       retval = (mins * 60) + secs;
1608     }
1609   }
1610   else*/ {
1611     int val, dec, args;
1612 
1613     if(((args = sscanf(time_str, "%d.%d", &val, &dec)) == 2) ||
1614        ((args = sscanf(time_str, "%d", &val)) == 1)) {
1615       switch(unit) {
1616       case 1: /* ms */
1617 	retval = (int) val / 1000;
1618 	break;
1619       case 2: /* s */
1620 	retval = val + ((args == 2) ? ((int)((((float) dec) * 10) / 1000)) : 0);
1621 	break;
1622       case 3: /* min */
1623 	retval = (val * 60) + ((args == 2) ? (dec * 10 * 60 / 100) : 0);
1624 	break;
1625       case 4: /* h */
1626 	retval = (val * 60 * 60) + ((args == 2) ? (dec * 10 * 60 / 100) : 0);
1627 	break;
1628       }
1629     }
1630   }
1631 
1632   return retval;
1633 }
smil_header(smil_t * smil,xml_property_t * props)1634 static void smil_header(smil_t *smil, xml_property_t *props) {
1635   xml_property_t  *prop;
1636 
1637 #ifdef DEBUG_SMIL
1638   offset += 2;
1639 #endif
1640 
1641   for(prop = props; prop; prop = prop->next) {
1642 #ifdef DEBUG_SMIL
1643     palign;
1644     printf("%s(): prop_name '%s', value: '%s'\n", __XINE_FUNCTION__,
1645     	   prop->name, prop->value ? prop->value : "<NULL>");
1646 #endif
1647 
1648     if(!strcasecmp(prop->name, "NAME")) {
1649 
1650       if(!strcasecmp(prop->value, "TITLE"))
1651 	smil->title = smil_get_prop_value(prop, "CONTENT");
1652       else if(!strcasecmp(prop->value, "AUTHOR"))
1653 	smil->author = smil_get_prop_value(prop, "CONTENT");
1654       else if(!strcasecmp(prop->value, "COPYRIGHT"))
1655 	smil->copyright = smil_get_prop_value(prop, "CONTENT");
1656       else if(!strcasecmp(prop->value, "YEAR"))
1657 	smil->year = smil_get_prop_value(prop, "CONTENT");
1658       else if(!strcasecmp(prop->value, "BASE"))
1659 	smil->base = smil_get_prop_value(prop, "CONTENT");
1660 
1661     }
1662   }
1663 #ifdef DEBUG_SMIL
1664   palign;
1665   printf("---\n");
1666   offset -= 2;
1667 #endif
1668 }
1669 
smil_get_properties(smil_property_t * dprop,xml_property_t * props)1670 static void smil_get_properties(smil_property_t *dprop, xml_property_t *props) {
1671   xml_property_t  *prop;
1672 
1673 #ifdef DEBUG_SMIL
1674   offset += 2;
1675 #endif
1676   for(prop = props; prop; prop = prop->next) {
1677 
1678     if(!strcasecmp(prop->name, "REPEAT")) {
1679       dprop->repeat = strtol(prop->value, &prop->value, 10);
1680 #ifdef DEBUG_SMIL
1681       palign;
1682       printf("REPEAT: %d\n", dprop->repeat);
1683 #endif
1684     }
1685     else if(!strcasecmp(prop->name, "BEGIN")) {
1686       dprop->begin = smil_get_time_in_seconds(prop->value);
1687 #ifdef DEBUG_SMIL
1688       palign;
1689       printf("BEGIN: %d\n", dprop->begin);
1690 #endif
1691     }
1692     else if((!strcasecmp(prop->name, "CLIP-BEGIN")) || (!strcasecmp(prop->name, "CLIPBEGIN"))) {
1693       dprop->clip_begin = smil_get_time_in_seconds(prop->value);
1694 #ifdef DEBUG_SMIL
1695       palign;
1696       printf("CLIP-BEGIN: %d\n", dprop->clip_begin);
1697 #endif
1698     }
1699     else if(!strcasecmp(prop->name, "END")) {
1700       dprop->end = smil_get_time_in_seconds(prop->value);
1701 #ifdef DEBUG_SMIL
1702       palign;
1703       printf("END: %d\n", dprop->end);
1704 #endif
1705     }
1706     else if((!strcasecmp(prop->name, "CLIP-END")) || (!strcasecmp(prop->name, "CLIPEND"))) {
1707       dprop->clip_end = smil_get_time_in_seconds(prop->value);
1708 #ifdef DEBUG_SMIL
1709       palign;
1710       printf("CLIP-END: %d\n", dprop->clip_end);
1711 #endif
1712     }
1713     else if(!strcasecmp(prop->name, "DUR")) {
1714       dprop->duration = smil_get_time_in_seconds(prop->value);
1715 #ifdef DEBUG_SMIL
1716       palign;
1717       printf("DURATION: %d\n", dprop->duration);
1718 #endif
1719     }
1720     else if(!strcasecmp(prop->name, "HREF")) {
1721 
1722       SAFE_FREE(dprop->anchor);
1723 
1724       dprop->anchor = strdup(prop->value);
1725 
1726 #ifdef DEBUG_SMIL
1727       palign;
1728       printf("HREF: %s\n", dprop->anchor);
1729 #endif
1730 
1731     }
1732   }
1733 #ifdef DEBUG_SMIL
1734   offset -= 2;
1735 #endif
1736 }
1737 
smil_properties(smil_t * smil,smil_node_t ** snode,xml_property_t * props,smil_property_t * sprop)1738 static void smil_properties(smil_t *smil, smil_node_t **snode,
1739 			    xml_property_t *props, smil_property_t *sprop) {
1740   xml_property_t  *prop;
1741   int              gotmrl = 0;
1742 
1743 #ifdef DEBUG_SMIL
1744   offset += 2;
1745 #endif
1746   for(prop = props; prop; prop = prop->next) {
1747 
1748 #ifdef DEBUG_SMIL
1749     palign;
1750     printf("%s(): prop_name '%s', value: '%s'\n", __XINE_FUNCTION__,
1751 	   prop->name, prop->value ? prop->value : "<NULL>");
1752 #endif
1753 
1754     if(prop->name) {
1755       if((!strcasecmp(prop->name, "SRC")) || (!strcasecmp(prop->name, "HREF"))) {
1756 
1757 	gotmrl++;
1758 
1759 	if((*snode)->mmk == NULL)
1760 	  (*snode)->mmk = smil_new_mediamark();
1761 
1762 	/*
1763 	  handle BASE, ANCHOR
1764 	*/
1765 	if(sprop && sprop->anchor)
1766 	  (*snode)->mmk->mrl = strdup(sprop->anchor);
1767 	else {
1768 	  char  buffer[((smil->base) ? (strlen(smil->base) + 1) : 0) + strlen(prop->value) + 1];
1769 	  char *p;
1770 
1771 	  memset(&buffer, 0, sizeof(buffer));
1772 	  p = buffer;
1773 
1774 	  if(smil->base && (!strstr(prop->value, "://"))) {
1775 	    strlcat(p, smil->base, sizeof(buffer));
1776 
1777 	    if(buffer[strlen(buffer) - 1] != '/')
1778 	      strlcat(p, "/", sizeof(buffer));
1779 	  }
1780 
1781 	  strlcat(p, prop->value, sizeof(buffer));
1782 
1783 	  (*snode)->mmk->mrl = strdup(buffer);
1784 	}
1785       }
1786       else if(!strcasecmp(prop->name, "TITLE")) {
1787 
1788 	gotmrl++;
1789 
1790 	if((*snode)->mmk == NULL) {
1791 	  (*snode)->mmk = smil_new_mediamark();
1792 	}
1793 
1794 	(*snode)->mmk->ident = strdup(prop->value);
1795       }
1796       else if(!strcasecmp(prop->name, "REPEAT")) {
1797 	(*snode)->prop.repeat = strtol(prop->value, &prop->value, 10);
1798       }
1799       else if(!strcasecmp(prop->name, "BEGIN")) {
1800 	(*snode)->prop.begin = smil_get_time_in_seconds(prop->value);
1801 #ifdef DEBUG_SMIL
1802 	printf("begin: %d\n",(*snode)->prop.begin);
1803 #endif
1804       }
1805       else if((!strcasecmp(prop->name, "CLIP-BEGIN")) || (!strcasecmp(prop->name, "CLIPBEGIN"))) {
1806 	(*snode)->prop.clip_begin = smil_get_time_in_seconds(prop->value);
1807 #ifdef DEBUG_SMIL
1808 	printf("clip-begin: %d\n",(*snode)->prop.clip_begin);
1809 #endif
1810       }
1811       else if(!strcasecmp(prop->name, "END")) {
1812 	(*snode)->prop.end = smil_get_time_in_seconds(prop->value);
1813 #ifdef DEBUG_SMIL
1814 	printf("end: %d\n",(*snode)->prop.end);
1815 #endif
1816       }
1817       else if((!strcasecmp(prop->name, "CLIP-END")) || (!strcasecmp(prop->name, "CLIPEND"))) {
1818 	(*snode)->prop.clip_end = smil_get_time_in_seconds(prop->value);
1819 #ifdef DEBUG_SMIL
1820 	printf("clip-end: %d\n",(*snode)->prop.clip_end);
1821 #endif
1822       }
1823       else if(!strcasecmp(prop->name, "DUR")) {
1824 	(*snode)->prop.duration = smil_get_time_in_seconds(prop->value);
1825 #ifdef DEBUG_SMIL
1826 	palign;
1827 	printf("DURATION: %d\n", (*snode)->prop.duration);
1828 #endif
1829       }
1830     }
1831   }
1832 
1833   if(gotmrl) {
1834     smil_node_t *node = (*snode);
1835     int          repeat = node->prop.repeat;
1836 
1837     if((*snode)->mmk->ident == NULL)
1838       (*snode)->mmk->ident = strdup((*snode)->mmk->mrl);
1839 
1840     if(sprop) {
1841       if(sprop->clip_begin && (node->prop.clip_begin == 0))
1842 	node->mmk->start = sprop->clip_begin;
1843       else
1844 	node->mmk->start = node->prop.clip_begin;
1845 
1846       if(sprop->clip_end && (node->prop.clip_end == -1))
1847 	node->mmk->end = sprop->clip_end;
1848       else
1849 	node->mmk->end = node->prop.clip_end;
1850 
1851       if(sprop->duration && (node->prop.duration == 0))
1852       	node->mmk->end = node->mmk->start + sprop->duration;
1853       else
1854       	node->mmk->end = node->mmk->start + node->prop.duration;
1855 
1856     }
1857     else {
1858       node->mmk->start = node->prop.clip_begin;
1859       node->mmk->end = node->prop.clip_end;
1860       if(node->prop.duration)
1861 	node->mmk->end = node->mmk->start + node->prop.duration;
1862     }
1863 
1864     smil_repeat((repeat <= 1) ? 1 : repeat, node, snode, sprop);
1865   }
1866 
1867 #ifdef DEBUG_SMIL
1868   palign;
1869   printf("---\n");
1870   offset -= 2;
1871 #endif
1872 }
1873 
smil_repeat(int times,smil_node_t * from,smil_node_t ** snode,smil_property_t * sprop)1874 static void smil_repeat(int times, smil_node_t *from,
1875 			smil_node_t **snode, smil_property_t *sprop) {
1876   int  i;
1877 
1878   if(times > 1) {
1879     smil_node_t *node, *to;
1880 
1881     to = (*snode);
1882 
1883     for(i = 0; i < (times - 1); i++) {
1884       int found = 0;
1885 
1886       node = from;
1887 
1888       while(!found) {
1889 	smil_node_t *nnode = smil_new_node();
1890 
1891 	nnode->mmk  = smil_duplicate_mmk(node->mmk);
1892 
1893 	(*snode)->next = nnode;
1894 	(*snode) = nnode;
1895 
1896 	if(node == to)
1897 	  found = 1;
1898 
1899 	node = node->next;
1900       }
1901     }
1902   }
1903   else {
1904     smil_node_t *nnode = smil_new_node();
1905 
1906     (*snode)->next = nnode;
1907     (*snode) = (*snode)->next;
1908   }
1909 }
1910 /*
1911  * Sequence playback
1912  */
smil_seq(smil_t * smil,smil_node_t ** snode,xml_node_t * node,smil_property_t * sprop)1913 static void smil_seq(smil_t *smil,
1914 		     smil_node_t **snode, xml_node_t *node, smil_property_t *sprop) {
1915   xml_node_t      *seq;
1916 
1917 #ifdef DEBUG_SMIL
1918   offset += 2;
1919 #endif
1920 
1921   for(seq = node; seq; seq = seq->next) {
1922 
1923     if(!strcasecmp(seq->name, "SEQ")) {
1924       smil_property_t  smil_props;
1925       smil_node_t     *node = (*snode);
1926 
1927       smil_init_smil_property(&smil_props);
1928 
1929 #ifdef DEBUG_SMIL
1930       offset += 2;
1931       palign;
1932       printf("seq NEW SEQ\n");
1933       offset -= 2;
1934 #endif
1935 
1936       smil_get_properties(&smil_props, seq->props);
1937       smil_seq(smil, snode, seq->child, &smil_props);
1938       smil_repeat(smil_props.repeat, node, snode, &smil_props);
1939       smil_free_properties(&smil_props);
1940     }
1941     else if(!strcasecmp(seq->name, "PAR")) {
1942       smil_property_t  smil_props;
1943       smil_node_t     *node = (*snode);
1944 
1945       smil_init_smil_property(&smil_props);
1946 
1947 #ifdef DEBUG_SMIL
1948       offset += 2;
1949       palign;
1950       printf("seq NEW PAR\n");
1951       offset -= 2;
1952 #endif
1953 
1954       smil_get_properties(&smil_props, seq->props);
1955       smil_par(smil, snode, seq->child, &smil_props);
1956       smil_repeat(smil_props.repeat, node, snode, &smil_props);
1957       smil_free_properties(&smil_props);
1958     }
1959     else if(!strcasecmp(seq->name, "SWITCH")) {
1960       smil_property_t  smil_props;
1961       smil_node_t     *node = (*snode);
1962 
1963       smil_init_smil_property(&smil_props);
1964 
1965 #ifdef DEBUG_SMIL
1966       offset += 2;
1967       palign;
1968       printf("seq NEW SWITCH\n");
1969       offset -= 2;
1970 #endif
1971 
1972       smil_get_properties(&smil_props, seq->props);
1973       smil_switch(smil, snode, seq->child, &smil_props);
1974       smil_repeat(smil_props.repeat, node, snode, &smil_props);
1975       smil_free_properties(&smil_props);
1976     }
1977     else if((!strcasecmp(seq->name, "VIDEO")) ||
1978 	    (!strcasecmp(seq->name, "AUDIO")) ||
1979 	    (!strcasecmp(seq->name, "A"))) {
1980       smil_property_t  smil_props;
1981 
1982       smil_init_smil_property(&smil_props);
1983 
1984       if(seq->child) {
1985 	xml_node_t *child = seq->child;
1986 
1987 	while(child) {
1988 
1989 	  if(!strcasecmp(child->name, "ANCHOR")) {
1990 #ifdef DEBUG_SMIL
1991 	    palign;
1992 	    printf("ANCHOR\n");
1993 #endif
1994 	    smil_get_properties(&smil_props, child->props);
1995 	  }
1996 
1997 	  child = child->next;
1998 	}
1999       }
2000 
2001       smil_properties(smil, snode, seq->props, &smil_props);
2002       smil_free_properties(&smil_props);
2003     }
2004   }
2005 #ifdef DEBUG_SMIL
2006   offset -= 2;
2007 #endif
2008 }
2009 /*
2010  * Parallel playback
2011  */
smil_par(smil_t * smil,smil_node_t ** snode,xml_node_t * node,smil_property_t * sprop)2012 static void smil_par(smil_t *smil, smil_node_t **snode, xml_node_t *node, smil_property_t *sprop) {
2013   xml_node_t      *par;
2014 
2015 #ifdef DEBUG_SMIL
2016   offset += 2;
2017 #endif
2018 
2019   for(par = node; par; par = par->next) {
2020 
2021     if(!strcasecmp(par->name, "SEQ")) {
2022       smil_property_t  smil_props;
2023       smil_node_t     *node = (*snode);
2024 
2025       smil_init_smil_property(&smil_props);
2026 
2027 #ifdef DEBUG_SMIL
2028       offset += 2;
2029       palign;
2030       printf("par NEW SEQ\n");
2031       offset -= 2;
2032 #endif
2033 
2034       smil_get_properties(&smil_props, par->props);
2035       smil_seq(smil, snode, par->child, &smil_props);
2036       smil_repeat(smil_props.repeat, node, snode, &smil_props);
2037       smil_free_properties(&smil_props);
2038     }
2039     else if(!strcasecmp(par->name, "PAR")) {
2040       smil_property_t  smil_props;
2041       smil_node_t     *node = (*snode);
2042 
2043       smil_init_smil_property(&smil_props);
2044 
2045 #ifdef DEBUG_SMIL
2046       offset += 2;
2047       palign;
2048       printf("par NEW PAR\n");
2049       offset -= 2;
2050 #endif
2051 
2052       smil_get_properties(&smil_props, par->props);
2053       smil_par(smil, snode, par->child, &smil_props);
2054       smil_repeat(smil_props.repeat, node, snode, &smil_props);
2055       smil_free_properties(&smil_props);
2056     }
2057     else if(!strcasecmp(par->name, "SWITCH")) {
2058       smil_property_t  smil_props;
2059       smil_node_t     *node = (*snode);
2060 
2061       smil_init_smil_property(&smil_props);
2062 
2063 #ifdef DEBUG_SMIL
2064       offset += 2;
2065       palign;
2066       printf("par NEW SWITCH\n");
2067       offset -= 2;
2068 #endif
2069 
2070       smil_get_properties(&smil_props, par->props);
2071       smil_switch(smil, snode, par->child, &smil_props);
2072       smil_repeat(smil_props.repeat, node, snode, &smil_props);
2073       smil_free_properties(&smil_props);
2074     }
2075     else if((!strcasecmp(par->name, "VIDEO")) ||
2076 	    (!strcasecmp(par->name, "AUDIO")) ||
2077 	    (!strcasecmp(par->name, "A"))) {
2078       smil_property_t  smil_props;
2079 
2080       smil_init_smil_property(&smil_props);
2081 
2082       if(par->child) {
2083 	xml_node_t *child = par->child;
2084 
2085 	while(child) {
2086 	  if(!strcasecmp(child->name, "ANCHOR")) {
2087 #ifdef DEBUG_SMIL
2088 	    palign;
2089 	    printf("ANCHOR\n");
2090 #endif
2091 	    smil_get_properties(&smil_props, child->props);
2092 	  }
2093 	  child = child->next;
2094 	}
2095       }
2096 
2097       smil_properties(smil, snode, par->props, &smil_props);
2098       smil_free_properties(&smil_props);
2099     }
2100   }
2101 
2102 #ifdef DEBUG_SMIL
2103   offset -= 2;
2104 #endif
2105 }
smil_switch(smil_t * smil,smil_node_t ** snode,xml_node_t * node,smil_property_t * sprop)2106 static void smil_switch(smil_t *smil, smil_node_t **snode, xml_node_t *node, smil_property_t *sprop) {
2107   xml_node_t      *nswitch;
2108 
2109 #ifdef DEBUG_SMIL
2110   offset += 2;
2111 #endif
2112 
2113   for(nswitch = node; nswitch; nswitch = nswitch->next) {
2114 
2115     if(!strcasecmp(nswitch->name, "SEQ")) {
2116       smil_property_t  smil_props;
2117       smil_node_t     *node = (*snode);
2118 
2119       smil_init_smil_property(&smil_props);
2120 
2121 #ifdef DEBUG_SMIL
2122       offset += 2;
2123       palign;
2124       printf("switch NEW SEQ\n");
2125       offset -= 2;
2126 #endif
2127 
2128       smil_get_properties(&smil_props, nswitch->props);
2129       smil_seq(smil, snode, nswitch->child, &smil_props);
2130       smil_repeat(smil_props.repeat, node, snode, &smil_props);
2131       smil_free_properties(&smil_props);
2132     }
2133     else if(!strcasecmp(nswitch->name, "PAR")) {
2134       smil_property_t  smil_props;
2135       smil_node_t     *node = (*snode);
2136 
2137       smil_init_smil_property(&smil_props);
2138 
2139 #ifdef DEBUG_SMIL
2140       offset += 2;
2141       palign;
2142       printf("switch NEW PAR\n");
2143       offset -= 2;
2144 #endif
2145 
2146       smil_get_properties(&smil_props, nswitch->props);
2147       smil_par(smil, snode, nswitch->child, &smil_props);
2148       smil_repeat(smil_props.repeat, node, snode, &smil_props);
2149       smil_free_properties(&smil_props);
2150     }
2151     else if((!strcasecmp(nswitch->name, "VIDEO")) ||
2152 	    (!strcasecmp(nswitch->name, "AUDIO")) ||
2153 	    (!strcasecmp(nswitch->name, "A"))) {
2154       smil_property_t  smil_props;
2155 
2156       smil_init_smil_property(&smil_props);
2157 
2158       if(nswitch->child) {
2159 	xml_node_t *child = nswitch->child;
2160 
2161 	while(child) {
2162 
2163 	  if(!strcasecmp(child->name, "ANCHOR")) {
2164 #ifdef DEBUG_SMIL
2165 	    palign;
2166 	    printf("ANCHOR\n");
2167 #endif
2168 	    smil_get_properties(&smil_props, child->props);
2169 	  }
2170 
2171 	  child = child->next;
2172 	}
2173       }
2174 
2175       smil_properties(smil, snode, nswitch->props, &smil_props);
2176       smil_free_properties(&smil_props);
2177     }
2178   }
2179 #ifdef DEBUG_SMIL
2180   offset -= 2;
2181 #endif
2182 }
2183 
smil_add_mmk(int * num,mediamark_t *** mmk,smil_node_t * node)2184 static void smil_add_mmk(int *num, mediamark_t ***mmk, smil_node_t *node) {
2185   if(node->mmk) {
2186     (*mmk) = (mediamark_t **) realloc((*mmk), sizeof(mediamark_t *) * (*num + 2));
2187 
2188     (*mmk)[*num] = node->mmk;
2189     *num += 1;
2190   }
2191 
2192   if(node->next)
2193     smil_add_mmk(num, mmk, node->next);
2194 }
smil_fill_mmk(smil_t * smil,mediamark_t *** mmk)2195 static int smil_fill_mmk(smil_t *smil, mediamark_t ***mmk) {
2196   int num = 0;
2197 
2198   if(smil->first)
2199     smil_add_mmk(&num, mmk, smil->first);
2200 
2201   return num;
2202 }
2203 
smil_free_node(smil_node_t * node)2204 static void smil_free_node(smil_node_t *node) {
2205   if(node->next)
2206     smil_free_node(node->next);
2207 
2208   /* mmk shouldn't be fried */
2209   smil_free_properties(&(node->prop));
2210   free(node);
2211 }
smil_free_smil(smil_t * smil)2212 static void smil_free_smil(smil_t *smil) {
2213   if(smil->first)
2214     smil_free_node(smil->first);
2215 
2216   SAFE_FREE(smil->title);
2217   SAFE_FREE(smil->author);
2218   SAFE_FREE(smil->copyright);
2219   SAFE_FREE(smil->year);
2220   SAFE_FREE(smil->base);
2221 }
2222 
xml_smil_playlist(playlist_t * playlist,const char * filename,char * xml_content,xml_node_t * xml_tree)2223 static mediamark_t **xml_smil_playlist(playlist_t *playlist, const char *filename, char *xml_content, xml_node_t *xml_tree) {
2224   mediamark_t **mmk = NULL;
2225 
2226   if(xml_tree) {
2227     xml_node_t      *smil_entry, *smil_ref;
2228     smil_t           smil;
2229     int              entries_smil = 0;
2230 
2231     memset(&smil, 0, sizeof(smil_t));
2232 
2233     if(!strcasecmp(xml_tree->name, "SMIL")) {
2234 
2235       smil.first = smil.node = smil_new_node();
2236       smil_entry = xml_tree->child;
2237 
2238       while(smil_entry) {
2239 
2240 #ifdef DEBUG_SMIL
2241 	printf("smil_entry '%s'\n", smil_entry->name);
2242 #endif
2243 	if(!strcasecmp(smil_entry->name, "HEAD")) {
2244 #ifdef DEBUG_SMIL
2245 	  printf("+---------+\n");
2246 	  printf("| IN HEAD |\n");
2247 	  printf("+---------+\n");
2248 #endif
2249 	  smil_ref = smil_entry->child;
2250 	  while(smil_ref) {
2251 #ifdef DEBUG_SMIL
2252 	    printf("  smil_ref '%s'\n", smil_ref->name);
2253 #endif
2254 	    smil_header(&smil, smil_ref->props);
2255 
2256 	    smil_ref = smil_ref->next;
2257 	  }
2258 	}
2259 	else if(!strcasecmp(smil_entry->name, "BODY")) {
2260 #ifdef DEBUG_SMIL
2261 	  printf("+---------+\n");
2262 	  printf("| IN BODY |\n");
2263 	  printf("+---------+\n");
2264 #endif
2265 	__kino:
2266 	  smil_ref = smil_entry->child;
2267 	  while(smil_ref) {
2268 #ifdef DEBUG_SMIL
2269 	    printf("  smil_ref '%s'\n", smil_ref->name);
2270 #endif
2271 	    if(!strcasecmp(smil_ref->name, "SEQ")) {
2272 	      smil_property_t  smil_props;
2273 	      smil_node_t     *node = smil.node;
2274 
2275 #ifdef DEBUG_SMIL
2276 	      palign;
2277 	      printf("SEQ Found\n");
2278 #endif
2279 	      smil_init_smil_property(&smil_props);
2280 
2281 	      smil_get_properties(&smil_props, smil_ref->props);
2282 	      smil_seq(&smil, &(smil.node), smil_ref->child, &smil_props);
2283 	      smil_repeat(smil_props.repeat, node, &(smil.node), &smil_props);
2284 	      smil_free_properties(&smil_props);
2285 	    }
2286 	    else if(!strcasecmp(smil_ref->name, "PAR")) {
2287 	      smil_property_t  smil_props;
2288 	      smil_node_t     *node = smil.node;
2289 
2290 #ifdef DEBUG_SMIL
2291 	      palign;
2292 	      printf("PAR Found\n");
2293 #endif
2294 
2295 	      smil_init_smil_property(&smil_props);
2296 
2297 	      smil_get_properties(&smil_props, smil_ref->props);
2298 	      smil_par(&smil, &(smil.node), smil_ref->child, &smil_props);
2299 	      smil_repeat(smil_props.repeat, node, &(smil.node), &smil_props);
2300 	      smil_free_properties(&smil_props);
2301 	    }
2302 	    else if(!strcasecmp(smil_ref->name, "A")) {
2303 #ifdef DEBUG_SMIL
2304 	      palign;
2305 	      printf("  IN A\n");
2306 #endif
2307 	      smil_properties(&smil, &(smil.node), smil_ref->props, NULL);
2308 	    }
2309 	    else if(!strcasecmp(smil_ref->name, "SWITCH")) {
2310 	      smil_property_t  smil_props;
2311 	      smil_node_t     *node = smil.node;
2312 
2313 #ifdef DEBUG_SMIL
2314 	      palign;
2315 	      printf("SWITCH Found\n");
2316 #endif
2317 
2318 	      smil_init_smil_property(&smil_props);
2319 
2320 	      smil_get_properties(&smil_props, smil_ref->props);
2321 	      smil_switch(&smil, &(smil.node), smil_ref->child, &smil_props);
2322 	      smil_repeat(smil_props.repeat, node, &(smil.node), &smil_props);
2323 	      smil_free_properties(&smil_props);
2324 	    }
2325 	    else {
2326 	      smil_properties(&smil, &smil.node, smil_ref->props, NULL);
2327 	    }
2328 
2329 	    smil_ref = smil_ref->next;
2330 	  }
2331 	}
2332 	else if(!strcasecmp(smil_entry->name, "SEQ")) { /* smil2, kino format(no body) */
2333 	  goto __kino;
2334 	}
2335 
2336 	smil_entry = smil_entry->next;
2337       }
2338 
2339 #ifdef DEBUG_SMIL
2340       printf("DUMPING TREE:\n");
2341       printf("-------------\n");
2342       smil_dump_tree(smil.first);
2343 
2344       printf("DUMPING HEADER:\n");
2345       printf("---------------\n");
2346       smil_dump_header(&smil);
2347 #endif
2348 
2349       entries_smil = smil_fill_mmk(&smil, &mmk);
2350       smil_free_smil(&smil);
2351     }
2352 #ifdef DEBUG
2353     else
2354       fprintf(stderr, "%s(): Unsupported XML type: '%s'.\n", __XINE_FUNCTION__, xml_tree->name);
2355 #endif
2356 
2357     if(entries_smil) {
2358       mmk[entries_smil] = NULL;
2359       playlist->entries = entries_smil;
2360       playlist->type    = strdup("SMIL");
2361       return mmk;
2362     }
2363   }
2364 
2365   return NULL;
2366 }
2367 /*
2368  * ********************************** SMIL END ***********************************
2369  */
2370 
xml_freevo_playlist(playlist_t * playlist,const char * filename,char * xml_content,xml_node_t * xml_tree)2371 static mediamark_t **xml_freevo_playlist(playlist_t *playlist, const char *filename, char *xml_content, xml_node_t *xml_tree) {
2372   mediamark_t **mmk = NULL;
2373 
2374   if(xml_tree) {
2375     xml_node_t      *fvo_entry;
2376     xml_property_t  *fvo_prop;
2377     int              entries_fvo = 0;
2378 
2379     if(!strcasecmp(xml_tree->name, "FREEVO")) {
2380       char *origin;
2381       char *url    = NULL;
2382       char *sub    = NULL;
2383       char *title  = NULL;
2384 
2385       origin = get_basedir(filename);
2386 
2387       fvo_entry = xml_tree->child;
2388 
2389       while(fvo_entry) {
2390 	if(!strcasecmp(fvo_entry->name, "MOVIE")) {
2391 	  xml_node_t *sentry;
2392 
2393 	  for(fvo_prop = fvo_entry->props; fvo_prop; fvo_prop = fvo_prop->next) {
2394 	    if(!strcasecmp(fvo_prop->name, "TITLE")) {
2395 	      title = fvo_prop->value;
2396 	    }
2397 	  }
2398 
2399 	  sentry = fvo_entry->child;
2400 	  while(sentry) {
2401 
2402 	    if((!strcasecmp(sentry->name, "VIDEO")) || (!strcasecmp(sentry->name, "AUDIO"))) {
2403 	      xml_node_t *ssentry = sentry->child;
2404 
2405 	      while(ssentry) {
2406 
2407 		if(!strcasecmp(ssentry->name, "SUBTITLE")) {
2408 		  sub = ssentry->data;
2409 		}
2410 		else if((!strcasecmp(ssentry->name, "URL")) ||
2411 			(!strcasecmp(ssentry->name, "DVD")) || (!strcasecmp(ssentry->name, "VCD"))) {
2412 		  url = strdup(ssentry->data);
2413 		}
2414 		else if(!strcasecmp(ssentry->name, "FILE")) {
2415 
2416 		  if(origin) {
2417 		    const size_t urlsize = strlen(origin) + strlen(ssentry->data) + 2;
2418 		    url = (char *) malloc(urlsize);
2419 		    strlcat(url, origin, urlsize);
2420 
2421 		    if((url[strlen(url) - 1] == '/') && (*ssentry->data == '/'))
2422 		      url[strlen(url) - 1] = '\0';
2423 
2424 		    strlcat(url, ssentry->data, urlsize);
2425 
2426 		  }
2427 		  else
2428 		    url = strdup(ssentry->data);
2429 
2430 		}
2431 
2432 		if(url) {
2433 
2434 		  mmk = (mediamark_t **) realloc(mmk, sizeof(mediamark_t *) * (entries_fvo + 2));
2435 
2436 		  mediamark_store_mmk(&mmk[entries_fvo], url, title, sub, 0, -1, 0, 0);
2437 		  playlist->entries = ++entries_fvo;
2438 
2439 		  free(url);
2440 		  url = NULL;
2441 		}
2442 
2443 		sub     = NULL;
2444 		ssentry = ssentry->next;
2445 	      }
2446 
2447 	    }
2448 
2449 	    sentry = sentry->next;
2450 	  }
2451 
2452 	}
2453 
2454 	title     = NULL;
2455 	fvo_entry = fvo_entry->next;
2456       }
2457       free(origin);
2458     }
2459 #ifdef DEBUG
2460     else
2461       fprintf(stderr, "%s(): Unsupported XML type: '%s'.\n", __XINE_FUNCTION__, xml_tree->name);
2462 #endif
2463 
2464     if(entries_fvo) {
2465       mmk[entries_fvo] = NULL;
2466       playlist->type = strdup("FREEVO");
2467       return mmk;
2468     }
2469   }
2470 
2471   return NULL;
2472 }
2473 
guess_xml_based_playlist(playlist_t * playlist,const char * filename)2474 static mediamark_t **guess_xml_based_playlist(playlist_t *playlist, const char *filename) {
2475   mediamark_t **mmk = NULL;
2476 
2477   if(filename) {
2478     char            *xml_content;
2479     int              size;
2480     int              result;
2481     xml_node_t      *xml_tree, *top_xml_tree;
2482 
2483     if((xml_content = _read_file(filename, &size)) != NULL) {
2484       int                         i;
2485       playlist_xml_guess_func_t   guess_functions[] = {
2486 	xml_asx_playlist,
2487 	xml_gx_playlist,
2488 	xml_noatun_playlist,
2489 	xml_smil_playlist,
2490 	xml_freevo_playlist,
2491 	NULL
2492       };
2493 
2494       xml_parser_init_R(xml_parser_t *xml, xml_content, size, XML_PARSER_CASE_INSENSITIVE);
2495       if((result = xml_parser_build_tree_R(xml, &xml_tree)) != XML_PARSER_OK)
2496 	goto __failure;
2497 
2498       top_xml_tree = xml_tree;
2499 
2500       /* Check all playlists */
2501       for(i = 0; guess_functions[i]; i++) {
2502 	if((mmk = guess_functions[i](playlist, filename, xml_content, xml_tree)))
2503 	  break;
2504       }
2505 
2506       xml_parser_free_tree(top_xml_tree);
2507     __failure:
2508 
2509       xml_parser_finalize_R(xml);
2510       free(xml_content);
2511     }
2512   }
2513 
2514   return mmk;
2515 }
2516 /*
2517  * XML based playlists end.
2518  */
2519 
2520 /*
2521  * Public
2522  */
mediamark_get_entry_from_id(const char * ident)2523 int mediamark_get_entry_from_id(const char *ident) {
2524   gGui_t *gui = gGui;
2525   if(ident && gui->playlist.num) {
2526     int i;
2527 
2528     for(i = 0; i < gui->playlist.num; i++) {
2529       if(!strcasecmp(ident, gui->playlist.mmk[i]->ident))
2530 	return i;
2531     }
2532   }
2533   return -1;
2534 }
2535 
mediamark_insert_entry(int index,const char * mrl,const char * ident,const char * sub,int start,int end,int av_offset,int spu_offset)2536 void mediamark_insert_entry(int index, const char *mrl, const char *ident,
2537 			    const char *sub, int start, int end, int av_offset, int spu_offset) {
2538   gGui_t *gui = gGui;
2539   char  autosub[2*XITK_PATH_MAX + XITK_NAME_MAX + 2];
2540   char  subpath[XITK_PATH_MAX + XITK_NAME_MAX + 2];
2541   DIR           *dir;
2542   struct dirent *dentry;
2543 
2544 
2545   gui->playlist.mmk = (mediamark_t **) realloc(gui->playlist.mmk, sizeof(mediamark_t *) * (gui->playlist.num + 2));
2546 
2547   if(index < gui->playlist.num)
2548     memmove(&gui->playlist.mmk[index+1], &gui->playlist.mmk[index],
2549 	    (gui->playlist.num - index) * sizeof(gui->playlist.mmk[0]) );
2550 
2551   /*
2552    * If subtitle_autoload is enabled and subtitle is NULL
2553    * then try to see if a matching subtitle exist
2554    */
2555   if(mrl && (!sub) && gui->subtitle_autoload) {
2556 
2557     if(mrl_look_like_file((char *) mrl)) {
2558       char        *know_subs = "sub,srt,asc,smi,ssa,ass,txt";
2559       char        *vsubs, *pvsubs;
2560       char        *_mrl, *ending, *ext, *path, *d_name;
2561       struct stat  pstat;
2562 
2563       _mrl = (char *) mrl;
2564       if(!strncasecmp(_mrl, "file:", 5))
2565 	_mrl += 5;
2566 
2567       strlcpy(autosub, _mrl, sizeof(autosub));
2568 
2569       if((ending = strrchr(autosub, '.')))
2570 	ending++;
2571       else {
2572 	ending = autosub + strlen(autosub);
2573 	*ending++ = '.';
2574       }
2575 
2576       vsubs = strdup(know_subs);
2577 
2578       pvsubs = vsubs;
2579       while((ext = xine_strsep(&pvsubs, ",")) && !sub) {
2580 	strcpy(ending, ext);
2581 	*(ending + strlen(ext) + 1) = '\0';
2582 
2583 	if(((stat(autosub, &pstat)) > -1) && (S_ISREG(pstat.st_mode)) && strcmp(autosub, _mrl))
2584 	  sub = autosub;
2585       }
2586       free(vsubs);
2587 
2588       /* try matching "<name>.*.<know_subs>" like used by opensubtitles */
2589       if( !sub ) {
2590         *ending = '\0';
2591 
2592         strlcpy(subpath, autosub, sizeof(subpath));
2593 
2594         if((d_name = strrchr(subpath, '/'))) {
2595           *d_name++ = '\0';
2596           path = subpath;
2597         } else {
2598           d_name = subpath;
2599           path = ".";
2600         }
2601 
2602         if((dir = opendir(path))) {
2603           while((dentry = readdir(dir))) {
2604             if( (strncmp(dentry->d_name, d_name, strlen(d_name)) == 0) &&
2605                 (ending = strrchr(dentry->d_name, '.')) ) {
2606 
2607               if( strstr(know_subs, ending+1) ) {
2608                 snprintf(autosub,sizeof(autosub),"%s/%s",path,dentry->d_name);
2609                 autosub[sizeof(autosub)-1]='\0';
2610                 if(((stat(autosub, &pstat)) > -1) && (S_ISREG(pstat.st_mode)) && strcmp(autosub, _mrl)) {
2611                   sub = autosub;
2612                   break;
2613                 }
2614               }
2615             }
2616           }
2617           closedir(dir);
2618         }
2619       }
2620     }
2621 
2622   }
2623 
2624   if(mediamark_store_mmk(&gui->playlist.mmk[index],
2625   			 mrl, ident, sub, start, end, av_offset, spu_offset))
2626     gui->playlist.num++;
2627 }
2628 
mediamark_append_entry(const char * mrl,const char * ident,const char * sub,int start,int end,int av_offset,int spu_offset)2629 void mediamark_append_entry(const char *mrl, const char *ident,
2630 			    const char *sub, int start, int end, int av_offset, int spu_offset) {
2631   gGui_t *gui = gGui;
2632 
2633   mediamark_insert_entry(gui->playlist.num, mrl, ident, sub, start, end, av_offset, spu_offset);
2634 }
2635 
mediamark_free_mediamarks(void)2636 void mediamark_free_mediamarks(void) {
2637   gGui_t *gui = gGui;
2638 
2639   if(gui->playlist.num) {
2640     int i;
2641 
2642     for(i = 0; i < gui->playlist.num; i++)
2643       mediamark_free_entry(i);
2644 
2645     SAFE_FREE(gui->playlist.mmk);
2646     gui->playlist.num = 0;
2647     gui->playlist.cur = -1;
2648   }
2649 }
2650 
mediamark_reset_played_state(void)2651 void mediamark_reset_played_state(void) {
2652   gGui_t *gui = gGui;
2653 
2654   if(gui->playlist.num) {
2655     int i;
2656 
2657     for(i = 0; i < gui->playlist.num; i++)
2658       gui->playlist.mmk[i]->played = 0;
2659   }
2660 }
2661 
mediamark_all_played(void)2662 int mediamark_all_played(void) {
2663   gGui_t *gui = gGui;
2664 
2665   if(gui->playlist.num) {
2666     int i;
2667 
2668     for(i = 0; i < gui->playlist.num; i++) {
2669       if(gui->playlist.mmk[i]->played == 0)
2670 	return 0;
2671     }
2672   }
2673 
2674   return 1;
2675 }
2676 
mediamark_get_shuffle_next(void)2677 int mediamark_get_shuffle_next(void) {
2678   gGui_t *gui = gGui;
2679   int  next = 0;
2680 
2681   if(gui->playlist.num >= 3 && gui->playlist.cur >= 0) {
2682     int    remain = gui->playlist.num;
2683     int    entries[remain];
2684     float  num = (float) gui->playlist.num;
2685     int    found = 0;
2686 
2687     memset(&entries, 0, sizeof(int) * remain);
2688 
2689     srandom((unsigned int)time(NULL));
2690     entries[gui->playlist.cur] = 1;
2691 
2692     do {
2693       next = (int) (num * random() / RAND_MAX);
2694 
2695       if(next != gui->playlist.cur) {
2696 
2697 	if(gui->playlist.mmk[next]->played == 0)
2698 	  found = 1;
2699 	else if(entries[next] == 0) {
2700 	  entries[next] = 1;
2701 	  remain--;
2702 	}
2703 
2704 	if((!found) && (!remain))
2705 	  found = 2; /* No more choice */
2706 
2707       }
2708 
2709     } while(!found);
2710 
2711     if(found == 2)
2712       next = gui->playlist.cur;
2713 
2714   }
2715   else if(gui->playlist.num == 2)
2716     next = !gui->playlist.cur;
2717 
2718   return next;
2719 }
2720 
mediamark_replace_entry(mediamark_t ** mmk,const char * mrl,const char * ident,const char * sub,int start,int end,int av_offset,int spu_offset)2721 void mediamark_replace_entry(mediamark_t **mmk,
2722 			     const char *mrl, const char *ident, const char *sub,
2723 			     int start, int end, int av_offset, int spu_offset) {
2724   SAFE_FREE((*mmk)->mrl);
2725   SAFE_FREE((*mmk)->ident);
2726   SAFE_FREE((*mmk)->sub);
2727 
2728   mediamark_free_alternates((*mmk));
2729   (*mmk)->start      = 0;
2730   (*mmk)->end        = -1;
2731   (*mmk)->av_offset  = 0;
2732   (*mmk)->spu_offset = 0;
2733 
2734 
2735   (void) mediamark_store_mmk(mmk, mrl, ident, sub, start, end, av_offset, spu_offset);
2736 }
2737 
mediamark_get_current_mmk(void)2738 mediamark_t *mediamark_get_current_mmk(void) {
2739   gGui_t *gui = gGui;
2740 
2741   if(gui->playlist.num && gui->playlist.cur >= 0 && gui->playlist.mmk &&
2742      gui->playlist.mmk[gui->playlist.cur])
2743     return gui->playlist.mmk[gui->playlist.cur];
2744 
2745   return (mediamark_t *) NULL;
2746 }
2747 
mediamark_get_mmk_by_index(int index)2748 mediamark_t *mediamark_get_mmk_by_index(int index) {
2749   gGui_t *gui = gGui;
2750 
2751   if(index < gui->playlist.num && index >= 0 && gui->playlist.mmk &&
2752      gui->playlist.mmk[index])
2753     return gui->playlist.mmk[index];
2754 
2755   return (mediamark_t *) NULL;
2756 }
2757 
mediamark_get_current_mrl(void)2758 const char *mediamark_get_current_mrl(void) {
2759   gGui_t *gui = gGui;
2760 
2761   if(gui->playlist.num && gui->playlist.cur >= 0 && gui->playlist.mmk &&
2762      gui->playlist.mmk[gui->playlist.cur] &&
2763      gui->playlist.cur < gui->playlist.num)
2764     return gui->playlist.mmk[gui->playlist.cur]->mrl;
2765 
2766   return NULL;
2767 }
2768 
mediamark_get_current_ident(void)2769 const char *mediamark_get_current_ident(void) {
2770   gGui_t *gui = gGui;
2771 
2772   if(gui->playlist.num && gui->playlist.cur >= 0 && gui->playlist.mmk &&
2773      gui->playlist.mmk[gui->playlist.cur])
2774     return gui->playlist.mmk[gui->playlist.cur]->ident;
2775 
2776   return NULL;
2777 }
2778 
mediamark_get_current_sub(void)2779 const char *mediamark_get_current_sub(void) {
2780   gGui_t *gui = gGui;
2781 
2782   if(gui->playlist.num && gui->playlist.cur >= 0 && gui->playlist.mmk &&
2783      gui->playlist.mmk[gui->playlist.cur])
2784     return gui->playlist.mmk[gui->playlist.cur]->sub;
2785 
2786   return NULL;
2787 }
2788 
mediamark_free_entry(int offset)2789 void mediamark_free_entry(int offset) {
2790   gGui_t *gui = gGui;
2791 
2792   if(offset < gui->playlist.num && offset >= 0 && gui->playlist.mmk &&
2793      gui->playlist.mmk[offset])
2794     if(mediamark_free_mmk(&gui->playlist.mmk[offset]))
2795       gui->playlist.num--;
2796 }
2797 
mediamark_concat_mediamarks(const char * _filename)2798 int mediamark_concat_mediamarks(const char *_filename) {
2799   gGui_t *gui = gGui;
2800   playlist_t             *playlist;
2801   int                     i;
2802   mediamark_t           **mmk = NULL;
2803   const char             *filename = _filename;
2804   playlist_guess_func_t   guess_functions[] = {
2805     guess_xml_based_playlist,
2806     guess_toxine_playlist,
2807     guess_pls_playlist,
2808     guess_m3u_playlist,
2809     guess_sfv_playlist,
2810     guess_raw_playlist,
2811     NULL
2812   };
2813 
2814   if(_filename) {
2815     if(!strncasecmp("file:/", _filename, 6))
2816       filename = (_filename + 6);
2817   }
2818 
2819   playlist = (playlist_t *) calloc(1, sizeof(playlist_t));
2820 
2821   for(i = 0; guess_functions[i]; i++) {
2822     if((mmk = guess_functions[i](playlist, filename)))
2823       break;
2824   }
2825 
2826   if(mmk) {
2827 #ifdef DEBUG
2828     printf("Playlist file (%s) is valid (%s).\n", filename, playlist->type);
2829 #endif
2830   }
2831   else {
2832     fprintf(stderr, _("Playlist file (%s) is invalid.\n"), filename);
2833     SAFE_FREE(playlist);
2834     return 0;
2835   }
2836 
2837   gui->playlist.cur = gui->playlist.num;
2838 
2839   for(i = 0; i < playlist->entries; i++)
2840     mediamark_append_entry(mmk[i]->mrl, mmk[i]->ident, mmk[i]->sub,
2841 			   mmk[i]->start, mmk[i]->end, mmk[i]->av_offset, mmk[i]->spu_offset);
2842 
2843   for(i = 0; i < playlist->entries; i++)
2844     (void) mediamark_free_mmk(&mmk[i]);
2845 
2846   SAFE_FREE(mmk);
2847   SAFE_FREE(playlist->type);
2848   SAFE_FREE(playlist);
2849 
2850   return 1;
2851 }
2852 
mediamark_load_mediamarks(const char * _filename)2853 void mediamark_load_mediamarks(const char *_filename) {
2854   gGui_t *gui = gGui;
2855   playlist_t             *playlist;
2856   int                     i, onum;
2857   mediamark_t           **mmk = NULL;
2858   mediamark_t           **ommk;
2859   const char             *filename = _filename;
2860   playlist_guess_func_t   guess_functions[] = {
2861     guess_xml_based_playlist,
2862     guess_toxine_playlist,
2863     guess_pls_playlist,
2864     guess_m3u_playlist,
2865     guess_sfv_playlist,
2866     guess_raw_playlist,
2867     NULL
2868   };
2869 
2870   if(_filename) {
2871     if(!strncasecmp("file:/", _filename, 6))
2872       filename = (_filename + 6);
2873   }
2874 
2875   playlist = (playlist_t *) calloc(1, sizeof(playlist_t));
2876 
2877   for(i = 0; guess_functions[i]; i++) {
2878     if((mmk = guess_functions[i](playlist, filename)))
2879       break;
2880   }
2881 
2882   if(mmk) {
2883 #ifdef DEBUG
2884     printf("Playlist file (%s) is valid (%s).\n", filename, playlist->type);
2885 #endif
2886   }
2887   else {
2888     fprintf(stderr, _("Playlist file (%s) is invalid.\n"), filename);
2889     SAFE_FREE(playlist);
2890     return;
2891   }
2892 
2893   ommk = gui->playlist.mmk;
2894   onum = gui->playlist.num;
2895 
2896   gui->playlist.mmk = mmk;
2897   gui->playlist.num = playlist->entries;
2898 
2899   if(gui->playlist.loop == PLAYLIST_LOOP_SHUFFLE)
2900     gui->playlist.cur = mediamark_get_shuffle_next();
2901   else
2902     gui->playlist.cur = 0;
2903 
2904   for(i = 0; i < onum; i++)
2905     (void) mediamark_free_mmk(&ommk[i]);
2906 
2907   SAFE_FREE(ommk);
2908 
2909   SAFE_FREE(playlist->type);
2910   SAFE_FREE(playlist);
2911 }
2912 
mediamark_save_mediamarks(const char * filename)2913 void mediamark_save_mediamarks(const char *filename) {
2914   gGui_t *gui = gGui;
2915   char *fullfn;
2916   char *pn;
2917   char *fn;
2918   int   status = 1;
2919 
2920   if(!gui->playlist.num)
2921     return;
2922 
2923   fullfn = strdup(filename);
2924 
2925   pn = fullfn;
2926 
2927   fn = strrchr(fullfn, '/');
2928 
2929   if(fn) {
2930     *fn = '\0';
2931     fn++;
2932     status = mkdir_safe(pn);
2933   }
2934   else
2935     fn = pn;
2936 
2937   if(status && fn) {
2938     int   i;
2939     FILE *fd;
2940     const char *store_item;
2941     char buffer[_PATH_MAX + _NAME_MAX + 2], current_dir[_PATH_MAX];
2942 
2943     if (getcwd(current_dir, sizeof(current_dir)) > 0) {
2944       if (current_dir[strlen(current_dir) - 1] != '/')
2945         strlcat(current_dir, "/", sizeof(current_dir));
2946     } else
2947       strcpy(current_dir, "");
2948 
2949     if((fd = fopen(filename, "w")) != NULL) {
2950 
2951       fprintf(fd, "# toxine playlist\n");
2952 
2953       for(i = 0; i < gui->playlist.num; i++) {
2954 	fprintf(fd, "\nentry {\n");
2955 	fprintf(fd, "\tidentifier = %s;\n", gui->playlist.mmk[i]->ident);
2956 	store_item = concat_basedir(buffer, sizeof(buffer), current_dir, gui->playlist.mmk[i]->mrl);
2957 	fprintf(fd, "\tmrl = %s;\n", store_item);
2958 	if(gui->playlist.mmk[i]->sub) {
2959 	  store_item = concat_basedir(buffer, sizeof(buffer), current_dir, gui->playlist.mmk[i]->sub);
2960 	  fprintf(fd, "\tsubtitle = %s;\n", store_item);
2961 	}
2962 	if(gui->playlist.mmk[i]->start > 0)
2963 	  fprintf(fd, "\tstart = %d;\n", gui->playlist.mmk[i]->start);
2964 	if(gui->playlist.mmk[i]->end != -1)
2965 	  fprintf(fd, "\tend = %d;\n", gui->playlist.mmk[i]->end);
2966 	if(gui->playlist.mmk[i]->av_offset != 0)
2967 	  fprintf(fd, "\tav_offset = %d;\n", gui->playlist.mmk[i]->av_offset);
2968 	if(gui->playlist.mmk[i]->spu_offset != 0)
2969 	  fprintf(fd, "\tspu_offset = %d;\n", gui->playlist.mmk[i]->spu_offset);
2970 	fprintf(fd,"};\n");
2971       }
2972 
2973       fprintf(fd, "# END\n");
2974 
2975       fclose(fd);
2976 #ifdef DEBUG
2977       printf("Playlist '%s' saved.\n", filename);
2978 #endif
2979     }
2980     else
2981       fprintf(stderr, _("Unable to save playlist (%s): %s.\n"), filename, strerror(errno));
2982 
2983   }
2984 
2985   free(fullfn);
2986 }
2987 
mrl_look_like_file(char * mrl)2988 int mrl_look_like_file(char *mrl) {
2989 
2990   if(mrl && strlen(mrl)) {
2991     if((strncasecmp(mrl, "file:", 5)) &&
2992        strstr (mrl, ":/") && (strstr (mrl, ":/") < strchr(mrl, '/')))
2993       return 0;
2994   }
2995 
2996   return 1;
2997 }
2998 
mediamark_collect_from_directory(char * filepathname)2999 void mediamark_collect_from_directory(char *filepathname) {
3000   DIR           *dir;
3001   struct dirent *dentry;
3002 
3003   if((dir = opendir(filepathname))) {
3004 
3005     while((dentry = readdir(dir))) {
3006       char fullpathname[XITK_PATH_MAX + XITK_NAME_MAX + 2] = "";
3007 
3008       snprintf(fullpathname, sizeof(fullpathname) - 1, "%s/%s", filepathname, dentry->d_name);
3009 
3010       if(strlen(fullpathname)) {
3011 
3012 	if(fullpathname[strlen(fullpathname) - 1] == '/')
3013 	  fullpathname[strlen(fullpathname) - 1] = '\0';
3014 
3015 	if(is_a_dir(fullpathname)) {
3016 	  if(!((strlen(dentry->d_name) == 1) && (dentry->d_name[0] == '.'))
3017 	     && !((strlen(dentry->d_name) == 2) &&
3018 		  ((dentry->d_name[0] == '.') && dentry->d_name[1] == '.'))) {
3019 	    mediamark_collect_from_directory(fullpathname);
3020 	  }
3021 	}
3022 	else {
3023 	  char *p, *extension;
3024 	  char  loname[XITK_PATH_MAX + XITK_NAME_MAX + 2] = "";
3025 
3026 	  p = strncat(loname, fullpathname, strlen(fullpathname));
3027 	  while(*p && (*p != '\0')) {
3028 	    *p = tolower(*p);
3029 	    p++;
3030 	  }
3031 
3032 	  if((extension = strrchr(loname, '.')) && (strlen(extension) > 1)) {
3033 	    char  ext[strlen(extension) + 2];
3034 	    char *valid_endings =
3035 	      ".pls .m3u .sfv .tox .asx .smi .smil .xml .fxd " /* Playlists */
3036 	      ".4xm .ac3 .aif .aiff .asf .wmv .wma .wvx .wax .aud .avi .cin .cpk .cak "
3037 	      ".film .dv .dif .fli .flc .mjpg .mov .qt .m2p .mp4 .mp3 .mp2 .mpa .mpega .mpg .mpeg "
3038 	      ".mpv .mve .mv8 .nsf .nsv .ogg .ogm .spx .pes .png .mng .pva .ra .rm "
3039 	      ".ram .rmvb .roq .snd .au .str .iki .ik2 .dps .dat .xa .xa1 .xa2 .xas .xap .ts .m2t "
3040 	      ".trp .vob .voc .vox .vqa .wav .wve .y4m ";
3041 
3042 	    snprintf(ext, sizeof(ext), "%s ", extension);
3043 
3044 	    if(strstr(valid_endings, ext)) {
3045 	      mediamark_append_entry((const char *)fullpathname,
3046 				     (const char *)fullpathname, NULL, 0, -1, 0, 0);
3047 	    }
3048 	  }
3049 	}
3050       }
3051     }
3052     closedir(dir);
3053   }
3054 }
3055 
3056 /*
3057  *  EDITOR
3058  */
mmkeditor_exit(xitk_widget_t * w,void * data)3059 static void mmkeditor_exit(xitk_widget_t *w, void *data) {
3060   gGui_t *gui = gGui;
3061 
3062   if(mmkeditor.running) {
3063     window_info_t wi;
3064 
3065     mmkeditor.running = 0;
3066     mmkeditor.visible = 0;
3067 
3068     if((xitk_get_window_info(mmkeditor.widget_key, &wi))) {
3069       config_update_num ("gui.mmk_editor_x", wi.x);
3070       config_update_num ("gui.mmk_editor_y", wi.y);
3071       WINDOW_INFO_ZERO(&wi);
3072     }
3073 
3074     mmkeditor.mmk = NULL;
3075 
3076     xitk_unregister_event_handler(&mmkeditor.widget_key);
3077 
3078     xitk_destroy_widgets(mmkeditor.widget_list);
3079     xitk_window_destroy_window(gui->imlib_data, mmkeditor.xwin);
3080 
3081     mmkeditor.xwin = NULL;
3082     /* xitk_dlist_init (&mmkeditor.widget_list.list); */
3083 
3084     gui->x_lock_display (gui->display);
3085     XFreeGC(gui->display, (XITK_WIDGET_LIST_GC(mmkeditor.widget_list)));
3086     gui->x_unlock_display (gui->display);
3087 
3088     XITK_WIDGET_LIST_FREE(mmkeditor.widget_list);
3089 
3090     playlist_get_input_focus();
3091   }
3092 }
3093 
mmkeditor_handle_event(XEvent * event,void * data)3094 static void mmkeditor_handle_event(XEvent *event, void *data) {
3095   switch(event->type) {
3096 
3097   case KeyPress:
3098     if(xitk_get_key_pressed(event) == XK_Escape)
3099       mmkeditor_exit(NULL, NULL);
3100     else
3101       gui_handle_event (event, gGui);
3102   }
3103 }
3104 
mmk_editor_show_tips(int enabled,unsigned long timeout)3105 void mmk_editor_show_tips(int enabled, unsigned long timeout) {
3106 
3107   if(mmkeditor.running) {
3108     if(enabled)
3109       xitk_set_widgets_tips_timeout(mmkeditor.widget_list, timeout);
3110     else
3111       xitk_disable_widgets_tips(mmkeditor.widget_list);
3112   }
3113 }
3114 
mmk_editor_update_tips_timeout(unsigned long timeout)3115 void mmk_editor_update_tips_timeout(unsigned long timeout) {
3116   if(mmkeditor.running)
3117     xitk_set_widgets_tips_timeout(mmkeditor.widget_list, timeout);
3118 }
3119 
mmk_editor_is_visible(void)3120 int mmk_editor_is_visible(void) {
3121   if(mmkeditor.running)
3122     return mmkeditor.visible;
3123 
3124   return 0;
3125 }
3126 
mmk_editor_is_running(void)3127 int mmk_editor_is_running(void) {
3128   return mmkeditor.running;
3129 }
3130 
mmk_editor_raise_window(void)3131 void mmk_editor_raise_window(void) {
3132   if(mmkeditor.running)
3133     raise_window(xitk_window_get_window(mmkeditor.xwin), mmkeditor.visible, mmkeditor.running);
3134 }
3135 
mmk_editor_toggle_visibility(void)3136 void mmk_editor_toggle_visibility(void) {
3137   if(mmkeditor.running)
3138     toggle_window(xitk_window_get_window(mmkeditor.xwin), mmkeditor.widget_list,
3139 		  &mmkeditor.visible, mmkeditor.running);
3140 }
3141 
mmk_editor_end(void)3142 void mmk_editor_end(void) {
3143   mmkeditor_exit(NULL, NULL);
3144 }
3145 
mmkeditor_set_mmk(mediamark_t ** mmk)3146 void mmkeditor_set_mmk(mediamark_t **mmk) {
3147 
3148   if(mmkeditor.running) {
3149     mmkeditor.mmk = mmk;
3150 
3151     xitk_inputtext_change_text(mmkeditor.mrl, (*mmk)->mrl);
3152     xitk_inputtext_change_text(mmkeditor.ident, (*mmk)->ident);
3153     xitk_inputtext_change_text(mmkeditor.sub, (*mmk)->sub);
3154     xitk_intbox_set_value(mmkeditor.start, (*mmk)->start);
3155     xitk_intbox_set_value(mmkeditor.end, (*mmk)->end);
3156     xitk_intbox_set_value(mmkeditor.av_offset, (*mmk)->av_offset);
3157     xitk_intbox_set_value(mmkeditor.spu_offset, (*mmk)->spu_offset);
3158   }
3159 }
3160 
mmkeditor_apply(xitk_widget_t * w,void * data)3161 static void mmkeditor_apply(xitk_widget_t *w, void *data) {
3162   const char *sub;
3163   char       *ident, *mrl;
3164   int         start, end, av_offset, spu_offset;
3165 
3166   if(mmkeditor.mmk) {
3167 
3168     mrl = atoa(xitk_inputtext_get_text(mmkeditor.mrl));
3169     if (!mrl[0])
3170       mrl = strdup((*mmkeditor.mmk)->mrl);
3171     else
3172       mrl = strdup(mrl);
3173 
3174     ident = atoa(xitk_inputtext_get_text(mmkeditor.ident));
3175     if (!ident[0])
3176       ident = strdup(mrl);
3177     else
3178       ident = strdup(ident);
3179 
3180     sub = xitk_inputtext_get_text(mmkeditor.sub);
3181     if(sub && (!strlen(sub)))
3182       sub = NULL;
3183 
3184     if((start = xitk_intbox_get_value(mmkeditor.start)) < 0)
3185       start = 0;
3186 
3187     if((end = xitk_intbox_get_value(mmkeditor.end)) <= -1)
3188       end = -1;
3189     else if (end < start)
3190       end = start + 1;
3191 
3192     av_offset  = xitk_intbox_get_value(mmkeditor.av_offset);
3193     spu_offset = xitk_intbox_get_value(mmkeditor.spu_offset);
3194 
3195     mediamark_replace_entry(mmkeditor.mmk, mrl, ident, sub, start, end, av_offset, spu_offset);
3196     if(mmkeditor.callback)
3197       mmkeditor.callback(mmkeditor.user_data);
3198 
3199     free(mrl);
3200     free(ident);
3201   }
3202 }
3203 
mmkeditor_ok(xitk_widget_t * w,void * data)3204 static void mmkeditor_ok(xitk_widget_t *w, void *data) {
3205   mmkeditor_apply(NULL, NULL);
3206   mmkeditor_exit(NULL, NULL);
3207 }
3208 
mmk_fileselector_callback(filebrowser_t * fb)3209 static void mmk_fileselector_callback(filebrowser_t *fb) {
3210   gGui_t *gui = gGui;
3211   char *file, *dir;
3212 
3213   if ((dir = filebrowser_get_current_dir(fb)) != NULL) {
3214     strlcpy(gui->curdir, dir, sizeof(gui->curdir));
3215     free(dir);
3216   }
3217 
3218   if((file = filebrowser_get_full_filename(fb)) != NULL) {
3219     if(file)
3220       xitk_inputtext_change_text(mmkeditor.sub, file);
3221     free(file);
3222   }
3223 
3224 }
mmkeditor_select_sub(xitk_widget_t * w,void * data)3225 static void mmkeditor_select_sub(xitk_widget_t *w, void *data) {
3226   gGui_t *gui = gGui;
3227   filebrowser_callback_button_t  cbb;
3228   char                           *path, *open_path;
3229 
3230   cbb.label = _("Select");
3231   cbb.callback = mmk_fileselector_callback;
3232   cbb.need_a_file = 1;
3233 
3234   path = (*mmkeditor.mmk)->sub ? (*mmkeditor.mmk)->sub : (*mmkeditor.mmk)->mrl;
3235 
3236   if(mrl_look_like_file(path)) {
3237     char *p;
3238 
3239     open_path = strdup(path);
3240 
3241     if(!strncasecmp(path, "file:", 5))
3242       path += 5;
3243 
3244     p = strrchr(open_path, '/');
3245     if (p && strlen(p))
3246       *p = '\0';
3247   }
3248   else
3249     open_path = strdup(gui->curdir);
3250 
3251   create_filebrowser(_("Pick a subtitle file"), open_path, hidden_file_cb, &cbb, NULL, NULL);
3252 
3253   free(open_path);
3254 }
3255 
mmk_edit_mediamark(mediamark_t ** mmk,apply_callback_t callback,void * data)3256 void mmk_edit_mediamark(mediamark_t **mmk, apply_callback_t callback, void *data) {
3257   gGui_t *gui = gGui;
3258   GC                          gc;
3259   xitk_labelbutton_widget_t   lb;
3260   xitk_label_widget_t         lbl;
3261   xitk_checkbox_widget_t      cb;
3262   xitk_inputtext_widget_t     inp;
3263   xitk_intbox_widget_t        ib;
3264   xitk_pixmap_t              *bg;
3265   xitk_widget_t              *b;
3266   int                         x, y, w, width, height;
3267 
3268   if(mmkeditor.running) {
3269     if(!mmkeditor.visible)
3270       mmkeditor.visible = !mmkeditor.visible;
3271     mmk_editor_raise_window();
3272     mmkeditor_set_mmk(mmk);
3273     return;
3274   }
3275 
3276   mmkeditor.callback = callback;
3277   mmkeditor.user_data = data;
3278 
3279   x = xine_config_register_num(__xineui_global_xine_instance, "gui.mmk_editor_x",
3280 			       80,
3281 			       CONFIG_NO_DESC,
3282 			       CONFIG_NO_HELP,
3283 			       CONFIG_LEVEL_DEB,
3284 			       CONFIG_NO_CB,
3285 			       CONFIG_NO_DATA);
3286   y = xine_config_register_num(__xineui_global_xine_instance, "gui.mmk_editor_y",
3287 			       80,
3288 			       CONFIG_NO_DESC,
3289 			       CONFIG_NO_HELP,
3290 			       CONFIG_LEVEL_DEB,
3291 			       CONFIG_NO_CB,
3292 			       CONFIG_NO_DATA);
3293 
3294   /* Create window */
3295   mmkeditor.xwin = xitk_window_create_dialog_window(gui->imlib_data, _("Mediamark Editor"), x, y,
3296 						     WINDOW_WIDTH, WINDOW_HEIGHT);
3297 
3298   set_window_states_start((xitk_window_get_window(mmkeditor.xwin)));
3299 
3300   gui->x_lock_display (gui->display);
3301   gc = XCreateGC(gui->display,
3302 		 (xitk_window_get_window(mmkeditor.xwin)), None, None);
3303   gui->x_unlock_display (gui->display);
3304 
3305   mmkeditor.widget_list = xitk_widget_list_new();
3306   xitk_widget_list_set(mmkeditor.widget_list,
3307 		       WIDGET_LIST_WINDOW, (void *) (xitk_window_get_window(mmkeditor.xwin)));
3308   xitk_widget_list_set(mmkeditor.widget_list, WIDGET_LIST_GC, gc);
3309 
3310   XITK_WIDGET_INIT(&lb, gui->imlib_data);
3311   XITK_WIDGET_INIT(&lbl, gui->imlib_data);
3312   XITK_WIDGET_INIT(&cb, gui->imlib_data);
3313   XITK_WIDGET_INIT(&inp, gui->imlib_data);
3314   XITK_WIDGET_INIT(&ib, gui->imlib_data);
3315 
3316   xitk_window_get_window_size(mmkeditor.xwin, &width, &height);
3317   bg = xitk_image_create_xitk_pixmap(gui->imlib_data, width, height);
3318   gui->x_lock_display (gui->display);
3319   XCopyArea(gui->display, (xitk_window_get_background(mmkeditor.xwin)), bg->pixmap,
3320 	    bg->gc, 0, 0, width, height, 0, 0);
3321   gui->x_unlock_display (gui->display);
3322 
3323   x = 15;
3324   y = 34 - 6;
3325   w = WINDOW_WIDTH - 30;
3326   draw_outter_frame(gui->imlib_data, bg, _("Identifier"), btnfontname,
3327 		    x, y, w, 45);
3328 
3329   inp.skin_element_name = NULL;
3330   inp.text              = NULL;
3331   inp.max_length        = 2048;
3332   inp.callback          = NULL;
3333   inp.userdata          = NULL;
3334   mmkeditor.ident = xitk_noskin_inputtext_create (mmkeditor.widget_list, &inp,
3335     x + 10, y + 16, w - 20, 20, "Black", "Black", fontname);
3336   xitk_add_widget (mmkeditor.widget_list, mmkeditor.ident);
3337   xitk_set_widget_tips_default(mmkeditor.ident, _("Mediamark Identifier"));
3338   xitk_enable_and_show_widget(mmkeditor.ident);
3339 
3340   y += 45 + 3;
3341   draw_outter_frame(gui->imlib_data, bg, _("Mrl"), btnfontname,
3342 		    x, y, w, 45);
3343 
3344   inp.skin_element_name = NULL;
3345   inp.text              = NULL;
3346   inp.max_length        = 2048;
3347   inp.callback          = NULL;
3348   inp.userdata          = NULL;
3349   mmkeditor.mrl = xitk_noskin_inputtext_create (mmkeditor.widget_list, &inp,
3350     x + 10, y + 16, w - 20, 20, "Black", "Black", fontname);
3351   xitk_add_widget (mmkeditor.widget_list, mmkeditor.mrl);
3352   xitk_set_widget_tips_default(mmkeditor.mrl, _("Mediamark Mrl"));
3353   xitk_enable_and_show_widget(mmkeditor.mrl);
3354 
3355   y += 45 + 3;
3356   draw_outter_frame(gui->imlib_data, bg, _("Subtitle"), btnfontname,
3357 		    x, y, w, 45);
3358 
3359   inp.skin_element_name = NULL;
3360   inp.text              = NULL;
3361   inp.max_length        = 2048;
3362   inp.callback          = NULL;
3363   inp.userdata          = NULL;
3364   mmkeditor.sub = xitk_noskin_inputtext_create (mmkeditor.widget_list, &inp,
3365     x + 10, y + 16, w - 20 - 100 - 10, 20, "Black", "Black", fontname);
3366   xitk_add_widget (mmkeditor.widget_list, mmkeditor.sub);
3367   xitk_set_widget_tips_default(mmkeditor.sub, _("Subtitle File"));
3368   xitk_enable_and_show_widget(mmkeditor.sub);
3369 
3370   lb.button_type       = CLICK_BUTTON;
3371   lb.label             = _("Sub File");
3372   lb.align             = ALIGN_CENTER;
3373   lb.callback          = mmkeditor_select_sub;
3374   lb.state_callback    = NULL;
3375   lb.userdata          = NULL;
3376   lb.skin_element_name = NULL;
3377   b =  xitk_noskin_labelbutton_create (mmkeditor.widget_list, &lb,
3378     x + 10 + w - 20 - 100, y + 16, 100, 20, "Black", "Black", "White", btnfontname);
3379   xitk_add_widget (mmkeditor.widget_list, b);
3380   xitk_set_widget_tips_default(b, _("Select a subtitle file to use together with the mrl."));
3381   xitk_enable_and_show_widget(b);
3382 
3383   y += 45 + 3;
3384   w = 120;
3385   draw_outter_frame(gui->imlib_data, bg, _("Start at"), btnfontname,
3386 		    x, y, w, 45);
3387 
3388   ib.skin_element_name = NULL;
3389   ib.value             = 0;
3390   ib.step              = 1;
3391   ib.parent_wlist      = mmkeditor.widget_list;
3392   ib.callback          = NULL;
3393   ib.userdata          = NULL;
3394   mmkeditor.start = xitk_noskin_intbox_create (mmkeditor.widget_list, &ib,
3395     x + 30, y + 16, w - 60, 20, NULL, NULL, NULL);
3396   xitk_add_widget (mmkeditor.widget_list, mmkeditor.start);
3397   xitk_set_widget_tips_default(mmkeditor.start, _("Mediamark start time (secs)."));
3398   xitk_enable_and_show_widget(mmkeditor.start);
3399 
3400   x += w + 5;
3401   draw_outter_frame(gui->imlib_data, bg, _("End at"), btnfontname,
3402 		    x, y, w, 45);
3403 
3404   ib.skin_element_name = NULL;
3405   ib.value             = -1;
3406   ib.step              = 1;
3407   ib.parent_wlist      = mmkeditor.widget_list;
3408   ib.callback          = NULL;
3409   ib.userdata          = NULL;
3410   mmkeditor.end = xitk_noskin_intbox_create (mmkeditor.widget_list, &ib,
3411     x + 30, y + 16, w - 60, 20, NULL, NULL, NULL);
3412   xitk_add_widget (mmkeditor.widget_list, mmkeditor.end);
3413   xitk_set_widget_tips_default(mmkeditor.end, _("Mediamark end time (secs)."));
3414   xitk_enable_and_show_widget(mmkeditor.end);
3415 
3416   x += w + 5;
3417   draw_outter_frame(gui->imlib_data, bg, _("A/V offset"), btnfontname,
3418 		    x, y, w, 45);
3419 
3420   ib.skin_element_name = NULL;
3421   ib.value             = 0;
3422   ib.step              = 1;
3423   ib.parent_wlist      = mmkeditor.widget_list;
3424   ib.callback          = NULL;
3425   ib.userdata          = NULL;
3426   mmkeditor.av_offset = xitk_noskin_intbox_create (mmkeditor.widget_list, &ib,
3427     x + 30, y + 16, w - 60, 20, NULL, NULL, NULL);
3428   xitk_add_widget (mmkeditor.widget_list, mmkeditor.av_offset);
3429   xitk_set_widget_tips_default(mmkeditor.av_offset, _("Offset of Audio and Video."));
3430   xitk_enable_and_show_widget(mmkeditor.av_offset);
3431 
3432   x += w + 5;
3433   draw_outter_frame(gui->imlib_data, bg, _("SPU offset"), btnfontname,
3434 		    x, y, w, 45);
3435 
3436   ib.skin_element_name = NULL;
3437   ib.value             = 0;
3438   ib.step              = 1;
3439   ib.parent_wlist      = mmkeditor.widget_list;
3440   ib.callback          = NULL;
3441   ib.userdata          = NULL;
3442   mmkeditor.spu_offset = xitk_noskin_intbox_create (mmkeditor.widget_list, &ib,
3443     x + 30, y + 16, w - 60, 20, NULL, NULL, NULL);
3444   xitk_add_widget (mmkeditor.widget_list, mmkeditor.spu_offset);
3445   xitk_set_widget_tips_default(mmkeditor.spu_offset, _("Subpicture offset."));
3446   xitk_enable_and_show_widget(mmkeditor.spu_offset);
3447 
3448   y = WINDOW_HEIGHT - (23 + 15);
3449   x = 15;
3450   lb.button_type       = CLICK_BUTTON;
3451   lb.label             = _("OK");
3452   lb.align             = ALIGN_CENTER;
3453   lb.callback          = mmkeditor_ok;
3454   lb.state_callback    = NULL;
3455   lb.userdata          = NULL;
3456   lb.skin_element_name = NULL;
3457   b =  xitk_noskin_labelbutton_create (mmkeditor.widget_list,
3458     &lb, x, y, 100, 23, "Black", "Black", "White", btnfontname);
3459   xitk_add_widget (mmkeditor.widget_list, b);
3460   xitk_set_widget_tips_default(b, _("Apply the changes and close the window."));
3461   xitk_enable_and_show_widget(b);
3462 
3463   x = (WINDOW_WIDTH - 100) / 2;
3464   lb.button_type       = CLICK_BUTTON;
3465   lb.label             = _("Apply");
3466   lb.align             = ALIGN_CENTER;
3467   lb.callback          = mmkeditor_apply;
3468   lb.state_callback    = NULL;
3469   lb.userdata          = NULL;
3470   lb.skin_element_name = NULL;
3471   b =  xitk_noskin_labelbutton_create (mmkeditor.widget_list,
3472     &lb, x, y, 100, 23, "Black", "Black", "White", btnfontname);
3473   xitk_add_widget (mmkeditor.widget_list, b);
3474   xitk_set_widget_tips_default(b, _("Apply the changes to the playlist."));
3475   xitk_enable_and_show_widget(b);
3476 
3477   x = WINDOW_WIDTH - (100 + 15);
3478   lb.button_type       = CLICK_BUTTON;
3479   lb.label             = _("Close");
3480   lb.align             = ALIGN_CENTER;
3481   lb.callback          = mmkeditor_exit;
3482   lb.state_callback    = NULL;
3483   lb.userdata          = NULL;
3484   lb.skin_element_name = NULL;
3485   b =  xitk_noskin_labelbutton_create (mmkeditor.widget_list,
3486     &lb, x, y, 100, 23, "Black", "Black", "White", btnfontname);
3487   xitk_add_widget (mmkeditor.widget_list, b);
3488   xitk_set_widget_tips_default(b, _("Discard changes and dismiss the window."));
3489   xitk_enable_and_show_widget(b);
3490   mmk_editor_show_tips (panel_get_tips_enable (gui->panel), panel_get_tips_timeout (gui->panel));
3491 
3492   xitk_window_change_background(gui->imlib_data, mmkeditor.xwin, bg->pixmap, width, height);
3493   xitk_image_destroy_xitk_pixmap(bg);
3494 
3495   mmkeditor.widget_key = xitk_register_event_handler("mmkeditor",
3496 						      (xitk_window_get_window(mmkeditor.xwin)),
3497 						      mmkeditor_handle_event,
3498 						      NULL,
3499 						      NULL,
3500 						      mmkeditor.widget_list,
3501 						      NULL);
3502 
3503   mmkeditor.visible = 1;
3504   mmkeditor.running = 1;
3505 
3506   mmkeditor_set_mmk(mmk);
3507   mmk_editor_raise_window();
3508 
3509   try_to_set_input_focus(xitk_window_get_window(mmkeditor.xwin));
3510 }
3511 
3512