/*
- * FAPG version 0.22
- *
* FAPG means Fast Audio Playlist Generator.
* It is a tool to generate list of audio files (Wav, MP3, Ogg, etc)
* in various formats (M3U, PLS, HTML, etc).
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
+#include <strings.h>
#include <limits.h>
#include <unistd.h>
#include <ctype.h>
+#include <time.h>
+#include <assert.h>
+#include "genres.h"
+#ifdef HAVE_LIBURIPARSER
+# include <uriparser/Uri.h>
+#endif
#define MP3_BASE 1024
#define OGG_BASE 1024*10
-#define MAX 1024*200 /* 200ko for ID3 with JPEG images in it */
-
-int debug=0;
-int format=0; /* 0 = m3u ; 1 = pls ; 2 = html */
-unsigned char *prefix="";
-int recursive=0;
-int separator='/';
-int skip=0;
-int windows=0;
-unsigned char *eol="\n";
+#define MAX 1024*250 /* 250ko for ID3 with JPEG images in it */
+#define MAX_ITEM 1024
+
+#define FORMAT_M3U 0 /* "0" is not a good choice for debugging, but OK for a default */
+#define FORMAT_PLS 1
+#define FORMAT_HTML 2
+#define FORMAT_RSS 3
+#define FORMAT_PLP 4
+#define FORMAT_UMS 5
+#ifdef HAVE_LIBURIPARSER
+# define FORMAT_XSPF 6
+#endif
+
+int debug = 0;
+int format = FORMAT_M3U;
+char *genrelist = NULL;
+unsigned char *prefix = "";
+unsigned char *base = "";
+unsigned char *dir = "";
+unsigned char *hostname = "fritzserver.de";
+// unsigned char *referal="/usr/local/bin/fapg-rss.sh";
+unsigned char *referal = NULL;
+//int windows=0;
+int fromstdin = 0;
+int recursive = 0;
+int avoidhlinked = 0;
+int separator = '/';
+unsigned char *eol = "\n";
unsigned char buffer[MAX];
-int counter=0;
+int counter = 0;
-unsigned char artist[1024];
-unsigned char title[1024];
+unsigned char artist[MAX_ITEM];
+unsigned char title[MAX_ITEM];
+unsigned char genrebuf[MAX_ITEM];
+unsigned char genre = 0;
int duration;
+#define MP2ENC 1
+#define MP3ENC 2
+#define MPCENC 3
+#define MPPENC 4
+#define OGGENC 5
+#define WAVENC 6
+#define WMAENC 7
+
+char *magic[] = { NULL,
+ "audio/mpeg", "audio/mpeg",
+ "audio/mpeg", "audio/mpeg",
+ "audio/ogg-vorbis", "audio/x-wav",
+ "audio/x-ms-wma",
+ NULL
+};
+
+unsigned char unix2dos[] =
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 70, 35, 36, 37, 38, 39, 40, 41, 82, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 84, 59, 36, 61, 65, 71,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 36, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 36, 125, 126, 127,
+ 199, 252, 233, 226, 228, 224, 229, 231,
+ 234, 235, 232, 239, 238, 236, 196, 197,
+ 201, 230, 198, 244, 246, 242, 251, 249,
+ 255, 214, 220, 248, 163, 216, 215, 131,
+ 225, 237, 243, 250, 241, 209, 170, 186,
+ 191, 174, 172, 189, 188, 161, 171, 187,
+ 166, 166, 166, 166, 166, 193, 194, 192,
+ 169, 166, 166, 43, 43, 162, 165, 43,
+ 43, 45, 45, 43, 45, 43, 227, 195,
+ 43, 43, 45, 45, 166, 45, 43, 164,
+ 240, 208, 202, 203, 200, 105, 205, 206,
+ 207, 43, 43, 166, 220, 166, 204, 175,
+ 211, 223, 212, 210, 245, 213, 181, 254,
+ 222, 218, 219, 217, 253, 221, 175, 180,
+ 173, 177, 61, 190, 182, 167, 247, 184,
+ 176, 168, 183, 185, 179, 178, 166, 160
+};
-unsigned char unix2dos[256]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,70,35,36,37,38,39,40,41,82,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,84,59,36,61,65,71,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,36,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,36,125,126,127,199,252,233,226,228,224,229,231,234,235,232,239,238,236,196,197,201,230,198,244,246,242,251,249,255,214,220,248,163,216,215,131,225,237,243,250,241,209,170,186,191,174,172,189,188,161,171,187,166,166,166,166,166,193,194,192,169,166,166,43,43,162,165,43,43,45,45,43,45,43,227,195,43,43,45,45,166,45,43,164,240,208,202,203,200,105,205,206,207,43,43,166,220,166,204,175,211,223,212,210,245,213,181,254,222,218,219,217,253,221,175,180,173,177,61,190,182,167,247,184,176,168,183,185,179,178,166,160};
+unsigned char *basemap;
+unsigned char *winorunix;
+unsigned char one2one[] =
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135,
+ 136, 137, 138, 139, 140, 141, 142, 143,
+ 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183,
+ 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 205, 206, 207,
+ 208, 209, 210, 211, 212, 213, 214, 215,
+ 216, 217, 218, 219, 220, 221, 222, 223,
+ 224, 225, 226, 227, 228, 229, 230, 231,
+ 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 254, 255
+}; /* identical mapping */
+
+unsigned char noand[256] =
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 43, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135,
+ 136, 137, 138, 139, 140, 141, 142, 143,
+ 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183,
+ 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 205, 206, 207,
+ 208, 209, 210, 211, 212, 213, 214, 215,
+ 216, 217, 218, 219, 220, 221, 222, 223,
+ 224, 225, 226, 227, 228, 229, 230, 231,
+ 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 254, 255
+}; /* only '&' is mapped to '+' */
+
+unsigned char *iso2web[256] = {
+ "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
+ "%08", "%09", "%0a", "%0b", "%0c", "%0d", "%0e", "%0f",
+ "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
+ "%18", "%19", "%1a", "%1b", "%1c", "%1d", "%1e", "%1f",
+ "%20", "%21", "%22", "%23", "%24", "%25", "%26", "%27",
+ "%28", "%29", "%2a", "+", ",", "-", ".", "/",
+ "0", "1", "2", "3", "4", "5", "6", "7",
+ "8", "9", ":", ";", "%3c", "=", "%3e", "%3f",
+ "@", "A", "B", "C", "D", "E", "F", "G",
+ "H", "I", "J", "K", "L", "M", "N", "O",
+ "P", "Q", "R", "S", "T", "U", "V", "W",
+ "X", "Y", "Z", "%5B", "\\", "%5D", "^", "_",
+ "`", "a", "b", "c", "d", "e", "f", "g",
+ "h", "i", "j", "k", "l", "m", "n", "o",
+ "p", "q", "r", "s", "t", "u", "v", "w",
+ "x", "y", "z", "%7b", "|", "%7d", "~", "%7f",
+ "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",
+ "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f",
+ "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",
+ "%98", "%99", "%9a", "%9b", "%9c", "%9d", "%9e", "%9f",
+ "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7",
+ "%a8", "%a9", "%aa", "%ab", "%ac", "%ad", "%ae", "%af",
+ "%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7",
+ "%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf",
+ "%c0", "%c1", "%c2", "%c3", "%c4", "%c5", "%c6", "%c7",
+ "%c8", "%c9", "%ca", "%cb", "%cc", "%cd", "%ce", "%cf",
+ "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
+ "%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df",
+ "%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7",
+ "%e8", "%e9", "%ea", "%eb", "%ec", "%ed", "%ee", "%ef",
+ "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
+ "%f8", "%f9", "%fa", "%fb", "%fc", "%fd", "%fe", "%ff"
+};
void usage()
{
- fprintf(stderr,"Usage >> fapg [-b|--backslash] [-d|--debug] [-f|--format=m3u|pls|html] [-o|--output=/path/to/file.m3u] [-p|--prefix=/the/prefix] [-r|--recursive] [-w|--windows] /path/to/mp3/dir1 [/path/to/mp3/dir2 ...]\n");
- exit(1);
+#ifdef HAVE_LIBURIPARSER
+# define FAPG_FORMATS "m3u|pls|xspf|html|rss|pla|txx"
+#else
+# define FAPG_FORMATS "m3u|pls|html|rss|pla|txx"
+#endif
+ fprintf(stderr,
+ "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");
+#undef FAPG_FORMATS
+ exit(1);
}
-void parse_options(int argc,char **argv)
+#define mywebputchar(x) { fputs(iso2web[(unsigned char)winorunix[(unsigned char)x]], stdout); }
+#define myputchar(x) { putchar(basemap[(unsigned char)winorunix[(unsigned char)x]]); }
+/* #define myplaputchar(x) { putchar(basemap[(unsigned char)winorunix[(unsigned char)x]]);putchar('\0');} */
+void myplaputchar(const char x)
{
- static char const short_options[]="bdf:o:p:rw";
- static struct option long_options[]=
- {
- {"backslash",no_argument,NULL,'b'},
- {"debug",no_argument,NULL,'d'},
- {"format",required_argument,NULL,'f'},
- {"output",required_argument,NULL,'o'},
- {"prefix",required_argument,NULL,'p'},
- {"recursive",no_argument,NULL,'r'},
- {"windows",no_argument,NULL,'w'}
- };
- int c;
- int option_index=0;
- while ((c=getopt_long(argc,argv,short_options,long_options,&option_index))!=-1)
- {
- switch(c)
- {
- case 'b':
- separator='\\';
- break;
- case 'd':
- debug=1;
- break;
- case 'f':
- if (strcmp(optarg,"m3u")==0) format=0; else
- if (strcmp(optarg,"pls")==0) format=1; else
- if (strcmp(optarg,"html")==0) format=2; else
- usage();
- break;
- case 'o':
- close(1);
- if (fopen(optarg,"w")==NULL) { fprintf(stderr,"Error >> unable to open output file : %s\n",optarg); exit(2); }
- break;
- case 'p':
- prefix=malloc(strlen(optarg)+1);
- strcpy(prefix,optarg);
- break;
- case 'r':
- recursive=1;
- break;
- case 'w':
- windows=1;
- eol="\r\n";
- break;
- default:
- usage();
+ putchar(basemap[(unsigned char)winorunix[(unsigned char)x]]);
+ putchar('\0');
+}
+
+void mywebputstr(const char *c)
+{
+ while(*c != 0) {
+ mywebputchar(*c);
+ c++;
}
- }
}
-void parse_mp3(unsigned char *file)
+void utf16toutf8(char *c,int n)
{
- int bitrates[2][3][15]=
- {{{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448},
- {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384},
- {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}},
- {{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256},
- {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160},
- {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}}};
- FILE *fic;
- unsigned char *c;
- int lus;
-
- if (debug) fprintf(stderr,"Debug >> parsing mp3 : %s\n",file);
-
- /* read header */
- if ((fic=fopen(file,"r"))==NULL) { fprintf(stderr,"Warning >> can't open file : %s\n",file); return; }
- lus=fread(buffer,1,MP3_BASE,fic);
- c=buffer;
-
- /* try ID3v2 */
- if (buffer[0]=='I' && buffer[1]=='D' && buffer[2]=='3')
- {
- int size;
- int version;
- version=*(buffer+3);
- if (version<2 || version>4)
- fprintf(stderr,"Warning >> ID3 v2.%d not implemented ! trying anyway : %s\n",version,file);
- if (*(buffer+5)!=0)
- fprintf(stderr,"Warning >> specials headers not implemented (%d) ! trying anyway : %s\n",*(buffer+5),file);
- c=buffer+6;
- size=(*c<<21)+(*(c+1)<<14)+(*(c+2)<<7)+(*(c+3));
- /* read more header */
- if (size+lus>MAX)
- {
- lus+=fread(buffer+lus,1,MAX-lus,fic);
- fprintf(stderr,"Warning >> ID3 header is huge (%d bytes) ! trying anyway : %s\n",size,file);
+ /* check whether the we need to convert UTF-16 to UTF-8 strings */
+ if ( ( c[0] != '\377' ) || ( c[1] != '\376' ) ) { return; }
+ /* only continue here, if the first 2 letters are 0xfffe *
+ * c references an UTF-16 input, where latin letters are *
+ * separated by zero bytes, which we need to eliminate */
+ int i=0; --n;
+ for(int j=2; (j<n) && (j<MAX_ITEM); j++ ) {
+ if( isprint(c[j]) ) { /* this is not perfect ! */
+ c[i++]=c[j];
+ } } c[i+1]=c[i]='\0';
+ /* the index i follows the zero-terminated "string" *
+ * now the read buffer is modified, not the file */
+ return;
+}
+
+void myplaputstr(const char *c)
+{
+ while(*c != 0) {
+ if(*c == '/')
+ myplaputchar('\\'); /* translate slash to backslash */
+ else
+ myplaputchar(*c);
+ c++;
+ /* remove multiple slashes "//" when parsing a directory ending with a "/" */
+ while(*c == '/' && c[1] == '/')
+ c++;
+ }
+}
+
+void myputstr(const char *c)
+{
+ while(*c != 0) {
+ if(*c == '/')
+ putchar(separator);
+ else
+ myputchar(*c);
+ c++;
+ /* remove multiple slashes "//" when parsing a directory ending with a "/" */
+ while(*c == '/' && c[1] == '/')
+ c++;
+ }
+}
+
+void txxputheader(const char *c)
+{
+ int cnt = 0;
+
+ while(*c != 0) {
+ myputchar(*c);
+ cnt++;
+ c++;
+ }
+
+ while(cnt < 512) {
+ putchar('\0');
+ cnt++;
+ }
+}
+
+void txxputnameoffset(const char *c)
+{
+ int pos = 0;
+ int cnt = 0;
+ char b;
+ unsigned char *prefx;
+
+ prefx = prefix;
+
+ if(*prefx != 0) {
+ while(*prefx != 0) {
+ if(*prefx == '/') {
+ pos = cnt;
+ }
+ cnt++;
+ prefx++;
+ }
+
+ cnt--; // skip the leading dot of the filepath
+ }
+
+ while(*c != 0) {
+ if(*c == '/') {
+ pos = cnt;
+ }
+ cnt++;
+ c++;
+ }
+
+ pos += 2;
+
+ b = (pos & 0xFF00) >> 8;
+ putchar(b);
+ b = (pos & 0x00FF);
+ putchar(b);
+}
+
+void txxputstr(const char *c)
+{
+ int cnt = 0;
+ int pos;
+ unsigned char *prefx;
+
+ txxputnameoffset(c);
+
+ prefx = prefix;
+ fprintf(stderr, "prefix: '%s'\n", prefx);
+
+ if(*prefx != 0) {
+ while(*prefx != 0) {
+ myputchar('\0');
+ cnt++;
+
+ if(*prefx == '/')
+ putchar(separator);
+ else
+ myputchar(*prefx);
+ cnt++;
+
+ prefx++;
+ }
+
+ c++; // skip the leading dot
}
+
+ while(*c != 0) {
+ myputchar('\0');
+ cnt++;
+
+ if(*c == '/')
+ putchar(separator);
+ else
+ myputchar(*c);
+ cnt++;
+
+ c++;
+ }
+
+ while(cnt < 510) {
+ myputchar('\0');
+ cnt++;
+ }
+}
+
+void txxputcounter(int c)
+{
+ int b;
+
+ rewind(stdout);
+
+ b = (c & 0xFF000000) >> 24;
+ putchar(b);
+ b = (c & 0x00FF0000) >> 16;
+ putchar(b);
+ b = (c & 0x0000FF00) >> 8;
+ putchar(b);
+ b = (c & 0x000000FF);
+ putchar(b);
+}
+
+/* remove spaces at beginning and end of string */
+void trim(char *c)
+{
+ char *p;
+ /* remove spaces at beginning ... */
+ while(*c == ' ') {
+ p = c;
+ while(*p != '\0') {
+ *p = *(p + 1);
+ p++;
+ }
+ }
+ /* ... and end of string */
+ p = c + strlen(c);
+ while(--p > c && *p == ' ')
+ *p = '\0';
+}
+
+void print_webpath(const char *path)
+{
+ const char *c = path;
+
+ printf("%s", prefix); /* we must not modify this part */
+ if(*c == '.' && c[1] == '/') { /* remove leading "./" when parsing current directory */
+ c += 2;
+ /* maybe there follow many slashes */
+ while(*c == '/')
+ c++;
+ }
+ for(; *c != '\0'; c++) {
+ mywebputchar(*c);
+ /* remove multiple "//" when parsing a directory ending with a "/" */
+ while(*c == '/' && c[1] == '/')
+ c++;
+ }
+}
+
+void print_path(const char *path)
+{
+ const char *c = path;
+ printf("%s", prefix);
+ /* skip leading "./" when parsing current directory */
+ if(*c == '.' && *(c + 1) == '/') {
+ c += 2;
+ /* maybe there follow more slashes */
+ while(*c == '/')
+ c++;
+ }
+ myputstr(c);
+}
+
+void print_pathtail(const char *path)
+{
+ const char *c;
+ c = strrchr(path, separator);
+ if(c != NULL)
+ c++;
else
- lus+=fread(buffer+lus,1,size,fic);
- if (size>lus) size=lus;
- c+=4;
- if (version==2) while (c<buffer+size)
- {
- int size=(*(c+3)<<16)+(*(c+4)<<8)+(*(c+5));
- if (*c==0) break;
- if (strncmp(c,"TT2",3)==0)
- {
- strncpy(title,c+7,size-1);
- title[size-1]='\0';
- }
- if (strncmp(c,"TP1",3)==0)
- {
- strncpy(artist,c+7,size-1);
- artist[size-1]='\0';
- }
- c+=size+6;
- }
- if (version==3 || version==4) while (c<buffer+size)
- {
- int size=(*(c+4)<<24)+(*(c+5)<<16)+(*(c+6)<<8)+(*(c+7));
- if (*c==0) break;
- if (strncmp(c,"TIT2",4)==0)
- {
- strncpy(title,c+11,size-1);
- title[size-1]='\0';
- }
- if (strncmp(c,"TPE1",4)==0)
- {
- strncpy(artist,c+11,size-1);
- artist[size-1]='\0';
- }
- c+=size+10;
- }
- }
-
- while (c<buffer+lus-10)
- {
- if (*c==0xFF && (*(c+1)&0xF0)==0xF0)
- {
- int version;
- int lay;
- int bitrate_index;
- int bitrate;
- version=2-(*(c+1)>>3&1);
- lay=4-(*(c+1)>>1&3);
- bitrate_index=*(c+2)>>4&0xF;
- if (version>=1 && version<=2 && lay-1>=0 && lay-1<=2 && bitrate_index>=0 && bitrate_index<=14)
- bitrate=bitrates[version-1][lay-1][bitrate_index];
- else
- bitrate=0;
- if (bitrate!=0)
- {
- fseek(fic,0,SEEK_END);
- duration=(ftell(fic)+buffer-c)/125/bitrate;
- }
- else
- duration=0;
- break;
- }
- c++;
- }
-
- /* try ID3v1 */
- if (strlen(artist)==0 && strlen(title)==0)
- {
- fseek(fic,-128,SEEK_END);
- lus=fread(buffer,1,128,fic);
- if (lus==128 && buffer[0]=='T' && buffer[1]=='A' && buffer[2]=='G')
- {
- strncpy(title,buffer+3,30);
- title[30]='\0';
- c=title+29;
- while (c>title && *c==' ') *(c--)='\0';
- strncpy(artist,buffer+33,30);
- artist[30]='\0';
- c=artist+29;
- while (c>artist && *c==' ') *(c--)='\0';
+ c = path;
+ myputstr(c);
+}
+
+void noreferal(const char *path, const char *artist, const char *title)
+{
+ printf("\t\t<description><![CDATA[<h4>");
+ myputstr(artist);
+ printf("</h4><h5>");
+ myputstr(title);
+ printf("</h5><a href=\"");
+ print_webpath(path);
+ printf
+ ("\"><br><br>Direct Link to Audiofile</a><br>]]></description>%s",
+ eol);
+}
+
+void reference(const char *title)
+{
+ FILE *pipe = NULL;
+ static char command[2048], buffer[1024];
+ int buflen = 8192;
+
+ buflen = strlen(title) + strlen(referal) + 3;
+ assert((buflen < 2046));
+ strcpy(command, referal);
+ buflen = strlen(command);
+ command[buflen] = ' ';
+ command[buflen + 1] = '"';
+ command[buflen + 2] = 0;
+ strcat(command, title);
+ buflen = strlen(command);
+ command[buflen] = '"';
+ command[buflen + 1] = 0;
+ if(debug)
+ fprintf(stderr, "Debug >> processing command: %s\n", command);
+ pipe = popen(command, "r");
+ if(pipe == NULL) {
+ fprintf(stderr, "Warning >> can't open pipe >%s< !\n", command);
+ return;
}
- }
+ fgets(buffer, 1020, pipe);
+ while(!feof(pipe)) {
+ fputs(buffer, stdout);
+ fgets(buffer, 1020, pipe);
+ }
+ pclose(pipe);
+ return;
+}
- fclose(fic);
-}
+void parse_options(int argc, char **argv)
+{
+ static char const short_options[] = "bc:df:g:lo:np:rsuwx:";
+ static struct option long_options[] = {
+ {"backslash", no_argument, NULL, 'b'},
+ {"command", required_argument, NULL, 'c'},
+ {"debug", no_argument, NULL, 'd'},
+ {"format", required_argument, NULL, 'f'},
+ {"genre", required_argument, NULL, 'g'},
+ {"nohardlink", no_argument, NULL, 'n'},
+ {"output", required_argument, NULL, 'o'},
+ {"prefix", required_argument, NULL, 'p'},
+ {"recursive", no_argument, NULL, 'r'},
+ {"stdin", no_argument, NULL, 's'},
+ {"windows", no_argument, NULL, 'w'},
+ {"exclude", required_argument, NULL, 'x'},
+ {NULL, 0, NULL, 0}
+ };
+ int c;
+ int option_index = 0;
+ while((c =
+ getopt_long(argc, argv, short_options, long_options,
+ &option_index)) != -1) {
+ switch (c) {
+ case 'b':
+ separator = '\\';
+ noand['/'] = '\\';
+ break;
+ case 'c':
+ if(strncmp(optarg, "intern", 6) == 0)
+ referal = NULL;
+ else
+ referal = strdup(optarg);
+ break;
+ case 'd':
+ debug = 1 - debug;
+ break;
+ case 'f':
+ if(strcmp(optarg, "m3u") == 0)
+ format = FORMAT_M3U;
+ else if(strcmp(optarg, "pls") == 0)
+ format = FORMAT_PLS;
+ else if(strcmp(optarg, "html") == 0)
+ format = FORMAT_HTML;
+ else if(strcmp(optarg, "rss") == 0)
+ format = FORMAT_RSS;
+ else if(strcmp(optarg, "pla") == 0)
+ format = FORMAT_PLP;
+ else if(strcmp(optarg, "txx") == 0)
+ format = FORMAT_UMS;
+#ifdef HAVE_LIBURIPARSER
+ else if(strcmp(optarg, "xspf") == 0)
+ format = FORMAT_XSPF;
+#endif
+ else
+ usage();
+ break;
+ case 'g':
+ if(genrelist == NULL)
+ genrelist = calloc(257, sizeof(char)); /* allow multiple includes/excludes */
+ if(genrelist == NULL) {
+ fprintf(stderr,
+ "Error >> unable to allocate cleared memory\n");
+ exit(2);
+ } else {
+ unsigned int n = 0;
+ while(n < strlen(optarg)) {
+ if(debug)
+ fprintf(stderr,
+ "Debug >> genrelist entry activting : %d\n",
+ atoi(&optarg[n]));
+ genrelist[atoi(&optarg[n])] = 1;
+ while(isdigit(optarg[n++]));
+ }
+ }
+ break;
+ case 'n':
+ avoidhlinked = 1;
+ break;
+ case 'o':
+ close(1);
+ if(fopen(optarg, "w") == NULL) {
+ fprintf(stderr,
+ "Error >> unable to open output file : %s\n",
+ optarg);
+ exit(2);
+ }
+ break;
+ case 'p':
+ prefix = malloc(strlen(optarg) + 1);
+ strcpy(prefix, optarg);
+ base = malloc(strlen(prefix) + 1);
+ strcpy(base, prefix);
+ dir = strchr(base, '/');
+ if((dir != NULL) && (dir[1] == '/'))
+ dir = strchr(dir + 2, '/');
+ if(dir != NULL)
+ *dir++ = 0;
+ else
+ dir = "";
+ /* if prefix is a weblink, base is the baselink, dir is the path */
+ break;
+ case 'r':
+ recursive = 1;
+ break;
+ case 'u':
+ winorunix = one2one;
+ eol = "\n";
+ break;
+ case 'w':
+ winorunix = unix2dos;
+ eol = "\r\n";
+ break;
+ case 'x':
+ if(genrelist == NULL) { /* allow multiple includes/excludes - not recommended (confusing) but possible */
+ int n = 0;
+ genrelist = calloc(257, sizeof(char));
+ while(n < 256)
+ genrelist[n++] = 1;
+ }
+ if(genrelist == NULL) {
+ fprintf(stderr,
+ "Error >> unable to allocate cleared memory\n");
+ exit(2);
+ } else {
+ unsigned int n = 0;
+ while(n < strlen(optarg)) {
+ if(debug)
+ fprintf(stderr,
+ "Debug >> genrelist entry activting : %d\n",
+ atoi(&optarg[n]));
+ genrelist[atoi(&optarg[n])] = 0;
+ while(isdigit(optarg[n++]));
+ }
+ }
+ break;
+ case 's':
+ fromstdin = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ /* hostname = getenv("HOSTNAME"); */
+ if(genrelist == NULL) {
+ genrelist = calloc(257, sizeof(char));
+ if(genrelist == NULL) {
+ fprintf(stderr,
+ "Error >> unable to allocate cleared memory\n");
+ exit(2);
+ } else {
+ int n = 0;
+ while(n < 256)
+ genrelist[n++] = 1;
+ }
+ }
+}
+
+void parse_mp3(unsigned char *file)
+{
+ int bitrates[2][3][15] =
+ { {{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384,
+ 416, 448},
+ {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,
+ 384},
+ {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256,
+ 320}},
+ {{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256},
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160},
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}}
+ };
+ FILE *fic;
+ unsigned char *c;
+ int lus;
+
+ genre = 0;
+ genrebuf[0] = 0;
+ if(debug)
+ fprintf(stderr, "Debug >> parsing mp3 : %s\n", file);
+
+ /* read header */
+ if((fic = fopen(file, "rb")) == NULL) {
+ fprintf(stderr, "Warning >> can't open file : %s\n", file);
+ return;
+ }
+ lus = fread(buffer, 1, MP3_BASE, fic);
+ c = buffer;
+
+ /* try ID3v2 */
+ if(buffer[0] == 'I' && buffer[1] == 'D' && buffer[2] == '3') {
+ int size;
+ int version;
+ version = *(buffer + 3);
+ if(version < 2 || version > 4)
+ fprintf(stderr,
+ "Warning >> ID3 v2.%d not implemented ! trying anyway : %s\n",
+ version, file);
+ if(*(buffer + 5) != 0)
+ fprintf(stderr,
+ "Warning >> specials headers not implemented (%d) ! trying anyway : %s\n",
+ *(buffer + 5), file);
+ c = buffer + 6;
+ size =
+ (*c << 21) + (*(c + 1) << 14) + (*(c + 2) << 7) + (*(c + 3));
+ /* read more header */
+ if(size + lus > MAX) {
+ lus += fread(buffer + lus, 1, MAX - lus, fic);
+ fprintf(stderr,
+ "Warning >> ID3 header is huge (%d bytes) ! trying anyway : %s\n",
+ size, file);
+ } else
+ lus += fread(buffer + lus, 1, size, fic);
+ if(size > lus)
+ size = lus;
+ c += 4;
+ if(version == 2)
+ while(c < buffer + size) {
+ int size = (*(c + 3) << 16) + (*(c + 4) << 8) + (*(c + 5));
+ if(*c == 0)
+ break;
+ if(strncmp(c, "TT2", 3) == 0) {
+ utf16toutf8(c+7,size);
+ strncpy(title, c + 7, size - 1);
+ title[size - 1] = '\0';
+ }
+ if(strncmp(c, "TP1", 3) == 0) {
+ utf16toutf8(c+7,size);
+ strncpy(artist, c + 7, size - 1);
+ artist[size - 1] = '\0';
+ }
+ if(strncmp(c, "TCO", 3) == 0) {
+ /* strncpy(genrebuf,c+7,size-1); */
+ /* genrebuf[size-1]='\0'; */
+ /* genre=atoi(&genrebuf[1]); */
+ genre = atoi(c + 8);
+ }
+ c += size + 6;
+ }
+ if(version == 3 || version == 4)
+ while(c < buffer + size) {
+ int size =
+ (*(c + 4) << 24) + (*(c + 5) << 16) + (*(c + 6) << 8) +
+ (*(c + 7));
+ if(*c == 0)
+ break;
+ if(strncmp(c, "TIT2", 4) == 0) {
+ utf16toutf8(c+11,size);
+ strncpy(title, c + 11, size - 1);
+ title[size - 1] = '\0';
+ }
+ if(strncmp(c, "TPE1", 4) == 0) {
+ utf16toutf8(c+11,size);
+ strncpy(artist, c + 11, size - 1);
+ artist[size - 1] = '\0';
+ }
+ if(strncmp(c, "TCON", 4) == 0) {
+ /* strncpy(genrebuf,c+11,size-1); */
+ /* genrebuf[size-1]='\0'; */
+ /* genre=atoi(&genrebuf[1]); */
+ genre = atoi(c + 12);
+ }
+ if(strncmp(c, "TLEN", 4) == 0) {
+ duration = atoi(c + 11) / 1000;
+ }
+ c += size + 10;
+ }
+ }
+
+ while(c < buffer + lus - 10) {
+ if(*c == 0xFF && (*(c + 1) & 0xF0) == 0xF0) {
+ int version;
+ int lay;
+ int bitrate_index;
+ int bitrate;
+ version = 2 - (*(c + 1) >> 3 & 1);
+ lay = 4 - (*(c + 1) >> 1 & 3);
+ bitrate_index = *(c + 2) >> 4 & 0xF;
+ if(version >= 1 && version <= 2 && lay - 1 >= 0 && lay - 1 <= 2
+ && bitrate_index >= 0 && bitrate_index <= 14)
+ bitrate = bitrates[version - 1][lay - 1][bitrate_index];
+ else
+ bitrate = 0;
+ if(bitrate != 0) {
+ fseek(fic, 0, SEEK_END);
+ duration = (ftell(fic) + buffer - c) / 125 / bitrate;
+ } else
+ duration = 0;
+ break;
+ }
+ c++;
+ }
+
+ /* try ID3v1 */
+ if(strlen(artist) == 0 && strlen(title) == 0) {
+ fseek(fic, -128, SEEK_END);
+ lus = fread(buffer, 1, 128, fic);
+ if(lus == 128 && buffer[0] == 'T' && buffer[1] == 'A'
+ && buffer[2] == 'G') {
+ strncpy(title, buffer + 3, 30);
+ title[30] = '\0';
+ c = title + 29;
+ while(c > title && *c == ' ')
+ *(c--) = '\0';
+ strncpy(artist, buffer + 33, 30);
+ artist[30] = '\0';
+ c = artist + 29;
+ while(c > artist && *c == ' ')
+ *(c--) = '\0';
+ /* strncpy(album,buffer+65,30); */
+ /* strncpy(year,buffer+97,4); */
+ /* strncpy(comment,buffer+101,30); */
+ /* strncpy(genrebuf,buffer+127,1); genre[1]=0; */
+ genre = buffer[127];
+ }
+ }
+
+ fclose(fic);
+}
void parse_ogg(unsigned char *file)
{
- FILE *fic;
- unsigned char *c;
- int lus;
- int sample_rate;
- int samples;
-
- if (debug) fprintf(stderr,"Debug >> parsing ogg : %s\n",file);
-
- /* read header */
- if ((fic=fopen(file,"r"))==NULL) { fprintf(stderr,"Warning >> can't open file : %s\n",file); return; }
- lus=fread(buffer,1,OGG_BASE,fic);
-
- /* try Ogg */
- if (buffer[0]!='O' && buffer[1]!='g' && buffer[2]!='g')
- {
- fprintf(stderr,"Warning >> not a Ogg header : %s\n",file);
- return;
- }
-
- c=buffer+0x28;
- sample_rate=(*c)+(*(c+1)<<8)+(*(c+2)<<16)+(*(c+3)<<24);
+ FILE *fic;
+ unsigned char *c;
+ int lus;
+ int sample_rate;
+ int samples;
- while (c<buffer+lus-10)
- {
- int size;
- if (strncasecmp(c,"TITLE=",6)==0)
- {
- size=*(c-4)+(*(c-3)<<8)+(*(c-2)<<16)+(*(c-1)<<24);
- strncpy(title,c+6,size-6);
- title[size-6]='\0';
- c+=size;
+ if(debug)
+ fprintf(stderr, "Debug >> parsing ogg : %s\n", file);
+
+ /* read header */
+ if((fic = fopen(file, "r")) == NULL) {
+ fprintf(stderr, "Warning >> can't open file : %s\n", file);
+ return;
}
- if (strncasecmp(c,"ARTIST=",7)==0)
- {
- size=*(c-4)+(*(c-3)<<8)+(*(c-2)<<16)+(*(c-1)<<24);
- strncpy(artist,c+7,size-7);
- artist[size-7]='\0';
- c+=size;
+ lus = fread(buffer, 1, OGG_BASE, fic);
+
+ /* try Ogg */
+ if(strncmp(buffer, "Ogg", 3) != 0) {
+ fprintf(stderr, "Warning >> not a Ogg header : %s\n", file);
+ return;
}
- c++;
- }
-
- fseek(fic,-OGG_BASE,SEEK_END);
- lus=fread(buffer,1,OGG_BASE,fic);
- c=buffer+lus-1;
- while (strncmp(c,"OggS",4)!=0 && c>buffer) c--;
- if (c!=buffer)
- {
- c+=6;
- samples=(*c)+(*(c+1)<<8)+(*(c+2)<<16)+(*(c+3)<<24);
- duration=samples/sample_rate;
- }
-
- fclose(fic);
+
+ c = buffer + 0x28;
+ sample_rate =
+ (*c) + (*(c + 1) << 8) + (*(c + 2) << 16) + (*(c + 3) << 24);
+
+ while(c < buffer + lus - 10) {
+ int size;
+ if(strncasecmp(c, "TITLE=", 6) == 0) {
+ size =
+ *(c - 4) + (*(c - 3) << 8) + (*(c - 2) << 16) +
+ (*(c - 1) << 24);
+ strncpy(title, c + 6, size - 6);
+ title[size - 6] = '\0';
+ c += size;
+ }
+ if(strncasecmp(c, "ALBUM ARTIST=", 13) == 0) {
+ // ignore tag
+ size =
+ *(c - 4) + (*(c - 3) << 8) + (*(c - 2) << 16) +
+ (*(c - 1) << 24);
+ c += size;
+ }
+ if(strncasecmp(c, "ARTIST=", 7) == 0) {
+ size =
+ *(c - 4) + (*(c - 3) << 8) + (*(c - 2) << 16) +
+ (*(c - 1) << 24);
+ strncpy(artist, c + 7, size - 7);
+ artist[size - 7] = '\0';
+ c += size;
+ }
+ if(strncasecmp(c, "GENRE=", 6) == 0) {
+ static int i = 0;
+ size =
+ *(c - 4) + (*(c - 3) << 8) + (*(c - 2) << 16) +
+ (*(c - 1) << 24);
+ strncpy(genrebuf, c + 6, size - 6);
+ genrebuf[size - 6] = '\0';
+ c += size;
+ for(i = 0; i < ID3_NR_OF_V1_GENRES; i++) {
+ if(strcasecmp(ID3_v1_genre_description[i], genrebuf) == 0) {
+ genre = i;
+ break;
+ }
+ if(i == ID3_NR_OF_V1_GENRES)
+ genre = 0;
+ }
+ }
+ c++;
+ }
+
+ fseek(fic, -OGG_BASE, SEEK_END);
+ lus = fread(buffer, 1, OGG_BASE, fic);
+ c = buffer + lus - 1;
+ while(strncmp(c, "OggS", 4) != 0 && c > buffer)
+ c--;
+ if(c != buffer) {
+ c += 6;
+ samples =
+ (*c) + (*(c + 1) << 8) + (*(c + 2) << 16) + (*(c + 3) << 24);
+ duration = samples / sample_rate;
+ }
+
+ fclose(fic);
}
-void parse_directory(unsigned char *path)
+void parse_mpc(unsigned char *file)
{
- int i,n;
- struct dirent **namelist;
- unsigned char newpath[PATH_MAX];
- unsigned char *c;
- struct stat infos;
-
- void print_faketitle()
- {
- c=newpath+strlen(newpath);
- while (c>newpath && *c!='/') c--;
- while (c<newpath+strlen(newpath)-5)
- {
- c++;
- if (*c=='_') putchar(' '); else if (windows) putchar(unix2dos[*c]); else putchar(*c);
+ FILE *fic;
+ unsigned char *c;
+ int lus;
+ int sample_rates[4] = { 44100, 48000, 37800, 32000 };
+ int frame_count;
+ int size, items;
+ int i;
+
+ if(debug)
+ fprintf(stderr, "Debug >> parsing mpc : %s\n", file);
+
+ /* read header */
+ if((fic = fopen(file, "r")) == NULL) {
+ fprintf(stderr, "Warning >> can't open file : %s\n", file);
+ return;
}
- }
+ lus = fread(buffer, 1, 12, fic);
+
+ /* try Musepack */
+ if (strncmp(buffer, "MP+", 3) != 0) {
+ fprintf(stderr, "Warning >> not a Musepack header : %s\n", file);
+ return;
+ }
+
+ /* only version 7 */
+ if(buffer[3] != 7) {
+ fprintf(stderr, "Warning >> only Musepack SV7 supported : %s\n",
+ file);
+ return;
+ }
+
+ /* duration */
+ c = buffer + 4;
+ frame_count =
+ (*c) + (*(c + 1) << 8) + (*(c + 2) << 16) + (*(c + 3) << 24);
+ c += 5;
+ duration = frame_count * 1152 / sample_rates[*c & 3];
+
+ /* try APETAGEX footer */
+ fseek(fic, -32, SEEK_END);
+ lus = fread(buffer, 1, 32, fic);
+ if(lus == 32 && strncmp(buffer, "APETAGEX", 8) == 0) {
+ c = buffer + 12;
+ size =
+ (*c) + (*(c + 1) << 8) + (*(c + 2) << 16) + (*(c + 3) << 24);
+ size += 32;
+ c += 4;
+ items =
+ (*c) + (*(c + 1) << 8) + (*(c + 2) << 16) + (*(c + 3) << 24);
+ fseek(fic, -size, SEEK_END);
+ lus = fread(buffer, 1, size, fic);
+ if(lus == size && strncmp(buffer, "APETAGEX", 8) == 0) {
+ c = buffer + 32;
+ while(items--) {
+ size =
+ (*c) + (*(c + 1) << 8) + (*(c + 2) << 16) +
+ (*(c + 3) << 24);
+ c += 8;
+ if(strcasecmp(c, "TITLE") == 0) {
+ strncpy(title, c + 6, size);
+ title[size] = '\0';
+ }
+ if(strcasecmp(c, "ARTIST") == 0) {
+ strncpy(artist, c + 7, size);
+ artist[size] = '\0';
+ }
+ if(strcasecmp(c, "GENRE") == 0) {
+ for(i = 0; i < ID3_NR_OF_V1_GENRES; i++) {
+ strncpy(genrebuf, c + 6, size);
+ genrebuf[size] = '\0';
+ if(strcasecmp
+ (ID3_v1_genre_description[i], genrebuf) == 0) {
+ genre = i;
+ break;
+ }
+ if(i == ID3_NR_OF_V1_GENRES)
+ genre = 0;
+ }
+ }
+ c += strlen(c) + 1 + size;
+ }
+ }
+ }
+
+ fclose(fic);
+}
+
+#define FSN 32
+#define MAXINO (1<<24)
+#define INOTYP unsigned long
+#define regbit_qry(x,y) ( x[( (y) / sizeof(INOTYP) )] & 1<<( (y) % sizeof(INOTYP) ) )
+#define regbit_set(x,y) ( x[( (y) / sizeof(INOTYP) )] |= 1<<( (y) % sizeof(INOTYP) ) )
+
+int hlink_check(struct stat *info)
+{
+ /*
+ * for speed this subroutine should only be called
+ * - if the file has more than one hardlink
+ * - if the file is a resolved softlink
+ */
+ /* the persistent variables */
+ static INOTYP *list[FSN];
+ static dev_t name[FSN];
+ /* some temporary variables */
+ int fsn, is_registered = 0;
+
+ /* assertions - in case parameters are lowered for less memory usage */
+ assert(fsn < FSN);
+ assert((info->st_ino) / sizeof(INOTYP) < MAXINO);
+
+ /* search which internal registration number is used for this filesystem */
+ for(fsn = 0; (name[fsn] != (info->st_dev)) && (name[fsn] != 0); fsn++);
+
+ /* if file system is not registered yet, do it and leave */
+ if(name[fsn] == 0) {
+ name[fsn] = (info->st_dev);
+ /* provide space for the bitmap that maps the inodes of this file system */
+ list[fsn] = (INOTYP *) calloc(MAXINO, sizeof(INOTYP));
+ /* no comparison is needed in empty lists ... return */
+ if(debug)
+ fprintf(stderr,
+ "Debug >> Linked >> Init List %04x @mem %04lx\n",
+ (int)name[fsn], (long)&list[fsn]);
+ } else {
+ /* this looks more complicated than it really is */
+ /* the idea is very simple:
+ * provide a bitmap that maps all inodes of a file system
+ * to mark all files that have already been visited.
+ * If it is already visited, do not add it to the playlist
+ */
+ /*
+ * The difficulty is as follows:
+ * struct inode_bitmap { char registered:1; } bitmap[1<<MAXINO];
+ * would be byte-aligned and would allocate at least eight times the needed space.
+ * Feel free to change the definitions that are involved here, if you know better.
+ */
+ if(regbit_qry(list[fsn], (info->st_ino)))
+ is_registered = 1;
+ else
+ regbit_set(list[fsn], (info->st_ino));
+ /*
+ * the debug expression is more complicated then the working stuff
+ */
+ if(debug)
+ fprintf(stderr, "Debug >> Linked >> DEV %04x INO %06x => "
+ "list[%02x][%04x] = %04x & %04x --> %s registered\n",
+ (int)info->st_dev, (int)info->st_ino, fsn,
+ (int)((info->st_ino) / sizeof(INOTYP)),
+ (int)list[fsn][(info->st_ino) / sizeof(INOTYP)],
+ 1 << ((info->st_ino) % sizeof(INOTYP)),
+ is_registered ? "Already" : "Not");
+ }
+ return is_registered;
+}
+
+#ifdef HAVE_LIBURIPARSER
+char * relative_uri_malloc(const char * unixFilename, const char * baseDir)
+{
+ char * absSourceFile;
+ size_t absSourceLen;
+ char * sourceUriString;
+ char * baseUriString;
+ UriParserStateA state;
+ UriUriA sourceUri;
+ UriUriA baseUri;
+ UriUriA relativeUri;
+ int charsRequired;
+ char * output;
- void print_path()
- {
- printf(prefix);
- for (c=newpath+skip;*c!='\0';c++) if (*c=='/') putchar(separator); else if (windows) putchar(unix2dos[*c]); else putchar(*c);
- }
-
- if (debug) fprintf(stderr,"Debug >> parsing directory : %s\n",path);
- if ((n=scandir(path,&namelist,0,alphasort))<0) { fprintf(stderr,"Warning >> can't open directory : %s\n",path); return; }
- for (i=0;i<n;i++)
- {
- sprintf(newpath,"%s/%s",path,namelist[i]->d_name);
- if (stat(newpath,&infos)!=0) { fprintf(stderr,"Warning >> can't stat file : %s\n",newpath); continue; }
- if (recursive && S_ISDIR(infos.st_mode) && strcmp(namelist[i]->d_name,".")!=0 && strcmp(namelist[i]->d_name,"..")!=0) parse_directory(newpath);
- if (S_ISREG(infos.st_mode))
- {
- unsigned char ext[5];
- int j;
- for (j=0;j<5;j++) ext[j]=tolower(namelist[i]->d_name[strlen(namelist[i]->d_name)-4+j]);
- artist[0]='\0';
- title[0]='\0';
- duration=-2;
- if (strcmp(".mp3",ext)==0) { duration=-1; parse_mp3(newpath); }
- if (strcmp(".ogg",ext)==0) { duration=-1; parse_ogg(newpath); }
- if (strcmp(".wav",ext)==0) { duration=-1; /* parse_wav(newpath); */ }
-
- if (duration!=-2) /* is it an audio file ? */
- {
+ /* checks */
+ if ((unixFilename == NULL) || (baseDir == NULL)) {
+ return NULL;
+ }
+
+ /* base URI */
+ baseUriString = malloc((7 + 3 * strlen(baseDir) + 1) * sizeof(char));
+ if (baseUriString == NULL) {
+ return NULL;
+ }
+ if (uriUnixFilenameToUriStringA(baseDir, baseUriString) != 0) {
+ free(baseUriString);
+ return NULL;
+ }
+ state.uri = &baseUri;
+ if (uriParseUriA(&state, baseUriString) != 0) {
+ free(baseUriString);
+ uriFreeUriMembersA(&baseUri);
+ return NULL;
+ }
+
+ /* source URI */
+ if (unixFilename[0] != '/') {
+ const int baseDirLen = strlen(baseDir);
+ const int sourceFileLen = strlen(unixFilename);
+ absSourceLen = baseDirLen + sourceFileLen;
+ absSourceFile = malloc((absSourceLen + 1) * sizeof(char));
+ sprintf(absSourceFile, "%s%s", baseDir, unixFilename);
+ } else {
+ absSourceLen = strlen(unixFilename);
+ absSourceFile = (char *)unixFilename;
+ }
+ sourceUriString = malloc((7 + 3 * absSourceLen + 1) * sizeof(char));
+ if (sourceUriString == NULL) {
+ free(baseUriString);
+ if (unixFilename[0] != '/') {
+ free(absSourceFile);
+ }
+ uriFreeUriMembersA(&baseUri);
+ return NULL;
+ }
+ if (uriUnixFilenameToUriStringA(absSourceFile, sourceUriString) != 0) {
+ free(baseUriString);
+ free(sourceUriString);
+ if (unixFilename[0] != '/') {
+ free(absSourceFile);
+ }
+ uriFreeUriMembersA(&baseUri);
+ return NULL;
+ }
+ state.uri = &sourceUri;
+ if (uriParseUriA(&state, sourceUriString) != 0) {
+ free(baseUriString);
+ free(sourceUriString);
+ uriFreeUriMembersA(&baseUri);
+ uriFreeUriMembersA(&sourceUri);
+ return NULL;
+ }
+ if (uriNormalizeSyntaxA(&sourceUri) != 0) {
+ free(baseUriString);
+ free(sourceUriString);
+ if (unixFilename[0] != '/') {
+ free(absSourceFile);
+ }
+ uriFreeUriMembersA(&baseUri);
+ uriFreeUriMembersA(&sourceUri);
+ return NULL;
+ }
+
+ /* make relative (or keep absolute if necessary) */
+ if (uriRemoveBaseUriA(&relativeUri, &sourceUri, &baseUri, URI_FALSE) != 0) {
+ free(baseUriString);
+ free(sourceUriString);
+ if (unixFilename[0] != '/') {
+ free(absSourceFile);
+ }
+ uriFreeUriMembersA(&baseUri);
+ uriFreeUriMembersA(&sourceUri);
+ uriFreeUriMembersA(&relativeUri);
+ return NULL;
+ }
+
+ /* back to string */
+ if (uriToStringCharsRequiredA(&relativeUri, &charsRequired) != 0) {
+ free(baseUriString);
+ free(sourceUriString);
+ if (unixFilename[0] != '/') {
+ free(absSourceFile);
+ }
+ uriFreeUriMembersA(&baseUri);
+ uriFreeUriMembersA(&sourceUri);
+ uriFreeUriMembersA(&relativeUri);
+ return NULL;
+ }
+ output = malloc((charsRequired + 1) * sizeof(char));
+ if (uriToStringA(output, &relativeUri, charsRequired + 1, NULL) != 0) {
+ free(baseUriString);
+ free(sourceUriString);
+ if (unixFilename[0] != '/') {
+ free(absSourceFile);
+ }
+ free(output);
+ uriFreeUriMembersA(&baseUri);
+ uriFreeUriMembersA(&sourceUri);
+ uriFreeUriMembersA(&relativeUri);
+ return NULL;
+ }
+
+ free(baseUriString);
+ free(sourceUriString);
+ if (unixFilename[0] != '/') {
+ free(absSourceFile);
+ }
+ uriFreeUriMembersA(&baseUri);
+ uriFreeUriMembersA(&sourceUri);
+ uriFreeUriMembersA(&relativeUri);
+
+ return output;
+}
+
+char * xml_escape_malloc(const char * input)
+{
+ const char * read = input;
+ char * output;
+ char * write;
+
+ if (input == NULL) {
+ return NULL;
+ }
+
+ output = malloc((6 * strlen(input) + 1) * sizeof(char));
+ if (output == NULL) {
+ return NULL;
+ }
+ write = output;
+
+ for (;;) {
+ if (*read == '\0') {
+ *write = '\0';
+ return output;
+ }
+
+ switch ((unsigned char)*read) {
+ case '&':
+ strcpy(write, "&");
+ write += 5;
+ break;
+ case '<':
+ strcpy(write, "<");
+ write += 4;
+ break;
+ case '>':
+ strcpy(write, ">");
+ write += 4;
+ break;
+ case '\'':
+ strcpy(write, "'");
+ write += 6;
+ break;
+ case '"':
+ strcpy(write, """);
+ write += 6;
+ break;
+ default:
+ *(write++) = *read;
+ }
+ read++;
+ }
+}
+#endif
+
+void parse_file(unsigned char *newpath, unsigned char * original_path)
+{
+ unsigned char ext[5];
+ int j, encoding = 0;
+
+ for(j = 0; j < 5; j++)
+ ext[j] = tolower(newpath[strlen(newpath) - 4 + j]);
+ artist[0] = '\0';
+ title[0] = '\0';
+ duration = -2;
+ if(strcmp(".mp2", ext) == 0) {
+ duration = -1;
+ parse_mp3(newpath);
+ encoding = MP2ENC;
+ }
+ if(strcmp(".mp3", ext) == 0) {
+ duration = -1;
+ parse_mp3(newpath);
+ encoding = MP3ENC;
+ }
+ if(strcmp(".mpc", ext) == 0) {
+ duration = -1;
+ parse_mpc(newpath);
+ encoding = MPCENC;
+ }
+ if(strcmp(".mp+", ext) == 0) {
+ duration = -1;
+ parse_mpc(newpath);
+ encoding = MPPENC;
+ }
+ if(strcmp(".ogg", ext) == 0) {
+ duration = -1;
+ parse_ogg(newpath);
+ encoding = OGGENC;
+ }
+ if(strcmp(".wav", ext) == 0) {
+ duration = -1;
+ /* parse_wav(newpath); */
+ encoding = WAVENC;
+ }
+ if(strcmp(".wma", ext) == 0) {
+ duration = -1;
+ /* parse_wma(newpath); */
+ encoding = WMAENC;
+ }
+ /* guesstitle() */
+ if((strlen(artist) == 0) && (strlen(title) == 0)) {
+ // there are no tag infos read
+ // use file name to state substitute it
+ char *c = strrchr(newpath, separator);
+ if(c == NULL)
+ c = newpath;
+ strcpy(artist, ++c);
+ // arbitrarily use the first '-'
+ // to separate artist and title
+ c = strchr(artist, '-');
+ if(c != NULL) { // if trenner found, divide file name
+ *c = '\0';
+ strcpy(title, ++c);
+ c = strrchr(title, '.');
+ if(c != NULL)
+ *c = '\0';
+ } else { // no trenner found, assume
+ // no artist, only title
+ strcpy(title, artist);
+ artist[0] = '\0';
+ }
+ // replace underscores by spaces
+ for(c = artist; (c = strchr(c, '_')) != NULL; c++)
+ *c = ' ';
+ for(c = title; (c = strchr(c, '_')) != NULL; c++)
+ *c = ' ';
+ // trim spaces
+ trim(artist);
+ trim(title);
+ }
+ /* guesstitle() end */
+
+ if(duration != -2 && genrelist[genre]) { /* is it an audio file ? */
counter++;
- switch (format)
- {
- case 0:
- if (duration!=-1)
- {
- printf("#EXTINF:%d,",duration);
- if (strlen(artist)==0 && strlen(title)==0) print_faketitle();
- else printf("%s - %s",artist,title);
- printf("%s",eol);
+ switch (format) {
+ case FORMAT_M3U:
+ printf("#EXTINF:%d,", duration);
+ if(strlen(artist) != 0)
+ printf("%s - ", artist);
+ printf("%s%s", title, eol);
+ print_path(newpath);
+ printf("%s", eol);
+ break;
+ case FORMAT_PLS:
+ printf("File%d=", counter);
+ print_path(newpath);
+ printf("%sTitle%d=", eol, counter);
+ if(strlen(artist) != 0)
+ printf("%s - ", artist);
+ printf("%s%s", title, eol);
+ if(duration != -1)
+ printf("Length%d=%d%s", counter, duration, eol);
+ break;
+ case FORMAT_HTML:
+ printf("<tr><td>%d</td><td>%s</td><td>%s</td><td>", counter,
+ artist, title);
+ if(duration == -1)
+ printf("?</td></tr>%s", eol);
+ else
+ printf("%d:%s%d</td></tr>%s", duration / 60,
+ duration % 60 < 10 ? "0" : "", duration % 60, eol);
+ break;
+ case FORMAT_RSS:
+ if(duration != -1) {
+ struct stat infos;
+ char timebuffer[256];
+
+ if(stat(newpath, &infos) != 0) {
+ fprintf(stderr, "Warning >> can't stat entry : %s\n",
+ newpath);
+ return;
+ }
+ strftime(timebuffer, 255, "%a %d %b %Y %T %z", localtime(&(infos.st_mtime))); /* ctime() had a trailing CR */
+ printf("\t<item>%s", eol);
+ printf("\t\t<author>");
+ myputstr(artist);
+ printf("</author>%s\t\t<title>", eol);
+ myputstr(title);
+ printf("</title>%s", eol);
+
+ if(referal == NULL) {
+ noreferal(newpath, artist, title);
+ } else
+ reference(newpath);
+ printf("\t\t<pubDate>%s</pubDate>%s\t\t<enclosure url=\"",
+ timebuffer, eol);
+ print_webpath(newpath);
+ printf("\" length=\"%d\" type=\"%s\"/>%s\t\t<guid>",
+ (int)infos.st_size, magic[encoding], eol);
+ print_pathtail(newpath);
+ printf("</guid>%s", eol);
+ if(duration > 3599)
+ printf
+ ("\t\t<itunes:duration>%d:%02d:%02d</itunes:duration>%s",
+ duration / 3600, (duration / 60) % 60,
+ duration % 60, eol);
+ else
+ printf
+ ("\t\t<itunes:duration>%d:%02d</itunes:duration>%s",
+ duration / 60, duration % 60, eol);
+ if(strlen(artist) != 0) {
+ printf("\t\t<itunes:author>");
+ myputstr(artist);
+ printf("</itunes:author>%s", eol);
+ }
+ printf("\t</item>%s", eol);
}
- print_path();
- printf("%s",eol);
break;
- case 1:
- printf("File%d=",counter);
- print_path();
- printf("%s",eol);
- printf("Title%d=",counter);
- if (strlen(artist)==0 && strlen(title)==0) print_faketitle();
- else printf("%s - %s",artist,title);
- printf("%s",eol);
- if (duration!=-1) printf("Length%d=%d%s",counter,duration,eol);
+ case FORMAT_PLP:
+ myplaputstr("HARP, ");
+ myplaputstr(newpath);
+ myplaputstr(eol);
break;
- case 2:
- printf("<tr><td>%d</td><td>%s</td><td>%s</td><td>",counter,artist,title);
- if (duration==-1) printf("?"); else printf("%d:%s%d",duration/60,duration%60<10?"0":"",duration%60);
- printf("</td></tr>%s",eol);
+ case FORMAT_UMS:
+ txxputstr(newpath);
break;
+#ifdef HAVE_LIBURIPARSER
+ case FORMAT_XSPF:
+ printf("<track>\n");
+ if (strlen(title) > 0) {
+ char * escaped_title = xml_escape_malloc(title);
+ if (escaped_title != NULL) {
+ printf(" <title>%s</title>\n", escaped_title);
+ free(escaped_title);
+ }
+ }
+ if (strlen(artist) > 0) {
+ char * escaped_artist = xml_escape_malloc(artist);
+ if (escaped_artist != NULL) {
+ printf(" <creator>%s</creator>\n", escaped_artist);
+ free(escaped_artist);
+ }
+ }
+ if (duration > 0) {
+ printf(" <duration>%d</duration>\n", duration);
+ }
+ {
+ char * relative_location;
+ char * escaped_location;
+ relative_location = relative_uri_malloc(newpath, original_path);
+ if (relative_location != NULL) {
+ escaped_location = xml_escape_malloc(relative_location);
+ if (escaped_location != NULL) {
+ printf(" <location>%s</location>\n", escaped_location);
+ free(escaped_location);
+ }
+ free(relative_location);
+ }
+ }
+ printf("</track>\n");
+ break;
+#endif
}
- }
}
- free(namelist[i]);
- }
- free(namelist);
}
-int main(int argc,char **argv)
+void parse_directory(unsigned char *path, unsigned char * original_path)
{
- parse_options(argc,argv);
- if (optind==argc) usage();
- switch (format)
- {
- case 0:
- printf("#EXTM3U%s",eol);
- break;
- case 1:
- printf("[playlist]%s,eol");
- break;
- case 2:
- printf("<!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 0.22</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",eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol,eol);
- break;
- }
- for (;optind<argc;optind++)
- {
- if (strlen(prefix)!=0) skip=strlen(argv[optind]);
- parse_directory(argv[optind]);
- }
- switch(format)
- {
- case 1:
- printf("NumberOfEntries=%d%sVersion=2%s",counter,eol,eol);
- break;
- case 2:
- printf("</table>%s%s<p>Playlist generated by <a href=\"http://royale.zerezo.com/fapg/\">FAPG 0.22</a></p>%s%s</body>%s%s</html>",eol,eol,eol,eol,eol,eol);
- break;
- }
- exit(0);
+ int i, n;
+ struct dirent **namelist;
+ unsigned char newpath[PATH_MAX];
+ struct stat infos;
+
+ if(debug)
+ fprintf(stderr, "Debug >> parsing directory : %s\n", path);
+ if(stat(path, &infos) != 0) {
+ fprintf(stderr, "Warning >> can't stat entry : %s\n", path);
+ return;
+ }
+ /* check if it is a filename */
+ if(S_ISREG(infos.st_mode) || S_ISLNK(infos.st_mode)) {
+ parse_file(path, original_path);
+ return;
+ }
+ /* must be a directory - or something unusable like pipe, socket, etc */
+ if((n = scandir(path, &namelist, 0, alphasort)) < 0) {
+ fprintf(stderr, "Warning >> can't open directory : %s\n", path);
+ return;
+ }
+ for(i = 0; i < n; i++) {
+ snprintf(newpath, PATH_MAX, "%s/%s", path, namelist[i]->d_name);
+
+ if(stat(newpath, &infos) != 0) {
+ fprintf(stderr, "Warning >> can't stat entry : %s\n", newpath);
+ continue;
+ }
+ if(recursive && S_ISDIR(infos.st_mode)
+ && strcmp(namelist[i]->d_name, ".") != 0
+ && strcmp(namelist[i]->d_name, "..") != 0)
+ parse_directory(newpath, original_path);
+ /* hlink_check() might be applied more selective ... avoidhlink is only a simple prereq */
+ if(S_ISREG(infos.st_mode)
+ && !(avoidhlinked && hlink_check(&infos))) {
+ parse_file(newpath, original_path);
+ }
+ free(namelist[i]);
+ }
+ free(namelist);
}
+
+int main(int argc, char **argv)
+{
+ winorunix = one2one;
+ basemap = one2one;
+ parse_options(argc, argv);
+
+ if(optind == argc && !fromstdin)
+ usage();
+
+ /* print header */
+ switch (format) {
+ case FORMAT_M3U:
+ printf("#EXTM3U%s", eol);
+ break;
+ case FORMAT_PLS:
+ printf("[playlist]%s", eol);
+ break;
+ case FORMAT_HTML:
+ printf
+ ("<!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 "
+ VERSION
+ "</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",
+ eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol,
+ eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol,
+ eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol,
+ eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol,
+ eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol,
+ eol, eol, eol);
+ break;
+ case FORMAT_RSS:
+ {
+ time_t zeit;
+ char timebuffer[256];
+ time(&zeit);
+ strftime(timebuffer, 255, "%a %d %b %Y %T %z",
+ localtime(&zeit));
+ printf
+ ("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>%s<!-- generator=\"FAPG "
+ VERSION
+ " -->%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 "
+ VERSION
+ "</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",
+ eol, eol, eol, eol, hostname, dir, argv[optind], eol,
+ prefix, eol, base, eol, prefix, eol, timebuffer, eol, eol,
+ eol, base, eol, eol, base, eol, eol, eol, eol, base, eol,
+ hostname, eol, eol, eol, dir, eol, getenv("LOGNAME"), eol,
+ eol, getenv("LANG"), eol, eol, eol);
+ unix2dos[38] = 43; // I never made an rss feed work with '&' in it
+ basemap = noand;
+ }
+ break;
+ case FORMAT_PLP:
+ {
+ eol = "\r\n";
+ myplaputstr("PLP PLAYLIST\r\nVERSION 1.20\r\n\r\n");
+ }
+ break;
+ case FORMAT_UMS:
+ {
+ txxputheader(" iriver UMS PLA");
+ }
+ break;
+#ifdef HAVE_LIBURIPARSER
+ case FORMAT_XSPF:
+ printf("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
+ "<!-- generator=\"FAPG " VERSION " -->\n"
+ "<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\">\n"
+ "<trackList>\n");
+ break;
+#endif
+ }
+
+ /* iterate through files */
+ {
+ const char * const pwd_source = getenv("PWD");
+ const int pwdlen = strlen(pwd_source);
+ char * const pwd = malloc((pwdlen + 1 + 1) * sizeof(char));
+ sprintf(pwd, "%s/", pwd_source);
+
+ if(fromstdin) {
+ unsigned char path[PATH_MAX];
+ int i;
+ while(fgets(path, PATH_MAX, stdin)) {
+ for(i = 0; i < PATH_MAX; i++)
+ if(path[i] == '\r' || path[i] == '\n')
+ path[i] = '\0';
+ if (i <= 0) {
+ continue;
+ }
+
+ /* strip trailing slash */
+ if (path[i - 1] == '/') {
+ path[i - 1] = '\0';
+ }
+
+ parse_directory(path, pwd);
+ }
+ } else
+ for(; optind < argc; optind++) {
+ /* strip trailing slash */
+ char * dup = strdup(argv[optind]);
+ const int len = strlen(dup);
+ if ((len > 0) && (dup[len - 1] == '/')) {
+ dup[len - 1] = '\0';
+ }
+
+ parse_directory(dup, pwd);
+ }
+
+ free(pwd);
+ }
+
+ /* print footer */
+ switch (format) {
+ case FORMAT_PLS:
+ printf("NumberOfEntries=%d%sVersion=2%s", counter, eol, eol);
+ break;
+ case FORMAT_HTML:
+ printf
+ ("</table>%s%s<p>Playlist generated by <a href=\"http://royale.zerezo.com/fapg/\">FAPG "
+ VERSION "</a></p>%s%s</body>%s%s</html>", eol, eol, eol, eol,
+ eol, eol);
+ break;
+ case FORMAT_RSS:
+ printf(" </channel>%s</rss>%s", eol, eol);
+ break;
+ case FORMAT_UMS:
+ txxputcounter(counter);
+ break;
+#ifdef HAVE_LIBURIPARSER
+ case FORMAT_XSPF:
+ printf("</trackList>\n"
+ "</playlist>\n");
+ break;
+#endif
+ }
+
+ if(genrelist)
+ free(genrelist);
+
+ exit(0);
+}
+