version 0.35 (by Andreas Neuper) v0.35
authorAntoine Jacquet <royale@zerezo.com>
Tue, 7 Feb 2006 23:00:00 +0000 (00:00 +0100)
committerAntoine Jacquet <royale@zerezo.com>
Tue, 7 Feb 2006 23:00:00 +0000 (00:00 +0100)
* BUG fix: no more ampersand in RSS feeds are passed
* BUG fix: no more spaces or non-printables in links of RSS feeds
* BUG fix: potential access violations removed
* speed up printing by using table assignment (since predictiv)
* added hook to get additional info for RSS feeds (default is to use internal only)
* remove multiple slashes like ./// and //// as well
* clean with gnu compiler V3.3 (you might add but never remove 'unsigned'!)
* RSS feed successfully tested with iPod/iTunes and German language

CHANGELOG
Makefile
README
fapg.1
fapg.c

index cfc88f1..32497e0 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,15 @@
 Change log file for FAPG
 
 Change log file for FAPG
 
+version 0.35   (2006-02-08) (by Andreas Neuper)
+       * BUG fix: no more ampersand in RSS feeds are passed
+       * BUG fix: no more spaces or non-printables in links of RSS feeds
+       * BUG fix: potential access violations removed
+       * speed up printing by using table assignment (since predictiv)
+       * added hook to get additional info for RSS feeds (default is to use internal only)
+       * remove multiple slashes like ./// and //// as well
+       * clean with gnu compiler V3.3 (you might add but never remove 'unsigned'!)
+       * RSS feed successfully tested with iPod/iTunes and German language
+
 version 0.34   (2006-01-21) (by Andreas Neuper)
        * BUG fix: "unsigned char" (like in 0.31) to get complete output and avoid core dumps
        * BUG fix: disabled "skip" variable to use multiple args
 version 0.34   (2006-01-21) (by Andreas Neuper)
        * BUG fix: "unsigned char" (like in 0.31) to get complete output and avoid core dumps
        * BUG fix: disabled "skip" variable to use multiple args
index 990ae9c..6e8000f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,14 +2,22 @@ PRE = /usr/local
 BIN = $(PRE)/bin
 DOC = $(PRE)/share/doc/fapg
 MAN = $(PRE)/share/man/man1
 BIN = $(PRE)/bin
 DOC = $(PRE)/share/doc/fapg
 MAN = $(PRE)/share/man/man1
+CFLAGS=-Wall -g2 -funsigned-char
 
 
-fapg:fapg.c genres.h
-       gcc -Wall -o fapg fapg.c
+fapg: fapg.c
+
+fapg.c:        genres.h
+       touch $@
 
 clean:
        rm -f fapg
 
 
 clean:
        rm -f fapg
 
-install:fapg
+beautify:
+       indent -nut -kr -ncs -nsaf -npcs -nsai -nsaw -o fapg.good.c fapg.c &&\
+       mv fapg.good.c fapg.c
+       splint -checks fapg.c
+
+install: fapg
        mkdir -p $(BIN) $(DOC) $(MAN)
        cp -f fapg $(BIN)
        cp -f COPYING README $(DOC)
        mkdir -p $(BIN) $(DOC) $(MAN)
        cp -f fapg $(BIN)
        cp -f COPYING README $(DOC)
diff --git a/README b/README
index 332dae5..d48459a 100644 (file)
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-FAPG 0.34 (Fast Audio Playlist Generator)
+FAPG 0.35 (Fast Audio Playlist Generator)
 site: http://royale.zerezo.com/fapg/
 mail: royale@zerezo.com
 
 site: http://royale.zerezo.com/fapg/
 mail: royale@zerezo.com
 
@@ -7,7 +7,7 @@ make
 make install
 
 usage:
 make install
 
 usage:
