version 0.36 (by Hank Barta)
[FAPG] / fapg.c
diff --git a/fapg.c b/fapg.c
index 529a26f..0ec3835 100644 (file)
--- a/fapg.c
+++ b/fapg.c
@@ -1,6 +1,4 @@
 /*
- * FAPG version 0.32
- *
  * 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 <limits.h>
 #include <unistd.h>
 #include <ctype.h>
+#include <time.h>
 #include <assert.h>
 #include "genres.h"
 
+#define VERSION "0.36"
 #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 */
-char *genrelist=NULL;
-unsigned char *prefix="";
-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];
 
-int counter=0;
+int counter = 0;
 
 unsigned char artist[1024];
 unsigned char title[1024];
 unsigned char genrebuf[1024];
-unsigned char genre=0;
+unsigned char genre = 0;
 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 *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 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 *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] [-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|pla] [-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);
 }
 
-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: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
-        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);
-        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); }
+    putchar(basemap[(unsigned char)winorunix[(unsigned char)x]]);
+    putchar('\0');
+}
+
+void mywebputstr(const char *c)
+{
+    while(*c != 0) {
+        mywebputchar(*c);
+        c++;
+    }
+}
+
+void myplaputstr(const char *c)
+{
+    while(*c != 0) {
+        if( *c == '/')
+            myplaputchar('\\'); /* translate slash to backslash */
         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();
+            myplaputchar(*c);
+        c++;
+        /* remove multiple slashes "//" when parsing a directory ending with a "/" */
+        while(*c == '/' && c[1] == '/')
+            c++;
     }
-  }
-  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 myputstr(const char *c)
+{
+    while(*c != 0) {
+        myputchar(*c);
+        c++;
+        /* remove multiple slashes "//" when parsing a directory ending with a "/" */
+        while(*c == '/' && c[1] == '/')
+            c++;
     }
-  }
 }
 
-void parse_mp3(unsigned char *file)
+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 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
-      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)
+{
+    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 *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);
+    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;
-  }
-  
-  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 if(strcmp(optarg, "pla") == 0)
+                format = 4;
+            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  8
-#define MAXINO  (1<<22)
+#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)
+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
+     * for speed this subroutine should only be called
+     * - if the file has more than one hardlink
+     * - if the file is a resolved softlink
      */
-    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_directory(unsigned char *path)
+
+void parse_file(unsigned char *newpath)
 {
-  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);
-    }
-  }
-  
-  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);
-    /* hlink_check() might be applied more selective ... avoidhlink is only a simple prereq */
-    if (S_ISREG(infos.st_mode) && ! ( avoidhlinked && hlink_check(&infos) ) )
-    {
-      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(".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);
+       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 = ' ';
+    }
+    /* faketitle() 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 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;
+        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;
-          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 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;
+        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;
-          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 4: // printing output for Sansa players
+            myplaputstr("HARP, ");
+            myplaputstr(newpath);
+            myplaputstr(eol);
             break;
         }
-      }
     }
-    free(namelist[i]);
-  }
-  free(namelist);
 }
 
-int main(int argc,char **argv)
+void parse_directory(unsigned char *path)
 {
-  parse_options(argc,argv);
-  if (optind==argc) usage();
-  switch (format)
-  {
+    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)
+{
+    winorunix = one2one;
+    basemap = one2one;
+    parse_options(argc, argv);
+    if(optind == argc)
+        usage();
+    switch (format) {
     case 0:
-      printf("#EXTM3U%s",eol);
-      break;
+        printf("#EXTM3U%s", eol);
+        break;
     case 1:
-      printf("[playlist]%s",eol);
-      break;
+        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.32</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)
-  {
+        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:
+        {
+            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 4: 
+        {
+            eol = "\r\n";
+            myplaputstr( "PLP PLAYLIST\r\nVERSION 1.20\r\n\r\n" );
+        }
+    }
+    for(; optind < argc; optind++) {
+        parse_directory(argv[optind]);
+    }
+    switch (format) {
     case 1:
-      printf("NumberOfEntries=%d%sVersion=2%s",counter,eol,eol);
-      break;
+        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.32</a></p>%s%s</body>%s%s</html>",eol,eol,eol,eol,eol,eol);
-      break;
-  }
-  if (genrelist) free(genrelist);
-  exit(0);
+        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:
+        printf("    </channel>%s</rss>%s", eol, eol);
+        break;
+    }
+    if(genrelist)
+        free(genrelist);
+    exit(0);
 }