From 326b491fdee96b1f93d27ea5ba86a7f394e5a1d3 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sat, 5 Apr 2025 02:02:50 -0400 Subject: [PATCH] Attempt to add opus support. --- Makefile.am | 19 +- configure.ac | 6 + filestructure.txt | 131 ++++++++ metadata.c | 54 +++- tagutils/opus.c | 59 ++++ tagutils/opus.h | 6 + tagutils/tagutils.c | 303 +++++++++--------- utils.c | 738 ++++++++++++++++++++++---------------------- 8 files changed, 792 insertions(+), 524 deletions(-) create mode 100644 filestructure.txt create mode 100644 tagutils/opus.c create mode 100644 tagutils/opus.h diff --git a/Makefile.am b/Makefile.am index 1e33833..162c961 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,7 +15,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA. -AM_CFLAGS = -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 +AM_CFLAGS = -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 @libpng_CFLAGS@ @opus_CFLAGS@ SUBDIRS=po @@ -55,14 +55,15 @@ endif endif minidlnad_LDADD = \ - @LIBJPEG_LIBS@ \ - @LIBID3TAG_LIBS@ \ - @LIBSQLITE3_LIBS@ \ - @LIBAVFORMAT_LIBS@ \ - @LIBEXIF_LIBS@ \ - @LIBINTL@ \ - @LIBICONV@ \ - -lFLAC $(flacogglibs) $(vorbislibs) $(avahilibs) + @LIBJPEG_LIBS@ \ + @LIBID3TAG_LIBS@ \ + @LIBSQLITE3_LIBS@ \ + @LIBAVFORMAT_LIBS@ \ + @LIBEXIF_LIBS@ \ + @LIBINTL@ \ + @LIBICONV@ \ + -lFLAC $(flacogglibs) $(vorbislibs) $(avahilibs) \ + @opus_LIBS@ testupnpdescgen_SOURCES = testupnpdescgen.c upnpdescgen.c testupnpdescgen_LDADD = \ diff --git a/configure.ac b/configure.ac index b632bcb..a7a2a0e 100644 --- a/configure.ac +++ b/configure.ac @@ -462,6 +462,12 @@ AC_CHECK_LIB(vorbisfile, ov_open_callbacks, AM_CONDITIONAL(HAVE_VORBISFILE, false))], AM_CONDITIONAL(HAVE_VORBISFILE, false), -lvorbis -logg) +#test if we have opus/opusfile + +PKG_CHECK_MODULES([opus], [opus opusfile], + AM_CONDITIONAL(HAVE_OPUS, true) + AC_DEFINE(HAVE_OPUS,1,[Define to 1 if you have opus/opusfile]), + AM_CONDITIONAL(HAVE_OPUS, false)) AC_CHECK_LIB(FLAC, FLAC__stream_decoder_init_stream, [AC_CHECK_HEADERS([FLAC/all.h], AM_CONDITIONAL(HAVE_FLAC, true) diff --git a/filestructure.txt b/filestructure.txt new file mode 100644 index 0000000..5e8048d --- /dev/null +++ b/filestructure.txt @@ -0,0 +1,131 @@ +./upnpreplyparse.h +./clients.h +./log.h +./playlist.h +./upnphttp.c +./albumart.c +./containers.h +./monitor.h +./tivo_commands.h +./minidlna.c +./02_minidlna-xiph.patch +./icons.c +./ChangeLog +./upnpreplyparse.c +./minidlna.conf.5 +./minixml.h +./sql.h +./TODO +./monitor.c +./playlist.c +./linux/inotify.h +./linux/minidlna.init.d.script.tmpl +./linux/inotify-syscalls.h +./select.c +./buildroot/readymedia/Config.in +./buildroot/readymedia/readymedia.mk +./buildroot/external.desc +./buildroot/Config.in +./buildroot/external.mk +./buildroot/readymedia_defconfig +./buildroot/build-static.sh +./upnpevents.h +./scanner.c +./upnpglobalvars.h +./avahi.h +./tivo_utils.c +./upnpdescgen.c +./clients.c +./options.c +./NEWS +./COPYING +./tivo_commands.c +./tivo_beacon.c +./Makefile.am +./getifaddr.c +./metadata.c +./log.c +./image_utils.h +./kqueue.c +./testupnpdescgen.c +./uuid.c +./minidlnad.8 +./LICENCE.miniupnpd +./upnpsoap.c +./avahi.c +./libav.h +./albumart.h +./sendfile.h +./tivo_beacon.h +./po/es.po +./po/it.po +./po/nl.po +./po/ko.po +./po/ja.po +./po/sv.po +./po/nb.po +./po/POTFILES.in +./po/pl.po +./po/minidlna.pot +./po/LINGUAS +./po/ru.po +./po/sl.po +./po/Makevars +./po/de.po +./po/fr.po +./po/da.po +./README +./tagutils/tagutils-plist.c +./tagutils/tagutils-aac.h +./tagutils/tagutils-ogg.h +./tagutils/tagutils-pcm.h +./tagutils/tagutils-mp3.c +./tagutils/tagutils-flc.c +./tagutils/tagutils-aac.c +./tagutils/tagutils-flc.h +./tagutils/tagutils-dsf.c +./tagutils/tagutils-asf.h +./tagutils/tagutils-wav.h +./tagutils/tagutils-misc.c +./tagutils/tagutils-dff.h +./tagutils/tagutils-dff.c +./tagutils/tagutils-pcm.c +./tagutils/tagutils.c +./tagutils/tagutils.h +./tagutils/tagutils-asf.c +./tagutils/tagutils-wav.c +./tagutils/tagutils-dsf.h +./tagutils/tagutils-mp3.h +./tagutils/tagutils-ogg.c +./autogen.sh +./upnpdescgen.h +./upnpglobalvars.c +./minidlna.conf +./minidlnatypes.h +./scanner_sqlite.h +./getifaddr.h +./minidlnapath.h +./scanner.h +./image_utils.c +./process.c +./minissdp.h +./monitor_inotify.c +./monitor_kqueue.c +./upnphttp.h +./AUTHORS +./sql.c +./minixml.c +./uuid.h +./configure.ac +./utils.c +./upnpevents.c +./minissdp.c +./event.h +./tivo_utils.h +./metadata.h +./process.h +./codelength.h +./utils.h +./upnpsoap.h +./options.h +./containers.c diff --git a/metadata.c b/metadata.c index 04ed27c..76f104b 100644 --- a/metadata.c +++ b/metadata.c @@ -343,11 +343,55 @@ GetAudioMetadata(const char *path, const char *name) strcpy(type, "wav"); m.mime = strdup("audio/x-wav"); } - else if( ends_with(path, ".ogg") || ends_with(path, ".oga") ) - { - strcpy(type, "ogg"); - m.mime = strdup("audio/ogg"); - } +else if( ends_with(path,".oga") || ends_with(path,".ogg")) +{ + /* The .ogg/.oga file extensions present something of a problem. + * ".ogg" has been deprecated in favor of ".oga" for some time, but + * many applications still only recognize ".ogg". + * + * This examines the file and causes .ogg to be presented for any naked + * Vorbis file (MIME type audio/ogg; codecs=vorbis) and .oga + * (audio/ogg) to be used for everything else. This is in line with + * the official ogg naming conventions and, hopefully, makes for a + * resonable compromise. + */ + uint8_t oggtestbuf[35]; + FILE *oggfile = fopen (path, "rb"); + + if (oggfile == (FILE *)NULL) + { + DPRINTF(E_ERROR, L_METADATA, "Error opening %s\n", path); + return 0; + } + + if (fread (oggtestbuf, 1, 35, oggfile) != 35) + { + DPRINTF(E_WARN, L_METADATA, "Premature EOF on %s\n", path); + fclose (oggfile); + return 0; + } + fclose (oggfile); + + if (memcmp (&oggtestbuf[28], "\x01vorbis", 7)) + m.mime = strdup ("audio/ogg"); + else + m.mime = strdup ("audio/ogg; codecs=vorbis"); + + strcpy(type, "ogg"); +} +else if ( ends_with(path, ".opus") ) +{ + strcpy(type,"ops"); + m.mime = strdup("audio/ogg; codecs=opus"); +} +#if 0 +/* Not supported yet, and probably won't be. */ +else if( ends_with(path, ".ogx") ) +{ + strcpy(type, "ogx"); + m.mime = strdup("application/ogg"); +} +#endif else if( ends_with(path, ".pcm") ) { strcpy(type, "pcm"); diff --git a/tagutils/opus.c b/tagutils/opus.c new file mode 100644 index 0000000..7c0940f --- /dev/null +++ b/tagutils/opus.c @@ -0,0 +1,59 @@ +//========================================================================= +// FILENAME : tagutils-opus.c +// DESCRIPTION : Opus metadata reader +//========================================================================= + +static int +_get_opusfileinfo(char *filename, struct song_metadata *psong) +{ + OggOpusFile *opusfile; + const OpusTags *tags; + char **comment; + int *commentlen; + int j, e; + + + opusfile = op_open_file (filename, &e); + if(!opusfile) + { + DPRINTF(E_WARN, L_SCANNER, + "Error opening input file \"%s\": %s\n", filename, opus_strerror(e)); + return -1; + } + + DPRINTF(E_MAXDEBUG, L_SCANNER, "Processing file \"%s\"...\n", filename); + + psong->song_length = op_pcm_total (opusfile, -1); + if (psong->song_length < 0) + { + DPRINTF(E_WARN, L_SCANNER, + "Unable to obtain length of %s\n", filename); + psong->song_length = 0; + } else + /* Sample rate is always 48k, so length in ms is just samples/48 */ + psong->song_length /= 48; + + /* Note that this gives only the first link's channel count. */ + psong->channels = op_channel_count (opusfile, -1); + + psong->samplerate = 48000; + psong->bitrate = op_bitrate (opusfile, -1); + + tags = op_tags (opusfile, -1); + + if (!tags) + { + DPRINTF(E_WARN, L_SCANNER, "Unable to obtain tags from %s\n", + filename); + return -1; + } + + comment = tags->user_comments; + commentlen = tags->comment_lengths; + + for (j = 0; j < tags->comments; j++) + vc_scan (psong, *(comment++), *(commentlen++)); + + op_free (opusfile); + return 0; +} diff --git a/tagutils/opus.h b/tagutils/opus.h new file mode 100644 index 0000000..b9472f6 --- /dev/null +++ b/tagutils/opus.h @@ -0,0 +1,6 @@ +//========================================================================= +// FILENAME : tagutils-opus.h +// DESCRIPTION : Opus metadata reader +//========================================================================= + +static int _get_opusfileinfo(char *filename, struct song_metadata *psong); diff --git a/tagutils/tagutils.c b/tagutils/tagutils.c index b38a8a5..374f4aa 100644 --- a/tagutils/tagutils.c +++ b/tagutils/tagutils.c @@ -1,6 +1,6 @@ //========================================================================= -// FILENAME : tagutils.c -// DESCRIPTION : MP3/MP4/Ogg/FLAC metadata reader +// FILENAME : tagutils.c +// DESCRIPTION : MP3/MP4/Ogg/FLAC metadata reader //========================================================================= // Copyright (c) 2008- NETGEAR, Inc. All Rights Reserved. //========================================================================= @@ -42,6 +42,10 @@ #ifdef HAVE_ICONV #include #endif +/* ADD THIS INCLUDE FOR OPUS */ +#ifdef HAVE_OPUS +#include +#endif #include #include "tagutils.h" #include "../metadata.h" @@ -49,51 +53,51 @@ #include "../log.h" struct id3header { - unsigned char id[3]; - unsigned char version[2]; - unsigned char flags; - unsigned char size[4]; + unsigned char id[3]; + unsigned char version[2]; + unsigned char flags; + unsigned char size[4]; } __attribute((packed)); char *winamp_genre[] = { - /*00*/ "Blues", "Classic Rock", "Country", "Dance", - "Disco", "Funk", "Grunge", "Hip-Hop", - /*08*/ "Jazz", "Metal", "New Age", "Oldies", - "Other", "Pop", "R&B", "Rap", - /*10*/ "Reggae", "Rock", "Techno", "Industrial", - "Alternative", "Ska", "Death Metal", "Pranks", - /*18*/ "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", - "Vocal", "Jazz+Funk", "Fusion", "Trance", - /*20*/ "Classical", "Instrumental", "Acid", "House", - "Game", "Sound Clip", "Gospel", "Noise", - /*28*/ "AlternRock", "Bass", "Soul", "Punk", - "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", - /*30*/ "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", - "Electronic", "Pop-Folk", "Eurodance", "Dream", - /*38*/ "Southern Rock", "Comedy", "Cult", "Gangsta", - "Top 40", "Christian Rap", "Pop/Funk", "Jungle", - /*40*/ "Native American", "Cabaret", "New Wave", "Psychedelic", - "Rave", "Showtunes", "Trailer", "Lo-Fi", - /*48*/ "Tribal", "Acid Punk", "Acid Jazz", "Polka", - "Retro", "Musical", "Rock & Roll", "Hard Rock", - /*50*/ "Folk", "Folk/Rock", "National folk", "Swing", - "Fast-fusion", "Bebob", "Latin", "Revival", - /*58*/ "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", - "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", - /*60*/ "Big Band", "Chorus", "Easy Listening", "Acoustic", - "Humour", "Speech", "Chanson", "Opera", - /*68*/ "Chamber Music", "Sonata", "Symphony", "Booty Bass", - "Primus", "Porn Groove", "Satire", "Slow Jam", - /*70*/ "Club", "Tango", "Samba", "Folklore", - "Ballad", "Powder Ballad", "Rhythmic Soul", "Freestyle", - /*78*/ "Duet", "Punk Rock", "Drum Solo", "A Capella", - "Euro-House", "Dance Hall", "Goa", "Drum & Bass", - /*80*/ "Club House", "Hardcore", "Terror", "Indie", - "BritPop", "NegerPunk", "Polsk Punk", "Beat", - /*88*/ "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover", - "Contemporary C", "Christian Rock", "Merengue", "Salsa", - /*90*/ "Thrash Metal", "Anime", "JPop", "SynthPop", - "Unknown" + /*00*/ "Blues", "Classic Rock", "Country", "Dance", + "Disco", "Funk", "Grunge", "Hip-Hop", + /*08*/ "Jazz", "Metal", "New Age", "Oldies", + "Other", "Pop", "R&B", "Rap", + /*10*/ "Reggae", "Rock", "Techno", "Industrial", + "Alternative", "Ska", "Death Metal", "Pranks", + /*18*/ "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", + "Vocal", "Jazz+Funk", "Fusion", "Trance", + /*20*/ "Classical", "Instrumental", "Acid", "House", + "Game", "Sound Clip", "Gospel", "Noise", + /*28*/ "AlternRock", "Bass", "Soul", "Punk", + "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", + /*30*/ "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", + "Electronic", "Pop-Folk", "Eurodance", "Dream", + /*38*/ "Southern Rock", "Comedy", "Cult", "Gangsta", + "Top 40", "Christian Rap", "Pop/Funk", "Jungle", + /*40*/ "Native American", "Cabaret", "New Wave", "Psychedelic", + "Rave", "Showtunes", "Trailer", "Lo-Fi", + /*48*/ "Tribal", "Acid Punk", "Acid Jazz", "Polka", + "Retro", "Musical", "Rock & Roll", "Hard Rock", + /*50*/ "Folk", "Folk/Rock", "National folk", "Swing", + "Fast-fusion", "Bebob", "Latin", "Revival", + /*58*/ "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", + "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", + /*60*/ "Big Band", "Chorus", "Easy Listening", "Acoustic", + "Humour", "Speech", "Chanson", "Opera", + /*68*/ "Chamber Music", "Sonata", "Symphony", "Booty Bass", + "Primus", "Porn Groove", "Satire", "Slow Jam", + /*70*/ "Club", "Tango", "Samba", "Folklore", + "Ballad", "Powder Ballad", "Rhythmic Soul", "Freestyle", + /*78*/ "Duet", "Punk Rock", "Drum Solo", "A Capella", + "Euro-House", "Dance Hall", "Goa", "Drum & Bass", + /*80*/ "Club House", "Hardcore", "Terror", "Indie", + "BritPop", "NegerPunk", "Polsk Punk", "Beat", + /*88*/ "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover", + "Contemporary C", "Christian Rock", "Merengue", "Salsa", + /*90*/ "Thrash Metal", "Anime", "JPop", "SynthPop", + "Unknown" }; #define WINAMP_GENRE_UNKNOWN ((sizeof(winamp_genre) / sizeof(winamp_genre[0])) - 1) @@ -104,8 +108,9 @@ char *winamp_genre[] = { */ #include "tagutils-mp3.h" #include "tagutils-aac.h" -#ifdef HAVE_VORBISFILE #include "tagutils-ogg.h" +#ifdef HAVE_OPUS +#include "tagutils-opus.h" #endif #include "tagutils-flc.h" #include "tagutils-asf.h" @@ -123,24 +128,25 @@ static int _get_fileinfo(char *file, struct song_metadata *psong); */ typedef struct { - char* type; - int (*get_tags)(char* file, struct song_metadata* psong); - int (*get_fileinfo)(char* file, struct song_metadata* psong); + char* type; + int (*get_tags)(char* file, struct song_metadata* psong); + int (*get_fileinfo)(char* file, struct song_metadata* psong); } taghandler; static taghandler taghandlers[] = { - { "aac", _get_aactags, _get_aacfileinfo }, - { "mp3", _get_mp3tags, _get_mp3fileinfo }, - { "flc", _get_flctags, _get_flcfileinfo }, -#ifdef HAVE_VORBISFILE - { "ogg", NULL, _get_oggfileinfo }, + { "aac", _get_aactags, _get_aacfileinfo }, + { "mp3", _get_mp3tags, _get_mp3fileinfo }, + { "flc", _get_flctags, _get_flcfileinfo }, + { "ogg", 0, _get_oggfileinfo }, +#ifdef HAVE_OPUS + { "ops", 0, _get_opusfileinfo }, #endif - { "asf", NULL, _get_asffileinfo }, - { "wav", _get_wavtags, _get_wavfileinfo }, - { "pcm", NULL, _get_pcmfileinfo }, - { "dsf", _get_dsftags, _get_dsffileinfo }, - { "dff", NULL, _get_dfffileinfo }, - { NULL, NULL, NULL } + { "asf", 0, _get_asffileinfo }, + { "wav", _get_wavtags, _get_wavfileinfo }, + { "pcm", 0, _get_pcmfileinfo }, + { "dsf", _get_dsftags, _get_dsffileinfo }, + { "dff", 0, _get_dfffileinfo }, + { NULL, NULL, NULL } }; @@ -149,8 +155,9 @@ static taghandler taghandlers[] = { #include "tagutils-misc.c" #include "tagutils-mp3.c" #include "tagutils-aac.c" -#ifdef HAVE_VORBISFILE #include "tagutils-ogg.c" +#ifdef HAVE_OPUS +#include "tagutils-opus.c" #endif #include "tagutils-flc.c" #include "tagutils-asf.c" @@ -166,86 +173,86 @@ static taghandler taghandlers[] = { void freetags(struct song_metadata *psong) { - int role; + int role; - MAYBEFREE(psong->path); - MAYBEFREE(psong->image); - MAYBEFREE(psong->title); - MAYBEFREE(psong->album); - MAYBEFREE(psong->genre); - MAYBEFREE(psong->comment); - for(role = ROLE_START; role <= ROLE_LAST; role++) - { - MAYBEFREE(psong->contributor[role]); - MAYBEFREE(psong->contributor_sort[role]); - } - MAYBEFREE(psong->grouping); - MAYBEFREE(psong->mime); - MAYBEFREE(psong->dlna_pn); - MAYBEFREE(psong->tagversion); - MAYBEFREE(psong->musicbrainz_albumid); - MAYBEFREE(psong->musicbrainz_trackid); - MAYBEFREE(psong->musicbrainz_artistid); - MAYBEFREE(psong->musicbrainz_albumartistid); + MAYBEFREE(psong->path); + MAYBEFREE(psong->image); + MAYBEFREE(psong->title); + MAYBEFREE(psong->album); + MAYBEFREE(psong->genre); + MAYBEFREE(psong->comment); + for(role = ROLE_START; role <= ROLE_LAST; role++) + { + MAYBEFREE(psong->contributor[role]); + MAYBEFREE(psong->contributor_sort[role]); + } + MAYBEFREE(psong->grouping); + MAYBEFREE(psong->mime); + MAYBEFREE(psong->dlna_pn); + MAYBEFREE(psong->tagversion); + MAYBEFREE(psong->musicbrainz_albumid); + MAYBEFREE(psong->musicbrainz_trackid); + MAYBEFREE(psong->musicbrainz_artistid); + MAYBEFREE(psong->musicbrainz_albumartistid); } // _get_fileinfo static int _get_fileinfo(char *file, struct song_metadata *psong) { - taghandler *hdl; + taghandler *hdl; - // dispatch to appropriate tag handler - for(hdl = taghandlers; hdl->type; ++hdl) - if(!strcmp(hdl->type, psong->type)) - break; + // dispatch to appropriate tag handler + for(hdl = taghandlers; hdl->type; ++hdl) + if(!strcmp(hdl->type, psong->type)) + break; - if(hdl->get_fileinfo) - return hdl->get_fileinfo(file, psong); + if(hdl->get_fileinfo) + return hdl->get_fileinfo(file, psong); - return 0; + return 0; } static void _make_composite_tags(struct song_metadata *psong) { - int len; + int len; - len = 1; + len = 1; - if(!psong->contributor[ROLE_ARTIST] && - (psong->contributor[ROLE_BAND] || psong->contributor[ROLE_CONDUCTOR])) - { - if(psong->contributor[ROLE_BAND]) - len += strlen(psong->contributor[ROLE_BAND]); - if(psong->contributor[ROLE_CONDUCTOR]) - len += strlen(psong->contributor[ROLE_CONDUCTOR]); + if(!psong->contributor[ROLE_ARTIST] && + (psong->contributor[ROLE_BAND] || psong->contributor[ROLE_CONDUCTOR])) + { + if(psong->contributor[ROLE_BAND]) + len += strlen(psong->contributor[ROLE_BAND]); + if(psong->contributor[ROLE_CONDUCTOR]) + len += strlen(psong->contributor[ROLE_CONDUCTOR]); - len += 3; + len += 3; - psong->contributor[ROLE_ARTIST] = (char*)calloc(len, 1); - if(psong->contributor[ROLE_ARTIST]) - { - if(psong->contributor[ROLE_BAND]) - strcat(psong->contributor[ROLE_ARTIST], psong->contributor[ROLE_BAND]); + psong->contributor[ROLE_ARTIST] = (char*)calloc(len, 1); + if(psong->contributor[ROLE_ARTIST]) + { + if(psong->contributor[ROLE_BAND]) + strcat(psong->contributor[ROLE_ARTIST], psong->contributor[ROLE_BAND]); - if(psong->contributor[ROLE_BAND] && psong->contributor[ROLE_CONDUCTOR]) - strcat(psong->contributor[ROLE_ARTIST], " - "); + if(psong->contributor[ROLE_BAND] && psong->contributor[ROLE_CONDUCTOR]) + strcat(psong->contributor[ROLE_ARTIST], " - "); - if(psong->contributor[ROLE_CONDUCTOR]) - strcat(psong->contributor[ROLE_ARTIST], psong->contributor[ROLE_CONDUCTOR]); - } - } + if(psong->contributor[ROLE_CONDUCTOR]) + strcat(psong->contributor[ROLE_ARTIST], psong->contributor[ROLE_CONDUCTOR]); + } + } #if 0 // already taken care of by scanner.c - if(!psong->title) - { - char *suffix; - psong->title = strdup(psong->basename); - suffix = strrchr(psong->title, '.'); - if(suffix) *suffix = '\0'; - } + if(!psong->title) + { + char *suffix; + psong->title = strdup(psong->basename); + suffix = strrchr(psong->title, '.'); + if(suffix) *suffix = '\0'; + } #endif } @@ -255,19 +262,19 @@ _make_composite_tags(struct song_metadata *psong) static int _get_tags(char *file, struct song_metadata *psong) { - taghandler *hdl; + taghandler *hdl; - // dispatch - for(hdl = taghandlers ; hdl->type ; ++hdl) - if(!strcasecmp(hdl->type, psong->type)) - break; + // dispatch + for(hdl = taghandlers ; hdl->type ; ++hdl) + if(!strcasecmp(hdl->type, psong->type)) + break; - if(hdl->get_tags) - { - return hdl->get_tags(file, psong); - } + if(hdl->get_tags) + { + return hdl->get_tags(file, psong); + } - return 0; + return 0; } /*****************************************************************************/ @@ -275,31 +282,31 @@ _get_tags(char *file, struct song_metadata *psong) int readtags(char *path, struct song_metadata *psong, struct stat *stat, char *lang, char *type) { - char *fname; + char *fname; - if(lang_index == -1) - lang_index = _lang2cp(lang); + if(lang_index == -1) + lang_index = _lang2cp(lang); - memset((void*)psong, 0, sizeof(struct song_metadata)); - psong->path = strdup(path); - psong->type = type; + memset((void*)psong, 0, sizeof(struct song_metadata)); + psong->path = strdup(path); + psong->type = type; - fname = strrchr(psong->path, '/'); - psong->basename = fname ? fname + 1 : psong->path; + fname = strrchr(psong->path, '/'); + psong->basename = fname ? fname + 1 : psong->path; - if(stat) - { - if(!psong->time_modified) - psong->time_modified = stat->st_mtime; - psong->file_size = stat->st_size; - } + if(stat) + { + if(!psong->time_modified) + psong->time_modified = stat->st_mtime; + psong->file_size = stat->st_size; + } - // get tag - if( _get_tags(path, psong) == 0 ) - { - _make_composite_tags(psong); - } - - // get fileinfo - return _get_fileinfo(path, psong); + // get tag + if( _get_tags(path, psong) == 0 ) + { + _make_composite_tags(psong); + } + + // get fileinfo + return _get_fileinfo(path, psong); } diff --git a/utils.c b/utils.c index 03ba850..f6cc6be 100644 --- a/utils.c +++ b/utils.c @@ -38,503 +38,517 @@ int xasprintf(char **strp, char *fmt, ...) { - va_list args; - int ret; + va_list args; + int ret; - va_start(args, fmt); - ret = vasprintf(strp, fmt, args); - va_end(args); - if( ret < 0 ) - { - DPRINTF(E_WARN, L_GENERAL, "xasprintf: allocation failed\n"); - *strp = NULL; - } - return ret; + va_start(args, fmt); + ret = vasprintf(strp, fmt, args); + va_end(args); + if( ret < 0 ) + { + DPRINTF(E_WARN, L_GENERAL, "xasprintf: allocation failed\n"); + *strp = NULL; + } + return ret; } int ends_with(const char * haystack, const char * needle) { - const char * end; - int nlen = strlen(needle); - int hlen = strlen(haystack); + const char * end; + int nlen = strlen(needle); + int hlen = strlen(haystack); - if( nlen > hlen ) - return 0; - end = haystack + hlen - nlen; + if( nlen > hlen ) + return 0; + end = haystack + hlen - nlen; - return (strcasecmp(end, needle) ? 0 : 1); + return (strcasecmp(end, needle) ? 0 : 1); } char * trim(char *str) { - int i; - int len; + int i; + int len; - if (!str) - return(NULL); + if (!str) + return(NULL); - len = strlen(str); - for (i=len-1; i >= 0 && isspace(str[i]); i--) - { - str[i] = '\0'; - len--; - } - while (isspace(*str)) - { - str++; - len--; - } + len = strlen(str); + for (i=len-1; i >= 0 && isspace(str[i]); i--) + { + str[i] = '\0'; + len--; + } + while (isspace(*str)) + { + str++; + len--; + } - if (str[0] == '"' && str[len-1] == '"') - { - str[0] = '\0'; - str[len-1] = '\0'; - str++; - } + if (str[0] == '"' && str[len-1] == '"') + { + str[0] = '\0'; + str[len-1] = '\0'; + str++; + } - return str; + return str; } /* Find the first occurrence of p in s, where s is terminated by t */ char * strstrc(const char *s, const char *p, const char t) { - char *endptr; - size_t slen, plen; + char *endptr; + size_t slen, plen; - endptr = strchr(s, t); - if (!endptr) - return strstr(s, p); + endptr = strchr(s, t); + if (!endptr) + return strstr(s, p); - plen = strlen(p); - slen = endptr - s; - while (slen >= plen) - { - if (*s == *p && strncmp(s+1, p+1, plen-1) == 0) - return (char*)s; - s++; - slen--; - } + plen = strlen(p); + slen = endptr - s; + while (slen >= plen) + { + if (*s == *p && strncmp(s+1, p+1, plen-1) == 0) + return (char*)s; + s++; + slen--; + } - return NULL; + return NULL; } char * strcasestrc(const char *s, const char *p, const char t) { - char *endptr; - size_t slen, plen; + char *endptr; + size_t slen, plen; - endptr = strchr(s, t); - if (!endptr) - return strcasestr(s, p); + endptr = strchr(s, t); + if (!endptr) + return strcasestr(s, p); - plen = strlen(p); - slen = endptr - s; - while (slen >= plen) - { - if (*s == *p && strncasecmp(s+1, p+1, plen-1) == 0) - return (char*)s; - s++; - slen--; - } + plen = strlen(p); + slen = endptr - s; + while (slen >= plen) + { + if (*s == *p && strncasecmp(s+1, p+1, plen-1) == 0) + return (char*)s; + s++; + slen--; + } - return NULL; + return NULL; } char * modifyString(char *string, const char *before, const char *after, int noalloc) { - int oldlen, newlen, chgcnt = 0; - char *s, *p; + int oldlen, newlen, chgcnt = 0; + char *s, *p; - /* If there is no match, just return */ - s = strstr(string, before); - if (!s) - return string; + /* If there is no match, just return */ + s = strstr(string, before); + if (!s) + return string; - oldlen = strlen(before); - newlen = strlen(after); - if (newlen > oldlen) - { - if (noalloc) - return string; + oldlen = strlen(before); + newlen = strlen(after); + if (newlen > oldlen) + { + if (noalloc) + return string; - while ((p = strstr(s, before))) - { - chgcnt++; - s = p + oldlen; - } - s = realloc(string, strlen(string)+((newlen-oldlen)*chgcnt)+1); - /* If we failed to realloc, return the original alloc'd string */ - if( s ) - string = s; - else - return string; - } + while ((p = strstr(s, before))) + { + chgcnt++; + s = p + oldlen; + } + s = realloc(string, strlen(string)+((newlen-oldlen)*chgcnt)+1); + /* If we failed to realloc, return the original alloc'd string */ + if( s ) + string = s; + else + return string; + } - s = string; - while (s) - { - p = strstr(s, before); - if (!p) - return string; - memmove(p + newlen, p + oldlen, strlen(p + oldlen) + 1); - memcpy(p, after, newlen); - s = p + newlen; - } + s = string; + while (s) + { + p = strstr(s, before); + if (!p) + return string; + memmove(p + newlen, p + oldlen, strlen(p + oldlen) + 1); + memcpy(p, after, newlen); + s = p + newlen; + } - return string; + return string; } char * unescape_tag(const char *tag, int force_alloc) { - char *esc_tag = NULL; + char *esc_tag = NULL; - if (strchr(tag, '&') && - (strstr(tag, "&") || strstr(tag, "<") || strstr(tag, ">") || - strstr(tag, """) || strstr(tag, "'"))) - { - esc_tag = strdup(tag); - esc_tag = modifyString(esc_tag, "&", "&", 1); - esc_tag = modifyString(esc_tag, "<", "<", 1); - esc_tag = modifyString(esc_tag, ">", ">", 1); - esc_tag = modifyString(esc_tag, """, "\"", 1); - esc_tag = modifyString(esc_tag, "'", "'", 1); - } - else if( force_alloc ) - esc_tag = strdup(tag); + if (strchr(tag, '&') && + (strstr(tag, "&") || strstr(tag, "<") || strstr(tag, ">") || + strstr(tag, """) || strstr(tag, "'"))) + { + esc_tag = strdup(tag); + esc_tag = modifyString(esc_tag, "&", "&", 1); + esc_tag = modifyString(esc_tag, "<", "<", 1); + esc_tag = modifyString(esc_tag, ">", ">", 1); + esc_tag = modifyString(esc_tag, """, "\"", 1); + esc_tag = modifyString(esc_tag, "'", "'", 1); + } + else if( force_alloc ) + esc_tag = strdup(tag); - return esc_tag; + return esc_tag; } char * escape_tag(const char *tag, int force_alloc) { - char *esc_tag = NULL; + char *esc_tag = NULL; - if( strchr(tag, '&') || strchr(tag, '<') || strchr(tag, '>') || strchr(tag, '"') ) - { - esc_tag = strdup(tag); - esc_tag = modifyString(esc_tag, "&", "&amp;", 0); - esc_tag = modifyString(esc_tag, "<", "&lt;", 0); - esc_tag = modifyString(esc_tag, ">", "&gt;", 0); - esc_tag = modifyString(esc_tag, "\"", "&quot;", 0); - } - else if( force_alloc ) - esc_tag = strdup(tag); + if( strchr(tag, '&') || strchr(tag, '<') || strchr(tag, '>') || strchr(tag, '"') ) + { + esc_tag = strdup(tag); + esc_tag = modifyString(esc_tag, "&", "&amp;", 0); + esc_tag = modifyString(esc_tag, "<", "&lt;", 0); + esc_tag = modifyString(esc_tag, ">", "&gt;", 0); + esc_tag = modifyString(esc_tag, "\"", "&quot;", 0); + } + else if( force_alloc ) + esc_tag = strdup(tag); - return esc_tag; + return esc_tag; } char * duration_str(int msec) { - char *str; + char *str; - xasprintf(&str, "%d:%02d:%02d.%03d", - (msec / 3600000), - (msec / 60000 % 60), - (msec / 1000 % 60), - (msec % 1000)); + xasprintf(&str, "%d:%02d:%02d.%03d", + (msec / 3600000), + (msec / 60000 % 60), + (msec / 1000 % 60), + (msec % 1000)); - return str; + return str; } char * strip_ext(char *name) { - char *period; + char *period; - if (!name) - return NULL; - period = strrchr(name, '.'); - if (period) - *period = '\0'; + if (!name) + return NULL; + period = strrchr(name, '.'); + if (period) + *period = '\0'; - return period; + return period; } /* Code basically stolen from busybox */ int make_dir(char * path, mode_t mode) { - char * s = path; - char c; - struct stat st; + char * s = path; + char c; + struct stat st; - do { - c = '\0'; + do { + c = '\0'; - /* Before we do anything, skip leading /'s, so we don't bother - * trying to create /. */ - while (*s == '/') - ++s; + /* Before we do anything, skip leading /'s, so we don't bother + * trying to create /. */ + while (*s == '/') + ++s; - /* Bypass leading non-'/'s and then subsequent '/'s. */ - while (*s) { - if (*s == '/') { - do { - ++s; - } while (*s == '/'); - c = *s; /* Save the current char */ - *s = '\0'; /* and replace it with nul. */ - break; - } - ++s; - } + /* Bypass leading non-'/'s and then subsequent '/'s. */ + while (*s) { + if (*s == '/') { + do { + ++s; + } while (*s == '/'); + c = *s; /* Save the current char */ + *s = '\0'; /* and replace it with nul. */ + break; + } + ++s; + } - if (mkdir(path, mode) < 0) { - /* If we failed for any other reason than the directory - * already exists, output a diagnostic and return -1.*/ - if ((errno != EEXIST && errno != EISDIR) - || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) { - DPRINTF(E_WARN, L_GENERAL, "make_dir: cannot create directory '%s'\n", path); - if (c) - *s = c; - return -1; - } - } - if (!c) - return 0; + if (mkdir(path, mode) < 0) { + /* If we failed for any other reason than the directory + * already exists, output a diagnostic and return -1.*/ + if ((errno != EEXIST && errno != EISDIR) + || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) { + DPRINTF(E_WARN, L_GENERAL, "make_dir: cannot create directory '%s'\n", path); + if (c) + *s = c; + return -1; + } + } + if (!c) + return 0; - /* Remove any inserted nul from the path. */ - *s = c; + /* Remove any inserted nul from the path. */ + *s = c; - } while (1); + } while (1); } /* Simple, efficient hash function from Daniel J. Bernstein */ unsigned int DJBHash(uint8_t *data, int len) { - unsigned int hash = 5381; - unsigned int i = 0; + unsigned int hash = 5381; + unsigned int i = 0; - for(i = 0; i < len; data++, i++) - { - hash = ((hash << 5) + hash) + (*data); - } + for(i = 0; i < len; data++, i++) + { + hash = ((hash << 5) + hash) + (*data); + } - return hash; + return hash; } const char * mime_to_ext(const char * mime) { - switch( *mime ) - { - /* Audio extensions */ - case 'a': - if( strcmp(mime+6, "mpeg") == 0 ) - return "mp3"; - else if( strcmp(mime+6, "mp4") == 0 ) - return "m4a"; - else if( strcmp(mime+6, "x-ms-wma") == 0 ) - return "wma"; - else if( strcmp(mime+6, "x-flac") == 0 ) - return "flac"; - else if( strcmp(mime+6, "flac") == 0 ) - return "flac"; - else if( strcmp(mime+6, "x-wav") == 0 ) - return "wav"; - else if( strncmp(mime+6, "L16", 3) == 0 ) - return "pcm"; - else if( strcmp(mime+6, "3gpp") == 0 ) - return "3gp"; - else if( strcmp(mime, "application/ogg") == 0 ) - return "ogg"; - else if( strcmp(mime+6, "x-dsd") == 0 ) - return "dsd"; - break; - case 'v': - if( strcmp(mime+6, "avi") == 0 ) - return "avi"; - else if( strcmp(mime+6, "divx") == 0 ) - return "avi"; - else if( strcmp(mime+6, "x-msvideo") == 0 ) - return "avi"; - else if( strcmp(mime+6, "mpeg") == 0 ) - return "mpg"; - else if( strcmp(mime+6, "mp4") == 0 ) - return "mp4"; - else if( strcmp(mime+6, "x-ms-wmv") == 0 ) - return "wmv"; - else if( strcmp(mime+6, "x-matroska") == 0 ) - return "mkv"; - else if( strcmp(mime+6, "x-mkv") == 0 ) - return "mkv"; - else if( strcmp(mime+6, "x-flv") == 0 ) - return "flv"; - else if( strcmp(mime+6, "vnd.dlna.mpeg-tts") == 0 ) - return "mpg"; - else if( strcmp(mime+6, "quicktime") == 0 ) - return "mov"; - else if( strcmp(mime+6, "3gpp") == 0 ) - return "3gp"; - else if( strncmp(mime+6, "x-tivo-mpeg", 11) == 0 ) - return "TiVo"; - break; - case 'i': - if( strcmp(mime+6, "jpeg") == 0 ) - return "jpg"; - else if( strcmp(mime+6, "png") == 0 ) - return "png"; - break; - default: - break; - } - return "dat"; + switch( *mime ) + { + /* Audio extensions */ + case 'a': + if( strcmp(mime+6, "mpeg") == 0 ) + return "mp3"; + else if( strcmp(mime+6, "mp4") == 0 ) + return "m4a"; + else if( strcmp(mime+6, "x-ms-wma") == 0 ) + return "wma"; + else if( strcmp(mime+6, "x-flac") == 0 ) + return "flac"; + else if( strcmp(mime+6, "flac") == 0 ) + return "flac"; + else if( strcmp(mime+6, "x-wav") == 0 ) + return "wav"; + else if( strncmp(mime+6, "L16", 3) == 0 ) + return "pcm"; + else if( strcmp(mime+6, "3gpp") == 0 ) + return "3gp"; + else if( strncmp(mime+6, "ogg", 3) == 0 ) + { + if( strstr(mime+9, "opus" ) != (char *)NULL ) + return "opus"; + else if( strstr (mime+9, "vorbis" ) != (char *)NULL ) + return "ogg"; + + return "oga"; + } + else if( strcmp(mime+6, "x-dsd") == 0 ) + return "dsd"; + break; + case 'v': + if( strcmp(mime+6, "avi") == 0 ) + return "avi"; + else if( strcmp(mime+6, "divx") == 0 ) + return "avi"; + else if( strcmp(mime+6, "x-msvideo") == 0 ) + return "avi"; + else if( strcmp(mime+6, "mpeg") == 0 ) + return "mpg"; + else if( strcmp(mime+6, "mp4") == 0 ) + return "mp4"; + else if( strcmp(mime+6, "x-ms-wmv") == 0 ) + return "wmv"; + else if( strcmp(mime+6, "x-matroska") == 0 ) + return "mkv"; + else if( strcmp(mime+6, "x-mkv") == 0 ) + return "mkv"; + else if( strcmp(mime+6, "x-flv") == 0 ) + return "flv"; + else if( strcmp(mime+6, "vnd.dlna.mpeg-tts") == 0 ) + return "mpg"; + else if( strcmp(mime+6, "quicktime") == 0 ) + return "mov"; + else if( strcmp(mime+6, "3gpp") == 0 ) + return "3gp"; + else if( strncmp(mime+6, "x-tivo-mpeg", 11) == 0 ) + return "TiVo"; + else if ( strcmp(mime+6, "ogg") == 0 ) + return "ogv"; + break; + case 'i': + if( strcmp(mime+6, "jpeg") == 0 ) + return "jpg"; + else if( strcmp(mime+6, "png") == 0 ) + return "png"; + break; + default: + break; + } + return "dat"; } int is_video(const char * file) { - return (ends_with(file, ".mpg") || ends_with(file, ".mpeg") || - ends_with(file, ".avi") || ends_with(file, ".divx") || - ends_with(file, ".asf") || ends_with(file, ".wmv") || - ends_with(file, ".mp4") || ends_with(file, ".m4v") || - ends_with(file, ".mts") || ends_with(file, ".m2ts") || - ends_with(file, ".m2t") || ends_with(file, ".mkv") || - ends_with(file, ".vob") || ends_with(file, ".ts") || - ends_with(file, ".flv") || ends_with(file, ".xvid") || + return (ends_with(file, ".mpg") || ends_with(file, ".mpeg") || + ends_with(file, ".avi") || ends_with(file, ".divx") || + ends_with(file, ".asf") || ends_with(file, ".wmv") || + ends_with(file, ".mp4") || ends_with(file, ".m4v") || + ends_with(file, ".mts") || ends_with(file, ".m2ts") || + ends_with(file, ".m2t") || ends_with(file, ".mkv") || + ends_with(file, ".vob") || ends_with(file, ".ts") || + ends_with(file, ".flv") || ends_with(file, ".xvid") || + ends_with(file, ".ogv") || #ifdef TIVO_SUPPORT - ends_with(file, ".TiVo") || + ends_with(file, ".TiVo") || #endif - ends_with(file, ".mov") || ends_with(file, ".3gp") || - ends_with(file, ".rm") || ends_with(file, ".rmvb") || - ends_with(file, ".webm")); + ends_with(file, ".mov") || ends_with(file, ".3gp") || + ends_with(file, ".rm") || ends_with(file, ".rmvb") || + ends_with(file, ".webm")); } int is_audio(const char * file) { - return (ends_with(file, ".mp3") || ends_with(file, ".flac") || - ends_with(file, ".wma") || ends_with(file, ".asf") || - ends_with(file, ".fla") || ends_with(file, ".flc") || - ends_with(file, ".m4a") || ends_with(file, ".aac") || - ends_with(file, ".mp4") || ends_with(file, ".m4p") || - ends_with(file, ".wav") || ends_with(file, ".ogg") || - ends_with(file, ".pcm") || ends_with(file, ".3gp") || - ends_with(file, ".dsf") || ends_with(file, ".dff")); + return (ends_with(file, ".mp3") || ends_with(file, ".flac") || + ends_with(file, ".wma") || ends_with(file, ".asf") || + ends_with(file, ".fla") || ends_with(file, ".flc") || + ends_with(file, ".m4a") || ends_with(file, ".aac") || + ends_with(file, ".mp4") || ends_with(file, ".m4p") || + ends_with(file, ".wav") || ends_with(file, ".ogg") || + ends_with(file, ".oga") || +#ifdef HAVE_OPUS + ends_with(file, ".opus") || +#endif + ends_with(file, ".pcm") || ends_with(file, ".3gp") || + ends_with(file, ".dsf") || ends_with(file, ".dff")); } int is_image(const char * file) { - return (ends_with(file, ".jpg") || ends_with(file, ".jpeg")); + return (ends_with(file, ".jpg") || ends_with(file, ".jpeg")); } int is_playlist(const char * file) { - return (ends_with(file, ".m3u") || ends_with(file, ".pls")); + return (ends_with(file, ".m3u") || ends_with(file, ".pls")); } int is_caption(const char * file) { - return (ends_with(file, ".srt") || ends_with(file, ".smi")); + return (ends_with(file, ".srt") || ends_with(file, ".smi")); } media_types get_media_type(const char *file) { - const char *ext = strrchr(file, '.'); - if (!ext) - return NO_MEDIA; - if (is_image(ext)) - return TYPE_IMAGE; - if (is_video(ext)) - return TYPE_VIDEO; - if (is_audio(ext)) - return TYPE_AUDIO; - if (is_playlist(ext)) - return TYPE_PLAYLIST; - if (is_caption(ext)) - return TYPE_CAPTION; - if (is_nfo(ext)) - return TYPE_NFO; - return NO_MEDIA; + const char *ext = strrchr(file, '.'); + if (!ext) + return NO_MEDIA; + if (is_image(ext)) + return TYPE_IMAGE; + if (is_video(ext)) + return TYPE_VIDEO; + if (is_audio(ext)) + return TYPE_AUDIO; + if (is_playlist(ext)) + return TYPE_PLAYLIST; + if (is_caption(ext)) + return TYPE_CAPTION; + if (is_nfo(ext)) + return TYPE_NFO; + return NO_MEDIA; } int is_album_art(const char * name) { - struct album_art_name_s * album_art_name; + struct album_art_name_s * album_art_name; - /* Check if this file name matches one of the default album art names */ - for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next ) - { - if( album_art_name->wildcard ) - { - if( strncmp(album_art_name->name, name, strlen(album_art_name->name)) == 0 ) - break; - } - else - { - if( strcmp(album_art_name->name, name) == 0 ) - break; - } - } + /* Check if this file name matches one of the default album art names */ + for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next ) + { + if( album_art_name->wildcard ) + { + if( strncmp(album_art_name->name, name, strlen(album_art_name->name)) == 0 ) + break; + } + else + { + if( strcmp(album_art_name->name, name) == 0 ) + break; + } + } - return (album_art_name ? 1 : 0); + return (album_art_name ? 1 : 0); } int resolve_unknown_type(const char * path, media_types dir_type) { - struct stat entry; - enum file_types type = TYPE_UNKNOWN; - char str_buf[PATH_MAX]; - ssize_t len; + struct stat entry; + enum file_types type = TYPE_UNKNOWN; + char str_buf[PATH_MAX]; + ssize_t len; - if( lstat(path, &entry) == 0 ) - { - if( S_ISLNK(entry.st_mode) ) - { - if( (len = readlink(path, str_buf, PATH_MAX-1)) > 0 ) - { - str_buf[len] = '\0'; - //DEBUG DPRINTF(E_DEBUG, L_GENERAL, "Checking for recursive symbolic link: %s (%s)\n", path, str_buf); - if( strncmp(path, str_buf, strlen(str_buf)) == 0 ) - { - DPRINTF(E_DEBUG, L_GENERAL, "Ignoring recursive symbolic link: %s (%s)\n", path, str_buf); - return type; - } - } - stat(path, &entry); - } + if( lstat(path, &entry) == 0 ) + { + if( S_ISLNK(entry.st_mode) ) + { + if( (len = readlink(path, str_buf, PATH_MAX-1)) > 0 ) + { + str_buf[len] = '\0'; + //DEBUG DPRINTF(E_DEBUG, L_GENERAL, "Checking for recursive symbolic link: %s (%s)\n", path, str_buf); + if( strncmp(path, str_buf, strlen(str_buf)) == 0 ) + { + DPRINTF(E_DEBUG, L_GENERAL, "Ignoring recursive symbolic link: %s (%s)\n", path, str_buf); + return type; + } + } + stat(path, &entry); + } - if( S_ISDIR(entry.st_mode) ) - { - type = TYPE_DIR; - } - else if( S_ISREG(entry.st_mode) ) - { - media_types mtype = get_media_type(path); - if (dir_type & mtype) - type = TYPE_FILE; - } - } - return type; + if( S_ISDIR(entry.st_mode) ) + { + type = TYPE_DIR; + } + else if( S_ISREG(entry.st_mode) ) + { + media_types mtype = get_media_type(path); + if (dir_type & mtype) + type = TYPE_FILE; + } + } + return type; } media_types valid_media_types(const char *path) { - struct media_dir_s *media_dir; + struct media_dir_s *media_dir; - for (media_dir = media_dirs; media_dir; media_dir = media_dir->next) - { - if (strncmp(path, media_dir->path, strlen(media_dir->path)) == 0) - return media_dir->types; - } + for (media_dir = media_dirs; media_dir; media_dir = media_dir->next) + { + if (strncmp(path, media_dir->path, strlen(media_dir->path)) == 0) + return media_dir->types; + } - return ALL_MEDIA; + return ALL_MEDIA; } /* @@ -544,35 +558,35 @@ valid_media_types(const char *path) * it just gets very confused in this case. * Caveat emptor. */ -static void timevalfix(struct timeval *); +static void timevalfix(struct timeval *); void timevaladd(struct timeval *t1, const struct timeval *t2) { - t1->tv_sec += t2->tv_sec; - t1->tv_usec += t2->tv_usec; - timevalfix(t1); + t1->tv_sec += t2->tv_sec; + t1->tv_usec += t2->tv_usec; + timevalfix(t1); } void timevalsub(struct timeval *t1, const struct timeval *t2) { - t1->tv_sec -= t2->tv_sec; - t1->tv_usec -= t2->tv_usec; - timevalfix(t1); + t1->tv_sec -= t2->tv_sec; + t1->tv_usec -= t2->tv_usec; + timevalfix(t1); } static void timevalfix(struct timeval *t1) { - if (t1->tv_usec < 0) { - t1->tv_sec--; - t1->tv_usec += 1000000; - } - if (t1->tv_usec >= 1000000) { - t1->tv_sec++; - t1->tv_usec -= 1000000; - } + if (t1->tv_usec < 0) { + t1->tv_sec--; + t1->tv_usec += 1000000; + } + if (t1->tv_usec >= 1000000) { + t1->tv_sec++; + t1->tv_usec -= 1000000; + } }