Git

1 /*
2  * FAPG means Fast Audio Playlist Generator.
3  * It is a tool to generate list of audio files (Wav, MP3, Ogg, etc)
4  * in various formats (M3U, PLS, HTML, etc).
5  * It is very usefull if you have a large amount of audio files
6  * and you want to quickly and frequently build a playlist.
7  *
8  * Copyright (C) 2003-2004  Antoine Jacquet <royale@zerezo.com>
9  * http://royale.zerezo.com/fapg/
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <getopt.h>
29 #include <dirent.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <limits.h>
35 #include <unistd.h>
36 #include <ctype.h>
37 #include <time.h>
38 #include <assert.h>
39 #include "genres.h"
40 #ifdef HAVE_LIBURIPARSER
41 # include <uriparser/Uri.h>
42 #endif
44 #define MP3_BASE 1024
45 #define OGG_BASE 1024*10
46 #define MAX 1024*250            /* 250ko for ID3 with JPEG images in it */
48 #define FORMAT_M3U 0
49 #define FORMAT_PLS 1
50 #define FORMAT_HTML 2
51 #define FORMAT_RSS 3
52 #define FORMAT_PLP 4
53 #define FORMAT_UMS 5
54 #ifdef HAVE_LIBURIPARSER
55 # define FORMAT_XSPF 6
56 #endif
58 int debug = 0;
59 int format = FORMAT_M3U;
60 char *genrelist = NULL;
61 unsigned char *prefix = "";
62 unsigned char *base = "";
63 unsigned char *dir = "";
64 unsigned char *hostname = "fritzserver.de";
65 // unsigned char *referal="/usr/local/bin/fapg-rss.sh";
66 unsigned char *referal = NULL;
67 //int windows=0;
68 int fromstdin = 0;
69 int recursive = 0;
70 int avoidhlinked = 0;
71 int separator = '/';
72 unsigned char *eol = "\n";
73 unsigned char buffer[MAX];
75 int counter = 0;
77 unsigned char artist[1024];
78 unsigned char title[1024];
79 unsigned char genrebuf[1024];
80 unsigned char genre = 0;
81 int duration;
82 #define MP2ENC 1
83 #define MP3ENC 2
84 #define MPCENC 3
85 #define MPPENC 4
86 #define OGGENC 5
87 #define WAVENC 6
88 #define WMAENC 7
90 char *magic[] = { NULL,
91     "audio/mpeg", "audio/mpeg",
92     "audio/mpeg", "audio/mpeg",
93     "audio/ogg-vorbis", "audio/x-wav",
94     "audio/x-ms-wma", 
95     NULL
96 };
98 unsigned char unix2dos[] =
99     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
100     16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
101     32, 33, 70, 35, 36, 37, 38, 39, 40, 41, 82, 43, 44, 45, 46, 47,
102     48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 84, 59, 36, 61, 65, 71,
103     64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
104     80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 36, 93, 94, 95,
105     96, 97, 98, 99, 100, 101, 102, 103,
106     104, 105, 106, 107, 108, 109, 110, 111,
107     112, 113, 114, 115, 116, 117, 118, 119,
108     120, 121, 122, 123, 36, 125, 126, 127,
109     199, 252, 233, 226, 228, 224, 229, 231,
110     234, 235, 232, 239, 238, 236, 196, 197,
111     201, 230, 198, 244, 246, 242, 251, 249,
112     255, 214, 220, 248, 163, 216, 215, 131,
113     225, 237, 243, 250, 241, 209, 170, 186,
114     191, 174, 172, 189, 188, 161, 171, 187,
115     166, 166, 166, 166, 166, 193, 194, 192,
116     169, 166, 166, 43, 43, 162, 165, 43,
117     43, 45, 45, 43, 45, 43, 227, 195,
118     43, 43, 45, 45, 166, 45, 43, 164,
119     240, 208, 202, 203, 200, 105, 205, 206,
120     207, 43, 43, 166, 220, 166, 204, 175,
121     211, 223, 212, 210, 245, 213, 181, 254,
122     222, 218, 219, 217, 253, 221, 175, 180,
123     173, 177, 61, 190, 182, 167, 247, 184,
124     176, 168, 183, 185, 179, 178, 166, 160
125 };
127 unsigned char *basemap;
128 unsigned char *winorunix;
129 unsigned char one2one[] =
130     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
131     16, 17, 18, 19, 20, 21, 22, 23,
132     24, 25, 26, 27, 28, 29, 30, 31,
133     32, 33, 34, 35, 36, 37, 38, 39,
134     40, 41, 42, 43, 44, 45, 46, 47,
135     48, 49, 50, 51, 52, 53, 54, 55,
136     56, 57, 58, 59, 60, 61, 62, 63,
137     64, 65, 66, 67, 68, 69, 70, 71,
138     72, 73, 74, 75, 76, 77, 78, 79,
139     80, 81, 82, 83, 84, 85, 86, 87,
140     88, 89, 90, 91, 92, 93, 94, 95,
141     96, 97, 98, 99, 100, 101, 102, 103,
142     104, 105, 106, 107, 108, 109, 110, 111,
143     112, 113, 114, 115, 116, 117, 118, 119,
144     120, 121, 122, 123, 124, 125, 126, 127,
145     128, 129, 130, 131, 132, 133, 134, 135,
146     136, 137, 138, 139, 140, 141, 142, 143,
147     144, 145, 146, 147, 148, 149, 150, 151,
148     152, 153, 154, 155, 156, 157, 158, 159,
149     160, 161, 162, 163, 164, 165, 166, 167,
150     168, 169, 170, 171, 172, 173, 174, 175,
151     176, 177, 178, 179, 180, 181, 182, 183,
152     184, 185, 186, 187, 188, 189, 190, 191,
153     192, 193, 194, 195, 196, 197, 198, 199,
154     200, 201, 202, 203, 204, 205, 206, 207,
155     208, 209, 210, 211, 212, 213, 214, 215,
156     216, 217, 218, 219, 220, 221, 222, 223,
157     224, 225, 226, 227, 228, 229, 230, 231,
158     232, 233, 234, 235, 236, 237, 238, 239,
159     240, 241, 242, 243, 244, 245, 246, 247,
160     248, 249, 250, 251, 252, 253, 254, 255
161 };                              /* identical mapping */
163 unsigned char noand[256] =
164     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
165     16, 17, 18, 19, 20, 21, 22, 23,
166     24, 25, 26, 27, 28, 29, 30, 31,
167     32, 33, 34, 35, 36, 37, 43, 39,
168     40, 41, 42, 43, 44, 45, 46, 47,
169     48, 49, 50, 51, 52, 53, 54, 55,
170     56, 57, 58, 59, 60, 61, 62, 63,
171     64, 65, 66, 67, 68, 69, 70, 71,
172     72, 73, 74, 75, 76, 77, 78, 79,
173     80, 81, 82, 83, 84, 85, 86, 87,
174     88, 89, 90, 91, 92, 93, 94, 95,
175     96, 97, 98, 99, 100, 101, 102, 103,
176     104, 105, 106, 107, 108, 109, 110, 111,
177     112, 113, 114, 115, 116, 117, 118, 119,
178     120, 121, 122, 123, 124, 125, 126, 127,
179     128, 129, 130, 131, 132, 133, 134, 135,
180     136, 137, 138, 139, 140, 141, 142, 143,
181     144, 145, 146, 147, 148, 149, 150, 151,
182     152, 153, 154, 155, 156, 157, 158, 159,
183     160, 161, 162, 163, 164, 165, 166, 167,
184     168, 169, 170, 171, 172, 173, 174, 175,
185     176, 177, 178, 179, 180, 181, 182, 183,
186     184, 185, 186, 187, 188, 189, 190, 191,
187     192, 193, 194, 195, 196, 197, 198, 199,
188     200, 201, 202, 203, 204, 205, 206, 207,
189     208, 209, 210, 211, 212, 213, 214, 215,
190     216, 217, 218, 219, 220, 221, 222, 223,
191     224, 225, 226, 227, 228, 229, 230, 231,
192     232, 233, 234, 235, 236, 237, 238, 239,
193     240, 241, 242, 243, 244, 245, 246, 247,
194     248, 249, 250, 251, 252, 253, 254, 255
195 };                              /* only '&' is mapped to '+' */
197 unsigned char *iso2web[256] = {
198     "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
199     "%08", "%09", "%0a", "%0b", "%0c", "%0d", "%0e", "%0f",
200     "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
201     "%18", "%19", "%1a", "%1b", "%1c", "%1d", "%1e", "%1f",
202     "%20", "%21", "%22", "%23", "%24", "%25", "%26", "%27",
203     "%28", "%29", "%2a", "+", ",", "-", ".", "/",
204     "0", "1", "2", "3", "4", "5", "6", "7",
205     "8", "9", ":", ";", "%3c", "=", "%3e", "%3f",
206     "@", "A", "B", "C", "D", "E", "F", "G",
207     "H", "I", "J", "K", "L", "M", "N", "O",
208     "P", "Q", "R", "S", "T", "U", "V", "W",
209     "X", "Y", "Z", "%5B", "\\", "%5D", "^", "_",
210     "`", "a", "b", "c", "d", "e", "f", "g",
211     "h", "i", "j", "k", "l", "m", "n", "o",
212     "p", "q", "r", "s", "t", "u", "v", "w",
213     "x", "y", "z", "%7b", "|", "%7d", "~", "%7f",
214     "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",
215     "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f",
216     "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",
217     "%98", "%99", "%9a", "%9b", "%9c", "%9d", "%9e", "%9f",
218     "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7",
219     "%a8", "%a9", "%aa", "%ab", "%ac", "%ad", "%ae", "%af",
220     "%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7",
221     "%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf",
222     "%c0", "%c1", "%c2", "%c3", "%c4", "%c5", "%c6", "%c7",
223     "%c8", "%c9", "%ca", "%cb", "%cc", "%cd", "%ce", "%cf",
224     "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
225     "%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df",
226     "%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7",
227     "%e8", "%e9", "%ea", "%eb", "%ec", "%ed", "%ee", "%ef",
228     "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
229     "%f8", "%f9", "%fa", "%fb", "%fc", "%fd", "%fe", "%ff"
230 };
232 void usage()
234 #ifdef HAVE_LIBURIPARSER
235 # define FAPG_FORMATS "m3u|pls|xspf|html|rss|pla|txx"
236 #else
237 # define FAPG_FORMATS "m3u|pls|html|rss|pla|txx"
238 #endif
239     fprintf(stderr,
240             "Usage >> fapg [-b|--backslash] [-d|--debug] [-f|--format=" FAPG_FORMATS "] [-g|--genre=#:#:...] [-n|--nohardlink] [-o|--output=/path/to/file.m3u] [-p|--prefix=/the/prefix] [-r|--recursive] [-w|--windows] [-c|--command=<intern|...>] [-x|--exclude=#:#:...] [-s|--stdin] /path/to/mp3/dir1 [/path/to/mp3/dir2 ...]\n");
241 #undef FAPG_FORMATS
242     exit(1);
245 #define mywebputchar(x) { fputs(iso2web[(unsigned char)winorunix[(unsigned char)x]], stdout); }
246 #define    myputchar(x) { putchar(basemap[(unsigned char)winorunix[(unsigned char)x]]); }
247 /* #define    myplaputchar(x) { putchar(basemap[(unsigned char)winorunix[(unsigned char)x]]);putchar('\0');} */
248 void myplaputchar(const char x)
250     putchar(basemap[(unsigned char)winorunix[(unsigned char)x]]);
251     putchar('\0');
254 void mywebputstr(const char *c)
256     while(*c != 0) {
257         mywebputchar(*c);
258         c++;
259     }
262 void myplaputstr(const char *c)
264     while(*c != 0) {
265         if(*c == '/')
266             myplaputchar('\\'); /* translate slash to backslash */
267         else
268             myplaputchar(*c);
269         c++;
270         /* remove multiple slashes "//" when parsing a directory ending with a "/" */
271         while(*c == '/' && c[1] == '/')
272             c++;
273     }
276 void myputstr(const char *c)
278     while(*c != 0) {
279         if(*c == '/')
280             putchar(separator);
281         else
282             myputchar(*c);
283         c++;
284         /* remove multiple slashes "//" when parsing a directory ending with a "/" */
285         while(*c == '/' && c[1] == '/')
286             c++;
287     }
290 void txxputheader(const char *c)
292     int cnt = 0;
294     while(*c != 0) {
295         myputchar(*c);
296         cnt++;
297         c++;
298     }
300     while(cnt < 512) {
301         putchar('\0');
302         cnt++;
303     }
306 void txxputnameoffset(const char *c)
308     int pos = 0;
309     int cnt = 0;
310     char b;
311     unsigned char *prefx;
313     prefx = prefix;
315     if(*prefx != 0) {
316         while(*prefx != 0) {
317             if(*prefx == '/') {
318                 pos = cnt;
319             }
320             cnt++;
321             prefx++;
322         }
324         cnt--;                  // skip the leading dot of the filepath
325     }
327     while(*c != 0) {
328         if(*c == '/') {
329             pos = cnt;
330         }
331         cnt++;
332         c++;
333     }
335     pos += 2;
337     b = (pos & 0xFF00) >> 8;
338     putchar(b);
339     b = (pos & 0x00FF);
340     putchar(b);
343 void txxputstr(const char *c)
345     int cnt = 0;
346     int pos;
347     unsigned char *prefx;
349     txxputnameoffset(c);
351     prefx = prefix;
352     fprintf(stderr, "prefix: '%s'\n", prefx);
354     if(*prefx != 0) {
355         while(*prefx != 0) {
356             myputchar('\0');
357             cnt++;
359             if(*prefx == '/')
360                 putchar(separator);
361             else
362                 myputchar(*prefx);
363             cnt++;
365             prefx++;
366         }
368         c++;                    // skip the leading dot
369     }
371     while(*c != 0) {
372         myputchar('\0');
373         cnt++;
375         if(*c == '/')
376             putchar(separator);
377         else
378             myputchar(*c);
379         cnt++;
381         c++;
382     }
384     while(cnt < 510) {
385         myputchar('\0');
386         cnt++;
387     }
390 void txxputcounter(int c)
392     int b;
394     rewind(stdout);
396     b = (c & 0xFF000000) >> 24;
397     putchar(b);
398     b = (c & 0x00FF0000) >> 16;
399     putchar(b);
400     b = (c & 0x0000FF00) >> 8;
401     putchar(b);
402     b = (c & 0x000000FF);
403     putchar(b);
406 /* remove spaces at beginning and end of string */
407 void trim(char *c)
409     char *p;
410     /* remove spaces at beginning ... */
411     while(*c == ' ') {
412         p = c;
413         while(*p != '\0') {
414             *p = *(p + 1);
415             p++;
416         }
417     }
418     /* ... and end of string */
419     p = c + strlen(c);
420     while(--p > c && *p == ' ')
421         *p = '\0';
424 void print_webpath(const char *path)
426     const char *c = path;
428     printf("%s", prefix);             /* we must not modify this part */
429     if(*c == '.' && c[1] == '/') {      /* remove leading "./" when parsing current directory */
430         c += 2;
431         /* maybe there follow many slashes */
432         while(*c == '/')
433             c++;
434     }
435     for(; *c != '\0'; c++) {
436         mywebputchar(*c);
437         /* remove multiple "//" when parsing a directory ending with a "/" */
438         while(*c == '/' && c[1] == '/')
439             c++;
440     }
443 void print_path(const char *path)
445     const char *c = path;
446     printf("%s", prefix);
447     /* skip leading "./" when parsing current directory */
448     if(*c == '.' && *(c + 1) == '/') {
449         c += 2;
450         /* maybe there follow more slashes */
451         while(*c == '/')
452             c++;
453     }
454     myputstr(c);
457 void print_pathtail(const char *path)
459     const char *c;
460     c = strrchr(path, separator);
461     if(c != NULL)
462         c++;
463     else
464         c = path;
465     myputstr(c);
468 void noreferal(const char *path, const char *artist, const char *title)
470     printf("\t\t<description><![CDATA[<h4>");
471     myputstr(artist);
472     printf("</h4><h5>");
473     myputstr(title);
474     printf("</h5><a href=\"");
475     print_webpath(path);
476     printf
477         ("\"><br><br>Direct Link to Audiofile</a><br>]]></description>%s",
478          eol);
481 void reference(const char *title)
483     FILE *pipe = NULL;
484     static char command[2048], buffer[1024];
485     int buflen = 8192;
487     buflen = strlen(title) + strlen(referal) + 3;
488     assert((buflen < 2046));
489     strcpy(command, referal);
490     buflen = strlen(command);
491     command[buflen] = ' ';
492     command[buflen + 1] = '"';
493     command[buflen + 2] = 0;
494     strcat(command, title);
495     buflen = strlen(command);
496     command[buflen] = '"';
497     command[buflen + 1] = 0;
498     if(debug)
499         fprintf(stderr, "Debug >> processing command: %s\n", command);
500     pipe = popen(command, "r");
501     if(pipe == NULL) {
502         fprintf(stderr, "Warning >> can't open pipe >%s< !\n", command);
503         return;
504     }
505     fgets(buffer, 1020, pipe);
506     while(!feof(pipe)) {
507         fputs(buffer, stdout);
508         fgets(buffer, 1020, pipe);
509     }
510     pclose(pipe);
511     return;
514 void parse_options(int argc, char **argv)
516     static char const short_options[] = "bc:df:g:lo:np:rsuwx:";
517     static struct option long_options[] = {
518         {"backslash", no_argument, NULL, 'b'},
519         {"command", required_argument, NULL, 'c'},
520         {"debug", no_argument, NULL, 'd'},
521         {"format", required_argument, NULL, 'f'},
522         {"genre", required_argument, NULL, 'g'},
523         {"nohardlink", no_argument, NULL, 'n'},
524         {"output", required_argument, NULL, 'o'},
525         {"prefix", required_argument, NULL, 'p'},
526         {"recursive", no_argument, NULL, 'r'},
527         {"stdin", no_argument, NULL, 's'},
528         {"windows", no_argument, NULL, 'w'},
529         {"exclude", required_argument, NULL, 'x'},
530         {NULL, 0, NULL, 0}
531     };
532     int c;
533     int option_index = 0;
534     while((c =
535            getopt_long(argc, argv, short_options, long_options,
536                        &option_index)) != -1) {
537         switch (c) {
538         case 'b':
539             separator = '\\';
540             noand['/'] = '\\';
541             break;
542         case 'c':
543             if(strncmp(optarg, "intern", 6) == 0)
544                 referal = NULL;
545             else
546                 referal = strdup(optarg);
547             break;
548         case 'd':
549             debug = 1 - debug;
550             break;
551         case 'f':
552             if(strcmp(optarg, "m3u") == 0)
553                 format = FORMAT_M3U;
554             else if(strcmp(optarg, "pls") == 0)
555                 format = FORMAT_PLS;
556             else if(strcmp(optarg, "html") == 0)
557                 format = FORMAT_HTML;
558             else if(strcmp(optarg, "rss") == 0)
559                 format = FORMAT_RSS;
560             else if(strcmp(optarg, "pla") == 0)
561                 format = FORMAT_PLP;
562             else if(strcmp(optarg, "txx") == 0)
563                 format = FORMAT_UMS;
564 #ifdef HAVE_LIBURIPARSER
565             else if(strcmp(optarg, "xspf") == 0)
566                 format = FORMAT_XSPF;
567 #endif
568             else
569                 usage();
570             break;
571         case 'g':
572             if(genrelist == NULL)
573                 genrelist = calloc(257, sizeof(char));  /* allow multiple includes/excludes */
574             if(genrelist == NULL) {
575                 fprintf(stderr,
576                         "Error >> unable to allocate cleared memory\n");
577                 exit(2);
578             } else {
579                 unsigned int n = 0;
580                 while(n < strlen(optarg)) {
581                     if(debug)
582                         fprintf(stderr,
583                                 "Debug >> genrelist entry activting : %d\n",
584                                 atoi(&optarg[n]));
585                     genrelist[atoi(&optarg[n])] = 1;
586                     while(isdigit(optarg[n++]));
587                 }
588             }
589             break;
590         case 'n':
591             avoidhlinked = 1;
592             break;
593         case 'o':
594             close(1);
595             if(fopen(optarg, "w") == NULL) {
596                 fprintf(stderr,
597                         "Error >> unable to open output file : %s\n",
598                         optarg);
599                 exit(2);
600             }
601             break;
602         case 'p':
603             prefix = malloc(strlen(optarg) + 1);
604             strcpy(prefix, optarg);
605             base = malloc(strlen(prefix) + 1);
606             strcpy(base, prefix);
607             dir = strchr(base, '/');
608             if((dir != NULL) && (dir[1] == '/'))
609                 dir = strchr(dir + 2, '/');
610             if(dir != NULL)
611                 *dir++ = 0;
612             else
613                 dir = "";
614             /* if prefix is a weblink, base is the baselink, dir is the path */
615             break;
616         case 'r':
617             recursive = 1;
618             break;
619         case 'u':
620             winorunix = one2one;
621             eol = "\n";
622             break;
623         case 'w':
624             winorunix = unix2dos;
625             eol = "\r\n";
626             break;
627         case 'x':
628             if(genrelist == NULL) {     /* allow multiple includes/excludes - not recommended (confusing) but possible */
629                 int n = 0;
630                 genrelist = calloc(257, sizeof(char));
631                 while(n < 256)
632                     genrelist[n++] = 1;
633             }
634             if(genrelist == NULL) {
635                 fprintf(stderr,
636                         "Error >> unable to allocate cleared memory\n");
637                 exit(2);
638             } else {
639                 unsigned int n = 0;
640                 while(n < strlen(optarg)) {
641                     if(debug)
642                         fprintf(stderr,
643                                 "Debug >> genrelist entry activting : %d\n",
644                                 atoi(&optarg[n]));
645                     genrelist[atoi(&optarg[n])] = 0;
646                     while(isdigit(optarg[n++]));
647                 }
648             }
649             break;
650         case 's':
651             fromstdin = 1;
652             break;
653         default:
654             usage();
655         }
656     }
657     /* hostname = getenv("HOSTNAME"); */
658     if(genrelist == NULL) {
659         genrelist = calloc(257, sizeof(char));
660         if(genrelist == NULL) {
661             fprintf(stderr,
662                     "Error >> unable to allocate cleared memory\n");
663             exit(2);
664         } else {
665             int n = 0;
666             while(n < 256)
667                 genrelist[n++] = 1;
668         }
669     }
672 void parse_mp3(unsigned char *file)
674     int bitrates[2][3][15] =
675         { {{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384,
676             416, 448},
677            {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,
678             384},
679            {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256,
680             320}},
681     {{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256},
682      {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160},
683      {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}}
684     };
685     FILE *fic;
686     unsigned char *c;
687     int lus;
689     genre = 0;
690     genrebuf[0] = 0;
691     if(debug)
692         fprintf(stderr, "Debug >> parsing mp3 : %s\n", file);
694     /* read header */
695     if((fic = fopen(file, "r")) == NULL) {
696         fprintf(stderr, "Warning >> can't open file : %s\n", file);
697         return;
698     }
699     lus = fread(buffer, 1, MP3_BASE, fic);
700     c = buffer;
702     /* try ID3v2 */
703     if(buffer[0] == 'I' && buffer[1] == 'D' && buffer[2] == '3') {
704         int size;
705         int version;
706         version = *(buffer + 3);
707         if(version < 2 || version > 4)
708             fprintf(stderr,
709                     "Warning >> ID3 v2.%d not implemented ! trying anyway : %s\n",
710                     version, file);
711         if(*(buffer + 5) != 0)
712             fprintf(stderr,
713                     "Warning >> specials headers not implemented (%d) ! trying anyway : %s\n",
714                     *(buffer + 5), file);
715         c = buffer + 6;
716         size =
717             (*c << 21) + (*(c + 1) << 14) + (*(c + 2) << 7) + (*(c + 3));
718         /* read more header */
719         if(size + lus > MAX) {
720             lus += fread(buffer + lus, 1, MAX - lus, fic);
721             fprintf(stderr,
722                     "Warning >> ID3 header is huge (%d bytes) ! trying anyway : %s\n",
723                     size, file);
724         } else
725             lus += fread(buffer + lus, 1, size, fic);
726         if(size > lus)
727             size = lus;
728         c += 4;
729         if(version == 2)
730             while(c < buffer + size) {
731                 int size = (*(c + 3) << 16) + (*(c + 4) << 8) + (*(c + 5));
732                 if(*c == 0)
733                     break;
734                 if(strncmp(c, "TT2", 3) == 0) {
735                     strncpy(title, c + 7, size - 1);
736                     title[size - 1] = '\0';
737                 }
738                 if(strncmp(c, "TP1", 3) == 0) {
739                     strncpy(artist, c + 7, size - 1);
740                     artist[size - 1] = '\0';
741                 }
742                 if(strncmp(c, "TCO", 3) == 0) {
743                     /* strncpy(genrebuf,c+7,size-1); */
744                     /* genrebuf[size-1]='\0'; */
745                     /* genre=atoi(&genrebuf[1]); */
746                     genre = atoi(c + 8);
747                 }
748                 c += size + 6;
749             }
750         if(version == 3 || version == 4)
751             while(c < buffer + size) {
752                 int size =
753                     (*(c + 4) << 24) + (*(c + 5) << 16) + (*(c + 6) << 8) +
754                     (*(c + 7));
755                 if(*c == 0)
756                     break;
757                 if(strncmp(c, "TIT2", 4) == 0) {
758                     strncpy(title, c + 11, size - 1);
759                     title[size - 1] = '\0';
760                 }
761                 if(strncmp(c, "TPE1", 4) == 0) {
762                     strncpy(artist, c + 11, size - 1);
763                     artist[size - 1] = '\0';
764                 }
765                 if(strncmp(c, "TCON", 4) == 0) {
766                     /* strncpy(genrebuf,c+11,size-1); */
767                     /* genrebuf[size-1]='\0'; */
768                     /* genre=atoi(&genrebuf[1]); */
769                     genre = atoi(c + 12);
770                 }
771                 c += size + 10;
772             }
773     }
775     while(c < buffer + lus - 10) {
776         if(*c == 0xFF && (*(c + 1) & 0xF0) == 0xF0) {
777             int version;
778             int lay;
779             int bitrate_index;
780             int bitrate;
781             version = 2 - (*(c + 1) >> 3 & 1);
782             lay = 4 - (*(c + 1) >> 1 & 3);
783             bitrate_index = *(c + 2) >> 4 & 0xF;
784             if(version >= 1 && version <= 2 && lay - 1 >= 0 && lay - 1 <= 2
785                && bitrate_index >= 0 && bitrate_index <= 14)
786                 bitrate = bitrates[version - 1][lay - 1][bitrate_index];
787             else
788                 bitrate = 0;
789             if(bitrate != 0) {
790                 fseek(fic, 0, SEEK_END);
791                 duration = (ftell(fic) + buffer - c) / 125 / bitrate;
792             } else
793                 duration = 0;
794             break;
795         }
796         c++;
797     }
799     /* try ID3v1 */
800     if(strlen(artist) == 0 && strlen(title) == 0) {
801         fseek(fic, -128, SEEK_END);
802         lus = fread(buffer, 1, 128, fic);
803         if(lus == 128 && buffer[0] == 'T' && buffer[1] == 'A'
804            && buffer[2] == 'G') {
805             strncpy(title, buffer + 3, 30);
806             title[30] = '\0';
807             c = title + 29;
808             while(c > title && *c == ' ')
809                 *(c--) = '\0';
810             strncpy(artist, buffer + 33, 30);
811             artist[30] = '\0';
812             c = artist + 29;
813             while(c > artist && *c == ' ')
814                 *(c--) = '\0';
815             /* strncpy(album,buffer+65,30); */
816             /* strncpy(year,buffer+97,4); */
817             /* strncpy(comment,buffer+101,30); */
818             /* strncpy(genrebuf,buffer+127,1); genre[1]=0; */
819             genre = buffer[127];
820         }
821     }
823     fclose(fic);
826 void parse_ogg(unsigned char *file)
828     FILE *fic;
829     unsigned char *c;
830     int lus;
831     int sample_rate;
832     int samples;
834     if(debug)
835         fprintf(stderr, "Debug >> parsing ogg : %s\n", file);
837     /* read header */
838     if((fic = fopen(file, "r")) == NULL) {
839         fprintf(stderr, "Warning >> can't open file : %s\n", file);
840         return;
841     }
842     lus = fread(buffer, 1, OGG_BASE, fic);
844     /* try Ogg */
845     if(strncmp(buffer, "Ogg", 3) != 0) {
846         fprintf(stderr, "Warning >> not a Ogg header : %s\n", file);
847         return;
848     }
850     c = buffer + 0x28;
851     sample_rate =
852         (*c) + (*(c + 1) << 8) + (*(c + 2) << 16) + (*(c + 3) << 24);
854     while(c < buffer + lus - 10) {
855         int size;
856         if(strncasecmp(c, "TITLE=", 6) == 0) {
857             size =
858                 *(c - 4) + (*(c - 3) << 8) + (*(c - 2) << 16) +
859                 (*(c - 1) << 24);
860             strncpy(title, c + 6, size - 6);
861             title[size - 6] = '\0';
862             c += size;
863         }
864         if(strncasecmp(c, "ALBUM ARTIST=", 13) == 0) {
865             // ignore tag
866             size =
867                 *(c - 4) + (*(c - 3) << 8) + (*(c - 2) << 16) +
868                 (*(c - 1) << 24);
869             c += size;
870         }
871         if(strncasecmp(c, "ARTIST=", 7) == 0) {
872             size =
873                 *(c - 4) + (*(c - 3) << 8) + (*(c - 2) << 16) +
874                 (*(c - 1) << 24);
875             strncpy(artist, c + 7, size - 7);
876             artist[size - 7] = '\0';
877             c += size;
878         }
879         if(strncasecmp(c, "GENRE=", 6) == 0) {
880             static int i = 0;
881             size =
882                 *(c - 4) + (*(c - 3) << 8) + (*(c - 2) << 16) +
883                 (*(c - 1) << 24);
884             strncpy(genrebuf, c + 6, size - 6);
885             genrebuf[size - 6] = '\0';
886             c += size;
887             for(i = 0; i < ID3_NR_OF_V1_GENRES; i++) {
888                 if(strcasecmp(ID3_v1_genre_description[i], genrebuf) == 0) {
889                     genre = i;
890                     break;
891                 }
892                 if(i == ID3_NR_OF_V1_GENRES)
893                     genre = 0;
894             }
895         }
896         c++;
897     }
899     fseek(fic, -OGG_BASE, SEEK_END);
900     lus = fread(buffer, 1, OGG_BASE, fic);
901     c = buffer + lus - 1;
902     while(strncmp(c, "OggS", 4) != 0 && c > buffer)
903         c--;
904     if(c != buffer) {
905         c += 6;
906         samples =
907             (*c) + (*(c + 1) << 8) + (*(c + 2) << 16) + (*(c + 3) << 24);
908         duration = samples / sample_rate;
909     }
911     fclose(fic);
914 void parse_mpc(unsigned char *file)
916     FILE *fic;
917     unsigned char *c;
918     int lus;
919     int sample_rates[4] = { 44100, 48000, 37800, 32000 };
920     int frame_count;
921     int size, items;
922     int i;
924     if(debug)
925         fprintf(stderr, "Debug >> parsing mpc : %s\n", file);
927     /* read header */
928     if((fic = fopen(file, "r")) == NULL) {
929         fprintf(stderr, "Warning >> can't open file : %s\n", file);
930         return;
931     }
932     lus = fread(buffer, 1, 12, fic);
934     /* try Musepack */
935     if (strncmp(buffer, "MP+", 3) != 0) {
936         fprintf(stderr, "Warning >> not a Musepack header : %s\n", file);
937         return;
938     }
940     /* only version 7 */
941     if(buffer[3] != 7) {
942         fprintf(stderr, "Warning >> only Musepack SV7 supported : %s\n",
943                 file);
944         return;
945     }
947     /* duration */
948     c = buffer + 4;
949     frame_count =
950         (*c) + (*(c + 1) << 8) + (*(c + 2) << 16) + (*(c + 3) << 24);
951     c += 5;
952     duration = frame_count * 1152 / sample_rates[*c & 3];
954     /* try APETAGEX footer */
955     fseek(fic, -32, SEEK_END);
956     lus = fread(buffer, 1, 32, fic);
957     if(lus == 32 && strncmp(buffer, "APETAGEX", 8) == 0) {
958         c = buffer + 12;
959         size =
960             (*c) + (*(c + 1) << 8) + (*(c + 2) << 16) + (*(c + 3) << 24);
961         size += 32;
962         c += 4;
963         items =
964             (*c) + (*(c + 1) << 8) + (*(c + 2) << 16) + (*(c + 3) << 24);
965         fseek(fic, -size, SEEK_END);
966         lus = fread(buffer, 1, size, fic);
967         if(lus == size && strncmp(buffer, "APETAGEX", 8) == 0) {
968             c = buffer + 32;
969             while(items--) {
970                 size =
971                     (*c) + (*(c + 1) << 8) + (*(c + 2) << 16) +
972                     (*(c + 3) << 24);
973                 c += 8;
974                 if(strcasecmp(c, "TITLE") == 0) {
975                     strncpy(title, c + 6, size);
976                     title[size] = '\0';
977                 }
978                 if(strcasecmp(c, "ARTIST") == 0) {
979                     strncpy(artist, c + 7, size);
980                     artist[size] = '\0';
981                 }
982                 if(strcasecmp(c, "GENRE") == 0) {
983                     for(i = 0; i < ID3_NR_OF_V1_GENRES; i++) {
984                         strncpy(genrebuf, c + 6, size);
985                         genrebuf[size] = '\0';
986                         if(strcasecmp
987                            (ID3_v1_genre_description[i], genrebuf) == 0) {
988                             genre = i;
989                             break;
990                         }
991                         if(i == ID3_NR_OF_V1_GENRES)
992                             genre = 0;
993                     }
994                 }
995                 c += strlen(c) + 1 + size;
996             }
997         }
998     }
1000     fclose(fic);
1003 #define FSN  32
1004 #define MAXINO  (1<<24)
1005 #define INOTYP  unsigned long
1006 #define regbit_qry(x,y)  ( x[( (y) / sizeof(INOTYP) )]  &  1<<( (y) % sizeof(INOTYP) ) )
1007 #define regbit_set(x,y)  ( x[( (y) / sizeof(INOTYP) )]  |=  1<<( (y) % sizeof(INOTYP) ) )
1009 int hlink_check(struct stat *info)
1011     /*
1012      * for speed this subroutine should only be called
1013      * - if the file has more than one hardlink
1014      * - if the file is a resolved softlink
1015      */
1016     /* the persistent variables */
1017     static INOTYP *list[FSN];
1018     static dev_t name[FSN];
1019     /* some temporary variables */
1020     int fsn, is_registered = 0;
1022     /* assertions - in case parameters are lowered for less memory usage */
1023     assert(fsn < FSN);
1024     assert((info->st_ino) / sizeof(INOTYP) < MAXINO);
1026     /* search which internal registration number is used for this filesystem */
1027     for(fsn = 0; (name[fsn] != (info->st_dev)) && (name[fsn] != 0); fsn++);
1029     /* if file system is not registered yet, do it and leave */
1030     if(name[fsn] == 0) {
1031         name[fsn] = (info->st_dev);
1032         /* provide space for the bitmap that maps the inodes of this file system */
1033         list[fsn] = (INOTYP *) calloc(MAXINO, sizeof(INOTYP));
1034         /* no comparison is needed in empty lists ... return */
1035         if(debug)
1036             fprintf(stderr,
1037                     "Debug >> Linked >> Init List %04x @mem %04lx\n",
1038                     (int)name[fsn], (long)&list[fsn]);
1039     } else {
1040         /* this looks more complicated than it really is */
1041         /* the idea is very simple: 
1042          *  provide a bitmap that maps all inodes of a file system
1043          *  to mark all files that have already been visited.
1044          *  If it is already visited, do not add it to the playlist
1045          */
1046         /*
1047          * The difficulty is as follows:
1048          *   struct inode_bitmap { char registered:1; } bitmap[1<<MAXINO];
1049          * would be byte-aligned and would allocate at least eight times the needed space.
1050          * Feel free to change the definitions that are involved here, if you know better.
1051          */
1052         if(regbit_qry(list[fsn], (info->st_ino)))
1053             is_registered = 1;
1054         else
1055             regbit_set(list[fsn], (info->st_ino));
1056         /*
1057          * the debug expression is more complicated then the working stuff
1058          */
1059         if(debug)
1060             fprintf(stderr, "Debug >> Linked >> DEV %04x INO %06x => "
1061                     "list[%02x][%04x] = %04x & %04x --> %s registered\n",
1062                     (int)info->st_dev, (int)info->st_ino, fsn,
1063                     (int)((info->st_ino) / sizeof(INOTYP)),
1064                     (int)list[fsn][(info->st_ino) / sizeof(INOTYP)],
1065                     1 << ((info->st_ino) % sizeof(INOTYP)),
1066                     is_registered ? "Already" : "Not");
1067     }
1068     return is_registered;
1071 #ifdef HAVE_LIBURIPARSER
1072 char * relative_uri_malloc(const char * unixFilename, const char * baseDir)
1074     char * absSourceFile;
1075     size_t absSourceLen;
1076     char * sourceUriString;
1077     char * baseUriString;
1078     UriParserStateA state;
1079     UriUriA sourceUri;
1080     UriUriA baseUri;
1081     UriUriA relativeUri;
1082     int charsRequired;
1083     char * output;
1084   
1085     /* checks */
1086     if ((unixFilename == NULL) || (baseDir == NULL)) {
1087         return NULL;
1088     }
1090     /* base URI */
1091     baseUriString = malloc((7 + 3 * strlen(baseDir) + 1) * sizeof(char));
1092     if (baseUriString == NULL) {
1093         return NULL;
1094     }
1095     if (uriUnixFilenameToUriStringA(baseDir, baseUriString) != 0) {
1096         free(baseUriString);
1097         return NULL;
1098     }
1099     state.uri = &baseUri;
1100     if (uriParseUriA(&state, baseUriString) != 0) {
1101         free(baseUriString);
1102         uriFreeUriMembersA(&baseUri);
1103         return NULL;
1104     }
1106     /* source URI */
1107     if (unixFilename[0] != '/') {
1108         const int baseDirLen = strlen(baseDir);
1109         const int sourceFileLen = strlen(unixFilename);
1110         absSourceLen = baseDirLen + sourceFileLen;
1111         absSourceFile = malloc((absSourceLen + 1) * sizeof(char));
1112         sprintf(absSourceFile, "%s%s", baseDir, unixFilename);
1113     } else {
1114         absSourceLen = strlen(unixFilename);
1115         absSourceFile = (char *)unixFilename;
1116     }
1117     sourceUriString = malloc((7 + 3 * absSourceLen + 1) * sizeof(char));
1118     if (sourceUriString == NULL) {
1119         free(baseUriString);
1120         if (unixFilename[0] != '/') {
1121             free(absSourceFile);
1122         }
1123         uriFreeUriMembersA(&baseUri);
1124         return NULL;
1125     }
1126     if (uriUnixFilenameToUriStringA(absSourceFile, sourceUriString) != 0) {
1127         free(baseUriString);
1128         free(sourceUriString);
1129         if (unixFilename[0] != '/') {
1130             free(absSourceFile);
1131         }
1132         uriFreeUriMembersA(&baseUri);
1133         return NULL;
1134     }
1135     state.uri = &sourceUri;
1136     if (uriParseUriA(&state, sourceUriString) != 0) {
1137         free(baseUriString);
1138         free(sourceUriString);
1139         uriFreeUriMembersA(&baseUri);
1140         uriFreeUriMembersA(&sourceUri);
1141         return NULL;
1142     }
1143     if (uriNormalizeSyntaxA(&sourceUri) != 0) {
1144         free(baseUriString);
1145         free(sourceUriString);
1146         if (unixFilename[0] != '/') {
1147             free(absSourceFile);
1148         }
1149         uriFreeUriMembersA(&baseUri);
1150         uriFreeUriMembersA(&sourceUri);
1151         return NULL;
1152     }
1154     /* make relative (or keep absolute if necessary) */
1155     if (uriRemoveBaseUriA(&relativeUri, &sourceUri, &baseUri, URI_FALSE) != 0) {
1156         free(baseUriString);
1157         free(sourceUriString);
1158         if (unixFilename[0] != '/') {
1159             free(absSourceFile);
1160         }
1161         uriFreeUriMembersA(&baseUri);
1162         uriFreeUriMembersA(&sourceUri);
1163         uriFreeUriMembersA(&relativeUri);
1164         return NULL;
1165     }
1166     
1167     /* back to string */
1168     if (uriToStringCharsRequiredA(&relativeUri, &charsRequired) != 0) {
1169         free(baseUriString);
1170         free(sourceUriString);
1171         if (unixFilename[0] != '/') {
1172             free(absSourceFile);
1173         }
1174         uriFreeUriMembersA(&baseUri);
1175         uriFreeUriMembersA(&sourceUri);
1176         uriFreeUriMembersA(&relativeUri);
1177         return NULL;
1178     }
1179     output = malloc((charsRequired + 1) * sizeof(char));
1180     if (uriToStringA(output, &relativeUri, charsRequired + 1, NULL) != 0) {
1181         free(baseUriString);
1182         free(sourceUriString);
1183         if (unixFilename[0] != '/') {
1184             free(absSourceFile);
1185         }
1186         free(output);
1187         uriFreeUriMembersA(&baseUri);
1188         uriFreeUriMembersA(&sourceUri);
1189         uriFreeUriMembersA(&relativeUri);
1190         return NULL;
1191     }
1193     free(baseUriString);
1194     free(sourceUriString);
1195     if (unixFilename[0] != '/') {
1196         free(absSourceFile);
1197     }
1198     uriFreeUriMembersA(&baseUri);
1199     uriFreeUriMembersA(&sourceUri);
1200     uriFreeUriMembersA(&relativeUri);
1202     return output;
1205 char * xml_escape_malloc(const char * input)
1207     const char * read = input;
1208     char * output;
1209     char * write;
1210     
1211     if (input == NULL) {
1212         return NULL;
1213     }
1214     
1215     output = malloc((6 * strlen(input) + 1) * sizeof(char));
1216     if (output == NULL) {
1217         return NULL;
1218     }
1219     write = output;
1220     
1221     for (;;) {
1222         if (*read == '\0') {
1223             *write = '\0';
1224             return output;
1225         }
1226     
1227         switch ((unsigned char)*read) {
1228         case '&':
1229             strcpy(write, "&amp;");
1230             write += 5;
1231             break;
1232         case '<':
1233             strcpy(write, "&lt;");
1234             write += 4;
1235             break;
1236         case '>':
1237             strcpy(write, "&gt;");
1238             write += 4;
1239             break;
1240         case '\'':
1241             strcpy(write, "&apos;");
1242             write += 6;
1243             break;
1244         case '"':
1245             strcpy(write, "&quot;");
1246             write += 6;
1247             break;
1248         default:
1249             *(write++) = *read;
1250         }
1251         read++;
1252     }
1254 #endif
1256 void parse_file(unsigned char *newpath, unsigned char * original_path)
1258     unsigned char ext[5];
1259     int j, encoding = 0;
1261     for(j = 0; j < 5; j++)
1262         ext[j] = tolower(newpath[strlen(newpath) - 4 + j]);
1263     artist[0] = '\0';
1264     title[0] = '\0';
1265     duration = -2;
1266     if(strcmp(".mp2", ext) == 0) {
1267         duration = -1;
1268         parse_mp3(newpath);
1269         encoding = MP2ENC;
1270     }
1271     if(strcmp(".mp3", ext) == 0) {
1272         duration = -1;
1273         parse_mp3(newpath);
1274         encoding = MP3ENC;
1275     }
1276     if(strcmp(".mpc", ext) == 0) {
1277         duration = -1;
1278         parse_mpc(newpath);
1279         encoding = MPCENC;
1280     }
1281     if(strcmp(".mp+", ext) == 0) {
1282         duration = -1;
1283         parse_mpc(newpath);
1284         encoding = MPPENC;
1285     }
1286     if(strcmp(".ogg", ext) == 0) {
1287         duration = -1;
1288         parse_ogg(newpath);
1289         encoding = OGGENC;
1290     }
1291     if(strcmp(".wav", ext) == 0) {
1292         duration = -1;
1293         /* parse_wav(newpath); */
1294         encoding = WAVENC;
1295     }
1296     if(strcmp(".wma", ext) == 0) {
1297         duration = -1;
1298         /* parse_wma(newpath); */
1299         encoding = WMAENC;
1300     }
1301     /* guesstitle() */
1302     if((strlen(artist) == 0) && (strlen(title) == 0)) {
1303         // there are no tag infos read
1304         // use file name to state substitute it
1305         char *c = strrchr(newpath, separator);
1306         if(c == NULL)
1307             c = newpath;
1308         strcpy(artist, ++c);
1309         // arbitrarily use the first '-'
1310         // to separate artist and title
1311         c = strchr(artist, '-');
1312         if(c != NULL) {         // if trenner found, divide file name 
1313             *c = '\0';
1314             strcpy(title, ++c);
1315             c = strrchr(title, '.');
1316             if(c != NULL)
1317                 *c = '\0';
1318         } else {                // no trenner found, assume
1319             // no artist, only title
1320             strcpy(title, artist);
1321             artist[0] = '\0';
1322         }
1323         // replace underscores by spaces
1324         for(c = artist; (c = strchr(c, '_')) != NULL; c++)
1325             *c = ' ';
1326         for(c = title; (c = strchr(c, '_')) != NULL; c++)
1327             *c = ' ';
1328         // trim spaces
1329         trim(artist);
1330         trim(title);
1331     }
1332     /* guesstitle() end */
1334     if(duration != -2 && genrelist[genre]) {    /* is it an audio file ? */
1335         counter++;
1336         switch (format) {
1337         case FORMAT_M3U:
1338             if(duration != -1) {
1339                 printf("#EXTINF:%d,", duration);
1340                 if(strlen(artist) != 0)
1341                     printf("%s - ", artist);
1342                 printf("%s%s", title, eol);
1343             }
1344             print_path(newpath);
1345             printf("%s", eol);
1346             break;
1347         case FORMAT_PLS:
1348             printf("File%d=", counter);
1349             print_path(newpath);
1350             printf("%sTitle%d=", eol, counter);
1351             if(strlen(artist) != 0)
1352                 printf("%s - ", artist);
1353             printf("%s%s", title, eol);
1354             if(duration != -1)
1355                 printf("Length%d=%d%s", counter, duration, eol);
1356             break;
1357         case FORMAT_HTML:
1358             printf("<tr><td>%d</td><td>%s</td><td>%s</td><td>", counter,
1359                    artist, title);
1360             if(duration == -1)
1361                 printf("?</td></tr>%s", eol);
1362             else
1363                 printf("%d:%s%d</td></tr>%s", duration / 60,
1364                        duration % 60 < 10 ? "0" : "", duration % 60, eol);
1365             break;
1366         case FORMAT_RSS:
1367             if(duration != -1) {
1368                 struct stat infos;
1369                 char timebuffer[256];
1371                 if(stat(newpath, &infos) != 0) {
1372                     fprintf(stderr, "Warning >> can't stat entry : %s\n",
1373                             newpath);
1374                     return;
1375                 }
1376                 strftime(timebuffer, 255, "%a %d %b %Y %T %z", localtime(&(infos.st_mtime)));   /* ctime() had a trailing CR */
1377                 printf("\t<item>%s", eol);
1378                 printf("\t\t<author>");
1379                 myputstr(artist);
1380                 printf("</author>%s\t\t<title>", eol);
1381                 myputstr(title);
1382                 printf("</title>%s", eol);
1384                 if(referal == NULL) {
1385                     noreferal(newpath, artist, title);
1386                 } else
1387                     reference(newpath);
1388                 printf("\t\t<pubDate>%s</pubDate>%s\t\t<enclosure url=\"",
1389                        timebuffer, eol);
1390                 print_webpath(newpath);
1391                 printf("\" length=\"%d\" type=\"%s\"/>%s\t\t<guid>",
1392                        (int)infos.st_size, magic[encoding], eol);
1393                 print_pathtail(newpath);
1394                 printf("</guid>%s", eol);
1395                 if(duration > 3599)
1396                     printf
1397                         ("\t\t<itunes:duration>%d:%02d:%02d</itunes:duration>%s",
1398                          duration / 3600, (duration / 60) % 60,
1399                          duration % 60, eol);
1400                 else
1401                     printf
1402                         ("\t\t<itunes:duration>%d:%02d</itunes:duration>%s",
1403                          duration / 60, duration % 60, eol);
1404                 if(strlen(artist) != 0) {
1405                     printf("\t\t<itunes:author>");
1406                     myputstr(artist);
1407                     printf("</itunes:author>%s", eol);
1408                 }
1409                 printf("\t</item>%s", eol);
1410             }
1411             break;
1412         case FORMAT_PLP:
1413             myplaputstr("HARP, ");
1414             myplaputstr(newpath);
1415             myplaputstr(eol);
1416             break;
1417         case FORMAT_UMS:
1418             txxputstr(newpath);
1419             break;
1420 #ifdef HAVE_LIBURIPARSER
1421         case FORMAT_XSPF:
1422             printf("<track>\n");
1423             if (strlen(title) > 0) {
1424                 char * escaped_title = xml_escape_malloc(title);
1425                 if (escaped_title != NULL) {
1426                     printf("    <title>%s</title>\n", escaped_title);
1427                     free(escaped_title);
1428                 }
1429             }
1430             if (strlen(artist) > 0) {
1431                 char * escaped_artist = xml_escape_malloc(artist);
1432                 if (escaped_artist != NULL) {
1433                     printf("    <creator>%s</creator>\n", escaped_artist);
1434                     free(escaped_artist);
1435                 }
1436             }
1437             if (duration > 0) {
1438                 printf("    <duration>%d</duration>\n", duration);
1439             }
1440             {
1441                 char * relative_location;
1442                 char * escaped_location;
1443                 relative_location = relative_uri_malloc(newpath, original_path);
1444                 if (relative_location != NULL) {
1445                     escaped_location = xml_escape_malloc(relative_location);
1446                     if (escaped_location != NULL) {
1447                         printf("    <location>%s</location>\n", escaped_location);
1448                         free(escaped_location);
1449                     }
1450                     free(relative_location);
1451                 }
1452             }
1453             printf("</track>\n");
1454             break;
1455 #endif
1456         }
1457     }
1460 void parse_directory(unsigned char *path, unsigned char * original_path)
1462     int i, n;
1463     struct dirent **namelist;
1464     unsigned char newpath[PATH_MAX];
1465     struct stat infos;
1467     if(debug)
1468         fprintf(stderr, "Debug >> parsing directory : %s\n", path);
1469     if(stat(path, &infos) != 0) {
1470         fprintf(stderr, "Warning >> can't stat entry : %s\n", path);
1471         return;
1472     }
1473     /* check if it is a filename */
1474     if(S_ISREG(infos.st_mode) || S_ISLNK(infos.st_mode)) {
1475         parse_file(path, original_path);
1476         return;
1477     }
1478     /* must be a directory - or something unusable like pipe, socket, etc */
1479     if((n = scandir(path, &namelist, 0, alphasort)) < 0) {
1480         fprintf(stderr, "Warning >> can't open directory : %s\n", path);
1481         return;
1482     }
1483     for(i = 0; i < n; i++) {
1484         snprintf(newpath, PATH_MAX, "%s/%s", path, namelist[i]->d_name);
1486         if(stat(newpath, &infos) != 0) {
1487             fprintf(stderr, "Warning >> can't stat entry : %s\n", newpath);
1488             continue;
1489         }
1490         if(recursive && S_ISDIR(infos.st_mode)
1491            && strcmp(namelist[i]->d_name, ".") != 0
1492            && strcmp(namelist[i]->d_name, "..") != 0)
1493             parse_directory(newpath, original_path);
1494         /* hlink_check() might be applied more selective ... avoidhlink is only a simple prereq */
1495         if(S_ISREG(infos.st_mode)
1496            && !(avoidhlinked && hlink_check(&infos))) {
1497             parse_file(newpath, original_path);
1498         }
1499         free(namelist[i]);
1500     }
1501     free(namelist);
1504 int main(int argc, char **argv)
1506     winorunix = one2one;
1507     basemap = one2one;
1508     parse_options(argc, argv);
1510     if(optind == argc && !fromstdin)
1511         usage();
1513     /* print header */
1514     switch (format) {
1515     case FORMAT_M3U:
1516         printf("#EXTM3U%s", eol);
1517         break;
1518     case FORMAT_PLS:
1519         printf("[playlist]%s", eol);
1520         break;
1521     case FORMAT_HTML:
1522         printf
1523             ("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">%s%s<html>%s%s<head>%s<title>Playlist generated by FAPG "
1524              VERSION
1525              "</title>%s<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" />%s<style type=\"text/css\">%s<!--%s%sbody,td,tr {%s font-family: Verdana, Arial, Helvetica, sans-serif;%s  font-size: 12px;%s  color: #000000;%s}%s%sbody {%s  background: #ffffff;%s}%s%sth {%s  text-align: center;%s  background: #ffcccc;%s  padding-left: 15px;%s  padding-right: 15px;%s  border: 1px #dd8888 solid;%s}%s%std {%s  text-align: center;%s  background: #eeeeee;%s  padding-left: 15px;%s  padding-right: 15px;%s  border: 1px #cccccc solid;%s}%s%sh1 {%s  font-size: 25px;%s}%s%sp {%s  font-size: 10px;%s}%s%sa {%s  color: #993333;%s  text-decoration: none;%s}%s%sa:hover {%s text-decoration: underline;%s}%s%s-->%s</style>%s</head>%s%s<body>%s%s<h1>Playlist</h1>%s%s<table>%s<tr><th>Entry</th><th>Artist</th><th>Title</th><th>Length</th></tr>%s",
1526              eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol,
1527              eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol,
1528              eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol,
1529              eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol,
1530              eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol,
1531              eol, eol, eol);
1532         break;
1533     case FORMAT_RSS:
1534         {
1535             time_t zeit;
1536             char timebuffer[256];
1537             time(&zeit);
1538             strftime(timebuffer, 255, "%a %d %b %Y %T %z",
1539                      localtime(&zeit));
1540             printf
1541                 ("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>%s<!-- generator=\"FAPG "
1542                  VERSION
1543                  " -->%s<rss xmlns:itunes=\"http://www.itunes.com/DTDs/Podcast-1.0.dtd\" version=\"2.0\">%s    <channel>%s\t<title>%s - %s - %s</title>%s\t<description>Directory Tree %s</description>%s\t<link>%s</link>%s\t<itunes:image href=\"%s/xml/podcast.jpg\"/>%s\t<lastBuildDate>%s</lastBuildDate>%s\t<generator>FAPG "
1544                  VERSION
1545                  "</generator>%s\t<image>%s\t\t<url>%s/podcast.jpg</url>%s\t\t<title>Server Logo</title>%s\t\t<link>%s</link>%s\t\t<description>Feed provided by FAPG. Click to visit.</description>%s\t</image>%s\t<itunes:owner>%s\t\t<itunes:name>Admin %s</itunes:name>%s\t\t<itunes:email>podcast@%s</itunes:email>%s\t</itunes:owner>%s\t<category>Various</category>%s\t<itunes:subtitle>Directory Tree %s</itunes:subtitle>%s\t<itunes:author>%s</itunes:author>%s\t<copyright>unknown</copyright>%s\t<language>%s</language>%s\t<itunes:explicit>No</itunes:explicit>%s\t<ttl>1800</ttl>%s",
1546                  eol, eol, eol, eol, hostname, dir, argv[optind], eol,
1547                  prefix, eol, base, eol, prefix, eol, timebuffer, eol, eol,
1548                  eol, base, eol, eol, base, eol, eol, eol, eol, base, eol,
1549                  hostname, eol, eol, eol, dir, eol, getenv("LOGNAME"), eol,
1550                  eol, getenv("LANG"), eol, eol, eol);
1551             unix2dos[38] = 43;  // I never made an rss feed work with '&' in it
1552             basemap = noand;
1553         }
1554         break;
1555     case FORMAT_PLP:
1556         {
1557             eol = "\r\n";
1558             myplaputstr("PLP PLAYLIST\r\nVERSION 1.20\r\n\r\n");
1559         }
1560         break;
1561     case FORMAT_UMS:
1562         {
1563             txxputheader("    iriver UMS PLA");
1564         }
1565         break;
1566 #ifdef HAVE_LIBURIPARSER
1567     case FORMAT_XSPF:
1568         printf("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
1569                 "<!-- generator=\"FAPG " VERSION " -->\n"
1570                 "<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\">\n"
1571                 "<trackList>\n");
1572         break;
1573 #endif
1574     }
1575     
1576     /* iterate through files */
1577     {
1578         const char * const pwd_source = getenv("PWD");
1579         const int pwdlen = strlen(pwd_source);
1580         char * const pwd = malloc((pwdlen + 1 + 1) * sizeof(char));
1581         sprintf(pwd, "%s/", pwd_source);
1582         
1583         if(fromstdin) {
1584             unsigned char path[PATH_MAX];
1585             int i;
1586             while(fgets(path, PATH_MAX, stdin)) {
1587                 for(i = 0; i < PATH_MAX; i++)
1588                     if(path[i] == '\r' || path[i] == '\n')
1589                         path[i] = '\0';
1590                 if (i <= 0) {
1591                     continue;
1592                 }
1594                 /* strip trailing slash */
1595                 if (path[i - 1] == '/') {
1596                     path[i - 1] = '\0';
1597                 }
1599                 parse_directory(path, pwd);
1600             }
1601         } else
1602             for(; optind < argc; optind++) {
1603                 /* strip trailing slash */
1604                 char * dup = strdup(argv[optind]);
1605                 const int len = strlen(dup);
1606                 if ((len > 0) && (dup[len - 1] == '/')) {
1607                     dup[len - 1] = '\0';
1608                 }
1610                 parse_directory(dup, pwd);
1611             }
1612             
1613         free(pwd);
1614     }
1616     /* print footer */
1617     switch (format) {
1618     case FORMAT_PLS:
1619         printf("NumberOfEntries=%d%sVersion=2%s", counter, eol, eol);
1620         break;
1621     case FORMAT_HTML:
1622         printf
1623             ("</table>%s%s<p>Playlist generated by <a href=\"http://royale.zerezo.com/fapg/\">FAPG "
1624              VERSION "</a></p>%s%s</body>%s%s</html>", eol, eol, eol, eol,
1625              eol, eol);
1626         break;
1627     case FORMAT_RSS:
1628         printf("    </channel>%s</rss>%s", eol, eol);
1629         break;
1630     case FORMAT_UMS:
1631         txxputcounter(counter);
1632         break;
1633 #ifdef HAVE_LIBURIPARSER
1634     case FORMAT_XSPF:
1635         printf("</trackList>\n"
1636                 "</playlist>\n");
1637         break;
1638 #endif
1639     }
1641     if(genrelist)
1642         free(genrelist);
1644     exit(0);