refer: switch to ISC
[neatrefer.git] / refer.c
blob1761a0a62c91ed6851793ba5dffb989a842153e3
1 /*
2 * NEATREFER - A REFER CLONE FOR NEATROFF
4 * Copyright (C) 2011-2016 Ali Gholami Rudi <ali at rudi dot ir>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <ctype.h>
19 #include <fcntl.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
25 #define NREFS (1 << 14)
26 #define LEN(a) (sizeof(a) / sizeof((a)[0]))
28 struct ref {
29 char *keys[128]; /* reference keys */
30 char *auth[128]; /* authors */
31 int id; /* allocated reference id */
32 int nauth;
35 static struct ref refs[NREFS]; /* all references in refer database */
36 static int refs_n;
37 static struct ref *cites[NREFS]; /* cited references */
38 static int cites_n = 1;
39 static int inserted; /* number of inserted references */
40 static int multiref; /* allow specifying multiple references */
41 static int accumulate; /* accumulate all references */
42 static char *refmac; /* citation macro name */
43 static FILE *refdb; /* the database file */
45 #define ref_label(ref) ((ref)->keys['L'])
47 /* the next input line */
48 static char *lnget(void)
50 static char buf[1024];
51 return fgets(buf, sizeof(buf), stdin);
54 /* write an output line */
55 static void lnput(char *s, int n)
57 write(1, s, n >= 0 ? n : strlen(s));
60 /* the next refer database input line */
61 static char *dbget(void)
63 static char buf[1024];
64 return refdb ? fgets(buf, sizeof(buf), refdb) : NULL;
67 static char *sdup(char *s)
69 char *e = strchr(s, '\n') ? strchr(s, '\n') : strchr(s, '\0');
70 char *r;
71 int n = e - s;
72 r = malloc(n + 1);
73 memcpy(r, s, n);
74 r[n] = '\0';
75 return r;
78 /* read a single refer record */
79 static void db_ref(struct ref *ref, char *ln)
81 do {
82 if (ln[0] == '%' && ln[1] >= 'A' && ln[1] <= 'Z') {
83 char *r = ln + 2;
84 while (isspace((unsigned char) *r))
85 r++;
86 if (ln[1] == 'A')
87 ref->auth[ref->nauth++] = sdup(r);
88 else
89 ref->keys[(unsigned char) ln[1]] = sdup(r);
91 } while ((ln = dbget()) && ln[0] != '\n');
94 /* parse a refer-style bib file and fill refs[] */
95 static int db_parse(void)
97 char *ln;
98 while ((ln = dbget()))
99 if (ln[0] != '\n')
100 db_ref(&refs[refs_n++], ln);
101 return 0;
104 static char fields[] = "LTABRJDVNPITO";
105 static char fields_flag[] = "OP";
106 static char *kinds[] = {"Other", "Article", "Book", "In book", "Report"};
108 static int ref_kind(struct ref *r)
110 if (r->keys['J'])
111 return 1;
112 if (r->keys['B'])
113 return 3;
114 if (r->keys['I'])
115 return 2;
116 if (r->keys['R'])
117 return 4;
118 return 0;
121 /* print the given reference */
122 static void ref_ins(struct ref *ref, int id)
124 char buf[1 << 12];
125 char *s = buf;
126 int kind = ref_kind(ref);
127 int j;
128 s += sprintf(s, ".ds [F %d\n", id);
129 s += sprintf(s, ".]-\n");
130 if (ref->nauth) {
131 s += sprintf(s, ".ds [A ");
132 for (j = 0; j < ref->nauth; j++)
133 s += sprintf(s, "%s%s", j ? ", " : "", ref->auth[j]);
134 s += sprintf(s, "\n");
136 for (j = 'B'; j <= 'Z'; j++) {
137 char *val = ref->keys[j];
138 if (!val || !strchr(fields, j))
139 continue;
140 s += sprintf(s, ".ds [%c %s\n", j, val ? val : "");
141 if (strchr(fields_flag, j))
142 s += sprintf(s, ".nr [%c 1\n", j);
144 s += sprintf(s, ".][ %d %s\n", kind, kinds[kind]);
145 lnput(buf, s - buf);
148 /* print all references */
149 static void ref_all(void)
151 int i;
152 lnput(".]<\n", -1);
153 for (i = 1; i < cites_n; i++)
154 ref_ins(cites[i], i);
155 lnput(".]>", -1);
158 static int intcmp(void *v1, void *v2)
160 return *(int *) v1 - *(int *) v2;
163 /* the given label was referenced; add it to cites[] */
164 static int refer_seen(char *label)
166 int i;
167 for (i = 0; i < refs_n; i++)
168 if (ref_label(&refs[i]) && !strcmp(label, ref_label(&refs[i])))
169 break;
170 if (i == refs_n)
171 return -1;
172 if (!refs[i].id) {
173 refs[i].id = cites_n++;
174 cites[refs[i].id] = &refs[i];
176 return refs[i].id;
179 /* replace .[ .] macros with reference numbers */
180 static void refer_cite(char *s)
182 char msg[256];
183 char label[256];
184 int id[256];
185 int nid = 0;
186 int i = 0;
187 while (!nid || multiref) {
188 char *r = label;
189 while (*s && strchr(" \t\n,", (unsigned char) *s))
190 s++;
191 while (*s && !strchr(" \t\n,]", (unsigned char) *s))
192 *r++ = *s++;
193 *r = '\0';
194 if (!strcmp("$LIST$", label)) {
195 ref_all();
196 break;
198 id[nid] = refer_seen(label);
199 if (id[nid] < 0)
200 fprintf(stderr, "refer: <%s> not found\n", label);
201 else
202 nid++;
203 if (!*s || *s == '\n' || *s == ']')
204 break;
206 /* sort references for cleaner reference intervals */
207 qsort(id, nid, sizeof(id[0]), (void *) intcmp);
208 msg[0] = '\0';
209 while (i < nid) {
210 int beg = i++;
211 /* reading reference intervals */
212 while (i < nid && id[i] == id[i - 1] + 1)
213 i++;
214 if (beg)
215 sprintf(msg + strlen(msg), ",");
216 if (beg == i - 1)
217 sprintf(msg + strlen(msg), "%d", id[beg]);
218 else
219 sprintf(msg + strlen(msg), "%d%s%d",
220 id[beg], beg < i - 2 ? "\\-" : ",", id[i - 1]);
222 lnput(msg, -1);
223 if (!accumulate)
224 for (i = 0; i < nid; i++)
225 ref_ins(cites[id[i]], ++inserted);
228 static int startswith(char *r, char *s)
230 while (*s)
231 if (*s++ != *r++)
232 return 0;
233 return 1;
236 static int slen(char *s, int delim)
238 char *r = strchr(s, delim);
239 return r ? r - s : strchr(s, '\0') - s;
242 static void refer(void)
244 char refsig[256];
245 char *s, *r, *ln;
246 sprintf(refsig, "*[%s ", refmac ? refmac : "cite");
247 while ((ln = lnget())) {
248 if (ln[0] == '.' && ln[1] == '[') {
249 lnput(ln + 2, slen(ln + 2, '\n'));
250 if ((ln = lnget())) {
251 refer_cite(ln);
252 while (ln && (ln[0] != '.' || ln[1] != ']'))
253 ln = lnget();
254 if (ln)
255 lnput(ln + 2, -1);
257 continue;
259 s = ln;
260 r = s;
261 while ((r = strchr(r, '\\'))) {
262 r++;
263 if (!startswith(r, refsig))
264 continue;
265 if (!strchr(r, ']'))
266 continue;
267 r += strlen(refsig);
268 lnput(s, r - s);
269 refer_cite(r);
270 s = strchr(r, ']');
272 lnput(s, -1);
276 static char *usage =
277 "Usage neatrefer [options] <input >output\n"
278 "Options:\n"
279 "\t-p bib \tspecify the database file\n"
280 "\t-e \taccumulate references\n"
281 "\t-m \tmerge multiple references in a single .[/.] block\n"
282 "\t-o xy \tinline citation macro (\\*[xy label])\n";
284 int main(int argc, char *argv[])
286 int i, j;
287 for (i = 1; i < argc; i++) {
288 switch (argv[i][0] == '-' ? argv[i][1] : 'h') {
289 case 'm':
290 multiref = 1;
291 break;
292 case 'e':
293 accumulate = 1;
294 break;
295 case 'p':
296 refdb = fopen(argv[i][2] ? argv[i] + 2 : argv[++i], "r");
297 if (refdb) {
298 db_parse();
299 fclose(refdb);
301 refdb = NULL;
302 break;
303 case 'o':
304 refmac = argv[i][2] ? argv[i] + 2 : argv[++i];
305 break;
306 default:
307 printf("%s", usage);
308 return 1;
311 refer();
312 for (i = 0; i < refs_n; i++)
313 for (j = 0; j < LEN(refs[i].keys); j++)
314 if (refs[i].keys[j])
315 free(refs[i].keys[j]);
316 for (i = 0; i < refs_n; i++)
317 for (j = 0; j < LEN(refs[i].auth); j++)
318 if (refs[i].auth[j])
319 free(refs[i].auth[j]);
320 return 0;