Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>

char *server = "freedb.freedb.org";

int debug;
#define DPRINT if(debug)fprint
int tflag;
int Tflag;

typedef struct Track Track;
struct Track {
        int n;
        char *title;
};

enum {
        MTRACK = 64,
};

typedef struct Toc Toc;
struct Toc {
        ulong diskid;
        int ntrack;
        char *title;
        Track track[MTRACK];
};

void*
emalloc(uint n)
{
        void *p;

        p = malloc(n);
        if(p == nil)
                sysfatal("can't malloc: %r");
        memset(p, 0, n);
        return p;
}

char*
estrdup(char *s)
{
        char *t;

        t = emalloc(strlen(s)+1);
        strcpy(t, s);
        return t;
}

static void
dumpcddb(Toc *t)
{
        int i, n, s;

        print("title    %s\n", t->title);
        for(i=0; i<t->ntrack; i++){
                if(tflag){
                        n = t->track[i+1].n;
                        if(i == t->ntrack-1)
                                n *= 75;
                        s = (n - t->track[i].n)/75;
                        print("%d\t%s\t%d:%2.2d\n", i+1, t->track[i].title, s/60, s%60);
                }
                else
                        print("%d\t%s\n", i+1, t->track[i].title);
        }
        if(Tflag){
                s = t->track[i].n;
                print("Total time: %d:%2.2d\n", s/60, s%60);
        }
}

char*
append(char *a, char *b)
{
        char *c;

        c = emalloc(strlen(a)+strlen(b)+1);
        strcpy(c, a);
        strcat(c, b);
        return c;
}

static int
cddbfilltoc(Toc *t)
{
        int fd;
        int i;
        char *p, *q;
        Biobuf bin;
        char *f[10];
        int nf;
        char *id, *categ;

        fd = dial(netmkaddr(server, "tcp", "888"), 0, 0, 0);
        if(fd < 0) {
                fprint(2, "%s: %s: cannot dial: %r\n", argv0, server);
                return -1;
        }
        Binit(&bin, fd, OREAD);

        if((p=Brdline(&bin, '\n')) == nil || atoi(p)/100 != 2) {
        died:
                close(fd);
                Bterm(&bin);
                fprint(2, "%s: error talking to cddb server %s\n",
                        argv0, server);
                if(p) {
                        p[Blinelen(&bin)-1] = 0;
                        fprint(2, "%s: server says: %s\n", argv0, p);
                }
                return -1;
        }

        fprint(fd, "cddb hello gre plan9 9cd 1.0\r\n");
        if((p = Brdline(&bin, '\n')) == nil || atoi(p)/100 != 2)
                goto died;

        /*
         *      Protocol level 6 is the same as level 5 except that
         *      the character set is now UTF-8 instead of ISO-8859-1. 
         */
        fprint(fd, "proto 6\r\n");
        DPRINT(2, "proto 6\r\n");
        if((p = Brdline(&bin, '\n')) == nil || atoi(p)/100 != 2)
                goto died;
        p[Blinelen(&bin)-1] = 0;
        DPRINT(2, "cddb: %s\n", p);

        fprint(fd, "cddb query %8.8lux %d", t->diskid, t->ntrack);
        DPRINT(2, "cddb query %8.8lux %d", t->diskid, t->ntrack);
        for(i=0; i<t->ntrack; i++) {
                fprint(fd, " %d", t->track[i].n);
                DPRINT(2, " %d", t->track[i].n);
        }
        fprint(fd, " %d\r\n", t->track[t->ntrack].n);
        DPRINT(2, " %d\r\n", t->track[t->ntrack].n);

        if((p = Brdline(&bin, '\n')) == nil || atoi(p)/100 != 2)
                goto died;
        p[Blinelen(&bin)-1] = 0;
        DPRINT(2, "cddb: %s\n", p);
        nf = tokenize(p, f, nelem(f));
        if(nf < 1)
                goto died;

        switch(atoi(f[0])) {
        case 200:       /* exact match */
                if(nf < 3)
                        goto died;
                categ = f[1];
                id = f[2];
                break;
        case 210:       /* exact matches */
        case 211:       /* close matches */
                if((p = Brdline(&bin, '\n')) == nil)
                        goto died;
                if(p[0] == '.') /* no close matches? */
                        goto died;
                p[Blinelen(&bin)-1] = '\0';

                /* accept first match */
                nf = tokenize(p, f, nelem(f));
                if(nf < 2)
                        goto died;
                categ = f[0];
                id = f[1];

                /* snarf rest of buffer */
                while(p[0] != '.') {
                        if((p = Brdline(&bin, '\n')) == nil)
                                goto died;
                        p[Blinelen(&bin)-1] = '\0';
                        DPRINT(2, "cddb: %s\n", p);
                }
                break;
        case 202: /* no match */
        default:
                goto died;
        }

        t->title = "";
        for(i=0; i<t->ntrack; i++)
                t->track[i].title = "";

        /* fetch results for this cd */
        fprint(fd, "cddb read %s %s\r\n", categ, id);
        do {
                if((p = Brdline(&bin, '\n')) == nil)
                        goto died;
                q = p+Blinelen(&bin)-1;
                while(isspace(*q))
                        *q-- = 0;
DPRINT(2, "cddb %s\n", p);
                if(strncmp(p, "DTITLE=", 7) == 0)
                        t->title = append(t->title, p+7);
                else if(strncmp(p, "TTITLE", 6) == 0 && isdigit(p[6])) {
                        i = atoi(p+6);
                        if(i < t->ntrack) {
                                p += 6;
                                while(isdigit(*p))
                                        p++;
                                if(*p == '=')
                                        p++;

                                t->track[i].title = append(t->track[i].title, estrdup(p));
                        }
                } 
        } while(*p != '.');

        fprint(fd, "quit\r\n");
        close(fd);
        Bterm(&bin);

        return 0;
}

void
usage(void)
{
        fprint(2, "usage: aux/cddb [-DTt] [-s server] query diskid n ...\n");
        exits("usage");
}

void
main(int argc, char **argv)
{
        int i;
        Toc toc;

        ARGBEGIN{
        case 'D':
                debug = 1;
                break;
        case 's':
                server = EARGF(usage());
                break;
        case 'T':
                Tflag = 1;
                /*FALLTHROUGH*/
        case 't':
                tflag = 1;
                break;
        }ARGEND

        if(argc < 3 || strcmp(argv[0], "query") != 0)
                usage();

        toc.diskid = strtoul(argv[1], 0, 16);
        toc.ntrack = atoi(argv[2]);
        if(argc != 3+toc.ntrack+1)
                sysfatal("argument count does not match given ntrack");

        for(i=0; i<=toc.ntrack; i++)
                toc.track[i].n = atoi(argv[3+i]);

        if(cddbfilltoc(&toc) < 0)
                exits("whoops");

        dumpcddb(&toc);
        exits(nil);
}