-fapg [-b|--backslash] [-d|--debug] [-f|--format=m3u|pls|html|rss] [-g|--genre=#:#:...] [-o|--output=/path/to/file.m3u] [-p|--prefix=/the/prefix] [-r|--recursive] [-w|--windows] [-x|--exclude=#:#:...] /path/to/mp3/dir1 [/path/to/mp3/dir2 ...]
+fapg [-b|--backslash] [-d|--debug] [-f|--format=m3u|pls|html|rss] [-g|--genre=#:#:...] [-o|--output=/path/to/file.m3u] [-p|--prefix=/the/prefix] [-r|--recursive] [-w|--windows] [-x|--exclude=#:#:...] [-c|--command=<intern|...>] /path/to/mp3/dir1 [/path/to/mp3/dir2 ...]
 - backslash : replace the '/' with '\' in Unix path. 
 - debug : display useful messages if the program fails ;) 
 - format : choose which format of playlist you want to generate (default is m3u). 
 - backslash : replace the '/' with '\' in Unix path. 
 - debug : display useful messages if the program fails ;) 
 - format : choose which format of playlist you want to generate (default is m3u). 
@@ -17,6 +17,7 @@ fapg [-b|--backslash] [-d|--debug] [-f|--format=m3u|pls|html|rss] [-g|--genre=#:
 - recursive : recursively read the subdirectories. 
 - windows : replace all Unix characters with Windows characters... 
 - exclude : choose which genres will be excluded in the generated playlist (default is none). 
 - recursive : recursively read the subdirectories. 
 - windows : replace all Unix characters with Windows characters... 
 - exclude : choose which genres will be excluded in the generated playlist (default is none). 
+- command : external binary or script that produces additional fields for RSS feeds (slow)
 
 links :
 http://geek.scorpiorising.ca/scripts.html
 
 links :
 http://geek.scorpiorising.ca/scripts.html
diff --git a/fapg.1 b/fapg.1
index 8b3588f..c7ca1ff 100644 (file)
--- a/fapg.1
+++ b/fapg.1
@@ -62,7 +62,7 @@ on the server containing the files is
 and is visible on the Windows network as 
 .I \\\\\\\\server\\\\mp3
 :
 and is visible on the Windows network as 
 .I \\\\\\\\server\\\\mp3
 :
-.B fapg --backslash --output=/samba/mp3/list.m3u --prefix='\\\\\\\\server\\\\mp3\\\\' --recursive --windows /samba/mp3
+.B cd /samba/mp3 && fapg --backslash --output=/samba/mp3/list.m3u --prefix='\\\\\\\\server\\\\mp3\\\\' --recursive --windows ./
  
 An HTML playlist for an album:
 .B fapg --output=fapg.html ~/path/to/album
  
 An HTML playlist for an album:
 .B fapg --output=fapg.html ~/path/to/album
@@ -70,9 +70,12 @@ An HTML playlist for an album:
 A playlist that contains all your classical tracks may receive the genres to include (or exclude) in one or multiple portions
 .B fapg --genre=32:105 --genre=106:104:103 /path/to/all/music
 
 A playlist that contains all your classical tracks may receive the genres to include (or exclude) in one or multiple portions
 .B fapg --genre=32:105 --genre=106:104:103 /path/to/all/music
 
-Generate a podcasting XML file:
-.B fapg --output=dir.xml -f rss -r --prefix=http://thisserver/basedir path/to/mp3
-Please note that in all given directories ( and in /rss ) files called podcast.jpg are referenced.
+Generate a podcasting XML file using an external program rss.sh to generate a detailed description:
+.B fapg --output=dir.xml -f rss -c /usr/local/bin/rss.sh -r --prefix=http://thisserver/basedir path/to/mp3
+if command starts with
+.I intern
+the build-in description (Author,Title,Link) is generated.
+Please note that in all given directories ( and in ...basedir/xml ) files called podcast.jpg are referenced.
 Many header fields are set to defaults using environment variables like LOGNAME or LANG.
 
 
 Many header fields are set to defaults using environment variables like LOGNAME or LANG.
 
 
diff --git a/fapg.c b/fapg.c
index e627ea5..19eb30a 100644 (file)
--- a/fapg.c
+++ b/fapg.c
@@ -1,7 +1,7 @@
 /*
  *              FAPG
  */
 /*
  *              FAPG
  */
-#define VERSION "0.34"
+#define VERSION "0.35"
 /*
  * FAPG means Fast Audio Playlist Generator.
  * It is a tool to generate list of audio files (Wav, MP3, Ogg, etc)
 /*
  * FAPG means Fast Audio Playlist Generator.
  * It is a tool to generate list of audio files (Wav, MP3, Ogg, etc)
 
 #define MP3_BASE 1024
 #define OGG_BASE 1024*10
 
 #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 ; 3 = rss */
-char *genrelist=NULL;
-unsigned char *prefix="";
-unsigned char *base="";
-unsigned char *dir="";
-unsigned char *hostname="fritzserver.de";
-unsigned char *referal="http://www.explaining.text.org/select.php?title=";
-int recursive=0;
-int avoidhlinked=0;
-int separator='/';
-int skip=0;
-int windows=0;
-unsigned char *eol="\n";
+#define MAX 1024*200            /* 200ko for ID3 with JPEG images in it */
+
+int debug = 0;
+int format = 0;                 /* 0 = m3u ; 1 = pls ; 2 = html ; 3 = rss */
+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 recursive = 0;
+int avoidhlinked = 0;
+int separator = '/';
+unsigned char *eol = "\n";
 unsigned char buffer[MAX];
 
 unsigned char buffer[MAX];
 
-int counter=0;
+int counter = 0;
 
 unsigned char artist[1024];
 unsigned char title[1024];
 unsigned char genrebuf[1024];
 
 unsigned char artist[1024];
 unsigned char title[1024];
 unsigned char genrebuf[1024];
-unsigned char genre=0;
+unsigned char genre = 0;
 int duration;
 int duration;
+#define MP2ENC 1
+#define MP3ENC 2
+#define MPCENC 3
+#define MPPENC 4
+#define OGGENC 5
+#define WAVENC 6
+
+char *magic[] = { NULL,
+    "audio/mpeg", "audio/mpeg",
+    "audio/mpeg", "audio/mpeg",
+    "audio/ogg-vorbis", "audio/x-wav",
+    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()
 {
 
 void usage()
 {
-  fprintf(stderr,"Usage >> fapg [-b|--backslash] [-d|--debug] [-f|--format=m3u|pls|html|rss] [-g|--genre=#:#:...] [-n|--nohardlink] [-o|--output=/path/to/file.m3u] [-p|--prefix=/the/prefix] [-r|--recursive] [-w|--windows] [-x|--exclude=#:#:...] /path/to/mp3/dir1 [/path/to/mp3/dir2 ...]\n");
-  exit(1);
+    fprintf(stderr,
+            "Usage >> fapg [-b|--backslash] [-d|--debug] [-f|--format=m3u|pls|html|rss] [-g|--genre=#:#:...] [-n|--nohardlink] [-o|--output=/path/to/file.m3u] [-p|--prefix=/the/prefix] [-r|--recursive] [-w|--windows] [-c|--command=<intern|....>] [-x|--exclude=#:#:...] /path/to/mp3/dir1 [/path/to/mp3/dir2 ...]\n");
+    exit(1);
 }
 
 }
 
-const char*reflink(const char*title)
+#define mywebputchar(x) { fputs(iso2web[(unsigned char)winorunix[(unsigned char)x]], stdout); }
+#define    myputchar(x) { putchar(basemap[(unsigned char)winorunix[(unsigned char)x]]); }
+
+void mywebputstr(const char *c)
 {
 {
-  int i=0;
-  static char *buffer;
-  buffer=malloc(strlen(title)+strlen(referal)+2);
-  strcpy(buffer,referal);
-  strcat(buffer,title);
-  for(i=strlen(referal);i<strlen(buffer);i++)
-  {
-     switch(buffer[i])
-     {
-       case ' ': buffer[i]='+'; break;
-       default: ;
-     }
-  }
-  return(buffer);
+    while(*c != 0) {
+        mywebputchar(*c);
+        c++;
+    }
 }
 
 }
 
-void parse_options(int argc,char **argv)
+void myputstr(const char *c)
 {
 {
-  static char const short_options[]="bdf:g:lo:np:rwx:";
-  static struct option long_options[]=
-  {
-    {"backslash",no_argument,NULL,'b'},
-    {"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'},
-    {"windows",no_argument,NULL,'w'},
-    {"exclude",required_argument,NULL,'x'}
-  };
-  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
-        if (strcmp(optarg,"rss")==0)  format=3; 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
-        {
-          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 'w':
-        windows=1;
-        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
-        {
-          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;
-      default:
-        usage();
+    while(*c != 0) {
+        myputchar(*c);
+        c++;
+        /* remove multiple slashes "//" when parsing a directory ending with a "/" */
+        while(*c == '/' && c[1] == '/')
+            c++;
     }
     }
-  }
-  /* 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 print_webpath(const char *path)
+{
+    const char *c = path;
+
+    printf(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 parse_mp3(unsigned char *file)
+void print_path(const char *path)
 {
 {
-  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,"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);
+    const char *c = path;
+    printf(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
     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';
-      }
-      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)
-      {
-        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';
-      }
-      if (strncmp(c,"TCON",4)==0)
-      {
-        /* strncpy(genrebuf,c+11,size-1); */
-        /* genrebuf[size-1]='\0'; */
-        /* genre=atoi(&genrebuf[1]); */
-        genre=atoi(c+12);
-      }
-      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);
-}  
+        c = path;
+    myputstr(c);
+}
 
 
-void parse_ogg(unsigned char *file)
+void noreferal(const char *path, const char *artist, const char *title)
 {
 {
-  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);
+    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);
+        free(command);
+        return;
+    }
+    fgets(buffer, 1020, pipe);
+    while(!feof(pipe)) {
+        fputs(buffer, stdout);
+        fgets(buffer, 1020, pipe);
+    }
+    pclose(pipe);
     return;
     return;
