/* * fref creates troff-formatted references according to specifications * given on standard in. The specification syntax is similar to that of * refer, but differs in that all identifiers are two letters instead of * one (like troff itself). This allows for a larger number of fields. * * To build fref, run the following commands: * $ lex fref.lex * $ cc -std=c89 -O2 -o fref lex.yy.c -lfl */ %x ENTRY #include #include #define MAXFIELD 255 #define FOR_FIELDS \ DO("ad",ad,e.ad) /* Access date. */ \ DO("bo",bo,e.bo) /* Book. */ \ DO("ci",ci,e.ci) /* City. */ \ DO("da",da,e.da) /* Date (year). */ \ DO("ed",ed,e.ed) /* Editor. */ \ DO("hr",hr,e.hr) /* Hypertext reference. */ \ DO("is",is,e.is) /* Issuer, publisher. */ \ DO("jo",jo,e.jo) /* Journal. */ \ DO("la",la,e.la) /* Label. */ \ DO("lc",lc,e.lc) /* Reference-specific language. */ \ DO("no",no,e.no) /* TODO: Issue number. */ \ DO("pg",pg,e.pg) /* Page number(s). */ \ DO("se",se,e.se) /* TODO: Series. */ \ DO("ti",ti,e.ti) /* Title. */ \ DO("tr",tr,e.tr) /* Translator. */ \ DO("vo",vo,e.vo) /* Volume. */ \ DO("xx",xx,e.xx) /* Extra information. */ \ /* END */ void entry(void); void field(); struct trans *gettrans(char *); int printif(char *, ...); struct trans { char *and; char *avail; char *ed; char *p; char *P; char *pp; char *Pp; char *tr; char *vo; char *Vo; }; /* Fields of current entry. */ struct entry { char au[MAXFIELD][MAXFIELD]; /* Authors. */ int a; /* Number of authors. */ #define DO(s,n,m) char n[MAXFIELD]; FOR_FIELDS #undef DO } e; extern char *optarg; extern int optind; char *name; /* Program name. */ char *lang; /* Main language (-l). */ struct trans *tglob; /* Global translation strings (-l). */ unsigned int line; /* Line at which entry begins. */ unsigned int lines; /* Total number of lines. */ unsigned int punct; /* Was punctuation printed? */ %% \n lines++; ECHO; ^%.*\n line = ++lines; field(); BEGIN(ENTRY); ^%.*\n lines++; field(); ^[^%] entry(); ECHO; BEGIN(0); <> entry(); yyterminate(); %% void main(int argc, char *argv[]) { int c; line = lines = punct = 0; lang = NULL; tglob = NULL; memset(&e, 0, sizeof(e)); name = strrchr(argv[0], '/'); if(name) name++; else name = argv[0]; while((c = getopt(argc, argv, "l:")) != -1) switch(c){ case 'l': lang = optarg; break; default: fprintf(stderr, "usage: %s [-lLANG]\n", argv[0]); exit(1); } argc -= optind; argv += optind; tglob = gettrans(lang? lang: "en"); yylex(); } /* Return pointer to translation struct. */ struct trans * gettrans(char *lg) { static struct trans *len = NULL; static struct trans *lru = NULL; static struct trans *lsv = NULL; if(strncmp(lg, "en", 2) == 0){ if(!len){ if(!(len = malloc(sizeof(struct trans)))) err(1, "malloc"); len->and = strdup("and"); len->avail = strdup("Available: "); len->ed = strdup("ed. "); len->p = strdup("p. "); len->P = strdup("P. "); len->pp = strdup("pp. "); len->Pp = strdup("Pp. "); len->tr = strdup("Trans. "); len->vo = strdup("vol. "); len->Vo = strdup("Vol. "); } return len; } if(strncmp(lg, "ru", 2) == 0){ if(!lru){ if(!(lru = malloc(sizeof(struct trans)))) err(1, "malloc"); lru->and = strdup("и"); lru->avail = strdup("Доступная: "); lru->ed = strdup("ред. "); lru->p = strdup("с. "); lru->P = strdup("С. "); lru->pp = strdup("с. "); lru->Pp = strdup("С. "); lru->tr = strdup("Перев. "); lru->vo = strdup("том "); lru->Vo = strdup("Том "); } return lru; } if(strncmp(lg, "sv", 2) == 0){ if(!lsv){ if(!(lsv = malloc(sizeof(struct trans)))) err(1, "malloc"); lsv->and = strdup("och"); lsv->avail = strdup("Tillgänglig: "); lsv->ed = strdup("red. "); lsv->p = strdup("s. "); lsv->P = strdup("S. "); lsv->pp = strdup("ss. "); lsv->Pp = strdup("Ss. "); lsv->tr = strdup("Övers. "); lsv->vo = strdup("vol. "); lsv->Vo = strdup("Vol. "); } return lsv; } if(line) fprintf(stderr, "%s: unrecognized language %s" " in reference at line %d\n", name, lg, line); else fprintf(stderr, "%s: unrecognized language %s\n", name, lg); exit(1); } /* Save entry field. */ void field() { yytext++; yytext[strcspn(yytext, "\n")] = 0; #define CMP(s,m) if(strncmp(yytext,s,2) == 0){ \ /* Don't copy empty field. */ \ if(strlen(yytext) < 3) \ return; \ yytext += 3; \ strcpy(m, yytext); \ return; \ } CMP("au",e.au[e.a++]); #define DO(s,n,m) CMP(s,m) FOR_FIELDS #undef DO fprintf(stderr, "%s: unrecognized field %%%.2s at line %d\n", name, yytext, lines); exit(1); } /* * This function prints a formatted entry with the hitherto gathered * fields. It uses the special printif formatting function (see below). */ void entry() { int i; struct trans *t; t = *e.lc? gettrans(e.lc): tglob; *e.lc = 0; /* Print label. */ printif("% = ", e.la); /* Print authors. */ for(i = 0; i < e.a-2; i++) printif("*, ", e.au[i]); if(i == e.a-2) printif("* * ", e.au[i++], t->and); if(i == e.a-1) printif("%", e.au[i]); /* Print date. */ printif(" (%)", e.da); printif(".\n"); /* Print title, book/journal. */ if(*e.bo || *e.jo){ printif("%.\n", e.ti); printif("\\fI%\\fP", e.bo? e.bo: e.jo); printif(" (*%)", t->ed, e.ed); }else{ printif("\\fI%\\fP", e.ti); } /* Print volume. */ printif(",\n*%", punct? t->Vo: t->vo, e.vo); /* Print pages, converting hyphens to en dashes. */ if(*e.pg){ if(strpbrk(e.pg, ",-")) printif(",\n*", punct? t->Pp: t->pp); else printif(",\n*", punct? t->P: t->p); for(i = 0; i < strlen(e.pg); i++){ if(e.pg[i] == '-') putchar('\\'); putchar(e.pg[i]); } *e.pg = 0; } printif(".\n"); /* Print translator. */ printif("*%.\n", t->tr, e.tr); /* Print city, issuer. */ printif("%*", e.ci, *e.is? ": ": ".\n"); printif("%.\n", e.is); /* Print other information. */ printif("%.\n", e.xx); /* Print hypertext reference. */ printif("*%\n", t->avail, e.hr); /* Print access date. */ printif("[%]\n", e.ad); /* Reset state. */ e.a = 0; #define DO(s,n,m) if(*m){ \ fprintf(stderr, "%s: unused field %%" s \ " in reference at line %d\n", \ name, line); \ *m = 0; \ } FOR_FIELDS #undef DO } /* Print formatted string if no string (* or %) is empty. */ int printif(char *fmt, ...) { char *buf, *p, **fs; int i, n, sz; va_list ap; for(n = 0, p = fmt; *p; p++) if(*p == '%' || *p == '*') n++; if(!(fs = malloc(sizeof(char *)*n))) err(1, "malloc"); va_start(ap, fmt); for(i = 0; i < n; i++){ p = va_arg(ap, char *); if(!p || !*p) return 0; fs[i] = p; } va_end(ap); /* * After use, a %-string is cleared and punct set to true if it * ended with a punctuation character (.?!). Punctuation in fmt * (.,?!) is skipped if punct is true. */ for(i = 0; *fmt; fmt++) switch(*fmt){ case '%': p = fs[i]+strlen(fs[i])-1; punct = strpbrk(p, ".?!")? 1: 0; printf("%s", fs[i]); fs[i][0] = 0; i++; break; case '*': punct = 0; printf("%s", fs[i]); i++; break; case '.': case ',': case '?': case '!': if(!punct) putchar(*fmt); punct = 0; break; case '\\': /* Don't reset punctuation for \fX escape. */ if(strncmp(fmt, "\\f", 2) == 0 && fmt[2]){ printf("%.3s", fmt); fmt += 2; break; } /* FALLTHROUGH */ default: punct = 0; putchar(*fmt); } return 1; }