2 * NEATREFER - A REFER CLONE FOR NEATROFF
4 * Copyright (C) 2011-2017 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.
25 #define NREFS (1 << 14)
26 #define LEN(a) (sizeof(a) / sizeof((a)[0]))
29 char *keys
[128]; /* reference keys */
30 char *auth
[128]; /* authors */
31 int id
; /* allocated reference id */
35 static struct ref refs
[NREFS
]; /* all references in refer database */
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 int initials
; /* initials for authors' first name */
43 static int refauth
; /* use author-year citations */
44 static char *refmac
; /* citation macro name */
45 static FILE *refdb
; /* the database file */
47 #define ref_label(ref) ((ref)->keys['L'])
49 /* the next input line */
50 static char *lnget(void)
52 static char buf
[1024];
53 return fgets(buf
, sizeof(buf
), stdin
);
56 /* write an output line */
57 static void lnput(char *s
, int n
)
59 write(1, s
, n
>= 0 ? n
: strlen(s
));
62 /* the next refer database input line */
63 static char *dbget(void)
65 static char buf
[1024];
66 return refdb
? fgets(buf
, sizeof(buf
), refdb
) : NULL
;
69 static char *sdup(char *s
)
71 char *e
= strchr(s
, '\n') ? strchr(s
, '\n') : strchr(s
, '\0');
80 /* format author names as J. Smith */
81 static char *ref_author(char *ref
)
88 res
= malloc(strlen(ref
) + 32);
91 while (*ref
== ' ' || *ref
== '.')
96 while (*ref
&& *ref
!= ' ' && *ref
!= '.')
100 if (islower((unsigned char) *beg
) || *ref
== '\0') {
103 } else { /* initials */
107 while (beg
< ref
&& *beg
!= '-')
109 if (*beg
== '-') /* handling J.-K. Smith */
118 /* strip excess whitespace */
119 static void rstrip(char *s
)
123 for (i
= 0; s
[i
]; i
++)
124 if (s
[i
] != ' ' && s
[i
] != '\n')
129 /* read a single refer record */
130 static void db_ref(struct ref
*ref
, char *ln
)
133 if (ln
[0] == '%' && ln
[1] >= 'A' && ln
[1] <= 'Z') {
135 while (isspace((unsigned char) *r
))
139 ref
->auth
[ref
->nauth
++] = ref_author(r
);
141 ref
->keys
[(unsigned char) ln
[1]] = sdup(r
);
143 } while ((ln
= dbget()) && ln
[0] != '\n');
146 /* parse a refer-style bib file and fill refs[] */
147 static int db_parse(void)
150 while ((ln
= dbget()))
152 db_ref(&refs
[refs_n
++], ln
);
156 static char fields
[] = "LTABRJDVNPITO";
157 static char fields_flag
[] = "OP";
158 static char *kinds
[] = {"Other", "Article", "Book", "In book", "Report"};
160 static int ref_kind(struct ref
*r
)
173 /* print the given reference */
174 static void ref_ins(struct ref
*ref
, int id
)
178 int kind
= ref_kind(ref
);
180 s
+= sprintf(s
, ".ds [F %d\n", id
);
181 s
+= sprintf(s
, ".]-\n");
183 s
+= sprintf(s
, ".ds [A ");
184 for (j
= 0; j
< ref
->nauth
; j
++)
185 s
+= sprintf(s
, "%s%s", j
? ", " : "", ref
->auth
[j
]);
186 s
+= sprintf(s
, "\n");
188 for (j
= 'B'; j
<= 'Z'; j
++) {
189 char *val
= ref
->keys
[j
];
190 if (!val
|| !strchr(fields
, j
))
192 s
+= sprintf(s
, ".ds [%c %s\n", j
, val
? val
: "");
193 if (strchr(fields_flag
, j
))
194 s
+= sprintf(s
, ".nr [%c 1\n", j
);
196 s
+= sprintf(s
, ".][ %d %s\n", kind
, kinds
[kind
]);
200 /* print all references */
201 static void ref_all(void)
205 for (i
= 1; i
< cites_n
; i
++)
206 ref_ins(cites
[i
], i
);
210 static int intcmp(void *v1
, void *v2
)
212 return *(int *) v1
- *(int *) v2
;
215 /* the given label was referenced; add it to cites[] */
216 static int refer_seen(char *label
)
219 for (i
= 0; i
< refs_n
; i
++)
220 if (ref_label(&refs
[i
]) && !strcmp(label
, ref_label(&refs
[i
])))
225 refs
[i
].id
= cites_n
++;
226 cites
[refs
[i
].id
] = &refs
[i
];
231 static char *refer_lastname(char *name
)
235 if (!islower((unsigned char) last
[0]))
237 while (*name
&& *name
!= ' ')
246 static void refer_quote(char *d
, char *s
)
248 if (!strchr(s
, ' ') && s
[0] != '"') {
262 /* replace .[ .] macros with reference numbers */
263 static void refer_cite(char *s
)
271 while (!nid
|| multiref
) {
273 while (*s
&& strchr(" \t\n,", (unsigned char) *s
))
275 while (*s
&& !strchr(" \t\n,]", (unsigned char) *s
))
278 if (!strcmp("$LIST$", label
)) {
282 id
[nid
] = refer_seen(label
);
284 fprintf(stderr
, "refer: <%s> not found\n", label
);
287 if (!*s
|| *s
== '\n' || *s
== ']')
290 if (!refauth
) { /* numbered citations */
291 /* sort references for cleaner reference intervals */
292 qsort(id
, nid
, sizeof(id
[0]), (void *) intcmp
);
295 /* reading reference intervals */
296 while (i
< nid
&& id
[i
] == id
[i
- 1] + 1)
299 sprintf(msg
+ strlen(msg
), ",");
301 sprintf(msg
+ strlen(msg
), "%d", id
[beg
]);
303 sprintf(msg
+ strlen(msg
), "%d%s%d",
304 id
[beg
], beg
< i
- 2 ? "\\-" : ",", id
[i
- 1]);
306 } else if (nid
) { /* year + authors citations */
307 struct ref
*ref
= cites
[id
[0]];
308 sprintf(msg
, "%s %d", ref
->keys
['D'] ? ref
->keys
['D'] : "-", ref
->nauth
);
309 for (i
= 0; i
< ref
->nauth
; i
++) {
310 sprintf(msg
+ strlen(msg
), " ");
311 refer_quote(msg
+ strlen(msg
), refer_lastname(ref
->auth
[i
]));
316 for (i
= 0; i
< nid
; i
++)
317 ref_ins(cites
[id
[i
]], ++inserted
);
320 static int slen(char *s
, int delim
)
322 char *r
= strchr(s
, delim
);
323 return r
? r
- s
: strchr(s
, '\0') - s
;
326 static int refer_reqname(char *mac
, int maclen
, char *s
)
331 for (i
= 0; i
< maclen
&& *s
&& *s
!= ' '; i
++)
337 static int refer_macname(char *mac
, int maclen
, char *s
)
346 for (i
= 0; i
< maclen
&& *s
&& *s
!= ' '; i
++)
352 /* return 1 if mac is a citation macro */
353 static int refer_refmac(char *mac
)
355 char *s
= refmac
? strstr(refmac
, mac
) : NULL
;
358 return (s
== refmac
|| s
[-1] == ',') &&
359 (!s
[strlen(mac
)] || s
[strlen(mac
)] == ',');
362 static void refer(void)
366 while ((ln
= lnget())) {
367 /* multi-line citations: .[ rudi17 .] */
368 if (ln
[0] == '.' && ln
[1] == '[') {
369 lnput(ln
+ 2, slen(ln
+ 2, '\n'));
370 if ((ln
= lnget())) {
372 while (ln
&& (ln
[0] != '.' || ln
[1] != ']'))
379 /* single line citation .cite rudi17 */
380 if (ln
[0] == '.' && !refer_reqname(mac
, sizeof(mac
), ln
) &&
383 while (ln
[i
] && ln
[i
] != ' ')
385 while (ln
[i
] && ln
[i
] == ' ')
389 while (ln
[i
] && ln
[i
] != ' ' && ln
[i
] != '\n')
396 /* inline citations \*[cite rudi17] */
397 while ((r
= strchr(r
, '\\'))) {
399 if (refer_macname(mac
, sizeof(mac
), r
- 1))
401 if (!refer_refmac(mac
))
405 r
= strchr(r
, ' ') + 1;
408 while (*r
&& *r
!= ' ' && *r
!= ']')
417 "Usage neatrefer [options] <input >output\n"
419 "\t-p bib \tspecify the database file\n"
420 "\t-e \taccumulate references\n"
421 "\t-m \tmerge multiple references in a single .[/.] block\n"
422 "\t-i \tinitials for authors' first and middle names\n"
423 "\t-o xy \tcitation macro (\\*[xy label])\n"
424 "\t-a \tuse author-year citation style\n";
426 int main(int argc
, char *argv
[])
429 for (i
= 1; i
< argc
; i
++) {
430 switch (argv
[i
][0] == '-' ? argv
[i
][1] : 'h') {
438 refdb
= fopen(argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
], "r");
446 refmac
= argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
];
459 if (refauth
&& multiref
) {
460 fprintf(stderr
, "refer: cannot use -m with -a\n");
464 for (i
= 0; i
< refs_n
; i
++)
465 for (j
= 0; j
< LEN(refs
[i
].keys
); j
++)
467 free(refs
[i
].keys
[j
]);
468 for (i
= 0; i
< refs_n
; i
++)
469 for (j
= 0; j
< LEN(refs
[i
].auth
); j
++)
471 free(refs
[i
].auth
[j
]);