xref: /netbsd-src/usr.bin/apropos/apropos.c (revision 0407c1b8850d3033e32f0dba437f85c7f114dc84)
1*0407c1b8Swiz /*	$NetBSD: apropos.c,v 1.30 2009/05/08 12:48:43 wiz Exp $	*/
20a334e76Sglass 
344aa3887Scgd /*
46662274fSglass  * Copyright (c) 1987, 1993, 1994
544aa3887Scgd  *	The Regents of the University of California.  All rights reserved.
644aa3887Scgd  *
744aa3887Scgd  * Redistribution and use in source and binary forms, with or without
844aa3887Scgd  * modification, are permitted provided that the following conditions
944aa3887Scgd  * are met:
1044aa3887Scgd  * 1. Redistributions of source code must retain the above copyright
1144aa3887Scgd  *    notice, this list of conditions and the following disclaimer.
1244aa3887Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1344aa3887Scgd  *    notice, this list of conditions and the following disclaimer in the
1444aa3887Scgd  *    documentation and/or other materials provided with the distribution.
1589aaa1bbSagc  * 3. Neither the name of the University nor the names of its contributors
1644aa3887Scgd  *    may be used to endorse or promote products derived from this software
1744aa3887Scgd  *    without specific prior written permission.
1844aa3887Scgd  *
1944aa3887Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2044aa3887Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2144aa3887Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2244aa3887Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2344aa3887Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2444aa3887Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2544aa3887Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2644aa3887Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2744aa3887Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2844aa3887Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2944aa3887Scgd  * SUCH DAMAGE.
3044aa3887Scgd  */
3144aa3887Scgd 
329653f53eSmikel #include <sys/cdefs.h>
339653f53eSmikel 
3444aa3887Scgd #ifndef lint
3598e5374cSlukem __COPYRIGHT("@(#) Copyright (c) 1987, 1993, 1994\
3698e5374cSlukem  The Regents of the University of California.  All rights reserved.");
3744aa3887Scgd #endif /* not lint */
3844aa3887Scgd 
3944aa3887Scgd #ifndef lint
400a334e76Sglass #if 0
41b1db8b26Stls static char sccsid[] = "@(#)apropos.c	8.8 (Berkeley) 5/4/95";
420a334e76Sglass #else
43*0407c1b8Swiz __RCSID("$NetBSD: apropos.c,v 1.30 2009/05/08 12:48:43 wiz Exp $");
440a334e76Sglass #endif
4544aa3887Scgd #endif /* not lint */
4644aa3887Scgd 
4744aa3887Scgd #include <sys/param.h>
4844aa3887Scgd #include <sys/queue.h>
4944aa3887Scgd 
5044aa3887Scgd #include <ctype.h>
5144aa3887Scgd #include <err.h>
52effc1539Smikel #include <glob.h>
536662274fSglass #include <limits.h>
546f6f89d2Schristos #include <stdbool.h>
5544aa3887Scgd #include <stdio.h>
5644aa3887Scgd #include <stdlib.h>
5744aa3887Scgd #include <string.h>
58b1db8b26Stls #include <unistd.h>
5944aa3887Scgd 
603bcb486fSlukem #include "manconf.h"		/* from ../man/ */
613bcb486fSlukem #include "pathnames.h"		/* from ../man/ */
6244aa3887Scgd 
636f6f89d2Schristos static bool *found;
646f6f89d2Schristos static bool foundman = false;
6544aa3887Scgd 
6681a056bcSmrg #define	MAXLINELEN	8192		/* max line handled */
6781a056bcSmrg 
68*0407c1b8Swiz static void apropos(char **, const char *, bool, const char *, const char *);
69*0407c1b8Swiz static void lowstr(const char *, char *);
70*0407c1b8Swiz static bool match(const char *, const char *);
716f6f89d2Schristos static void usage(void) __dead;
726662274fSglass 
7344aa3887Scgd int
main(int argc,char * argv[])74971b39dfSxtraeme main(int argc, char *argv[])
7544aa3887Scgd {
7644aa3887Scgd 	ENTRY *ep;
7744aa3887Scgd 	TAG *tp;
7844aa3887Scgd 	int ch, rv;
79*0407c1b8Swiz 	char *conffile, *machine, **p, *p_augment, *p_path, *sflag;
80effc1539Smikel 	glob_t pg;
8144aa3887Scgd 
8244aa3887Scgd 	conffile = NULL;
8344aa3887Scgd 	p_augment = p_path = NULL;
84*0407c1b8Swiz 	machine = sflag = NULL;
85*0407c1b8Swiz 	while ((ch = getopt(argc, argv, "C:M:m:P:S:s:")) != -1) {
8644aa3887Scgd 		switch (ch) {
8744aa3887Scgd 		case 'C':
8844aa3887Scgd 			conffile = optarg;
8944aa3887Scgd 			break;
9044aa3887Scgd 		case 'M':
9144aa3887Scgd 		case 'P':		/* backward compatible */
9244aa3887Scgd 			p_path = optarg;
9344aa3887Scgd 			break;
9444aa3887Scgd 		case 'm':
9544aa3887Scgd 			p_augment = optarg;
9644aa3887Scgd 			break;
97*0407c1b8Swiz 		case 'S':
98*0407c1b8Swiz 			machine = optarg;
99*0407c1b8Swiz 			lowstr(machine, machine);
100*0407c1b8Swiz 			break;
101*0407c1b8Swiz 		case 's':
102*0407c1b8Swiz 			sflag = optarg;
103*0407c1b8Swiz 			lowstr(sflag, sflag);
104*0407c1b8Swiz 			break;
10544aa3887Scgd 		case '?':
10644aa3887Scgd 		default:
10744aa3887Scgd 			usage();
10844aa3887Scgd 		}
1096f6f89d2Schristos 	}
11044aa3887Scgd 	argv += optind;
11144aa3887Scgd 	argc -= optind;
11244aa3887Scgd 
11344aa3887Scgd 	if (argc < 1)
11444aa3887Scgd 		usage();
11544aa3887Scgd 
1166f6f89d2Schristos 	if ((found = malloc(argc * sizeof(*found))) == NULL)
1176f6f89d2Schristos 		err(EXIT_FAILURE, "malloc");
1186f6f89d2Schristos 	(void)memset(found, 0, argc * sizeof(*found));
11944aa3887Scgd 
12044aa3887Scgd 	for (p = argv; *p; ++p)			/* convert to lower-case */
12144aa3887Scgd 		lowstr(*p, *p);
12244aa3887Scgd 
12344aa3887Scgd 	if (p_augment)
124*0407c1b8Swiz 		apropos(argv, p_augment, true, sflag, machine);
12544aa3887Scgd 	if (p_path || (p_path = getenv("MANPATH")))
126*0407c1b8Swiz 		apropos(argv, p_path, true, sflag, machine);
12744aa3887Scgd 	else {
12844aa3887Scgd 		config(conffile);
129d09fe2c4Schuck 		tp = gettag("_whatdb", 1);
130d09fe2c4Schuck 		if (!tp)
131d09fe2c4Schuck 			errx(EXIT_FAILURE, "malloc");
132d09fe2c4Schuck 		TAILQ_FOREACH(ep, &tp->entrylist, q) {
133d39ba37cSkleink 			if ((rv = glob(ep->s, GLOB_BRACE | GLOB_NOSORT, NULL,
134d39ba37cSkleink 			    &pg)) != 0) {
135d39ba37cSkleink 				if (rv == GLOB_NOMATCH)
136d39ba37cSkleink 					continue;
137d39ba37cSkleink 				else
1380f794d6dSjdolecek 					err(EXIT_FAILURE, "glob");
139d39ba37cSkleink 			}
14044fd5cbfSmikel 			if (pg.gl_pathc)
14144fd5cbfSmikel 				for (p = pg.gl_pathv; *p; p++)
142*0407c1b8Swiz 					apropos(argv, *p, false, sflag,
143*0407c1b8Swiz 						machine);
144effc1539Smikel 			globfree(&pg);
145effc1539Smikel 		}
14644aa3887Scgd 	}
14744aa3887Scgd 
1486662274fSglass 	if (!foundman)
1496f6f89d2Schristos 		errx(EXIT_FAILURE, "no %s file found", _PATH_WHATIS);
1506662274fSglass 
15144aa3887Scgd 	rv = 1;
15244aa3887Scgd 	for (p = argv; *p; ++p)
15344aa3887Scgd 		if (found[p - argv])
15444aa3887Scgd 			rv = 0;
15544aa3887Scgd 		else
15644aa3887Scgd 			(void)printf("%s: nothing appropriate\n", *p);
1576f6f89d2Schristos 	return rv;
15844aa3887Scgd }
15944aa3887Scgd 
1606f6f89d2Schristos static void
apropos(char ** argv,const char * path,bool buildpath,const char * sflag,const char * machine)161*0407c1b8Swiz apropos(char **argv, const char *path, bool buildpath,
162*0407c1b8Swiz 	const char *sflag, const char *machine)
16344aa3887Scgd {
164*0407c1b8Swiz 	char *end, **p;
165*0407c1b8Swiz 	const char *name;
1666f6f89d2Schristos 	char buf[MAXLINELEN + 1];
1679653f53eSmikel 	char hold[MAXPATHLEN + 1];
1686f6f89d2Schristos 	char wbuf[MAXLINELEN + 1];
169*0407c1b8Swiz 	size_t slen = 0, mlen = 0;
170*0407c1b8Swiz 
171*0407c1b8Swiz 	if (sflag)
172*0407c1b8Swiz 		slen = strlen(sflag);
173*0407c1b8Swiz 	if (machine)
174*0407c1b8Swiz 		mlen = strlen(machine);
17544aa3887Scgd 
17644aa3887Scgd 	for (name = path; name; name = end) {	/* through name list */
1776f6f89d2Schristos 		if ((end = strchr(name, ':')) != NULL)
17844aa3887Scgd 			*end++ = '\0';
17944aa3887Scgd 
18044aa3887Scgd 		if (buildpath) {
181d09fe2c4Schuck 			(void)snprintf(hold, sizeof(hold), "%s/%s", name,
182d09fe2c4Schuck 					_PATH_WHATIS);
18344aa3887Scgd 			name = hold;
18444aa3887Scgd 		}
18544aa3887Scgd 
18644aa3887Scgd 		if (!freopen(name, "r", stdin))
18744aa3887Scgd 			continue;
18844aa3887Scgd 
1896f6f89d2Schristos 		foundman = true;
19044aa3887Scgd 
19144aa3887Scgd 		/* for each file found */
1926f6f89d2Schristos 		while (fgets(buf, (int)sizeof(buf), stdin)) {
1936662274fSglass 			if (!strchr(buf, '\n')) {
1946662274fSglass 				warnx("%s: line too long", name);
19544aa3887Scgd 				continue;
19644aa3887Scgd 			}
19744aa3887Scgd 			lowstr(buf, wbuf);
198*0407c1b8Swiz 			if (machine) {
199*0407c1b8Swiz 				if ((strncmp(wbuf, machine, mlen) != 0) ||
200*0407c1b8Swiz 				    strlen(wbuf) <= mlen || wbuf[mlen] != '/')
201*0407c1b8Swiz 					continue;
202*0407c1b8Swiz 			}
203*0407c1b8Swiz 			if (sflag) {
204*0407c1b8Swiz 				char *s = strchr(wbuf, '(');
205*0407c1b8Swiz 
206*0407c1b8Swiz 				if (!s)
207*0407c1b8Swiz 					continue;
208*0407c1b8Swiz 				if (strncmp(s+1, sflag, slen) != 0)
209*0407c1b8Swiz 					continue;
210*0407c1b8Swiz 			}
2116f6f89d2Schristos 			for (p = argv; *p; ++p) {
21244aa3887Scgd 				if (match(wbuf, *p)) {
21344aa3887Scgd 					(void)printf("%s", buf);
2146f6f89d2Schristos 					found[p - argv] = true;
21544aa3887Scgd 
21644aa3887Scgd 					/* only print line once */
21744aa3887Scgd 					while (*++p)
21844aa3887Scgd 						if (match(wbuf, *p))
2196f6f89d2Schristos 							found[p - argv] = true;
22044aa3887Scgd 					break;
22144aa3887Scgd 				}
22244aa3887Scgd 			}
22344aa3887Scgd 		}
22444aa3887Scgd 	}
2256f6f89d2Schristos }
22644aa3887Scgd 
22744aa3887Scgd /*
22844aa3887Scgd  * match --
22944aa3887Scgd  *	match anywhere the string appears
23044aa3887Scgd  */
2316f6f89d2Schristos static bool
match(const char * bp,const char * str)232*0407c1b8Swiz match(const char *bp, const char *str)
23344aa3887Scgd {
2346f6f89d2Schristos 	size_t len;
2356662274fSglass 	char test;
23644aa3887Scgd 
23744aa3887Scgd 	if (!*bp)
2386f6f89d2Schristos 		return false;
23944aa3887Scgd 	/* backward compatible: everything matches empty string */
24044aa3887Scgd 	if (!*str)
2416f6f89d2Schristos 		return true;
24244aa3887Scgd 	for (test = *str++, len = strlen(str); *bp;)
24344aa3887Scgd 		if (test == *bp++ && !strncmp(bp, str, len))
2446f6f89d2Schristos 			return true;
2456f6f89d2Schristos 	return false;
24644aa3887Scgd }
24744aa3887Scgd 
24844aa3887Scgd /*
24944aa3887Scgd  * lowstr --
25044aa3887Scgd  *	convert a string to lower case
25144aa3887Scgd  */
2526f6f89d2Schristos static void
lowstr(const char * from,char * to)253*0407c1b8Swiz lowstr(const char *from, char *to)
25444aa3887Scgd {
2556662274fSglass 	char ch;
25644aa3887Scgd 
25744aa3887Scgd 	while ((ch = *from++) && ch != '\n')
2583cca093eSdsl 		*to++ = tolower((unsigned char)ch);
25944aa3887Scgd 	*to = '\0';
26044aa3887Scgd }
26144aa3887Scgd 
26244aa3887Scgd /*
26344aa3887Scgd  * usage --
26444aa3887Scgd  *	print usage message and die
26544aa3887Scgd  */
2666f6f89d2Schristos __dead
2676f6f89d2Schristos static void
usage(void)268971b39dfSxtraeme usage(void)
26944aa3887Scgd {
270a8ec668dScgd 
27144aa3887Scgd 	(void)fprintf(stderr,
272*0407c1b8Swiz 	    "usage: %s [-C file] [-M path] [-m path] "
273*0407c1b8Swiz 	    "[-S subsection] [-s section]\n"
274*0407c1b8Swiz 	    "       keyword ...\n",
275a8ec668dScgd 	    getprogname());
27644aa3887Scgd 	exit(1);
27744aa3887Scgd }
278