[procps] [PATCH 1/2] free: great modernization

  • From: Sami Kerola <kerolasa@xxxxxx>
  • To: procps@xxxxxxxxxxxxx
  • Date: Wed, 13 Apr 2011 22:31:37 +0200

Support long options, use program_invocation_short_name, print
version up on request, new giga & tera byte sizes switches, exit
when numeric arguments has garbage... and for rest see the diff.

Signed-off-by: Sami Kerola <kerolasa@xxxxxx>
---
 free.c |  462 ++++++++++++++++++++++++++++++++++++++++++++++++----------------
 1 files changed, 350 insertions(+), 112 deletions(-)

diff --git a/free.c b/free.c
index bd78f02..c02a4d6 100644
--- a/free.c
+++ b/free.c
@@ -1,122 +1,360 @@
-// free.c - free(1)
-// procps utility to display free memory information
-//
-// All new, Robert Love <rml@xxxxxxxxx>             18 Nov 2002
-// Original by Brian Edmonds and Rafal Maszkowski   14 Dec 1992
-//
-// This program is licensed under the GNU Library General Public License, v2
-//
-// Copyright 2003 Robert Love
-// Copyright 2004 Albert Cahalan
-
+/*
+ * free.c - free(1)
+ * procps utility to display free memory information
+ *
+ * All new, Robert Love <rml@xxxxxxxxx>             18 Nov 2002
+ * Original by Brian Edmonds and Rafal Maszkowski   14 Dec 1992
+ *
+ * This program is licensed under the GNU Library General Public License, v2
+ *
+ * Copyright 2003 Robert Love
+ * Copyright 2004 Albert Cahalan
+ */
 #include "proc/sysinfo.h"
 #include "proc/version.h"
-//#include <errno.h>
-#include <fcntl.h>
+#include <errno.h>
+#include <err.h>
+#include <limits.h>
+#include <ctype.h>
 #include <getopt.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 #include <unistd.h>
 