-  }
-  
-  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,"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;
+}
+
+void parse_options(int argc, char **argv)
+{
+    static char const short_options[] = "c:bdf:g:lo:np:ruwx:";
+    static struct option long_options[] = {
+        {"backslash", no_argument, NULL, 'b'},
+        {"command", required_argument, NULL, 'b'},
+        {"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'},
+        {"windows", no_argument, NULL, 'w'},
+        {"exclude", required_argument, NULL, 'x'}
+    };
+    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 = 0;
+            else if(strcmp(optarg, "pls") == 0)
+                format = 1;
+            else if(strcmp(optarg, "html") == 0)
+                format = 2;
+            else if(strcmp(optarg, "rss") == 0)
+                format = 3;
+            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;
+        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;
         }
         }
-        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_mpc(unsigned char *file)
+void parse_mp3(unsigned char *file)
 {
 {
-  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 (buffer[0]!='M' && buffer[1]!='P' && buffer[2]!='+')
-  {
-    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';
+    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, "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);
+        } 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';
+                }
+                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) {
+                    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';
+                }
+                if(strncmp(c, "TCON", 4) == 0) {
+                    /* strncpy(genrebuf,c+11,size-1); */
+                    /* genrebuf[size-1]='\0'; */
+                    /* genre=atoi(&genrebuf[1]); */
+                    genre = atoi(c + 12);
+                }
+                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;
         }
         }
