#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>
typedef struct Bibent Bibent;
enum {
Nfields = 100,
Ntoks = 256,
};
#define Skipchars "{\"}"
struct Bibent {
int nfields;
char* tag[Nfields];
char* val[Nfields];
};
static char* lastln;
static char*
eatcomment(char* ln)
{
char* p;
char* sp;
if (ln == nil)
return nil;
p = strchr(ln, '%');
if (p)
*p = 0;
for(sp = ln; sp && *sp && (*sp == ' '||*sp == '\t'); sp++)
;
if (sp != ln){
sp = strdup(sp);
free(ln);
return sp;
}
return ln;
}
static char*
getln(Biobuf* bin)
{
char* ln;
if (lastln){
ln = lastln;
lastln = nil;
} else
ln = eatcomment(Brdstr(bin, '\n', 1));
return ln;
}
static void
ungetln(char* ln)
{
lastln = ln;
}
static int
skiptobib(Biobuf* bin)
{
char* ln;
while(ln = getln(bin)){
if (strchr(ln, '@'))
break;
free(ln);
}
if (ln != nil){
ungetln(ln);
return 1;
}
return 0;
}
enum {
Author = 0,
};
static struct {
char* bib;
char* ref;
} tags[] = {
"author", "%A",
"title", "%T",
"journal", "%J",
"month", "%D",
"year", "%Y",
"volume", "%V",
"number", "%N",
"pages", "%P",
"keyword", "%K",
"booktitle", "%B",
"comment", "#",
"address", "%C",
"date", "%D",
"organization", "%I",
"publisher", "%I",
"institution", "%I",
"school", "%I",
"abstract", "#",
"key", "%K",
};
static int
tagid(char* tname)
{
int i;
for (i = 0; i < nelem(tags); i++)
if (strcmp(tags[i].bib, tname) == 0)
return i;
return -1;
}
static void
printbibent(Bibent* be)
{
int i, t;
char* a;
char* s;
for(i = 0; i < be->nfields; i++){
t = tagid(be->tag[i]);
switch(t){
case Author:
a = s = be->val[i];
while(s && *s){
s = strstr(a, " and ");
if (s){
*s = 0;
s+=5;
while(*s && (*s == ' ' || *s == '\t'))
s++;
}
print("%s %s\n", tags[Author].ref, a);
a = s;
}
break;
default:
if (t < 0)
print("#%s:\t%s\n", be->tag[i], be->val[i]);
else
print("%s %s\n", tags[t].ref, be->val[i]);
}
}
print("\n");
}
static void
freebibent(Bibent* be)
{
int i;
for (i = 0; i < be->nfields; i++){
free(be->tag[i]);
free(be->val[i]);
}
free(be);
}
static char*
cleanbibval(char* old)
{
char* n;
char* np;
char* op;
if (!old)
return strdup("");
if (*old == 0)
return old;
op = old;
n = malloc(strlen(old)+1);
for(np = n; *old; old++){
if (strchr(Skipchars, *old))
continue;
*np++ = *old;
}
*np = 0;
while(np > n && (np[-1] == ' ' || np[-1] == '\t'))
*--np = 0;
if (np > n && np[-1] == ',')
*--np = 0;
free(op);
return n;
}
static int
readbibtag(Biobuf* bin, Bibent* be)
{
char* eq;
char* t;
char* ln;
char* s;
while(ln = getln(bin)){
if (strchr(ln, '@')){
ungetln(ln);
return 0;
}
eq = strchr(ln, '=');
if (!eq){
free(ln); // skip down to next tag
continue; // (a comment?)
}
for (t = ln; *t && (*t == ' ' || *t == '\t'); t++)
;
*eq++ = 0;
while(*eq && (*eq == ' ' || *eq == '\t'))
eq++;
s = strchr(t, ' ');
if (s)
*s = 0;
s = strchr(t, '\t');
if (s)
*s = 0;
for (s = t; *s; s++)
*s = tolower(*s);
be->tag[be->nfields] = strdup(t);
be->val[be->nfields] = strdup(eq);
be->nfields++;
free(ln);
return 1;
}
return 0;
}
static int
readbibfield(Biobuf* bin, Bibent* be)
{
int fid;
char* nf;
char* ln;
fid = be->nfields;
if (!readbibtag(bin, be))
return 0;
while(ln = getln(bin)){
if (strchr(ln, '@') || strchr(ln, '=')){
ungetln(ln);
goto done;
}
nf = smprint("%s %s", be->val[fid], ln);
free(be->val[fid]);
free(ln);
be->val[fid] = nf;
}
done:
be->val[fid] = cleanbibval(be->val[fid]);
return 1;
}
static Bibent*
readbibentry(Biobuf* bin, Bibent* be)
{
char* ln;
ln = getln(bin);
if (ln == nil || !strchr(ln, '@')){
freebibent(be);
return nil;
}
while(readbibfield(bin, be))
;
return be;
}
static Bibent*
readbibent(Biobuf* bin)
{
Bibent* be;
be = malloc(sizeof(Bibent));
memset(be, 0, sizeof(be));
if (!skiptobib(bin))
goto fail; // no more entries
be = readbibentry(bin, be);
return be;
fail:
freebibent(be);
return nil;
}
static void
readbibfile(Biobuf* bin)
{
Bibent* be;
while(be = readbibent(bin)){
printbibent(be);
freebibent(be);
}
}
void
main(int argc, char*argv[])
{
Biobuf* bin;
Biobuf bsin;
ARGBEGIN{
default:
fprint(2, "usage: %s file...\n", argv0);
exits("usage");
} ARGEND;
if (argc == 0){
Binit(&bsin, 0, OREAD);
readbibfile(&bsin);
Bterm(&bsin);
} else {
for(; argc--; argv++){
if((bin = Bopen(*argv, OREAD)) == nil)
sysfatal("%s cannot open - %r\n", *argv);
readbibfile(bin);
Bterm(bin);
}
}
exits(nil);
}
|