199821Sjoerg/* 2228991Suqs * 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$"); 3699406Sjohan 3799373Sjohan#include <sys/stat.h> 3899373Sjohan#include <sys/sysctl.h> 3999373Sjohan 4099821Sjoerg#include <dirent.h> 4199373Sjohan#include <err.h> 4299373Sjohan#include <errno.h> 43132198Stjr#include <locale.h> 4499821Sjoerg#include <regex.h> 4599373Sjohan#include <stdio.h> 4699373Sjohan#include <stdlib.h> 4799373Sjohan#include <string.h> 4899821Sjoerg#include <sysexits.h> 4999373Sjohan#include <unistd.h> 5099373Sjohan 5199821Sjoerg#include "pathnames.h" 5299373Sjohan 53102246Sjohan#define NO_BIN_FOUND 1 54102246Sjohan#define NO_MAN_FOUND 2 55102246Sjohan#define NO_SRC_FOUND 4 56102246Sjohan 5799821Sjoergtypedef const char *ccharp; 5899821Sjoerg 59227244Sedstatic int opt_a, opt_b, opt_m, opt_q, opt_s, opt_u, opt_x; 60227244Sedstatic ccharp *bindirs, *mandirs, *sourcedirs; 61227244Sedstatic char **query; 6299821Sjoerg 63227244Sedstatic const char *sourcepath = PATH_SOURCES; 6499821Sjoerg 65227244Sedstatic char *colonify(ccharp *); 66227244Sedstatic int contains(ccharp *, const char *); 67227244Sedstatic void decolonify(char *, ccharp **, int *); 68227244Sedstatic void defaults(void); 69227244Sedstatic void scanopts(int, char **); 70227244Sedstatic void usage(void); 7199821Sjoerg 7299821Sjoerg/* 7399821Sjoerg * Throughout this program, a number of strings are dynamically 7499821Sjoerg * allocated but never freed. Their memory is written to when 7599821Sjoerg * splitting the strings into string lists which will later be 7699821Sjoerg * processed. Since it's important that those string lists remain 7799821Sjoerg * valid even after the functions allocating the memory returned, 7899821Sjoerg * those functions cannot free them. They could be freed only at end 7999821Sjoerg * of main(), which is pretty pointless anyway. 8099821Sjoerg * 8199821Sjoerg * The overall amount of memory to be allocated for processing the 8299821Sjoerg * strings is not expected to exceed a few kilobytes. For that 8399821Sjoerg * reason, allocation can usually always be assumed to succeed (within 8499821Sjoerg * a virtual memory environment), thus we simply bail out using 8599821Sjoerg * abort(3) in case of an allocation failure. 8699821Sjoerg */ 8799821Sjoerg 88227244Sedstatic void 8999821Sjoergusage(void) 9099373Sjohan{ 91141657Sru (void)fprintf(stderr, 92141657Sru "usage: whereis [-abmqsux] [-BMS dir ... -f] program ...\n"); 93141657Sru exit(EX_USAGE); 9499821Sjoerg} 9599373Sjohan 9699821Sjoerg/* 9799821Sjoerg * Scan options passed to program. 9899821Sjoerg * 9999821Sjoerg * Note that the -B/-M/-S options expect a list of directory 10099821Sjoerg * names that must be terminated with -f. 10199821Sjoerg */ 102227244Sedstatic void 10399821Sjoergscanopts(int argc, char **argv) 10499821Sjoerg{ 105144840Sstefanf int c, i; 10699821Sjoerg ccharp **dirlist; 10799821Sjoerg 108102246Sjohan while ((c = getopt(argc, argv, "BMSabfmqsux")) != -1) 10999821Sjoerg switch (c) { 11099821Sjoerg case 'B': 11199821Sjoerg dirlist = &bindirs; 11299821Sjoerg goto dolist; 11399821Sjoerg 11499821Sjoerg case 'M': 11599821Sjoerg dirlist = &mandirs; 11699821Sjoerg goto dolist; 11799821Sjoerg 11899821Sjoerg case 'S': 11999821Sjoerg dirlist = &sourcedirs; 12099821Sjoerg dolist: 12199821Sjoerg i = 0; 122102072Sjohan *dirlist = realloc(*dirlist, (i + 1) * sizeof(char *)); 123102072Sjohan (*dirlist)[i] = NULL; 12499821Sjoerg while (optind < argc && 12599821Sjoerg strcmp(argv[optind], "-f") != 0 && 12699821Sjoerg strcmp(argv[optind], "-B") != 0 && 12799821Sjoerg strcmp(argv[optind], "-M") != 0 && 12899821Sjoerg strcmp(argv[optind], "-S") != 0) { 129102072Sjohan decolonify(argv[optind], dirlist, &i); 13099821Sjoerg optind++; 13199821Sjoerg } 13299821Sjoerg break; 13399821Sjoerg 134102246Sjohan case 'a': 135102246Sjohan opt_a = 1; 136102246Sjohan break; 137102246Sjohan 13899821Sjoerg case 'b': 13999821Sjoerg opt_b = 1; 14099821Sjoerg break; 14199821Sjoerg 14299821Sjoerg case 'f': 14399821Sjoerg goto breakout; 14499821Sjoerg 14599821Sjoerg case 'm': 14699821Sjoerg opt_m = 1; 14799821Sjoerg break; 14899821Sjoerg 14999821Sjoerg case 'q': 15099821Sjoerg opt_q = 1; 15199821Sjoerg break; 15299821Sjoerg 15399821Sjoerg case 's': 15499821Sjoerg opt_s = 1; 15599821Sjoerg break; 15699821Sjoerg 15799821Sjoerg case 'u': 15899821Sjoerg opt_u = 1; 15999821Sjoerg break; 16099821Sjoerg 16199821Sjoerg case 'x': 16299821Sjoerg opt_x = 1; 16399821Sjoerg break; 16499821Sjoerg 16599373Sjohan default: 16699373Sjohan usage(); 16799373Sjohan } 16899821Sjoerg breakout: 16999821Sjoerg if (optind == argc) 17099373Sjohan usage(); 17199821Sjoerg query = argv + optind; 17299821Sjoerg} 17399373Sjohan 17499821Sjoerg/* 17599821Sjoerg * Find out whether string `s' is contained in list `cpp'. 17699821Sjoerg */ 177227244Sedstatic int 17899821Sjoergcontains(ccharp *cpp, const char *s) 17999821Sjoerg{ 18099821Sjoerg ccharp cp; 18199821Sjoerg 18299821Sjoerg if (cpp == NULL) 18399821Sjoerg return (0); 18499821Sjoerg 18599821Sjoerg while ((cp = *cpp) != NULL) { 18699821Sjoerg if (strcmp(cp, s) == 0) 18799821Sjoerg return (1); 18899821Sjoerg cpp++; 18999373Sjohan } 19099821Sjoerg return (0); 19199821Sjoerg} 19299373Sjohan 19399821Sjoerg/* 19499821Sjoerg * Split string `s' at colons, and pass it to the string list pointed 19599821Sjoerg * to by `cppp' (which has `*ip' elements). Note that the original 19699821Sjoerg * string is modified by replacing the colon with a NUL byte. The 19799821Sjoerg * partial string is only added if it has a length greater than 0, and 19899821Sjoerg * if it's not already contained in the string list. 19999821Sjoerg */ 200227244Sedstatic void 20199821Sjoergdecolonify(char *s, ccharp **cppp, int *ip) 20299821Sjoerg{ 20399821Sjoerg char *cp; 20499821Sjoerg 20599821Sjoerg while ((cp = strchr(s, ':')), *s != '\0') { 20699821Sjoerg if (cp) 20799821Sjoerg *cp = '\0'; 20899821Sjoerg if (strlen(s) && !contains(*cppp, s)) { 20999821Sjoerg *cppp = realloc(*cppp, (*ip + 2) * sizeof(char *)); 21099821Sjoerg if (cppp == NULL) 21199821Sjoerg abort(); 21299821Sjoerg (*cppp)[*ip] = s; 21399821Sjoerg (*cppp)[*ip + 1] = NULL; 21499821Sjoerg (*ip)++; 21599373Sjohan } 21699821Sjoerg if (cp) 21799821Sjoerg s = cp + 1; 21899821Sjoerg else 21999821Sjoerg break; 22099821Sjoerg } 22199821Sjoerg} 22299373Sjohan 22399821Sjoerg/* 22499821Sjoerg * Join string list `cpp' into a colon-separated string. 22599821Sjoerg */ 226227244Sedstatic char * 22799821Sjoergcolonify(ccharp *cpp) 22899821Sjoerg{ 22999821Sjoerg size_t s; 23099821Sjoerg char *cp; 23199821Sjoerg int i; 23299821Sjoerg 23399821Sjoerg if (cpp == NULL) 23499821Sjoerg return (0); 23599821Sjoerg 23699821Sjoerg for (s = 0, i = 0; cpp[i] != NULL; i++) 23799821Sjoerg s += strlen(cpp[i]) + 1; 23899821Sjoerg if ((cp = malloc(s + 1)) == NULL) 23999821Sjoerg abort(); 24099821Sjoerg for (i = 0, *cp = '\0'; cpp[i] != NULL; i++) { 24199821Sjoerg strcat(cp, cpp[i]); 24299821Sjoerg strcat(cp, ":"); 24399821Sjoerg } 24499821Sjoerg cp[s - 1] = '\0'; /* eliminate last colon */ 24599821Sjoerg 24699821Sjoerg return (cp); 24799373Sjohan} 24899373Sjohan 24999821Sjoerg/* 25099821Sjoerg * Provide defaults for all options and directory lists. 25199821Sjoerg */ 252227244Sedstatic void 25399821Sjoergdefaults(void) 25499373Sjohan{ 25599821Sjoerg size_t s; 25699821Sjoerg char *b, buf[BUFSIZ], *cp; 25799821Sjoerg int nele; 25899821Sjoerg FILE *p; 25999821Sjoerg DIR *dir; 26099821Sjoerg struct stat sb; 26199821Sjoerg struct dirent *dirp; 26299373Sjohan 26399821Sjoerg /* default to -bms if none has been specified */ 26499821Sjoerg if (!opt_b && !opt_m && !opt_s) 26599821Sjoerg opt_b = opt_m = opt_s = 1; 26699821Sjoerg 267100608Sjohan /* -b defaults to default path + /usr/libexec + 268100608Sjohan * /usr/games + user's path */ 26999821Sjoerg if (!bindirs) { 27099821Sjoerg if (sysctlbyname("user.cs_path", (void *)NULL, &s, 27199821Sjoerg (void *)NULL, 0) == -1) 27299821Sjoerg err(EX_OSERR, "sysctlbyname(\"user.cs_path\")"); 27399821Sjoerg if ((b = malloc(s + 1)) == NULL) 27499821Sjoerg abort(); 27599821Sjoerg if (sysctlbyname("user.cs_path", b, &s, (void *)NULL, 0) == -1) 27699821Sjoerg err(EX_OSERR, "sysctlbyname(\"user.cs_path\")"); 27799821Sjoerg nele = 0; 27899821Sjoerg decolonify(b, &bindirs, &nele); 279100608Sjohan bindirs = realloc(bindirs, (nele + 3) * sizeof(char *)); 28099821Sjoerg if (bindirs == NULL) 28199821Sjoerg abort(); 282100691Sjohan bindirs[nele++] = PATH_LIBEXEC; 283100691Sjohan bindirs[nele++] = PATH_GAMES; 28499821Sjoerg bindirs[nele] = NULL; 28599821Sjoerg if ((cp = getenv("PATH")) != NULL) { 28699821Sjoerg /* don't destroy the original environment... */ 28799821Sjoerg if ((b = malloc(strlen(cp) + 1)) == NULL) 28899821Sjoerg abort(); 28999821Sjoerg strcpy(b, cp); 29099821Sjoerg decolonify(b, &bindirs, &nele); 29199821Sjoerg } 29299821Sjoerg } 29399821Sjoerg 29499821Sjoerg /* -m defaults to $(manpath) */ 29599821Sjoerg if (!mandirs) { 29699821Sjoerg if ((p = popen(MANPATHCMD, "r")) == NULL) 29799821Sjoerg err(EX_OSERR, "cannot execute manpath command"); 29899821Sjoerg if (fgets(buf, BUFSIZ - 1, p) == NULL || 29999821Sjoerg pclose(p)) 30099821Sjoerg err(EX_OSERR, "error processing manpath results"); 30199821Sjoerg if ((b = strchr(buf, '\n')) != NULL) 30299821Sjoerg *b = '\0'; 30399821Sjoerg if ((b = malloc(strlen(buf) + 1)) == NULL) 30499821Sjoerg abort(); 30599821Sjoerg strcpy(b, buf); 30699821Sjoerg nele = 0; 30799821Sjoerg decolonify(b, &mandirs, &nele); 30899821Sjoerg } 30999821Sjoerg 31099821Sjoerg /* -s defaults to precompiled list, plus subdirs of /usr/ports */ 31199821Sjoerg if (!sourcedirs) { 31299821Sjoerg if ((b = malloc(strlen(sourcepath) + 1)) == NULL) 31399821Sjoerg abort(); 31499821Sjoerg strcpy(b, sourcepath); 31599821Sjoerg nele = 0; 31699821Sjoerg decolonify(b, &sourcedirs, &nele); 31799821Sjoerg 31899821Sjoerg if (stat(PATH_PORTS, &sb) == -1) { 31999821Sjoerg if (errno == ENOENT) 32099821Sjoerg /* no /usr/ports, we are done */ 32199821Sjoerg return; 32299821Sjoerg err(EX_OSERR, "stat(" PATH_PORTS ")"); 32399821Sjoerg } 32499821Sjoerg if ((sb.st_mode & S_IFMT) != S_IFDIR) 32599821Sjoerg /* /usr/ports is not a directory, ignore */ 32699821Sjoerg return; 32799821Sjoerg if (access(PATH_PORTS, R_OK | X_OK) != 0) 32899821Sjoerg return; 32999821Sjoerg if ((dir = opendir(PATH_PORTS)) == NULL) 33099821Sjoerg err(EX_OSERR, "opendir" PATH_PORTS ")"); 33199821Sjoerg while ((dirp = readdir(dir)) != NULL) { 332179888Sjoerg /* 333179888Sjoerg * Not everything below PATH_PORTS is of 334179888Sjoerg * interest. First, all dot files and 335179888Sjoerg * directories (e. g. .snap) can be ignored. 336179888Sjoerg * Also, all subdirectories starting with a 337179888Sjoerg * capital letter are not going to be 338179888Sjoerg * examined, as they are used for internal 339179888Sjoerg * purposes (Mk, Tools, ...). This also 340179888Sjoerg * matches a possible CVS subdirectory. 341179888Sjoerg * Finally, the distfiles subdirectory is also 342179888Sjoerg * special, and should not be considered to 343179888Sjoerg * avoid false matches. 344179888Sjoerg */ 34599821Sjoerg if (dirp->d_name[0] == '.' || 346179888Sjoerg /* 347179888Sjoerg * isupper() not used on purpose: the 348179888Sjoerg * check is supposed to default to the C 349179888Sjoerg * locale instead of the current user's 350179888Sjoerg * locale. 351179888Sjoerg */ 352179888Sjoerg (dirp->d_name[0] >= 'A' && dirp->d_name[0] <= 'Z') || 353179888Sjoerg strcmp(dirp->d_name, "distfiles") == 0) 35499821Sjoerg continue; 35599821Sjoerg if ((b = malloc(sizeof PATH_PORTS + 1 + dirp->d_namlen)) 35699821Sjoerg == NULL) 35799821Sjoerg abort(); 35899821Sjoerg strcpy(b, PATH_PORTS); 35999821Sjoerg strcat(b, "/"); 36099821Sjoerg strcat(b, dirp->d_name); 36199821Sjoerg if (stat(b, &sb) == -1 || 36299821Sjoerg (sb.st_mode & S_IFMT) != S_IFDIR || 36399821Sjoerg access(b, R_OK | X_OK) != 0) { 36499821Sjoerg free(b); 36599821Sjoerg continue; 36699821Sjoerg } 36799821Sjoerg sourcedirs = realloc(sourcedirs, 36899821Sjoerg (nele + 2) * sizeof(char *)); 36999821Sjoerg if (sourcedirs == NULL) 37099821Sjoerg abort(); 37199821Sjoerg sourcedirs[nele++] = b; 37299821Sjoerg sourcedirs[nele] = NULL; 37399821Sjoerg } 37499821Sjoerg closedir(dir); 37599821Sjoerg } 37699373Sjohan} 37799821Sjoerg 37899821Sjoergint 37999821Sjoergmain(int argc, char **argv) 38099821Sjoerg{ 38199821Sjoerg int unusual, i, printed; 38299821Sjoerg char *bin, buf[BUFSIZ], *cp, *cp2, *man, *name, *src; 38399821Sjoerg ccharp *dp; 384102246Sjohan size_t nlen, olen, s; 38599821Sjoerg struct stat sb; 38699821Sjoerg regex_t re, re2; 38799821Sjoerg regmatch_t matches[2]; 38899821Sjoerg regoff_t rlen; 38999821Sjoerg FILE *p; 39099821Sjoerg 391132198Stjr setlocale(LC_ALL, ""); 392132198Stjr 39399821Sjoerg scanopts(argc, argv); 39499821Sjoerg defaults(); 39599821Sjoerg 39699821Sjoerg if (mandirs == NULL) 39799821Sjoerg opt_m = 0; 39899821Sjoerg if (bindirs == NULL) 39999821Sjoerg opt_b = 0; 40099821Sjoerg if (sourcedirs == NULL) 40199821Sjoerg opt_s = 0; 40299821Sjoerg if (opt_m + opt_b + opt_s == 0) 40399821Sjoerg errx(EX_DATAERR, "no directories to search"); 40499821Sjoerg 40599821Sjoerg if (opt_m) { 40699821Sjoerg setenv("MANPATH", colonify(mandirs), 1); 40799821Sjoerg if ((i = regcomp(&re, MANWHEREISMATCH, REG_EXTENDED)) != 0) { 40899821Sjoerg regerror(i, &re, buf, BUFSIZ - 1); 40999821Sjoerg errx(EX_UNAVAILABLE, "regcomp(%s) failed: %s", 41099821Sjoerg MANWHEREISMATCH, buf); 41199821Sjoerg } 41299821Sjoerg } 41399821Sjoerg 41499821Sjoerg for (; (name = *query) != NULL; query++) { 41599821Sjoerg /* strip leading path name component */ 41699821Sjoerg if ((cp = strrchr(name, '/')) != NULL) 41799821Sjoerg name = cp + 1; 41899821Sjoerg /* strip SCCS or RCS suffix/prefix */ 41999821Sjoerg if (strlen(name) > 2 && strncmp(name, "s.", 2) == 0) 42099821Sjoerg name += 2; 42199821Sjoerg if ((s = strlen(name)) > 2 && strcmp(name + s - 2, ",v") == 0) 42299821Sjoerg name[s - 2] = '\0'; 42399821Sjoerg /* compression suffix */ 42499821Sjoerg s = strlen(name); 42599821Sjoerg if (s > 2 && 42699821Sjoerg (strcmp(name + s - 2, ".z") == 0 || 42799821Sjoerg strcmp(name + s - 2, ".Z") == 0)) 42899821Sjoerg name[s - 2] = '\0'; 42999821Sjoerg else if (s > 3 && 43099821Sjoerg strcmp(name + s - 3, ".gz") == 0) 43199821Sjoerg name[s - 3] = '\0'; 43299821Sjoerg else if (s > 4 && 43399821Sjoerg strcmp(name + s - 4, ".bz2") == 0) 43499821Sjoerg name[s - 4] = '\0'; 43599821Sjoerg 43699821Sjoerg unusual = 0; 43799821Sjoerg bin = man = src = NULL; 43899821Sjoerg s = strlen(name); 43999821Sjoerg 44099821Sjoerg if (opt_b) { 44199821Sjoerg /* 44299821Sjoerg * Binaries have to match exactly, and must be regular 44399821Sjoerg * executable files. 44499821Sjoerg */ 445102246Sjohan unusual = unusual | NO_BIN_FOUND; 44699821Sjoerg for (dp = bindirs; *dp != NULL; dp++) { 44799821Sjoerg cp = malloc(strlen(*dp) + 1 + s + 1); 44899821Sjoerg if (cp == NULL) 44999821Sjoerg abort(); 45099821Sjoerg strcpy(cp, *dp); 45199821Sjoerg strcat(cp, "/"); 45299821Sjoerg strcat(cp, name); 45399821Sjoerg if (stat(cp, &sb) == 0 && 45499821Sjoerg (sb.st_mode & S_IFMT) == S_IFREG && 45599821Sjoerg (sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) 45699821Sjoerg != 0) { 457102246Sjohan unusual = unusual & ~NO_BIN_FOUND; 458102246Sjohan if (bin == NULL) { 459102246Sjohan bin = strdup(cp); 460102246Sjohan } else { 461102246Sjohan olen = strlen(bin); 462102246Sjohan nlen = strlen(cp); 463102246Sjohan bin = realloc(bin, 464102246Sjohan olen + nlen + 2); 465102246Sjohan if (bin == 0) 466102246Sjohan abort(); 467102246Sjohan strcat(bin, " "); 468102246Sjohan strcat(bin, cp); 469102246Sjohan } 470102246Sjohan if (!opt_a) { 471102246Sjohan free(cp); 472102246Sjohan break; 473102246Sjohan } 47499821Sjoerg } 47599821Sjoerg free(cp); 47699821Sjoerg } 47799821Sjoerg } 47899821Sjoerg 47999821Sjoerg if (opt_m) { 48099821Sjoerg /* 48199821Sjoerg * Ask the man command to perform the search for us. 48299821Sjoerg */ 483102246Sjohan unusual = unusual | NO_MAN_FOUND; 484102246Sjohan if (opt_a) 485102246Sjohan cp = malloc(sizeof MANWHEREISALLCMD - 2 + s); 486102246Sjohan else 487102246Sjohan cp = malloc(sizeof MANWHEREISCMD - 2 + s); 488102246Sjohan 48999821Sjoerg if (cp == NULL) 49099821Sjoerg abort(); 491102246Sjohan 492102246Sjohan if (opt_a) 493102246Sjohan sprintf(cp, MANWHEREISALLCMD, name); 494102246Sjohan else 495102246Sjohan sprintf(cp, MANWHEREISCMD, name); 496102246Sjohan 497102246Sjohan if ((p = popen(cp, "r")) != NULL) { 498102246Sjohan 499102246Sjohan while (fgets(buf, BUFSIZ - 1, p) != NULL) { 500102246Sjohan unusual = unusual & ~NO_MAN_FOUND; 501102246Sjohan 502102246Sjohan if ((cp2 = strchr(buf, '\n')) != NULL) 503102246Sjohan *cp2 = '\0'; 504102246Sjohan if (regexec(&re, buf, 2, 505102246Sjohan matches, 0) == 0 && 506102246Sjohan (rlen = matches[1].rm_eo - 507102246Sjohan matches[1].rm_so) > 0) { 508102246Sjohan /* 509102246Sjohan * man -w found formated 510102246Sjohan * page, need to pick up 511102246Sjohan * source page name. 512102246Sjohan */ 513102246Sjohan cp2 = malloc(rlen + 1); 514102246Sjohan if (cp2 == NULL) 515102246Sjohan abort(); 516102246Sjohan memcpy(cp2, 517102246Sjohan buf + matches[1].rm_so, 518102246Sjohan rlen); 519102246Sjohan cp2[rlen] = '\0'; 520102246Sjohan } else { 521102246Sjohan /* 522102246Sjohan * man -w found plain source 523102246Sjohan * page, use it. 524102246Sjohan */ 525102246Sjohan s = strlen(buf); 526102246Sjohan cp2 = malloc(s + 1); 527102246Sjohan if (cp2 == NULL) 528102246Sjohan abort(); 529102246Sjohan strcpy(cp2, buf); 530102246Sjohan } 531102246Sjohan 532102246Sjohan if (man == NULL) { 533102246Sjohan man = strdup(cp2); 534102246Sjohan } else { 535102246Sjohan olen = strlen(man); 536102246Sjohan nlen = strlen(cp2); 537102246Sjohan man = realloc(man, 538102246Sjohan olen + nlen + 2); 539102246Sjohan if (man == 0) 540102246Sjohan abort(); 541102246Sjohan strcat(man, " "); 542102246Sjohan strcat(man, cp2); 543102246Sjohan } 544102246Sjohan 545102246Sjohan free(cp2); 546102246Sjohan 547102246Sjohan if (!opt_a) 548102246Sjohan break; 54999821Sjoerg } 550102246Sjohan pclose(p); 551102246Sjohan free(cp); 55299821Sjoerg } 55399821Sjoerg } 55499821Sjoerg 55599821Sjoerg if (opt_s) { 55699821Sjoerg /* 55799821Sjoerg * Sources match if a subdir with the exact 55899821Sjoerg * name is found. 55999821Sjoerg */ 560102246Sjohan unusual = unusual | NO_SRC_FOUND; 56199821Sjoerg for (dp = sourcedirs; *dp != NULL; dp++) { 56299821Sjoerg cp = malloc(strlen(*dp) + 1 + s + 1); 56399821Sjoerg if (cp == NULL) 56499821Sjoerg abort(); 56599821Sjoerg strcpy(cp, *dp); 56699821Sjoerg strcat(cp, "/"); 56799821Sjoerg strcat(cp, name); 56899821Sjoerg if (stat(cp, &sb) == 0 && 56999821Sjoerg (sb.st_mode & S_IFMT) == S_IFDIR) { 570102246Sjohan unusual = unusual & ~NO_SRC_FOUND; 571102246Sjohan if (src == NULL) { 572102246Sjohan src = strdup(cp); 573102246Sjohan } else { 574102246Sjohan olen = strlen(src); 575102246Sjohan nlen = strlen(cp); 576102246Sjohan src = realloc(src, 577102246Sjohan olen + nlen + 2); 578102246Sjohan if (src == 0) 579102246Sjohan abort(); 580102246Sjohan strcat(src, " "); 581102246Sjohan strcat(src, cp); 582102246Sjohan } 583102246Sjohan if (!opt_a) { 584102246Sjohan free(cp); 585102246Sjohan break; 586102246Sjohan } 58799821Sjoerg } 58899821Sjoerg free(cp); 58999821Sjoerg } 59099821Sjoerg /* 59199821Sjoerg * If still not found, ask locate to search it 59299821Sjoerg * for us. This will find sources for things 59399821Sjoerg * like lpr that are well hidden in the 59499821Sjoerg * /usr/src tree, but takes a lot longer. 59599821Sjoerg * Thus, option -x (`expensive') prevents this 59699821Sjoerg * search. 59799821Sjoerg * 59899821Sjoerg * Do only match locate output that starts 59999821Sjoerg * with one of our source directories, and at 60099821Sjoerg * least one further level of subdirectories. 60199821Sjoerg */ 602102246Sjohan if (opt_x || (src && !opt_a)) 60399821Sjoerg goto done_sources; 60499821Sjoerg 60599821Sjoerg cp = malloc(sizeof LOCATECMD - 2 + s); 60699821Sjoerg if (cp == NULL) 60799821Sjoerg abort(); 60899821Sjoerg sprintf(cp, LOCATECMD, name); 60999821Sjoerg if ((p = popen(cp, "r")) == NULL) 61099821Sjoerg goto done_sources; 611102246Sjohan while ((src == NULL || opt_a) && 61299821Sjoerg (fgets(buf, BUFSIZ - 1, p)) != NULL) { 61399821Sjoerg if ((cp2 = strchr(buf, '\n')) != NULL) 61499821Sjoerg *cp2 = '\0'; 61599821Sjoerg for (dp = sourcedirs; 616102246Sjohan (src == NULL || opt_a) && *dp != NULL; 61799821Sjoerg dp++) { 61899821Sjoerg cp2 = malloc(strlen(*dp) + 9); 61999821Sjoerg if (cp2 == NULL) 62099821Sjoerg abort(); 62199821Sjoerg strcpy(cp2, "^"); 62299821Sjoerg strcat(cp2, *dp); 62399821Sjoerg strcat(cp2, "/[^/]+/"); 62499821Sjoerg if ((i = regcomp(&re2, cp2, 62599821Sjoerg REG_EXTENDED|REG_NOSUB)) 62699821Sjoerg != 0) { 62799821Sjoerg regerror(i, &re, buf, 62899821Sjoerg BUFSIZ - 1); 62999821Sjoerg errx(EX_UNAVAILABLE, 63099821Sjoerg "regcomp(%s) failed: %s", 63199821Sjoerg cp2, buf); 63299821Sjoerg } 63399821Sjoerg free(cp2); 63499821Sjoerg if (regexec(&re2, buf, 0, 63599821Sjoerg (regmatch_t *)NULL, 0) 63699821Sjoerg == 0) { 637102246Sjohan unusual = unusual & 638102246Sjohan ~NO_SRC_FOUND; 639102246Sjohan if (src == NULL) { 640102246Sjohan src = strdup(buf); 641102246Sjohan } else { 642102246Sjohan olen = strlen(src); 643102246Sjohan nlen = strlen(buf); 644102246Sjohan src = realloc(src, 645102246Sjohan olen + 646102246Sjohan nlen + 2); 647102246Sjohan if (src == 0) 648102246Sjohan abort(); 649102246Sjohan strcat(src, " "); 650102246Sjohan strcat(src, buf); 651102246Sjohan } 65299821Sjoerg } 65399821Sjoerg regfree(&re2); 65499821Sjoerg } 65599821Sjoerg } 65699821Sjoerg pclose(p); 65799821Sjoerg free(cp); 65899821Sjoerg } 65999821Sjoerg done_sources: 66099821Sjoerg 66199821Sjoerg if (opt_u && !unusual) 66299821Sjoerg continue; 66399821Sjoerg 66499821Sjoerg printed = 0; 66599821Sjoerg if (!opt_q) { 66699821Sjoerg printf("%s:", name); 66799821Sjoerg printed++; 66899821Sjoerg } 66999821Sjoerg if (bin) { 67099821Sjoerg if (printed++) 67199821Sjoerg putchar(' '); 67299821Sjoerg fputs(bin, stdout); 67399821Sjoerg } 67499821Sjoerg if (man) { 67599821Sjoerg if (printed++) 67699821Sjoerg putchar(' '); 67799821Sjoerg fputs(man, stdout); 67899821Sjoerg } 67999821Sjoerg if (src) { 68099821Sjoerg if (printed++) 68199821Sjoerg putchar(' '); 68299821Sjoerg fputs(src, stdout); 68399821Sjoerg } 68499821Sjoerg if (printed) 68599821Sjoerg putchar('\n'); 68699821Sjoerg } 68799821Sjoerg 68899821Sjoerg if (opt_m) 68999821Sjoerg regfree(&re); 69099821Sjoerg 69199821Sjoerg return (0); 69299821Sjoerg} 693