summaryrefslogtreecommitdiffstats
path: root/misc-utils/whereis.c
diff options
context:
space:
mode:
authorSami Kerola2013-03-13 23:27:12 +0100
committerKarel Zak2013-03-19 15:44:45 +0100
commit9db54a7e08fbfbe96b211cd684dd6be457b4621a (patch)
tree54d66b341e8f79b07fd335b7c2bfc655d1a0bec1 /misc-utils/whereis.c
parentdocs: add TB to list of supported suffixes (diff)
downloadkernel-qcow2-util-linux-9db54a7e08fbfbe96b211cd684dd6be457b4621a.tar.gz
kernel-qcow2-util-linux-9db54a7e08fbfbe96b211cd684dd6be457b4621a.tar.xz
kernel-qcow2-util-linux-9db54a7e08fbfbe96b211cd684dd6be457b4621a.zip
whereis: rewrite most of the command
The earlier code gave little or no change to fix bugs and improve the command. This rewrite attempts to make further patching easier. Signed-off-by: Sami Kerola <kerolasa@iki.fi> Signed-off-by: Karel Zak <kzak@redhat.com> Co-Author: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'misc-utils/whereis.c')
-rw-r--r--misc-utils/whereis.c573
1 files changed, 310 insertions, 263 deletions
diff --git a/misc-utils/whereis.c b/misc-utils/whereis.c
index 4194ad5dc..97ec45cb2 100644
--- a/misc-utils/whereis.c
+++ b/misc-utils/whereis.c
@@ -29,16 +29,14 @@
* 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.
- */
-
-/* *:aeb */
-
-/* 1999-02-22 Arkadiusz Miśkiewicz <misiek@pld.ORG.PL>
+ *
+ * 1999-02-22 Arkadiusz Miśkiewicz <misiek@pld.ORG.PL>
* - added Native Language Support
- */
-
-/* 2011-08-12 Davidlohr Bueso <dave@gnu.org>
+ * 2011-08-12 Davidlohr Bueso <dave@gnu.org>
* - added $PATH lookup
+ *
+ * Copyright (C) 2013 Karel Zak <kzak@redhat.com>
+ * 2013 Sami Kerola <kerolasa@iki.fi>
*/
#include <sys/param.h>
@@ -49,13 +47,37 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
+#include <assert.h>
#include "xalloc.h"
#include "nls.h"
#include "c.h"
#include "closestream.h"
-static char *bindirs[] = {
+/*#define DEBUG*/
+
+static char uflag = 0;
+
+/* supported types */
+enum {
+ BIN_DIR = (1 << 1),
+ MAN_DIR = (1 << 2),
+ SRC_DIR = (1 << 3),
+
+ ALL_DIRS = BIN_DIR | MAN_DIR | SRC_DIR
+};
+
+/* directories */
+struct wh_dirlist {
+ int type;
+ dev_t st_dev;
+ ino_t st_ino;
+ char *path;
+
+ struct wh_dirlist *next;
+};
+
+static const char *bindirs[] = {
"/bin",
"/usr/bin",
"/sbin",
@@ -106,35 +128,40 @@ static char *bindirs[] = {
"/usr/share",
"/opt/*/bin",
-
- 0
+ NULL
};
-static char *mandirs[] = {
+static const char *mandirs[] = {
"/usr/man/*",
"/usr/share/man/*",
"/usr/X386/man/*",
"/usr/X11/man/*",
"/usr/TeX/man/*",
"/usr/interviews/man/mann",
- 0
+ NULL
};
-static char *srcdirs[] = {
+static const char *srcdirs[] = {
"/usr/src/*",
"/usr/src/lib/libc/*",
"/usr/src/lib/libc/net/*",
"/usr/src/ucb/pascal",
"/usr/src/ucb/pascal/utilities",
"/usr/src/undoc",
- 0
+ NULL
};
-static char sflag = 1, bflag = 1, mflag = 1, uflag;
-static char **Sflag, **Bflag, **Mflag, **pathdir, **pathdir_p;
-static int Scnt, Bcnt, Mcnt, count, print;
-
-static void __attribute__ ((__noreturn__)) usage(FILE * out)
+#ifdef DEBUG
+# define DBG(_x) do { \
+ printf("DEBUG: "); \
+ _x; \
+ fputc('\n', stdout); \
+ } while (0)
+#else
+# define DBG(_x)
+#endif
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
{
fputs(_("\nUsage:\n"), out);
fprintf(out,
@@ -156,313 +183,338 @@ static void __attribute__ ((__noreturn__)) usage(FILE * out)
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
-static int
-itsit(char *cp, char *dp)
+static void dirlist_add_dir(struct wh_dirlist **ls0, int type, const char *dir)
{
- int i = strlen(dp);
+ struct stat st;
+ struct wh_dirlist *prev = NULL, *ls = *ls0;
- if (dp[0] == 's' && dp[1] == '.' && itsit(cp, dp + 2))
- return 1;
- if (!strcmp(dp + i - 2, ".Z"))
- i -= 2;
- else if (!strcmp(dp + i - 3, ".gz"))
- i -= 3;
- else if (!strcmp(dp + i - 4, ".bz2"))
- i -= 4;
- while (*cp && *dp && *cp == *dp)
- cp++, dp++, i--;
- if (*cp == 0 && *dp == 0)
- return 1;
- while (isdigit(*dp))
- dp++;
- if (*cp == 0 && *dp++ == '.') {
- --i;
- while (i > 0 && *dp)
- if (--i, *dp++ == '.')
- return (*dp++ == 'C' && *dp++ == 0);
- return 1;
- }
- return 0;
-}
+ DBG(printf("add dir: '%s'", dir));
-static void
-findin(char *dir, char *cp)
-{
- DIR *dirp;
- struct dirent *dp;
- char *d, *dd;
- size_t l;
- char dirbuf[1024];
- struct stat statbuf;
-
- dd = strchr(dir, '*');
- if (!dd) {
- dirp = opendir(dir);
- if (dirp == NULL)
+ if (access(dir, R_OK) != 0)
+ return;
+ if (stat(dir, &st) != 0 || !S_ISDIR(st.st_mode))
+ return;
+ while (ls) {
+ if (ls->st_ino == st.st_ino &&
+ ls->st_dev == st.st_dev &&
+ ls->type == type) {
+ DBG(printf(" already in the list, ignore"));
return;
- while ((dp = readdir(dirp)) != NULL) {
- if (itsit(cp, dp->d_name)) {
- count++;
- if (print)
- printf(" %s/%s", dir, dp->d_name);
- }
}
- closedir(dirp);
- return;
+ prev = ls;
+ ls = ls->next;
}
- l = strlen(dir);
- if (l < sizeof(dirbuf)) {
- /* refuse excessively long names */
- strcpy(dirbuf, dir);
- d = strchr(dirbuf, '*');
- if (d)
- *d = 0;
- dirp = opendir(dirbuf);
- if (dirp == NULL)
- return;
- while ((dp = readdir(dirp)) != NULL) {
- if (!strcmp(dp->d_name, ".") ||
- !strcmp(dp->d_name, ".."))
- continue;
- if (strlen(dp->d_name) + l > sizeof(dirbuf))
- continue;
- sprintf(d, "%s", dp->d_name);
- if (stat(dirbuf, &statbuf))
- continue;
- if (!S_ISDIR(statbuf.st_mode))
- continue;
- strcat(d, dd + 1);
- findin(dirbuf, cp);
- }
- closedir(dirp);
+ DBG(printf(" adding new directory"));
+
+ ls = xcalloc(1, sizeof(*ls));
+ ls->st_ino = st.st_ino;
+ ls->st_dev = st.st_dev;
+ ls->type = type;
+ ls->path = xstrdup(dir);
+
+ if (!*ls0)
+ *ls0 = ls; /* first in the list */
+ else {
+ assert(prev);
+ prev->next = ls; /* add to the end of the list */
}
return;
-
}
-static int inpath(const char *str)
+/* special case for '*' in the paths */
+static void dirlist_add_subdir(struct wh_dirlist **ls, int type, const char *dir)
{
- size_t i;
+ char buf[PATH_MAX], *d;
+ DIR *dirp;
+ struct dirent *dp;
- for (i = 0; i < ARRAY_SIZE(bindirs) - 1 ; i++)
- if (!strcmp(bindirs[i], str))
- return 1;
+ strncpy(buf, dir, PATH_MAX);
+ buf[PATH_MAX - 1] = '\0';
- for (i = 0; i < ARRAY_SIZE(mandirs) - 1; i++)
- if (!strcmp(mandirs[i], str))
- return 1;
+ DBG(printf("add subdir: %s", buf));
- for (i = 0; i < ARRAY_SIZE(srcdirs) - 1; i++)
- if (!strcmp(srcdirs[i], str))
- return 1;
+ d = strchr(buf, '*');
+ if (!d)
+ return;
+ *d = 0;
- return 0;
+ dirp = opendir(buf);
+ if (!dirp)
+ return;
+
+ DBG(printf(" scan: %s", buf));
+
+ while ((dp = readdir(dirp)) != NULL) {
+ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
+ continue;
+ snprintf(d, PATH_MAX - (d - buf), "%s", dp->d_name);
+ /* a dir definition can have a star in middle of path */
+ strcat(buf, strchr(dir, '*') + 1);
+ dirlist_add_dir(ls, type, buf);
+ }
+ closedir(dirp);
+ return;
}
-static void fillpath(void)
+static void construct_dirlist_from_env(const char *env,
+ struct wh_dirlist **ls,
+ int type)
{
- char *key=NULL, *tok=NULL, *pathcp, *path = getenv("PATH");
- int i = 0;
-
+ char *key = NULL, *tok = NULL, *pathcp, *path = getenv(env);
if (!path)
return;
pathcp = xstrdup(path);
- for (tok = strtok_r(pathcp, ":", &key); tok;
- tok = strtok_r(NULL, ":", &key)) {
-
- /* make sure we don't repeat the search path */
- if (inpath(tok))
- continue;
-
- pathdir = xrealloc(pathdir, (i + 1) * sizeof(char *));
- pathdir[i++] = xstrdup(tok);
- }
+ DBG(printf("construct from env: %s", path));
- pathdir = xrealloc(pathdir, (i + 1) * sizeof(char *));
- pathdir[i] = NULL;
+ for (tok = strtok_r(pathcp, ":", &key); tok;
+ tok = strtok_r(NULL, ":", &key))
+ dirlist_add_dir(ls, type, tok);
- pathdir_p = pathdir;
free(pathcp);
+ return;
}
-static void freepath(void)
+static void construct_dirlist_from_argv(struct wh_dirlist **ls,
+ int *idx,
+ int argc,
+ char *argv[],
+ int type)
{
- free(pathdir);
+ DBG(printf("construct argv[%d..]", *idx));
+
+ for (; *idx < argc; (*idx)++) {
+ if (*argv[*idx] == '-') /* end of the list */
+ return;
+ dirlist_add_dir(ls, type, argv[*idx]);
+ }
+ return;
}
-static void
-findv(char **dirv, int dirc, char *cp)
+static void construct_dirlist(struct wh_dirlist **ls,
+ int type,
+ const char **paths)
{
+ size_t i;
- while (dirc > 0)
- findin(*dirv++, cp), dirc--;
-}
+ DBG(printf("construct from dirs"));
-static void
-looksrc(char *cp)
-{
- if (Sflag == NULL)
- findv(srcdirs, ARRAY_SIZE(srcdirs)-1, cp);
- else
- findv(Sflag, Scnt, cp);
+ for (i = 0; paths[i]; i++) {
+ if (!strchr(paths[i], '*'))
+ dirlist_add_dir(ls, type, paths[i]);
+ else
+ dirlist_add_subdir(ls, type, paths[i]);
+ }
+ return;
}
-static void
-lookbin(char *cp)
+static void free_dirlist(struct wh_dirlist **ls0, int type)
{
- if (Bflag == NULL) {
- findv(bindirs, ARRAY_SIZE(bindirs)-1, cp);
- while (*pathdir_p)
- findin(*pathdir_p++, cp); /* look $PATH */
- } else
- findv(Bflag, Bcnt, cp);
-}
+ struct wh_dirlist *prev = NULL, *next, *ls = *ls0;
-static void
-lookman(char *cp)
-{
- if (Mflag == NULL)
- findv(mandirs, ARRAY_SIZE(mandirs)-1, cp);
- else
- findv(Mflag, Mcnt, cp);
-}
+ *ls0 = NULL;
-static void
-getlist(int *argcp, char ***argvp, char ***flagp, int *cntp)
-{
- (*argvp)++;
- *flagp = *argvp;
- *cntp = 0;
- for ((*argcp)--; *argcp > 0 && (*argvp)[0][0] != '-'; (*argcp)--)
- (*cntp)++, (*argvp)++;
- (*argcp)++;
- (*argvp)--;
+ DBG(printf("freeing dirlist"));
+
+ while (ls) {
+ if (ls->type & type) {
+ next = ls->next;
+
+ DBG(printf("freeing dir: %s", ls->path));
+
+ free(ls->path);
+ free(ls);
+ ls = next;
+ if (prev)
+ prev->next = ls;
+ } else {
+ if (!prev)
+ *ls0 = ls; /* first unremoved */
+ prev = ls;
+ ls = ls->next;
+ }
+ }
+
+ return;
}
-static void
-zerof(void)
+
+static int filename_equal(const char *cp, const char *dp)
{
- if (sflag && bflag && mflag)
- sflag = bflag = mflag = 0;
+ int i = strlen(dp);
+
+ /*DBG(printf("compare '%s' and '%s'", cp, dp));*/
+
+ if (dp[0] == 's' && dp[1] == '.' && filename_equal(cp, dp + 2))
+ return 1;
+ if (!strcmp(dp + i - 2, ".Z"))
+ i -= 2;
+ else if (!strcmp(dp + i - 3, ".gz"))
+ i -= 3;
+ else if (!strcmp(dp + i - 3, ".xz"))
+ i -= 3;
+ else if (!strcmp(dp + i - 4, ".bz2"))
+ i -= 4;
+ while (*cp && *dp && *cp == *dp)
+ cp++, dp++, i--;
+ if (*cp == 0 && *dp == 0)
+ return 1;
+ while (isdigit(*dp))
+ dp++;
+ if (*cp == 0 && *dp++ == '.') {
+ --i;
+ while (i > 0 && *dp)
+ if (--i, *dp++ == '.')
+ return (*dp++ == 'C' && *dp++ == 0);
+ return 1;
+ }
+ return 0;
}
-static int
-print_again(char *cp)
+static void findin(const char *dir, const char *pattern, int *count, char **wait)
{
- if (print)
- printf("%s:", cp);
- if (sflag) {
- looksrc(cp);
- if (uflag && print == 0 && count != 1) {
- print = 1;
- return 1;
- }
- }
- count = 0;
- if (bflag) {
- lookbin(cp);
- if (uflag && print == 0 && count != 1) {
- print = 1;
- return 1;
- }
- }
- count = 0;
- if (mflag) {
- lookman(cp);
- if (uflag && print == 0 && count != 1) {
- print = 1;
- return 1;
- }
+ DIR *dirp;
+ struct dirent *dp;
+
+ dirp = opendir(dir);
+ if (dirp == NULL)
+ return;
+
+ DBG(printf("find '%s' in '%s'", pattern, dir));
+
+ while ((dp = readdir(dirp)) != NULL) {
+ if (!filename_equal(pattern, dp->d_name))
+ continue;
+
+ if (uflag && *count == 0)
+ xasprintf(wait, "%s/%s", dir, dp->d_name);
+
+ else if (uflag && *count == 1 && *wait) {
+ printf("%s: %s %s/%s", pattern, *wait, dir, dp->d_name);
+ free(*wait);
+ *wait = NULL;
+ } else
+ printf(" %s/%s", dir, dp->d_name);
+ ++(*count);
}
- return 0;
+ closedir(dirp);
+ return;
}
-static void
-lookup(char *cp)
+static void lookup(const char *pattern, struct wh_dirlist *ls, int want)
{
- register char *dp;
-
- for (dp = cp; *dp; dp++)
- continue;
- for (; dp > cp; dp--) {
- if (*dp == '.') {
- *dp = 0;
- break;
- }
+ char patbuf[PATH_MAX];
+ int count = 0;
+ char *wait = NULL, *p;
+
+ DBG(printf("lookup dirs for '%s' (%s)", patbuf, pattern));
+
+ /* canonicalize pattern -- remove path suffix etc. */
+ p = strrchr(pattern, '/');
+ p = p ? p + 1 : (char *) pattern;
+ strncpy(patbuf, p, PATH_MAX);
+ patbuf[PATH_MAX - 1] = '\0';
+ p = strrchr(patbuf, '.');
+ if (p)
+ *p = '\0';
+
+ if (!uflag)
+ /* if -u not specified then we always print the pattern */
+ printf("%s:", patbuf);
+
+ for (; ls; ls = ls->next) {
+ if ((ls->type & want) && ls->path)
+ findin(ls->path, patbuf, &count, &wait);
}
- for (dp = cp; *dp; dp++)
- if (*dp == '/')
- cp = dp + 1;
- if (uflag) {
- print = 0;
- count = 0;
- } else
- print = 1;
-
- while (print_again(cp))
- /* all in print_again() */ ;
-
- if (print)
- printf("\n");
+
+ free(wait);
+
+ if ((count && !uflag) || (uflag && count > 1))
+ putchar('\n');
+ return;
}
-/*
- * whereis name
- * look for source, documentation and binaries
- */
-int
-main(int argc, char **argv)
+int main(int argc, char **argv)
{
+ struct wh_dirlist *ls = NULL;
+ int want = ALL_DIRS;
+ int i;
+
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
atexit(close_stdout);
- argc--, argv++;
- if (argc == 0)
+ if (argc == 1)
usage(stderr);
- do
- if (argv[0][0] == '-') {
- register char *cp = argv[0] + 1;
- while (*cp) switch (*cp++) {
+ construct_dirlist(&ls, BIN_DIR, bindirs);
+ construct_dirlist_from_env("PATH", &ls, BIN_DIR);
+
+ construct_dirlist(&ls, MAN_DIR, mandirs);
+ construct_dirlist(&ls, SRC_DIR, srcdirs);
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (*arg != '-') {
+ lookup(arg, ls, want);
+ continue;
+ }
+
+ if (i > 1 && *argv[i - 1] != '-')
+ /* the list of search patterns has been interupted by
+ * any non-pattern option, then reset the mask for
+ * wanted directories. For example:
+ *
+ * whereis -m ls -b tr
+ *
+ * search for "ls" in mandirs and "tr" in bindirs
+ */
+ want = ALL_DIRS;
+
+ for (++arg; arg && *arg; arg++) {
+ switch (*arg) {
case 'f':
break;
-
- case 'S':
- getlist(&argc, &argv, &Sflag, &Scnt);
+ case 'u':
+ uflag = 1;
break;
-
case 'B':
- getlist(&argc, &argv, &Bflag, &Bcnt);
+ if (*(arg + 1))
+ usage(stderr);
+ i++;
+ free_dirlist(&ls, BIN_DIR);
+ construct_dirlist_from_argv(
+ &ls, &i, argc, argv, BIN_DIR);
break;
-
case 'M':
- getlist(&argc, &argv, &Mflag, &Mcnt);
+ if (*(arg + 1))
+ usage(stderr);
+ i++;
+ free_dirlist(&ls, MAN_DIR);
+ construct_dirlist_from_argv(
+ &ls, &i, argc, argv, MAN_DIR);
+ break;
+ case 'S':
+ if (*(arg + 1))
+ usage(stderr);
+ i++;
+ free_dirlist(&ls, SRC_DIR);
+ construct_dirlist_from_argv(
+ &ls, &i, argc, argv, SRC_DIR);
break;
-
- case 's':
- zerof();
- sflag++;
- continue;
-
- case 'u':
- uflag++;
- continue;
-
case 'b':
- zerof();
- bflag++;
- continue;
-
+ want = want == ALL_DIRS ? BIN_DIR : want | BIN_DIR;
+ break;
case 'm':
- zerof();
- mflag++;
- continue;
+ want = want == ALL_DIRS ? MAN_DIR : want | MAN_DIR;
+ break;
+ case 's':
+ want = want == ALL_DIRS ? SRC_DIR : want | SRC_DIR;
+ break;
case 'V':
printf(UTIL_LINUX_VERSION);
return EXIT_SUCCESS;
@@ -471,14 +523,9 @@ main(int argc, char **argv)
default:
usage(stderr);
}
- argv++;
- } else {
- if (Bcnt == 0 && pathdir == NULL)
- fillpath();
- lookup(*argv++);
}
- while (--argc > 0);
+ }
- freepath();
+ free_dirlist(&ls, ALL_DIRS);
return EXIT_SUCCESS;
}