This is something I wrote today to help manage large MP3 collections from the console.
The patch adds --enable-id3 to ./configure, and, when enabled, adds a -3 and an --id3 option to ls. These options cause ls to open() all regular files, seek to the end, and check for an ID3v1 tag. ID3v1 tags are always at a fixed offset from the end of the file. If present, ls will read the rest of the tag before closing the file, and will display the artist and track title after the filename. Information on MP3 ID3 tags is available from http://www.id3.org/id3v1.html . I added a "path" attribute to struct fileinfo that duplicates the full file path; added the -3 and --id3 options to the getopt code; and added code to the end of print_long_format(). The format of the output is ' == ARTIST "TITLE"' after the file name, where ' ARTIST' and/or ' "TITLE"' can be left out if the field is empty in the ID3 tag. The tag reading code does pull/parse the entire tag, including the ID3v1.1 track number, so it would be trivial to adjust the hard-coded format, and should be easy to create an additional argument [or environment variable] to allow the user to adjust the output format at runtime. This is primarily for my personal use, but I am submitting the patch in case there is any general interest. I was unaware of the existence of any version of coreutils past 5.0 when I wrote this; if there is any interest I will look into porting it to 5.0.91/CVS. Thanks, -- Daniel Reed <[EMAIL PROTECTED]> http://naim-users.org/nmlorg/ http://naim.n.ml.org/ Never be afraid to try something new. Remember: Amateurs built the ark. Professionals built the Titanic. *** coreutils-5.0,original/configure.ac Sun Mar 30 07:11:59 2003 --- coreutils-5.0/configure.ac Sun Dec 7 17:19:15 2003 *************** *** 257,262 **** --- 257,269 ---- AM_GNU_GETTEXT([external], [need-ngettext]) AM_GNU_GETTEXT_VERSION(0.11.5) + AC_ARG_ENABLE(id3, + [ --enable-id3 enable 'ls -3' to view MP3 ID3 tags [[default=no]]], + [ + AC_DEFINE(ENABLE_ID3, 1, [Enable 'ls -3' to view MP3 ID3 tags]) + ] + ) + AC_CONFIG_FILES( Makefile doc/Makefile *** coreutils-5.0,original/src/ls.c Wed Mar 19 18:01:51 2003 --- coreutils-5.0/src/ls.c Sun Dec 7 20:04:22 2003 *************** *** 63,68 **** --- 63,69 ---- #include <pwd.h> #include <getopt.h> #include <signal.h> + #include <string.h> /* Get MB_CUR_MAX. */ #if HAVE_STDLIB_H *************** *** 208,213 **** --- 209,218 ---- /* The file name. */ char *name; + #ifdef ENABLE_ID3 + char *path; + #endif + struct stat stat; /* For symbolic link, name of the file linked to, otherwise zero. */ *************** *** 439,444 **** --- 444,456 ---- static int sort_reverse; + #ifdef ENABLE_ID3 + /* Nonzero means to open all .[mM][pP]3 files, read the last 128 characters + and display an MP3 ID3v1.1 tag if found. -3 or --id3 enable this. */ + + static int print_id3 = 0; + #endif + /* Nonzero means to display owner information. -g turns this off. */ static int print_owner = 1; *************** *** 744,749 **** --- 756,764 ---- {"color", optional_argument, 0, COLOR_OPTION}, {"block-size", required_argument, 0, BLOCK_SIZE_OPTION}, {"author", no_argument, 0, AUTHOR_OPTION}, + #ifdef ENABLE_ID3 + {"id3", no_argument, 0, '3'}, + #endif {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, {NULL, 0, NULL, 0} *************** *** 1369,1374 **** --- 1384,1392 ---- } while ((c = getopt_long (argc, argv, + #ifdef ENABLE_ID3 + "3" + #endif "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UX1", long_options, NULL)) != -1) { *************** *** 1575,1580 **** --- 1593,1604 ---- format = one_per_line; break; + #ifdef ENABLE_ID3 + case '3': + print_id3 = true; + break; + #endif + case AUTHOR_OPTION: print_author = true; break; *************** *** 2299,2304 **** --- 2323,2331 ---- for (i = 0; i < files_index; i++) { free (files[i].name); + #ifdef ENABLE_ID3 + free (files[i].path); + #endif if (files[i].linkname) free (files[i].linkname); } *************** *** 2474,2479 **** --- 2501,2509 ---- } files[files_index].name = xstrdup (name); + #ifdef ENABLE_ID3 + files[files_index].path = xstrdup (path); + #endif files_index++; return blocks; *************** *** 2911,2916 **** --- 2941,3019 ---- return strlen (buffer); } + /* The following code is Copyright 1998-2003 Daniel Reed <[EMAIL PROTECTED]> */ + #ifdef ENABLE_ID3 + typedef struct { + unsigned char title[31], + artist[31], + album[31], + year[5], + comment[31], + track, + genre; + } id3_t; + + static int id3_readtag(const char *const filename, id3_t *tag, size_t taglen) { + char buf[128-3]; + FILE *mp3file; + int i; + + if (taglen < sizeof(*tag)) { + errno = EMSGSIZE; + return(-1); + } + + if ((mp3file = fopen(filename, "r")) == NULL) + return(-1); + + if (fseek(mp3file, -128, SEEK_END) != 0) { + i = errno; + fclose(mp3file); + errno = i; + return(-1); + } + + if ((fgetc(mp3file) != 'T') || (fgetc(mp3file) != 'A') || + (fgetc(mp3file) != 'G')) { + fclose(mp3file); + errno = ENOMSG; + return(-1); + } + + /* fseek(mp3file, -125, SEEK_END); */ + fread(buf, sizeof(char), 125, mp3file); + memset(tag, 0, sizeof(*tag)); + + strncpy(tag->title, buf, 30); + for (i = strlen(tag->title); (i > 0) && isspace(tag->title[i-1]); i--) + tag->title[i-1] = 0; + + strncpy(tag->artist, buf+30, 30); + for (i = strlen(tag->artist); (i > 0) && isspace(tag->artist[i-1]); i--) + tag->artist[i-1] = 0; + + strncpy(tag->album, buf+60, 30); + for (i = strlen(tag->album); (i > 0) && isspace(tag->album[i-1]); i--) + tag->album[i-1] = 0; + + strncpy(tag->year, buf+90, 4); + for (i = strlen(tag->year); (i > 0) && isspace(tag->year[i-1]); i--) + tag->year[i-1] = 0; + + strncpy(tag->comment, buf+94, 30); + for (i = strlen(tag->comment); (i > 0) && isspace(tag->comment[i-1]); i--) + tag->comment[i-1] = 0; + + if ((tag->comment[28] == 0) && (tag->comment[29] != 0)) + tag->track = tag->comment[29]; + + tag->genre = buf[124]; + + fclose(mp3file); + return(0); + } + #endif + /* Print information about F in long format. */ static void *************** *** 3100,3105 **** --- 3203,3227 ---- } else if (indicator_style != none) print_type_indicator (f->stat.st_mode); + + #ifdef ENABLE_ID3 + if (print_id3 && (f->filetype == normal)) { + id3_t id3; + + if (id3_readtag(f->path, &id3, sizeof(id3)) == 0) { + DIRED_FPUTS_LITERAL(" ==", stdout); + if (*id3.artist != 0) { + DIRED_FPUTS_LITERAL(" ", stdout); + DIRED_FPUTS(id3.artist, stdout, strlen(id3.artist)); + } + if (*id3.title != 0) { + DIRED_FPUTS_LITERAL(" \"", stdout); + DIRED_FPUTS(id3.title, stdout, strlen(id3.title)); + DIRED_FPUTS_LITERAL("\"", stdout); + } + } + } + #endif } /* Output to OUT a quoted representation of the file name NAME, *************** *** 3874,3879 **** --- 3996,4006 ---- -X sort alphabetically by entry extension\n\ -1 list one file per line\n\ "), stdout); + #ifdef ENABLE_ID3 + fputs (_("\ + -3, --id3 display MP3 ID3v1.1 tags\n\ + "), stdout); + #endif fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); fputs (_("\n\ _______________________________________________ Bug-coreutils mailing list [EMAIL PROTECTED] http://mail.gnu.org/mailman/listinfo/bug-coreutils
