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 char *refmac_auth
; /* author-year citation macro name */
47 static FILE *refdb
; /* the database file */
49 #define ref_label(ref) ((ref)->keys['L'])
51 /* the next input line */
52 static char *lnget(void)
54 static char buf
[1024];
55 return fgets(buf
, sizeof(buf
), stdin
);
58 /* write an output line */
59 static void lnput(char *s
, int n
)
61 write(1, s
, n
>= 0 ? n
: strlen(s
));
64 /* the next refer database input line */
65 static char *dbget(void)
67 static char buf
[1024];
68 return refdb
? fgets(buf
, sizeof(buf
), refdb
) : NULL
;
71 static char *sdup(char *s
)
73 char *e
= strchr(s
, '\n') ? strchr(s
, '\n') : strchr(s
, '\0');
82 /* format author names as J. Smith */
83 static char *ref_author(char *ref
)
90 res
= malloc(strlen(ref
) + 32);
93 while (*ref
== ' ' || *ref
== '.')
98 while (*ref
&& *ref
!= ' ' && *ref
!= '.')
102 if (islower((unsigned char) *beg
) || *ref
== '\0') {
105 } else { /* initials */
109 while (beg
< ref
&& *beg
!= '-')
111 if (*beg
== '-') /* handling J.-K. Smith */
120 /* strip excess whitespace */
121 static void rstrip(char *s
)
125 for (i
= 0; s
[i
]; i
++)
126 if (s
[i
] != ' ' && s
[i
] != '\n')
131 /* read a single refer record */
132 static void db_ref(struct ref
*ref
, char *ln
)
135 if (ln
[0] == '%' && ln
[1] >= 'A' && ln
[1] <= 'Z') {
137 while (isspace((unsigned char) *r
))
141 ref
->auth
[ref
->nauth
++] = ref_author(r
);
143 ref
->keys
[(unsigned char) ln
[1]] = sdup(r
);
146 } while ((ln
= dbget()) && ln
[0] != '\n');
149 /* parse a refer-style bib file and fill refs[] */
150 static int db_parse(void)
153 while ((ln
= dbget()))
155 db_ref(&refs
[refs_n
++], ln
);
159 static char fields
[] = "LTABERJDVNPITOH";
160 static char fields_flag
[] = "OP";
161 static char *kinds
[] = {"Other", "Article", "Book", "In book", "Report"};
163 static int ref_kind(struct ref
*r
)
176 /* print the given reference */
177 static void ref_ins(struct ref
*ref
, int id
)
181 int kind
= ref_kind(ref
);
183 s
+= sprintf(s
, ".ds [F %d\n", id
);
184 s
+= sprintf(s
, ".]-\n");
186 s
+= sprintf(s
, ".ds [A ");
187 for (j
= 0; j
< ref
->nauth
; j
++)
188 s
+= sprintf(s
, "%s%s", j
? ", " : "", ref
->auth
[j
]);
189 s
+= sprintf(s
, "\n");
191 for (j
= 'B'; j
<= 'Z'; j
++) {
192 char *val
= ref
->keys
[j
];
193 if (!val
|| !strchr(fields
, j
))
195 s
+= sprintf(s
, ".ds [%c %s\n", j
, val
? val
: "");
196 if (strchr(fields_flag
, j
))
197 s
+= sprintf(s
, ".nr [%c 1\n", j
);
199 s
+= sprintf(s
, ".][ %d %s\n", kind
, kinds
[kind
]);
203 static char *lastname(char *name
)
207 if (!islower((unsigned char) last
[0]))
209 while (*name
&& *name
!= ' ')
218 static int refcmp(struct ref
*r1
, struct ref
*r2
)
220 if (!r2
->nauth
|| (r1
->keys
['H'] && !r2
->keys
['H']))
222 if (!r1
->nauth
|| (!r1
->keys
['H'] && r2
->keys
['H']))
224 return strcmp(lastname(r1
->auth
[0]), lastname(r2
->auth
[0]));
227 /* print all references */
228 static void ref_all(void)
232 sorted
= malloc(cites_n
* sizeof(sorted
[0]));
233 memcpy(sorted
, cites
, cites_n
* sizeof(sorted
[0]));
234 if (sortall
== 'a') {
235 for (i
= 1; i
< cites_n
; i
++) {
236 for (j
= i
- 1; j
>= 0 && refcmp(cites
[i
], sorted
[j
]) < 0; j
--)
237 sorted
[j
+ 1] = sorted
[j
];
238 sorted
[j
+ 1] = cites
[i
];
242 for (i
= 0; i
< cites_n
; i
++)
243 ref_ins(sorted
[i
], sorted
[i
]->id
+ 1);
248 static int intcmp(void *v1
, void *v2
)
250 return *(int *) v1
- *(int *) v2
;
253 /* the given label was referenced; add it to cites[] */
254 static int refer_seen(char *label
)
257 for (i
= 0; i
< refs_n
; i
++)
258 if (ref_label(&refs
[i
]) && !strcmp(label
, ref_label(&refs
[i
])))
262 if (refs
[i
].id
< 0) {
263 refs
[i
].id
= cites_n
++;
264 cites
[refs
[i
].id
] = &refs
[i
];
269 static void refer_quote(char *d
, char *s
)
271 if (!strchr(s
, ' ') && s
[0] != '"') {
285 /* replace .[ .] macros with reference numbers or author-year */
286 static int refer_cite(int *id
, char *s
, int auth
)
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 (!auth
) { /* 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
]));
340 static int slen(char *s
, int delim
)
342 char *r
= strchr(s
, delim
);
343 return r
? r
- s
: strchr(s
, '\0') - s
;
346 static int refer_reqname(char *mac
, int maclen
, char *s
)
351 for (i
= 0; i
< maclen
&& *s
&& *s
!= ' '; i
++)
357 static int refer_macname(char *mac
, int maclen
, char *s
)
366 for (i
= 0; i
< maclen
&& *s
&& *s
!= ' '; i
++)
372 /* return 1 if mac is a citation macro */
373 static int refer_refmac(char *pat
, char *mac
)
375 char *s
= pat
? strstr(pat
, mac
) : NULL
;
378 return (s
== pat
|| s
[-1] == ',') &&
379 (!s
[strlen(mac
)] || s
[strlen(mac
)] == ',');
382 static void refer_insert(int *id
, int id_n
)
385 for (i
= 0; i
< id_n
; i
++)
386 ref_ins(cites
[id
[i
]], ++inserted
);
389 static void refer(void)
394 while ((ln
= lnget())) {
396 /* multi-line citations: .[ rudi17 .] */
397 if (ln
[0] == '.' && ln
[1] == '[') {
398 lnput(ln
+ 2, slen(ln
+ 2, '\n'));
399 if ((ln
= lnget())) {
400 id_n
= refer_cite(id
, ln
, 0);
401 while (ln
&& (ln
[0] != '.' || ln
[1] != ']'))
407 refer_insert(id
, id_n
);
410 /* single line citation .cite rudi17 */
411 if (ln
[0] == '.' && !refer_reqname(mac
, sizeof(mac
), ln
) &&
412 (refer_refmac(refmac
, mac
) || refer_refmac(refmac_auth
, mac
))) {
414 while (ln
[i
] && ln
[i
] != ' ')
416 while (ln
[i
] && ln
[i
] == ' ')
419 id_n
= refer_cite(id
, ln
+ i
, refer_refmac(refmac_auth
, mac
));
420 while (ln
[i
] && ln
[i
] != ' ' && ln
[i
] != '\n')
424 refer_insert(id
, id_n
);
429 /* inline citations \*[cite rudi17] */
430 while ((r
= strchr(r
, '\\'))) {
432 if (refer_macname(mac
, sizeof(mac
), r
- 1))
434 if (!refer_refmac(refmac
, mac
) && !refer_refmac(refmac_auth
, mac
))
438 r
= strchr(r
, ' ') + 1;
440 id_n
= refer_cite(id
, r
, refer_refmac(refmac_auth
, mac
));
441 while (*r
&& *r
!= ' ' && *r
!= ']')
447 refer_insert(id
, id_n
);
452 "Usage neatrefer [options] <input >output\n"
454 "\t-p bib \tspecify the database file\n"
455 "\t-e \taccumulate references\n"
456 "\t-m \tmerge multiple references in a single .[/.] block\n"
457 "\t-i \tinitials for authors' first and middle names\n"
458 "\t-o xy \tcitation macro (\\*[xy label])\n"
459 "\t-a xy \tauthor-year citation macro (\\*[xy label])\n"
460 "\t-sa \tsort by author last names\n";
462 int main(int argc
, char *argv
[])
465 for (i
= 1; i
< argc
; i
++) {
466 switch (argv
[i
][0] == '-' ? argv
[i
][1] : 'h') {
474 refdb
= fopen(argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
], "r");
482 refmac
= argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
];
488 refmac_auth
= argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
];
491 sortall
= (unsigned char) (argv
[i
][2] ? argv
[i
][2] : argv
[++i
][0]);
494 fprintf(stderr
, "%s", usage
);
498 if (refauth
&& multiref
) {
499 fprintf(stderr
, "refer: cannot use -m with -a\n");
503 for (i
= 0; i
< refs_n
; i
++)
504 for (j
= 0; j
< LEN(refs
[i
].keys
); j
++)
506 free(refs
[i
].keys
[j
]);
507 for (i
= 0; i
< refs_n
; i
++)
508 for (j
= 0; j
< LEN(refs
[i
].auth
); j
++)
510 free(refs
[i
].auth
[j
]);