whereis.c revision 102072
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 102072 2002-08-18 18:21:18Z 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; 117102072Sjohan *dirlist = realloc(*dirlist, (i + 1) * sizeof(char *)); 118102072Sjohan (*dirlist)[i] = NULL; 11999821Sjoerg while (optind < argc && 12099821Sjoerg strcmp(argv[optind], "-f") != 0 && 12199821Sjoerg strcmp(argv[optind], "-B") != 0 && 12299821Sjoerg strcmp(argv[optind], "-M") != 0 && 12399821Sjoerg strcmp(argv[optind], "-S") != 0) { 124102072Sjohan decolonify(argv[optind], dirlist, &i); 12599821Sjoerg optind++; 12699821Sjoerg } 12799821Sjoerg break; 12899821Sjoerg 12999821Sjoerg case 'b': 13099821Sjoerg opt_b = 1; 13199821Sjoerg break; 13299821Sjoerg 13399821Sjoerg case 'f': 13499821Sjoerg goto breakout; 13599821Sjoerg 13699821Sjoerg case 'm': 13799821Sjoerg opt_m = 1; 13899821Sjoerg break; 13999821Sjoerg 14099821Sjoerg case 'q': 14199821Sjoerg opt_q = 1; 14299821Sjoerg break; 14399821Sjoerg 14499821Sjoerg case 's': 14599821Sjoerg opt_s = 1; 14699821Sjoerg break; 14799821Sjoerg 14899821Sjoerg case 'u': 14999821Sjoerg opt_u = 1; 15099821Sjoerg break; 15199821Sjoerg 15299821Sjoerg case 'x': 15399821Sjoerg opt_x = 1; 15499821Sjoerg break; 15599821Sjoerg 15699373Sjohan default: 15799373Sjohan usage(); 15899373Sjohan } 15999821Sjoerg breakout: 16099821Sjoerg if (optind == argc) 16199373Sjohan usage(); 16299821Sjoerg query = argv + optind; 16399821Sjoerg} 16499373Sjohan 16599821Sjoerg/* 16699821Sjoerg * Find out whether string `s' is contained in list `cpp'. 16799821Sjoerg */ 16899821Sjoergint 16999821Sjoergcontains(ccharp *cpp, const char *s) 17099821Sjoerg{ 17199821Sjoerg ccharp cp; 17299821Sjoerg 17399821Sjoerg if (cpp == NULL) 17499821Sjoerg return (0); 17599821Sjoerg 17699821Sjoerg while ((cp = *cpp) != NULL) { 17799821Sjoerg if (strcmp(cp, s) == 0) 17899821Sjoerg return (1); 17999821Sjoerg cpp++; 18099373Sjohan } 18199821Sjoerg return (0); 18299821Sjoerg} 18399373Sjohan 18499821Sjoerg/* 18599821Sjoerg * Split string `s' at colons, and pass it to the string list pointed 18699821Sjoerg * to by `cppp' (which has `*ip' elements). Note that the original 18799821Sjoerg * string is modified by replacing the colon with a NUL byte. The 18899821Sjoerg * partial string is only added if it has a length greater than 0, and 18999821Sjoerg * if it's not already contained in the string list. 19099821Sjoerg */ 19199821Sjoergvoid 19299821Sjoergdecolonify(char *s, ccharp **cppp, int *ip) 19399821Sjoerg{ 19499821Sjoerg char *cp; 19599821Sjoerg 19699821Sjoerg while ((cp = strchr(s, ':')), *s != '\0') { 19799821Sjoerg if (cp) 19899821Sjoerg *cp = '\0'; 19999821Sjoerg if (strlen(s) && !contains(*cppp, s)) { 20099821Sjoerg *cppp = realloc(*cppp, (*ip + 2) * sizeof(char *)); 20199821Sjoerg if (cppp == NULL) 20299821Sjoerg abort(); 20399821Sjoerg (*cppp)[*ip] = s; 20499821Sjoerg (*cppp)[*ip + 1] = NULL; 20599821Sjoerg (*ip)++; 20699373Sjohan } 20799821Sjoerg if (cp) 20899821Sjoerg s = cp + 1; 20999821Sjoerg else 21099821Sjoerg break; 21199821Sjoerg } 21299821Sjoerg} 21399373Sjohan 21499821Sjoerg/* 21599821Sjoerg * Join string list `cpp' into a colon-separated string. 21699821Sjoerg */ 21799821Sjoergchar * 21899821Sjoergcolonify(ccharp *cpp) 21999821Sjoerg{ 22099821Sjoerg size_t s; 22199821Sjoerg char *cp; 22299821Sjoerg int i; 22399821Sjoerg 22499821Sjoerg if (cpp == NULL) 22599821Sjoerg return (0); 22699821Sjoerg 22799821Sjoerg for (s = 0, i = 0; cpp[i] != NULL; i++) 22899821Sjoerg s += strlen(cpp[i]) + 1; 22999821Sjoerg if ((cp = malloc(s + 1)) == NULL) 23099821Sjoerg abort(); 23199821Sjoerg for (i = 0, *cp = '\0'; cpp[i] != NULL; i++) { 23299821Sjoerg strcat(cp, cpp[i]); 23399821Sjoerg strcat(cp, ":"); 23499821Sjoerg } 23599821Sjoerg cp[s - 1] = '\0'; /* eliminate last colon */ 23699821Sjoerg 23799821Sjoerg return (cp); 23899373Sjohan} 23999373Sjohan 24099821Sjoerg/* 24199821Sjoerg * Provide defaults for all options and directory lists. 24299821Sjoerg */ 24399373Sjohanvoid 24499821Sjoergdefaults(void) 24599373Sjohan{ 24699821Sjoerg size_t s; 24799821Sjoerg char *b, buf[BUFSIZ], *cp; 24899821Sjoerg int nele; 24999821Sjoerg FILE *p; 25099821Sjoerg DIR *dir; 25199821Sjoerg struct stat sb; 25299821Sjoerg struct dirent *dirp; 25399373Sjohan 25499821Sjoerg /* default to -bms if none has been specified */ 25599821Sjoerg if (!opt_b && !opt_m && !opt_s) 25699821Sjoerg opt_b = opt_m = opt_s = 1; 25799821Sjoerg 258100608Sjohan /* -b defaults to default path + /usr/libexec + 259100608Sjohan * /usr/games + user's path */ 26099821Sjoerg if (!bindirs) { 26199821Sjoerg if (sysctlbyname("user.cs_path", (void *)NULL, &s, 26299821Sjoerg (void *)NULL, 0) == -1) 26399821Sjoerg err(EX_OSERR, "sysctlbyname(\"user.cs_path\")"); 26499821Sjoerg if ((b = malloc(s + 1)) == NULL) 26599821Sjoerg abort(); 26699821Sjoerg if (sysctlbyname("user.cs_path", b, &s, (void *)NULL, 0) == -1) 26799821Sjoerg err(EX_OSERR, "sysctlbyname(\"user.cs_path\")"); 26899821Sjoerg nele = 0; 26999821Sjoerg decolonify(b, &bindirs, &nele); 270100608Sjohan bindirs = realloc(bindirs, (nele + 3) * sizeof(char *)); 27199821Sjoerg if (bindirs == NULL) 27299821Sjoerg abort(); 273100691Sjohan bindirs[nele++] = PATH_LIBEXEC; 274100691Sjohan bindirs[nele++] = PATH_GAMES; 27599821Sjoerg bindirs[nele] = NULL; 27699821Sjoerg if ((cp = getenv("PATH")) != NULL) { 27799821Sjoerg /* don't destroy the original environment... */ 27899821Sjoerg if ((b = malloc(strlen(cp) + 1)) == NULL) 27999821Sjoerg abort(); 28099821Sjoerg strcpy(b, cp); 28199821Sjoerg decolonify(b, &bindirs, &nele); 28299821Sjoerg } 28399821Sjoerg } 28499821Sjoerg 28599821Sjoerg /* -m defaults to $(manpath) */ 28699821Sjoerg if (!mandirs) { 28799821Sjoerg if ((p = popen(MANPATHCMD, "r")) == NULL) 28899821Sjoerg err(EX_OSERR, "cannot execute manpath command"); 28999821Sjoerg if (fgets(buf, BUFSIZ - 1, p) == NULL || 29099821Sjoerg pclose(p)) 29199821Sjoerg err(EX_OSERR, "error processing manpath results"); 29299821Sjoerg if ((b = strchr(buf, '\n')) != NULL) 29399821Sjoerg *b = '\0'; 29499821Sjoerg if ((b = malloc(strlen(buf) + 1)) == NULL) 29599821Sjoerg abort(); 29699821Sjoerg strcpy(b, buf); 29799821Sjoerg nele = 0; 29899821Sjoerg decolonify(b, &mandirs, &nele); 29999821Sjoerg } 30099821Sjoerg 30199821Sjoerg /* -s defaults to precompiled list, plus subdirs of /usr/ports */ 30299821Sjoerg if (!sourcedirs) { 30399821Sjoerg if ((b = malloc(strlen(sourcepath) + 1)) == NULL) 30499821Sjoerg abort(); 30599821Sjoerg strcpy(b, sourcepath); 30699821Sjoerg nele = 0; 30799821Sjoerg decolonify(b, &sourcedirs, &nele); 30899821Sjoerg 30999821Sjoerg if (stat(PATH_PORTS, &sb) == -1) { 31099821Sjoerg if (errno == ENOENT) 31199821Sjoerg /* no /usr/ports, we are done */ 31299821Sjoerg return; 31399821Sjoerg err(EX_OSERR, "stat(" PATH_PORTS ")"); 31499821Sjoerg } 31599821Sjoerg if ((sb.st_mode & S_IFMT) != S_IFDIR) 31699821Sjoerg /* /usr/ports is not a directory, ignore */ 31799821Sjoerg return; 31899821Sjoerg if (access(PATH_PORTS, R_OK | X_OK) != 0) 31999821Sjoerg return; 32099821Sjoerg if ((dir = opendir(PATH_PORTS)) == NULL) 32199821Sjoerg err(EX_OSERR, "opendir" PATH_PORTS ")"); 32299821Sjoerg while ((dirp = readdir(dir)) != NULL) { 32399821Sjoerg if (dirp->d_name[0] == '.' || 32499821Sjoerg strcmp(dirp->d_name, "CVS") == 0) 32599821Sjoerg /* ignore dot entries and CVS subdir */ 32699821Sjoerg continue; 32799821Sjoerg if ((b = malloc(sizeof PATH_PORTS + 1 + dirp->d_namlen)) 32899821Sjoerg == NULL) 32999821Sjoerg abort(); 33099821Sjoerg strcpy(b, PATH_PORTS); 33199821Sjoerg strcat(b, "/"); 33299821Sjoerg strcat(b, dirp->d_name); 33399821Sjoerg if (stat(b, &sb) == -1 || 33499821Sjoerg (sb.st_mode & S_IFMT) != S_IFDIR || 33599821Sjoerg access(b, R_OK | X_OK) != 0) { 33699821Sjoerg free(b); 33799821Sjoerg continue; 33899821Sjoerg } 33999821Sjoerg sourcedirs = realloc(sourcedirs, 34099821Sjoerg (nele + 2) * sizeof(char *)); 34199821Sjoerg if (sourcedirs == NULL) 34299821Sjoerg abort(); 34399821Sjoerg sourcedirs[nele++] = b; 34499821Sjoerg sourcedirs[nele] = NULL; 34599821Sjoerg } 34699821Sjoerg closedir(dir); 34799821Sjoerg } 34899373Sjohan} 34999821Sjoerg 35099821Sjoergint 35199821Sjoergmain(int argc, char **argv) 35299821Sjoerg{ 35399821Sjoerg int unusual, i, printed; 35499821Sjoerg char *bin, buf[BUFSIZ], *cp, *cp2, *man, *name, *src; 35599821Sjoerg ccharp *dp; 35699821Sjoerg size_t s; 35799821Sjoerg struct stat sb; 35899821Sjoerg regex_t re, re2; 35999821Sjoerg regmatch_t matches[2]; 36099821Sjoerg regoff_t rlen; 36199821Sjoerg FILE *p; 36299821Sjoerg 36399821Sjoerg scanopts(argc, argv); 36499821Sjoerg defaults(); 36599821Sjoerg 36699821Sjoerg if (mandirs == NULL) 36799821Sjoerg opt_m = 0; 36899821Sjoerg if (bindirs == NULL) 36999821Sjoerg opt_b = 0; 37099821Sjoerg if (sourcedirs == NULL) 37199821Sjoerg opt_s = 0; 37299821Sjoerg if (opt_m + opt_b + opt_s == 0) 37399821Sjoerg errx(EX_DATAERR, "no directories to search"); 37499821Sjoerg 37599821Sjoerg if (opt_m) { 37699821Sjoerg setenv("MANPATH", colonify(mandirs), 1); 37799821Sjoerg if ((i = regcomp(&re, MANWHEREISMATCH, REG_EXTENDED)) != 0) { 37899821Sjoerg regerror(i, &re, buf, BUFSIZ - 1); 37999821Sjoerg errx(EX_UNAVAILABLE, "regcomp(%s) failed: %s", 38099821Sjoerg MANWHEREISMATCH, buf); 38199821Sjoerg } 38299821Sjoerg } 38399821Sjoerg 38499821Sjoerg for (; (name = *query) != NULL; query++) { 38599821Sjoerg /* strip leading path name component */ 38699821Sjoerg if ((cp = strrchr(name, '/')) != NULL) 38799821Sjoerg name = cp + 1; 38899821Sjoerg /* strip SCCS or RCS suffix/prefix */ 38999821Sjoerg if (strlen(name) > 2 && strncmp(name, "s.", 2) == 0) 39099821Sjoerg name += 2; 39199821Sjoerg if ((s = strlen(name)) > 2 && strcmp(name + s - 2, ",v") == 0) 39299821Sjoerg name[s - 2] = '\0'; 39399821Sjoerg /* compression suffix */ 39499821Sjoerg s = strlen(name); 39599821Sjoerg if (s > 2 && 39699821Sjoerg (strcmp(name + s - 2, ".z") == 0 || 39799821Sjoerg strcmp(name + s - 2, ".Z") == 0)) 39899821Sjoerg name[s - 2] = '\0'; 39999821Sjoerg else if (s > 3 && 40099821Sjoerg strcmp(name + s - 3, ".gz") == 0) 40199821Sjoerg name[s - 3] = '\0'; 40299821Sjoerg else if (s > 4 && 40399821Sjoerg strcmp(name + s - 4, ".bz2") == 0) 40499821Sjoerg name[s - 4] = '\0'; 40599821Sjoerg 40699821Sjoerg unusual = 0; 40799821Sjoerg bin = man = src = NULL; 40899821Sjoerg s = strlen(name); 40999821Sjoerg 41099821Sjoerg if (opt_b) { 41199821Sjoerg /* 41299821Sjoerg * Binaries have to match exactly, and must be regular 41399821Sjoerg * executable files. 41499821Sjoerg */ 41599821Sjoerg unusual++; 41699821Sjoerg for (dp = bindirs; *dp != NULL; dp++) { 41799821Sjoerg cp = malloc(strlen(*dp) + 1 + s + 1); 41899821Sjoerg if (cp == NULL) 41999821Sjoerg abort(); 42099821Sjoerg strcpy(cp, *dp); 42199821Sjoerg strcat(cp, "/"); 42299821Sjoerg strcat(cp, name); 42399821Sjoerg if (stat(cp, &sb) == 0 && 42499821Sjoerg (sb.st_mode & S_IFMT) == S_IFREG && 42599821Sjoerg (sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) 42699821Sjoerg != 0) { 42799821Sjoerg unusual--; 42899821Sjoerg bin = cp; 42999821Sjoerg break; 43099821Sjoerg } 43199821Sjoerg free(cp); 43299821Sjoerg } 43399821Sjoerg } 43499821Sjoerg 43599821Sjoerg if (opt_m) { 43699821Sjoerg /* 43799821Sjoerg * Ask the man command to perform the search for us. 43899821Sjoerg */ 43999821Sjoerg unusual++; 44099821Sjoerg cp = malloc(sizeof MANWHEREISCMD - 2 + s); 44199821Sjoerg if (cp == NULL) 44299821Sjoerg abort(); 44399821Sjoerg sprintf(cp, MANWHEREISCMD, name); 44499821Sjoerg if ((p = popen(cp, "r")) != NULL && 44599821Sjoerg fgets(buf, BUFSIZ - 1, p) != NULL && 44699821Sjoerg pclose(p) == 0) { 44799821Sjoerg unusual--; 44899821Sjoerg if ((cp2 = strchr(buf, '\n')) != NULL) 44999821Sjoerg *cp2 = '\0'; 45099821Sjoerg if (regexec(&re, buf, 2, matches, 0) == 0 && 45199821Sjoerg (rlen = matches[1].rm_eo - matches[1].rm_so) 45299821Sjoerg > 0) { 45399821Sjoerg /* 45499821Sjoerg * man -w found compressed 45599821Sjoerg * page, need to pick up 45699821Sjoerg * source page name. 45799821Sjoerg */ 45899821Sjoerg cp2 = malloc(rlen + 1); 45999821Sjoerg if (cp2 == NULL) 46099821Sjoerg abort(); 46199821Sjoerg memcpy(cp2, buf + matches[1].rm_so, 46299821Sjoerg rlen); 46399821Sjoerg cp2[rlen] = '\0'; 46499821Sjoerg man = cp2; 46599821Sjoerg } else { 46699821Sjoerg /* 46799821Sjoerg * man -w found plain source 46899821Sjoerg * page, use it. 46999821Sjoerg */ 47099821Sjoerg s = strlen(buf); 47199821Sjoerg cp2 = malloc(s + 1); 47299821Sjoerg if (cp2 == NULL) 47399821Sjoerg abort(); 47499821Sjoerg strcpy(cp2, buf); 47599821Sjoerg man = cp2; 47699821Sjoerg } 47799821Sjoerg } 47899821Sjoerg free(cp); 47999821Sjoerg } 48099821Sjoerg 48199821Sjoerg if (opt_s) { 48299821Sjoerg /* 48399821Sjoerg * Sources match if a subdir with the exact 48499821Sjoerg * name is found. 48599821Sjoerg */ 48699821Sjoerg unusual++; 48799821Sjoerg for (dp = sourcedirs; *dp != NULL; dp++) { 48899821Sjoerg cp = malloc(strlen(*dp) + 1 + s + 1); 48999821Sjoerg if (cp == NULL) 49099821Sjoerg abort(); 49199821Sjoerg strcpy(cp, *dp); 49299821Sjoerg strcat(cp, "/"); 49399821Sjoerg strcat(cp, name); 49499821Sjoerg if (stat(cp, &sb) == 0 && 49599821Sjoerg (sb.st_mode & S_IFMT) == S_IFDIR) { 49699821Sjoerg unusual--; 49799821Sjoerg src = cp; 49899821Sjoerg break; 49999821Sjoerg } 50099821Sjoerg free(cp); 50199821Sjoerg } 50299821Sjoerg /* 50399821Sjoerg * If still not found, ask locate to search it 50499821Sjoerg * for us. This will find sources for things 50599821Sjoerg * like lpr that are well hidden in the 50699821Sjoerg * /usr/src tree, but takes a lot longer. 50799821Sjoerg * Thus, option -x (`expensive') prevents this 50899821Sjoerg * search. 50999821Sjoerg * 51099821Sjoerg * Do only match locate output that starts 51199821Sjoerg * with one of our source directories, and at 51299821Sjoerg * least one further level of subdirectories. 51399821Sjoerg */ 51499821Sjoerg if (opt_x || src) 51599821Sjoerg goto done_sources; 51699821Sjoerg 51799821Sjoerg cp = malloc(sizeof LOCATECMD - 2 + s); 51899821Sjoerg if (cp == NULL) 51999821Sjoerg abort(); 52099821Sjoerg sprintf(cp, LOCATECMD, name); 52199821Sjoerg if ((p = popen(cp, "r")) == NULL) 52299821Sjoerg goto done_sources; 52399821Sjoerg while (src == NULL && 52499821Sjoerg (fgets(buf, BUFSIZ - 1, p)) != NULL) { 52599821Sjoerg if ((cp2 = strchr(buf, '\n')) != NULL) 52699821Sjoerg *cp2 = '\0'; 52799821Sjoerg for (dp = sourcedirs; 52899821Sjoerg src == NULL && *dp != NULL; 52999821Sjoerg dp++) { 53099821Sjoerg cp2 = malloc(strlen(*dp) + 9); 53199821Sjoerg if (cp2 == NULL) 53299821Sjoerg abort(); 53399821Sjoerg strcpy(cp2, "^"); 53499821Sjoerg strcat(cp2, *dp); 53599821Sjoerg strcat(cp2, "/[^/]+/"); 53699821Sjoerg if ((i = regcomp(&re2, cp2, 53799821Sjoerg REG_EXTENDED|REG_NOSUB)) 53899821Sjoerg != 0) { 53999821Sjoerg regerror(i, &re, buf, 54099821Sjoerg BUFSIZ - 1); 54199821Sjoerg errx(EX_UNAVAILABLE, 54299821Sjoerg "regcomp(%s) failed: %s", 54399821Sjoerg cp2, buf); 54499821Sjoerg } 54599821Sjoerg free(cp2); 54699821Sjoerg if (regexec(&re2, buf, 0, 54799821Sjoerg (regmatch_t *)NULL, 0) 54899821Sjoerg == 0) { 54999821Sjoerg unusual--; 55099821Sjoerg src = buf; 55199821Sjoerg } 55299821Sjoerg regfree(&re2); 55399821Sjoerg } 55499821Sjoerg } 55599821Sjoerg pclose(p); 55699821Sjoerg free(cp); 55799821Sjoerg } 55899821Sjoerg done_sources: 55999821Sjoerg 56099821Sjoerg if (opt_u && !unusual) 56199821Sjoerg continue; 56299821Sjoerg 56399821Sjoerg printed = 0; 56499821Sjoerg if (!opt_q) { 56599821Sjoerg printf("%s:", name); 56699821Sjoerg printed++; 56799821Sjoerg } 56899821Sjoerg if (bin) { 56999821Sjoerg if (printed++) 57099821Sjoerg putchar(' '); 57199821Sjoerg fputs(bin, stdout); 57299821Sjoerg } 57399821Sjoerg if (man) { 57499821Sjoerg if (printed++) 57599821Sjoerg putchar(' '); 57699821Sjoerg fputs(man, stdout); 57799821Sjoerg } 57899821Sjoerg if (src) { 57999821Sjoerg if (printed++) 58099821Sjoerg putchar(' '); 58199821Sjoerg fputs(src, stdout); 58299821Sjoerg } 58399821Sjoerg if (printed) 58499821Sjoerg putchar('\n'); 58599821Sjoerg } 58699821Sjoerg 58799821Sjoerg if (opt_m) 58899821Sjoerg regfree(&re); 58999821Sjoerg 59099821Sjoerg return (0); 59199821Sjoerg} 592