-        if (strcasecmp(c,"ARTIST")==0)
-        {
-          strncpy(artist,c+7,size);
-          artist[size]='\0';
+        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];
         }
         }
-        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;
+    }
+
+    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);
+
+    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, "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_mpc(unsigned char *file)
+{
+    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(buffer[0] != 'M' && buffer[1] != 'P' && buffer[2] != '+') {
+        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;
             }
             }
-            if (i==ID3_NR_OF_V1_GENRES) genre=0;
-          }
         }
         }
-        c+=strlen(c)+1+size;
-      }
     }
     }
-  }
 
 
-  fclose(fic);
+    fclose(fic);
 }
 
 #define FSN  32
 }
 
 #define FSN  32
@@ -518,268 +813,325 @@ void parse_mpc(unsigned char *file)
 #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) ) )
 
 #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)
+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.
+     * for speed this subroutine should only be called
+     * - if the file has more than one hardlink
+     * - if the file is a resolved softlink
      */
      */
-    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;
+    /* 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;
 }
 
 
 void parse_file(unsigned char *newpath)
 {
 }
 
 
 void parse_file(unsigned char *newpath)
 {
-  unsigned char *c;
-  unsigned char ext[5];
-  int j;
-
-  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);
-    }
-  }
-  
-  void print_path()
-  {
-    char last=0;
-    printf(prefix);
-    c=newpath+skip;
-    /* remove leading "./" when parsing current directory */
-    if (*c=='.' && *(c+1)=='/') { c+=2; last='/'; }
-    for (;*c!='\0';c++)
-    {
-      /* remove double "//" when parsing a directory ending with a "/" */
-      if (last=='/' && *c=='/') continue;
-      last=*c;
-      if (*c=='/') putchar(separator); else if (windows) putchar(unix2dos[*c]); else putchar(*c);
-    }
-  }
-
-  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); }
-  if (strcmp(".mp3",ext)==0) { duration=-1; parse_mp3(newpath); }
-  if (strcmp(".mpc",ext)==0) { duration=-1; parse_mpc(newpath); }
-  if (strcmp(".mp+",ext)==0) { duration=-1; parse_mpc(newpath); }
-  if (strcmp(".ogg",ext)==0) { duration=-1; parse_ogg(newpath); }
-  if (strcmp(".wav",ext)==0) { duration=-1; /* parse_wav(newpath); */ }
-      
-      if (duration!=-2 && genrelist[genre]) /* is it an audio file ? */
-      {
+    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;
+    }
+    /* faketitle() */
+    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);
+        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 = ' ';
+    }
+    /* faketitle() end */
+
+    if(duration != -2 && genrelist[genre]) {    /* is it an audio file ? */
         counter++;
         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 0:
+            if(duration != -1) {
+                printf("#EXTINF:%d,%s - %s%s", duration, artist, title,
+                       eol);
             }
             }
