find:: get rid of nested function (it's a gcc-ism)
[oweals/busybox.git] / miscutils / time.c
index d21944e01704152e445849c3d79b1445c4e640dd..19b0b44c91c0ba406d0f06135257cb5f4abc0f98 100644 (file)
@@ -1,15 +1,22 @@
 /* vi: set sw=4 ts=4: */
-/* `time' utility to display resource usage of processes.
+/* 'time' utility to display resource usage of processes.
    Copyright (C) 1990, 91, 92, 93, 96 Free Software Foundation, Inc.
 
-   Licensed under GPL version 2, see file LICENSE in this tarball for details.
+   Licensed under GPLv2, see file LICENSE in this source tree.
 */
 /* Originally written by David Keppel <pardo@cs.washington.edu>.
    Heavily modified by David MacKenzie <djm@gnu.ai.mit.edu>.
    Heavily modified for busybox by Erik Andersen <andersen@codepoet.org>
 */
 
+//usage:#define time_trivial_usage
+//usage:       "[-v] PROG ARGS"
+//usage:#define time_full_usage "\n\n"
+//usage:       "Run PROG, display resource usage when it exits\n"
+//usage:     "\n       -v      Verbose"
+
 #include "libbb.h"
+#include <sys/resource.h> /* getrusage */
 
 /* Information on the resources used by a child process.  */
 typedef struct {
@@ -28,7 +35,6 @@ static const char default_format[] ALIGN1 = "real\t%E\nuser\t%u\nsys\t%T";
 /* The output format for the -p option .*/
 static const char posix_format[] ALIGN1 = "real %e\nuser %U\nsys %S";
 
-
 /* Format string for printing all statistics verbosely.
    Keep this output to 24 lines so users on terminals can see it all.*/
 static const char long_format[] ALIGN1 =
@@ -56,35 +62,31 @@ static const char long_format[] ALIGN1 =
        "\tPage size (bytes): %Z\n"
        "\tExit status: %x";
 
-
 /* Wait for and fill in data on child process PID.
    Return 0 on error, 1 if ok.  */
-
 /* pid_t is short on BSDI, so don't try to promote it.  */
-static int resuse_end(pid_t pid, resource_t * resp)
+static void resuse_end(pid_t pid, resource_t *resp)
 {
-       int status;
        pid_t caught;
 
        /* Ignore signals, but don't ignore the children.  When wait3
-          returns the child process, set the time the command finished. */
-       while ((caught = wait3(&status, 0, &resp->ru)) != pid) {
-               if (caught == -1)
-                       return 0;
+        * returns the child process, set the time the command finished. */
+       while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) {
+               if (caught == -1 && errno != EINTR) {
+                       bb_perror_msg("wait");
+                       return;
+               }
        }
-       resp->elapsed_ms = (monotonic_us() / 1000) - resp->elapsed_ms;
-       resp->waitstatus = status;
-       return 1;
+       resp->elapsed_ms = monotonic_ms() - resp->elapsed_ms;
 }
 
-/* Print ARGV, with each entry in ARGV separated by FILLER.  */
-static void printargv(char *const *argv, const char *filler)
+static void printargv(char *const *argv)
 {
-       fputs(*argv, stdout);
-       while (*++argv) {
-               fputs(filler, stdout);
-               fputs(*argv, stdout);
-       }
+       const char *fmt = " %s" + 1;
+       do {
+               printf(fmt, *argv);
+               fmt = " %s";
+       } while (*++argv);
 }
 
 /* Return the number of kilobytes corresponding to a number of pages PAGES.
@@ -94,24 +96,18 @@ static void printargv(char *const *argv, const char *filler)
    This is funky since the pagesize could be less than 1K.
    Note: Some machines express getrusage statistics in terms of K,
    others in terms of pages.  */
-
-static unsigned long ptok(unsigned long pages)
+static unsigned long ptok(const unsigned pagesize, const unsigned long pages)
 {
-       static unsigned long ps;
        unsigned long tmp;
 
-       /* Initialization.  */
-       if (ps == 0)
-               ps = getpagesize();
-
        /* Conversion.  */
-       if (pages > (LONG_MAX / ps)) {  /* Could overflow.  */
-               tmp = pages / 1024;     /* Smaller first, */
-               return tmp * ps;        /* then larger.  */
+       if (pages > (LONG_MAX / pagesize)) { /* Could overflow.  */
+               tmp = pages / 1024;     /* Smaller first, */
+               return tmp * pagesize;  /* then larger.  */
        }
        /* Could underflow.  */
-       tmp = pages * ps;       /* Larger first, */
-       return tmp / 1024;      /* then smaller.  */
+       tmp = pages * pagesize; /* Larger first, */
+       return tmp / 1024;      /* then smaller.  */
 }
 
 /* summarize: Report on the system use of a command.
@@ -162,15 +158,18 @@ static unsigned long ptok(unsigned long pages)
 #define TICKS_PER_SEC 100
 #endif
 
-static void summarize(const char *fmt, char **command, resource_t * resp)
+static void summarize(const char *fmt, char **command, resource_t *resp)
 {
        unsigned vv_ms;     /* Elapsed virtual (CPU) milliseconds */
        unsigned cpu_ticks; /* Same, in "CPU ticks" */
+       unsigned pagesize = getpagesize();
 
+       /* Impossible: we do not use WUNTRACED flag in wait()...
        if (WIFSTOPPED(resp->waitstatus))
                printf("Command stopped by signal %u\n",
                                WSTOPSIG(resp->waitstatus));
-       else if (WIFSIGNALED(resp->waitstatus))
+       else */
+       if (WIFSIGNALED(resp->waitstatus))
                printf("Command terminated by signal %u\n",
                                WTERMSIG(resp->waitstatus));
        else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus))
@@ -181,7 +180,7 @@ static void summarize(const char *fmt, char **command, resource_t * resp)
              + (resp->ru.ru_utime.tv_usec + resp->ru.ru_stime.tv_usec) / 1000;
 
 #if (1000 / TICKS_PER_SEC) * TICKS_PER_SEC == 1000
-       /* 1000 is exactly divisible by TICKS_PER_SEC */
+       /* 1000 is exactly divisible by TICKS_PER_SEC (typical) */
        cpu_ticks = vv_ms / (1000 / TICKS_PER_SEC);
 #else
        cpu_ticks = vv_ms * (unsigned long long)TICKS_PER_SEC / 1000;
@@ -221,12 +220,12 @@ static void summarize(const char *fmt, char **command, resource_t * resp)
                                break;
 #endif
                        case 'C':       /* The command that got timed.  */
-                               printargv(command, " ");
+                               printargv(command);
                                break;
                        case 'D':       /* Average unshared data size.  */
                                printf("%lu",
-                                               ptok((UL) resp->ru.ru_idrss) / cpu_ticks +
-                                               ptok((UL) resp->ru.ru_isrss) / cpu_ticks);
+                                       (ptok(pagesize, (UL) resp->ru.ru_idrss) +
+                                        ptok(pagesize, (UL) resp->ru.ru_isrss)) / cpu_ticks);
                                break;
                        case 'E': {     /* Elapsed real (wall clock) time.  */
                                unsigned seconds = resp->elapsed_ms / 1000;
@@ -250,12 +249,12 @@ static void summarize(const char *fmt, char **command, resource_t * resp)
                                break;
                        case 'K':       /* Average mem usage == data+stack+text.  */
                                printf("%lu",
-                                               ptok((UL) resp->ru.ru_idrss) / cpu_ticks +
-                                               ptok((UL) resp->ru.ru_isrss) / cpu_ticks +
-                                               ptok((UL) resp->ru.ru_ixrss) / cpu_ticks);
+                                       (ptok(pagesize, (UL) resp->ru.ru_idrss) +
+                                        ptok(pagesize, (UL) resp->ru.ru_isrss) +
+                                        ptok(pagesize, (UL) resp->ru.ru_ixrss)) / cpu_ticks);
                                break;
                        case 'M':       /* Maximum resident set size.  */
