111a8fa6cSceastha /*
211a8fa6cSceastha * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
311a8fa6cSceastha * Use is subject to license terms.
411a8fa6cSceastha */
511a8fa6cSceastha
67c478bd9Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
77c478bd9Sstevel@tonic-gate /* All Rights Reserved */
87c478bd9Sstevel@tonic-gate
97c478bd9Sstevel@tonic-gate /*
107c478bd9Sstevel@tonic-gate * Copyright (c) 1980 Regents of the University of California.
117c478bd9Sstevel@tonic-gate * All rights reserved. The Berkeley software License Agreement
127c478bd9Sstevel@tonic-gate * specifies the terms and conditions for redistribution.
137c478bd9Sstevel@tonic-gate */
147c478bd9Sstevel@tonic-gate
157c478bd9Sstevel@tonic-gate #include <locale.h>
167c478bd9Sstevel@tonic-gate #include <stdio.h>
177c478bd9Sstevel@tonic-gate #include <signal.h>
187c478bd9Sstevel@tonic-gate #include <stdlib.h>
197c478bd9Sstevel@tonic-gate
207c478bd9Sstevel@tonic-gate #define BUF BUFSIZ
217c478bd9Sstevel@tonic-gate #define MXFILES 16
227c478bd9Sstevel@tonic-gate
237c478bd9Sstevel@tonic-gate char tempfile[32]; /* temporary file for sorting keys */
247c478bd9Sstevel@tonic-gate int tmpfd = -1;
257c478bd9Sstevel@tonic-gate char *keystr = "AD"; /* default sorting on author and date */
267c478bd9Sstevel@tonic-gate int multauth = 0; /* by default sort on senior author only */
277c478bd9Sstevel@tonic-gate int oneauth; /* has there been author in the record? */
287c478bd9Sstevel@tonic-gate
2911a8fa6cSceastha static int article(char *);
3011a8fa6cSceastha static void deliver(FILE *[], FILE *);
3111a8fa6cSceastha static int endcomma(char *);
3211a8fa6cSceastha static void error(char *);
3311a8fa6cSceastha static void eval(char []);
3411a8fa6cSceastha static void parse(char [], char fld[][BUF]);
3511a8fa6cSceastha static void sortbib(FILE *, FILE *, int);
3611a8fa6cSceastha static void onintr(void);
3711a8fa6cSceastha
3811a8fa6cSceastha /* sortbib: sort bibliographic database in place */
3911a8fa6cSceastha int
main(int argc,char * argv[])4011a8fa6cSceastha main(int argc, char *argv[])
417c478bd9Sstevel@tonic-gate {
427c478bd9Sstevel@tonic-gate FILE *fp[MXFILES], *tfp;
437c478bd9Sstevel@tonic-gate int i;
447c478bd9Sstevel@tonic-gate
457c478bd9Sstevel@tonic-gate (void) setlocale(LC_ALL, "");
467c478bd9Sstevel@tonic-gate
477c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
487c478bd9Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST"
497c478bd9Sstevel@tonic-gate #endif
507c478bd9Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN);
517c478bd9Sstevel@tonic-gate
5211a8fa6cSceastha if (argc == 1) { /* can't use stdin for seeking anyway */
537c478bd9Sstevel@tonic-gate puts(gettext("Usage: sortbib [-sKEYS] database [...]\n\
547c478bd9Sstevel@tonic-gate \t-s: sort by fields in KEYS (default is AD)"));
557c478bd9Sstevel@tonic-gate exit(1);
567c478bd9Sstevel@tonic-gate }
5711a8fa6cSceastha if (argc > 2 && argv[1][0] == '-' && argv[1][1] == 's') {
587c478bd9Sstevel@tonic-gate /* if a key is specified use it, otherwise use default key */
597c478bd9Sstevel@tonic-gate if (argv[1][2] != '\0')
607c478bd9Sstevel@tonic-gate keystr = argv[1] + 2;
617c478bd9Sstevel@tonic-gate eval(keystr); /* evaluate A+ for multiple authors */
627c478bd9Sstevel@tonic-gate argv++; argc--;
637c478bd9Sstevel@tonic-gate }
6411a8fa6cSceastha if (argc > MXFILES+1) { /* too many open file streams */
657c478bd9Sstevel@tonic-gate fprintf(stderr,
667c478bd9Sstevel@tonic-gate gettext("sortbib: More than %d databases specified\n"),
677c478bd9Sstevel@tonic-gate MXFILES);
687c478bd9Sstevel@tonic-gate exit(1);
697c478bd9Sstevel@tonic-gate }
707c478bd9Sstevel@tonic-gate for (i = 1; i < argc; i++) /* open files in arg list */
717c478bd9Sstevel@tonic-gate if ((fp[i-1] = fopen(argv[i], "r")) == NULL)
727c478bd9Sstevel@tonic-gate error(argv[i]);
737c478bd9Sstevel@tonic-gate strcpy(tempfile, "/tmp/SbibXXXXXX"); /* tempfile for sorting keys */
747c478bd9Sstevel@tonic-gate if ((tmpfd = mkstemp(tempfile)) == -1)
757c478bd9Sstevel@tonic-gate error(tempfile);
767c478bd9Sstevel@tonic-gate
777c478bd9Sstevel@tonic-gate (void) close(tmpfd);
787c478bd9Sstevel@tonic-gate if (signal(SIGINT, SIG_IGN) != SIG_IGN) /* remove if interrupted */
7911a8fa6cSceastha signal(SIGINT, (void(*)())onintr);
807c478bd9Sstevel@tonic-gate if ((tfp = fopen(tempfile, "w")) == NULL) {
817c478bd9Sstevel@tonic-gate (void) unlink(tempfile);
827c478bd9Sstevel@tonic-gate error(tempfile);
837c478bd9Sstevel@tonic-gate }
847c478bd9Sstevel@tonic-gate for (i = 0; i < argc-1; i++) /* read keys from bib files */
857c478bd9Sstevel@tonic-gate sortbib(fp[i], tfp, i);
867c478bd9Sstevel@tonic-gate fclose(tfp);
877c478bd9Sstevel@tonic-gate deliver(fp, tfp); /* do disk seeks and read from biblio files */
887c478bd9Sstevel@tonic-gate (void) unlink(tempfile);
8911a8fa6cSceastha return (0);
907c478bd9Sstevel@tonic-gate }
917c478bd9Sstevel@tonic-gate
927c478bd9Sstevel@tonic-gate int rsmode = 0; /* record separator: 1 = null line, 2 = bracket */
937c478bd9Sstevel@tonic-gate
9411a8fa6cSceastha /* read records, prepare list for sorting */
9511a8fa6cSceastha static void
sortbib(FILE * fp,FILE * tfp,int i)9611a8fa6cSceastha sortbib(FILE *fp, FILE *tfp, int i)
977c478bd9Sstevel@tonic-gate {
987c478bd9Sstevel@tonic-gate long offset, lastoffset = 0, ftell(); /* byte offsets in file */
997c478bd9Sstevel@tonic-gate int length, newrec, recno = 0; /* reclen, new rec'd?, number */
1007c478bd9Sstevel@tonic-gate char line[BUF], fld[4][BUF]; /* one line, the sort fields */
1017c478bd9Sstevel@tonic-gate
1027c478bd9Sstevel@tonic-gate /* measure byte offset, then get new line */
10311a8fa6cSceastha while (offset = ftell(fp), fgets(line, BUF, fp)) {
1047c478bd9Sstevel@tonic-gate if (recno == 0) /* accept record w/o initial newline */
1057c478bd9Sstevel@tonic-gate newrec = 1;
10611a8fa6cSceastha if (line[0] == '\n') { /* accept null line record separator */
1077c478bd9Sstevel@tonic-gate if (!rsmode)
1087c478bd9Sstevel@tonic-gate rsmode = 1; /* null line mode */
1097c478bd9Sstevel@tonic-gate if (rsmode == 1)
1107c478bd9Sstevel@tonic-gate newrec = 1;
1117c478bd9Sstevel@tonic-gate }
11211a8fa6cSceastha if (line[0] == '.' && line[1] == '[') { /* also accept .[ .] */
1137c478bd9Sstevel@tonic-gate if (!rsmode)
1147c478bd9Sstevel@tonic-gate rsmode = 2; /* bracket pair mode */
1157c478bd9Sstevel@tonic-gate if (rsmode == 2)
1167c478bd9Sstevel@tonic-gate newrec = 1;
1177c478bd9Sstevel@tonic-gate }
11811a8fa6cSceastha if (newrec) { /* by whatever means above */
1197c478bd9Sstevel@tonic-gate newrec = 0;
1207c478bd9Sstevel@tonic-gate length = offset - lastoffset; /* measure rec len */
1217c478bd9Sstevel@tonic-gate if (length > BUF*8) {
1227c478bd9Sstevel@tonic-gate fprintf(stderr,
12311a8fa6cSceastha gettext("sortbib: record %d longer than %d "
12411a8fa6cSceastha "(%d)\n"), recno, BUF*8, length);
1257c478bd9Sstevel@tonic-gate (void) unlink(tempfile);
1267c478bd9Sstevel@tonic-gate exit(1);
1277c478bd9Sstevel@tonic-gate }
12811a8fa6cSceastha if (recno++) { /* info for sorting */
1297c478bd9Sstevel@tonic-gate fprintf(tfp, "%d %d %d : %s %s %s %s\n",
1307c478bd9Sstevel@tonic-gate i, lastoffset, length,
1317c478bd9Sstevel@tonic-gate fld[0], fld[1], fld[2], fld[3]);
1327c478bd9Sstevel@tonic-gate if (ferror(tfp)) {
1337c478bd9Sstevel@tonic-gate (void) unlink(tempfile);
1347c478bd9Sstevel@tonic-gate error(tempfile);
1357c478bd9Sstevel@tonic-gate }
1367c478bd9Sstevel@tonic-gate }
137*7bc3049fSToomas Soome *fld[0] = *fld[1] = *fld[2] = *fld[3] = '\0';
1387c478bd9Sstevel@tonic-gate oneauth = 0; /* reset number of authors */
1397c478bd9Sstevel@tonic-gate lastoffset = offset; /* save for next time */
1407c478bd9Sstevel@tonic-gate }
1417c478bd9Sstevel@tonic-gate if (line[0] == '%') /* parse out fields to be sorted */
1427c478bd9Sstevel@tonic-gate parse(line, fld);
1437c478bd9Sstevel@tonic-gate }
1447c478bd9Sstevel@tonic-gate offset = ftell(fp); /* measure byte offset at EOF */
1457c478bd9Sstevel@tonic-gate length = offset - lastoffset; /* measure final record length */
14611a8fa6cSceastha if (length > BUF*8) {
1477c478bd9Sstevel@tonic-gate fprintf(stderr,
1487c478bd9Sstevel@tonic-gate gettext("sortbib: record %d longer than %d (%d)\n"),
1497c478bd9Sstevel@tonic-gate recno, BUF*8, length);
1507c478bd9Sstevel@tonic-gate (void) unlink(tempfile);
1517c478bd9Sstevel@tonic-gate exit(1);
1527c478bd9Sstevel@tonic-gate }
15311a8fa6cSceastha if (line[0] != '\n') { /* ignore null line just before EOF */
1547c478bd9Sstevel@tonic-gate fprintf(tfp, "%d %d %d : %s %s %s %s\n",
15511a8fa6cSceastha i, lastoffset, length, fld[0], fld[1], fld[2], fld[3]);
1567c478bd9Sstevel@tonic-gate if (ferror(tfp)) {
1577c478bd9Sstevel@tonic-gate (void) unlink(tempfile);
1587c478bd9Sstevel@tonic-gate error(tempfile); /* disk error in /tmp */
1597c478bd9Sstevel@tonic-gate }
1607c478bd9Sstevel@tonic-gate }
1617c478bd9Sstevel@tonic-gate }
1627c478bd9Sstevel@tonic-gate
16311a8fa6cSceastha /* deliver sorted entries out of database(s) */
16411a8fa6cSceastha static void
deliver(FILE * fp[],FILE * tfp)16511a8fa6cSceastha deliver(FILE *fp[], FILE *tfp)
1667c478bd9Sstevel@tonic-gate {
1677c478bd9Sstevel@tonic-gate char str[BUF], buff[BUF*8]; /* for tempfile & databases */
1687c478bd9Sstevel@tonic-gate char cmd[80]; /* for using system sort command */
1697c478bd9Sstevel@tonic-gate long int offset;
1707c478bd9Sstevel@tonic-gate int i, length;
1717c478bd9Sstevel@tonic-gate
1727c478bd9Sstevel@tonic-gate /* when sorting, ignore case distinctions; tab char is ':' */
1737c478bd9Sstevel@tonic-gate sprintf(cmd, "sort +4f +0n +1n %s -o %s", tempfile, tempfile);
1747c478bd9Sstevel@tonic-gate if (system(cmd) == 127) {
1757c478bd9Sstevel@tonic-gate (void) unlink(tempfile);
1767c478bd9Sstevel@tonic-gate error("sortbib");
1777c478bd9Sstevel@tonic-gate }
1787c478bd9Sstevel@tonic-gate tfp = fopen(tempfile, "r");
17911a8fa6cSceastha while (fgets(str, sizeof (str), tfp)) {
1807c478bd9Sstevel@tonic-gate /* get file pointer, record offset, and length */
1817c478bd9Sstevel@tonic-gate if (sscanf(str, "%d %d %d :", &i, &offset, &length) != 3)
1827c478bd9Sstevel@tonic-gate error(gettext("sortbib: sorting error"));
1837c478bd9Sstevel@tonic-gate /* seek to proper disk location in proper file */
1847c478bd9Sstevel@tonic-gate if (fseek(fp[i], offset, 0) == -1) {
1857c478bd9Sstevel@tonic-gate (void) unlink(tempfile);
1867c478bd9Sstevel@tonic-gate error("sortbib");
1877c478bd9Sstevel@tonic-gate }
1887c478bd9Sstevel@tonic-gate /* read exactly one record from bibliography */
1897c478bd9Sstevel@tonic-gate if (fread(buff, sizeof (*buff), length, fp[i]) == 0) {
1907c478bd9Sstevel@tonic-gate (void) unlink(tempfile);
1917c478bd9Sstevel@tonic-gate error("sortbib");
1927c478bd9Sstevel@tonic-gate }
1937c478bd9Sstevel@tonic-gate /* add newline between unseparated records */
1947c478bd9Sstevel@tonic-gate if (buff[0] != '\n' && rsmode == 1)
1957c478bd9Sstevel@tonic-gate putchar('\n');
1967c478bd9Sstevel@tonic-gate /* write record buffer to standard output */
1977c478bd9Sstevel@tonic-gate if (fwrite(buff, sizeof (*buff), length, stdout) == 0) {
1987c478bd9Sstevel@tonic-gate (void) unlink(tempfile);
1997c478bd9Sstevel@tonic-gate error("sortbib");
2007c478bd9Sstevel@tonic-gate }
2017c478bd9Sstevel@tonic-gate }
2027c478bd9Sstevel@tonic-gate }
2037c478bd9Sstevel@tonic-gate
20411a8fa6cSceastha /* get fields out of line, prepare for sorting */
20511a8fa6cSceastha static void
parse(char line[],char fld[][BUF])20611a8fa6cSceastha parse(char line[], char fld[][BUF])
2077c478bd9Sstevel@tonic-gate {
2087c478bd9Sstevel@tonic-gate char wd[8][BUF/4], *strcat();
2097c478bd9Sstevel@tonic-gate int n, i, j;
2107c478bd9Sstevel@tonic-gate
2117c478bd9Sstevel@tonic-gate for (i = 0; i < 8; i++) /* zap out old strings */
212*7bc3049fSToomas Soome *wd[i] = '\0';
2137c478bd9Sstevel@tonic-gate n = sscanf(line, "%s %s %s %s %s %s %s %s",
2147c478bd9Sstevel@tonic-gate wd[0], wd[1], wd[2], wd[3], wd[4], wd[5], wd[6], wd[7]);
21511a8fa6cSceastha for (i = 0; i < 4; i++) {
21611a8fa6cSceastha if (wd[0][1] == keystr[i]) {
21711a8fa6cSceastha if (wd[0][1] == 'A') {
2187c478bd9Sstevel@tonic-gate if (oneauth && !multauth) /* no repeat */
2197c478bd9Sstevel@tonic-gate break;
2207c478bd9Sstevel@tonic-gate else if (oneauth) /* mult auths */
2217c478bd9Sstevel@tonic-gate strcat(fld[i], "~~");
2227c478bd9Sstevel@tonic-gate if (!endcomma(wd[n-2])) /* surname */
2237c478bd9Sstevel@tonic-gate strcat(fld[i], wd[n-1]);
2247c478bd9Sstevel@tonic-gate else { /* jr. or ed. */
2257c478bd9Sstevel@tonic-gate strcat(fld[i], wd[n-2]);
2267c478bd9Sstevel@tonic-gate n--;
2277c478bd9Sstevel@tonic-gate }
2287c478bd9Sstevel@tonic-gate strcat(fld[i], " ");
2297c478bd9Sstevel@tonic-gate for (j = 1; j < n-1; j++)
2307c478bd9Sstevel@tonic-gate strcat(fld[i], wd[j]);
2317c478bd9Sstevel@tonic-gate oneauth = 1;
2327c478bd9Sstevel@tonic-gate } else if (wd[0][1] == 'D') {
2337c478bd9Sstevel@tonic-gate strcat(fld[i], wd[n-1]); /* year */
2347c478bd9Sstevel@tonic-gate if (n > 2)
2357c478bd9Sstevel@tonic-gate strcat(fld[i], wd[1]); /* month */
2367c478bd9Sstevel@tonic-gate } else if (wd[0][1] == 'T' || wd[0][1] == 'J') {
2377c478bd9Sstevel@tonic-gate j = 1;
2387c478bd9Sstevel@tonic-gate if (article(wd[1])) /* skip article */
2397c478bd9Sstevel@tonic-gate j++;
2407c478bd9Sstevel@tonic-gate for (; j < n; j++)
2417c478bd9Sstevel@tonic-gate strcat(fld[i], wd[j]);
2427c478bd9Sstevel@tonic-gate } else /* any other field */
2437c478bd9Sstevel@tonic-gate for (j = 1; j < n; j++)
2447c478bd9Sstevel@tonic-gate strcat(fld[i], wd[j]);
2457c478bd9Sstevel@tonic-gate }
2467c478bd9Sstevel@tonic-gate /* %Q quorporate or queer author - unreversed %A */
2477c478bd9Sstevel@tonic-gate else if (wd[0][1] == 'Q' && keystr[i] == 'A')
2487c478bd9Sstevel@tonic-gate for (j = 1; j < n; j++)
2497c478bd9Sstevel@tonic-gate strcat(fld[i], wd[j]);
2507c478bd9Sstevel@tonic-gate }
2517c478bd9Sstevel@tonic-gate }
2527c478bd9Sstevel@tonic-gate
25311a8fa6cSceastha /* see if string contains an article */
25411a8fa6cSceastha static int
article(char * str)25511a8fa6cSceastha article(char *str)
2567c478bd9Sstevel@tonic-gate {
2577c478bd9Sstevel@tonic-gate if (strcmp("The", str) == 0) /* English */
2587c478bd9Sstevel@tonic-gate return (1);
2597c478bd9Sstevel@tonic-gate if (strcmp("A", str) == 0)
2607c478bd9Sstevel@tonic-gate return (1);
2617c478bd9Sstevel@tonic-gate if (strcmp("An", str) == 0)
2627c478bd9Sstevel@tonic-gate return (1);
2637c478bd9Sstevel@tonic-gate if (strcmp("Le", str) == 0) /* French */
2647c478bd9Sstevel@tonic-gate return (1);
2657c478bd9Sstevel@tonic-gate if (strcmp("La", str) == 0)
2667c478bd9Sstevel@tonic-gate return (1);
2677c478bd9Sstevel@tonic-gate if (strcmp("Der", str) == 0) /* German */
2687c478bd9Sstevel@tonic-gate return (1);
2697c478bd9Sstevel@tonic-gate if (strcmp("Die", str) == 0)
2707c478bd9Sstevel@tonic-gate return (1);
2717c478bd9Sstevel@tonic-gate if (strcmp("Das", str) == 0)
2727c478bd9Sstevel@tonic-gate return (1);
2737c478bd9Sstevel@tonic-gate if (strcmp("El", str) == 0) /* Spanish */
2747c478bd9Sstevel@tonic-gate return (1);
2757c478bd9Sstevel@tonic-gate if (strcmp("Den", str) == 0) /* Scandinavian */
2767c478bd9Sstevel@tonic-gate return (1);
2777c478bd9Sstevel@tonic-gate return (0);
2787c478bd9Sstevel@tonic-gate }
2797c478bd9Sstevel@tonic-gate
28011a8fa6cSceastha /* evaluate key string for A+ marking */
28111a8fa6cSceastha static void
eval(char keystr[])28211a8fa6cSceastha eval(char keystr[])
2837c478bd9Sstevel@tonic-gate {
2847c478bd9Sstevel@tonic-gate int i, j;
2857c478bd9Sstevel@tonic-gate
28611a8fa6cSceastha for (i = 0, j = 0; keystr[i]; i++, j++) {
28711a8fa6cSceastha if (keystr[i] == '+') {
2887c478bd9Sstevel@tonic-gate multauth = 1;
2897c478bd9Sstevel@tonic-gate i++;
2907c478bd9Sstevel@tonic-gate }
291*7bc3049fSToomas Soome if (keystr[i] == '\0')
2927c478bd9Sstevel@tonic-gate break;
2937c478bd9Sstevel@tonic-gate keystr[j] = keystr[i];
2947c478bd9Sstevel@tonic-gate }
295*7bc3049fSToomas Soome keystr[j] = '\0';
2967c478bd9Sstevel@tonic-gate }
2977c478bd9Sstevel@tonic-gate
29811a8fa6cSceastha /* exit in case of various system errors */
29911a8fa6cSceastha static void
error(char * s)30011a8fa6cSceastha error(char *s)
3017c478bd9Sstevel@tonic-gate {
3027c478bd9Sstevel@tonic-gate perror(s);
3037c478bd9Sstevel@tonic-gate exit(1);
3047c478bd9Sstevel@tonic-gate }
3057c478bd9Sstevel@tonic-gate
30611a8fa6cSceastha /* remove tempfile in case of interrupt */
30711a8fa6cSceastha static void
onintr(void)30811a8fa6cSceastha onintr(void)
3097c478bd9Sstevel@tonic-gate {
3107c478bd9Sstevel@tonic-gate fprintf(stderr, gettext("\nInterrupt\n"));
3117c478bd9Sstevel@tonic-gate unlink(tempfile);
3127c478bd9Sstevel@tonic-gate exit(1);
3137c478bd9Sstevel@tonic-gate }
3147c478bd9Sstevel@tonic-gate
31511a8fa6cSceastha static int
endcomma(char * str)31611a8fa6cSceastha endcomma(char *str)
3177c478bd9Sstevel@tonic-gate {
3187c478bd9Sstevel@tonic-gate int n;
3197c478bd9Sstevel@tonic-gate
3207c478bd9Sstevel@tonic-gate n = strlen(str) - 1;
32111a8fa6cSceastha if (str[n] == ',') {
322*7bc3049fSToomas Soome str[n] = '\0';
3237c478bd9Sstevel@tonic-gate return (1);
3247c478bd9Sstevel@tonic-gate }
3257c478bd9Sstevel@tonic-gate return (0);
3267c478bd9Sstevel@tonic-gate }
327