# HG changeset patch # User landley@driftwood # Date 1162190280 18000 # Node ID fc9c0503d5e212c53659e7dfbc0aa042315123f8 # Parent 7806b8026931785c7d7e2a8d032631a1e3ae8b07 Implement df. Add -Wall to build and fix up warnings. Add copyright notices. Add error_msg() and itoa() to library. Remove argc from globals (since argv is null terminated), add optflags to globals. diff -r 7806b8026931 -r fc9c0503d5e2 Makefile --- a/Makefile Thu Oct 26 12:04:37 2006 -0400 +++ b/Makefile Mon Oct 30 01:38:00 2006 -0500 @@ -1,5 +1,5 @@ all: - $(CC) -Os -s $(CFLAGS) -I . main.c toys/*.c lib/*.c -o toybox + $(CC) -Wall -Os -s $(CFLAGS) -I . main.c toys/*.c lib/*.c -o toybox clean: rm toybox diff -r 7806b8026931 -r fc9c0503d5e2 lib/functions.c --- a/lib/functions.c Thu Oct 26 12:04:37 2006 -0400 +++ b/lib/functions.c Mon Oct 30 01:38:00 2006 -0500 @@ -5,19 +5,59 @@ * succeed or kill the program with an error message, but never return failure. * They usually have the same arguments and return value as the function they * wrap. + * + * Copyright 2006 Rob Landley */ #include "toys.h" +void verror_msg(char *msg, int err, va_list va) +{ + fprintf(stderr, "%s: ", toys.which->name); + vfprintf(stderr, msg, va); + if (err) fprintf(stderr, ": %s", strerror(err)); + putc('\n', stderr); +} + +void error_msg(char *msg, ...) +{ + va_list va; + + va_start(va, msg); + verror_msg(msg, 0, va); + va_end(va); +} + +void perror_msg(char *msg, ...) +{ + va_list va; + + va_start(va, msg); + verror_msg(msg, errno, va); + va_end(va); +} + // Die with an error message. void error_exit(char *msg, ...) { - va_list args; + va_list va; + + va_start(va, msg); + verror_msg(msg, 0, va); + va_end(va); + + exit(toys.exitval); +} - va_start(args, msg); - fprintf(stderr, "%s: ", toys.which->name); - vfprintf(stderr, msg, args); - va_end(args); +// Die with an error message and strerror(errno) +void perror_exit(char *msg, ...) +{ + va_list va; + + va_start(va, msg); + verror_msg(msg, errno, va); + va_end(va); + exit(toys.exitval); } @@ -87,7 +127,7 @@ // Die unless we can exec argv[] (or run builtin command). Note that anything // with a path isn't a builtin, so /bin/sh won't match the builtin sh. -void *xexec(char **argv) +void xexec(char **argv) { toy_exec(argv); execvp(argv[0], argv); @@ -118,6 +158,8 @@ { char *buf = getcwd(NULL, 0); if (!buf) error_exit("xgetcwd"); + + return buf; } // Find this file in a colon-separated path. @@ -126,7 +168,7 @@ { char *next, *res = NULL, *cwd = xgetcwd(); - while (next = index(path,':')) { + while ((next = index(path,':'))) { int len = next-path; if (len==1) res = xmsprintf("%s/%s", cwd, filename); @@ -144,3 +186,59 @@ return res; } + +// Convert unsigned int to ascii, writing into supplied buffer. A truncated +// result contains the first few digits of the result ala strncpy, and is +// always null terminated (unless buflen is 0). +void utoa_to_buf(unsigned n, char *buf, unsigned buflen) +{ + int i, out = 0; + + if (buflen) { + for (i=1000000000; i; i/=10) { + int res = n/i; + + if ((res || out || i == 1) && --buflen>0) { + out++; + n -= res*i; + *buf++ = '0' + res; + } + } + *buf = 0; + } +} + +// Convert signed integer to ascii, using utoa_to_buf() +void itoa_to_buf(int n, char *buf, unsigned buflen) +{ + if (buflen && n<0) { + n = -n; + *buf++ = '-'; + buflen--; + } + utoa_to_buf((unsigned)n, buf, buflen); +} + +// This static buffer is used by both utoa() and itoa(), calling either one a +// second time will overwrite the previous results. +// +// The longest 32 bit integer is -2 billion plus a null terminator: 12 bytes. +// Note that int is always 32 bits on any remotely unix-like system, see +// http://www.unix.org/whitepapers/64bit.html for details. + +static char itoa_buf[12]; + +// Convert unsigned integer to ascii, returning a static buffer. +char *utoa(unsigned n) +{ + utoa_to_buf(n, itoa_buf, sizeof(itoa_buf)); + + return itoa_buf; +} + +char *itoa(int n) +{ + itoa_to_buf(n, itoa_buf, sizeof(itoa_buf)); + + return itoa_buf; +} diff -r 7806b8026931 -r fc9c0503d5e2 lib/getmountlist.c --- a/lib/getmountlist.c Thu Oct 26 12:04:37 2006 -0400 +++ b/lib/getmountlist.c Mon Oct 30 01:38:00 2006 -0500 @@ -1,4 +1,8 @@ /* vi: set sw=4 ts=4 : */ +/* getmountlist.c - Get a linked list of mount points, with stat information. + * + * Copyright 2006 Rob Landley + */ #include "toys.h" @@ -6,8 +10,9 @@ char *path_mounts = "/proc/mounts"; -// Get a list of mount points from /etc/mtab or /proc/mounts. This returns -// a reversed list, which is good for finding overmounts and such. +// Get a list of mount points from /etc/mtab or /proc/mounts, including +// statvfs() information. This returns a reversed list, which is good for +// finding overmounts and such. struct mtab_list *getmountlist(int die) { @@ -21,9 +26,13 @@ if (die) error_exit("cannot open %s", path_mounts); } else { while (getmntent_r(fp, &me, evilbuf, sizeof(evilbuf))) { - mt = xmalloc(sizeof(struct mtab_list) + strlen(me.mnt_fsname) + + mt = xzalloc(sizeof(struct mtab_list) + strlen(me.mnt_fsname) + strlen(me.mnt_dir) + strlen(me.mnt_type) + 3); mt->next = mtlist; + // Get information about this filesystem. Yes, we need both. + stat(me.mnt_dir, &(mt->stat)); + statvfs(me.mnt_dir, &(mt->statvfs)); + // Remember information from /proc/mounts strcpy(mt->type, me.mnt_type); mt->dir = mt->type + strlen(mt->type) + 1; strcpy(mt->dir, me.mnt_dir); diff -r 7806b8026931 -r fc9c0503d5e2 lib/lib.h --- a/lib/lib.h Thu Oct 26 12:04:37 2006 -0400 +++ b/lib/lib.h Mon Oct 30 01:38:00 2006 -0500 @@ -1,25 +1,44 @@ /* vi: set ts=4 :*/ +/* lib.h - header file for lib directory + * + * Copyright 2006 Rob Landley + */ // functions.c +void verror_msg(char *msg, int err, va_list va); +void error_msg(char *msg, ...); +void perror_msg(char *msg, ...); void error_exit(char *msg, ...); +void perror_exit(char *msg, ...); void strlcpy(char *dest, char *src, size_t size); void *xmalloc(size_t size); void *xzalloc(size_t size); void xrealloc(void **ptr, size_t size); void *xstrndup(char *s, size_t n); char *xmsprintf(char *format, ...); -void *xexec(char **argv); +void xexec(char **argv); int xopen(char *path, int flags, int mode); FILE *xfopen(char *path, char *mode); char *xgetcwd(void); char *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; + struct stat stat; + struct statvfs statvfs; char *dir; char *device; char type[0]; diff -r 7806b8026931 -r fc9c0503d5e2 main.c --- a/main.c Thu Oct 26 12:04:37 2006 -0400 +++ b/main.c Mon Oct 30 01:38:00 2006 -0500 @@ -58,7 +58,6 @@ toys.which = which; toys.argv = argv; - for (toys.argc = 0; argv[toys.argc]; toys.argc++); toys.exitval = 1; } diff -r 7806b8026931 -r fc9c0503d5e2 toys.h --- a/toys.h Thu Oct 26 12:04:37 2006 -0400 +++ b/toys.h Mon Oct 30 01:38:00 2006 -0500 @@ -6,13 +6,21 @@ * Licensed under GPL version 2, see file LICENSE in this tarball for details. */ +#include +#include +#include +#include #include #include +#include #include #include #include #include #include +#include +#include +#include #include #include "lib/lib.h" @@ -44,8 +52,8 @@ extern struct toy_context { struct toy_list *which; // Which entry in toy_list is this one? int exitval; // Value error_exit feeds to exit() - int argc; - char **argv; + int optflags; // Command line option flags + char **argv; // Command line arguments char buf[4096]; } toys; @@ -53,7 +61,10 @@ struct cd_data {;}; struct toybox_data {;}; struct toysh_data {;}; -struct df_data {;}; +struct df_data { + struct string_list *fstype; + long units; +}; union toy_union { struct exit_data exit; @@ -73,3 +84,5 @@ #define CFG_TOYSH_ENVVARS 0 // Environment variables #define CFG_TOYSH_LOCVARS 0 // Local, synthetic, fancy prompts, set, $? #define CFG_TOYSH_PIPES 0 // Pipes and redirects: | > < >> << && || & () ; + +#define CFG_DF_PEDANTIC 1 // Support -P and -k in df diff -r 7806b8026931 -r fc9c0503d5e2 toys/df.c --- a/toys/df.c Thu Oct 26 12:04:37 2006 -0400 +++ b/toys/df.c Mon Oct 30 01:38:00 2006 -0500 @@ -2,7 +2,7 @@ /* * df.c - report free disk space. * - * Implemented according to SUSv3: + * Implemented roughly according to SUSv3: * http://www.opengroup.org/onlinepubs/009695399/utilities/df.html * * usage: df [-k] [-P|-t] [file...] @@ -10,16 +10,110 @@ #include "toys.h" +static void show_mt(struct mtab_list *mt) +{ + int len; + long size, used, avail; + uint64_t block; + + // Return if it wasn't found (should never happen, but with /etc/mtab...) + if (!mt) return; + + // If we have -t, skip other filesystem types + if (toy.df.fstype) { + struct string_list *sl; + + for (sl = toy.df.fstype; sl; sl = sl->next) + if (!strcmp(mt->type, sl->str)) break; + if (!sl) return; + } + + // If we don't have -a, skip synthetic filesystems + if (!(toys.optflags & 1) && !mt->statvfs.f_blocks) return; + + // Figure out how much total/used/free space this filesystem has, + // forcing 64-bit math because filesystems are big now. + block = mt->statvfs.f_bsize ? : 1; + + size = (long)((block * mt->statvfs.f_blocks) / toy.df.units); + used = (long)((block * (mt->statvfs.f_blocks-mt->statvfs.f_bfree)) + / toy.df.units); + avail = (long)((block + * (getuid() ? mt->statvfs.f_bavail : mt->statvfs.f_bfree)) + / toy.df.units); + + // Figure out appropriate spacing + len = 25 - strlen(mt->device); + if (len < 1) len = 1; + printf("%s% *ld % 10ld % 9ld % 3ld%% %s\n",mt->device, len, + size, used, avail, 100-(long)((100*(uint64_t)avail)/size), mt->dir); +} + int df_main(void) { - struct mtab_list *mt, *mtlist; + struct mtab_list *mt, *mt2, *mtlist; + char **argv; + + // get_optflags("Pkt:a",&(toy.df.fstype)); + argv = NULL; - //int units = 512; + // Handle -P and -k + toy.df.units = 1024; + if (CFG_DF_PEDANTIC && (toys.optflags & 8)) { + // Units are 512 bytes if you select "pedantic" without "kilobytes". + if ((toys.optflags&3) == 1) toy.df.units = 512; + printf("Filesystem %ld-blocks Used Available Capacity Mounted on\n", + toy.df.units); + } else puts("Filesystem\t1K-blocks\tUsed Available Use% Mounted on"); + mtlist = getmountlist(1); - // Zap overmounts - for (mt = mtlist; mt; mt = mt->next) { - printf("type=%s dir=%s device=%s\n",mt->type,mt->dir,mt->device); + + // If we have a list of filesystems on the command line, loop through them. + if (argv) { + char *next; + + for(next = *argv; *next; next++) { + struct stat st; + + // Stat it (complain if we can't). + if(!stat(next, &st)) { + perror_msg("`%s'", next); + toys.exitval = 1; + continue; + } + + // Find and display this filesystem. Use _last_ hit in case of + // -- bind mounts. + mt2 = NULL; + for (mt = mtlist; mt; mt = mt->next) + if (st.st_dev == mt->stat.st_dev) mt2 = mt; + show_mt(mt2); + } + } else { + // Get and loop through mount list. + + for (mt = mtlist; mt; mt = mt->next) { + struct mtab_list *mt2, *mt3; + + if (!mt->stat.st_dev) continue; + + // Filter out overmounts. + mt3 = mt; + for (mt2 = mt->next; mt2; mt2 = mt2->next) { + if (mt->stat.st_dev == mt2->stat.st_dev) { + // For --bind mounts, take last match + if (!strcmp(mt->device, mt2->device)) mt3 = mt2; + // Filter out overmounts + mt2->stat.st_dev = 0; + } + } + show_mt(mt3); + } } + if (CFG_TOYS_FREE) { + llist_free(mtlist, NULL); + free(argv); + } return 0; } diff -r 7806b8026931 -r fc9c0503d5e2 toys/toysh.c --- a/toys/toysh.c Thu Oct 26 12:04:37 2006 -0400 +++ b/toys/toysh.c Mon Oct 30 01:38:00 2006 -0500 @@ -181,14 +181,14 @@ int cd_main(void) { - char *dest = toys.argc>1 ? toys.argv[1] : getenv("HOME"); + char *dest = toys.argv[1] ? toys.argv[1]: getenv("HOME"); if (chdir(dest)) error_exit("chdir %s",dest); return 0; } int exit_main(void) { - exit(toys.argc>1 ? atoi(toys.argv[1]) : 0); + exit(toys.argv[1] ? atoi(toys.argv[1]) : 0); } int toysh_main(void)