xref: /netbsd/libexec/getNAME/getNAME.c (revision 6550d01e)
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
38 static 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 
59 static int tocrc;
60 static int intro;
61 static int typeflag;
62 static int verbose;
63 
64 #define SLOP 10	/* strlen(" () - ") < 10 */
65 
66 static char *linebuf = NULL;
67 static size_t maxlen = 0;
68 
69 
70 static void doname(char *);
71 static void dorefname(char *);
72 static void getfrom(char *);
73 static void oldman(char *, char *);
74 static void newman(char *, char *);
75 static void remcomma(char *, size_t *);
76 static void remquote(char *, size_t *);
77 static void fixxref(char *, size_t *);
78 static void split(char *, char *);
79 static void usage(void);
80 
81 int main(int, char *[]);
82 
83 /* The .SH NAMEs that are allowed. */
84 static const char *names[] = { "name", "namn", 0 };
85 
86 int
87 main(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 
120 static void
121 getfrom(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 
160 static void
161 oldman(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 
280 static void
281 newman(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  */
400 static void
401 remcomma(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  */
421 static void
422 remquote(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  */
442 static void
443 fixxref(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 
462 static void
463 doname(char *name)
464 {
465 	char *dp = name, *ep;
466 
467 again:
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 
485 static void
486 split(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 
519 static void
520 dorefname(char *name)
521 {
522 	char *dp = name, *ep;
523 
524 again:
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 
540 static void
541 usage(void)
542 {
543 
544 	(void)fprintf(stderr, "Usage: %s [-itw] file ...\n", getprogname());
545 	exit(1);
546 }
547