commit: ea3d5264debd829e18beba3670306acc184fc6ab
parent:
author: Chris Noxz <chris@noxz.tech>
date: Fri, 8 Nov 2019 19:35:49 +0100
Initial commit (be aware, quick hack)
7 files changed, 442 insertions(+)
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;
+}