-#define S(X) ( ((unsigned long long)(X) << 10) >> shift)
-
-const char help_message[] =
-"usage: free [-b|-k|-m|-g] [-l] [-o] [-t] [-s delay] [-c count] [-V]\n"
-"  -b,-k,-m,-g show output in bytes, KB, MB, or GB\n"
-"  -l show detailed low and high memory statistics\n"
-"  -o use old format (no -/+buffers/cache line)\n"
-"  -t display total for RAM + swap\n"
-"  -s update every [delay] seconds\n"
-"  -c update [count] times\n"
-"  -V display version information and exit\n"
-;
-
-int main(int argc, char *argv[]){
-    int i;
-    int count = 0;
-    int shift = 10;
-    int pause_length = 0;
-    int show_high = 0;
-    int show_total = 0;
-    int old_fmt = 0;
-
-    /* check startup flags */
-    while( (i = getopt(argc, argv, "bkmglotc:s:V") ) != -1 )
-        switch (i) {
-        case 'b': shift = 0;  break;
-        case 'k': shift = 10; break;
-        case 'm': shift = 20; break;
-        case 'g': shift = 30; break;
-        case 'l': show_high = 1; break;
-        case 'o': old_fmt = 1; break;
-        case 't': show_total = 1; break;
-        case 's': pause_length = 1000000 * atof(optarg); break;
-        case 'c': count = strtoul(optarg, NULL, 10); break;
-       case 'V': display_version(); exit(0);
-        default:
-            fwrite(help_message,1,strlen(help_message),stderr);
-           return 1;
-    }
-
-    do {
-        meminfo();
-        printf("             total       used       free     shared    buffers 
    cached\n");
-        printf(
-            "%-7s %10Lu %10Lu %10Lu %10Lu %10Lu %10Lu\n", "Mem:",
-            S(kb_main_total),
-            S(kb_main_used),
-            S(kb_main_free),
-            S(kb_main_shared),
-            S(kb_main_buffers),
-            S(kb_main_cached)
-        );
-        // Print low vs. high information, if the user requested it.
-        // Note we check if low_total==0: if so, then this kernel does
-        // not export the low and high stats.  Note we still want to
-        // print the high info, even if it is zero.
-        if (show_high) {
-            printf(
-                "%-7s %10Lu %10Lu %10Lu\n", "Low:",
-                S(kb_low_total),
-                S(kb_low_total - kb_low_free),
-                S(kb_low_free)
-            );
-            printf(
-                "%-7s %10Lu %10Lu %10Lu\n", "High:",
-                S(kb_high_total),
-                S(kb_high_total - kb_high_free),
-                S(kb_high_free)
-            );
-        }
-        if(!old_fmt){
-            unsigned KLONG buffers_plus_cached = kb_main_buffers + 
kb_main_cached;
-            printf(
-                "-/+ buffers/cache: %10Lu %10Lu\n", 
-                S(kb_main_used - buffers_plus_cached),
-                S(kb_main_free + buffers_plus_cached)
-            );
-        }
-        printf(
-            "%-7s %10Lu %10Lu %10Lu\n", "Swap:",
-            S(kb_swap_total),
-            S(kb_swap_used),
-            S(kb_swap_free)
-        );
-        if(show_total){
-            printf(
-                "%-7s %10Lu %10Lu %10Lu\n", "Total:",
-                S(kb_main_total + kb_swap_total),
-                S(kb_main_used  + kb_swap_used),
-                S(kb_main_free  + kb_swap_free)
-            );
-        }
-        if(pause_length){
-           fputc('\n', stdout);
-           fflush(stdout);
-           if (count != 1) usleep(pause_length);
+#ifndef SIZE_MAX
+  #define SIZE_MAX             32
+#endif
+
+#define FREE_HUMANREADABLE     (1 << 1)
+#define FREE_LOHI              (1 << 2)
+#define FREE_OLDFMT            (1 << 3)
+#define FREE_TOTAL             (1 << 4)
+#define FREE_SI                        (1 << 5)
+#define FREE_REPEAT            (1 << 6)
+#define FREE_REPEATCOUNT       (1 << 7)
+
+/*
+ * For long options that have no equivalent short option, use a
+ * non-character as a pseudo short option, starting with
+ * CHAR_MAX + 1.
+ */
+static const enum {
+       SI_OPTION = CHAR_MAX + 1,
+       TERA_OPTION,
+       HELP_OPTION,
+       VERSION_OPTION
+};
+
+static const struct option longopts[] = {
+       {  "bytes",     no_argument,        NULL,  'b'             },
+       {  "kilo",      no_argument,        NULL,  'k'             },
+       {  "mega",      no_argument,        NULL,  'm'             },
+       {  "giga",      no_argument,        NULL,  'g'             },
+       {  "tera",      no_argument,        NULL,  TERA_OPTION     },
+       {  "human",     no_argument,        NULL,  'h'             },
+       {  "si",        no_argument,        NULL,  SI_OPTION       },
+       {  "lohi",      no_argument,        NULL,  'l'             },
+       {  "old",       no_argument,        NULL,  'o'             },
+       {  "total",     no_argument,        NULL,  't'             },
+       {  "seconds",   required_argument,  NULL,  's'             },
+       {  "count",     required_argument,  NULL,  'c'             },
+       {  "help",      no_argument,        NULL,  HELP_OPTION     },
+       {  "version",   no_argument,        NULL,  VERSION_OPTION  },
+       {  NULL,        0,                  NULL,  0               }
+};
+
+static int flags;
+
+struct commandline_arguments {
+       int exponent;                   /* demanded in kilos, magas... */
+       float repeat_interval;          /* delay in seconds */
+       int repeat_counter;             /* number of repeats */
+};
+struct commandline_arguments args;
+
+/* function prototypes */
+static void usage(FILE * out);
+double power(unsigned int base, unsigned int expo);
+static const char *scale_size(unsigned long size);
+
+static void __attribute__ ((__noreturn__))
+    usage(FILE * out)
+{
+       fprintf(out,
+               "\nUsage: %s [options]\n"
+               "\nOptions:\n", program_invocation_short_name);
+       fprintf(out,
+               "  -b, --bytes         show output in bytes\n"
+               "  -k, --kilo          show output in kilobytes\n"
+               "  -m, --mega          show output in megabytes\n"
+               "  -g, --giga          show output in gigabytes\n"
+               "      --tera          show output in terabytes\n"
+               "  -h, --human         show human readable output\n"
+               "      --si            use powers of 1000 not 1024\n"
+               "  -l, --lohi          show detailed low and high memory 
statistics\n"
+               "  -o, --old           use old format (no -/+buffers/cache 
line)\n"
+               "  -t, --total         show total for RAM + swap\n"
+               "  -s N, --seconds N   repeat printing every N seconds\n"
+               "  -c N, --count N     repeat printing N times\n");
+       fprintf(out,
+               "      --help          display this help text\n"
+               "      --version       display version information and exit\n");
+       fprintf(out, "\nFor more information see free(1).\n");
+
+       exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+double power(unsigned int base, unsigned int expo)
+{
+       return (expo == 0) ? 1 : base * power(base, expo - 1);
+}
+
+/* idea of this function is copied from top size scaling */
+static const char *scale_size(unsigned long size)
+{
+       static char nextup[] = { 'B', 'K', 'M', 'G', 'T', 0 };
+       static char buf[SIZE_MAX];
+       int i;
+       char *up;
+       float base;
+
+       if (flags & FREE_SI)
+               base = 1000.0;
+       else
+               base = 1024.0;
+
+       /* default output */
+       if (args.exponent == 0 && !(flags & FREE_HUMANREADABLE)) {
+               snprintf(buf, sizeof(buf), "%ld", size);
+               return buf;
        }
-    } while(pause_length && --count);
 
-    return 0;
+       if (!(flags & FREE_HUMANREADABLE)) {
+               if (args.exponent == 1) {
+                       /* in bytes, which can not be in SI */
+                       snprintf(buf, sizeof(buf), "%ld",
+                                (long int) (size * 1024));
+                       return buf;
+               }
+               if (args.exponent == 2) {
+                       if (!(flags & FREE_SI))
+                               snprintf(buf, sizeof(buf), "%ld", size);
+                       else
+                               snprintf(buf, sizeof(buf), "%ld",
+                                        (long int) (size / 0.9765625));
+                       return buf;
+               }
+               if (args.exponent > 2) {
+                       /* In desired scale. */
+                       snprintf(buf, sizeof(buf), "%ld",
+                                (long int) (size /
+                                            power(base,
+                                                  args.exponent - 2))
+                           );
+                       return buf;
+               }
+       }
+
+       /* human readable output */
+       up = nextup;
+       for (i = 1; up[0] != '0'; i++, up++) {
+               switch (i) {
+               case 1:
+                       if (4 >=
+                           snprintf(buf, sizeof(buf), "%ld%c",
+                                    (long) size * 1024, *up))
+                               return buf;
+                       break;
+               case 2:
+
+                       if (!(flags & FREE_SI)) {
+                               if (4 >=
+                                   snprintf(buf, sizeof(buf), "%ld%c",
+                                            size, *up))
+                                       return buf;
+                       } else {
+                               if (4 >=
+                                   snprintf(buf, sizeof(buf), "%ld%c",
+                                            (long) (size / 0.9765625),
+                                            *up))
+                                       return buf;
+                       }
+                       break;
+               case 3:
+               case 4:
+               case 5:
+                       if (4 >=
+                           snprintf(buf, sizeof(buf), "%.1f%c",
+                                    (float) (size / power(base, i - 2)),
+                                    *up))
+                               return buf;
+                       if (4 >=
+                           snprintf(buf, sizeof(buf), "%ld%c",
+                                    (long) (size / power(base, i - 2)),
+                                    *up))
+                               return buf;
+                       break;
+               case 6:
+                       return "too big";
+                       break;
+               }
+       }
+       return "report a bug";
+}
+
+int main(int argc, char **argv)
+{
+       int c;
+       char *endptr;
+
+       /* defaults to old format */
+       args.exponent = 0;
+       args.repeat_interval = 1000000;
+
+       while ((c =
+               getopt_long(argc, argv, "bkmghlotc:s:", longopts,
+                           NULL)) != -1)
+               switch (c) {
+               case 'b':
+                       args.exponent = 1;
+                       break;
+               case 'k':
+                       args.exponent = 2;
+                       break;
+               case 'm':
+                       args.exponent = 3;
+                       break;
+               case 'g':
+                       args.exponent = 4;
+                       break;
+               case TERA_OPTION:
+                       args.exponent = 5;
+                       break;
+               case 'h':
+                       flags |= FREE_HUMANREADABLE;
+                       break;
+               case SI_OPTION:
+                       flags |= FREE_SI;
+                       break;
+               case 'l':
+                       flags |= FREE_LOHI;
+                       break;
+               case 'o':
+                       flags |= FREE_OLDFMT;
+                       break;
+               case 't':
+                       flags |= FREE_TOTAL;
+                       break;
+               case 's':
+                       flags |= FREE_REPEAT;
+                       args.repeat_interval =
+                           (1000000 * strtof(optarg, &endptr));
+                       if (errno || optarg == endptr
+                           || (endptr && *endptr))
+                               errx(EXIT_FAILURE,
+                                   "seconds argument `%s' failed",
+                                   optarg);
+                       if (args.repeat_interval < 1)
+                               errx(EXIT_FAILURE,
+                                   "seconds argument `%s' is not positive 
number",
+                                   optarg);
+                       break;
+               case 'c':
+                       flags |= FREE_REPEAT;
+                       flags |= FREE_REPEATCOUNT;
+                       args.repeat_counter = strtoul(optarg, &endptr, 10);
+                       if (errno || optarg == endptr
+                           || (endptr && *endptr))
+                               errx(EXIT_FAILURE,
+                                   "count argument `%s' failed", optarg);
+
+                       break;
+               case HELP_OPTION:
+                       usage(stdout);
+               case VERSION_OPTION:
+                       printf("%s from procps\n",
+                              program_invocation_short_name);
+                       exit(EXIT_SUCCESS);
+               default:
+                       usage(stderr);
+               }
+
+       do {
+
+               meminfo();
+
+               printf
+                   ("             total       used       free     shared    
buffers     cached\n");
+               printf("%-7s", "Mem:");
+               printf(" %10s", scale_size(kb_main_total));
+               printf(" %10s", scale_size(kb_main_used));
+               printf(" %10s", scale_size(kb_main_free));
+               printf(" %10s", scale_size(kb_main_shared));
+               printf(" %10s", scale_size(kb_main_buffers));
+               printf(" %10s", scale_size(kb_main_cached));
+               printf("\n");
+               /*
+                * Print low vs. high information, if the user
+                * requested it. Note we check if low_total == 0:
+                * if so, then this kernel does not export the
+                * low and high stats. Note we still want to
+                * print the high info, even if it is zero.
+                */
+               if (flags & FREE_LOHI) {
+                       printf("%-7s", "Low:");
+                       printf(" %10s", scale_size(kb_low_total));
+                       printf(" %10s",
+                              scale_size(kb_low_total - kb_low_free));
+                       printf(" %10s", scale_size(kb_low_free));
+                       printf("\n");
+
+                       printf("%-7s", "High:");
+                       printf(" %10s", scale_size(kb_high_total));
+                       printf(" %10s",
+                              scale_size(kb_high_total - kb_high_free));
+                       printf(" %10s", scale_size(kb_high_free));
+                       printf("\n");
+               }
+
+               if (!(flags & FREE_OLDFMT)) {
+                       unsigned KLONG buffers_plus_cached =
+                           kb_main_buffers + kb_main_cached;
+                       printf("-/+ buffers/cache:");
+                       printf(" %10s",
+                              scale_size(kb_main_used -
+                                         buffers_plus_cached));
+                       printf(" %10s",
+                              scale_size(kb_main_free +
+                                         buffers_plus_cached));
+                       printf("\n");
+               }
+               printf("%-7s", "Swap:");
+               printf(" %10s", scale_size(kb_swap_total));
+               printf(" %10s", scale_size(kb_swap_used));
+               printf(" %10s", scale_size(kb_swap_free));
+               printf("\n");
+
+               if (flags & FREE_TOTAL) {
+                       printf("%-7s", "Total:");
+                       printf(" %10s",
+                              scale_size(kb_main_total + kb_swap_total));
+                       printf(" %10s",
+                              scale_size(kb_main_used + kb_swap_used));
+                       printf(" %10s",
+                              scale_size(kb_main_free + kb_swap_free));
+                       printf("\n");
+               }
+               fflush(stdout);
+               if (flags & FREE_REPEATCOUNT) {
+                       args.repeat_counter--;
+                       if (args.repeat_counter < 1)
+                               exit(EXIT_SUCCESS);
+               }
+               if (flags & FREE_REPEAT) {
+                       printf("\n");
+                       usleep(args.repeat_interval);
+               }
+       } while ((flags & FREE_REPEAT));
+
+       exit(EXIT_SUCCESS);
 }
-- 
1.7.4.4


Other related posts: