X-Git-Url: http://royale.zerezo.com/git/?p=FAPG;a=blobdiff_plain;f=fapg.c;fp=fapg.c;h=58f3f20336b2ca82a7fadfdf12f833a5be36751f;hp=a72d4e7683d8ab5e2ec964f43af7a6fa3a9679f1;hb=dfbf4f525560afb358cec09a9411712ded235b05;hpb=389b0b695d6dd0e98f3ad67a47d6f6e48ed1b62e diff --git a/fapg.c b/fapg.c index a72d4e7..58f3f20 100644 --- a/fapg.c +++ b/fapg.c @@ -37,14 +37,26 @@ #include #include #include "genres.h" +#ifdef HAVE_LIBURIPARSER +# include +#endif -#define VERSION "0.38" #define MP3_BASE 1024 #define OGG_BASE 1024*10 #define MAX 1024*200 /* 200ko for ID3 with JPEG images in it */ +#define FORMAT_M3U 0 +#define FORMAT_PLS 1 +#define FORMAT_HTML 2 +#define FORMAT_RSS 3 +#define FORMAT_PLP 4 +#define FORMAT_UMS 5 +#ifdef HAVE_LIBURIPARSER +# define FORMAT_XSPF 6 +#endif + int debug = 0; -int format = 0; /* 0 = m3u ; 1 = pls ; 2 = html ; 3 = rss */ +int format = FORMAT_M3U; char *genrelist = NULL; unsigned char *prefix = ""; unsigned char *base = ""; @@ -217,8 +229,14 @@ unsigned char *iso2web[256] = { void usage() { +#ifdef HAVE_LIBURIPARSER +# define FAPG_FORMATS "m3u|pls|xspf|html|rss|pla|txx" +#else +# define FAPG_FORMATS "m3u|pls|html|rss|pla|txx" +#endif fprintf(stderr, - "Usage >> fapg [-b|--backslash] [-d|--debug] [-f|--format=m3u|pls|html|rss|pla|txx] [-g|--genre=#:#:...] [-n|--nohardlink] [-o|--output=/path/to/file.m3u] [-p|--prefix=/the/prefix] [-r|--recursive] [-w|--windows] [-c|--command=] [-x|--exclude=#:#:...] [-s|--stdin] /path/to/mp3/dir1 [/path/to/mp3/dir2 ...]\n"); + "Usage >> fapg [-b|--backslash] [-d|--debug] [-f|--format=" FAPG_FORMATS "] [-g|--genre=#:#:...] [-n|--nohardlink] [-o|--output=/path/to/file.m3u] [-p|--prefix=/the/prefix] [-r|--recursive] [-w|--windows] [-c|--command=] [-x|--exclude=#:#:...] [-s|--stdin] /path/to/mp3/dir1 [/path/to/mp3/dir2 ...]\n"); +#undef FAPG_FORMATS exit(1); } @@ -530,17 +548,21 @@ void parse_options(int argc, char **argv) break; case 'f': if(strcmp(optarg, "m3u") == 0) - format = 0; + format = FORMAT_M3U; else if(strcmp(optarg, "pls") == 0) - format = 1; + format = FORMAT_PLS; else if(strcmp(optarg, "html") == 0) - format = 2; + format = FORMAT_HTML; else if(strcmp(optarg, "rss") == 0) - format = 3; + format = FORMAT_RSS; else if(strcmp(optarg, "pla") == 0) - format = 4; + format = FORMAT_PLP; else if(strcmp(optarg, "txx") == 0) - format = 5; + format = FORMAT_UMS; +#ifdef HAVE_LIBURIPARSER + else if(strcmp(optarg, "xspf") == 0) + format = FORMAT_XSPF; +#endif else usage(); break; @@ -880,7 +902,6 @@ void parse_ogg(unsigned char *file) fclose(fic); } - void parse_mpc(unsigned char *file) { FILE *fic; @@ -1038,8 +1059,192 @@ int hlink_check(struct stat *info) return is_registered; } +#ifdef HAVE_LIBURIPARSER +char * relative_uri_malloc(const char * unixFilename, const char * baseDir) +{ + char * absSourceFile; + size_t absSourceLen; + char * sourceUriString; + char * baseUriString; + UriParserStateA state; + UriUriA sourceUri; + UriUriA baseUri; + UriUriA relativeUri; + int charsRequired; + char * output; + + /* checks */ + if ((unixFilename == NULL) || (baseDir == NULL)) { + return NULL; + } + + /* base URI */ + baseUriString = malloc((7 + 3 * strlen(baseDir) + 1) * sizeof(char)); + if (baseUriString == NULL) { + return NULL; + } + if (uriUnixFilenameToUriStringA(baseDir, baseUriString) != 0) { + free(baseUriString); + return NULL; + } + state.uri = &baseUri; + if (uriParseUriA(&state, baseUriString) != 0) { + free(baseUriString); + uriFreeUriMembersA(&baseUri); + return NULL; + } -void parse_file(unsigned char *newpath) + /* source URI */ + if (unixFilename[0] != '/') { + const int baseDirLen = strlen(baseDir); + const int sourceFileLen = strlen(unixFilename); + absSourceLen = baseDirLen + sourceFileLen; + absSourceFile = malloc((absSourceLen + 1) * sizeof(char)); + sprintf(absSourceFile, "%s%s", baseDir, unixFilename); + } else { + absSourceLen = strlen(unixFilename); + absSourceFile = (char *)unixFilename; + } + sourceUriString = malloc((7 + 3 * absSourceLen + 1) * sizeof(char)); + if (sourceUriString == NULL) { + free(baseUriString); + if (unixFilename[0] != '/') { + free(absSourceFile); + } + uriFreeUriMembersA(&baseUri); + return NULL; + } + if (uriUnixFilenameToUriStringA(absSourceFile, sourceUriString) != 0) { + free(baseUriString); + free(sourceUriString); + if (unixFilename[0] != '/') { + free(absSourceFile); + } + uriFreeUriMembersA(&baseUri); + return NULL; + } + state.uri = &sourceUri; + if (uriParseUriA(&state, sourceUriString) != 0) { + free(baseUriString); + free(sourceUriString); + uriFreeUriMembersA(&baseUri); + uriFreeUriMembersA(&sourceUri); + return NULL; + } + if (uriNormalizeSyntaxA(&sourceUri) != 0) { + free(baseUriString); + free(sourceUriString); + if (unixFilename[0] != '/') { + free(absSourceFile); + } + uriFreeUriMembersA(&baseUri); + uriFreeUriMembersA(&sourceUri); + return NULL; + } + + /* make relative (or keep absolute if necessary) */ + if (uriRemoveBaseUriA(&relativeUri, &sourceUri, &baseUri, URI_FALSE) != 0) { + free(baseUriString); + free(sourceUriString); + if (unixFilename[0] != '/') { + free(absSourceFile); + } + uriFreeUriMembersA(&baseUri); + uriFreeUriMembersA(&sourceUri); + uriFreeUriMembersA(&relativeUri); + return NULL; + } + + /* back to string */ + if (uriToStringCharsRequiredA(&relativeUri, &charsRequired) != 0) { + free(baseUriString); + free(sourceUriString); + if (unixFilename[0] != '/') { + free(absSourceFile); + } + uriFreeUriMembersA(&baseUri); + uriFreeUriMembersA(&sourceUri); + uriFreeUriMembersA(&relativeUri); + return NULL; + } + output = malloc((charsRequired + 1) * sizeof(char)); + if (uriToStringA(output, &relativeUri, charsRequired + 1, NULL) != 0) { + free(baseUriString); + free(sourceUriString); + if (unixFilename[0] != '/') { + free(absSourceFile); + } + free(output); + uriFreeUriMembersA(&baseUri); + uriFreeUriMembersA(&sourceUri); + uriFreeUriMembersA(&relativeUri); + return NULL; + } + + free(baseUriString); + free(sourceUriString); + if (unixFilename[0] != '/') { + free(absSourceFile); + } + uriFreeUriMembersA(&baseUri); + uriFreeUriMembersA(&sourceUri); + uriFreeUriMembersA(&relativeUri); + + return output; +} + +char * xml_escape_malloc(const char * input) +{ + const char * read = input; + char * output; + char * write; + + if (input == NULL) { + return NULL; + } + + output = malloc((6 * strlen(input) + 1) * sizeof(char)); + if (output == NULL) { + return NULL; + } + write = output; + + for (;;) { + if (*read == '\0') { + *write = '\0'; + return output; + } + + switch ((unsigned char)*read) { + case '&': + strcpy(write, "&"); + write += 5; + break; + case '<': + strcpy(write, "<"); + write += 4; + break; + case '>': + strcpy(write, ">"); + write += 4; + break; + case '\'': + strcpy(write, "'"); + write += 6; + break; + case '"': + strcpy(write, """); + write += 6; + break; + default: + *(write++) = *read; + } + read++; + } +} +#endif + +void parse_file(unsigned char *newpath, unsigned char * original_path) { unsigned char ext[5]; int j, encoding = 0; @@ -1114,7 +1319,7 @@ void parse_file(unsigned char *newpath) if(duration != -2 && genrelist[genre]) { /* is it an audio file ? */ counter++; switch (format) { - case 0: + case FORMAT_M3U: if(duration != -1) { printf("#EXTINF:%d,", duration); if(strlen(artist) != 0) @@ -1124,7 +1329,7 @@ void parse_file(unsigned char *newpath) print_path(newpath); printf("%s", eol); break; - case 1: + case FORMAT_PLS: printf("File%d=", counter); print_path(newpath); printf("%sTitle%d=", eol, counter); @@ -1134,7 +1339,7 @@ void parse_file(unsigned char *newpath) if(duration != -1) printf("Length%d=%d%s", counter, duration, eol); break; - case 2: + case FORMAT_HTML: printf("%d%s%s", counter, artist, title); if(duration == -1) @@ -1143,7 +1348,7 @@ void parse_file(unsigned char *newpath) printf("%d:%s%d%s", duration / 60, duration % 60 < 10 ? "0" : "", duration % 60, eol); break; - case 3: + case FORMAT_RSS: if(duration != -1) { struct stat infos; char timebuffer[256]; @@ -1189,19 +1394,55 @@ void parse_file(unsigned char *newpath) printf("\t%s", eol); } break; - case 4: // printing output for Sansa players + case FORMAT_PLP: myplaputstr("HARP, "); myplaputstr(newpath); myplaputstr(eol); break; - case 5: //t-series playlist + case FORMAT_UMS: txxputstr(newpath); break; +#ifdef HAVE_LIBURIPARSER + case FORMAT_XSPF: + printf("\n"); + if (strlen(title) > 0) { + char * escaped_title = xml_escape_malloc(title); + if (escaped_title != NULL) { + printf(" %s\n", escaped_title); + free(escaped_title); + } + } + if (strlen(artist) > 0) { + char * escaped_artist = xml_escape_malloc(artist); + if (escaped_artist != NULL) { + printf(" %s\n", escaped_artist); + free(escaped_artist); + } + } + if (duration > 0) { + printf(" %d\n", duration); + } + { + char * relative_location; + char * escaped_location; + relative_location = relative_uri_malloc(newpath, original_path); + if (relative_location != NULL) { + escaped_location = xml_escape_malloc(relative_location); + if (escaped_location != NULL) { + printf(" %s\n", escaped_location); + free(escaped_location); + } + free(relative_location); + } + } + printf("\n"); + break; +#endif } } } -void parse_directory(unsigned char *path) +void parse_directory(unsigned char *path, unsigned char * original_path) { int i, n; struct dirent **namelist; @@ -1216,7 +1457,7 @@ void parse_directory(unsigned char *path) } /* check if it is a filename */ if(S_ISREG(infos.st_mode) || S_ISLNK(infos.st_mode)) { - parse_file(path); + parse_file(path, original_path); return; } /* must be a directory - or something unusable like pipe, socket, etc */ @@ -1225,7 +1466,7 @@ void parse_directory(unsigned char *path) return; } for(i = 0; i < n; i++) { - sprintf(newpath, "%s/%s", path, namelist[i]->d_name); + snprintf(newpath, PATH_MAX, "%s/%s", path, namelist[i]->d_name); if(stat(newpath, &infos) != 0) { fprintf(stderr, "Warning >> can't stat entry : %s\n", newpath); @@ -1234,11 +1475,11 @@ void parse_directory(unsigned char *path) if(recursive && S_ISDIR(infos.st_mode) && strcmp(namelist[i]->d_name, ".") != 0 && strcmp(namelist[i]->d_name, "..") != 0) - parse_directory(newpath); + parse_directory(newpath, original_path); /* hlink_check() might be applied more selective ... avoidhlink is only a simple prereq */ if(S_ISREG(infos.st_mode) && !(avoidhlinked && hlink_check(&infos))) { - parse_file(newpath); + parse_file(newpath, original_path); } free(namelist[i]); } @@ -1250,16 +1491,19 @@ int main(int argc, char **argv) winorunix = one2one; basemap = one2one; parse_options(argc, argv); + if(optind == argc && !fromstdin) usage(); + + /* print header */ switch (format) { - case 0: + case FORMAT_M3U: printf("#EXTM3U%s", eol); break; - case 1: + case FORMAT_PLS: printf("[playlist]%s", eol); break; - case 2: + case FORMAT_HTML: printf ("%s%s%s%s%sPlaylist generated by FAPG " VERSION @@ -1271,7 +1515,7 @@ int main(int argc, char **argv) eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol, eol); break; - case 3: + case FORMAT_RSS: { time_t zeit; char timebuffer[256]; @@ -1293,48 +1537,95 @@ int main(int argc, char **argv) basemap = noand; } break; - case 4: + case FORMAT_PLP: { eol = "\r\n"; myplaputstr("PLP PLAYLIST\r\nVERSION 1.20\r\n\r\n"); } break; - case 5: + case FORMAT_UMS: { txxputheader(" iriver UMS PLA"); } + break; +#ifdef HAVE_LIBURIPARSER + case FORMAT_XSPF: + printf("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n" + "<!-- generator=\"FAPG " VERSION " -->\n" + "<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\">\n" + "<trackList>\n"); + break; +#endif } - if(fromstdin) { - unsigned char path[PATH_MAX]; - int i; - while(fgets(path, PATH_MAX, stdin)) { - for(i = 0; i < PATH_MAX; i++) - if(path[i] == '\r' || path[i] == '\n') - path[i] = '\0'; - parse_directory(path); - } - } else - for(; optind < argc; optind++) { - parse_directory(argv[optind]); - } + + /* iterate through files */ + { + const char * const pwd_source = getenv("PWD"); + const int pwdlen = strlen(pwd_source); + char * const pwd = malloc((pwdlen + 1 + 1) * sizeof(char)); + sprintf(pwd, "%s/", pwd_source); + + if(fromstdin) { + unsigned char path[PATH_MAX]; + int i; + while(fgets(path, PATH_MAX, stdin)) { + for(i = 0; i < PATH_MAX; i++) + if(path[i] == '\r' || path[i] == '\n') + path[i] = '\0'; + if (i <= 0) { + continue; + } + + /* strip trailing slash */ + if (path[i - 1] == '/') { + path[i - 1] = '\0'; + } + + parse_directory(path, pwd); + } + } else + for(; optind < argc; optind++) { + /* strip trailing slash */ + char * dup = strdup(argv[optind]); + const int len = strlen(dup); + if ((len > 0) && (dup[len - 1] == '/')) { + dup[len - 1] = '\0'; + } + + parse_directory(dup, pwd); + } + + free(pwd); + } + + /* print footer */ switch (format) { - case 1: + case FORMAT_PLS: printf("NumberOfEntries=%d%sVersion=2%s", counter, eol, eol); break; - case 2: + case FORMAT_HTML: printf ("</table>%s%s<p>Playlist generated by <a href=\"http://royale.zerezo.com/fapg/\">FAPG " VERSION "</a></p>%s%s</body>%s%s</html>", eol, eol, eol, eol, eol, eol); break; - case 3: + case FORMAT_RSS: printf(" </channel>%s</rss>%s", eol, eol); break; - case 5: + case FORMAT_UMS: txxputcounter(counter); break; +#ifdef HAVE_LIBURIPARSER + case FORMAT_XSPF: + printf("</trackList>\n" + "</playlist>\n"); + break; +#endif } + if(genrelist) free(genrelist); + exit(0); } +