yet another cddb patch

Thomas HERAULT <[email protected]> Tue, 19 Nov 2002 18:51:49 +0100
Newsgroups gmane.comp.audio.cd-paranoia.devel
Organization Laboratoire de Recherche en Informatique (parallelisme)
Message-ID <[email protected]>
Hello,

I'm an over-satisfied user of cdparanoia, and I've made a patch to
fetch track titles from freedb databases. I know this will not be
included in the release of cdparanoia, since it is not its goal, but
if someone need it, it applies against cdparanoia-III-alpha-9.8 
(couldn't find cdparanoia-IV to test).

Cheers, /T
cdparanoia-III-cddb.patch (application/octet-stream, 45.6 KB)
diff -urN cdparanoia-III-alpha9.8/Makefile.in cdparanoia-III-alpha9.8-cddb/Makefile.in
--- cdparanoia-III-alpha9.8/Makefile.in	Wed Mar 28 00:46:58 2001
+++ cdparanoia-III-alpha9.8-cddb/Makefile.in	Wed Sep 12 13:28:32 2001
@@ -25,7 +25,7 @@
 LIBDIR=@libdir@
 PWD = $(shell pwd)
 
-OFILES = main.o report.o header.o buffering_write.o
+OFILES = main.o report.o header.o buffering_write.o cddb.o
 
 export STATIC 
 export VERSION
diff -urN cdparanoia-III-alpha9.8/cddb.c cdparanoia-III-alpha9.8-cddb/cddb.c
--- cdparanoia-III-alpha9.8/cddb.c	Thu Jan  1 01:00:00 1970
+++ cdparanoia-III-alpha9.8-cddb/cddb.c	Tue Nov 19 17:58:36 2002
@@ -0,0 +1,907 @@
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <linux/cdrom.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#define _CDDB_C_
+#include "cddb.h"
+
+static const char CDDB_SIGNON_400[] = "Signon protocol error";
+static const char CDDB_SIGNON_200[] = "OK, read/write allowed";
+static const char CDDB_SIGNON_201[] = "OK, read only";
+static const char CDDB_SIGNON_432[] = "No connections allowed: permission denied";
+static const char CDDB_SIGNON_433[] = "No connections allowed: too much users";
+static const char CDDB_SIGNON_434[] = "No connections allowed: system load too high";
+static const char CDDB_HANDSHAKE_200[] = "Handshake successful";
+static const char CDDB_HANDSHAKE_431[] = "Handshake not successful, closing connection";
+static const char CDDB_HANDSHAKE_402[] = "Already shook hands";
+static const char CDDB_HANDSHAKE_400[] = "Handshake protocol error";
+static const char CDDB_QUERY_200[] = "Found exact match";
+static const char CDDB_QUERY_211[] = "Found inexact matches, list follows (until terminating marker)";
+static const char CDDB_QUERY_210[] = "Found exact matches, list follows (until terminating marker)";
+static const char CDDB_QUERY_400[] = "Query protocol error";
+static const char CDDB_QUERY_202[] = "No match found";
+static const char CDDB_QUERY_403[] = "Database entry is corrupt";
+static const char CDDB_QUERY_409[] = "No handshake";
+static const char CDDB_IDENTIFY_454[] = "Unable to read TOC header";
+static const char CDDB_IDENTIFY_455[] = "Unable to read TOC entry";
+static const char CDDB_READ_210[] = "OK, CDDB database entry follows (until terminating marker)";
+static const char CDDB_READ_401[] = "Specified CDDB entry not found";
+static const char CDDB_READ_402[] = "Server error";
+static const char CDDB_READ_403[] = "Database entry is corrupt";
+static const char CDDB_READ_409[] = "No handshake";
+static const char CDDB_READ_400[] = "Read protocol error";
+static const char CDDB_DB_ERROR[] = "Syntax error while parsing database entry";
+ 
+//#define DEBUG(toto...)  fprintf(stderr, toto)
+#define DEBUG(toto...)
+
+int cddb_is_fatal(int errno)
+{
+  return (errno < 200) || (errno == 400) || ((errno >= 430) && (errno < 440));
+}
+
+int cddb_is_ok(int errno)
+{
+  return (errno >= 200) && (errno < 300);
+}
+
+int cddb_is_system(int errno)
+{
+  return (errno < 200);
+}
+
+char *read_line(struct cddb *c)
+{
+  int i,size;
+  static unsigned int pos = 0;
+  char *line;
+
+ yep:
+  for(i = pos; i < cddb_speech_size; i++)
+    if(cddb_speech[i] == '
')
+      break;
+  if(cddb_speech && cddb_speech[i] == '
')
+    {
+      line = cddb_speech + pos;
+      pos = i+2;
+      cddb_speech[i] = 0;
+      DEBUG("<-[%s]\n", line);
+      return line;
+    }
+
+  if(ioctl(c->fd, FIONREAD, &size) < 0)
+    {
+      cddb_errno  = errno;
+      cddb_errstr = sys_errlist[errno];
+      return NULL;
+    }
+  cddb_speech = (char*)realloc(cddb_speech, cddb_speech_size + size + 1);
+  cddb_speech_size += size;
+  if(!cddb_speech)
+    {
+      cddb_errno  = errno;
+      cddb_errstr = sys_errlist[errno];
+      return NULL;
+    }
+  if(recv(c->fd, cddb_speech + cddb_speech_size-size, size, 0) < size)
+    {
+      cddb_speech[cddb_speech_size] = 0;
+      DEBUG("<!- [%s]\n", cddb_speech + cddb_speech_size-size);
+      cddb_errno  = errno;
+      cddb_errstr = sys_errlist[errno];
+      return NULL;
+    }
+  cddb_speech[cddb_speech_size] = 0;
+  goto yep;
+}
+
+int write_packet(struct cddb *c, char *msg)
+{
+  char *emsg;
+
+  emsg = (char*)alloca(strlen(msg)+2);
+  sprintf(emsg, "%s\n", msg);
+  DEBUG("-> [%s]\n", msg);
+
+  if(send(c->fd, emsg, strlen(msg)+1, 0) < strlen(msg)+1)
+    {
+      cddb_errno  = errno;
+      cddb_errstr = sys_errlist[errno];
+      return -1;
+    }
+  return strlen(msg)+1;
+}
+
+int returncode(char *line)
+{
+  int r;
+  if(sscanf(line, "%d", &r) != 1)
+    return -1;
+  return r;
+}
+
+struct cddb *cddb_connect(char *hostname, short port)
+{
+  struct cddb *c;
+  struct hostent *hp;
+  struct sockaddr_in remote;
+  int nr;
+  char *line;
+
+  c = (struct cddb *)malloc(sizeof(struct cddb));
+  c->hostname = (char *)malloc(strlen(hostname)+1);
+  strcpy(c->hostname, hostname);
+  c->port = port;
+  hp = gethostbyname(hostname);
+  if(!hp)
+    {
+      cddb_errstr = sys_errlist[errno];
+      cddb_errno  = errno;
+      free(c);
+      return NULL;
+    }
+
+  c->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+  if(c->fd < 0)
+    {
+      cddb_errstr = sys_errlist[errno];
+      cddb_errno  = errno;
+      free(c);
+      return NULL;
+    }
+
+  remote.sin_family = AF_INET;
+  remote.sin_port   = htons(port);
+  memcpy(&remote.sin_addr, hp->h_addr, hp->h_length);
+
+  if(connect(c->fd, (struct sockaddr*)&remote, sizeof(remote))<0)
+    {
+      cddb_errstr = sys_errlist[errno];
+      cddb_errno  = errno;
+      close(c->fd);
+      free(c);
+      return NULL;
+    }
+
+  if((line = read_line(c)))
+    {
+      switch(returncode(line))
+	{
+	case 200:
+	  cddb_errno  = 200;
+	  cddb_errstr = CDDB_SIGNON_200;
+	  return c;
+	case 201:
+	  cddb_errno  = 201;
+	  cddb_errstr = CDDB_SIGNON_201;
+	  return c;
+	case 432:
+	  cddb_errno  = 432;
+	  cddb_errstr = CDDB_SIGNON_432;
+	  break;
+	case 433:
+	  cddb_errno  = 433;
+	  cddb_errstr = CDDB_SIGNON_433;
+	  break;
+	case 434:
+	  cddb_errno  = 434;
+	  cddb_errstr = CDDB_SIGNON_434;
+	  break;
+	default:
+	  cddb_errno  = 400;
+	  cddb_errstr = CDDB_SIGNON_400;
+	}
+    }
+  close(c->fd);
+  free(c);
+  return NULL;
+}
+
+int cddb_handshake(struct cddb *c, char *client, char *version)
+{
+  char msg[strlen(client)+strlen(version)+78+L_cuserid];
+  char *line;
+
+  sprintf(msg, "cddb hello ");
+  cuserid(msg + strlen(msg));
+  strcat(msg, " ");
+  if(gethostname(msg+strlen(msg), 64)<0)
+    {
+      cddb_errno  = errno;
+      cddb_errstr = sys_errlist[errno];
+      return -1;
+    }
+  strcat(msg, " ");
+  strcat(msg, client);
+  strcat(msg, " ");
+  strcat(msg, version);
+  
+  if(write_packet(c, msg) < 0)
+    return -1;
+  if(!(line = read_line(c)))
+    return -1;
+  
+  switch(returncode(line))
+    {
+    case 200:
+      cddb_errno  = 200;
+      cddb_errstr = CDDB_HANDSHAKE_200;
+      return 1;      
+    case 431:
+      cddb_errno  = 431;
+      cddb_errstr = CDDB_HANDSHAKE_431;
+      close(c->fd);
+      free(c);
+      return -1;
+    case 402:
+      cddb_errno  = 402;
+      cddb_errstr = CDDB_HANDSHAKE_402;
+      return 0;
+    default:
+      cddb_errno  = 400;
+      cddb_errstr = CDDB_HANDSHAKE_400;
+      close(c->fd);
+      free(c);
+      return -1;
+    }
+}
+
+struct cddb_disc *cddb_identify_disc(int ioctl_fd)
+{
+  int i;
+  int tracks;
+  struct cdrom_tochdr hdr;
+  struct cdrom_tocentry entry;
+  struct cddb_disc *d;
+
+  int cddb_sum(int n)
+    {
+      int ret; /* For backward compatibility this algorithm must not change */ 
+      ret = 0; 
+      while (n > 0) {
+	ret = ret + (n % 10);
+	n = n / 10;
+      } 
+      return (ret);
+    } 
+
+  unsigned long cddb_discid(int tot_trks, int *minutes, int *seconds)
+    {
+      int i,
+	t = 0,
+	n = 0; /* For backward compatibility this algorithm must not change */ 
+      i = 0; 
+      while (i < tot_trks) {
+	n = n + cddb_sum((minutes[i] * 60) + seconds[i]);
+	i++;
+      } 
+      t = ((minutes[tot_trks] * 60) + seconds[tot_trks]) -
+	((minutes[0] * 60) + seconds[0]); 
+      return ((n % 0xff) << 24 | t << 8 | tot_trks);
+    }
+
+  cddb_errno = 0;
+  cddb_errstr = sys_errlist[0];
+
+  /* get TocHeader to find out how many entries there are */
+  if(ioctl(ioctl_fd, CDROMREADTOCHDR, &hdr ))
+    switch(errno){
+    case EPERM:
+      cddb_errno  = EPERM;
+      cddb_errstr = sys_errlist[errno];
+      return NULL;
+    default:
+      cddb_errno  = 454;
+      cddb_errstr = CDDB_IDENTIFY_454;
+      return NULL;
+    }
+
+  d = (struct cddb_disc*)malloc(sizeof(struct cddb_disc));
+  d->trackoffsets = (int*)malloc(sizeof(int)*hdr.cdth_trk1+1);
+  d->minutes      = (int*)malloc(sizeof(int)*hdr.cdth_trk1+1);
+  d->seconds      = (int*)malloc(sizeof(int)*hdr.cdth_trk1+1);
+  d->frames       = (int*)malloc(sizeof(int)*hdr.cdth_trk1+1);
+  d->nbtracks     = hdr.cdth_trk1;
+
+  /* get all TocEntries */
+  for(i=0;i<hdr.cdth_trk1;i++){
+    entry.cdte_track= i+1;
+    entry.cdte_format = CDROM_MSF;
+    if(ioctl(ioctl_fd,CDROMREADTOCENTRY,&entry)){
+      cddb_errno  = 455;
+      cddb_errstr = CDDB_IDENTIFY_455;
+      free(d->trackoffsets);
+      free(d->minutes);
+      free(d->seconds);
+      free(d->frames);
+      free(d);
+      return NULL;
+    }
+    
+    d->minutes[i] = entry.cdte_addr.msf.minute;
+    d->seconds[i] = entry.cdte_addr.msf.second;
+    d->frames[i]  = entry.cdte_addr.msf.frame;
+    d->trackoffsets[i] = 75*(60*d->minutes[i]+d->seconds[i])+d->frames[i];
+  }
+
+  entry.cdte_track = CDROM_LEADOUT;
+  entry.cdte_format = CDROM_MSF;
+  if(ioctl(ioctl_fd, CDROMREADTOCENTRY, &entry)){
+    cddb_errno  = 455;
+    cddb_errstr = CDDB_IDENTIFY_455;
+    free(d->trackoffsets);
+    free(d->minutes);
+    free(d->seconds);
+    free(d->frames);
+    free(d);
+    return NULL;
+  }
+  d->minutes[i] = entry.cdte_addr.msf.minute;
+  d->seconds[i] = entry.cdte_addr.msf.second;
+  d->frames[i]  = entry.cdte_addr.msf.frame;
+  d->trackoffsets[i] = 60*d->minutes[i]+d->seconds[i]+(d->frames[i]/75);
+  d->discid = cddb_discid(d->nbtracks, d->minutes, d->seconds);
+
+  return d;
+}
+
+void cddb_query_free(struct cddb_query *q)
+{
+  if(q)
+    {
+      free(q->categ);
+      free(q->title);
+      cddb_query_free(q->next);
+      free(q);
+    }
+}
+
+int until_first_space(char *line)
+{
+  int i;
+  for(i = 0; line[i]; i++)
+    if(line[i] == ' ')
+      return i;
+  return -1;
+}
+
+int isendmark(char *line)
+{
+  return (!strcmp(line, "."));
+}
+
+struct cddb_query *cddb_query(struct cddb *c, struct cddb_disc *d)
+{
+  char msg[22 + 10*(d->nbtracks+1)];
+  char ent[10];
+  int i;
+  struct cddb_query *q, *prev;
+  int categlen;
+  char *line;
+
+  sprintf(msg, "cddb query %08x %d",
+	  d->discid, d->nbtracks);
+  for(i = 0; i < d->nbtracks+1; i++)
+    {
+      sprintf(ent, " %d", d->trackoffsets[i]);
+      strcat(msg, ent);
+    }
+  
+  if(write_packet(c, msg) < 0)
+    return NULL;
+  if(!(line = read_line(c)))
+    return NULL;
+
+  switch(returncode(line))
+    {
+    case 200:
+      cddb_errno  = 200;
+      cddb_errstr = CDDB_QUERY_200;
+      q = (struct cddb_query*)malloc(sizeof(struct cddb_query));
+      categlen = until_first_space(line) + 1;
+      q->categ = (char *)malloc(categlen);
+      q->title = (char *)malloc(strlen(line) - 9 - categlen);
+      q->next = NULL;
+      if(sscanf(line, "%*d %s %08x %[^\n]",
+		q->categ, &q->discid, q->title) != 3)
+	{
+	  cddb_query_free(q);
+	  cddb_errstr = CDDB_QUERY_400;
+	  cddb_errno  = 400;
+	  close(c->fd);
+	  free(c);
+	  return NULL;
+	}
+      return q;
+      break;
+    case 211:
+      cddb_errno  = 211;
+      cddb_errstr = CDDB_QUERY_211;
+      goto do_211_210;
+    case 210:
+      cddb_errno  = 210;
+      cddb_errstr = CDDB_QUERY_210;
+    do_211_210:
+      q = NULL;
+      do {
+	if(!(line=read_line(c)))
+	  {
+	    cddb_query_free(q);
+	    return NULL;
+	  }
+	if(!isendmark(line))
+	  {
+	    if(!q)
+	      {
+		q = (struct cddb_query*)malloc(sizeof(struct cddb_query));
+		categlen = until_first_space(line) + 1;
+		q->categ = (char *)malloc(categlen);
+		q->title = (char *)malloc(strlen(line) - 9 - categlen);
+		q->next = NULL;
+		if(sscanf(line, "%s %08x %[^\n]",
+			  q->categ, &q->discid, q->title) != 3)
+		  {
+		    cddb_query_free(q);
+		    cddb_errstr = CDDB_QUERY_400;
+		    cddb_errno  = 400;
+		    close(c->fd);
+		    free(c);
+		    return NULL;
+		  }
+		prev = q;
+	      }
+	    else
+	      {
+		prev->next = (struct cddb_query*)malloc(sizeof(struct cddb_query));
+		categlen = until_first_space(line + 4) + 1;
+		prev->next->categ = (char *)malloc(categlen);
+		prev->next->title = (char *)malloc(strlen(line) - 9 - categlen);
+		prev->next->next = NULL;
+		if(sscanf(line, "%s %08x %[^\n]",
+			  prev->next->categ, &prev->next->discid, prev->next->title) != 3)
+		  {
+		    cddb_query_free(q);
+		    cddb_errstr = CDDB_QUERY_400;
+		    cddb_errno  = 400;
+		    close(c->fd);
+		    free(c);
+		    return NULL;
+		  }
+		prev = prev->next;
+	      }
+	  }
+      } while(!isendmark(line));
+      return q;
+      break;
+    case 202:
+      cddb_errstr = CDDB_QUERY_202;
+      cddb_errno  = 202;
+      return NULL;
+      break;
+    case 403:
+      cddb_errstr = CDDB_QUERY_403;
+      cddb_errno  = 403;
+      return NULL;
+      break;
+    case 409:
+      cddb_errstr = CDDB_QUERY_409;
+      cddb_errno  = 409;
+      return NULL;
+      break;
+    default:
+      cddb_errstr = CDDB_QUERY_400;
+      cddb_errno  = 400;
+      close(c->fd);
+      free(c);
+      return NULL;
+    }
+}
+
+void cddb_db_entry_free(struct cddb_db_entry *e)
+{
+  int i;
+  if(e->comm)
+    free(e->comm);
+  if(e->disctitle)
+    free(e->disctitle);
+  if(e->author)
+    free(e->author);
+  if(e->dgenre)
+    free(e->dgenre);
+  if(e->extended_data)
+    free(e->extended_data);
+  if(e->titles)
+    {
+      for(i = 0; i < e->nbtitles; i++)
+	if(e->titles[i])
+	  free(e->titles[i]);
+      free(e->titles);
+    }
+  if(e->extended_title_data)
+    {
+      for(i = 0; i < e->nbtitles; i++)
+	if(e->extended_title_data[i])
+	  free(e->extended_title_data[i]);
+      free(e->extended_title_data);
+    }
+  free(e);
+}
+
+void cddb_print_db_entry(struct cddb_db_entry *e)
+{
+  int i;
+
+  if(e->comm)
+    printf("Commentaire : %s\n\n", e->comm);
+  else
+    printf("pas de commentaire\n\n");
+  if(e->disctitle)
+    printf("titre : %s\n\n", e->disctitle);
+  else
+    printf("pas de titre\n\n");
+  if(e->author)
+    printf("Artiste : %s\n\n", e->author);
+  else
+    printf("pas d'artiste\n\n");
+  if(e->dgenre)
+    printf("Genre : %s\n\n", e->dgenre);
+  else
+    printf("pas de genre\n\n");
+  if(e->extended_data)
+    printf("Données supplémentaires sur le disque : %s\n\n", e->extended_data);
+  else
+    printf("pas de données supplémentaires sur le disque\n\n");
+  for(i = 0; i < e->nbtitles; i++)
+    {
+      printf("TITRE %d : ", i);
+      if(e->titles[i])
+	printf("%s", e->titles[i]);
+      else
+	printf("???");
+      if(e->extended_title_data[i])
+	printf(" (%s)\n", e->extended_title_data[i]);
+      else
+	printf("\n");
+    }
+  printf("\n");
+}
+
+struct cddb_db_entry *cddb_read(struct cddb *c, struct cddb_disc *d, struct cddb_query *q)
+{
+  char msg[21+strlen(q->categ)];
+  char *line;
+  struct cddb_db_entry *e;
+  char *p;
+  int len, n;
+
+  sprintf(msg, "cddb read %s %08x", q->categ, q->discid);
+  if(write_packet(c, msg) < 0)
+    return NULL;
+  if(!(line = read_line(c)))
+    return NULL;
+  
+  switch(returncode(line))
+    {
+    case 210:
+      cddb_errno  = 210;
+      cddb_errstr = CDDB_READ_210;
+      e = (struct cddb_db_entry*)calloc(1,sizeof(struct cddb_db_entry));
+      e->nbtitles = d->nbtracks;
+      e->titles   = (char**)calloc(d->nbtracks,sizeof(char*));
+      e->extended_title_data = (char**)calloc(d->nbtracks,sizeof(char*));
+      e->comm = (char*)malloc(1);
+      e->comm[0] = 0;
+      do {
+	if(!(line=read_line(c)))
+	  {
+	    cddb_db_entry_free(e);
+	    return NULL;
+	  }
+	if(!isendmark(line))
+	  {
+	    switch(line[0])
+	      {
+	      case '#': //commentary
+		len = strlen(e->comm);
+		e->comm = (char*)realloc(e->comm, len+strlen(line+1)+2);
+		strcat(e->comm, line+1);
+		strcat(e->comm, "\n");
+		break;
+	      case 'D':
+		switch(line[1])
+		  {
+		  case 'I': //DISCID
+		    if(sscanf(line, "DISCID=%08x", &e->discid) != 1)
+		      goto dberror;
+		    break;
+		  case 'T': //DTITLE
+		    if(strncmp(line, "DTITLE=", 7))
+		      goto dberror;
+		    if( (p = strstr(line+7, " / ")) )
+		      {
+			e->author = (char*)malloc(((p-line)-7));
+			memcpy(e->author, line+7, ((p-line)-7));
+			e->author[((p-line)-7)] = 0;
+			e->disctitle = (char*)malloc(strlen(p+1)+1);
+			strcpy(e->disctitle, p+3);
+		      }
+		    else
+		      {
+			e->disctitle = (char*)malloc(strlen(line+7)+1);
+			strcpy(e->disctitle, line+7);
+		      }
+		    break;
+		  case 'Y': //DYEAR
+		    if(sscanf(line, "DYEAR=%d", &e->dyear) != 1)
+		      goto dberror;
+		    break;
+		  case 'G': //DGENRE
+		    if(strncmp(line, "DGENRE=", 7))
+		      goto dberror;
+		    break;
+		  default:  //Unknown
+		    goto dberror;
+		  }
+		break;
+	      case 'T': // TTITLEN
+		if(strncmp(line, "TTITLE", 6))
+		  goto dberror;
+		if(sscanf(line+6, "%d=", &n) != 1)
+		  goto dberror;
+		if(n < 0 || n >= e->nbtitles)
+		  goto dberror;
+		p = strstr(line, "=")+1;
+		if(p == (char*)1)
+		  goto dberror;
+		len = (e->titles[n]?strlen(e->titles[n]):0);
+		e->titles[n] = (char*)realloc(e->titles[n], len+strlen(p)+2);
+		if(!len)
+		  e->titles[n][0] = 0;
+		else
+		  strcat(e->titles[n], "\n");
+		strcat(e->titles[n], p);
+		break;
+	      case 'E':
+		switch(line[3])
+		  {
+		  case 'D': // EXTD
+		    if(strncmp(line, "EXTD=", 5))
+		      goto dberror;
+		    len = (e->extended_data?strlen(e->extended_data):0);
+		    e->extended_data = (char*)realloc(e->extended_data, len+strlen(line+5)+2);
+		    if(!len)
+		      e->extended_data[0] = 0;
+		    else
+		      strcat(e->extended_data, "\n");
+		    strcat(e->extended_data, line+5);
+		    break;
+		  case 'T': // EXTTN
+		    if(strncmp(line, "EXTT", 4))
+		      goto dberror;
+		    if(sscanf(line+4, "%d=", &n) != 1)
+		      goto dberror;
+		    if(n < 0 || n >=e->nbtitles)
+		      goto dberror;
+		    p = strstr(line, "=")+1;
+		    if(p == (char*)1)
+		      goto dberror;
+		    len = (e->extended_title_data[n]?strlen(e->extended_title_data[n]):0);
+		    e->extended_title_data[n] = (char*)realloc(e->extended_title_data[n], len+strlen(p)+2);
+		    if(!len)
+		      e->extended_title_data[n][0] = 0;
+		    else
+		      strcat(e->extended_title_data[n], "\n");
+		    strcat(e->extended_title_data[n], p);
+		    break;
+		  default:  // Unknown
+		    goto dberror;
+		  }
+		break;
+	      case 'P': // PLAYORDER
+		break;
+	      default:  // Unknown
+		goto dberror;
+	      }
+	  }
+      }	while(!isendmark(line));
+      return e;
+    dberror:
+      cddb_db_entry_free(e);
+      cddb_errno  = 408;
+      cddb_errstr = CDDB_DB_ERROR;
+      return NULL;
+    case 401:
+      cddb_errno  = 401;
+      cddb_errstr = CDDB_READ_401;
+      return NULL;
+    case 402:
+      cddb_errno  = 402;
+      cddb_errstr = CDDB_READ_402;
+      return NULL;
+    case 403:
+      cddb_errno  = 403;
+      cddb_errstr = CDDB_READ_403;
+      return NULL;
+    case 409:
+      cddb_errno  = 409;
+      cddb_errstr = CDDB_READ_409;
+      return NULL;
+    default:
+      cddb_errno  = 400;
+      cddb_errstr = CDDB_READ_400;
+      return NULL;
+    }
+}
+
+void cddb_quit(struct cddb *c)
+{
+  char msg[] = "quit";
+  
+  write_packet(c, msg);
+  read_line(c);
+  
+  close(c->fd);
+}
+
+void cddb_print_speech(void)
+{
+  int i;
+  for(i = 0; i < cddb_speech_size; i++)
+    if(cddb_speech[i] == 0)
+      printf("\n");
+    else
+      printf("%c", cddb_speech[i]);
+}
+
+void cddb_speech_erase()
+{
+  free(cddb_speech);
+  cddb_speech = NULL;
+  cddb_speech_size = 0;
+}
+
+void cddb_disc_free(struct cddb_disc *cd)
+{
+  if(cd)
+    {
+      if(cd->trackoffsets)
+	free(cd->trackoffsets);
+      if(cd->minutes)
+	free(cd->minutes);
+      if(cd->seconds)
+	free(cd->seconds);
+      if(cd->frames)
+	free(cd->frames);
+      free(cd);
+    }
+}
+
+void cddb_free(struct cddb *c)
+{
+  if(c)
+    {
+      if(c->hostname)
+	free(c->hostname);
+      free(c);
+    }
+}
+
+static char *filize(char *f)
+{
+  char *c;
+
+  if(!f)
+    return NULL;
+
+  for(c=f; *c; c++)
+    if(*c == '/')
+      *c = ',';
+
+  return f;
+}
+
+int cddb_snprintf(char *outfile, int maxlen, char *format, struct cddb_db_entry *e, int title,
+		  char *path, char *extension)
+{
+  int pos = 0;
+  int i;
+
+  fprintf(stderr, "snprinteffing\n");
+
+  outfile[0] = 0;
+  if(path && ( (pos += snprintf(outfile+pos, maxlen-pos, "%s", path)) >= maxlen))
+    goto out_err;
+
+  for(i=0;format[i];i++)
+    {
+      switch(format[i])
+	{
+	case '%':
+	  i++;
+	  switch(format[i])
+	    {
+	    case 'c':
+	      if( (pos += snprintf(outfile+pos, maxlen-pos, "%s", e->comm?filize(e->comm):"none")) >= maxlen)
+		goto out_err;
+	      break;
+	    case 'D':
+	      if( (pos += snprintf(outfile+pos, maxlen-pos, "%s", e->disctitle?filize(e->disctitle):"none")) >= maxlen )
+		goto out_err;
+	      break;
+	    case 'A':
+	      if( (pos += snprintf(outfile+pos, maxlen-pos, "%s", e->author?filize(e->author):"none")) >= maxlen )
+		goto out_err;
+	      break; 
+	    case 'Y':
+	      if( (pos += snprintf(outfile+pos, maxlen-pos, "%04d", e->dyear)) >= maxlen )
+		goto out_err;
+	      break;
+	    case 'y':
+	      if( (pos += snprintf(outfile+pos, maxlen-pos, "%02d", e->dyear % 100)) >= maxlen )
+		goto out_err;
+	      break;
+	    case 'G':
+	      if( (pos += snprintf(outfile+pos, maxlen-pos, "%s", e->dgenre?filize(e->dgenre):"none")) >= maxlen )
+		goto out_err;
+	      break;
+	    case 'X':
+	      if( (pos += snprintf(outfile+pos, maxlen-pos, "%s", e->extended_data?filize(e->extended_data):"none")) >= maxlen )
+		goto out_err;
+	      break;
+	    case 'N':
+	      if( (pos += snprintf(outfile+pos, maxlen-pos, "%d", e->nbtitles)) >= maxlen )
+		goto out_err;
+	      break;
+	    case 'T':
+	      if( (pos += snprintf(outfile+pos, maxlen-pos, "%s", 
+				   ( (title<e->nbtitles) && e->titles[title])?
+				   filize(e->titles[title]):
+				   "none")) >= maxlen )
+		goto out_err;
+	      break;
+	    case 'x':
+	      if( (pos += snprintf(outfile+pos, maxlen-pos, "%s", 
+				   ((title<e->nbtitles) && e->extended_title_data[title])?
+				   filize(e->extended_title_data[title]):
+				   "none")) >= maxlen )
+		goto out_err;
+	      break;
+	    case 'C':
+	      if( (pos += snprintf(outfile+pos, maxlen-pos, "%02d", title+1)) >= maxlen )
+		goto out_err;
+	      break;
+	    case '%':
+	      if( (pos += snprintf(outfile+pos, maxlen-pos, "%%")) >= maxlen)
+		goto out_err;
+	      break;
+	    case 0:
+	      goto out;
+	    default:
+	      fprintf(stderr, "cddb wrong output format flag <%c>\n", format[i]);
+	      return -1;
+	    }
+	  break;
+	case 0:
+	  goto out;
+	default:
+	  if( (pos += snprintf(outfile+pos, maxlen-pos, "%c", format[i])) >= maxlen)
+	    goto out_err;
+	}
+    }
+
+ out:
+  if(extension)
+    pos += snprintf(outfile+pos, maxlen-pos, "%s", extension);
+
+ out_err:
+  return pos;
+}
diff -urN cdparanoia-III-alpha9.8/cddb.h cdparanoia-III-alpha9.8-cddb/cddb.h
--- cdparanoia-III-alpha9.8/cddb.h	Thu Jan  1 01:00:00 1970
+++ cdparanoia-III-alpha9.8-cddb/cddb.h	Tue Nov 19 16:10:28 2002
@@ -0,0 +1,70 @@
+#ifndef _CDDB_H_
+#define _CDDB_H_
+
+struct cddb {
+  char *hostname;
+  short port;
+  int fd;
+};
+
+struct cddb_disc {
+  unsigned long discid;
+  int nbtracks;
+  int *trackoffsets;
+  int *minutes;
+  int *seconds;
+  int *frames;
+};
+
+struct cddb_query {
+  unsigned long discid;
+  char *categ;
+  char *title;
+  struct cddb_query *next;
+};
+
+struct cddb_db_entry {
+  char *comm;                  /* %c */
+  unsigned long discid;
+  char *disctitle;             /* %D */
+  char *author;                /* %A */
+  short dyear;                 /* %Y|%y */
+  char *dgenre;                /* %G */
+  char *extended_data;         /* %X */
+  int nbtitles;                /* %N */
+  char **titles;               /* %T */
+  char **extended_title_data;  /* %x */
+                               /* %C : title index */
+};
+
+#ifndef _CDDB_C_
+#define GLOBHACK extern 
+#else 
+#define GLOBHACK 
+#endif
+
+GLOBHACK int cddb_errno;
+GLOBHACK int cddb_speech_size;
+GLOBHACK char *cddb_speech;
+GLOBHACK const char *cddb_errstr;
+
+int cddb_is_fatal(int errno);
+int cddb_is_ok(int errno);
+int cddb_is_system(int errno);
+struct cddb *cddb_connect(char *hostname, short port);
+int cddb_handshake(struct cddb *c, char *client, char *version);
+struct cddb_disc *cddb_identify_disc(int ioctl_fd);
+void cddb_query_free(struct cddb_query *q);
+struct cddb_query *cddb_query(struct cddb *c, struct cddb_disc *d);
+void cddb_db_entry_free(struct cddb_db_entry *e);
+void cddb_print_db_entry(struct cddb_db_entry *e);
+struct cddb_db_entry *cddb_read(struct cddb *c, struct cddb_disc *d, struct cddb_query *q);
+void cddb_quit(struct cddb *c);
+void cddb_speech_erase();
+void cddb_disc_free(struct cddb_disc *cd);
+void cddb_free(struct cddb *c);
+int  cddb_snprintf(char *outfile, int maxlen, char *format, 
+		   struct cddb_db_entry *e, int title,
+		   char *path, char *extension);
+
+#endif
diff -urN cdparanoia-III-alpha9.8/cddbtest.c cdparanoia-III-alpha9.8-cddb/cddbtest.c
--- cdparanoia-III-alpha9.8/cddbtest.c	Thu Jan  1 01:00:00 1970
+++ cdparanoia-III-alpha9.8-cddb/cddbtest.c	Thu Sep 13 16:01:56 2001
@@ -0,0 +1,57 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "cddb.h"
+
+int main(int argc, char *argv[])
+{
+  struct cddb *c;
+  struct cddb_disc *cd;
+  struct cddb_query *q;
+  struct cddb_db_entry *e;
+  int fd;
+
+  c = cddb_connect("freedb.freedb.org", 888);
+
+      printf("connect : %s\n", cddb_errstr);
+    
+
+  cddb_handshake(c, "cddblib", "0.1");
+
+      printf("handshake : %s\n", cddb_errstr);
+     
+
+  fd = open("/dev/hdc", O_RDONLY);
+  if(fd<0)
+    {
+      perror("open");
+      exit(0);
+    }
+  cd = cddb_identify_disc(fd);
+  close(fd);
+
+      printf("identify : %s\n", cddb_errstr);
+      
+
+  q = cddb_query(c, cd);
+
+      printf("query : %s\n", cddb_errstr);
+
+  e = cddb_read(c, cd, q);
+
+      printf("read : %s\n", cddb_errstr);
+     
+  cddb_print_db_entry(e);
+
+  cddb_quit(c);
+
+  //cddb_print_speech();  
+  cddb_speech_erase();
+
+  cddb_disc_free(cd);
+  cddb_query_free(q);
+  cddb_db_entry_free(e);
+  cddb_free(c);
+
+  exit(0);
+}
diff -urN cdparanoia-III-alpha9.8/cdparanoia.1 cdparanoia-III-alpha9.8-cddb/cdparanoia.1
--- cdparanoia-III-alpha9.8/cdparanoia.1	Mon Mar 26 05:44:50 2001
+++ cdparanoia-III-alpha9.8-cddb/cdparanoia.1	Tue Nov 19 18:29:11 2002
@@ -192,6 +192,68 @@
 .B \-X --abort-on-skip
 If the read skips due to imperfect data, a scratch, whatever, abort reading this track.  If output is to a file, delete the partially completed file.
 
+.SH CDDB FLAGS
+
+.TP
+.B \-l --cddb-lookup
+In conjunction with -B flag, perform a cddb query to fetch informations on the cd, and names each track accordingly. Default is off
+
+.TP
+.B \-b --cddb-server <name[:port]>
+Set the server used for cddb queries and the port to connect to it. Default is freedb.freedb.org on port 888
+
+.TP
+.B \-k --cddb-output-format <format>
+Set the format of files prefix. The format is printf-like, using the following tags :
+.TP
+.B
+  %D
+Disc Title (that is the part of the string before the first '/' in the database DTITLE entry)
+.TP
+.B
+  %A
+Disc Author (that is the part of the string between the first and the second '/' in the database DTITLE entry)
+.TP
+.B
+  %c
+General commentary on the disc (Namely, the data after the second '/' in the database DTITLE entry)
+.TP
+.B
+  %Y
+The 4-digit year of the CD release (0 if no DYEAR entry in the database).
+.TP
+.B
+  %y
+The last 2 digits of the %Y flag
+.TP
+.B
+  %G
+The genre of the CD (DGENRE entry)
+.TP
+.B
+  %X
+Extended data of the disc (generally, indexes informations and references of the people who entered the entry in the database)
+.TP
+.B
+  %N
+Number of tracks referenced in the database
+.TP
+.B
+  %T
+Title of current track. If the disc has more than one author, author name will appear first, then track title separated by a comma
+.TP
+.B
+  %C
+Index of current track on 2 digits at least filled with 0s
+.TP
+.B
+  %%
+a '%'
+.TP
+  every other %sequence produce an error
+.TP
+  every other character is printed as is
+
 .SH OUTPUT SMILIES
 .TP
 .B
@@ -328,6 +390,11 @@
        cdparanoia -- "-3"
 .TP
 The "--" above is to distinguish "-3" from an option flag.
+
+.TP 
+Extract the whole disc, putting each track on a separate file, whose name is <title>:<number>-<track-title>.cdda.wav this information being fetch from the cddb server mydb on port 2222:
+.P
+       cdparanoia -B -l -b mydb:2222 -k "%D:%C-%T"
 .SH OUTPUT
 
 The output file argument is optional; if it is not specified,
diff -urN cdparanoia-III-alpha9.8/cdparanoia.1~ cdparanoia-III-alpha9.8-cddb/cdparanoia.1~
--- cdparanoia-III-alpha9.8/cdparanoia.1~	Thu Jan  1 01:00:00 1970
+++ cdparanoia-III-alpha9.8-cddb/cdparanoia.1~	Mon Mar 26 05:44:50 2001
@@ -0,0 +1,356 @@
+.TH CDPARANOIA 1
+.SH NAME
+cdparanoia (Paranoia release III) \- an audio CD reading utility which includes extra data verification features
+.SH DATE
+version III release alpha 9.8 (02 Mar 2001)
+.SH SYNOPSIS
+.B cdparanoia
+.RB [ options ]
+.B span 
+.RB [ outfile ]
+.SH DESCRIPTION
+.B cdparanoia
+retrieves audio tracks from CDDA capable CDROM drives.  The data can
+be saved to a file or directed to standard output in WAV, AIFF, AIFF-C
+or raw format.  Most ATAPI, SCSI and several proprietary CDROM drive
+makes are supported;
+.B cdparanoia
+can determine if the target drive is CDDA capable.
+.P
+In addition to simple reading, 
+.B cdparanoia
+adds extra-robust data verification, synchronization, error handling
+and scratch reconstruction capability.
+.SH OPTIONS
+
+.TP
+.B \-v --verbose
+Be absurdly verbose about the autosensing and reading process. Good
+for setup and debugging.
+
+.TP
+.B \-q --quiet
+Do not print any progress or error information during the reading process.
+
+.TP
+.B \-e --stderr-progress
+Force output of progress information to stderr (for wrapper scripts).
+
+.TP
+.B \-V --version
+Print the program version and quit.
+
+.TP
+.B \-Q --query
+Perform CDROM drive autosense, query and print the CDROM table of
+contents, then quit.
+
+.TP
+.B \-s --search-for-drive
+Forces a complete search for a cdrom drive, even if the /dev/cdrom link exists.
+
+.TP
+.B \-h --help
+Print a brief synopsis of 
+.B cdparanoia 
+usage and options.
+
+.TP
+.B \-p --output-raw
+Output headerless data as raw 16 bit PCM data with interleaved samples in host byte order.  To force little or big endian byte order, use 
+.B \-r 
+or 
+.B \-R
+as described below.
+
+.TP
+.B \-r --output-raw-little-endian
+Output headerless data as raw 16 bit PCM data with interleaved samples in LSB first byte order.
+
+.TP
+.B \-R --output-raw-big-endian
+Output headerless data as raw 16 bit PCM data with interleaved samples in MSB first byte order.
+
+.TP
+.B \-w --output-wav
+Output data in Micro$oft RIFF WAV format (note that WAV data is always
+LSB first byte order).
+
+.TP
+.B \-f --output-aiff
+Output data in Apple AIFF format (note that AIFC data is
+always in MSB first byte order).
+
+.TP
+.B \-a --output-aifc
+Output data in uncompressed Apple AIFF-C format (note that AIFF-C data is
+always in MSB first byte order).
+
+.TP
+.BI "\-B --batch "
+
+Cdda2wav-style batch output flag; cdparanoia will split the output
+into multiple files at track boundaries.  Output file names are
+prepended with 'track#.'
+
+.TP
+.B \-c --force-cdrom-little-endian
+Some CDROM drives misreport their endianness (or do not report it at
+all); it's possible that cdparanoia will guess wrong.  Use
+.B \-c
+to force cdparanoia to treat the drive as a little endian device.
+
+.TP
+.B \-C --force-cdrom-big-endian
+As above but force cdparanoia to treat the drive as a big endian device.
+
+.TP
+.BI "\-n --force-default-sectors " n
+Force the interface backend to do atomic reads of 
+.B n
+sectors per read.  This number can be misleading; the kernel will often
+split read requests into multiple atomic reads (the automated Paranoia
+code is aware of this) or allow reads only wihin a restricted size
+range. 
+.B This option should generally not be used.
+
+.TP
+.BI "\-d --force-cdrom-device " device
+Force the interface backend to read from
+.B device
+rather than the first readable CDROM drive it finds.  This can be used
+to specify devices of any valid interface type (ATAPI, SCSI or
+proprietary).
+
+.TP
+.BI "\-g --force-generic-device " device
+This option is used along with
+.B \-d
+when one wants explicit control in setting both the SCSI cdrom and
+generic devices seperately. This option is only useful on
+non-standard SCSI setups.
+
+.TP
+.BI "\-S --force-read-speed " number
+Use this option explicitly to set the read rate of the CD drive (where
+supported).  This can reduce underruns on machines with slow disks, or
+which are low on memory.
+
+.TP
+.BI "\-t --toc-offset " number
+Use this option to force the entire disc LBA addressing to shift by
+the given amount; the value is added to the beginning offsets in the
+TOC.  This can be used to shift track boundaries for the whole disc
+manually on sector granularity.  The next option does something
+similar...
+
+.TP
+.BI "\-T --toc-bias "
+Some drives (usually random Toshibas) report the actual track
+beginning offset values in the TOC, but then treat the beginning of
+track 1 index 1 as sector 0 for all read operations.  This results in
+every track seeming to start too late (losing a bit of the beginning
+and catching a bit of the next track).
+.B \-T
+accounts for this behavior.  Note that this option will cause
+cdparanoia to attempt to read sectors before or past the known user
+data area of the disc, resulting in read errors at disc edges on most
+drives and possibly even hard lockups on some buggy hardware.
+
+.TP
+.BI "\-O --sample-offset " number
+Use this option to force the entire disc to shift sample position
+output by the given amount; This can be used to shift track boundaries
+for the whole disc manually on sample granularity. Note that this will
+cause cdparanoia to attempt to read partial sectors before or past the
+known user data area of the disc, probably causing read errors on most
+drives and possibly even hard lockups on some buggy hardware.
+
+
+.TP
+.B \-Z --disable-paranoia
+Disable 
+.B all
+data verification and correction features.  When using -Z, cdparanoia
+reads data exactly as would cdda2wav with an overlap setting of zero.
+This option implies that 
+.B \-Y
+is active.
+
+.TP
+.B \-z --never-skip[=max_retries]
+Do not accept any skips; retry forever if needed.  An optional maximum
+number of retries can be specified; for comparison, default without -z is
+currently 20.
+
+.TP
+.B \-Y --disable-extra-paranoia
+Disables intra-read data verification; only overlap checking at read
+boundaries is performed. It can wedge if errors occur in the attempted overlap area. Not recommended.
+
+.TP
+.B \-X --abort-on-skip
+If the read skips due to imperfect data, a scratch, whatever, abort reading this track.  If output is to a file, delete the partially completed file.
+
+.SH OUTPUT SMILIES
+.TP
+.B
+  :-)   
+Normal operation, low/no jitter
+.TP
+.B
+  :-|   
+Normal operation, considerable jitter
+.TP
+.B
+  :-/   
+Read drift
+.TP
+.B
+  :-P   
+Unreported loss of streaming in atomic read operation
+.TP
+.B
+  8-|   
+Finding read problems at same point during reread; hard to correct
+.TP
+.B
+  :-0   
+SCSI/ATAPI transport error
+.TP
+.B
+  :-(   
+Scratch detected
+.TP
+.B
+  ;-(   
+Gave up trying to perform a correction
+.TP
+.B
+  8-X
+Aborted read due to known, uncorrectable error
+.TP
+.B
+  :^D   
+Finished extracting
+
+.SH PROGRESS BAR SYMBOLS
+.TP
+.B
+<space> 
+No corrections needed
+.TP
+.B
+   -    
+Jitter correction required
+.TP
+.B
+   +    
+Unreported loss of streaming/other error in read
+.TP
+.B
+   !  
+Errors found after stage 1 correction; the drive is making the
+same error through multiple re-reads, and cdparanoia is having trouble
+detecting them.
+.TP
+.B
+   e    
+SCSI/ATAPI transport error (corrected)
+.TP
+.B
+   V    
+Uncorrected error/skip
+
+.SH SPAN ARGUMENT
+
+The span argument specifies which track, tracks or subsections of
+tracks to read.  This argument is required. 
+.B NOTE:
+Unless the span is a simple number, it's generally a good idea to
+quote the span argument to protect it from the shell.
+.P
+The span argument may be a simple track number or an offset/span
+specification.  The syntax of an offset/span takes the rough form:
+.P
+1[ww:xx:yy.zz]-2[aa:bb:cc.dd] 
+.P
+Here, 1 and 2 are track numbers; the numbers in brackets provide a
+finer grained offset within a particular track. [aa:bb:cc.dd] is in
+hours/minutes/seconds/sectors format. Zero fields need not be
+specified: [::20], [:20], [20], [20.], etc, would be interpreted as
+twenty seconds, [10:] would be ten minutes, [.30] would be thirty
+sectors (75 sectors per second).
+.P
+When only a single offset is supplied, it is interpreted as a starting
+offset and ripping will continue to the end of the track.  If a single
+offset is preceeded or followed by a hyphen, the implicit missing
+offset is taken to be the start or end of the disc, respectively. Thus:
+
+.TP
+.B  1:[20.35]    
+Specifies ripping from track 1, second 20, sector 35 to the end of
+track 1.
+.TP
+.B 1:[20.35]-   
+Specifies ripping from 1[20.35] to the end of the disc
+.TP
+.B \-2           
+Specifies ripping from the beginning of the disc up to (and including) track 2
+.TP
+.B \-2:[30.35]   
+Specifies ripping from the beginning of the disc up to 2:[30.35]
+.TP
+.B 2-4          
+Specifies ripping from the beginning of track 2 to the end of track 4.
+.P
+Again, don't forget to protect square brackets and preceeding hyphens from
+the shell.
+
+.SH EXAMPLES
+
+A few examples, protected from the shell:
+.TP
+Query only with exhaustive search for a drive and full reporting of autosense:
+.P
+       cdparanoia -vsQ
+.TP
+Extract an entire disc, putting each track in a seperate file:
+.P
+       cdparanoia -B 
+.TP
+Extract from track 1, time 0:30.12 to 1:10.00:
+.P
+       cdparanoia "1[:30.12]-1[1:10]"
+.TP
+Extract from the beginning of the disc up to track 3:
+.P
+       cdparanoia -- "-3"
+.TP
+The "--" above is to distinguish "-3" from an option flag.
+.SH OUTPUT
+
+The output file argument is optional; if it is not specified,
+cdparanoia will output samples to one of
+.BR cdda.wav ", " cdda.aifc ", or " cdda.raw
+depending on whether 
+.BR \-w ", " \-a ", " \-r " or " \-R " is used (" \-w 
+is the implicit default).  The output file argument of 
+.B \-
+specifies standard output; all data formats may be piped. 
+
+.SH ACKNOWLEDGEMENTS
+Cdparanoia sprang from and once drew heavily from the interface of
+Heiko Eissfeldt's ([email protected]) 'cdda2wav'
+package. Cdparanoia would not have happened without it.
+.P
+Joerg Schilling has also contributed SCSI expertise through his
+generic SCSI transport library.
+.P
+.SH AUTHOR
+Monty <[email protected]>
+.P
+Cdparanoia's homepage may be found at:
+.P
+.ce 
+http://www.xiph.org/paranoia/
diff -urN cdparanoia-III-alpha9.8/main.c cdparanoia-III-alpha9.8-cddb/main.c
--- cdparanoia-III-alpha9.8/main.c	Mon Mar 26 05:44:50 2001
+++ cdparanoia-III-alpha9.8-cddb/main.c	Tue Nov 19 17:45:14 2002
@@ -61,6 +61,7 @@
 #include "report.h"
 #include "version.h"
 #include "header.h"
+#include "cddb.h"
 
 extern int verbose;
 extern int quiet;
@@ -223,8 +224,12 @@
   report("");
 }
 
-static void usage(FILE *f){
-  fprintf( f,
+#define CDDBSERVER       "freedb.freedb.org"
+#define CDDBPORT          888
+#define CDDBPORT_STR     "888"
+#define CDDBOUTPUTFORMAT "%D[%A]-%C:%T"
+
+static char usage_str[] = 
 VERSION"\n"
 
 "USAGE:\n"
@@ -277,6 +282,29 @@
 "  -Y --disable-extra-paranoia     : only do cdda2wav-style overlap checking\n"
 "  -X --abort-on-skip              : abort on imperfect reads/skips\n\n"
 
+"CDDB OPTIONS:\n"
+"  -l --cddb-lookup                : do cddb lookup when in batch mode\n"
+"                                    default dont do cddb-lookup\n"
+"  -b --cddb-server <name[:port]>  : use name on port port for cddb server\n"
+"                                    default " CDDBSERVER":"CDDBPORT_STR "\n"
+"  -k --cddb-output-format <f>     : use the format f for file names\n"
+"                                     flags for format :\n"
+"                                       %% : put a '%'\n"
+"                                       %c : put the general cddb commentary\n"
+"                                       %D : put the disc title\n"
+"                                       %A : put the disc author\n"
+"                                       %Y : put the year 4 digits\n"
+"                                       %y : put the year 2 last digits\n"
+"                                       %G : put the disc genre (cddb codes)\n"
+"                                       %X : put the disc extented data\n"
+"                                       %N : put the number of titles\n"
+"                                       %T : put the title of the current track\n"
+"                                       %x : put the extended title data of the\n"
+"                                            current track\n"
+"                                       %C : put the number of current track\n"
+"                                       other char : put the char\n"
+"                                     default : "CDDBOUTPUTFORMAT"\n\n"
+
 "OUTPUT SMILIES:\n"
 "  :-)   Normal operation, low/no jitter\n"
 "  :-|   Normal operation, considerable jitter\n"
@@ -338,10 +366,17 @@
 "  B) extract up to and including track 3, putting each track in a seperate\n"
 "     file:\n"
 "       cdparanoia -B -- \"-3\"\n\n"
-"  C) extract from track 1, time 0:30.12 to 1:10.00:\n"
+"  C) extract every track, putting each track in a separate file whose name\n"
+"     is <track-number>-<track-name>.cdda.wav, informations being\n"
+"     fetch from mydb.freedb.org on port 333:\n"
+"       cdparanoia -B -l -b mydb.freedb.org:333 -k \"%C-%T\"\n\n"
+"  D) extract from track 1, time 0:30.12 to 1:10.00:\n"
 "       cdparanoia \"1[:30.12]-1[1:10]\"\n\n"
 
-"Submit bug reports to [email protected]\n\n");
+"Submit bug reports to [email protected]\n\n";
+
+static void usage(FILE *f){
+  fprintf( f, "%s", usage_str);
 }
 
 long callbegin;
@@ -585,7 +620,7 @@
     memset(dispcache,' ',graph);
 }
 
-const char *optstring = "escCn:o:O:d:g:S:prRwafvqVQhZz::YXWBi:Tt:";
+const char *optstring = "escCn:o:O:d:g:S:prRwafvqVQhZz::YXWBi:Tt:b:lk:";
 
 struct option options [] = {
 	{"stderr-progress",no_argument,NULL,'e'},
@@ -618,6 +653,9 @@
 	{"disable-fragmentation",no_argument,NULL,'F'},
 	{"output-info",required_argument,NULL,'i'},
 	{"never-skip",optional_argument,NULL,'z'},
+	{"cddb-server",required_argument,NULL,'b'},
+	{"cddb-lookup",no_argument,NULL,'l'},
+	{"cddb-output-format",required_argument,NULL,'k'},
 
 	{NULL,0,NULL,0}
 };
@@ -661,6 +699,11 @@
   int output_endian=0; /* -1=host, 0=little, 1=big */
   int query_only=0;
   int batch=0,i;
+  char *cddb_server_name = CDDBSERVER;
+  unsigned short cddb_server_port = CDDBPORT;
+  int cddb_lookup = 0;
+  char *cddb_output_format = CDDBOUTPUTFORMAT;
+  struct cddb_db_entry *e = NULL;
 
   /* full paranoia, but allow skipping */
   int paranoia_mode=PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP; 
@@ -790,6 +833,24 @@
     case 'O':
       sample_offset=atoi(optarg);
       break;
+    case 'b':
+      cddb_server_name = copystring(optarg);
+      { 
+	int i;
+	for(i=0; cddb_server_name[i] && cddb_server_name[i] != ':'; i++) ;
+	if(cddb_server_name[i] == ':')
+	  {
+	    cddb_server_name[i] = 0;
+	    cddb_server_port = atoi(cddb_server_name + i + 1);
+	  }
+      }
+      break;
+    case 'l':
+      cddb_lookup = 1;
+      break;
+    case 'k':
+      cddb_output_format = copystring(optarg);
+      break;
     default:
       usage(stderr);
       exit(1);
@@ -806,6 +867,8 @@
     }
   }else
     span=copystring(argv[optind]);
