1/*	$NetBSD: getNAME.c,v 1.26 2008/07/20 01:09:07 lukem Exp $	*/
2
3/*-
4 * Copyright (c) 1997, Christos Zoulas.  All rights reserved.
5 * Copyright (c) 1980, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34#ifndef lint
35__COPYRIGHT("@(#) Copyright (c) 1980, 1993\
36 The Regents of the University of California.  All rights reserved.");
37#if 0
38static char sccsid[] = "@(#)getNAME.c	8.1 (Berkeley) 6/30/93";
39#else
40__RCSID("$NetBSD: getNAME.c,v 1.26 2008/07/20 01:09:07 lukem Exp $");
41#endif
42#endif /* not lint */
43
44/*
45 * Get name sections from manual pages.
46 *	-t	for building toc
47 *	-i	for building intro entries
48 *	-w	for querying type of manual source
49 *	-v	verbose
50 *	other	apropos database
51 */
52#include <err.h>
53#include <ctype.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#include <unistd.h>
58
59static int tocrc;
60static int intro;
61static int typeflag;
62static int verbose;
63
64#define SLOP 10	/* strlen(" () - ") < 10 */
65
66static char *linebuf = NULL;
67static size_t maxlen = 0;
68
69
70static void doname(char *);
71static void dorefname(char *);
72static void getfrom(char *);
73static void oldman(char *, char *);
74static void newman(char *, char *);
75static void remcomma(char *, size_t *);
76static void remquote(char *, size_t *);
77static void fixxref(char *, size_t *);
78static void split(char *, char *);
79__dead static void usage(void);
80
81int main(int, char *[]);
82
83/* The .SH NAMEs that are allowed. */
84static const char *names[] = { "name", "namn", 0 };
85
86int
87main(int argc, char *argv[])
88{
89	int ch;
90
91	while ((ch = getopt(argc, argv, "itvw")) != -1)
92		switch (ch) {
93		case 'i':
94			intro = 1;
95			break;
96		case 't':
97			tocrc = 1;
98			break;
99		case 'v':
100			verbose = 1;
101			break;
102		case 'w':
103			typeflag = 1;
104			break;
105		case '?':
106		default:
107			usage();
108		}
109	argc -= optind;
110	argv += optind;
111
112	if (!*argv)
113		usage();
114
115	for (; *argv; ++argv)
116		getfrom(*argv);
117	return 0;
118}
119
120static void
121getfrom(char *pathname)
122{
123	char *name;
124	char *line;
125	size_t len;
126
127	if (freopen(pathname, "r", stdin) == 0) {
128		warn("Cannot open `%s'", pathname);
129		return;
130	}
131	if ((name = strrchr(pathname, '/')) != NULL)
132		name++;
133	else
134		name = pathname;
135	for (;;) {
136		if ((line = fgetln(stdin, &len)) == NULL) {
137			if (typeflag)
138				(void)printf("%-60s\tUNKNOWN\n", pathname);
139			if (verbose)
140				warnx("missing .TH or .Dt section in `%s'",
141				    pathname);
142			return;
143		}
144		if (len < 3)
145			continue;
146		if (line[0] != '.')
147			continue;
148		if ((line[1] == 'T' && line[2] == 'H') ||
149		    (line[1] == 't' && line[2] == 'h')) {
150			oldman(pathname, name);
151			return;
152		}
153		if (line[1] == 'D' && line[2] == 't') {
154			newman(pathname, name);
155			return;
156		}
157	}
158}
159
160static void
161oldman(char *pathname, char *name)
162{
163	char *line, *ext, *s, *newlinebuf;
164	size_t len, i, extlen;
165	size_t curlen = 0;
166	size_t newmaxlen;
167	size_t ocurlen = -1;
168
169	if (typeflag) {
170		(void)printf("%-60s\tOLD\n", pathname);
171		return;
172	}
173	for (;;) {
174		if ((line = fgetln(stdin, &len)) == NULL) {
175			if (verbose)
176				warnx("missing .SH section in `%s'", pathname);
177			return;
178		}
179		if (len < 4)
180			continue;
181		if (line[0] != '.')
182			continue;
183		if (line[1] == 'S' && line[2] == 'H')
184			break;
185		if (line[1] == 's' && line[2] == 'h')
186			break;
187	}
188
189	for (s = &line[3]; s < &line[len] &&
190	    (isspace((unsigned char) *s) || *s == '"' || *s == '\''); s++)
191		continue;
192	if (s == &line[len]) {
193		warnx("missing argument to .SH in `%s'", pathname);
194		return;
195	}
196	for (i = 0; names[i]; i++)
197		if (strncasecmp(s, names[i], strlen(names[i])) == 0)
198			break;
199	if (names[i] == NULL) {
200		warnx("first .SH section is not \"NAME\" in `%s'", pathname);
201		return;
202	}
203
204 again:
205	if (tocrc)
206		doname(name);
207
208	for (i = 0;; i++) {
209		if ((line = fgetln(stdin, &len)) == NULL)
210			break;
211		if (line[0] == '.') {
212			if (line[1] == '\\' && line[2] == '"')
213				continue;	/* [nt]roff comment */
214			if (line[1] == 'S' && line[2] == 'H')
215				break;
216			if (line[1] == 's' && line[2] == 'h')
217				break;
218			if (line[1] == 'P' && line[2] == 'P')
219				break;
220			if (line[1] == 'b' && line[2] == 'r') {
221				if (intro)
222					split(linebuf, name);
223				else
224					(void)printf("%s\n", linebuf);
225				curlen = ocurlen;
226				goto again;
227			}
228		}
229		if (line[len - 1] == '\n') {
230			line[len - 1] = '\0';
231			len--;
232		}
233		if ((ext = strrchr(name, '.')) != NULL) {
234			ext++;
235			extlen = strlen(ext);
236		}
237		else
238			extlen = 0;
239
240		if (maxlen + extlen < curlen + len + SLOP) {
241			newmaxlen = 2 * (curlen + len) + SLOP + extlen;
242			if ((newlinebuf = realloc(linebuf, newmaxlen)) == NULL)
243				err(1, NULL);
244			linebuf = newlinebuf;
245			maxlen = newmaxlen;
246		}
247		if (i != 0)
248			linebuf[curlen++] = ' ';
249		(void)memcpy(&linebuf[curlen], line, len);
250		ocurlen = curlen;
251		curlen += len;
252		linebuf[curlen] = '\0';
253
254		if(!tocrc && !intro) {
255			/* change the \- into (N) - */
256			if ((s = strstr(linebuf, "\\-")) != NULL) {
257				(void)memmove(s + extlen + 3, s + 1,
258					      curlen - (s + 1 - linebuf));
259				curlen--;
260				if (extlen) {
261					*s++ = '(';
262					while (*ext)
263						*s++ = *ext++;
264					*s++ = ')';
265					*s++ = ' ';
266					curlen += extlen + 3;
267				}
268				linebuf[curlen] = '\0';
269			}
270		}
271	}
272
273	if (intro)
274		split(linebuf, name);
275	else
276		(void)printf("%s\n", linebuf);
277	return;
278}
279
280static void
281newman(char *pathname, char *name)
282{
283	char *line, *ext, *s, *newlinebuf;
284	size_t len, i, extlen;
285	size_t curlen = 0;
286	size_t newmaxlen;
287
288	if (typeflag) {
289		(void)printf("%-60s\tNEW\n", pathname);
290		return;
291	}
292	for (;;) {
293		if ((line = fgetln(stdin, &len)) == NULL) {
294			if (verbose)
295				warnx("missing .Sh section in `%s'", pathname);
296			return;
297		}
298		if (line[0] != '.')
299			continue;
300		if (line[1] == 'S' && line[2] == 'h')
301			break;
302	}
303
304	for (s = &line[3]; s < &line[len] && isspace((unsigned char) *s); s++)
305		continue;
306	if (s == &line[len]) {
307		warnx("missing argument to .Sh in `%s'", pathname);
308		return;
309	}
310	for (i = 0; names[i]; i++)
311		if (strncasecmp(s, names[i], strlen(names[i])) == 0)
312			break;
313	if (names[i] == NULL) {
314		warnx("first .SH section is not \"NAME\" in `%s'", pathname);
315		return;
316	}
317
318	if (tocrc)
319		doname(name);
320
321	for (i = 0;; i++) {
322		if ((line = fgetln(stdin, &len)) == NULL)
323			break;
324
325		if (line[0] == '.') {
326			if (line[1] == '\\' && line[2] == '"')
327				continue;	/* [nt]roff comment */
328			if (line[1] == 'S' && line[2] == 'h')
329				break;
330		}
331
332		if (line[len - 1] == '\n') {
333			line[len - 1] = '\0';
334			len--;
335		}
336
337		if ((ext = strrchr(name, '.')) != NULL) {
338			ext++;
339			extlen = strlen(ext);
340		}
341		else
342			extlen = 0;
343
344		if (maxlen + extlen < curlen + len + SLOP) {
345			newmaxlen = 2 * (curlen + len) + SLOP + extlen;
346			if ((newlinebuf = realloc(linebuf, newmaxlen)) == NULL)
347				err(1, NULL);
348			linebuf = newlinebuf;
349			maxlen = newmaxlen;
350		}
351
352		if (i != 0)
353			linebuf[curlen++] = ' ';
354
355		remcomma(line, &len);
356
357		if (line[0] != '.') {
358			(void)memcpy(&linebuf[curlen], line, len);
359			curlen += len;
360		}
361		else {
362			remquote(line, &len);
363			fixxref(line, &len);
364
365			/*
366			 * Put section and dash between names and description.
367			 */
368			if (line[1] == 'N' && line[2] == 'd') {
369				if(!tocrc && !intro) {
370					if (extlen) {
371						linebuf[curlen++] = '(';
372						while (*ext)
373							linebuf[curlen++] = *ext++;
374						linebuf[curlen++] = ')';
375						linebuf[curlen++] = ' ';
376					}
377				}
378				linebuf[curlen++] = '-';
379				linebuf[curlen++] = ' ';
380			}
381			/*
382			 * Skip over macro names.
383			 */
384			if (len <= 4)
385				continue;
386			(void)memcpy(&linebuf[curlen], &line[4], len - 4);
387			curlen += len - 4;
388		}
389	}
390	linebuf[curlen] = '\0';
391	if (intro)
392		split(linebuf, name);
393	else
394		(void)printf("%s\n", linebuf);
395}
396
397/*
398 * convert " ," -> " "
399 */
400static void
401remcomma(char *line, size_t *len)
402{
403	char *pline = line, *loc;
404	size_t plen = *len;
405
406	while ((loc = memchr(pline, ' ', plen)) != NULL) {
407		plen -= loc - pline + 1;
408		pline = loc;
409		if (loc[1] == ',') {
410			(void)memcpy(loc, &loc[1], plen);
411			(*len)--;
412		}
413		else
414			pline++;
415	}
416}
417
418/*
419 * Get rid of quotes in macros.
420 */
421static void
422remquote(char *line, size_t *len)
423{
424	char *loc;
425	char *pline = &line[4];
426	size_t plen = *len - 4;
427
428	if (*len < 4)
429		return;
430
431	while ((loc = memchr(pline, '"', plen)) != NULL) {
432		plen -= loc - pline + 1;
433		pline = loc;
434		(void)memcpy(loc, &loc[1], plen);
435		(*len)--;
436	}
437}
438
439/*
440 * Handle cross references
441 */
442static void
443fixxref(char *line, size_t *len)
444{
445	char *loc;
446	char *pline = &line[4];
447	size_t plen = *len - 4;
448
449	if (*len < 4)
450		return;
451
452	if (line[1] == 'X' && line[2] == 'r') {
453		if ((loc = memchr(pline, ' ', plen)) != NULL) {
454			*loc++ = '(';
455			loc++;
456			*loc++ = ')';
457			*len = loc - line;
458		}
459	}
460}
461
462static void
463doname(char *name)
464{
465	char *dp = name, *ep;
466
467again:
468	while (*dp && *dp != '.')
469		(void)putchar(*dp++);
470	if (*dp)
471		for (ep = dp+1; *ep; ep++)
472			if (*ep == '.') {
473				(void)putchar(*dp++);
474				goto again;
475			}
476	(void)putchar('(');
477	if (*dp)
478		dp++;
479	while (*dp)
480		(void)putchar(*dp++);
481	(void)putchar(')');
482	(void)putchar(' ');
483}
484
485static void
486split(char *line, char *name)
487{
488	char *cp, *dp;
489	char *sp;
490	const char *sep;
491
492	cp = strchr(line, '-');
493	if (cp == 0)
494		return;
495	sp = cp + 1;
496	for (--cp; *cp == ' ' || *cp == '\t' || *cp == '\\'; cp--)
497		;
498	*++cp = '\0';
499	while (*sp && (*sp == ' ' || *sp == '\t'))
500		sp++;
501	for (sep = "", dp = line; dp && *dp; dp = cp, sep = "\n") {
502		cp = strchr(dp, ',');
503		if (cp) {
504			char *tp;
505
506			for (tp = cp - 1; *tp == ' ' || *tp == '\t'; tp--)
507				;
508			*++tp = '\0';
509			for (++cp; *cp == ' ' || *cp == '\t'; cp++)
510				;
511		}
512		(void)printf("%s%s\t", sep, dp);
513		dorefname(name);
514		(void)printf("\t- %s", sp);
515	}
516	(void)putchar('\n');
517}
518
519static void
520dorefname(char *name)
521{
522	char *dp = name, *ep;
523
524again:
525	while (*dp && *dp != '.')
526		(void)putchar(*dp++);
527	if (*dp)
528		for (ep = dp+1; *ep; ep++)
529			if (*ep == '.') {
530				(void)putchar(*dp++);
531				goto again;
532			}
533	(void)putchar('.');
534	if (*dp)
535		dp++;
536	while (*dp)
537		(void)putchar(*dp++);
538}
539
540static void
541usage(void)
542{
543
544	(void)fprintf(stderr, "Usage: %s [-itw] file ...\n", getprogname());
545	exit(1);
546}
547