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

commit ea3d5264debd829e18beba3670306acc184fc6ab
Author: Chris Noxz <chris@noxz.tech>
Date:   Fri,  8 Nov 2019 19:35:49 +0100

Initial commit (be aware, quick hack)

Diffstat:
A.gitignore | 3+++
ALICENSE | 21+++++++++++++++++++++
AMakefile | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
AREADME.md | 31+++++++++++++++++++++++++++++++
Aconfig.def.h | 10++++++++++
Aconfig.mk | 15+++++++++++++++
Atapas.c | 311+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 442 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,3 @@ +tapas +config.h +*.o diff --git a/LICENSE b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +© 2019 Chris Noxz <chris@noxz.tech> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile @@ -0,0 +1,51 @@ +.POSIX: + +include config.mk + +SRC = tapas.c +OBJ = $(SRC:.c=.o) + +all: options tapas + +options: + @echo tapas build options: + @echo "VERSION = $(VERSION)" + @echo "CFLAGS = $(STCFLAGS)" + @echo "LDFLAGS = $(STLDFLAGS)" + @echo "CC = $(CC)" + +.c.o: + @echo CC $< + @$(CC) $(STCFLAGS) -c $< + +${OBJ}: config.h config.mk + +config.h: + @echo creating $@ from config.def.h + @cp config.def.h $@ + +tapas: $(OBJ) + @echo CC -o $@ + @$(CC) -o $@ $(OBJ) $(STLDFLAGS) + +clean: + @echo cleaning + @rm -f tapas $(OBJ) + +install: tapas + @echo installing executables to ${PREFIX}/bin + @mkdir -p ${PREFIX}/bin + @cp -f tapas ${PREFIX}/bin + @chmod 755 ${PREFIX}/bin/tapas +# @echo installing manual page to ${MANPREFIX}/man1 +# @mkdir -p ${MANPREFIX}/man1 +# @cp -f tapas.1 ${MANPREFIX}/man1 +# @chmod 644 ${MANPREFIX}/man1/tapas.1 + +uninstall: + @echo removing executable files from ${PREFIX}/bin + @rm -f ${PREFIX}/bin/tapas +# @echo removing manual page from ${MANPREFIX}/man1 +# @rm -f ${MANPREFIX}/man1/tapas.1 + +.PHONY: all options clean install uninstall diff --git a/README.md b/README.md @@ -0,0 +1,31 @@ +tapas +===== +The name stands for **t**o **APA**, and the *s* is for added deliciousness. +It's a small program used for compiling refer output into the APA reference +format. + +The program reads refer output from *stdin* and prints the compiled result to +*stdout*. + +Installation +------------ +Edit config.mk to match your local setup (tapas is installed into the +/usr/local namespace by default), then simply enter the following command to +install (if necessary as root): + + make clean install + +Customization +------------- +tapas can be customized by creating a custom config.h and (re)compiling the +source code. + +Usage +----- +Tapas is intended to run in a pipe chain directly after refer, like so: + + ... | refer | tapas | ... | troff + +License +------- +The program is licensed under the MIT license. diff --git a/config.def.h b/config.def.h @@ -0,0 +1,10 @@ +#define BUF_SIZE 128 +#define BIB_START ".]<\n" +#define BIB_END ".]>\n" +#define REF_START ".]-\n" +#define REF_END ".][ %d" +#define REF_ATTR ".ds [%c %127[^\n]" +#define STR_HE ".ds APA_HE %127[^\n]" +#define STR_AN ".ds APA_AN %127[^\n]" +#define STR_IN ".ds APA_IN %127[^\n]" +#define STR_MS ".ds APA_MS %127[^\n]" diff --git a/config.mk b/config.mk @@ -0,0 +1,15 @@ +# tapas version +VERSION = 0.0.1 + +# paths +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +# flags +CFLAGS = -Wall -Wno-format-truncation -pedantic -std=c99 +CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 +STCFLAGS = $(CPPFLAGS) $(CFLAGS) +STLDFLAGS = $(LDFLAGS) + +# compiler and linker +CC = gcc diff --git a/tapas.c b/tapas.c @@ -0,0 +1,311 @@ +/* + * MIT License + * + * © 2019 Chris Noxz <chris@noxz.tech> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "config.h" + +struct Settings { + char heading[BUF_SIZE]; + char and[BUF_SIZE]; + char in[BUF_SIZE]; + char ms[BUF_SIZE]; +}; +struct Settings settings; + +enum macrosets { + ms_ms, +// ms_mom, + /* add more macro sets here... */ +}; + +typedef struct { + char author[BUF_SIZE]; + char title[BUF_SIZE]; + char book_title[BUF_SIZE]; + char report_number[BUF_SIZE]; + char journal_name[BUF_SIZE]; + char editor[BUF_SIZE]; + char edition[BUF_SIZE]; + char volume[BUF_SIZE]; + char journal_number[BUF_SIZE]; + char series[BUF_SIZE]; + char city[BUF_SIZE]; + char publisher[BUF_SIZE]; + char publication_date[BUF_SIZE]; + char page_number[BUF_SIZE]; + char gov_number[BUF_SIZE]; + char other[BUF_SIZE]; + char keywords[BUF_SIZE]; + char original_pub_date[BUF_SIZE]; + char additions[BUF_SIZE]; + char reprint_title[BUF_SIZE]; + char translator[BUF_SIZE]; + char translator_editor[BUF_SIZE]; + char site_name[BUF_SIZE]; + char site_content[BUF_SIZE]; + char organization[BUF_SIZE]; + char url[BUF_SIZE]; +} Referece; + +void +trim(char *source) +{ + char *ptr = source; + + while (*ptr == ' ') + ptr++; + memmove(source, ptr, strlen(ptr) + 1); + + ptr = source + strlen(source) - 1; + + while (*ptr == ' ') + ptr--; + *(ptr + 1) = 0; + + /* also, remove surounding quotes if existing */ + if (source[0] == '"' && source[strlen(source) - 1] == '"') { + ptr = source + strlen(source) - 2; + memmove(source, source + 1, strlen(source)); + *ptr = 0; + } +} + +void +findreplace(char *source, const char *find, const char *replace) +{ + char *dest = malloc(strlen(source) - strlen(find) + strlen(replace) + 1); + char *ptr; + + strcpy(dest, source); + + ptr = strstr(dest, find); + if (ptr) { + memmove( + ptr + strlen(replace), + ptr + strlen(find), + strlen(ptr+strlen(find)) + 1 + ); + strncpy(ptr, replace, strlen(replace)); + } + + *(source + strlen(dest)) = 0; + strncpy(source, dest, strlen(dest)); +} + +void +wordsym(Referece *ref) +{ + char str_and[BUF_SIZE]; + + snprintf(str_and, BUF_SIZE, " %s ", settings.and); + + if (ref->author[0]) + findreplace(ref->author, " and ", str_and); + if (ref->editor[0]) + findreplace(ref->editor, " and ", str_and); +} + +void +format_article(Referece *ref, int macroset) +{ + if (!ref->author + || !ref->publication_date + || !ref->title + || !ref->journal_name + || !ref->journal_number + || !ref->city + || !ref->publisher) + return; + switch (macroset) { + case ms_ms: + fprintf(stdout, + ".XP\n%s (%s). \n.I \"%s\" \" (%s, %s).\"\n%s: %s.\n", + ref->author, + ref->publication_date, + ref->title, + ref->journal_name, + ref->journal_number, + ref->city, + ref->publisher + ); break; + } +} + +void +format_book(Referece *ref, int macroset) +{ + if (!ref->author + || !ref->publication_date + || !ref->title + || !ref->city + || !ref->publisher) + return; + switch (macroset) { + case ms_ms: + fprintf(stdout, + ".XP\n%s (%s).\n.I \"%s\" .\n%s: %s.\n", + ref->author, + ref->publication_date, + ref->title, + ref->city, + ref->publisher + ); break; + } +} + +void +format_article_in_book(Referece *ref, int macroset) +{ + if (!ref->author + || !ref->publication_date + || !ref->title + || !ref->editor + || !ref->book_title + || !ref->page_number + || !ref->city + || !ref->publisher) + return; + switch (macroset) { + case ms_ms: + fprintf(stdout, + ".XP\n%s (%s). %s. %s %s, \n.I \"%s\" \" (s. %s).\"\n%s: %s.\n", + ref->author, + ref->publication_date, + ref->title, + settings.in, + ref->editor, + ref->book_title, + ref->page_number, + ref->city, + ref->publisher + ); break; + } +} + +int +loadstr(char *line) +{ + if (sscanf(line, STR_HE, &settings.heading) == 1) + trim(settings.heading); + else if (sscanf(line, STR_AN, &settings.and) == 1) + trim(settings.and); + else if (sscanf(line, STR_IN, &settings.in) == 1) + trim(settings.in); + else if (sscanf(line, STR_MS, &settings.ms) == 1) + trim(settings.ms); + else + return 0; + return 1; +} + +int +main(int arc, char *argv[]) +{ + int inbib = 0; + int macroset = ms_ms; + int reftype; + char atype; + char aval[BUF_SIZE]; + size_t size; + char *line; + Referece *ref = NULL; + + /* default settings */ + strcpy(settings.heading, "References"); + strcpy(settings.and, "&"); + strcpy(settings.in, "In"); + strcpy(settings.ms, ""); + + while (getline(&line, &size, stdin) != EOF) { + if (loadstr(line)) + continue; + if (!inbib && strcmp(line, BIB_START) == 0) { + inbib = 1; + switch (macroset) { + case ms_ms: + fprintf(stdout, ".SH\n%s\n", settings.heading); break; + } continue; + } + if (inbib && strcmp(line, BIB_END) == 0) { + inbib = 0; + if (strcmp(settings.ms, "ms")) + macroset = ms_ms; + //else if (strcmp(settings.ms, "mom")) + // macroset = ms_mom; + /* add more macro sets here... */ + else + macroset = ms_ms; + continue; + } + if (inbib) { + if (!ref && strcmp(line, REF_START) == 0) { + ref = (Referece*)calloc(1, sizeof(Referece)); + } else if (ref && sscanf(line, REF_ATTR, &atype, &aval) == 2) { + switch (atype) { + case 'A': strcpy(ref->author, aval); break; + case 'T': strcpy(ref->title, aval); break; + case 'B': strcpy(ref->book_title, aval); break; + case 'R': strcpy(ref->report_number, aval); break; + case 'J': strcpy(ref->journal_name, aval); break; + case 'E': strcpy(ref->editor, aval); break; + case 'e': strcpy(ref->edition, aval); break; + case 'V': strcpy(ref->volume, aval); break; + case 'N': strcpy(ref->journal_number, aval); break; + case 'S': strcpy(ref->series, aval); break; + case 'C': strcpy(ref->city, aval); break; + case 'I': strcpy(ref->publisher, aval); break; + case 'D': strcpy(ref->publication_date, aval); break; + case 'P': strcpy(ref->page_number, aval); break; + case 'G': strcpy(ref->gov_number, aval); break; + case 'O': strcpy(ref->other, aval); break; + case 'K': strcpy(ref->keywords, aval); break; + case 'd': strcpy(ref->original_pub_date, aval); break; + case 'a': strcpy(ref->additions, aval); break; + case 't': strcpy(ref->reprint_title, aval); break; + case 'l': strcpy(ref->translator, aval); break; + case 'r': strcpy(ref->translator_editor, aval); break; + case 's': strcpy(ref->site_name, aval); break; + case 'c': strcpy(ref->site_content, aval); break; + case 'o': strcpy(ref->organization, aval); break; + case 'u': strcpy(ref->url, aval); break; + default: continue; + } + } else if (ref && sscanf(line, REF_END, &reftype) == 1) { + wordsym(ref); + switch (reftype) { + case 1: format_article(ref, macroset); break; + case 2: format_book(ref, macroset); break; + case 3: format_article_in_book(ref, macroset); break; + } + free(ref); + ref = NULL; + } + continue; + } + printf("%s", line); + } + return 0; +}