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 */
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 int sortall
; /* sort references */
45 static char *refmac
; /* citation macro name */
46 static FILE *refdb
; /* the database file */
48 #define ref_label(ref) ((ref)->keys['L'])
50 /* the next input line */
51 static char *lnget(void)
53 static char buf
[1024];
54 return fgets(buf
, sizeof(buf
), stdin
);
57 /* write an output line */
58 static void lnput(char *s
, int n
)
60 write(1, s
, n
>= 0 ? n
: strlen(s
));
63 /* the next refer database input line */
64 static char *dbget(void)
66 static char buf
[1024];
67 return refdb
? fgets(buf
, sizeof(buf
), refdb
) : NULL
;
70 static char *sdup(char *s
)
72 char *e
= strchr(s
, '\n') ? strchr(s
, '\n') : strchr(s
, '\0');
81 /* format author names as J. Smith */
82 static char *ref_author(char *ref
)
89 res
= malloc(strlen(ref
) + 32);
92 while (*ref
== ' ' || *ref
== '.')
97 while (*ref
&& *ref
!= ' ' && *ref
!= '.')
101 if (islower((unsigned char) *beg
) || *ref
== '\0') {
104 } else { /* initials */
108 while (beg
< ref
&& *beg
!= '-')
110 if (*beg
== '-') /* handling J.-K. Smith */
119 /* strip excess whitespace */
120 static void rstrip(char *s
)
124 for (i
= 0; s
[i
]; i
++)
125 if (s
[i
] != ' ' && s
[i
] != '\n')
130 /* read a single refer record */
131 static void db_ref(struct ref
*ref
, char *ln
)
134 if (ln
[0] == '%' && ln
[1] >= 'A' && ln
[1] <= 'Z') {
136 while (isspace((unsigned char) *r
))
140 ref
->auth
[ref
->nauth
++] = ref_author(r
);
142 ref
->keys
[(unsigned char) ln
[1]] = sdup(r
);
145 } while ((ln
= dbget()) && ln
[0] != '\n');
148 /* parse a refer-style bib file and fill refs[] */
149 static int db_parse(void)
152 while ((ln
= dbget()))
154 db_ref(&refs
[refs_n
++], ln
);
158 static char fields
[] = "LTABERJDVNPITOH";
159 static char fields_flag
[] = "OP";
160 static char *kinds
[] = {"Other", "Article", "Book", "In book", "Report"};
162 static int ref_kind(struct ref
*r
)
175 /* print the given reference */
176 static void ref_ins(struct ref
*ref
, int id
)
180 int kind
= ref_kind(ref
);
182 s
+= sprintf(s
, ".ds [F %d\n", id
);
183 s
+= sprintf(s
, ".]-\n");
185 s
+= sprintf(s
, ".ds [A ");
186 for (j
= 0; j
< ref
->nauth
; j
++)
187 s
+= sprintf(s
, "%s%s", j
? ", " : "", ref
->auth
[j
]);
188 s
+= sprintf(s
, "\n");
190 for (j
= 'B'; j
<= 'Z'; j
++) {
191 char *val
= ref
->keys
[j
];
192 if (!val
|| !strchr(fields
, j
))
194 s
+= sprintf(s
, ".ds [%c %s\n", j
, val
? val
: "");
195 if (strchr(fields_flag
, j
))
196 s
+= sprintf(s
, ".nr [%c 1\n", j
);
198 s
+= sprintf(s
, ".][ %d %s\n", kind
, kinds
[kind
]);
202 static char *lastname(char *name
)
206 if (!islower((unsigned char) last
[0]))
208 while (*name
&& *name
!= ' ')
217 static int refcmp(struct ref
*r1
, struct ref
*r2
)
219 if (!r2
->nauth
|| (r1
->keys
['H'] && !r2
->keys
['H']))
221 if (!r1
->nauth
|| (!r1
->keys
['H'] && r2
->keys
['H']))
223 return strcmp(lastname(r1
->auth
[0]), lastname(r2
->auth
[0]));
226 /* print all references */
227 static void ref_all(void)
231 sorted
= malloc(cites_n
* sizeof(sorted
[0]));
232 memcpy(sorted
, cites
, cites_n
* sizeof(sorted
[0]));
233 if (sortall
== 'a') {
234 for (i
= 1; i
< cites_n
; i
++) {
235 for (j
= i
- 1; j
>= 0 && refcmp(cites
[i
], sorted
[j
]) < 0; j
--)
236 sorted
[j
+ 1] = sorted
[j
];
237 sorted
[j
+ 1] = cites
[i
];
241 for (i
= 0; i
< cites_n
; i
++)
242 ref_ins(sorted
[i
], sorted
[i
]->id
+ 1);
247 static int intcmp(void *v1
, void *v2
)
249 return *(int *) v1
- *(int *) v2
;
252 /* the given label was referenced; add it to cites[] */
253 static int refer_seen(char *label
)
256 for (i
= 0; i
< refs_n
; i
++)
257 if (ref_label(&refs
[i
]) && !strcmp(label
, ref_label(&refs
[i
])))
261 if (refs
[i
].id
< 0) {
262 refs
[i
].id
= cites_n
++;
263 cites
[refs
[i
].id
] = &refs
[i
];
268 static void refer_quote(char *d
, char *s
)
270 if (!strchr(s
, ' ') && s
[0] != '"') {
284 /* replace .[ .] macros with reference numbers */
285 static void refer_cite(char *s
)
293 while (!nid
|| multiref
) {
295 while (*s
&& strchr(" \t\n,", (unsigned char) *s
))
297 while (*s
&& !strchr(" \t\n,]", (unsigned char) *s
))
300 if (!strcmp("$LIST$", label
)) {
304 id
[nid
] = refer_seen(label
);
306 fprintf(stderr
, "refer: <%s> not found\n", label
);
309 if (!*s
|| *s
== '\n' || *s
== ']')
312 if (!refauth
) { /* numbered citations */
313 /* sort references for cleaner reference intervals */
314 qsort(id
, nid
, sizeof(id
[0]), (void *) intcmp
);
317 /* reading reference intervals */
318 while (i
< nid
&& id
[i
] == id
[i
- 1] + 1)
321 sprintf(msg
+ strlen(msg
), ",");
323 sprintf(msg
+ strlen(msg
), "%d", id
[beg
] + 1);
325 sprintf(msg
+ strlen(msg
), "%d%s%d",
326 id
[beg
] + 1, beg
< i
- 2 ? "\\-" : ",", id
[i
- 1] + 1);
328 } else if (nid
) { /* year + authors citations */
329 struct ref
*ref
= cites
[id
[0]];
330 sprintf(msg
, "%s %d", ref
->keys
['D'] ? ref
->keys
['D'] : "-", ref
->nauth
);
331 for (i
= 0; i
< ref
->nauth
; i
++) {
332 sprintf(msg
+ strlen(msg
), " ");
333 refer_quote(msg
+ strlen(msg
), lastname(ref
->auth
[i
]));
338 for (i
= 0; i
< nid
; i
++)
339 ref_ins(cites
[id
[i
]], ++inserted
);
342 static int slen(char *s
, int delim
)
344 char *r
= strchr(s
, delim
);
345 return r
? r
- s
: strchr(s
, '\0') - s
;
348 static int refer_reqname(char *mac
, int maclen
, char *s
)
353 for (i
= 0; i
< maclen
&& *s
&& *s
!= ' '; i
++)
359 static int refer_macname(char *mac
, int maclen
, char *s
)
368 for (i
= 0; i
< maclen
&& *s
&& *s
!= ' '; i
++)
374 /* return 1 if mac is a citation macro */
375 static int refer_refmac(char *mac
)
377 char *s
= refmac
? strstr(refmac
, mac
) : NULL
;
380 return (s
== refmac
|| s
[-1] == ',') &&
381 (!s
[strlen(mac
)] || s
[strlen(mac
)] == ',');
384 static void refer(void)
388 while ((ln
= lnget())) {
389 /* multi-line citations: .[ rudi17 .] */
390 if (ln
[0] == '.' && ln
[1] == '[') {
391 lnput(ln
+ 2, slen(ln
+ 2, '\n'));
392 if ((ln
= lnget())) {
394 while (ln
&& (ln
[0] != '.' || ln
[1] != ']'))
401 /* single line citation .cite rudi17 */
402 if (ln
[0] == '.' && !refer_reqname(mac
, sizeof(mac
), ln
) &&
405 while (ln
[i
] && ln
[i
] != ' ')
407 while (ln
[i
] && ln
[i
] == ' ')
411 while (ln
[i
] && ln
[i
] != ' ' && ln
[i
] != '\n')
418 /* inline citations \*[cite rudi17] */
419 while ((r
= strchr(r
, '\\'))) {
421 if (refer_macname(mac
, sizeof(mac
), r
- 1))
423 if (!refer_refmac(mac
))
427 r
= strchr(r
, ' ') + 1;
430 while (*r
&& *r
!= ' ' && *r
!= ']')
439 "Usage neatrefer [options] <input >output\n"
441 "\t-p bib \tspecify the database file\n"
442 "\t-e \taccumulate references\n"
443 "\t-m \tmerge multiple references in a single .[/.] block\n"
444 "\t-i \tinitials for authors' first and middle names\n"
445 "\t-o xy \tcitation macro (\\*[xy label])\n"
446 "\t-a \tuse author-year citation style\n"
447 "\t-sa \tsort by author last names\n";
449 int main(int argc
, char *argv
[])
452 for (i
= 1; i
< argc
; i
++) {
453 switch (argv
[i
][0] == '-' ? argv
[i
][1] : 'h') {
461 refdb
= fopen(argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
], "r");
469 refmac
= argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
];
478 sortall
= (unsigned char) (argv
[i
][2] ? argv
[i
][2] : argv
[++i
][0]);
485 if (refauth
&& multiref
) {
486 fprintf(stderr
, "refer: cannot use -m with -a\n");
490 for (i
= 0; i
< refs_n
; i
++)
491 for (j
= 0; j
< LEN(refs
[i
].keys
); j
++)
493 free(refs
[i
].keys
[j
]);
494 for (i
= 0; i
< refs_n
; i
++)
495 for (j
= 0; j
< LEN(refs
[i
].auth
); j
++)
497 free(refs
[i
].auth
[j
]);