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}