-            print_path();
-            printf("%s",eol);
+            print_path(newpath);
+            printf("%s", eol);
             break;
             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 1:
+            printf("File%d=", counter);
+            print_path(newpath);
+            printf("%sTitle%d=%s - %s%s", eol, counter, artist, title,
+                   eol);
+            if(duration != -1)
+                printf("Length%d=%d%s", counter, duration, eol);
             break;
             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 2:
+            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;
             break;
-          case 3:
-            if (duration!=-1)
-            {   time_t zeit;
-               time(&zeit);
-               char timebuffer[256];
-               strftime(timebuffer,255, "%a %d %b %Y %T %Z", localtime(&zeit) ); /* ctime() had a trailing CR */
-               printf("\t<item>%s",eol);
-                if (strlen(artist)==0 && strlen(title)==0) 
-               {
-                 /* find a better solution for this */
-                 printf("\t\t<author>%s</author>%s\t\t<title>%s</title>%s",newpath,eol, newpath,eol );
-               }
-               else printf("\t\t<author>%s</author>%s\t\t<title>%s</title>%s",artist,eol, title,eol );
-               /* you might want to add more into description --> look for a smart, not a fast program */
-               printf("\t\t<description><![CDATA[<h4>%s</h4><h5>%s</h5><a href=\"",title,artist);
-                print_path();
-               printf("\">Direct Link to Audiofile</a><br>]]></description>%s",eol);
-#if 1
-               printf("\t\t<link>%s</link>%s",reflink(title),eol);
-#endif
-               printf("\t\t<pubDate>%s</pubDate>%s",timebuffer,eol);
-               printf("\t\t<enclosure url=\"");
-                print_path();
-               printf("\" length=\"%d\" type=\"audio/mpeg\"/>%s\t\t<guid>",duration, eol);
-                print_path();
-               printf("</guid>%s",eol );
-               printf("\t\t<itunes:duration>%d:%d:%d</itunes:duration>%s",duration/3600,(duration/60)%60,duration%60,eol);
-               printf("\t\t<itunes:author>%s</itunes:author>%s", artist, eol);
-               printf("\t</item>%s",eol );
+        case 3:
+            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);
             }
             break;
         }
             }
             break;
         }
