bug-coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

new feature patch: ls -l3 (MP3 ID3 tag) (fwd)


From: Daniel Reed
Subject: new feature patch: ls -l3 (MP3 ID3 tag) (fwd)
Date: Sun, 7 Dec 2003 23:00:32 -0500 (EST)

I am including my original message (with a new patch) as my original post
seems to have disappeared.

I already found one problem in my first patch. gobble_file() has a variable
`path' which remains in scope for the entire function, but only has a
meaningful value within an if { }. I would have originally proposed `path'
be moved into the if { }, but since I use the value of `path' later, I moved
the alloca() outside of the if { } so I can use its value at the end of the
function.

-- 
Daniel Reed <address@hidden>    http://naim-users.org/nmlorg/   
http://naim.n.ml.org/
"If you place a small value on yourself, rest assured, the world will
not raise your price."

---------- Forwarded message ----------
Date: Sun, 7 Dec 2003 21:23:00 -0500 (EST)
From: Daniel Reed <address@hidden>
To: address@hidden
Subject: new feature patch: ls -l3 (MP3 ID3 tag)

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 <address@hidden>    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 22:13:55 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);
      }
***************
*** 2324,2329 ****
--- 2351,2364 ----
        files = XREALLOC (files, struct fileinfo, nfiles);
      }

+   if (name[0] == '/' || dirname[0] == 0)
+     path = (char *) name;
+   else
+     {
+       path = (char *) alloca (strlen (name) + strlen (dirname) + 2);
+       attach (path, dirname, name);
+     }
+
    files[files_index].linkname = 0;
    files[files_index].linkmode = 0;
    files[files_index].linkok = 0;
***************
*** 2352,2365 ****
        /* `path' is the absolute pathname of this file. */
        int err;

-       if (name[0] == '/' || dirname[0] == 0)
-       path = (char *) name;
-       else
-       {
-         path = (char *) alloca (strlen (name) + strlen (dirname) + 2);
-         attach (path, dirname, name);
-       }
-
        switch (dereference)
        {
        case DEREF_ALWAYS:
--- 2387,2392 ----
***************
*** 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 <address@hidden> */
+ #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\




reply via email to

[Prev in Thread] Current Thread [Next in Thread]