ref: 0b3f9e8300f084a030c21db509c1b31cfcc29ab5
author: Tevo <estevan.cps@gmail.com>
date: Fri Nov 20 13:24:23 EST 2020
Semifunctional cuesheet parser
--- /dev/null
+++ b/cue.c
@@ -1,0 +1,129 @@
+#include <u.h>
+#include <libc.h>
+
+#include "cuefs.h"
+#include "y.tab.h"
+
+Timestamp
+parsetime(int min, int sec, int frames)
+{
+ sec += min*60;
+ frames += sec*75;
+
+ return (Timestamp){frames};
+}
+
+Cuesheet*
+newsheet(void)
+{
+ return emallocz(sizeof(Cuesheet), 1);
+}
+
+AFile*
+lastfile(Cuesheet *c)
+{
+ if(c->files == nil)
+ return nil;
+
+ if(c->curfile == nil)
+ c->curfile = c->files;
+
+ while(c->curfile->next != nil)
+ c->curfile = c->curfile->next;
+
+ return c->curfile;
+}
+
+Entry*
+lastentry(Cuesheet *c)
+{
+ if(c->entries == nil)
+ return nil;
+
+ if(c->curentry == nil)
+ c->curentry = c->entries;
+
+ while(c->curentry->next != nil)
+ c->curentry = c->curentry->next;
+
+ return c->curentry;
+}
+
+void
+atleast(Timestamps *ts, int val)
+{
+ if(ts->maxindex > val)
+ return;
+ ts->starts = erealloc(ts->starts, (val+1) * sizeof(*ts->starts));
+ ts->maxindex = val;
+}
+
+void
+setperformer(Cuesheet *c, char *artist)
+{
+ artist = strdup(artist);
+ if(c->curentry == nil)
+ setstr(nil, &c->performer, artist);
+ else
+ setstr(c->performer, &c->curentry->performer, artist);
+}
+
+void
+settitle(Cuesheet *c, char *title)
+{
+ title = strdup(title);
+ if(c->curentry == nil)
+ setstr(nil, &c->title, title);
+ else
+ setstr(nil, &c->curentry->title, title);
+}
+
+void
+addfile(Cuesheet *c, char *name, int format)
+{
+ AFile *new;
+
+ lastfile(c);
+
+ new = emalloc(sizeof(*new));
+ new->name = strdup(name);
+ new->type = format;
+ new->next = nil;
+
+ if(c->files == nil)
+ c->files = new;
+ else
+ c->curfile->next = new;
+
+ c->curfile = new;
+}
+
+void
+addnewtrack(Cuesheet *c, int i)
+{
+ Entry *new;
+
+ lastentry(c);
+
+ new = emallocz(sizeof(*new), 1);
+ new->file = c->curfile;
+ new->performer = c->performer;
+ new->index = i;
+
+ if(c->entries == nil)
+ c->entries = new;
+ else
+ c->curentry->next = nil;
+
+ c->curentry = new;
+}
+
+void
+settimestamp(Cuesheet *c, int i, Timestamp t)
+{
+ if(c->curentry == nil)
+ parserfatal("timestamp outside of track");
+
+ atleast(c->curentry, i);
+ c->curentry->starts[i] = t;
+}
--- /dev/null
+++ b/cue.l
@@ -1,0 +1,51 @@
+%{
+#include "cuefs.h"
+#include "y.tab.h"
+
+%}
+
+%%
+\".*\" {
+ yylval.str = strdup(yytext+1);
+
+ if(yylval.str[yyleng-2] != '"')
+ parserwarn("improperly terminated string");
+ else
+ yylval.str[yyleng-2] = '\0';
+
+ return STRING;
+ }
+
+[0-9]+ {
+ yylval.i = atoi(yytext);
+ return INTEGER;
+ }
+
+"CATALOG" return CATALOG;
+"CDTEXTFILE" return CDTEXTFILE;
+"FILE" return FILE;
+"FLAGS" return FLAGS;
+"DCP" return DCP;
+"4CH" return CHAN4;
+"PRE" return PREEMPH;
+"SCMS" return SCMS;
+"INDEX" return INDEX;
+"ISRC" return ISRC;
+"PERFORMER" return PERFORMER;
+"POSTGAP" return POSTGAP;
+"PREGAP" return PREGAP;
+"SONGWRITER" return SONGWRITER;
+"TITLE" return TITLE;
+"TRACK" return TRACK;
+"WAVE" return FWAVE;
+"MP3" return FMP3;
+"AIFF" return FAIFF;
+"BINARY" return FBINARY;
+"MOTOROLA" return FMOTOROLA;
+"AUDIO" return AUDIO;
+
+[:\n] return *yytext;
+
+"REM".*\n ;
+[ \t]+ ;
+%%
--- /dev/null
+++ b/cue.y
@@ -1,0 +1,49 @@
+%{
+#include <u.h>
+#include <libc.h>
+
+#include "cuefs.h"
+%}
+
+%union
+{
+ int i;
+ char *str;
+ Timestamp time;
+}
+
+%token <i> INTEGER
+%token <str> STRING
+
+%type <i> filetype
+%type <time> timestamp
+
+%token CATALOG CDTEXTFILE FLAGS DCP CHAN4 PREEMPH SCMS INDEX
+%token ISRC PERFORMER POSTGAP PREGAP SONGWRITER TITLE TRACK
+%token FILE FWAVE FMP3 FAIFF FBINARY FMOTOROLA AUDIO
+
+%%
+cuesheet:
+ | expr '\n' cuesheet
+ ;
+
+expr:
+ | PERFORMER STRING { setperformer(cursheet, $2); }
+ | TITLE STRING { settitle(cursheet, $2); }
+ | FILE STRING filetype { addfile(cursheet, $2, $3); }
+ | TRACK INTEGER AUDIO { addnewtrack(cursheet, $2); }
+ | INDEX INTEGER timestamp { settimestamp(cursheet, $2, $3); }
+ ;
+
+filetype:
+ FWAVE { $$ = WAVE; }
+ | FMP3 { $$ = MP3; }
+ | FAIFF { $$ = AIFF; }
+ | FBINARY { $$ = BINARY; }
+ | FMOTOROLA { $$ = MOTOROLA; }
+ ;
+
+timestamp:
+ INTEGER ':' INTEGER ':' INTEGER { $$ = parsetime($1, $3, $5); }
+ ;
+%%
--- /dev/null
+++ b/cuefs.h
@@ -1,0 +1,72 @@
+
+extern char *fname;
+extern int infd;
+
+/* lex/yacc */
+int yylex(void);
+int yyparse(void);
+void yyerror(char*);
+
+/* misc.c */
+void* erealloc(void*, ulong);
+void* emallocz(ulong, int);
+void* emalloc(ulong);
+
+char* setstr(char*, char**, char*);
+
+void parserwarn(char*, ...);
+void parserfatal(char*, ...);
+
+/*****/
+
+enum
+{
+ WAVE, MP3, AIFF, BINARY, MOTOROLA
+};
+
+typedef struct
+{
+ u64int frames;
+} Timestamp;
+
+typedef struct AFile
+{
+ char *name;
+ int type;
+ struct AFile *next;
+} AFile;
+
+typedef struct
+{
+ u8int maxindex;
+ Timestamp *starts;
+} Timestamps;
+
+typedef struct Entry
+{
+ Timestamps;
+ int index;
+ AFile *file;
+ char *title, *performer;
+ Timestamp start;
+ struct Entry *next;
+} Entry;
+
+typedef struct
+{
+ char *title, *performer;
+ AFile *files, *curfile;
+ Entry *entries, *curentry;
+} Cuesheet;
+
+extern Cuesheet *cursheet;
+
+Timestamp parsetime(int, int, int);
+
+Cuesheet* newsheet(void);
+
+void setperformer(Cuesheet*, char*);
+void settitle(Cuesheet*, char*);
+void addfile(Cuesheet*, char*, int);
+void addnewtrack(Cuesheet*, int);
+void settimestamp(Cuesheet*, int, Timestamp);
--- /dev/null
+++ b/fs.c
@@ -1,0 +1,7 @@
+#include <u.h>
+#include <libc.h>
+
+#include "cuefs.h"
+#include "y.tab.h"
+
+Cuesheet *cursheet;
--- /dev/null
+++ b/main.c
@@ -1,0 +1,38 @@
+#include <u.h>
+#include <libc.h>
+
+#include "cuefs.h"
+
+char *fname = "<stdin>";
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ ARGBEGIN {
+ default:
+ usage();
+ } ARGEND;
+
+ if(argc > 1)
+ usage();
+
+ if(argc == 1)
+ {
+ fname = argv[0];
+ infd = open(fname, OREAD);
+ }
+
+ cursheet = newsheet();
+ yyparse();
+
+ close(infd);
+
+ exits(0);
+}
--- /dev/null
+++ b/misc.c
@@ -1,0 +1,78 @@
+#include <u.h>
+#include <libc.h>
+
+#include "cuefs.h"
+#include "y.tab.h"
+
+void*
+erealloc(void *p, ulong s)
+{
+ p = realloc(p, s);
+ if(s > 0 && p == nil)
+ sysfatal("realloc: %r");
+ setrealloctag(p, getcallerpc(&p));
+ return p;
+}
+
+void*
+emallocz(ulong s, int clr)
+{
+ void* p = mallocz(s, clr);
+ if(p == nil)
+ sysfatal("mallocz: %r");
+ setmalloctag(p, getcallerpc(&s));
+ return p;
+}
+
+void*
+emalloc(ulong s)
+{
+ void* p = malloc(s);
+ if(p == nil)
+ sysfatal("malloc: %r");
+ setmalloctag(p, getcallerpc(&s));
+ return p;
+}
+
+void
+yyerror(char *str)
+{
+ extern int yylineno;
+ fprint(2, "%s:%d: %s\n", fname, yylineno, str);
+}
+
+char*
+setstr(char *baseline, char **dest, char *str)
+{
+ if(*dest != nil && *dest != baseline)
+ free(*dest);
+ *dest = str;
+ return str;
+}
+
+void
+parserwarn(char *fmt, ...)
+{
+ char *str;
+ va_list args;
+
+ va_start(args, fmt);
+ str = vsmprint(fmt, args);
+ yyerror(str);
+ free(str);
+ va_end(args);
+}
+
+void
+parserfatal(char *fmt, ...)
+{
+ char *str;
+ va_list args;
+
+ va_start(args, fmt);
+ str = vsmprint(fmt, args);
+ yyerror(str);
+ free(str);
+ va_end(args);
+ exits("cantparse");
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,30 @@
+</$objtype/mkfile
+
+LFILES=\
+ cue.l
+
+YFILES=\
+ cue.y
+
+OBJ=\
+ lex.yy.$O \
+ y.tab.$O \
+ main.$O \
+ misc.$O \
+ cue.$O \
+ fs.$O
+
+%.$O: %.c
+ $CC $CFLAGS $prereq
+
+$O.out: $OBJ
+ $LD $prereq
+
+lex.yy.c: $LFILES y.tab.h
+ $LEX -9 $LFILES
+
+y.tab.c y.tab.h: $YFILES
+ $YACC -d $YFILES
+
+clean nuke:V:
+ rm -f lex.yy.c y.debug y.tab.[ch] *.[$OS] [$OS].*