From 1d2b625a3d41d8f930ed8a0085a7ed7857f71501 Mon Sep 17 00:00:00 2001 From: joerg Date: Thu, 11 Jul 2002 21:20:54 +0000 Subject: [PATCH] Complete rewrite, once again. This is basically a ``C compilation'' of the former whereis.pl file, employing the same algorithms, and aiming at being mostly UI-compatible to the old (legally tainted) 4.3BSD whereis(1). In comparision, the 4.4BSD-Lite version is just another variant of which(1) only, where in particular the option to search for source directories is sorely missing. While i was at it, i added two more options which i contemplated doing long since. -x will suppress the run of locate(1) to find sources that could not be found otherwise, potentially saving a lot of time (but obviously, risking to not find some sources that are well hidden in the tree). -q will omit the leading name of the query, so in particular, you can now do something like: cd `whereis -qs ls` I'd explicitly like to thank johan for his review which was quite a bit more than an average review, including sending me a lot of diffs. Reviewed by: johan --- usr.bin/whereis/Makefile | 2 + usr.bin/whereis/pathnames.h | 50 +++ usr.bin/whereis/whereis.1 | 131 +++++++- usr.bin/whereis/whereis.c | 638 +++++++++++++++++++++++++++++++----- usr.bin/whereis/whereis.pl | 242 -------------- 5 files changed, 722 insertions(+), 341 deletions(-) create mode 100644 usr.bin/whereis/pathnames.h delete mode 100644 usr.bin/whereis/whereis.pl diff --git a/usr.bin/whereis/Makefile b/usr.bin/whereis/Makefile index 20dcf0a1fc8b..0d93791a657e 100644 --- a/usr.bin/whereis/Makefile +++ b/usr.bin/whereis/Makefile @@ -2,5 +2,7 @@ PROG= whereis MAN= whereis.1 +WARNS?= 6 +NO_WERROR= true .include diff --git a/usr.bin/whereis/pathnames.h b/usr.bin/whereis/pathnames.h new file mode 100644 index 000000000000..2ffe2f19072d --- /dev/null +++ b/usr.bin/whereis/pathnames.h @@ -0,0 +1,50 @@ +/* + * Copyright © 2002, Jörg Wunsch + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* Where to look for sources. */ +#define PATH_SOURCES \ +"/usr/src/bin:/usr/src/usr.bin:/usr/src/sbin:" \ +"/usr/src/usr.sbin:/usr/src/libexec:" \ +"/usr/src/gnu/bin:/usr/src/gnu/usr.bin:" \ +"/usr/src/gnu/sbin:/usr/src/gnu/usr.sbin:" \ +"/usr/src/gnu/libexec:/usr/src/contrib:" \ +"/usr/src/secure/bin:/usr/src/secure/usr.bin:" \ +"/usr/src/secure/sbin:/usr/src/secure/usr.sbin:" \ +"/usr/src/secure/libexec:/usr/src/crypto" + +/* Each subdirectory of PATH_PORTS will be appended to PATH_SOURCES. */ +#define PATH_PORTS "/usr/ports" + +/* How to query the current manpath. */ +#define MANPATHCMD "manpath -q" + +/* How to obtain the location of manpages, and how to match this result. */ +#define MANWHEREISCMD "man -S1:8 -w %s 2>/dev/null" +#define MANWHEREISMATCH "^.* [(]source: (.*)[)]$" + +/* Command used to locate sources that have not been found yet. */ +#define LOCATECMD "locate '*'/%s 2>/dev/null" diff --git a/usr.bin/whereis/whereis.1 b/usr.bin/whereis/whereis.1 index c9cb55a5f6c9..c286edbf425e 100644 --- a/usr.bin/whereis/whereis.1 +++ b/usr.bin/whereis/whereis.1 @@ -1,9 +1,8 @@ -.\" $NetBSD: whereis.1,v 1.12 2001/12/01 16:43:27 wiz Exp $ -.\" $FreeBSD$ -.\" .\" Copyright (c) 1993 .\" The Regents of the University of California. All rights reserved. .\" +.\" Copyright 2002 Joerg Wunsch +.\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: @@ -32,9 +31,11 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)whereis.1 8.3 (Berkeley) 4/27/95 +.\" @(#)whereis.1 8.2 (Berkeley) 12/30/93 .\" -.Dd April 27, 1995 +.\" $FreeBSD$ +.\" +.Dd July 11, 2002 .Dt WHEREIS 1 .Os .Sh NAME @@ -42,27 +43,133 @@ .Nd locate programs .Sh SYNOPSIS .Nm +.Op Fl bmqsux +.Op Fl BMS Ar dir Ar ... Fl f .Ar program ... .Sh DESCRIPTION The .Nm -utility checks the standard binary directories for the specified programs, -printing out the paths of any it finds. +utility checks the standard binary, manual page, and source +directories for the specified programs, printing out the paths of any +it finds. The supplied program names are first stripped of leading +path name components, any single trailing extension added by +.Xr gzip 1 , +.Xr compress 1 , +or +.Xr bzip2 1 , +and the leading +.Ql s.\& +or trailing +.Ql ,v +from a source code control system. .Pp The default path searched is the string returned by the .Xr sysctl 8 utility for the .Dq user.cs_path -string. +string, with +.Pa /usr/libexec +and the current user's +.Ev $PATH +appended. Manual pages are searched by default along the +.Ev $MANPATH . +Program sources are located in a list of known standard places, +including all the subdirectories of +.Pa /usr/src +and +.Pa /usr/ports . +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl B +Specify directories to search for binaries. Requires the +.Fl f +option. +.It Fl M +Specify directories to search for manual pages. Requires the +.Fl f +option. +.It Fl S +Specify directories to search for program sources. Requires the +.Fl f +option. +.It Fl b +Search for binaries. +.It Fl f +Delimits the list of directories after the +.Fl B , +.Fl M , +or +.Fl S +options, and indicates the beginning of the +.Ar program +list. +.It Fl m +Search for manual pages. +.It Fl q +.Pq Dq quiet . +Suppress the output of the utility name in front of the normal +output line. +This can become handy for use in a backquote substitution of a +shell command line, see +.Sx EXAMPLES . +.It Fl s +Search for source directories. +.It Fl u +Search for +.Dq unusual +entries. A file is said to be unusual if it does not have one entry +of each requested type. +Only the name of the unusual entry is printed. +.It Fl x +Do not use +.Dq expensive +tools when searching for source directories. +Normally, after unsuccessfully searching all the first-level +subdirectories of the source directory list, +.Nm +will ask +.Xr locate 1 +to find the entry on its behalf. +Since this can take much longer, it can be turned off with +.Fl x . +.El +.Sh EXAMPLES +The following finds all utilities under +.Pa /usr/bin +that do not have documentation: +.Pp +.Dl whereis -m -u /usr/bin/* +.Pp +Change to the source code directory of +.Xr ls 1 : +.Pp +.Dl cd `whereis -sq ls` .Sh SEE ALSO +.Xr find 1 , +.Xr locate 1 , +.Xr man 1 , .Xr which 1 , .Xr sysctl 8 -.Sh COMPATIBILITY -The historic flags and arguments for the -.Nm -utility are no longer available in this version. .Sh HISTORY The .Nm command appeared in .Bx 3.0 . +This version re-implements the historical +functionality that was lost in +.Bx 4.4 . +.Sh AUTHORS +This implementation of the +.Nm +command was written by +.An J\(:org Wunsch . +.Sh BUGS +This re-implementation of the +.Nm +utility is not bug-for-bug compatible with historical versions. +It is believed to be compatible with the version that was shipping with +.Fx 2.2 +through +.Fx 4.5 +though. diff --git a/usr.bin/whereis/whereis.c b/usr.bin/whereis/whereis.c index bfbab549908e..84584b2244c5 100644 --- a/usr.bin/whereis/whereis.c +++ b/usr.bin/whereis/whereis.c @@ -1,6 +1,5 @@ -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. +/* + * Copyright © 2002, Jörg Wunsch * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,120 +9,585 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static char copyright[] = -"@(#) Copyright (c) 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ +/* + * 4.3BSD UI-compatible whereis(1) utility. Rewritten from scratch + * since the original 4.3BSD version suffers legal problems that + * prevent it from being redistributed, and since the 4.4BSD version + * was pretty inferior in functionality. + */ -#if 0 -#ifndef lint -static char sccsid[] = "@(#)whereis.c 8.3 (Berkeley) 5/4/95"; -#endif /* not lint */ -__RCSID("$NetBSD: whereis.c,v 1.11 2002/06/11 06:06:21 itojun Exp $"); -#endif +#include -#include __FBSDID("$FreeBSD$"); -#include #include #include +#include #include #include +#include #include #include #include +#include #include -void usage __P((void)); -int main __P((int, char *[])); +#include "pathnames.h" -int -main(argc, argv) - int argc; - char *argv[]; +typedef const char *ccharp; + +int opt_b, opt_m, opt_q, opt_s, opt_u, opt_x; +ccharp *bindirs, *mandirs, *sourcedirs; +char **query; + +const char *sourcepath = PATH_SOURCES; + +char *colonify(ccharp *); +int contains(ccharp *, const char *); +void decolonify(char *, ccharp **, int *); +void defaults(void); +void scanopts(int, char **); +void usage(void); + +/* + * Throughout this program, a number of strings are dynamically + * allocated but never freed. Their memory is written to when + * splitting the strings into string lists which will later be + * processed. Since it's important that those string lists remain + * valid even after the functions allocating the memory returned, + * those functions cannot free them. They could be freed only at end + * of main(), which is pretty pointless anyway. + * + * The overall amount of memory to be allocated for processing the + * strings is not expected to exceed a few kilobytes. For that + * reason, allocation can usually always be assumed to succeed (within + * a virtual memory environment), thus we simply bail out using + * abort(3) in case of an allocation failure. + */ + +void +usage(void) { - struct stat sb; - size_t len; - int ch, sverrno, mib[2]; - char *p, *t, *std, path[MAXPATHLEN]; + errx(EX_USAGE, + "usage: whereis [-bmqsux] [-BMS dir... -f] name ..."); +} + +/* + * Scan options passed to program. + * + * Note that the -B/-M/-S options expect a list of directory + * names that must be terminated with -f. + */ +void +scanopts(int argc, char **argv) +{ + int c, i, opt_f; + ccharp **dirlist; + + opt_f = 0; + while ((c = getopt(argc, argv, "BMSbfmqsux")) != -1) + switch (c) { + case 'B': + dirlist = &bindirs; + goto dolist; + + case 'M': + dirlist = &mandirs; + goto dolist; + + case 'S': + dirlist = &sourcedirs; + dolist: + i = 0; + while (optind < argc && + strcmp(argv[optind], "-f") != 0 && + strcmp(argv[optind], "-B") != 0 && + strcmp(argv[optind], "-M") != 0 && + strcmp(argv[optind], "-S") != 0) { + *dirlist = realloc(*dirlist, + (i + 2) * sizeof(char *)); + if (*dirlist == NULL) + abort(); + (*dirlist)[i] = argv[optind]; + i++; + optind++; + } + (*dirlist)[i] = NULL; + break; + + case 'b': + opt_b = 1; + break; + + case 'f': + goto breakout; + + case 'm': + opt_m = 1; + break; + + case 'q': + opt_q = 1; + break; + + case 's': + opt_s = 1; + break; + + case 'u': + opt_u = 1; + break; + + case 'x': + opt_x = 1; + break; - while ((ch = getopt(argc, argv, "")) != -1) - switch (ch) { - case '?': default: usage(); } - argc -= optind; - argv += optind; - - if (argc == 0) + breakout: + if (optind == argc) usage(); + query = argv + optind; +} - /* Retrieve the standard path. */ - mib[0] = CTL_USER; - mib[1] = USER_CS_PATH; - if (sysctl(mib, 2, NULL, &len, NULL, 0) == -1) - return (-1); - if (len == 0) - err(1, "user_cs_path: sysctl: zero length"); - if ((std = malloc(len)) == NULL) - err(1, NULL); - if (sysctl(mib, 2, std, &len, NULL, 0) == -1) { - sverrno = errno; - free(std); - errno = sverrno; - err(1, "sysctl: user_cs_path"); +/* + * Find out whether string `s' is contained in list `cpp'. + */ +int +contains(ccharp *cpp, const char *s) +{ + ccharp cp; + + if (cpp == NULL) + return (0); + + while ((cp = *cpp) != NULL) { + if (strcmp(cp, s) == 0) + return (1); + cpp++; } - - /* For each path, for each program... */ - for (; *argv; ++argv) - for (p = std;; *p++ = ':') { - t = p; - if ((p = strchr(p, ':')) != NULL) { - *p = '\0'; - if (t == p) - t = "."; - } else - if (strlen(t) == 0) - t = "."; - (void)snprintf(path, sizeof(path), "%s/%s", t, *argv); - if (!stat(path, &sb)) - (void)printf("%s\n", path); - if (p == NULL) - break; - } - return (0); } +/* + * Split string `s' at colons, and pass it to the string list pointed + * to by `cppp' (which has `*ip' elements). Note that the original + * string is modified by replacing the colon with a NUL byte. The + * partial string is only added if it has a length greater than 0, and + * if it's not already contained in the string list. + */ void -usage() +decolonify(char *s, ccharp **cppp, int *ip) { + char *cp; - (void)fprintf(stderr, "usage: whereis program ...\n"); - exit (1); + while ((cp = strchr(s, ':')), *s != '\0') { + if (cp) + *cp = '\0'; + if (strlen(s) && !contains(*cppp, s)) { + *cppp = realloc(*cppp, (*ip + 2) * sizeof(char *)); + if (cppp == NULL) + abort(); + (*cppp)[*ip] = s; + (*cppp)[*ip + 1] = NULL; + (*ip)++; + } + if (cp) + s = cp + 1; + else + break; + } +} + +/* + * Join string list `cpp' into a colon-separated string. + */ +char * +colonify(ccharp *cpp) +{ + size_t s; + char *cp; + int i; + + if (cpp == NULL) + return (0); + + for (s = 0, i = 0; cpp[i] != NULL; i++) + s += strlen(cpp[i]) + 1; + if ((cp = malloc(s + 1)) == NULL) + abort(); + for (i = 0, *cp = '\0'; cpp[i] != NULL; i++) { + strcat(cp, cpp[i]); + strcat(cp, ":"); + } + cp[s - 1] = '\0'; /* eliminate last colon */ + + return (cp); +} + +/* + * Provide defaults for all options and directory lists. + */ +void +defaults(void) +{ + size_t s; + char *b, buf[BUFSIZ], *cp; + int nele; + FILE *p; + DIR *dir; + struct stat sb; + struct dirent *dirp; + + /* default to -bms if none has been specified */ + if (!opt_b && !opt_m && !opt_s) + opt_b = opt_m = opt_s = 1; + + /* -b defaults to default path + /usr/libexec + user's path */ + if (!bindirs) { + if (sysctlbyname("user.cs_path", (void *)NULL, &s, + (void *)NULL, 0) == -1) + err(EX_OSERR, "sysctlbyname(\"user.cs_path\")"); + if ((b = malloc(s + 1)) == NULL) + abort(); + if (sysctlbyname("user.cs_path", b, &s, (void *)NULL, 0) == -1) + err(EX_OSERR, "sysctlbyname(\"user.cs_path\")"); + nele = 0; + decolonify(b, &bindirs, &nele); + bindirs = realloc(bindirs, (nele + 2) * sizeof(char *)); + if (bindirs == NULL) + abort(); + bindirs[nele++] = "/usr/libexec"; + bindirs[nele] = NULL; + if ((cp = getenv("PATH")) != NULL) { + /* don't destroy the original environment... */ + if ((b = malloc(strlen(cp) + 1)) == NULL) + abort(); + strcpy(b, cp); + decolonify(b, &bindirs, &nele); + } + } + + /* -m defaults to $(manpath) */ + if (!mandirs) { + if ((p = popen(MANPATHCMD, "r")) == NULL) + err(EX_OSERR, "cannot execute manpath command"); + if (fgets(buf, BUFSIZ - 1, p) == NULL || + pclose(p)) + err(EX_OSERR, "error processing manpath results"); + if ((b = strchr(buf, '\n')) != NULL) + *b = '\0'; + if ((b = malloc(strlen(buf) + 1)) == NULL) + abort(); + strcpy(b, buf); + nele = 0; + decolonify(b, &mandirs, &nele); + } + + /* -s defaults to precompiled list, plus subdirs of /usr/ports */ + if (!sourcedirs) { + if ((b = malloc(strlen(sourcepath) + 1)) == NULL) + abort(); + strcpy(b, sourcepath); + nele = 0; + decolonify(b, &sourcedirs, &nele); + + if (stat(PATH_PORTS, &sb) == -1) { + if (errno == ENOENT) + /* no /usr/ports, we are done */ + return; + err(EX_OSERR, "stat(" PATH_PORTS ")"); + } + if ((sb.st_mode & S_IFMT) != S_IFDIR) + /* /usr/ports is not a directory, ignore */ + return; + if (access(PATH_PORTS, R_OK | X_OK) != 0) + return; + if ((dir = opendir(PATH_PORTS)) == NULL) + err(EX_OSERR, "opendir" PATH_PORTS ")"); + while ((dirp = readdir(dir)) != NULL) { + if (dirp->d_name[0] == '.' || + strcmp(dirp->d_name, "CVS") == 0) + /* ignore dot entries and CVS subdir */ + continue; + if ((b = malloc(sizeof PATH_PORTS + 1 + dirp->d_namlen)) + == NULL) + abort(); + strcpy(b, PATH_PORTS); + strcat(b, "/"); + strcat(b, dirp->d_name); + if (stat(b, &sb) == -1 || + (sb.st_mode & S_IFMT) != S_IFDIR || + access(b, R_OK | X_OK) != 0) { + free(b); + continue; + } + sourcedirs = realloc(sourcedirs, + (nele + 2) * sizeof(char *)); + if (sourcedirs == NULL) + abort(); + sourcedirs[nele++] = b; + sourcedirs[nele] = NULL; + } + closedir(dir); + } +} + +int +main(int argc, char **argv) +{ + int unusual, i, printed; + char *bin, buf[BUFSIZ], *cp, *cp2, *man, *name, *src; + ccharp *dp; + size_t s; + struct stat sb; + regex_t re, re2; + regmatch_t matches[2]; + regoff_t rlen; + FILE *p; + + scanopts(argc, argv); + defaults(); + + if (mandirs == NULL) + opt_m = 0; + if (bindirs == NULL) + opt_b = 0; + if (sourcedirs == NULL) + opt_s = 0; + if (opt_m + opt_b + opt_s == 0) + errx(EX_DATAERR, "no directories to search"); + + if (opt_m) { + setenv("MANPATH", colonify(mandirs), 1); + if ((i = regcomp(&re, MANWHEREISMATCH, REG_EXTENDED)) != 0) { + regerror(i, &re, buf, BUFSIZ - 1); + errx(EX_UNAVAILABLE, "regcomp(%s) failed: %s", + MANWHEREISMATCH, buf); + } + } + + for (; (name = *query) != NULL; query++) { + /* strip leading path name component */ + if ((cp = strrchr(name, '/')) != NULL) + name = cp + 1; + /* strip SCCS or RCS suffix/prefix */ + if (strlen(name) > 2 && strncmp(name, "s.", 2) == 0) + name += 2; + if ((s = strlen(name)) > 2 && strcmp(name + s - 2, ",v") == 0) + name[s - 2] = '\0'; + /* compression suffix */ + s = strlen(name); + if (s > 2 && + (strcmp(name + s - 2, ".z") == 0 || + strcmp(name + s - 2, ".Z") == 0)) + name[s - 2] = '\0'; + else if (s > 3 && + strcmp(name + s - 3, ".gz") == 0) + name[s - 3] = '\0'; + else if (s > 4 && + strcmp(name + s - 4, ".bz2") == 0) + name[s - 4] = '\0'; + + unusual = 0; + bin = man = src = NULL; + s = strlen(name); + + if (opt_b) { + /* + * Binaries have to match exactly, and must be regular + * executable files. + */ + unusual++; + for (dp = bindirs; *dp != NULL; dp++) { + cp = malloc(strlen(*dp) + 1 + s + 1); + if (cp == NULL) + abort(); + strcpy(cp, *dp); + strcat(cp, "/"); + strcat(cp, name); + if (stat(cp, &sb) == 0 && + (sb.st_mode & S_IFMT) == S_IFREG && + (sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) + != 0) { + unusual--; + bin = cp; + break; + } + free(cp); + } + } + + if (opt_m) { + /* + * Ask the man command to perform the search for us. + */ + unusual++; + cp = malloc(sizeof MANWHEREISCMD - 2 + s); + if (cp == NULL) + abort(); + sprintf(cp, MANWHEREISCMD, name); + if ((p = popen(cp, "r")) != NULL && + fgets(buf, BUFSIZ - 1, p) != NULL && + pclose(p) == 0) { + unusual--; + if ((cp2 = strchr(buf, '\n')) != NULL) + *cp2 = '\0'; + if (regexec(&re, buf, 2, matches, 0) == 0 && + (rlen = matches[1].rm_eo - matches[1].rm_so) + > 0) { + /* + * man -w found compressed + * page, need to pick up + * source page name. + */ + cp2 = malloc(rlen + 1); + if (cp2 == NULL) + abort(); + memcpy(cp2, buf + matches[1].rm_so, + rlen); + cp2[rlen] = '\0'; + man = cp2; + } else { + /* + * man -w found plain source + * page, use it. + */ + s = strlen(buf); + cp2 = malloc(s + 1); + if (cp2 == NULL) + abort(); + strcpy(cp2, buf); + man = cp2; + } + } + free(cp); + } + + if (opt_s) { + /* + * Sources match if a subdir with the exact + * name is found. + */ + unusual++; + for (dp = sourcedirs; *dp != NULL; dp++) { + cp = malloc(strlen(*dp) + 1 + s + 1); + if (cp == NULL) + abort(); + strcpy(cp, *dp); + strcat(cp, "/"); + strcat(cp, name); + if (stat(cp, &sb) == 0 && + (sb.st_mode & S_IFMT) == S_IFDIR) { + unusual--; + src = cp; + break; + } + free(cp); + } + /* + * If still not found, ask locate to search it + * for us. This will find sources for things + * like lpr that are well hidden in the + * /usr/src tree, but takes a lot longer. + * Thus, option -x (`expensive') prevents this + * search. + * + * Do only match locate output that starts + * with one of our source directories, and at + * least one further level of subdirectories. + */ + if (opt_x || src) + goto done_sources; + + cp = malloc(sizeof LOCATECMD - 2 + s); + if (cp == NULL) + abort(); + sprintf(cp, LOCATECMD, name); + if ((p = popen(cp, "r")) == NULL) + goto done_sources; + while (src == NULL && + (fgets(buf, BUFSIZ - 1, p)) != NULL) { + if ((cp2 = strchr(buf, '\n')) != NULL) + *cp2 = '\0'; + for (dp = sourcedirs; + src == NULL && *dp != NULL; + dp++) { + cp2 = malloc(strlen(*dp) + 9); + if (cp2 == NULL) + abort(); + strcpy(cp2, "^"); + strcat(cp2, *dp); + strcat(cp2, "/[^/]+/"); + if ((i = regcomp(&re2, cp2, + REG_EXTENDED|REG_NOSUB)) + != 0) { + regerror(i, &re, buf, + BUFSIZ - 1); + errx(EX_UNAVAILABLE, + "regcomp(%s) failed: %s", + cp2, buf); + } + free(cp2); + if (regexec(&re2, buf, 0, + (regmatch_t *)NULL, 0) + == 0) { + unusual--; + src = buf; + } + regfree(&re2); + } + } + pclose(p); + free(cp); + } + done_sources: + + if (opt_u && !unusual) + continue; + + printed = 0; + if (!opt_q) { + printf("%s:", name); + printed++; + } + if (bin) { + if (printed++) + putchar(' '); + fputs(bin, stdout); + } + if (man) { + if (printed++) + putchar(' '); + fputs(man, stdout); + } + if (src) { + if (printed++) + putchar(' '); + fputs(src, stdout); + } + if (printed) + putchar('\n'); + } + + if (opt_m) + regfree(&re); + + return (0); } diff --git a/usr.bin/whereis/whereis.pl b/usr.bin/whereis/whereis.pl deleted file mode 100644 index 1af1d4769f97..000000000000 --- a/usr.bin/whereis/whereis.pl +++ /dev/null @@ -1,242 +0,0 @@ -#!/usr/bin/perl -# -# Copyright © 1995, 1996 Jörg Wunsch -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR -# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# whereis -- search for binaries, man pages and source directories. -# -# Rewritten from scratch for FreeBSD after the 4.3BSD manual page. -# -# $FreeBSD$ -# - -sub usage -{ - print STDERR "usage: $0 [-bms] [-u] [-BMS dir... -f] name ...\n"; - exit 1; -} - -sub scanopts -{ - local($i, $j); - arg: - while ($ARGV[$i] =~ /^-/) { - opt: - for ($j = 1; $j < length($ARGV[$i]); $j++) { - local($_) = substr($ARGV[$i], $j, 1); - local($what, @list); - $opt_b++, next opt if /b/; - $opt_m++, next opt if /m/; - $opt_s++, next opt if /s/; - $opt_u++, next opt if /u/; - &usage unless /[BMS]/; - - # directory list processing - $what = $_; @list = (); - push(@list, substr($ARGV[$i], $j+1)) if $j+1 < length($ARGV[$i]); - $i++; - while ($i <= $#ARGV && $ARGV[$i] !~ /^-/) { - push(@list, $ARGV[$i++]); - } - if ($what eq "B") {@binaries = @list;} - elsif ($what eq "M") {@manuals = @list;} - elsif ($what eq "S") {@sources = @list;} - - $i++, last arg if $ARGV[$i] =~ /^-f$/; - next arg; - } - $i++; - } - &usage if $i > $#ARGV; - - while ($ARGV[$i]) { - push(@names, $ARGV[$i++]); - } -} - - -sub decolonify -{ - local($list) = @_; - local($_, @rv); - foreach(split(/:/, $list)) { - push(@rv, $_); - } - return @rv; -} - - -&scanopts; - -# default to all if no type requested -if ($opt_b + $opt_m + $opt_s == 0) {$opt_b = $opt_m = $opt_s = 1;} - -if (!defined(@binaries)) { - # - # first, use default path, then append /usr/libexec and the user's path - # - local($cs_path) = `/sbin/sysctl -n user.cs_path`; - local(@list, %path); - - chop($cs_path); - - @list = &decolonify($cs_path); - push(@list, "/usr/libexec"); - push(@list, &decolonify($ENV{'PATH'})); - - # resolve ~, remove duplicates - foreach (@list) { - s/^~/$ENV{'HOME'}/ if /^~/; - push(@binaries, $_) if !$path{$_}; - $path{$_}++; - } -} - -if (!defined(@manuals)) { - # - # first, use default manpath, then append user's $MANPATH - # - local($usermanpath) = $ENV{'MANPATH'}; - delete $ENV{'MANPATH'}; - local($manpath) = `/usr/bin/manpath`; - local(@list, %path, $i); - - chop($manpath); - - @list = &decolonify($manpath); - push(@list, &decolonify($usermanpath)); - - # remove duplicates - foreach (@list) { - push(@manuals, $_) if !$path{$_}; - $path{$_}++; - } -} - -if (!defined(@sources)) { - # - # default command sources - # - local($_); - - @sources = ("/usr/src/bin", "/usr/src/usr.bin", "/usr/src/sbin", - "/usr/src/usr.sbin", "/usr/src/libexec", - "/usr/src/gnu/bin", "/usr/src/gnu/usr.bin", - "/usr/src/gnu/sbin", "/usr/src/gnu/usr.sbin", - "/usr/src/gnu/libexec", "/usr/src/contrib"); - - # - # if /usr/ports exists, look in all its subdirs, too - # - if (-d "/usr/ports" && opendir(PORTS, "/usr/ports")) { - while ($_ = readdir(PORTS)) { - next if /^\.\.?$/; - next if /^distfiles$/; # magic - next if ! -d "/usr/ports/$_"; - push(@sources, "/usr/ports/$_"); - } - closedir(PORTS); - } -} - -if ($opt_m) { - # construct a new MANPATH - foreach (@manuals) { - next if ! -d $_; - if ($manpath) { $manpath .= ":$_"; } - else { $manpath = $_; } - } -} - -# -# main loop -# -foreach $name (@names) { - $name =~ s|^.*/||; # strip leading path name component - $name =~ s/,v$//; $name =~ s/^s\.//; # RCS or SCCS suffix/prefix - $name =~ s/\.(Z|z|gz)$//; # compression suffix - - $line = ""; - $unusual = 0; - - if ($opt_b) { - # - # Binaries have to match exactly, and must be regular executable - # files. - # - $unusual++; - foreach (@binaries) { - $line .= " $_/$name", $unusual--, last if -f "$_/$name" && -x _; - } - } - - if ($opt_m) { - # - # Ask the man command to do the search for us. - # - $unusual++; - chop($result = `man -S 1:8 -M $manpath -w $name 2> /dev/null`); - if ($result ne '') { - $unusual--; - ($cat, $junk, $src) = split(/[() \t\n]+/, $result); - if ($src ne '') { $line .= " $src"; } - else { $line .= " $cat"; } - } - } - - if ($opt_s) { - # - # Sources match if a subdir with the exact name is found. - # - $found = 0; - $unusual++; - foreach (@sources) { - $line .= " $_/$name", $unusual--, $found++ if -d "$_/$name"; - } - # - # If not yet found, ask locate(1) to do the search for us. - # This will find sources for things like lpr, but take longer. - # Do only match locate output that starts with one of our - # source directories, and at least one further level of - # subdirectories. - # - if (!$found && open(LOCATE, "locate */$name 2>/dev/null |")) { - locate_item: - while (chop($loc = )) { - foreach (@sources) { - $line .= " $loc", $unusual--, last locate_item - if $loc =~ m|^$_/[^/]+/|; - } - } - close(LOCATE); - } - } - - if ($opt_u) { - print "$name:\n" if $unusual; - } else { - print "$name:$line\n"; - } -} -