-/*\r
- * FAPG 0.0 released under GPL\r
- * http://royale.zerezo.com/fapg/\r
- */\r
-\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#include <getopt.h>\r
-#include <dirent.h>\r
-#include <sys/types.h>\r
-#include <string.h>\r
-#include <limits.h>\r
-\r
-#define MAX 10240\r
-\r
-int debug=0;\r
-int format=0; /* 0 = m3u ; 1 = pls ; 2 = html */\r
-unsigned char *prefix="";\r
-int recursive=0;\r
-int separator='/';\r
-int skip=0;\r
-int windows=0;\r
-unsigned char buffer[MAX];\r
-\r
-int counter=0;\r
-\r
-unsigned char artist[1024];\r
-unsigned char title[1024];\r
-int time;\r
-\r
-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};\r
-\r
-void usage()\r
-{\r
- fprintf(stderr,"Usage >> fapg [-backslash] [-debug] [-format=m3u|pls|html] [-output=/path/to/file.m3u] [-prefix=/the/prefix] [-recursive] [-windows] /path/to/mp3/dir1 [/path/to/mp3/dir2 ...]\n");\r
- exit(1);\r
-}\r
-\r
-void parse_options(int argc,char **argv)\r
-{\r
- static struct option long_options[]=\r
- {\r
- {"backslash",no_argument,&separator,'\\'},\r
- {"debug",no_argument,&debug,1},\r
- {"format",required_argument,0,'f'},\r
- {"output",required_argument,0,'o'},\r
- {"prefix",required_argument,0,'p'},\r
- {"recursive",no_argument,&recursive,1},\r
- {"windows",no_argument,&windows,1}\r
- };\r
- int c;\r
- int option_index=0;\r
- while ((c=getopt_long_only(argc,argv,"",long_options,&option_index))!=-1)\r
- {\r
- switch(c)\r
- {\r
- case 0:\r
- break;\r
- case 'f':\r
- if (strcmp(optarg,"m3u")==0) format=0; else\r
- if (strcmp(optarg,"pls")==0) format=1; else\r
- if (strcmp(optarg,"html")==0) format=2; else\r
- usage();\r
- break;\r
- case 'o':\r
- close(1);\r
- if (fopen(optarg,"w")==NULL) { fprintf(stderr,"Error >> unable to open output file : %s\n",optarg); exit(2); }\r
- break;\r
- case 'p':\r
- prefix=malloc(strlen(optarg)+1);\r
- strcpy(prefix,optarg);\r
- break;\r
- default:\r
- usage();\r
- }\r
- }\r
-}\r
-\r
-void parse_mp3(unsigned char *file)\r
-{\r
- int bitrates[2][3][15]=\r
- {{{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448},\r
- {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384},\r
- {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}},\r
- {{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256},\r
- {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160},\r
- {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}}};\r
- FILE *fic;\r
- unsigned char *c;\r
- int lus;\r
-\r
- if (debug) fprintf(stderr,"Debug >> parsing mp3 : %s\n",file);\r
-\r
- /* read header */\r
- if ((fic=fopen(file,"r"))==NULL) { fprintf(stderr,"Warning >> can't open file : %s\n",file); return; }\r
- lus=fread(buffer,1,MAX,fic);\r
- c=buffer; \r
-\r
- /* try ID3v2 */\r
- if (buffer[0]=='I' && buffer[1]=='D' && buffer[2]=='3')\r
- {\r
- int size;\r
- int version;\r
- version=*(buffer+3);\r
- if (version<2 || version>4)\r
- fprintf(stderr,"Warning >> ID3 v2.%d not implemented ! trying anyway : %s\n",version,file);\r
- if (*(buffer+5)!=0)\r
- fprintf(stderr,"Warning >> specials headers not implemented (%d) ! trying anyway : %s\n",*(buffer+5),file);\r
- c=buffer+6;\r
- size=(*c<<24)+(*(c+1)<<16)+(*(c+2)<<8)+(*(c+3));\r
- if (size>lus) size=lus;\r
- c+=4;\r
- if (version==2) while (c<buffer+size-5)\r
- {\r
- int size=(*(c+3)<<16)+(*(c+4)<<8)+(*(c+5));\r
- if (*c==0) break;\r
- if (strncmp(c,"TT2",3)==0)\r
- {\r
- strncpy(title,c+7,size-1);\r
- title[size-1]='\0';\r
- }\r
- if (strncmp(c,"TP1",3)==0)\r
- {\r
- strncpy(artist,c+7,size-1);\r
- artist[size-1]='\0';\r
- }\r
- c+=size+6;\r
- }\r
- if (version==3 || version==4) while (c<buffer+size-7)\r
- {\r
- int size=(*(c+4)<<24)+(*(c+5)<<16)+(*(c+6)<<8)+(*(c+7));\r
- if (*c==0) break;\r
- if (strncmp(c,"TIT2",4)==0)\r
- {\r
- strncpy(title,c+11,size-1);\r
- title[size-1]='\0';\r
- }\r
- if (strncmp(c,"TPE1",4)==0)\r
- {\r
- strncpy(artist,c+11,size-1);\r
- artist[size-1]='\0';\r
- }\r
- c+=size+10;\r
- }\r
- }\r
- \r
- while (c<buffer+lus-10)\r
- {\r
- if (*c==0xFF && (*(c+1)&0xF0)==0xF0)\r
- {\r
- int version;\r
- int lay;\r
- int bitrate_index;\r
- int bitrate;\r
- version=2-(*(c+1)>>3&1);\r
- lay=4-(*(c+1)>>1&3);\r
- bitrate_index=*(c+2)>>4&0xF;\r
- if (version>=1 && version<=2 && lay-1>=0 && lay-1<=2 && bitrate_index>=0 && bitrate_index<=14)\r
- bitrate=bitrates[version-1][lay-1][bitrate_index];\r
- else\r
- bitrate=0;\r
- if (bitrate!=0)\r
- {\r
- fseek(fic,0,SEEK_END);\r
- time=(ftell(fic)+buffer-c)/125/bitrate;\r
- }\r
- else\r
- time=0;\r
- break;\r
- }\r
- c++;\r
- }\r
-\r
- /* try ID3v1 */\r
- if (strlen(artist)==0 && strlen(title)==0)\r
- {\r
- fseek(fic,-128,SEEK_END);\r
- lus=fread(buffer,1,128,fic);\r
- if (lus==128 && buffer[0]=='T' && buffer[1]=='A' && buffer[2]=='G')\r
- {\r
- strncpy(title,buffer+3,30);\r
- title[30]='\0';\r
- c=title+29;\r
- while (c>title && *c==' ') *(c--)='\0';\r
- strncpy(artist,buffer+33,30);\r
- artist[30]='\0';\r
- c=artist+29;\r
- while (c>artist && *c==' ') *(c--)='\0';\r
- }\r
- }\r
-\r
- fclose(fic);\r
-} \r
-\r
-void parse_ogg(unsigned char *file)\r
-{\r
- FILE *fic;\r
- unsigned char *c;\r
- int lus;\r
- int sample_rate;\r
- int samples;\r
-\r
- if (debug) fprintf(stderr,"Debug >> parsing ogg : %s\n",file);\r
-\r
- /* read header */\r
- if ((fic=fopen(file,"r"))==NULL) { fprintf(stderr,"Warning >> can't open file : %s\n",file); return; }\r
- lus=fread(buffer,1,MAX,fic);\r
-\r
- /* try Ogg */\r
- if (buffer[0]!='O' && buffer[1]!='g' && buffer[2]!='g')\r
- {\r
- fprintf(stderr,"Warning >> not a Ogg header : %s\n",file);\r
- return;\r
- }\r
- \r
- c=buffer+0x28;\r
- sample_rate=(*c)+(*(c+1)<<8)+(*(c+2)<<16)+(*(c+3)<<24);\r
-\r
- while (c<buffer+lus-10)\r
- {\r
- int size;\r
- if (strncmp(c,"TITLE=",6)==0)\r
- {\r
- size=*(c-4)+(*(c-3)<<8)+(*(c-2)<<16)+(*(c-1)<<24);\r
- strncpy(title,c+6,size-6);\r
- title[size-6]='\0';\r
- c+=size;\r
- }\r
- if (strncmp(c,"ARTIST=",7)==0)\r
- {\r
- size=*(c-4)+(*(c-3)<<8)+(*(c-2)<<16)+(*(c-1)<<24);\r
- strncpy(artist,c+7,size-7);\r
- artist[size-7]='\0';\r
- c+=size;\r
- }\r
- c++;\r
- }\r
- \r
- fseek(fic,-MAX,SEEK_END);\r
- lus=fread(buffer,1,MAX,fic);\r
- c=buffer+lus-1;\r
- while (strncmp(c,"OggS",4)!=0 && c>buffer) c--;\r
- if (c!=buffer)\r
- {\r
- c+=6;\r
- samples=(*c)+(*(c+1)<<8)+(*(c+2)<<16)+(*(c+3)<<24);\r
- time=samples/sample_rate;\r
- }\r
- \r
- fclose(fic);\r
-}\r
-\r
-void parse_directory(unsigned char *path)\r
-{\r
- DIR *dirp;\r
- struct dirent *dp;\r
- unsigned char newpath[PATH_MAX];\r
- unsigned char *c;\r
-\r
- void print_faketitle()\r
- {\r
- c=newpath+strlen(newpath);\r
- while (c>newpath && *c!='/') c--;\r
- while (c<newpath+strlen(newpath)-5)\r
- {\r
- c++;\r
- if (*c=='_') putchar(' '); else if (windows) putchar(unix2dos[*c]); else putchar(*c);\r
- }\r
- }\r
- \r
- void print_path()\r
- {\r
- printf(prefix);\r
- for (c=newpath+skip;*c!='\0';c++) if (*c=='/') putchar(separator); else if (windows) putchar(unix2dos[*c]); else putchar(*c);\r
- }\r
-\r
- if (debug) fprintf(stderr,"Debug >> parsing directory : %s\n",path);\r
- if ((dirp=opendir(path))==NULL) { fprintf(stderr,"Warning >> can't open directory : %s\n",path); return; }\r
- while ((dp=readdir(dirp))!=NULL)\r
- {\r
- sprintf(newpath,"%s/%s",path,dp->d_name);\r
- if (recursive && dp->d_type & DT_DIR && strcmp(dp->d_name,".")!=0 && strcmp(dp->d_name,"..")!=0)\r
- {\r
- parse_directory(newpath);\r
- }\r
- if (dp->d_type & DT_REG)\r
- {\r
- unsigned char ext[5];\r
- int i;\r
- for (i=0;i<5;i++) ext[i]=tolower(dp->d_name[strlen(dp->d_name)-4+i]);\r
- artist[0]='\0';\r
- title[0]='\0';\r
- time=-2;\r
- if (strcmp(".mp3",ext)==0) { time=-1; parse_mp3(newpath); }\r
- if (strcmp(".ogg",ext)==0) { time=-1; parse_ogg(newpath); }\r
- if (strcmp(".wav",ext)==0) { time=-1; /* parse_wav(newpath); */ }\r
- \r
- if (time!=-2) /* is it an audio file ? */\r
- {\r
- counter++;\r
- switch (format)\r
- {\r
- case 0:\r
- if (time!=-1)\r
- {\r
- printf("#EXTINF:%d,",time);\r
- if (strlen(artist)==0 && strlen(title)==0) print_faketitle();\r
- else printf("%s - %s",artist,title);\r
- putchar('\n');\r
- }\r
- print_path();\r
- putchar('\n');\r
- break;\r
- case 1:\r
- printf("File%d=",counter);\r
- print_path();\r
- putchar('\n');\r
- printf("Title%d=",counter);\r
- if (strlen(artist)==0 && strlen(title)==0) print_faketitle();\r
- else printf("%s - %s",artist,title);\r
- putchar('\n');\r
- if (time!=-1) printf("Length%d=%d\n",counter,time);\r
- break;\r
- case 2:\r
- printf("<tr><td>%d</td><td>%s</td><td>%s</td><td>",counter,artist,title);\r
- if (time==-1) printf("?"); else printf("%d",time);\r
- printf("</td></tr>\n");\r
- break;\r
- }\r
- }\r
- }\r
- }\r
- closedir(dirp);\r
-}\r
-\r
-int main(int argc,char **argv)\r
-{\r
- parse_options(argc,argv);\r
- if (optind==argc) usage();\r
- switch (format)\r
- {\r
- case 0:\r
- printf("#EXTM3U\n");\r
- break;\r
- case 1:\r
- printf("[playlist]\n");\r
- break;\r
- case 2:\r
- printf("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n\n<html>\n\n<head>\n<title>Playlist generated by FAPG 0.0</title>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" />\n<link rel=\"stylesheet\" type=\"text/css\" href=\"fapg.css\" />\n</head>\n\n<body>\n\n<h1>Playlist</h1>\n\n<table>\n<tr><th>Entry</th><th>Artist</th><th>Title</th><th>Length</th></tr>\n");\r
- break;\r
- }\r
- for (;optind<argc;optind++)\r
- {\r
- if (strlen(prefix)!=0) skip=strlen(argv[optind]);\r
- parse_directory(argv[optind]);\r
- }\r
- switch(format)\r
- {\r
- case 1:\r
- printf("NumberOfEntries=%d\nVersion=2\n",counter);\r
- break;\r
- case 2:\r
- printf("</table>\n\n<p>Playlist generated by <a href=\"http://royale.zerezo.com/fapg/\">FAPG 0.0</a></p>\n\n</body>\n\n</html>");\r
- break;\r
- }\r
- exit(0);\r
-}\r
+/*
+ * FAPG
+ */
+#define VERSION "0.35"
+/*
+ * 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).
+ * It is very usefull if you have a large amount of audio files
+ * and you want to quickly and frequently build a playlist.
+ *
+ * Copyright (C) 2003-2004 Antoine Jacquet <royale@zerezo.com>
+ * http://royale.zerezo.com/fapg/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <string.h>
+#include <strings.h>
+#include <limits.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+#include <assert.h>
+#include "genres.h"
+
+#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="/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;
+
+unsigned char artist[1024];
+unsigned char title[1024];
+unsigned char genrebuf[1024];
+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 *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|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);
+}
+
+#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)
+{
+ while(*c != 0) {
+ mywebputchar(*c);
+ c++;
+ }
+}
+
+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 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)
+{
+ 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
+ c = path;
+ myputstr(c);
+}
+
+void noreferal(const char *path, const char *artist, const char *title)
+{
+ printf("\t\t<description><![CDATA[<h4>");
+ myputstr(artist);
+ printf("</h4><h5>");
+ myputstr(title);
+ printf("</h5><a href=\"");
+ print_webpath(path);
+ printf
+ ("\"><br><br>Direct Link to Audiofile</a><br>]]></description>%s",
+ eol);
+}
+
+void reference(const char *title)
+{
+ FILE *pipe = NULL;
+ static char command[2048], buffer[1024];
+ int buflen = 8192;
+
+ buflen = strlen(title) + strlen(referal) + 3;
+ assert((buflen < 2046));
+ strcpy(command, referal);
+ buflen = strlen(command);
+ command[buflen] = ' ';
+ command[buflen + 1] = '"';
+ command[buflen + 2] = 0;
+ strcat(command, title);
+ buflen = strlen(command);
+ command[buflen] = '"';
+ command[buflen + 1] = 0;
+ if(debug)
+ fprintf(stderr, "Debug >> processing command: %s\n", command);
+ pipe = popen(command, "r");
+ if(pipe == NULL) {
+ fprintf(stderr, "Warning >> can't open pipe >%s< !\n", command);
+ free(command);
+ return;
+ }
+ fgets(buffer, 1020, pipe);
+ while(!feof(pipe)) {
+ fputs(buffer, stdout);
+ fgets(buffer, 1020, pipe);
+ }
+ pclose(pipe);
+ return;
+}
+
+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;
+ }
+ }
+}
+
+void parse_mp3(unsigned char *file)
+{
+ int bitrates[2][3][15] =
+ { {{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384,
+ 416, 448},
+ {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,
+ 384},
+ {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256,
+ 320}},
+ {{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256},
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160},
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}}
+ };
+ FILE *fic;
+ unsigned char *c;
+ int lus;
+
+ genre = 0;
+ genrebuf[0] = 0;
+ if(debug)
+ fprintf(stderr, "Debug >> parsing mp3 : %s\n", file);
+
+ /* read header */
+ if((fic = fopen(file, "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;
+ }
+ c++;
+ }
+
+ /* try ID3v1 */
+ if(strlen(artist) == 0 && strlen(title) == 0) {
+ fseek(fic, -128, SEEK_END);
+ lus = fread(buffer, 1, 128, fic);
+ if(lus == 128 && buffer[0] == 'T' && buffer[1] == 'A'
+ && buffer[2] == 'G') {
+ strncpy(title, buffer + 3, 30);
+ title[30] = '\0';
+ c = title + 29;
+ while(c > title && *c == ' ')
+ *(c--) = '\0';
+ strncpy(artist, buffer + 33, 30);
+ artist[30] = '\0';
+ c = artist + 29;
+ while(c > artist && *c == ' ')
+ *(c--) = '\0';
+ /* strncpy(album,buffer+65,30); */
+ /* strncpy(year,buffer+97,4); */
+ /* strncpy(comment,buffer+101,30); */
+ /* strncpy(genrebuf,buffer+127,1); genre[1]=0; */
+ genre = buffer[127];
+ }
+ }
+
+ fclose(fic);
+}
+
+void parse_ogg(unsigned char *file)
+{
+ FILE *fic;
+ unsigned char *c;
+ int lus;
+ int sample_rate;
+ int samples;
+
+ if(debug)
+ fprintf(stderr, "Debug >> parsing ogg : %s\n", file);
+
+ /* read header */
+ if((fic = fopen(file, "r")) == NULL) {
+ fprintf(stderr, "Warning >> can't open file : %s\n", file);
+ return;
+ }
+ lus = fread(buffer, 1, OGG_BASE, fic);
+
+ /* try Ogg */
+ if(buffer[0] != 'O' && buffer[1] != 'g' && buffer[2] != 'g') {
+ fprintf(stderr, "Warning >> not a Ogg header : %s\n", file);
+ return;
+ }
+
+ c = buffer + 0x28;
+ sample_rate =
+ (*c) + (*(c + 1) << 8) + (*(c + 2) << 16) + (*(c + 3) << 24);
+
+ 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;
+ }
+ }
+ }
+
+ fclose(fic);
+}
+
+#define FSN 32
+#define MAXINO (1<<24)
+#define INOTYP unsigned long
+#define regbit_qry(x,y) ( x[( (y) / sizeof(INOTYP) )] & 1<<( (y) % sizeof(INOTYP) ) )
+#define regbit_set(x,y) ( x[( (y) / sizeof(INOTYP) )] |= 1<<( (y) % sizeof(INOTYP) ) )
+
+int hlink_check(struct stat *info)
+{
+ /*
+ * for speed this subroutine should only be called
+ * - if the file has more than one hardlink
+ * - if the file is a resolved softlink
+ */
+ /* the persistent variables */
+ static INOTYP *list[FSN];
+ static dev_t name[FSN];
+ /* some temporary variables */
+ int fsn, is_registered = 0;
+
+ /* assertions - in case parameters are lowered for less memory usage */
+ assert(fsn < FSN);
+ assert((info->st_ino) / sizeof(INOTYP) < MAXINO);
+
+ /* search which internal registration number is used for this filesystem */
+ for(fsn = 0; (name[fsn] != (info->st_dev)) && (name[fsn] != 0); fsn++);
+
+ /* if file system is not registered yet, do it and leave */
+ if(name[fsn] == 0) {
+ name[fsn] = (info->st_dev);
+ /* provide space for the bitmap that maps the inodes of this file system */
+ list[fsn] = (INOTYP *) calloc(MAXINO, sizeof(INOTYP));
+ /* no comparison is needed in empty lists ... return */
+ if(debug)
+ fprintf(stderr,
+ "Debug >> Linked >> Init List %04x @mem %04lx\n",
+ (int)name[fsn], (long)&list[fsn]);
+ } else {
+ /* this looks more complicated than it really is */
+ /* the idea is very simple:
+ * provide a bitmap that maps all inodes of a file system
+ * to mark all files that have already been visited.
+ * If it is already visited, do not add it to the playlist
+ */
+ /*
+ * The difficulty is as follows:
+ * struct inode_bitmap { char registered:1; } bitmap[1<<MAXINO];
+ * would be byte-aligned and would allocate at least eight times the needed space.
+ * Feel free to change the definitions that are involved here, if you know better.
+ */
+ if(regbit_qry(list[fsn], (info->st_ino)))
+ is_registered = 1;
+ else
+ regbit_set(list[fsn], (info->st_ino));
+ /*
+ * the debug expression is more complicated then the working stuff
+ */
+ if(debug)
+ fprintf(stderr, "Debug >> Linked >> DEV %04x INO %06x => "
+ "list[%02x][%04x] = %04x & %04x --> %s registered\n",
+ (int)info->st_dev, (int)info->st_ino, fsn,
+ (int)((info->st_ino) / sizeof(INOTYP)),
+ (int)list[fsn][(info->st_ino) / sizeof(INOTYP)],
+ 1 << ((info->st_ino) % sizeof(INOTYP)),
+ is_registered ? "Already" : "Not");
+ }
+ return is_registered;
+}
+
+
+void parse_file(unsigned char *newpath)
+{
+ 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++;
+ switch (format) {
+ case 0:
+ if(duration != -1) {
+ printf("#EXTINF:%d,%s - %s%s", duration, artist, title,
+ 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 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;
+ }
+ }
+}
+
+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 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;
+ case 1:
+ printf("[playlist]%s", eol);
+ break;
+ case 2:
+ printf
+ ("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">%s%s<html>%s%s<head>%s<title>Playlist generated by FAPG "
+ 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;
+ }
+ for(; optind < argc; optind++) {
+ parse_directory(argv[optind]);
+ }
+ switch (format) {
+ case 1:
+ printf("NumberOfEntries=%d%sVersion=2%s", counter, eol, eol);
+ break;
+ case 2:
+ printf
+ ("</table>%s%s<p>Playlist generated by <a href=\"http://royale.zerezo.com/fapg/\">FAPG "
+ 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);
+}