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