xref: /original-bsd/usr.bin/apropos/apropos.c (revision 65ba69af)
1 /*
2  * Copyright (c) 1987 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1987 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)apropos.c	5.6 (Berkeley) 06/29/88";
26 #endif /* not lint */
27 
28 #include <sys/param.h>
29 #include <stdio.h>
30 #include <ctype.h>
31 #include <strings.h>
32 
33 #define	DEF_PATH	"/usr/man:/usr/new/man:/usr/local/man"
34 #define	MAXLINELEN	1000			/* max line handled */
35 #define	WHATIS		"whatis"		/* database name */
36 
37 #define	NO	0				/* no/false */
38 #define	YES	1				/* yes/true */
39 
40 static char *myname;
41 
42 main(argc, argv)
43 	int argc;
44 	char **argv;
45 {
46 	extern char *optarg;
47 	extern int optind;
48 	register char *beg, *end, **C;
49 	int ch, foundman = NO, *found, isapropos;
50 	int a_match(), w_match(), (*match)();
51 	char *manpath = NULL, buf[MAXLINELEN + 1], fname[MAXPATHLEN + 1];
52 	char wbuf[MAXLINELEN + 1], *getenv(), *malloc();
53 
54 	myname = (beg = rindex(*argv, '/')) ? beg + 1 : *argv;
55 	if (!strcmp(myname, "apropos")) {
56 		isapropos = YES;
57 		match = a_match;
58 	}
59 	else {
60 		isapropos = NO;
61 		match = w_match;
62 	}
63 	while ((ch = getopt(argc, argv, "M:P:")) != EOF)
64 		switch((char)ch) {
65 			case 'M':
66 			case 'P':		/* backward contemptible */
67 				manpath = optarg;
68 				break;
69 			case '?':
70 			default:
71 				usage();
72 		}
73 	argv += optind;
74 	argc -= optind;
75 	if (argc < 1)
76 		usage();
77 
78 	if (!(manpath = getenv("MANPATH")))
79 		manpath = DEF_PATH;
80 
81 	/*NOSTRICT*/
82 	if (!(found = (int *)malloc((u_int)argc))) {
83 		fprintf(stderr, "%s: out of space.\n", myname);
84 		exit(1);
85 	}
86 	bzero((char *)found, argc * sizeof(int));
87 
88 	if (isapropos)
89 		for (C = argv; *C; ++C)		/* convert to lower-case */
90 			lowstr(*C, *C);
91 	else for (C = argv; *C; ++C)		/* trim full paths */
92 		if (beg = rindex(*C, '/'))
93 			*C = beg + 1;
94 
95 	for (beg = manpath; beg; beg = end) {	/* through path list */
96 		end = index(beg, ':');
97 		if (!end)
98 			(void)sprintf(fname, "%s/%s", beg, WHATIS);
99 		else {
100 			(void)sprintf(fname, "%.*s/%s", end - beg, beg, WHATIS);
101 			++end;
102 		}
103 		if (!freopen(fname, "r", stdin))
104 			continue;
105 
106 						/* for each file found */
107 		for (foundman = YES; gets(buf);) {
108 			if (isapropos)
109 				lowstr(buf, wbuf);
110 			else
111 				dashtrunc(buf, wbuf);
112 			for (C = argv; *C; ++C)
113 				if ((*match)(wbuf, *C)) {
114 					puts(buf);
115 					found[C - argv] = YES;
116 
117 					/* only print line once */
118 					while (*++C)
119 						if ((*match)(wbuf, *C))
120 							found[C - argv] = YES;
121 					break;
122 				}
123 		}
124 	}
125 	if (!foundman) {
126 		fprintf(stderr, "%s: no %s file found in %s.\n", myname, WHATIS, manpath);
127 		exit(1);
128 	}
129 	for (C = argv; *C; ++C)
130 		if (!found[C - argv])
131 			printf("%s: %s\n", *C, isapropos ? "nothing appropriate" : "not found");
132 }
133 
134 /*
135  * a_match --
136  *	match for apropos; anywhere the string appears
137  */
138 static
139 a_match(bp, str)
140 	register char *bp, *str;
141 {
142 	register int len;
143 	register char test;
144 
145 	if (!*bp)
146 		return(NO);
147 	/* backward compatible: everything matches empty string */
148 	if (!*str)
149 		return(YES);
150 	for (test = *str++, len = strlen(str); *bp;)
151 		if (test == *bp++ && !strncmp(bp, str, len))
152 			return(YES);
153 	return(NO);
154 }
155 
156 /*
157  * w_match --
158  *	match for whatis; looks for full word match
159  */
160 static
161 w_match(bp, str)
162 	register char *bp, *str;
163 {
164 	register int len;
165 	register char *start;
166 
167 	if (!*str || !*bp)
168 		return(NO);
169 	for (len = strlen(str);;) {
170 		for (; *bp && !isdigit(*bp) && !isalpha(*bp); ++bp);
171 		if (!*bp)
172 			break;
173 		for (start = bp++; *bp && (isdigit(*bp) || isalpha(*bp)); ++bp);
174 		if (bp - start == len && !strncasecmp(start, str, len))
175 			return(YES);
176 	}
177 	return(NO);
178 }
179 
180 /*
181  * dashtrunc --
182  *	truncate a string at " - "
183  */
184 static
185 dashtrunc(from, to)
186 	register char *from, *to;
187 {
188 	do {
189 		if (from[0] == ' ' && from[1] == '-' && from[2] == ' ')
190 			break;
191 	} while (*to++ = *from++);
192 	*to = '\0';
193 }
194 
195 /*
196  * lowstr --
197  *	convert a string to lower case
198  */
199 static
200 lowstr(from, to)
201 	register char *from, *to;
202 {
203 	do {
204 		*to++ = isupper(*from) ? tolower(*from) : *from;
205 	} while (*from++);
206 }
207 
208 /*
209  * usage --
210  *	print usage message and die
211  */
212 static
213 usage()
214 {
215 	fprintf(stderr, "usage: %s [-M path] string ...\n", myname);
216 	exit(1);
217 }
218