tapas

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

tapas.c (13575B)


      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 
     43 typedef 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 
     73 struct State {
     74     int inbib;
     75     Referece *ref;
     76 };
     77 struct State state;
     78 
     79 struct 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 };
     89 struct Settings settings;
     90 
     91 enum macrosets {
     92     ms_ms,
     93 //    ms_mom,
     94     /* add more macro sets here... */
     95 };
     96 
     97 static void trim(char*);
     98 static int readstr(const char*, const char*, char*, int);
     99 static int readattr(const char*, const char*, char*, char*, int);
    100 static void setattr(char, char*);
    101 static void printref(int);
    102 static int matchcount(char*, const char*);
    103 static void findreplace(char*, const char*, const char*, int);
    104 static void wordsym();
    105 static void format_other();
    106 static void format_article();
    107 static void format_book();
    108 static void format_article_in_book();
    109 static int loadstr(char*);
    110 static void beginbib(void);
    111 
    112 void
    113 trim(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 
    135 int
    136 readstr(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 
    153 int
    154 readattr(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 
    171 void
    172 setattr(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 
    205 void
    206 printref(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 
    220 int
    221 matchcount(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 
    238 void
    239 findreplace(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 
    267 void
    268 wordsym()
    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 
    280 void /* TODO :: Check type of other, website etc. */
    281 format_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 
    318 void
    319 format_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 
    345 void
    346 format_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 
    367 void
    368 format_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 
    396 int
    397 loadstr(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 
    418 void
    419 beginbib(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 
    434 int
    435 main(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 }