-                               printf("%lu", ptok((UL) resp->ru.ru_maxrss));
+                               printf("%lu", ptok(pagesize, (UL) resp->ru.ru_maxrss));
                                break;
                        case 'O':       /* Outputs.  */
                                printf("%lu", resp->ru.ru_oublock);
@@ -308,10 +307,10 @@ static void summarize(const char *fmt, char **command, resource_t * resp)
                                printf("%lu", resp->ru.ru_nswap);
                                break;
                        case 'X':       /* Average shared text size.  */
-                               printf("%lu", ptok((UL) resp->ru.ru_ixrss) / cpu_ticks);
+                               printf("%lu", ptok(pagesize, (UL) resp->ru.ru_ixrss) / cpu_ticks);
                                break;
                        case 'Z':       /* Page size.  */
-                               printf("%u", getpagesize());
+                               printf("%u", pagesize);
                                break;
                        case 'c':       /* Involuntary context switches.  */
                                printf("%lu", resp->ru.ru_nivcsw);
@@ -325,7 +324,7 @@ static void summarize(const char *fmt, char **command, resource_t * resp)
                                printf("%lu", resp->ru.ru_nsignals);
                                break;
                        case 'p':       /* Average stack segment.  */
-                               printf("%lu", ptok((UL) resp->ru.ru_isrss) / cpu_ticks);
+                               printf("%lu", ptok(pagesize, (UL) resp->ru.ru_isrss) / cpu_ticks);
                                break;
                        case 'r':       /* Incoming socket messages received.  */
                                printf("%lu", resp->ru.ru_msgrcv);
