# HG changeset patch # User Rob Landley # Date 1162530352 18000 # Node ID 3981c96f9285c63830b0f0d144d821c010addf55 # Parent 414625f9766768a0ebd8ba66b51f9508bc77ef85 Implement which. Add hello world to menuconfig. Wrap the various applet main functions in main.c with USE() macros so --gc-sections can strip them. diff -r 414625f97667 -r 3981c96f9285 lib/functions.c --- a/lib/functions.c Thu Nov 02 19:50:02 2006 -0500 +++ b/lib/functions.c Fri Nov 03 00:05:52 2006 -0500 @@ -241,69 +241,45 @@ return path; } -// Check whether a file exists, or is executable, or... -int is_file_type(char *path, int type) -{ - // Is there a file here we can execute? - if (!access(path, type)) { - struct stat st; - // Confirm it's not a directory. - if (!stat(path, &st) && S_ISREG(st.st_mode)) return 1; - } - - return 0; -} - - -// Find an exectuable file either at a path with a slash in it (absolute or -// relative to current directory), or in $PATH. Returns absolute path to file, -// or NULL if not found. - +// Find all file in a colon-separated path with access type "type" (generally +// X_OK or R_OK). Returns a list of absolute paths to each file found, in +// order. -char *which_in_path(char *filename) +struct string_list *find_in_path(char *path, char *filename) { - char *res; - - if (index(filename, '/')) { - res = xabspath(filename); - if (is_file_type(filename, X_OK)) return res; - free(res); - return NULL; - } - return find_in_path(getenv("PATH"), filename, X_OK); -} - -// Find file in a colon-separated path with access type "type" (generally -// X_OK or R_OK). Returns absolute path to file, or NULL if not found. - -char *find_in_path(char *path, char *filename, int type) -{ - char *res = NULL, *cwd = xgetcwd(); + struct string_list *rlist = NULL; + char *cwd = xgetcwd(); for (;;) { char *next = path ? index(path, ':') : NULL; int len = next ? next-path : strlen(path); + struct string_list *rnext; + struct stat st; - if (!len) res = xmsprintf("%s/%s", cwd, filename); + rnext = xmalloc(sizeof(void *) + strlen(filename) + + (len ? len : strlen(cwd)) + 2); + if (!len) sprintf(rnext->str, "%s/%s", cwd, filename); else { - res = xmalloc(len+strlen(filename)+2); + char *res = rnext->str; strncpy(res, path, len); - res[len] = '/'; - strcpy(res+len+1, filename); + res += len; + *(res++) = '/'; + strcpy(res, filename); } - // Is there a file here we can execute? - if (is_file_type(res, type)) break; + // Confirm it's not a directory. + if (!stat(rnext->str, &st) && S_ISREG(st.st_mode)) { + rnext->next = rlist; + rlist = rnext; + } else free(rnext); - free(res); - res = NULL; if (!next) break; path += len; path++; } free(cwd); - return res; + return rlist; } diff -r 414625f97667 -r 3981c96f9285 lib/lib.h --- a/lib/lib.h Thu Nov 02 19:50:02 2006 -0500 +++ b/lib/lib.h Fri Nov 03 00:05:52 2006 -0500 @@ -4,6 +4,15 @@ * Copyright 2006 Rob Landley */ +// llist.c +void llist_free(void *list, void (*freeit)(void *data)); +void *llist_pop(void *list); // actually void **list, but the compiler's dumb + +struct string_list { + struct string_list *next; + char str[0]; +}; + // functions.c void verror_msg(char *msg, int err, va_list va); void error_msg(char *msg, ...); @@ -25,22 +34,12 @@ void xread(int fd, char *buf, size_t count); char *xgetcwd(void); char *xabspath(char *path); -int is_file_type(char *path, int type); -char *which_in_path(char *filename); -char *find_in_path(char *path, char *filename, int type); +struct string_list *find_in_path(char *path, char *filename); void utoa_to_buf(unsigned n, char *buf, unsigned buflen); void itoa_to_buf(int n, char *buf, unsigned buflen); char *utoa(unsigned n); char *itoa(int n); -// llist.c -void llist_free(void *list, void (*freeit)(void *data)); - -struct string_list { - struct string_list *next; - char *str; -}; - // getmountlist.c struct mtab_list { struct mtab_list *next; diff -r 414625f97667 -r 3981c96f9285 lib/llist.c --- a/lib/llist.c Thu Nov 02 19:50:02 2006 -0500 +++ b/lib/llist.c Fri Nov 03 00:05:52 2006 -0500 @@ -12,10 +12,21 @@ void llist_free(void *list, void (*freeit)(void *data)) { while (list) { - void **next = (void **)list; - void *list_next = *next; - if (freeit) freeit(list); - free(list); - list = list_next; + void *pop = llist_pop(&list); + if (freeit) freeit(pop); } } + +// Return the first item from the list, advancing the list (which must be called +// as &list) +void *llist_pop(void *list) +{ + // I'd use a void ** for the argument, and even accept the typecast in all + // callers as documentation you need the &, except the stupid compiler + // would then scream about type-punned pointers. Screw it. + void **llist = (void **)list; + void **next = (void **)*llist; + *llist = *next; + + return (void *)next; +} diff -r 414625f97667 -r 3981c96f9285 main.c --- a/main.c Thu Nov 02 19:50:02 2006 -0500 +++ b/main.c Fri Nov 03 00:05:52 2006 -0500 @@ -14,12 +14,13 @@ // This one is out of order on purpose. {"toybox", toybox_main, 0}, // The rest of these are alphabetical, for binary search. - {"cd", cd_main, TOYFLAG_NOFORK}, - {"df", df_main, TOYFLAG_USR|TOYFLAG_SBIN}, - {"exit", exit_main, TOYFLAG_NOFORK}, - {"hello", hello_main, TOYFLAG_NOFORK|TOYFLAG_USR}, - {"sh", toysh_main, TOYFLAG_BIN}, - {"toysh", toysh_main, TOYFLAG_BIN} + USE_TOYSH({"cd", cd_main, TOYFLAG_NOFORK},) + USE_DF({"df", df_main, TOYFLAG_USR|TOYFLAG_SBIN},) + USE_TOYSH({"exit", exit_main, TOYFLAG_NOFORK},) + USE_HELLO({"hello", hello_main, TOYFLAG_NOFORK|TOYFLAG_USR},) + USE_TOYSH({"sh", toysh_main, TOYFLAG_BIN},) + USE_TOYSH({"toysh", toysh_main, TOYFLAG_BIN},) + USE_WHICH({"which", which_main, TOYFLAG_USR|TOYFLAG_BIN},) }; #define TOY_LIST_LEN (sizeof(toy_list)/sizeof(struct toy_list)) diff -r 414625f97667 -r 3981c96f9285 toys.h --- a/toys.h Thu Nov 02 19:50:02 2006 -0500 +++ b/toys.h Fri Nov 03 00:05:52 2006 -0500 @@ -31,6 +31,7 @@ int hello_main(void); int toybox_main(void); int toysh_main(void); +int which_main(void); #define TOYFLAG_USR (1<<0) #define TOYFLAG_BIN (1<<1) diff -r 414625f97667 -r 3981c96f9285 toys/Config.in --- a/toys/Config.in Thu Nov 02 19:50:02 2006 -0500 +++ b/toys/Config.in Fri Nov 03 00:05:52 2006 -0500 @@ -28,6 +28,12 @@ -k Sets units back to 1024 bytes (the default without -P) +config HELLO + bool "hello" + default n + help + A hello world program. You don't need this. + config TOYSH bool "sh (toysh)" default n @@ -146,5 +152,15 @@ Adds the commands exec, fg, bg, help, jobs, pwd, export, source, set, unset, read, alias. +config WHICH + bool "Which" + default n + help + usage: which [-a] filename ... + + Search $PATH for executable files matching filename(s). + + -a Show all matches + endmenu diff -r 414625f97667 -r 3981c96f9285 toys/which.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toys/which.c Fri Nov 03 00:05:52 2006 -0500 @@ -0,0 +1,72 @@ +/* vi: set sw=4 ts=4: */ +/* + * which.c - + * + * Copyright 2006 Rob landley + */ + +#include "toys.h" + +#define OPTIONS "a" +#define OPT_a 1 + +// Find an exectuable file either at a path with a slash in it (absolute or +// relative to current directory), or in $PATH. Returns absolute path to file, +// or NULL if not found. + +static int which_in_path(char *filename) +{ + struct string_list *list; + + // If they gave us a path, don't worry about $PATH or -a + + if (index(filename, '/')) { + // Confirm it has the executable bit set, and it's not a directory. + if (!access(filename, X_OK)) { + struct stat st; + + if (!stat(filename, &st) && S_ISREG(st.st_mode)) { + puts(filename); + return 0; + } + return 1; + } + } + + // Search $PATH for matches. + list = find_in_path(getenv("PATH"), filename); + if (!list) return 1; + + // Print out matches + while (list) { + if (!access(list->str, X_OK)) { + puts(list->str); + // If we should stop at one match, do so + if (toys.optflags & OPT_a) { + llist_free(list, NULL); + break; + } + } + free(llist_pop(&list)); + } + + return 0; +} + +int which_main(void) +{ + char **argv; + int rc = 0; + + // get_optflags(OPTIONS); + argv = toys.argv+1; + + if (!*argv) rc++; + else { + int i; + for (i=0; argv[i]; i++) rc |= which_in_path(argv[i]); + } + // if (CFG_TOYS_FREE) free(argv); + + return rc; +}