2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
6 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
10 * Copyright (c) 1980 Regents of the University of California.
11 * All rights reserved. The Berkeley software License Agreement
12 * specifies the terms and conditions for redistribution.
15 #pragma ident "%Z%%M% %I% %E% SMI"
25 char tempfile
[32]; /* temporary file for sorting keys */
27 char *keystr
= "AD"; /* default sorting on author and date */
28 int multauth
= 0; /* by default sort on senior author only */
29 int oneauth
; /* has there been author in the record? */
31 static int article(char *);
32 static void deliver(FILE *[], FILE *);
33 static int endcomma(char *);
34 static void error(char *);
35 static void eval(char []);
36 static void parse(char [], char fld
[][BUF
]);
37 static void sortbib(FILE *, FILE *, int);
38 static void onintr(void);
40 /* sortbib: sort bibliographic database in place */
42 main(int argc
, char *argv
[])
44 FILE *fp
[MXFILES
], *tfp
;
47 (void) setlocale(LC_ALL
, "");
49 #if !defined(TEXT_DOMAIN)
50 #define TEXT_DOMAIN "SYS_TEST"
52 (void) textdomain(TEXT_DOMAIN
);
54 if (argc
== 1) { /* can't use stdin for seeking anyway */
55 puts(gettext("Usage: sortbib [-sKEYS] database [...]\n\
56 \t-s: sort by fields in KEYS (default is AD)"));
59 if (argc
> 2 && argv
[1][0] == '-' && argv
[1][1] == 's') {
60 /* if a key is specified use it, otherwise use default key */
61 if (argv
[1][2] != '\0')
63 eval(keystr
); /* evaluate A+ for multiple authors */
66 if (argc
> MXFILES
+1) { /* too many open file streams */
68 gettext("sortbib: More than %d databases specified\n"),
72 for (i
= 1; i
< argc
; i
++) /* open files in arg list */
73 if ((fp
[i
-1] = fopen(argv
[i
], "r")) == NULL
)
75 strcpy(tempfile
, "/tmp/SbibXXXXXX"); /* tempfile for sorting keys */
76 if ((tmpfd
= mkstemp(tempfile
)) == -1)
80 if (signal(SIGINT
, SIG_IGN
) != SIG_IGN
) /* remove if interrupted */
81 signal(SIGINT
, (void(*)())onintr
);
82 if ((tfp
= fopen(tempfile
, "w")) == NULL
) {
83 (void) unlink(tempfile
);
86 for (i
= 0; i
< argc
-1; i
++) /* read keys from bib files */
87 sortbib(fp
[i
], tfp
, i
);
89 deliver(fp
, tfp
); /* do disk seeks and read from biblio files */
90 (void) unlink(tempfile
);
94 int rsmode
= 0; /* record separator: 1 = null line, 2 = bracket */
96 /* read records, prepare list for sorting */
98 sortbib(FILE *fp
, FILE *tfp
, int i
)
100 long offset
, lastoffset
= 0, ftell(); /* byte offsets in file */
101 int length
, newrec
, recno
= 0; /* reclen, new rec'd?, number */
102 char line
[BUF
], fld
[4][BUF
]; /* one line, the sort fields */
104 /* measure byte offset, then get new line */
105 while (offset
= ftell(fp
), fgets(line
, BUF
, fp
)) {
106 if (recno
== 0) /* accept record w/o initial newline */
108 if (line
[0] == '\n') { /* accept null line record separator */
110 rsmode
= 1; /* null line mode */
114 if (line
[0] == '.' && line
[1] == '[') { /* also accept .[ .] */
116 rsmode
= 2; /* bracket pair mode */
120 if (newrec
) { /* by whatever means above */
122 length
= offset
- lastoffset
; /* measure rec len */
123 if (length
> BUF
*8) {
125 gettext("sortbib: record %d longer than %d "
126 "(%d)\n"), recno
, BUF
*8, length
);
127 (void) unlink(tempfile
);
130 if (recno
++) { /* info for sorting */
131 fprintf(tfp
, "%d %d %d : %s %s %s %s\n",
132 i
, lastoffset
, length
,
133 fld
[0], fld
[1], fld
[2], fld
[3]);
135 (void) unlink(tempfile
);
139 *fld
[0] = *fld
[1] = *fld
[2] = *fld
[3] = NULL
;
140 oneauth
= 0; /* reset number of authors */
141 lastoffset
= offset
; /* save for next time */
143 if (line
[0] == '%') /* parse out fields to be sorted */
146 offset
= ftell(fp
); /* measure byte offset at EOF */
147 length
= offset
- lastoffset
; /* measure final record length */
148 if (length
> BUF
*8) {
150 gettext("sortbib: record %d longer than %d (%d)\n"),
151 recno
, BUF
*8, length
);
152 (void) unlink(tempfile
);
155 if (line
[0] != '\n') { /* ignore null line just before EOF */
156 fprintf(tfp
, "%d %d %d : %s %s %s %s\n",
157 i
, lastoffset
, length
, fld
[0], fld
[1], fld
[2], fld
[3]);
159 (void) unlink(tempfile
);
160 error(tempfile
); /* disk error in /tmp */
165 /* deliver sorted entries out of database(s) */
167 deliver(FILE *fp
[], FILE *tfp
)
169 char str
[BUF
], buff
[BUF
*8]; /* for tempfile & databases */
170 char cmd
[80]; /* for using system sort command */
174 /* when sorting, ignore case distinctions; tab char is ':' */
175 sprintf(cmd
, "sort +4f +0n +1n %s -o %s", tempfile
, tempfile
);
176 if (system(cmd
) == 127) {
177 (void) unlink(tempfile
);
180 tfp
= fopen(tempfile
, "r");
181 while (fgets(str
, sizeof (str
), tfp
)) {
182 /* get file pointer, record offset, and length */
183 if (sscanf(str
, "%d %d %d :", &i
, &offset
, &length
) != 3)
184 error(gettext("sortbib: sorting error"));
185 /* seek to proper disk location in proper file */
186 if (fseek(fp
[i
], offset
, 0) == -1) {
187 (void) unlink(tempfile
);
190 /* read exactly one record from bibliography */
191 if (fread(buff
, sizeof (*buff
), length
, fp
[i
]) == 0) {
192 (void) unlink(tempfile
);
195 /* add newline between unseparated records */
196 if (buff
[0] != '\n' && rsmode
== 1)
198 /* write record buffer to standard output */
199 if (fwrite(buff
, sizeof (*buff
), length
, stdout
) == 0) {
200 (void) unlink(tempfile
);
206 /* get fields out of line, prepare for sorting */
208 parse(char line
[], char fld
[][BUF
])
210 char wd
[8][BUF
/4], *strcat();
213 for (i
= 0; i
< 8; i
++) /* zap out old strings */
215 n
= sscanf(line
, "%s %s %s %s %s %s %s %s",
216 wd
[0], wd
[1], wd
[2], wd
[3], wd
[4], wd
[5], wd
[6], wd
[7]);
217 for (i
= 0; i
< 4; i
++) {
218 if (wd
[0][1] == keystr
[i
]) {
219 if (wd
[0][1] == 'A') {
220 if (oneauth
&& !multauth
) /* no repeat */
222 else if (oneauth
) /* mult auths */
223 strcat(fld
[i
], "~~");
224 if (!endcomma(wd
[n
-2])) /* surname */
225 strcat(fld
[i
], wd
[n
-1]);
226 else { /* jr. or ed. */
227 strcat(fld
[i
], wd
[n
-2]);
231 for (j
= 1; j
< n
-1; j
++)
232 strcat(fld
[i
], wd
[j
]);
234 } else if (wd
[0][1] == 'D') {
235 strcat(fld
[i
], wd
[n
-1]); /* year */
237 strcat(fld
[i
], wd
[1]); /* month */
238 } else if (wd
[0][1] == 'T' || wd
[0][1] == 'J') {
240 if (article(wd
[1])) /* skip article */
243 strcat(fld
[i
], wd
[j
]);
244 } else /* any other field */
245 for (j
= 1; j
< n
; j
++)
246 strcat(fld
[i
], wd
[j
]);
248 /* %Q quorporate or queer author - unreversed %A */
249 else if (wd
[0][1] == 'Q' && keystr
[i
] == 'A')
250 for (j
= 1; j
< n
; j
++)
251 strcat(fld
[i
], wd
[j
]);
255 /* see if string contains an article */
259 if (strcmp("The", str
) == 0) /* English */
261 if (strcmp("A", str
) == 0)
263 if (strcmp("An", str
) == 0)
265 if (strcmp("Le", str
) == 0) /* French */
267 if (strcmp("La", str
) == 0)
269 if (strcmp("Der", str
) == 0) /* German */
271 if (strcmp("Die", str
) == 0)
273 if (strcmp("Das", str
) == 0)
275 if (strcmp("El", str
) == 0) /* Spanish */
277 if (strcmp("Den", str
) == 0) /* Scandinavian */
282 /* evaluate key string for A+ marking */
288 for (i
= 0, j
= 0; keystr
[i
]; i
++, j
++) {
289 if (keystr
[i
] == '+') {
293 if (keystr
[i
] == NULL
)
295 keystr
[j
] = keystr
[i
];
300 /* exit in case of various system errors */
308 /* remove tempfile in case of interrupt */
312 fprintf(stderr
, gettext("\nInterrupt\n"));