@@ -334,7 +333,7 @@ static void summarize(const char *fmt, char **command, resource_t * resp)
                                printf("%lu", resp->ru.ru_msgsnd);
                                break;
                        case 't':       /* Average resident set size.  */
-                               printf("%lu", ptok((UL) resp->ru.ru_idrss) / cpu_ticks);
+                               printf("%lu", ptok(pagesize, (UL) resp->ru.ru_idrss) / cpu_ticks);
                                break;
                        case 'w':       /* Voluntary context switches.  */
                                printf("%lu", resp->ru.ru_nvcsw);
@@ -373,29 +372,25 @@ static void summarize(const char *fmt, char **command, resource_t * resp)
 
 /* Run command CMD and return statistics on it.
    Put the statistics in *RESP.  */
-static void run_command(char *const *cmd, resource_t * resp)
+static void run_command(char *const *cmd, resource_t *resp)
 {
-       pid_t pid;                      /* Pid of child.  */
-       __sighandler_t interrupt_signal, quit_signal;
-
-       resp->elapsed_ms = monotonic_us() / 1000;
-       pid = vfork();          /* Run CMD as child process.  */
-       if (pid < 0)
-               bb_error_msg_and_die("cannot fork");
-       else if (pid == 0) {    /* If child.  */
-               /* Don't cast execvp arguments; that causes errors on some systems,
-                  versus merely warnings if the cast is left off.  */
-               BB_EXECVP(cmd[0], cmd);
-               bb_error_msg("cannot run %s", cmd[0]);
-               _exit(errno == ENOENT ? 127 : 126);
+       pid_t pid;
+       void (*interrupt_signal)(int);
+       void (*quit_signal)(int);
+
+       resp->elapsed_ms = monotonic_ms();
+       pid = xvfork();
+       if (pid == 0) {
+               /* Child */
+               BB_EXECVP_or_die((char**)cmd);
        }
 
        /* Have signals kill the child but not self (if possible).  */
+//TODO: just block all sigs? and reenable them in the very end in main?
        interrupt_signal = signal(SIGINT, SIG_IGN);
        quit_signal = signal(SIGQUIT, SIG_IGN);
 
-       if (resuse_end(pid, resp) == 0)
-               bb_error_msg("error waiting for child process");
+       resuse_end(pid, resp);
 
        /* Re-enable signals.  */
        signal(SIGINT, interrupt_signal);
@@ -403,41 +398,25 @@ static void run_command(char *const *cmd, resource_t * resp)
 }
 
 int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int time_main(int argc, char **argv)
+int time_main(int argc UNUSED_PARAM, char **argv)
 {
        resource_t res;
        const char *output_format = default_format;
-       char c;
-
-       goto next;
-       /* Parse any options  -- don't use getopt() here so we don't
-        * consume the args of our client application... */
-       while (argc > 0 && argv[0][0] == '-') {
-               while ((c = *++*argv)) {
-                       switch (c) {
-                       case 'v':
-                               output_format = long_format;
-                               break;
-                       case 'p':
-                               output_format = posix_format;
-                               break;
-                       default:
-                               bb_show_usage();
-                       }
-               }
- next:
-               argv++;
-               argc--;
-               if (!argc)
-                       bb_show_usage();
-       }
+       int opt;
+
+       opt_complementary = "-1"; /* at least one arg */
+       /* "+": stop on first non-option */
+       opt = getopt32(argv, "+vp");
+       argv += optind;
+       if (opt & 1)
+               output_format = long_format;
+       if (opt & 2)
+               output_format = posix_format;
 
        run_command(argv, &res);
 
        /* Cheat. printf's are shorter :) */
-       /* (but see bb_putchar() body for additional wrinkle!) */
-       stdout = stderr;
-       dup2(2, 1); /* just in case libc does something silly :( */
+       xdup2(STDERR_FILENO, STDOUT_FILENO);
        summarize(output_format, argv, &res);
 
        if (WIFSTOPPED(res.waitstatus))
@@ -446,5 +425,5 @@ int time_main(int argc, char **argv)
                return WTERMSIG(res.waitstatus);
        if (WIFEXITED(res.waitstatus))
                return WEXITSTATUS(res.waitstatus);
-       fflush_stdout_and_exit(0);
+       fflush_stdout_and_exit(EXIT_SUCCESS);
 }