xref: /dragonfly/usr.bin/whereis/whereis.c (revision 6d08986d)
1a1b96748SDavid Rhodus /*
21765f996Szrj  * Copyright (c) 2002, Jörg Wunsch
3a1b96748SDavid Rhodus  *
4a1b96748SDavid Rhodus  * Redistribution and use in source and binary forms, with or without
5a1b96748SDavid Rhodus  * modification, are permitted provided that the following conditions
6a1b96748SDavid Rhodus  * are met:
7a1b96748SDavid Rhodus  * 1. Redistributions of source code must retain the above copyright
8a1b96748SDavid Rhodus  *    notice, this list of conditions and the following disclaimer.
9a1b96748SDavid Rhodus  * 2. Redistributions in binary form must reproduce the above copyright
10a1b96748SDavid Rhodus  *    notice, this list of conditions and the following disclaimer in the
11a1b96748SDavid Rhodus  *    documentation and/or other materials provided with the distribution.
12a1b96748SDavid Rhodus  *
13a1b96748SDavid Rhodus  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
14a1b96748SDavid Rhodus  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15a1b96748SDavid Rhodus  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16a1b96748SDavid Rhodus  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
17a1b96748SDavid Rhodus  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18a1b96748SDavid Rhodus  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19a1b96748SDavid Rhodus  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20a1b96748SDavid Rhodus  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
21a1b96748SDavid Rhodus  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
22a1b96748SDavid Rhodus  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
23a1b96748SDavid Rhodus  * POSSIBILITY OF SUCH DAMAGE.
24a1b96748SDavid Rhodus  * $FreeBSD: src/usr.bin/whereis/whereis.c,v 1.12 2002/08/22 01:50:51 johan Exp $
25a1b96748SDavid Rhodus  */
26a1b96748SDavid Rhodus 
27a1b96748SDavid Rhodus /*
28a1b96748SDavid Rhodus  * 4.3BSD UI-compatible whereis(1) utility.  Rewritten from scratch
29a1b96748SDavid Rhodus  * since the original 4.3BSD version suffers legal problems that
30a1b96748SDavid Rhodus  * prevent it from being redistributed, and since the 4.4BSD version
31a1b96748SDavid Rhodus  * was pretty inferior in functionality.
32a1b96748SDavid Rhodus  */
33a1b96748SDavid Rhodus 
34a1b96748SDavid Rhodus #include <sys/types.h>
35a1b96748SDavid Rhodus 
36a1b96748SDavid Rhodus 
37a1b96748SDavid Rhodus #include <sys/stat.h>
38a1b96748SDavid Rhodus #include <sys/sysctl.h>
39a1b96748SDavid Rhodus 
40a1b96748SDavid Rhodus #include <dirent.h>
41a1b96748SDavid Rhodus #include <err.h>
42a1b96748SDavid Rhodus #include <errno.h>
4342bcedebSHiten Pandya #include <locale.h>
44a1b96748SDavid Rhodus #include <regex.h>
45a1b96748SDavid Rhodus #include <stdio.h>
46a1b96748SDavid Rhodus #include <stdlib.h>
47a1b96748SDavid Rhodus #include <string.h>
48a1b96748SDavid Rhodus #include <sysexits.h>
49a1b96748SDavid Rhodus #include <unistd.h>
50a1b96748SDavid Rhodus 
51a1b96748SDavid Rhodus #include "pathnames.h"
52a1b96748SDavid Rhodus 
53a1b96748SDavid Rhodus #define	NO_BIN_FOUND	1
54a1b96748SDavid Rhodus #define	NO_MAN_FOUND	2
55a1b96748SDavid Rhodus #define	NO_SRC_FOUND	4
56a1b96748SDavid Rhodus 
57a1b96748SDavid Rhodus typedef const char *ccharp;
58a1b96748SDavid Rhodus 
59bb7c8242SSascha Wildner static int opt_a, opt_b, opt_m, opt_q, opt_s, opt_u, opt_x;
60bb7c8242SSascha Wildner static ccharp *bindirs, *mandirs, *sourcedirs;
61bb7c8242SSascha Wildner static char **query;
62a1b96748SDavid Rhodus 
63bb7c8242SSascha Wildner static const char *sourcepath = PATH_SOURCES;
64a1b96748SDavid Rhodus 
65bb7c8242SSascha Wildner static char	*colonify(ccharp *);
66bb7c8242SSascha Wildner static int	 contains(ccharp *, const char *);
67bb7c8242SSascha Wildner static void	 decolonify(char *, ccharp **, int *);
68bb7c8242SSascha Wildner static void	 defaults(void);
69bb7c8242SSascha Wildner static void	 scanopts(int, char **);
70*6d08986dSSascha Wildner static void	 usage(void) __dead2;
71a1b96748SDavid Rhodus 
72a1b96748SDavid Rhodus /*
73a1b96748SDavid Rhodus  * Throughout this program, a number of strings are dynamically
74a1b96748SDavid Rhodus  * allocated but never freed.  Their memory is written to when
75a1b96748SDavid Rhodus  * splitting the strings into string lists which will later be
76a1b96748SDavid Rhodus  * processed.  Since it's important that those string lists remain
77a1b96748SDavid Rhodus  * valid even after the functions allocating the memory returned,
78a1b96748SDavid Rhodus  * those functions cannot free them.  They could be freed only at end
79a1b96748SDavid Rhodus  * of main(), which is pretty pointless anyway.
80a1b96748SDavid Rhodus  *
81a1b96748SDavid Rhodus  * The overall amount of memory to be allocated for processing the
82a1b96748SDavid Rhodus  * strings is not expected to exceed a few kilobytes.  For that
83a1b96748SDavid Rhodus  * reason, allocation can usually always be assumed to succeed (within
84a1b96748SDavid Rhodus  * a virtual memory environment), thus we simply bail out using
85a1b96748SDavid Rhodus  * abort(3) in case of an allocation failure.
86a1b96748SDavid Rhodus  */
87a1b96748SDavid Rhodus 
88bb7c8242SSascha Wildner static void
usage(void)89a1b96748SDavid Rhodus usage(void)
90a1b96748SDavid Rhodus {
91a1b96748SDavid Rhodus 	errx(EX_USAGE,
92a1b96748SDavid Rhodus 	     "usage: whereis [-abmqsux] [-BMS dir... -f] name ...");
93a1b96748SDavid Rhodus }
94a1b96748SDavid Rhodus 
95a1b96748SDavid Rhodus /*
96a1b96748SDavid Rhodus  * Scan options passed to program.
97a1b96748SDavid Rhodus  *
98a1b96748SDavid Rhodus  * Note that the -B/-M/-S options expect a list of directory
99a1b96748SDavid Rhodus  * names that must be terminated with -f.
100a1b96748SDavid Rhodus  */
101bb7c8242SSascha Wildner static void
scanopts(int argc,char ** argv)102a1b96748SDavid Rhodus scanopts(int argc, char **argv)
103a1b96748SDavid Rhodus {
1041330fdf1SLiam J. Foy 	int c, i;
105a1b96748SDavid Rhodus 	ccharp **dirlist;
106a1b96748SDavid Rhodus 
107a1b96748SDavid Rhodus 	while ((c = getopt(argc, argv, "BMSabfmqsux")) != -1)
108a1b96748SDavid Rhodus 		switch (c) {
109a1b96748SDavid Rhodus 		case 'B':
110a1b96748SDavid Rhodus 			dirlist = &bindirs;
111a1b96748SDavid Rhodus 			goto dolist;
112a1b96748SDavid Rhodus 
113a1b96748SDavid Rhodus 		case 'M':
114a1b96748SDavid Rhodus 			dirlist = &mandirs;
115a1b96748SDavid Rhodus 			goto dolist;
116a1b96748SDavid Rhodus 
117a1b96748SDavid Rhodus 		case 'S':
118a1b96748SDavid Rhodus 			dirlist = &sourcedirs;
119a1b96748SDavid Rhodus 		  dolist:
120a1b96748SDavid Rhodus 			i = 0;
121a1b96748SDavid Rhodus 			*dirlist = realloc(*dirlist, (i + 1) * sizeof(char *));
122a1b96748SDavid Rhodus 			(*dirlist)[i] = NULL;
123a1b96748SDavid Rhodus 			while (optind < argc &&
124a1b96748SDavid Rhodus 			       strcmp(argv[optind], "-f") != 0 &&
125a1b96748SDavid Rhodus 			       strcmp(argv[optind], "-B") != 0 &&
126a1b96748SDavid Rhodus 			       strcmp(argv[optind], "-M") != 0 &&
127a1b96748SDavid Rhodus 			       strcmp(argv[optind], "-S") != 0) {
128a1b96748SDavid Rhodus 				decolonify(argv[optind], dirlist, &i);
129a1b96748SDavid Rhodus 				optind++;
130a1b96748SDavid Rhodus 			}
131a1b96748SDavid Rhodus 			break;
132a1b96748SDavid Rhodus 
133a1b96748SDavid Rhodus 		case 'a':
134a1b96748SDavid Rhodus 			opt_a = 1;
135a1b96748SDavid Rhodus 			break;
136a1b96748SDavid Rhodus 
137a1b96748SDavid Rhodus 		case 'b':
138a1b96748SDavid Rhodus 			opt_b = 1;
139a1b96748SDavid Rhodus 			break;
140a1b96748SDavid Rhodus 
141a1b96748SDavid Rhodus 		case 'f':
142a1b96748SDavid Rhodus 			goto breakout;
143a1b96748SDavid Rhodus 
144a1b96748SDavid Rhodus 		case 'm':
145a1b96748SDavid Rhodus 			opt_m = 1;
146a1b96748SDavid Rhodus 			break;
147a1b96748SDavid Rhodus 
148a1b96748SDavid Rhodus 		case 'q':
149a1b96748SDavid Rhodus 			opt_q = 1;
150a1b96748SDavid Rhodus 			break;
151a1b96748SDavid Rhodus 
152a1b96748SDavid Rhodus 		case 's':
153a1b96748SDavid Rhodus 			opt_s = 1;
154a1b96748SDavid Rhodus 			break;
155a1b96748SDavid Rhodus 
156a1b96748SDavid Rhodus 		case 'u':
157a1b96748SDavid Rhodus 			opt_u = 1;
158a1b96748SDavid Rhodus 			break;
159a1b96748SDavid Rhodus 
160a1b96748SDavid Rhodus 		case 'x':
161a1b96748SDavid Rhodus 			opt_x = 1;
162a1b96748SDavid Rhodus 			break;
163a1b96748SDavid Rhodus 
164a1b96748SDavid Rhodus 		default:
165a1b96748SDavid Rhodus 			usage();
166a1b96748SDavid Rhodus 		}
167a1b96748SDavid Rhodus breakout:
168a1b96748SDavid Rhodus 	if (optind == argc)
169a1b96748SDavid Rhodus 		usage();
170a1b96748SDavid Rhodus 	query = argv + optind;
171a1b96748SDavid Rhodus }
172a1b96748SDavid Rhodus 
173a1b96748SDavid Rhodus /*
174a1b96748SDavid Rhodus  * Find out whether string `s' is contained in list `cpp'.
175a1b96748SDavid Rhodus  */
176bb7c8242SSascha Wildner static int
contains(ccharp * cpp,const char * s)177a1b96748SDavid Rhodus contains(ccharp *cpp, const char *s)
178a1b96748SDavid Rhodus {
179a1b96748SDavid Rhodus 	ccharp cp;
180a1b96748SDavid Rhodus 
181a1b96748SDavid Rhodus 	if (cpp == NULL)
182a1b96748SDavid Rhodus 		return (0);
183a1b96748SDavid Rhodus 
184a1b96748SDavid Rhodus 	while ((cp = *cpp) != NULL) {
185a1b96748SDavid Rhodus 		if (strcmp(cp, s) == 0)
186a1b96748SDavid Rhodus 			return (1);
187a1b96748SDavid Rhodus 		cpp++;
188a1b96748SDavid Rhodus 	}
189a1b96748SDavid Rhodus 	return (0);
190a1b96748SDavid Rhodus }
191a1b96748SDavid Rhodus 
192a1b96748SDavid Rhodus /*
193a1b96748SDavid Rhodus  * Split string `s' at colons, and pass it to the string list pointed
194a1b96748SDavid Rhodus  * to by `cppp' (which has `*ip' elements).  Note that the original
195a1b96748SDavid Rhodus  * string is modified by replacing the colon with a NUL byte.  The
196a1b96748SDavid Rhodus  * partial string is only added if it has a length greater than 0, and
197a1b96748SDavid Rhodus  * if it's not already contained in the string list.
198a1b96748SDavid Rhodus  */
199bb7c8242SSascha Wildner static void
decolonify(char * s,ccharp ** cppp,int * ip)200a1b96748SDavid Rhodus decolonify(char *s, ccharp **cppp, int *ip)
201a1b96748SDavid Rhodus {
202a1b96748SDavid Rhodus 	char *cp;
203a1b96748SDavid Rhodus 
204a1b96748SDavid Rhodus 	while ((cp = strchr(s, ':')), *s != '\0') {
205a1b96748SDavid Rhodus 		if (cp)
206a1b96748SDavid Rhodus 			*cp = '\0';
207a1b96748SDavid Rhodus 		if (strlen(s) && !contains(*cppp, s)) {
208a1b96748SDavid Rhodus 			*cppp = realloc(*cppp, (*ip + 2) * sizeof(char *));
209a1b96748SDavid Rhodus 			if (cppp == NULL)
210a1b96748SDavid Rhodus 				abort();
211a1b96748SDavid Rhodus 			(*cppp)[*ip] = s;
212a1b96748SDavid Rhodus 			(*cppp)[*ip + 1] = NULL;
213a1b96748SDavid Rhodus 			(*ip)++;
214a1b96748SDavid Rhodus 		}
215a1b96748SDavid Rhodus 		if (cp)
216a1b96748SDavid Rhodus 			s = cp + 1;
217a1b96748SDavid Rhodus 		else
218a1b96748SDavid Rhodus 			break;
219a1b96748SDavid Rhodus 	}
220a1b96748SDavid Rhodus }
221a1b96748SDavid Rhodus 
222a1b96748SDavid Rhodus /*
223a1b96748SDavid Rhodus  * Join string list `cpp' into a colon-separated string.
224a1b96748SDavid Rhodus  */
225bb7c8242SSascha Wildner static char *
colonify(ccharp * cpp)226a1b96748SDavid Rhodus colonify(ccharp *cpp)
227a1b96748SDavid Rhodus {
228a1b96748SDavid Rhodus 	size_t s;
229a1b96748SDavid Rhodus 	char *cp;
230a1b96748SDavid Rhodus 	int i;
231a1b96748SDavid Rhodus 
232a1b96748SDavid Rhodus 	if (cpp == NULL)
233a1b96748SDavid Rhodus 		return (0);
234a1b96748SDavid Rhodus 
235a1b96748SDavid Rhodus 	for (s = 0, i = 0; cpp[i] != NULL; i++)
236a1b96748SDavid Rhodus 		s += strlen(cpp[i]) + 1;
237a1b96748SDavid Rhodus 	if ((cp = malloc(s + 1)) == NULL)
238a1b96748SDavid Rhodus 		abort();
239a1b96748SDavid Rhodus 	for (i = 0, *cp = '\0'; cpp[i] != NULL; i++) {
240a1b96748SDavid Rhodus 		strcat(cp, cpp[i]);
241a1b96748SDavid Rhodus 		strcat(cp, ":");
242a1b96748SDavid Rhodus 	}
243a1b96748SDavid Rhodus 	cp[s - 1] = '\0';		/* eliminate last colon */
244a1b96748SDavid Rhodus 
245a1b96748SDavid Rhodus 	return (cp);
246a1b96748SDavid Rhodus }
247a1b96748SDavid Rhodus 
248a1b96748SDavid Rhodus /*
249a1b96748SDavid Rhodus  * Provide defaults for all options and directory lists.
250a1b96748SDavid Rhodus  */
251bb7c8242SSascha Wildner static void
defaults(void)252a1b96748SDavid Rhodus defaults(void)
253a1b96748SDavid Rhodus {
254a1b96748SDavid Rhodus 	size_t s;
255a1b96748SDavid Rhodus 	char *b, buf[BUFSIZ], *cp;
256a1b96748SDavid Rhodus 	int nele;
257a1b96748SDavid Rhodus 	FILE *p;
258a1b96748SDavid Rhodus 	DIR *dir;
259a1b96748SDavid Rhodus 	struct stat sb;
260a1b96748SDavid Rhodus 	struct dirent *dirp;
261a1b96748SDavid Rhodus 
262a1b96748SDavid Rhodus 	/* default to -bms if none has been specified */
263a1b96748SDavid Rhodus 	if (!opt_b && !opt_m && !opt_s)
264a1b96748SDavid Rhodus 		opt_b = opt_m = opt_s = 1;
265a1b96748SDavid Rhodus 
266a1b96748SDavid Rhodus 	/* -b defaults to default path + /usr/libexec +
267a1b96748SDavid Rhodus 	 * /usr/games + user's path */
268a1b96748SDavid Rhodus 	if (!bindirs) {
2693641b7caSSascha Wildner 		if (sysctlbyname("user.cs_path", NULL, &s, NULL, 0) == -1)
270a1b96748SDavid Rhodus 			err(EX_OSERR, "sysctlbyname(\"user.cs_path\")");
271a1b96748SDavid Rhodus 		if ((b = malloc(s + 1)) == NULL)
272a1b96748SDavid Rhodus 			abort();
2733641b7caSSascha Wildner 		if (sysctlbyname("user.cs_path", b, &s, NULL, 0) == -1)
274a1b96748SDavid Rhodus 			err(EX_OSERR, "sysctlbyname(\"user.cs_path\")");
275a1b96748SDavid Rhodus 		nele = 0;
276a1b96748SDavid Rhodus 		decolonify(b, &bindirs, &nele);
277a1b96748SDavid Rhodus 		bindirs = realloc(bindirs, (nele + 3) * sizeof(char *));
278a1b96748SDavid Rhodus 		if (bindirs == NULL)
279a1b96748SDavid Rhodus 			abort();
280a1b96748SDavid Rhodus 		bindirs[nele++] = PATH_LIBEXEC;
281a1b96748SDavid Rhodus 		bindirs[nele++] = PATH_GAMES;
282a1b96748SDavid Rhodus 		bindirs[nele] = NULL;
283a1b96748SDavid Rhodus 		if ((cp = getenv("PATH")) != NULL) {
284a1b96748SDavid Rhodus 			/* don't destroy the original environment... */
285a1b96748SDavid Rhodus 			if ((b = malloc(strlen(cp) + 1)) == NULL)
286a1b96748SDavid Rhodus 				abort();
287a1b96748SDavid Rhodus 			strcpy(b, cp);
288a1b96748SDavid Rhodus 			decolonify(b, &bindirs, &nele);
289a1b96748SDavid Rhodus 		}
290a1b96748SDavid Rhodus 	}
291a1b96748SDavid Rhodus 
292a1b96748SDavid Rhodus 	/* -m defaults to $(manpath) */
293a1b96748SDavid Rhodus 	if (!mandirs) {
294a1b96748SDavid Rhodus 		if ((p = popen(MANPATHCMD, "r")) == NULL)
295a1b96748SDavid Rhodus 			err(EX_OSERR, "cannot execute manpath command");
296a1b96748SDavid Rhodus 		if (fgets(buf, BUFSIZ - 1, p) == NULL ||
297a1b96748SDavid Rhodus 		    pclose(p))
298a1b96748SDavid Rhodus 			err(EX_OSERR, "error processing manpath results");
299a1b96748SDavid Rhodus 		if ((b = strchr(buf, '\n')) != NULL)
300a1b96748SDavid Rhodus 			*b = '\0';
301a1b96748SDavid Rhodus 		if ((b = malloc(strlen(buf) + 1)) == NULL)
302a1b96748SDavid Rhodus 			abort();
303a1b96748SDavid Rhodus 		strcpy(b, buf);
304a1b96748SDavid Rhodus 		nele = 0;
305a1b96748SDavid Rhodus 		decolonify(b, &mandirs, &nele);
306a1b96748SDavid Rhodus 	}
307a1b96748SDavid Rhodus 
30845fa0bf8SSascha Wildner 	/* -s defaults to precompiled list, plus subdirs of /usr/dports */
309a1b96748SDavid Rhodus 	if (!sourcedirs) {
310a1b96748SDavid Rhodus 		if ((b = malloc(strlen(sourcepath) + 1)) == NULL)
311a1b96748SDavid Rhodus 			abort();
312a1b96748SDavid Rhodus 		strcpy(b, sourcepath);
313a1b96748SDavid Rhodus 		nele = 0;
314a1b96748SDavid Rhodus 		decolonify(b, &sourcedirs, &nele);
315a1b96748SDavid Rhodus 
316cc4a00acSSascha Wildner 		if (stat(PATH_DPORTS, &sb) == -1) {
317cc4a00acSSascha Wildner 			if (errno != ENOENT)
318cc4a00acSSascha Wildner 				err(EX_OSERR, "stat(" PATH_DPORTS ")");
319cc4a00acSSascha Wildner 		} else {
320cc4a00acSSascha Wildner 			if ((sb.st_mode & S_IFMT) != S_IFDIR)
321cc4a00acSSascha Wildner 				/* /usr/dports is not a directory, ignore */
322cc4a00acSSascha Wildner 				return;
323cc4a00acSSascha Wildner 			if (access(PATH_DPORTS, R_OK | X_OK) != 0)
324cc4a00acSSascha Wildner 				return;
325cc4a00acSSascha Wildner 			if ((dir = opendir(PATH_DPORTS)) == NULL)
326cc4a00acSSascha Wildner 				err(EX_OSERR, "opendir" PATH_DPORTS ")");
327cc4a00acSSascha Wildner 			while ((dirp = readdir(dir)) != NULL) {
328cc4a00acSSascha Wildner 				if (dirp->d_name[0] == '.')
329cc4a00acSSascha Wildner 					/* ignore dot entries */
330cc4a00acSSascha Wildner 					continue;
331cc4a00acSSascha Wildner 				b = malloc(sizeof PATH_DPORTS + 1 +
332cc4a00acSSascha Wildner 				    dirp->d_namlen);
333cc4a00acSSascha Wildner 				if (b == NULL)
334cc4a00acSSascha Wildner 					abort();
335cc4a00acSSascha Wildner 				strcpy(b, PATH_DPORTS);
336cc4a00acSSascha Wildner 				strcat(b, "/");
337cc4a00acSSascha Wildner 				strcat(b, dirp->d_name);
338cc4a00acSSascha Wildner 				if (stat(b, &sb) == -1 ||
339cc4a00acSSascha Wildner 				    (sb.st_mode & S_IFMT) != S_IFDIR ||
340cc4a00acSSascha Wildner 				    access(b, R_OK | X_OK) != 0) {
341cc4a00acSSascha Wildner 					free(b);
342cc4a00acSSascha Wildner 					continue;
343cc4a00acSSascha Wildner 				}
344cc4a00acSSascha Wildner 				sourcedirs = realloc(sourcedirs,
345cc4a00acSSascha Wildner 				    (nele + 2) * sizeof(char *));
346cc4a00acSSascha Wildner 				if (sourcedirs == NULL)
347cc4a00acSSascha Wildner 					abort();
348cc4a00acSSascha Wildner 				sourcedirs[nele++] = b;
349cc4a00acSSascha Wildner 				sourcedirs[nele] = NULL;
350cc4a00acSSascha Wildner 			}
351cc4a00acSSascha Wildner 			closedir(dir);
352cc4a00acSSascha Wildner 		}
353a1b96748SDavid Rhodus 	}
354a1b96748SDavid Rhodus }
355a1b96748SDavid Rhodus 
356a1b96748SDavid Rhodus int
main(int argc,char ** argv)357a1b96748SDavid Rhodus main(int argc, char **argv)
358a1b96748SDavid Rhodus {
359a1b96748SDavid Rhodus 	int unusual, i, printed;
360a1b96748SDavid Rhodus 	char *bin, buf[BUFSIZ], *cp, *cp2, *man, *name, *src;
361a1b96748SDavid Rhodus 	ccharp *dp;
362a1b96748SDavid Rhodus 	size_t nlen, olen, s;
363a1b96748SDavid Rhodus 	struct stat sb;
364a1b96748SDavid Rhodus 	regex_t re, re2;
365a1b96748SDavid Rhodus 	regmatch_t matches[2];
366a1b96748SDavid Rhodus 	regoff_t rlen;
367a1b96748SDavid Rhodus 	FILE *p;
368a1b96748SDavid Rhodus 
36942bcedebSHiten Pandya 	setlocale(LC_ALL, "");
370a1b96748SDavid Rhodus 	scanopts(argc, argv);
371a1b96748SDavid Rhodus 	defaults();
372a1b96748SDavid Rhodus 
373a1b96748SDavid Rhodus 	if (mandirs == NULL)
374a1b96748SDavid Rhodus 		opt_m = 0;
375a1b96748SDavid Rhodus 	if (bindirs == NULL)
376a1b96748SDavid Rhodus 		opt_b = 0;
377a1b96748SDavid Rhodus 	if (sourcedirs == NULL)
378a1b96748SDavid Rhodus 		opt_s = 0;
379a1b96748SDavid Rhodus 	if (opt_m + opt_b + opt_s == 0)
380a1b96748SDavid Rhodus 		errx(EX_DATAERR, "no directories to search");
381a1b96748SDavid Rhodus 
382a1b96748SDavid Rhodus 	if (opt_m) {
3836fb88001SSimon Schubert 		if (setenv("MANPATH", colonify(mandirs), 1) == -1)
3846fb88001SSimon Schubert 			err(1, "setenv: cannot set MANPATH=%s", colonify(mandirs));
385a1b96748SDavid Rhodus 		if ((i = regcomp(&re, MANWHEREISMATCH, REG_EXTENDED)) != 0) {
386a1b96748SDavid Rhodus 			regerror(i, &re, buf, BUFSIZ - 1);
387a1b96748SDavid Rhodus 			errx(EX_UNAVAILABLE, "regcomp(%s) failed: %s",
388a1b96748SDavid Rhodus 			     MANWHEREISMATCH, buf);
389a1b96748SDavid Rhodus 		}
390a1b96748SDavid Rhodus 	}
391a1b96748SDavid Rhodus 
392a1b96748SDavid Rhodus 	for (; (name = *query) != NULL; query++) {
393a1b96748SDavid Rhodus 		/* strip leading path name component */
394a1b96748SDavid Rhodus 		if ((cp = strrchr(name, '/')) != NULL)
395a1b96748SDavid Rhodus 			name = cp + 1;
396a1b96748SDavid Rhodus 		/* strip SCCS or RCS suffix/prefix */
397a1b96748SDavid Rhodus 		if (strlen(name) > 2 && strncmp(name, "s.", 2) == 0)
398a1b96748SDavid Rhodus 			name += 2;
399a1b96748SDavid Rhodus 		if ((s = strlen(name)) > 2 && strcmp(name + s - 2, ",v") == 0)
400a1b96748SDavid Rhodus 			name[s - 2] = '\0';
401a1b96748SDavid Rhodus 		/* compression suffix */
402a1b96748SDavid Rhodus 		s = strlen(name);
403a1b96748SDavid Rhodus 		if (s > 2 &&
404a1b96748SDavid Rhodus 		    (strcmp(name + s - 2, ".z") == 0 ||
405a1b96748SDavid Rhodus 		     strcmp(name + s - 2, ".Z") == 0))
406a1b96748SDavid Rhodus 			name[s - 2] = '\0';
407a1b96748SDavid Rhodus 		else if (s > 3 &&
408a1b96748SDavid Rhodus 			 strcmp(name + s - 3, ".gz") == 0)
409a1b96748SDavid Rhodus 			name[s - 3] = '\0';
410a1b96748SDavid Rhodus 		else if (s > 4 &&
411a1b96748SDavid Rhodus 			 strcmp(name + s - 4, ".bz2") == 0)
412a1b96748SDavid Rhodus 			name[s - 4] = '\0';
413a1b96748SDavid Rhodus 
414a1b96748SDavid Rhodus 		unusual = 0;
415a1b96748SDavid Rhodus 		bin = man = src = NULL;
416a1b96748SDavid Rhodus 		s = strlen(name);
417a1b96748SDavid Rhodus 
418a1b96748SDavid Rhodus 		if (opt_b) {
419a1b96748SDavid Rhodus 			/*
420a1b96748SDavid Rhodus 			 * Binaries have to match exactly, and must be regular
421a1b96748SDavid Rhodus 			 * executable files.
422a1b96748SDavid Rhodus 			 */
423a1b96748SDavid Rhodus 			unusual = unusual | NO_BIN_FOUND;
424a1b96748SDavid Rhodus 			for (dp = bindirs; *dp != NULL; dp++) {
425a1b96748SDavid Rhodus 				cp = malloc(strlen(*dp) + 1 + s + 1);
426a1b96748SDavid Rhodus 				if (cp == NULL)
427a1b96748SDavid Rhodus 					abort();
428a1b96748SDavid Rhodus 				strcpy(cp, *dp);
429a1b96748SDavid Rhodus 				strcat(cp, "/");
430a1b96748SDavid Rhodus 				strcat(cp, name);
431a1b96748SDavid Rhodus 				if (stat(cp, &sb) == 0 &&
432a1b96748SDavid Rhodus 				    (sb.st_mode & S_IFMT) == S_IFREG &&
433a1b96748SDavid Rhodus 				    (sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
434a1b96748SDavid Rhodus 				    != 0) {
435a1b96748SDavid Rhodus 					unusual = unusual & ~NO_BIN_FOUND;
436a1b96748SDavid Rhodus 					if (bin == NULL) {
437a1b96748SDavid Rhodus 						bin = strdup(cp);
438a1b96748SDavid Rhodus 					} else {
439a1b96748SDavid Rhodus 						olen = strlen(bin);
440a1b96748SDavid Rhodus 						nlen = strlen(cp);
441a1b96748SDavid Rhodus 						bin = realloc(bin,
442a1b96748SDavid Rhodus 							      olen + nlen + 2);
443678e8cc6SSascha Wildner 						if (bin == NULL)
444a1b96748SDavid Rhodus 							abort();
445a1b96748SDavid Rhodus 						strcat(bin, " ");
446a1b96748SDavid Rhodus 						strcat(bin, cp);
447a1b96748SDavid Rhodus 					}
448a1b96748SDavid Rhodus 					if (!opt_a) {
449a1b96748SDavid Rhodus 						free(cp);
450a1b96748SDavid Rhodus 						break;
451a1b96748SDavid Rhodus 					}
452a1b96748SDavid Rhodus 				}
453a1b96748SDavid Rhodus 				free(cp);
454a1b96748SDavid Rhodus 			}
455a1b96748SDavid Rhodus 		}
456a1b96748SDavid Rhodus 
457a1b96748SDavid Rhodus 		if (opt_m) {
458a1b96748SDavid Rhodus 			/*
459a1b96748SDavid Rhodus 			 * Ask the man command to perform the search for us.
460a1b96748SDavid Rhodus 			 */
461a1b96748SDavid Rhodus 			unusual = unusual | NO_MAN_FOUND;
462a1b96748SDavid Rhodus 			if (opt_a)
463a1b96748SDavid Rhodus 				cp = malloc(sizeof MANWHEREISALLCMD - 2 + s);
464a1b96748SDavid Rhodus 			else
465a1b96748SDavid Rhodus 				cp = malloc(sizeof MANWHEREISCMD - 2 + s);
466a1b96748SDavid Rhodus 
467a1b96748SDavid Rhodus 			if (cp == NULL)
468a1b96748SDavid Rhodus 				abort();
469a1b96748SDavid Rhodus 
470a1b96748SDavid Rhodus 			if (opt_a)
471a1b96748SDavid Rhodus 				sprintf(cp, MANWHEREISALLCMD, name);
472a1b96748SDavid Rhodus 			else
473a1b96748SDavid Rhodus 				sprintf(cp, MANWHEREISCMD, name);
474a1b96748SDavid Rhodus 
475a1b96748SDavid Rhodus 			if ((p = popen(cp, "r")) != NULL) {
476a1b96748SDavid Rhodus 
477a1b96748SDavid Rhodus 				while (fgets(buf, BUFSIZ - 1, p) != NULL) {
478a1b96748SDavid Rhodus 					unusual = unusual & ~NO_MAN_FOUND;
479a1b96748SDavid Rhodus 
480a1b96748SDavid Rhodus 					if ((cp2 = strchr(buf, '\n')) != NULL)
481a1b96748SDavid Rhodus 						*cp2 = '\0';
482a1b96748SDavid Rhodus 					if (regexec(&re, buf, 2,
483a1b96748SDavid Rhodus 						    matches, 0) == 0 &&
484a1b96748SDavid Rhodus 					    (rlen = matches[1].rm_eo -
485a1b96748SDavid Rhodus 					     matches[1].rm_so) > 0) {
486a1b96748SDavid Rhodus 						/*
487a1b96748SDavid Rhodus 						 * man -w found formated
488a1b96748SDavid Rhodus 						 * page, need to pick up
489a1b96748SDavid Rhodus 						 * source page name.
490a1b96748SDavid Rhodus 						 */
491a1b96748SDavid Rhodus 						cp2 = malloc(rlen + 1);
492a1b96748SDavid Rhodus 						if (cp2 == NULL)
493a1b96748SDavid Rhodus 							abort();
494a1b96748SDavid Rhodus 						memcpy(cp2,
495a1b96748SDavid Rhodus 						       buf + matches[1].rm_so,
496a1b96748SDavid Rhodus 						       rlen);
497a1b96748SDavid Rhodus 						cp2[rlen] = '\0';
498a1b96748SDavid Rhodus 					} else {
499a1b96748SDavid Rhodus 						/*
500a1b96748SDavid Rhodus 						 * man -w found plain source
501a1b96748SDavid Rhodus 						 * page, use it.
502a1b96748SDavid Rhodus 						 */
503a1b96748SDavid Rhodus 						s = strlen(buf);
504a1b96748SDavid Rhodus 						cp2 = malloc(s + 1);
505a1b96748SDavid Rhodus 						if (cp2 == NULL)
506a1b96748SDavid Rhodus 							abort();
507a1b96748SDavid Rhodus 						strcpy(cp2, buf);
508a1b96748SDavid Rhodus 					}
509a1b96748SDavid Rhodus 
510a1b96748SDavid Rhodus 					if (man == NULL) {
511a1b96748SDavid Rhodus 						man = strdup(cp2);
512a1b96748SDavid Rhodus 					} else {
513a1b96748SDavid Rhodus 						olen = strlen(man);
514a1b96748SDavid Rhodus 						nlen = strlen(cp2);
515a1b96748SDavid Rhodus 						man = realloc(man,
516a1b96748SDavid Rhodus 							      olen + nlen + 2);
517678e8cc6SSascha Wildner 						if (man == NULL)
518a1b96748SDavid Rhodus 							abort();
519a1b96748SDavid Rhodus 						strcat(man, " ");
520a1b96748SDavid Rhodus 						strcat(man, cp2);
521a1b96748SDavid Rhodus 					}
522a1b96748SDavid Rhodus 
523a1b96748SDavid Rhodus 					free(cp2);
524a1b96748SDavid Rhodus 
525a1b96748SDavid Rhodus 					if (!opt_a)
526a1b96748SDavid Rhodus 						break;
527a1b96748SDavid Rhodus 				}
528a1b96748SDavid Rhodus 				pclose(p);
529a1b96748SDavid Rhodus 				free(cp);
530a1b96748SDavid Rhodus 			}
531a1b96748SDavid Rhodus 		}
532a1b96748SDavid Rhodus 
533a1b96748SDavid Rhodus 		if (opt_s) {
534a1b96748SDavid Rhodus 			/*
535a1b96748SDavid Rhodus 			 * Sources match if a subdir with the exact
536a1b96748SDavid Rhodus 			 * name is found.
537a1b96748SDavid Rhodus 			 */
538a1b96748SDavid Rhodus 			unusual = unusual | NO_SRC_FOUND;
539a1b96748SDavid Rhodus 			for (dp = sourcedirs; *dp != NULL; dp++) {
540a1b96748SDavid Rhodus 				cp = malloc(strlen(*dp) + 1 + s + 1);
541a1b96748SDavid Rhodus 				if (cp == NULL)
542a1b96748SDavid Rhodus 					abort();
543a1b96748SDavid Rhodus 				strcpy(cp, *dp);
544a1b96748SDavid Rhodus 				strcat(cp, "/");
545a1b96748SDavid Rhodus 				strcat(cp, name);
546a1b96748SDavid Rhodus 				if (stat(cp, &sb) == 0 &&
547a1b96748SDavid Rhodus 				    (sb.st_mode & S_IFMT) == S_IFDIR) {
548a1b96748SDavid Rhodus 					unusual = unusual & ~NO_SRC_FOUND;
549a1b96748SDavid Rhodus 					if (src == NULL) {
550a1b96748SDavid Rhodus 						src = strdup(cp);
551a1b96748SDavid Rhodus 					} else {
552a1b96748SDavid Rhodus 						olen = strlen(src);
553a1b96748SDavid Rhodus 						nlen = strlen(cp);
554a1b96748SDavid Rhodus 						src = realloc(src,
555a1b96748SDavid Rhodus 							      olen + nlen + 2);
556678e8cc6SSascha Wildner 						if (src == NULL)
557a1b96748SDavid Rhodus 							abort();
558a1b96748SDavid Rhodus 						strcat(src, " ");
559a1b96748SDavid Rhodus 						strcat(src, cp);
560a1b96748SDavid Rhodus 					}
561a1b96748SDavid Rhodus 					if (!opt_a) {
562a1b96748SDavid Rhodus 						free(cp);
563a1b96748SDavid Rhodus 						break;
564a1b96748SDavid Rhodus 					}
565a1b96748SDavid Rhodus 				}
566a1b96748SDavid Rhodus 				free(cp);
567a1b96748SDavid Rhodus 			}
568a1b96748SDavid Rhodus 			/*
569a1b96748SDavid Rhodus 			 * If still not found, ask locate to search it
570a1b96748SDavid Rhodus 			 * for us.  This will find sources for things
571a1b96748SDavid Rhodus 			 * like lpr that are well hidden in the
572a1b96748SDavid Rhodus 			 * /usr/src tree, but takes a lot longer.
573a1b96748SDavid Rhodus 			 * Thus, option -x (`expensive') prevents this
574a1b96748SDavid Rhodus 			 * search.
575a1b96748SDavid Rhodus 			 *
576a1b96748SDavid Rhodus 			 * Do only match locate output that starts
577a1b96748SDavid Rhodus 			 * with one of our source directories, and at
578a1b96748SDavid Rhodus 			 * least one further level of subdirectories.
579a1b96748SDavid Rhodus 			 */
580a1b96748SDavid Rhodus 			if (opt_x || (src && !opt_a))
581a1b96748SDavid Rhodus 				goto done_sources;
582a1b96748SDavid Rhodus 
583a1b96748SDavid Rhodus 			cp = malloc(sizeof LOCATECMD - 2 + s);
584a1b96748SDavid Rhodus 			if (cp == NULL)
585a1b96748SDavid Rhodus 				abort();
586a1b96748SDavid Rhodus 			sprintf(cp, LOCATECMD, name);
587a1b96748SDavid Rhodus 			if ((p = popen(cp, "r")) == NULL)
588a1b96748SDavid Rhodus 				goto done_sources;
589a1b96748SDavid Rhodus 			while ((src == NULL || opt_a) &&
590a1b96748SDavid Rhodus 			       (fgets(buf, BUFSIZ - 1, p)) != NULL) {
591a1b96748SDavid Rhodus 				if ((cp2 = strchr(buf, '\n')) != NULL)
592a1b96748SDavid Rhodus 					*cp2 = '\0';
593a1b96748SDavid Rhodus 				for (dp = sourcedirs;
594a1b96748SDavid Rhodus 				     (src == NULL || opt_a) && *dp != NULL;
595a1b96748SDavid Rhodus 				     dp++) {
596a1b96748SDavid Rhodus 					cp2 = malloc(strlen(*dp) + 9);
597a1b96748SDavid Rhodus 					if (cp2 == NULL)
598a1b96748SDavid Rhodus 						abort();
599a1b96748SDavid Rhodus 					strcpy(cp2, "^");
600a1b96748SDavid Rhodus 					strcat(cp2, *dp);
601a1b96748SDavid Rhodus 					strcat(cp2, "/[^/]+/");
602a1b96748SDavid Rhodus 					if ((i = regcomp(&re2, cp2,
603a1b96748SDavid Rhodus 							 REG_EXTENDED|REG_NOSUB))
604a1b96748SDavid Rhodus 					    != 0) {
605a1b96748SDavid Rhodus 						regerror(i, &re, buf,
606a1b96748SDavid Rhodus 							 BUFSIZ - 1);
607a1b96748SDavid Rhodus 						errx(EX_UNAVAILABLE,
608a1b96748SDavid Rhodus 						     "regcomp(%s) failed: %s",
609a1b96748SDavid Rhodus 						     cp2, buf);
610a1b96748SDavid Rhodus 					}
611a1b96748SDavid Rhodus 					free(cp2);
6122038fb68SSascha Wildner 					if (regexec(&re2, buf, 0, NULL, 0)
613a1b96748SDavid Rhodus 					    == 0) {
614a1b96748SDavid Rhodus 						unusual = unusual &
615a1b96748SDavid Rhodus 						          ~NO_SRC_FOUND;
616a1b96748SDavid Rhodus 						if (src == NULL) {
617a1b96748SDavid Rhodus 							src = strdup(buf);
618a1b96748SDavid Rhodus 						} else {
619a1b96748SDavid Rhodus 							olen = strlen(src);
620a1b96748SDavid Rhodus 							nlen = strlen(buf);
621a1b96748SDavid Rhodus 							src = realloc(src,
622a1b96748SDavid Rhodus 								      olen +
623a1b96748SDavid Rhodus 								      nlen + 2);
624678e8cc6SSascha Wildner 							if (src == NULL)
625a1b96748SDavid Rhodus 								abort();
626a1b96748SDavid Rhodus 							strcat(src, " ");
627a1b96748SDavid Rhodus 							strcat(src, buf);
628a1b96748SDavid Rhodus 						}
629a1b96748SDavid Rhodus 					}
630a1b96748SDavid Rhodus 					regfree(&re2);
631a1b96748SDavid Rhodus 				}
632a1b96748SDavid Rhodus 			}
633a1b96748SDavid Rhodus 			pclose(p);
634a1b96748SDavid Rhodus 			free(cp);
635a1b96748SDavid Rhodus 		}
636a1b96748SDavid Rhodus done_sources:
637a1b96748SDavid Rhodus 
638a1b96748SDavid Rhodus 		if (opt_u && !unusual)
639a1b96748SDavid Rhodus 			continue;
640a1b96748SDavid Rhodus 
641a1b96748SDavid Rhodus 		printed = 0;
642a1b96748SDavid Rhodus 		if (!opt_q) {
643a1b96748SDavid Rhodus 			printf("%s:", name);
644a1b96748SDavid Rhodus 			printed++;
645a1b96748SDavid Rhodus 		}
646a1b96748SDavid Rhodus 		if (bin) {
647a1b96748SDavid Rhodus 			if (printed++)
648a1b96748SDavid Rhodus 				putchar(' ');
649a1b96748SDavid Rhodus 			fputs(bin, stdout);
650a1b96748SDavid Rhodus 		}
651a1b96748SDavid Rhodus 		if (man) {
652a1b96748SDavid Rhodus 			if (printed++)
653a1b96748SDavid Rhodus 				putchar(' ');
654a1b96748SDavid Rhodus 			fputs(man, stdout);
655a1b96748SDavid Rhodus 		}
656a1b96748SDavid Rhodus 		if (src) {
657a1b96748SDavid Rhodus 			if (printed++)
658a1b96748SDavid Rhodus 				putchar(' ');
659a1b96748SDavid Rhodus 			fputs(src, stdout);
660a1b96748SDavid Rhodus 		}
661a1b96748SDavid Rhodus 		if (printed)
662a1b96748SDavid Rhodus 			putchar('\n');
663a1b96748SDavid Rhodus 	}
664a1b96748SDavid Rhodus 
665a1b96748SDavid Rhodus 	if (opt_m)
666a1b96748SDavid Rhodus 		regfree(&re);
667a1b96748SDavid Rhodus 
668a1b96748SDavid Rhodus 	return (0);
669a1b96748SDavid Rhodus }
670