-      }
+    }
 }
 
 void parse_directory(unsigned char *path)
 {
 }
 
 void parse_directory(unsigned char *path)
 {
-  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);
-    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++)
-  {
-    sprintf(newpath,"%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);
-    /* 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);
-    }
-    free(namelist[i]);
-  }
-  free(namelist);
+    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);
+        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++) {
+        sprintf(newpath, "%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);
+        /* 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);
+        }
+        free(namelist[i]);
+    }
+    free(namelist);
 }
 
 }
 
-int main(int argc,char **argv)
+int main(int argc, char **argv)
 {
 {
-  parse_options(argc,argv);
-  if (optind==argc) usage();
-  switch (format)
-  {
+    winorunix = one2one;
+    basemap = one2one;
+    parse_options(argc, argv);
+    if(optind == argc)
+        usage();
+    switch (format) {
     case 0:
     case 0:
-      printf("#EXTM3U%s",eol);
-      break;
+        printf("#EXTM3U%s", eol);
+        break;
     case 1:
     case 1:
-      printf("[playlist]%s",eol);
-      break;
+        printf("[playlist]%s", eol);
+        break;
     case 2:
     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 " 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;
+        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 3:
     case 3:
-      { time_t zeit;
-       time(&zeit);
-       char timebuffer[256];
-       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</title>%s\t<description>Directory Tree %s</description>%s\t<link>%s</link>%s\t<itunes:image href=\"%s/rss/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,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);
-      }
-      break;
-  }
-  for (;optind<argc;optind++)
-  {
-#if 0
-    /* activating skip means that all given path components are stripped */
-    /* in consequence a list of subdirs is always mapped to the same prefix */
-    /* a recommended workaround is to move to the desired subdir and avoid absolute directories in the list */
-    if (strlen(prefix)!=0) skip=strlen(argv[optind]);
-#endif
-    parse_directory(argv[optind]);
-  }
-  switch(format)
-  {
+        {
+            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;
+    }
+    for(; optind < argc; optind++) {
+        parse_directory(argv[optind]);
+    }
+    switch (format) {
     case 1:
     case 1:
-      printf("NumberOfEntries=%d%sVersion=2%s",counter,eol,eol);
-      break;
+        printf("NumberOfEntries=%d%sVersion=2%s", counter, eol, eol);
+        break;
     case 2:
     case 2:
-      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;
+        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 3:
     case 3:
-      printf("    </channel>%s</rss>%s",eol,eol);
-      break;
-  }
-  if (genrelist) free(genrelist);
-  exit(0);
+        printf("    </channel>%s</rss>%s", eol, eol);
+        break;
+    }
+    if(genrelist)
+        free(genrelist);
+    exit(0);
 }
 }