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