tapas

[discontinued] A small program used for compiling refer output into the APA reference format.
git clone https://noxz.tech/git/tapas.git
Log | Files | README | LICENSE

tapas.c
1/*
2 * MIT License
3 *
4 * © 2019 Chris Noxz <chris@noxz.tech>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include "config.h"
29
30#define BIB_START   ".]<\n"
31#define BIB_END     ".]>\n"
32#define REF_START   ".]-\n"
33#define REF_END     ".][ %d"
34#define REF_ATTR    ".ds [%c"
35#define STR_HE      ".ds APA_HE "
36#define STR_AN      ".ds APA_AN "
37#define STR_IN      ".ds APA_IN "
38#define STR_FR      ".ds APA_FR "
39#define STR_RE      ".ds APA_RE "
40#define STR_MS      ".ds APA_MS "
41#define STR_HC      ".ds APA_HC "
42
43typedef struct {
44    char author[BUF_SIZE];
45    char title[BUF_SIZE];
46    char book_title[BUF_SIZE];
47    char report_number[BUF_SIZE];
48    char journal_name[BUF_SIZE];
49    char editor[BUF_SIZE];
50    char edition[BUF_SIZE];
51    char volume[BUF_SIZE];
52    char journal_number[BUF_SIZE];
53    char series[BUF_SIZE];
54    char city[BUF_SIZE];
55    char publisher[BUF_SIZE];
56    char publication_date[BUF_SIZE];
57    char page_number[BUF_SIZE];
58    char gov_number[BUF_SIZE];
59    char other[BUF_SIZE];
60    char keywords[BUF_SIZE];
61    char original_pub_date[BUF_SIZE];
62    char additions[BUF_SIZE];
63    char reprint_title[BUF_SIZE];
64    char translator[BUF_SIZE];
65    char translator_editor[BUF_SIZE];
66    char site_name[BUF_SIZE];
67    char site_content[BUF_SIZE];
68    char organization[BUF_SIZE];
69    char url[BUF_SIZE];
70    char url_raw[BUF_SIZE];
71} Referece;
72
73struct State {
74    int inbib;
75    Referece *ref;
76};
77struct State state;
78
79struct Settings {
80    int macroset;
81    char heading[BUF_SIZE];
82    char and[BUF_SIZE];
83    char in[BUF_SIZE];
84    char from[BUF_SIZE];
85    char retrieved[BUF_SIZE];
86    char hrefcolor[BUF_SIZE];
87    char ms[BUF_SIZE];
88};
89struct Settings settings;
90
91enum macrosets {
92    ms_ms,
93//    ms_mom,
94    /* add more macro sets here... */
95};
96
97static void trim(char*);
98static int readstr(const char*, const char*, char*, int);
99static int readattr(const char*, const char*, char*, char*, int);
100static void setattr(char, char*);
101static void printref(int);
102static int matchcount(char*, const char*);
103static void findreplace(char*, const char*, const char*, int);
104static void wordsym();
105static void format_other();
106static void format_article();
107static void format_book();
108static void format_article_in_book();
109static int loadstr(char*);
110static void beginbib(void);
111
112void
113trim(char *source)
114{
115    char *ptr = source;
116
117    while (*ptr == ' ')
118        ptr++;
119    memmove(source, ptr, strlen(ptr) + 1);
120
121    ptr = source + strlen(source) - 1;
122
123    while (*ptr == ' ')
124        ptr--;
125    *(ptr + 1) = 0;
126
127    /* also, remove surounding quotes if existing */
128    if (source[0] == '"' && source[strlen(source) - 1] == '"') {
129        ptr = source + strlen(source) - 2;
130        memmove(source, source + 1, strlen(source));
131        *ptr = 0;
132    }
133}
134
135int
136readstr(const char *src, const char *pre, char *str, int length)
137{
138    int c = 0;
139    const char *p;
140
141    if (strncmp(src, pre, strlen(pre)) != 0)
142        return 0;
143
144    memset(str, 0, length);
145    p = (src + strlen(pre));
146
147    while (*p != '\n' && c < length)
148        str[c++] = *p++;
149
150    return 1;
151}
152
153int
154readattr(const char *src, const char *fmt, char *t, char *val, int length)
155{
156    int c = 0;
157    const char *p;
158
159    if (sscanf(src, fmt, t) == 0)
160        return 0;
161
162    memset(val, 0, length);
163    p = (src + strlen(fmt));
164
165    while (*p != '\n' && c < length)
166        val[c++] = *p++;
167
168    return 1;
169}
170
171void
172setattr(char type, char *val)
173{
174    switch (type) {
175    case 'A': strncpy(state.ref->author, val, BUF_SIZE); break;
176    case 'T': strncpy(state.ref->title, val, BUF_SIZE); break;
177    case 'B': strncpy(state.ref->book_title, val, BUF_SIZE); break;
178    case 'R': strncpy(state.ref->report_number, val, BUF_SIZE); break;
179    case 'J': strncpy(state.ref->journal_name, val, BUF_SIZE); break;
180    case 'E': strncpy(state.ref->editor, val, BUF_SIZE); break;
181    case 'e': strncpy(state.ref->edition, val, BUF_SIZE); break;
182    case 'V': strncpy(state.ref->volume, val, BUF_SIZE); break;
183    case 'N': strncpy(state.ref->journal_number, val, BUF_SIZE); break;
184    case 'S': strncpy(state.ref->series, val, BUF_SIZE); break;
185    case 'C': strncpy(state.ref->city, val, BUF_SIZE); break;
186    case 'I': strncpy(state.ref->publisher, val, BUF_SIZE); break;
187    case 'D': strncpy(state.ref->publication_date, val, BUF_SIZE); break;
188    case 'P': strncpy(state.ref->page_number, val, BUF_SIZE); break;
189    case 'G': strncpy(state.ref->gov_number, val, BUF_SIZE); break;
190    case 'O': strncpy(state.ref->other, val, BUF_SIZE); break;
191    case 'K': strncpy(state.ref->keywords, val, BUF_SIZE); break;
192    case 'd': strncpy(state.ref->original_pub_date, val, BUF_SIZE); break;
193    case 'a': strncpy(state.ref->additions, val, BUF_SIZE); break;
194    case 't': strncpy(state.ref->reprint_title, val, BUF_SIZE); break;
195    case 'l': strncpy(state.ref->translator, val, BUF_SIZE); break;
196    case 'r': strncpy(state.ref->translator_editor, val, BUF_SIZE); break;
197    case 's': strncpy(state.ref->site_name, val, BUF_SIZE); break;
198    case 'c': strncpy(state.ref->site_content, val, BUF_SIZE); break;
199    case 'o': strncpy(state.ref->organization, val, BUF_SIZE); break;
200    case 'u': strncpy(state.ref->url, val, BUF_SIZE);
201              strncpy(state.ref->url_raw, val, BUF_SIZE); break;
202    }
203}
204
205void
206printref(int type)
207{
208    wordsym();
209    switch (type) {
210    case 0: format_other(); break;
211    case 1: format_article(); break;
212    case 2: format_book(); break;
213    case 3: format_article_in_book(); break;
214    }
215
216    free(state.ref);
217    state.ref = NULL;
218}
219
220int
221matchcount(char *source, const char *find)
222{
223    int i, j, m, c;
224    int slen = strlen(source),
225        flen = strlen(find);
226
227    for (i = 0, c = 0; i <= slen - flen; i++) {
228        for (j = 0, m = 1; j < flen; j++) {
229            if (source[i + j] != find[j] && (m = 0) == 0)
230                break;
231        }
232        if (m == 1)
233            c++;
234    }
235    return c;
236}
237
238void
239findreplace(char *source, const char *find, const char *replace, int start)
240{
241    char *dest;
242    char *ptr;
243    int count = matchcount(source + start, find);
244    int size = (strlen(source) + (strlen(replace) - strlen(find)) * count + 1);
245    int i;
246
247    dest = malloc(size);
248    strcpy(dest, source);
249    ptr = dest + start;
250
251    for (i = 0; i < count; i++) {
252        if ((ptr = strstr(ptr, find))) {
253            memmove(
254                ptr + strlen(replace),
255                ptr + strlen(find),
256                strlen(ptr+strlen(find)) + 1
257            );
258            strncpy(ptr, replace, strlen(replace));
259        }
260        ptr++;
261    }
262
263    *(source + strlen(dest)) = 0;
264    strncpy(source, dest, strlen(dest));
265}
266
267void
268wordsym()
269{
270    char str_and[BUF_SIZE];
271
272    snprintf(str_and, BUF_SIZE, " %s ", settings.and);
273
274    if (state.ref->author[0])
275        findreplace(state.ref->author, " and ", str_and, 0);
276    if (state.ref->editor[0])
277        findreplace(state.ref->editor, " and ", str_and, 0);
278}
279
280void /* TODO :: Check type of other, website etc. */
281format_other()
282{
283    char *proto;
284
285    if (!state.ref->author
286        || !state.ref->publication_date
287        || !state.ref->additions    /* access date */
288        || !state.ref->site_name
289        || !state.ref->url)
290        return;
291
292    proto = strstr(state.ref->url, "://");
293    findreplace(state.ref->url, "/", "/\\:\\%",
294        proto ? (proto - state.ref->url) + 3: 0);
295    switch (settings.macroset) {
296    case ms_ms:
297        fprintf(stdout, ".rm PDFHREF.TEXT.COLOUR\n");
298        fprintf(stdout, ".ds PDFHREF.TEXT.COLOUR apa:h\n");
299        fprintf(stdout,
300            "%s%s\n.XP\n%s (%s). \n.I \"%s\" \".\"\n%s %s, %s \n"
301            ".pdfhref W -D \"%s\" -A \"\\c\" -- \"%s\"\n.\n",
302            ".defcolor apa:h ",
303            settings.hrefcolor,
304            state.ref->author,
305            state.ref->publication_date,
306            state.ref->site_name,
307            settings.retrieved,
308            state.ref->additions,
309            settings.from,
310            state.ref->url_raw,
311            state.ref->url
312        );
313        fprintf(stdout, ".als PDFHREF.TEXT.COLOUR PDFHREF.TEXT.COLOR\n"); break;
314        break;
315    }
316}
317
318void
319format_article()
320{
321    if (!state.ref->author
322        || !state.ref->publication_date
323        || !state.ref->title
324        || !state.ref->journal_name
325        || !state.ref->journal_number
326        || !state.ref->page_number)
327        return;
328    if (!state.ref->other)
329        strncpy(state.ref->other, "", BUF_SIZE);
330    switch (settings.macroset) {
331    case ms_ms:
332        fprintf(stdout,
333            ".XP\n%s (%s). %s.\n.I \"%s\" \", %s, %s. %s\"\n",
334            state.ref->author,
335            state.ref->publication_date,
336            state.ref->title,
337            state.ref->journal_name,
338            state.ref->journal_number,
339            state.ref->page_number,
340            state.ref->other /* assume doi to be stored in other */
341        ); break;
342    }
343}
344
345void
346format_book()
347{
348    if (!state.ref->author
349        || !state.ref->publication_date
350        || !state.ref->title
351        || !state.ref->city
352        || !state.ref->publisher)
353        return;
354    switch (settings.macroset) {
355    case ms_ms:
356    fprintf(stdout,
357        ".XP\n%s (%s).\n.I \"%s\" .\n%s: %s.\n",
358        state.ref->author,
359        state.ref->publication_date,
360        state.ref->title,
361        state.ref->city,
362        state.ref->publisher
363        ); break;
364    }
365}
366
367void
368format_article_in_book()
369{
370    if (!state.ref->author
371        || !state.ref->publication_date
372        || !state.ref->title
373        || !state.ref->editor
374        || !state.ref->book_title
375        || !state.ref->page_number
376        || !state.ref->city
377        || !state.ref->publisher)
378        return;
379    switch (settings.macroset) {
380    case ms_ms:
381    fprintf(stdout,
382        ".XP\n%s (%s). %s. %s %s, \n.I \"%s\" \" (s. %s).\"\n%s: %s.\n",
383        state.ref->author,
384        state.ref->publication_date,
385        state.ref->title,
386        settings.in,
387        state.ref->editor,
388        state.ref->book_title,
389        state.ref->page_number,
390        state.ref->city,
391        state.ref->publisher
392        ); break;
393    }
394}
395
396int
397loadstr(char *line)
398{
399    if (readstr(line, STR_HE, settings.heading, BUF_SIZE))
400        trim(settings.heading);
401    else if (readstr(line, STR_AN, settings.and, BUF_SIZE))
402        trim(settings.and);
403    else if (readstr(line, STR_IN, settings.in, BUF_SIZE))
404        trim(settings.in);
405    else if (readstr(line, STR_FR, settings.from, BUF_SIZE))
406        trim(settings.from);
407    else if (readstr(line, STR_RE, settings.retrieved, BUF_SIZE))
408        trim(settings.retrieved);
409    else if (readstr(line, STR_HC, settings.hrefcolor, BUF_SIZE))
410        trim(settings.hrefcolor);
411    else if (readstr(line, STR_MS, settings.ms, BUF_SIZE))
412        trim(settings.ms);
413    else
414        return 0;
415    return 1;
416}
417
418void
419beginbib(void)
420{
421    state.inbib = 1;
422
423    if (strcmp(settings.ms, "ms"))
424        settings.macroset = ms_ms;
425    else
426        settings.macroset = ms_ms;
427
428    switch (settings.macroset) {
429    case ms_ms:
430        fprintf(stdout, ".SH\n%s\n", settings.heading); break;
431    }
432}
433
434int
435main(int arc, char *argv[])
436{
437    char type, val[BUF_SIZE];
438    char *line;
439    size_t size;
440    int reftype;
441
442    /* default settings */
443    strcpy(settings.heading, "References");
444    strcpy(settings.and, "&");
445    strcpy(settings.in, "In");
446    strcpy(settings.from, "from");
447    strcpy(settings.retrieved, "Retrieved");
448    strcpy(settings.hrefcolor, "rgb 0f 0.325f 0.525f");
449    strcpy(settings.ms, "");
450
451    /* default state */
452    state.inbib = 0;
453    state.ref = NULL;
454
455    /* handle lines */
456    while (getline(&line, &size, stdin) != EOF) {
457        /* handle strings */
458        if (loadstr(line))
459            continue;
460
461        /* handle states */
462        if (!state.inbib && strcmp(line, BIB_START) == 0) {
463            beginbib();
464            continue;
465        } else if (state.inbib && strcmp(line, BIB_END) == 0) {
466            state.inbib = 0;
467            continue;
468        }
469
470        /* print line if not in bibliography */
471        if (!state.inbib) {
472            printf("%s", line);
473            continue;
474        }
475
476        /* handle bibliography */
477        if (!state.ref && strcmp(line, REF_START) == 0)
478            state.ref = (Referece*)calloc(1, sizeof(Referece));
479        else if (state.ref && readattr(line, REF_ATTR, &type, val, BUF_SIZE))
480            setattr(type, val);
481        else if (state.ref && sscanf(line, REF_END, &reftype) == 1)
482            printref(reftype);
483    }
484    return 0;
485}