+    
+  fprintf(stderr, "%d %s %u %s\n", cddb_lookup, cddb_server_name, cddb_server_port, cddb_output_format);
 
   report(VERSION);
 
@@ -1027,6 +1090,34 @@
       int16_t offset_buffer[1176];
       int offset_buffer_used=0;
       int offset_skip=sample_offset*4;
+      struct cddb *c;
+      struct cddb_disc *cd;
+      struct cddb_query *q;
+
+      if(batch) {
+	if(cddb_lookup)
+	  c = cddb_connect(cddb_server_name, cddb_server_port);
+	else
+	  c = NULL;
+	if(c)
+	  {
+	    cddb_handshake(c, "cdparanoia", "III-a9.8-cddb0.2");
+	    cd = cddb_identify_disc(d->ioctl_fd);
+	    if(cd)
+	      {
+		q = cddb_query(c, cd);
+		if(q)
+		  e = cddb_read(c, cd, q);
+		else
+		  fprintf(stderr, "CDDB error while query : %s\n", cddb_errstr);
+	      }
+	    else
+	      fprintf(stderr, "CDDB error while identify or handshake : %s\n", cddb_errstr);
+	  }
+	else
+	  if(cddb_lookup)
+	    fprintf(stderr, "CDDB error while connect : %s\n", cddb_errstr);
+      }
 
       p=paranoia_init(d);
       paranoia_modeset(p,paranoia_mode);
@@ -1090,7 +1181,11 @@
 	      strncat(path,argv[optind+1],pos>256?256:pos);
 
 	    if(batch)
-	      snprintf(outfile_name,246,"%strack%02d.%s",path,batch_track,file);
+	      {
+		if( !(e && (batch_track-1 >= 0) && (batch_track-1 < e->nbtitles) &&
+		      ( cddb_snprintf(outfile_name, 246, cddb_output_format, e, batch_track-1, path, file) < 0) ) )
+		  snprintf(outfile_name,246,"%strack%02d.%s",path,batch_track,file);
+	      }
 	    else
 	      snprintf(outfile_name,246,"%s%s",path,file);
 
@@ -1124,7 +1219,11 @@
 	}else{
 	  /* default */
 	  if(batch)
-	    sprintf(outfile_name,"track%02d.",batch_track);
+	    {
+	      if(! (e && (batch_track-1 >= 0) && (batch_track-1 < e->nbtitles) &&
+		    (cddb_snprintf(outfile_name, 246, cddb_output_format, e, batch_track-1, NULL, ".")>0)) )
+		snprintf(outfile_name,246,"track%02d",batch_track);
+	    }
 	  else
 	    outfile_name[0]='\0';