whereis.c revision 100691
199821Sjoerg/* 299821Sjoerg * Copyright � 2002, J�rg Wunsch 399373Sjohan * 499373Sjohan * Redistribution and use in source and binary forms, with or without 599373Sjohan * modification, are permitted provided that the following conditions 699373Sjohan * are met: 799373Sjohan * 1. Redistributions of source code must retain the above copyright 899373Sjohan * notice, this list of conditions and the following disclaimer. 999373Sjohan * 2. Redistributions in binary form must reproduce the above copyright 1099373Sjohan * notice, this list of conditions and the following disclaimer in the 1199373Sjohan * documentation and/or other materials provided with the distribution. 1299373Sjohan * 1399821Sjoerg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 1499821Sjoerg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 1599821Sjoerg * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 1699821Sjoerg * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, 1799821Sjoerg * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 1899821Sjoerg * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 1999821Sjoerg * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2099821Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 2199821Sjoerg * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 2299821Sjoerg * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2399821Sjoerg * POSSIBILITY OF SUCH DAMAGE. 2499373Sjohan */ 2599373Sjohan 2699821Sjoerg/* 2799821Sjoerg * 4.3BSD UI-compatible whereis(1) utility. Rewritten from scratch 2899821Sjoerg * since the original 4.3BSD version suffers legal problems that 2999821Sjoerg * prevent it from being redistributed, and since the 4.4BSD version 3099821Sjoerg * was pretty inferior in functionality. 3199821Sjoerg */ 3299373Sjohan 3399821Sjoerg#include <sys/types.h> 3499373Sjohan 3599406Sjohan__FBSDID("$FreeBSD: head/usr.bin/whereis/whereis.c 100691 2002-07-25 23:04:31Z johan $"); 3699406Sjohan 3799373Sjohan#include <sys/stat.h> 3899373Sjohan#include <sys/sysctl.h> 3999373Sjohan 4099821Sjoerg#include <dirent.h> 4199373Sjohan#include <err.h> 4299373Sjohan#include <errno.h> 4399821Sjoerg#include <regex.h> 4499373Sjohan#include <stdio.h> 4599373Sjohan#include <stdlib.h> 4699373Sjohan#include <string.h> 4799821Sjoerg#include <sysexits.h> 4899373Sjohan#include <unistd.h> 4999373Sjohan 5099821Sjoerg#include "pathnames.h" 5199373Sjohan 5299821Sjoergtypedef const char *ccharp; 5399821Sjoerg 5499821Sjoergint opt_b, opt_m, opt_q, opt_s, opt_u, opt_x; 5599821Sjoergccharp *bindirs, *mandirs, *sourcedirs; 5699821Sjoergchar **query; 5799821Sjoerg 5899821Sjoergconst char *sourcepath = PATH_SOURCES; 5999821Sjoerg 6099821Sjoergchar *colonify(ccharp *); 6199821Sjoergint contains(ccharp *, const char *); 6299821Sjoergvoid decolonify(char *, ccharp **, int *); 6399821Sjoergvoid defaults(void); 6499821Sjoergvoid scanopts(int, char **); 6599821Sjoergvoid usage(void); 6699821Sjoerg 6799821Sjoerg/* 6899821Sjoerg * Throughout this program, a number of strings are dynamically 6999821Sjoerg * allocated but never freed. Their memory is written to when 7099821Sjoerg * splitting the strings into string lists which will later be 7199821Sjoerg * processed. Since it's important that those string lists remain 7299821Sjoerg * valid even after the functions allocating the memory returned, 7399821Sjoerg * those functions cannot free them. They could be freed only at end 7499821Sjoerg * of main(), which is pretty pointless anyway. 7599821Sjoerg * 7699821Sjoerg * The overall amount of memory to be allocated for processing the 7799821Sjoerg * strings is not expected to exceed a few kilobytes. For that 7899821Sjoerg * reason, allocation can usually always be assumed to succeed (within 7999821Sjoerg * a virtual memory environment), thus we simply bail out using 8099821Sjoerg * abort(3) in case of an allocation failure. 8199821Sjoerg */ 8299821Sjoerg 8399821Sjoergvoid 8499821Sjoergusage(void) 8599373Sjohan{ 8699821Sjoerg errx(EX_USAGE, 8799821Sjoerg "usage: whereis [-bmqsux] [-BMS dir... -f] name ..."); 8899821Sjoerg} 8999373Sjohan 9099821Sjoerg/* 9199821Sjoerg * Scan options passed to program. 9299821Sjoerg * 9399821Sjoerg * Note that the -B/-M/-S options expect a list of directory 9499821Sjoerg * names that must be terminated with -f. 9599821Sjoerg */ 9699821Sjoergvoid 9799821Sjoergscanopts(int argc, char **argv) 9899821Sjoerg{ 9999821Sjoerg int c, i, opt_f; 10099821Sjoerg ccharp **dirlist; 10199821Sjoerg 10299821Sjoerg opt_f = 0; 10399821Sjoerg while ((c = getopt(argc, argv, "BMSbfmqsux")) != -1) 10499821Sjoerg switch (c) { 10599821Sjoerg case 'B': 10699821Sjoerg dirlist = &bindirs; 10799821Sjoerg goto dolist; 10899821Sjoerg 10999821Sjoerg case 'M': 11099821Sjoerg dirlist = &mandirs; 11199821Sjoerg goto dolist; 11299821Sjoerg 11399821Sjoerg case 'S': 11499821Sjoerg dirlist = &sourcedirs; 11599821Sjoerg dolist: 11699821Sjoerg i = 0; 11799821Sjoerg while (optind < argc && 11899821Sjoerg strcmp(argv[optind], "-f") != 0 && 11999821Sjoerg strcmp(argv[optind], "-B") != 0 && 12099821Sjoerg strcmp(argv[optind], "-M") != 0 && 12199821Sjoerg strcmp(argv[optind], "-S") != 0) { 12299821Sjoerg *dirlist = realloc(*dirlist, 12399821Sjoerg (i + 2) * sizeof(char *)); 12499821Sjoerg if (*dirlist == NULL) 12599821Sjoerg abort(); 12699821Sjoerg (*dirlist)[i] = argv[optind]; 12799821Sjoerg i++; 12899821Sjoerg optind++; 12999821Sjoerg } 13099821Sjoerg (*dirlist)[i] = NULL; 13199821Sjoerg break; 13299821Sjoerg 13399821Sjoerg case 'b': 13499821Sjoerg opt_b = 1; 13599821Sjoerg break; 13699821Sjoerg 13799821Sjoerg case 'f': 13899821Sjoerg goto breakout; 13999821Sjoerg 14099821Sjoerg case 'm': 14199821Sjoerg opt_m = 1; 14299821Sjoerg break; 14399821Sjoerg 14499821Sjoerg case 'q': 14599821Sjoerg opt_q = 1; 14699821Sjoerg break; 14799821Sjoerg 14899821Sjoerg case 's': 14999821Sjoerg opt_s = 1; 15099821Sjoerg break; 15199821Sjoerg 15299821Sjoerg case 'u': 15399821Sjoerg opt_u = 1; 15499821Sjoerg break; 15599821Sjoerg 15699821Sjoerg case 'x': 15799821Sjoerg opt_x = 1; 15899821Sjoerg break; 15999821Sjoerg 16099373Sjohan default: 16199373Sjohan usage(); 16299373Sjohan } 16399821Sjoerg breakout: 16499821Sjoerg if (optind == argc) 16599373Sjohan usage(); 16699821Sjoerg query = argv + optind; 16799821Sjoerg} 16899373Sjohan 16999821Sjoerg/* 17099821Sjoerg * Find out whether string `s' is contained in list `cpp'. 17199821Sjoerg */ 17299821Sjoergint 17399821Sjoergcontains(ccharp *cpp, const char *s) 17499821Sjoerg{ 17599821Sjoerg ccharp cp; 17699821Sjoerg 17799821Sjoerg if (cpp == NULL) 17899821Sjoerg return (0); 17999821Sjoerg 18099821Sjoerg while ((cp = *cpp) != NULL) { 18199821Sjoerg if (strcmp(cp, s) == 0) 18299821Sjoerg return (1); 18399821Sjoerg cpp++; 18499373Sjohan } 18599821Sjoerg return (0); 18699821Sjoerg} 18799373Sjohan 18899821Sjoerg/* 18999821Sjoerg * Split string `s' at colons, and pass it to the string list pointed 19099821Sjoerg * to by `cppp' (which has `*ip' elements). Note that the original 19199821Sjoerg * string is modified by replacing the colon with a NUL byte. The 19299821Sjoerg * partial string is only added if it has a length greater than 0, and 19399821Sjoerg * if it's not already contained in the string list. 19499821Sjoerg */ 19599821Sjoergvoid 19699821Sjoergdecolonify(char *s, ccharp **cppp, int *ip) 19799821Sjoerg{ 19899821Sjoerg char *cp; 19999821Sjoerg 20099821Sjoerg while ((cp = strchr(s, ':')), *s != '\0') { 20199821Sjoerg if (cp) 20299821Sjoerg *cp = '\0'; 20399821Sjoerg if (strlen(s) && !contains(*cppp, s)) { 20499821Sjoerg *cppp = realloc(*cppp, (*ip + 2) * sizeof(char *)); 20599821Sjoerg if (cppp == NULL) 20699821Sjoerg abort(); 20799821Sjoerg (*cppp)[*ip] = s; 20899821Sjoerg (*cppp)[*ip + 1] = NULL; 20999821Sjoerg (*ip)++; 21099373Sjohan } 21199821Sjoerg if (cp) 21299821Sjoerg s = cp + 1; 21399821Sjoerg else 21499821Sjoerg break; 21599821Sjoerg } 21699821Sjoerg} 21799373Sjohan 21899821Sjoerg/* 21999821Sjoerg * Join string list `cpp' into a colon-separated string. 22099821Sjoerg */ 22199821Sjoergchar * 22299821Sjoergcolonify(ccharp *cpp) 22399821Sjoerg{ 22499821Sjoerg size_t s; 22599821Sjoerg char *cp; 22699821Sjoerg int i; 22799821Sjoerg 22899821Sjoerg if (cpp == NULL) 22999821Sjoerg return (0); 23099821Sjoerg 23199821Sjoerg for (s = 0, i = 0; cpp[i] != NULL; i++) 23299821Sjoerg s += strlen(cpp[i]) + 1; 23399821Sjoerg if ((cp = malloc(s + 1)) == NULL) 23499821Sjoerg abort(); 23599821Sjoerg for (i = 0, *cp = '\0'; cpp[i] != NULL; i++) { 23699821Sjoerg strcat(cp, cpp[i]); 23799821Sjoerg strcat(cp, ":"); 23899821Sjoerg } 23999821Sjoerg cp[s - 1] = '\0'; /* eliminate last colon */ 24099821Sjoerg 24199821Sjoerg return (cp); 24299373Sjohan} 24399373Sjohan 24499821Sjoerg/* 24599821Sjoerg * Provide defaults for all options and directory lists. 24699821Sjoerg */ 24799373Sjohanvoid 24899821Sjoergdefaults(void) 24999373Sjohan{ 25099821Sjoerg size_t s; 25199821Sjoerg char *b, buf[BUFSIZ], *cp; 25299821Sjoerg int nele; 25399821Sjoerg FILE *p; 25499821Sjoerg DIR *dir; 25599821Sjoerg struct stat sb; 25699821Sjoerg struct dirent *dirp; 25799373Sjohan 25899821Sjoerg /* default to -bms if none has been specified */ 25999821Sjoerg if (!opt_b && !opt_m && !opt_s) 26099821Sjoerg opt_b = opt_m = opt_s = 1; 26199821Sjoerg 262100608Sjohan /* -b defaults to default path + /usr/libexec + 263100608Sjohan * /usr/games + user's path */ 26499821Sjoerg if (!bindirs) { 26599821Sjoerg if (sysctlbyname("user.cs_path", (void *)NULL, &s, 26699821Sjoerg (void *)NULL, 0) == -1) 26799821Sjoerg err(EX_OSERR, "sysctlbyname(\"user.cs_path\")"); 26899821Sjoerg if ((b = malloc(s + 1)) == NULL) 26999821Sjoerg abort(); 27099821Sjoerg if (sysctlbyname("user.cs_path", b, &s, (void *)NULL, 0) == -1) 27199821Sjoerg err(EX_OSERR, "sysctlbyname(\"user.cs_path\")"); 27299821Sjoerg nele = 0; 27399821Sjoerg decolonify(b, &bindirs, &nele); 274100608Sjohan bindirs = realloc(bindirs, (nele + 3) * sizeof(char *)); 27599821Sjoerg if (bindirs == NULL) 27699821Sjoerg abort(); 277100691Sjohan bindirs[nele++] = PATH_LIBEXEC; 278100691Sjohan bindirs[nele++] = PATH_GAMES; 27999821Sjoerg bindirs[nele] = NULL; 28099821Sjoerg if ((cp = getenv("PATH")) != NULL) { 28199821Sjoerg /* don't destroy the original environment... */ 28299821Sjoerg if ((b = malloc(strlen(cp) + 1)) == NULL) 28399821Sjoerg abort(); 28499821Sjoerg strcpy(b, cp); 28599821Sjoerg decolonify(b, &bindirs, &nele); 28699821Sjoerg } 28799821Sjoerg } 28899821Sjoerg 28999821Sjoerg /* -m defaults to $(manpath) */ 29099821Sjoerg if (!mandirs) { 29199821Sjoerg if ((p = popen(MANPATHCMD, "r")) == NULL) 29299821Sjoerg err(EX_OSERR, "cannot execute manpath command"); 29399821Sjoerg if (fgets(buf, BUFSIZ - 1, p) == NULL || 29499821Sjoerg pclose(p)) 29599821Sjoerg err(EX_OSERR, "error processing manpath results"); 29699821Sjoerg if ((b = strchr(buf, '\n')) != NULL) 29799821Sjoerg *b = '\0'; 29899821Sjoerg if ((b = malloc(strlen(buf) + 1)) == NULL) 29999821Sjoerg abort(); 30099821Sjoerg strcpy(b, buf); 30199821Sjoerg nele = 0; 30299821Sjoerg decolonify(b, &mandirs, &nele); 30399821Sjoerg } 30499821Sjoerg 30599821Sjoerg /* -s defaults to precompiled list, plus subdirs of /usr/ports */ 30699821Sjoerg if (!sourcedirs) { 30799821Sjoerg if ((b = malloc(strlen(sourcepath) + 1)) == NULL) 30899821Sjoerg abort(); 30999821Sjoerg strcpy(b, sourcepath); 31099821Sjoerg nele = 0; 31199821Sjoerg decolonify(b, &sourcedirs, &nele); 31299821Sjoerg 31399821Sjoerg if (stat(PATH_PORTS, &sb) == -1) { 31499821Sjoerg if (errno == ENOENT) 31599821Sjoerg /* no /usr/ports, we are done */ 31699821Sjoerg return; 31799821Sjoerg err(EX_OSERR, "stat(" PATH_PORTS ")"); 31899821Sjoerg } 31999821Sjoerg if ((sb.st_mode & S_IFMT) != S_IFDIR) 32099821Sjoerg /* /usr/ports is not a directory, ignore */ 32199821Sjoerg return; 32299821Sjoerg if (access(PATH_PORTS, R_OK | X_OK) != 0) 32399821Sjoerg return; 32499821Sjoerg if ((dir = opendir(PATH_PORTS)) == NULL) 32599821Sjoerg err(EX_OSERR, "opendir" PATH_PORTS ")"); 32699821Sjoerg while ((dirp = readdir(dir)) != NULL) { 32799821Sjoerg if (dirp->d_name[0] == '.' || 32899821Sjoerg strcmp(dirp->d_name, "CVS") == 0) 32999821Sjoerg /* ignore dot entries and CVS subdir */ 33099821Sjoerg continue; 33199821Sjoerg if ((b = malloc(sizeof PATH_PORTS + 1 + dirp->d_namlen)) 33299821Sjoerg == NULL) 33399821Sjoerg abort(); 33499821Sjoerg strcpy(b, PATH_PORTS); 33599821Sjoerg strcat(b, "/"); 33699821Sjoerg strcat(b, dirp->d_name); 33799821Sjoerg if (stat(b, &sb) == -1 || 33899821Sjoerg (sb.st_mode & S_IFMT) != S_IFDIR || 33999821Sjoerg access(b, R_OK | X_OK) != 0) { 34099821Sjoerg free(b); 34199821Sjoerg continue; 34299821Sjoerg } 34399821Sjoerg sourcedirs = realloc(sourcedirs, 34499821Sjoerg (nele + 2) * sizeof(char *)); 34599821Sjoerg if (sourcedirs == NULL) 34699821Sjoerg abort(); 34799821Sjoerg sourcedirs[nele++] = b; 34899821Sjoerg sourcedirs[nele] = NULL; 34999821Sjoerg } 35099821Sjoerg closedir(dir); 35199821Sjoerg } 35299373Sjohan} 35399821Sjoerg 35499821Sjoergint 35599821Sjoergmain(int argc, char **argv) 35699821Sjoerg{ 35799821Sjoerg int unusual, i, printed; 35899821Sjoerg char *bin, buf[BUFSIZ], *cp, *cp2, *man, *name, *src; 35999821Sjoerg ccharp *dp; 36099821Sjoerg size_t s; 36199821Sjoerg struct stat sb; 36299821Sjoerg regex_t re, re2; 36399821Sjoerg regmatch_t matches[2]; 36499821Sjoerg regoff_t rlen; 36599821Sjoerg FILE *p; 36699821Sjoerg 36799821Sjoerg scanopts(argc, argv); 36899821Sjoerg defaults(); 36999821Sjoerg 37099821Sjoerg if (mandirs == NULL) 37199821Sjoerg opt_m = 0; 37299821Sjoerg if (bindirs == NULL) 37399821Sjoerg opt_b = 0; 37499821Sjoerg if (sourcedirs == NULL) 37599821Sjoerg opt_s = 0; 37699821Sjoerg if (opt_m + opt_b + opt_s == 0) 37799821Sjoerg errx(EX_DATAERR, "no directories to search"); 37899821Sjoerg 37999821Sjoerg if (opt_m) { 38099821Sjoerg setenv("MANPATH", colonify(mandirs), 1); 38199821Sjoerg if ((i = regcomp(&re, MANWHEREISMATCH, REG_EXTENDED)) != 0) { 38299821Sjoerg regerror(i, &re, buf, BUFSIZ - 1); 38399821Sjoerg errx(EX_UNAVAILABLE, "regcomp(%s) failed: %s", 38499821Sjoerg MANWHEREISMATCH, buf); 38599821Sjoerg } 38699821Sjoerg } 38799821Sjoerg 38899821Sjoerg for (; (name = *query) != NULL; query++) { 38999821Sjoerg /* strip leading path name component */ 39099821Sjoerg if ((cp = strrchr(name, '/')) != NULL) 39199821Sjoerg name = cp + 1; 39299821Sjoerg /* strip SCCS or RCS suffix/prefix */ 39399821Sjoerg if (strlen(name) > 2 && strncmp(name, "s.", 2) == 0) 39499821Sjoerg name += 2; 39599821Sjoerg if ((s = strlen(name)) > 2 && strcmp(name + s - 2, ",v") == 0) 39699821Sjoerg name[s - 2] = '\0'; 39799821Sjoerg /* compression suffix */ 39899821Sjoerg s = strlen(name); 39999821Sjoerg if (s > 2 && 40099821Sjoerg (strcmp(name + s - 2, ".z") == 0 || 40199821Sjoerg strcmp(name + s - 2, ".Z") == 0)) 40299821Sjoerg name[s - 2] = '\0'; 40399821Sjoerg else if (s > 3 && 40499821Sjoerg strcmp(name + s - 3, ".gz") == 0) 40599821Sjoerg name[s - 3] = '\0'; 40699821Sjoerg else if (s > 4 && 40799821Sjoerg strcmp(name + s - 4, ".bz2") == 0) 40899821Sjoerg name[s - 4] = '\0'; 40999821Sjoerg 41099821Sjoerg unusual = 0; 41199821Sjoerg bin = man = src = NULL; 41299821Sjoerg s = strlen(name); 41399821Sjoerg 41499821Sjoerg if (opt_b) { 41599821Sjoerg /* 41699821Sjoerg * Binaries have to match exactly, and must be regular 41799821Sjoerg * executable files. 41899821Sjoerg */ 41999821Sjoerg unusual++; 42099821Sjoerg for (dp = bindirs; *dp != NULL; dp++) { 42199821Sjoerg cp = malloc(strlen(*dp) + 1 + s + 1); 42299821Sjoerg if (cp == NULL) 42399821Sjoerg abort(); 42499821Sjoerg strcpy(cp, *dp); 42599821Sjoerg strcat(cp, "/"); 42699821Sjoerg strcat(cp, name); 42799821Sjoerg if (stat(cp, &sb) == 0 && 42899821Sjoerg (sb.st_mode & S_IFMT) == S_IFREG && 42999821Sjoerg (sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) 43099821Sjoerg != 0) { 43199821Sjoerg unusual--; 43299821Sjoerg bin = cp; 43399821Sjoerg break; 43499821Sjoerg } 43599821Sjoerg free(cp); 43699821Sjoerg } 43799821Sjoerg } 43899821Sjoerg 43999821Sjoerg if (opt_m) { 44099821Sjoerg /* 44199821Sjoerg * Ask the man command to perform the search for us. 44299821Sjoerg */ 44399821Sjoerg unusual++; 44499821Sjoerg cp = malloc(sizeof MANWHEREISCMD - 2 + s); 44599821Sjoerg if (cp == NULL) 44699821Sjoerg abort(); 44799821Sjoerg sprintf(cp, MANWHEREISCMD, name); 44899821Sjoerg if ((p = popen(cp, "r")) != NULL && 44999821Sjoerg fgets(buf, BUFSIZ - 1, p) != NULL && 45099821Sjoerg pclose(p) == 0) { 45199821Sjoerg unusual--; 45299821Sjoerg if ((cp2 = strchr(buf, '\n')) != NULL) 45399821Sjoerg *cp2 = '\0'; 45499821Sjoerg if (regexec(&re, buf, 2, matches, 0) == 0 && 45599821Sjoerg (rlen = matches[1].rm_eo - matches[1].rm_so) 45699821Sjoerg > 0) { 45799821Sjoerg /* 45899821Sjoerg * man -w found compressed 45999821Sjoerg * page, need to pick up 46099821Sjoerg * source page name. 46199821Sjoerg */ 46299821Sjoerg cp2 = malloc(rlen + 1); 46399821Sjoerg if (cp2 == NULL) 46499821Sjoerg abort(); 46599821Sjoerg memcpy(cp2, buf + matches[1].rm_so, 46699821Sjoerg rlen); 46799821Sjoerg cp2[rlen] = '\0'; 46899821Sjoerg man = cp2; 46999821Sjoerg } else { 47099821Sjoerg /* 47199821Sjoerg * man -w found plain source 47299821Sjoerg * page, use it. 47399821Sjoerg */ 47499821Sjoerg s = strlen(buf); 47599821Sjoerg cp2 = malloc(s + 1); 47699821Sjoerg if (cp2 == NULL) 47799821Sjoerg abort(); 47899821Sjoerg strcpy(cp2, buf); 47999821Sjoerg man = cp2; 48099821Sjoerg } 48199821Sjoerg } 48299821Sjoerg free(cp); 48399821Sjoerg } 48499821Sjoerg 48599821Sjoerg if (opt_s) { 48699821Sjoerg /* 48799821Sjoerg * Sources match if a subdir with the exact 48899821Sjoerg * name is found. 48999821Sjoerg */ 49099821Sjoerg unusual++; 49199821Sjoerg for (dp = sourcedirs; *dp != NULL; dp++) { 49299821Sjoerg cp = malloc(strlen(*dp) + 1 + s + 1); 49399821Sjoerg if (cp == NULL) 49499821Sjoerg abort(); 49599821Sjoerg strcpy(cp, *dp); 49699821Sjoerg strcat(cp, "/"); 49799821Sjoerg strcat(cp, name); 49899821Sjoerg if (stat(cp, &sb) == 0 && 49999821Sjoerg (sb.st_mode & S_IFMT) == S_IFDIR) { 50099821Sjoerg unusual--; 50199821Sjoerg src = cp; 50299821Sjoerg break; 50399821Sjoerg } 50499821Sjoerg free(cp); 50599821Sjoerg } 50699821Sjoerg /* 50799821Sjoerg * If still not found, ask locate to search it 50899821Sjoerg * for us. This will find sources for things 50999821Sjoerg * like lpr that are well hidden in the 51099821Sjoerg * /usr/src tree, but takes a lot longer. 51199821Sjoerg * Thus, option -x (`expensive') prevents this 51299821Sjoerg * search. 51399821Sjoerg * 51499821Sjoerg * Do only match locate output that starts 51599821Sjoerg * with one of our source directories, and at 51699821Sjoerg * least one further level of subdirectories. 51799821Sjoerg */ 51899821Sjoerg if (opt_x || src) 51999821Sjoerg goto done_sources; 52099821Sjoerg 52199821Sjoerg cp = malloc(sizeof LOCATECMD - 2 + s); 52299821Sjoerg if (cp == NULL) 52399821Sjoerg abort(); 52499821Sjoerg sprintf(cp, LOCATECMD, name); 52599821Sjoerg if ((p = popen(cp, "r")) == NULL) 52699821Sjoerg goto done_sources; 52799821Sjoerg while (src == NULL && 52899821Sjoerg (fgets(buf, BUFSIZ - 1, p)) != NULL) { 52999821Sjoerg if ((cp2 = strchr(buf, '\n')) != NULL) 53099821Sjoerg *cp2 = '\0'; 53199821Sjoerg for (dp = sourcedirs; 53299821Sjoerg src == NULL && *dp != NULL; 53399821Sjoerg dp++) { 53499821Sjoerg cp2 = malloc(strlen(*dp) + 9); 53599821Sjoerg if (cp2 == NULL) 53699821Sjoerg abort(); 53799821Sjoerg strcpy(cp2, "^"); 53899821Sjoerg strcat(cp2, *dp); 53999821Sjoerg strcat(cp2, "/[^/]+/"); 54099821Sjoerg if ((i = regcomp(&re2, cp2, 54199821Sjoerg REG_EXTENDED|REG_NOSUB)) 54299821Sjoerg != 0) { 54399821Sjoerg regerror(i, &re, buf, 54499821Sjoerg BUFSIZ - 1); 54599821Sjoerg errx(EX_UNAVAILABLE, 54699821Sjoerg "regcomp(%s) failed: %s", 54799821Sjoerg cp2, buf); 54899821Sjoerg } 54999821Sjoerg free(cp2); 55099821Sjoerg if (regexec(&re2, buf, 0, 55199821Sjoerg (regmatch_t *)NULL, 0) 55299821Sjoerg == 0) { 55399821Sjoerg unusual--; 55499821Sjoerg src = buf; 55599821Sjoerg } 55699821Sjoerg regfree(&re2); 55799821Sjoerg } 55899821Sjoerg } 55999821Sjoerg pclose(p); 56099821Sjoerg free(cp); 56199821Sjoerg } 56299821Sjoerg done_sources: 56399821Sjoerg 56499821Sjoerg if (opt_u && !unusual) 56599821Sjoerg continue; 56699821Sjoerg 56799821Sjoerg printed = 0; 56899821Sjoerg if (!opt_q) { 56999821Sjoerg printf("%s:", name); 57099821Sjoerg printed++; 57199821Sjoerg } 57299821Sjoerg if (bin) { 57399821Sjoerg if (printed++) 57499821Sjoerg putchar(' '); 57599821Sjoerg fputs(bin, stdout); 57699821Sjoerg } 57799821Sjoerg if (man) { 57899821Sjoerg if (printed++) 57999821Sjoerg putchar(' '); 58099821Sjoerg fputs(man, stdout); 58199821Sjoerg } 58299821Sjoerg if (src) { 58399821Sjoerg if (printed++) 58499821Sjoerg putchar(' '); 58599821Sjoerg fputs(src, stdout); 58699821Sjoerg } 58799821Sjoerg if (printed) 58899821Sjoerg putchar('\n'); 58999821Sjoerg } 59099821Sjoerg 59199821Sjoerg if (opt_m) 59299821Sjoerg regfree(&re); 59399821Sjoerg 59499821Sjoerg return (0); 59599821Sjoerg} 596