top: utilize getopt and introduce long cmdline options
For quite some time now, top has stood out like a sore thumb regarding the approach to cmdline options & help text. Only short options were used and that same help text was displayed for '-h' (help) plus 'v' (version). [ also, top 'rolled his own' when it came to parsing ] [ options while avoiding that getopt implementation. ] Well, with this commit all of that has changed and top now has added a long form of his options. Additionally he employs getopt_long() for the bulk of that parsing. [ however, top will still avoid separate fputs calls ] [ characteristic of other procps-ng programs when it ] [ comes to help. rather all such text is one string. ] Along the way, the following major getopt deficiencies were addressed, assuming the absence of a new #define: * an equals sign ('=') is allowed on both option forms * whitespace is allowed before & after the equals sign * optional arguments needn't abut their related option for short form nor is an '=' required with either form Signed-off-by: Jim Warner <james.warner@comcast.net>
This commit is contained in:
parent
b8118b9fee
commit
c91b371485
200
top/top.c
200
top/top.c
@ -21,6 +21,7 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <float.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
@ -3789,101 +3790,100 @@ default_or_error:
|
||||
* and our job is to see if any of those options are to be
|
||||
* overridden -- we'll force some on and negate others in our
|
||||
* best effort to honor the loser's (oops, user's) wishes... */
|
||||
static void parse_args (char **args) {
|
||||
/* differences between us and the former top:
|
||||
-C (separate CPU states for SMP) is left to an rcfile
|
||||
-u (user monitoring) added to compliment interactive 'u'
|
||||
-p (pid monitoring) allows a comma delimited list
|
||||
-q (zero delay) eliminated as redundant, incomplete and inappropriate
|
||||
use: "nice -n-10 top -d0" to achieve what was only claimed
|
||||
. most switches act as toggles (not 'on' sw) for more user flexibility
|
||||
. no deprecated/illegal use of 'breakargv:' with goto
|
||||
. bunched args are actually handled properly and none are ignored
|
||||
. we tolerate NO whitespace and NO switches -- maybe too tolerant? */
|
||||
static const char numbs_str[] = "+,-.0123456789";
|
||||
static void parse_args (int argc, char **argv) {
|
||||
static const char sopts[] = "bcd:E:e:Hhin:Oo:p:SsU:u:Vw::1";
|
||||
static const struct option lopts[] = {
|
||||
{ "batch-mode", no_argument, NULL, 'b' },
|
||||
{ "cmdline-toggle", no_argument, NULL, 'c' },
|
||||
{ "delay", required_argument, NULL, 'd' },
|
||||
{ "scale-summary-mem", required_argument, NULL, 'E' },
|
||||
{ "scale-task-mem", required_argument, NULL, 'e' },
|
||||
{ "threads-show", no_argument, NULL, 'H' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "idle-toggle", no_argument, NULL, 'i' },
|
||||
{ "iterations", required_argument, NULL, 'n' },
|
||||
{ "list-fields", no_argument, NULL, 'O' },
|
||||
{ "sort-override", required_argument, NULL, 'o' },
|
||||
{ "pid", required_argument, NULL, 'p' },
|
||||
{ "accum-time-toggle", no_argument, NULL, 'S' },
|
||||
{ "secure-mode", no_argument, NULL, 's' },
|
||||
{ "filter-any-user", required_argument, NULL, 'U' },
|
||||
{ "filter-only-euser", required_argument, NULL, 'u' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
{ "width", optional_argument, NULL, 'w' },
|
||||
{ "single-cpu-toggle", no_argument, NULL, '1' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
float tmp_delay = FLT_MAX;
|
||||
int ch;
|
||||
|
||||
while (-1 != (ch = getopt_long(argc, argv, sopts, lopts, NULL))) {
|
||||
int i;
|
||||
|
||||
while (*args) {
|
||||
const char *cp = *(args++);
|
||||
|
||||
while (*cp) {
|
||||
char ch;
|
||||
float tmp;
|
||||
char *cp = optarg;
|
||||
|
||||
switch ((ch = *cp)) {
|
||||
case '\0':
|
||||
break;
|
||||
case '-':
|
||||
if (cp[1]) ++cp;
|
||||
else if (*args) cp = *args++;
|
||||
if (strspn(cp, "+,-."))
|
||||
error_exit(fmtmk(N_fmt(WRONG_switch_fmt)
|
||||
, cp, Myname, N_txt(USAGE_abbrev_txt)));
|
||||
continue;
|
||||
#ifndef GETOPTFIX_NO
|
||||
/* first, let's plug some awful gaps in the getopt implementation,
|
||||
especially relating to short options with (optional) arguments! */
|
||||
if (!cp && optind < argc && argv[optind][0] != '-')
|
||||
cp = argv[optind++];
|
||||
if (cp) {
|
||||
if (*cp == '=') ++cp;
|
||||
if (*cp == '\0' && optind < argc) cp = argv[optind++];
|
||||
if (!cp || *cp == '\0') error_exit(fmtmk(N_fmt(MISSING_args_fmt), ch));
|
||||
}
|
||||
#endif
|
||||
switch (ch) {
|
||||
case '1': // ensure behavior identical to run-time toggle
|
||||
if (CHKw(Curwin, View_CPUNOD)) OFFw(Curwin, View_CPUSUM);
|
||||
else TOGw(Curwin, View_CPUSUM);
|
||||
OFFw(Curwin, View_CPUNOD);
|
||||
SETw(Curwin, View_STATES);
|
||||
goto bump_cp;
|
||||
break;
|
||||
case 'b':
|
||||
Batch = 1;
|
||||
goto bump_cp;
|
||||
break;
|
||||
case 'c':
|
||||
TOGw(Curwin, Show_CMDLIN);
|
||||
goto bump_cp;
|
||||
break;
|
||||
case 'd':
|
||||
if (cp[1]) ++cp;
|
||||
else if (*args) cp = *args++;
|
||||
else error_exit(fmtmk(N_fmt(MISSING_args_fmt), ch));
|
||||
if (!mkfloat(cp, &tmp_delay, 0))
|
||||
error_exit(fmtmk(N_fmt(BAD_delayint_fmt), cp));
|
||||
if (0 > tmp_delay)
|
||||
error_exit(N_txt(DELAY_badarg_txt));
|
||||
break;
|
||||
case 'e':
|
||||
{ const char *get = "kmgtp", *got;
|
||||
if (cp[1]) cp++;
|
||||
else if (*args) cp = *args++;
|
||||
else error_exit(fmtmk(N_fmt(MISSING_args_fmt), ch));
|
||||
if (!(got = strchr(get, tolower(*cp))))
|
||||
error_exit(fmtmk(N_fmt(BAD_memscale_fmt), *cp));
|
||||
Rc.task_mscale = (int)(got - get);
|
||||
} goto bump_cp;
|
||||
continue;
|
||||
case 'E':
|
||||
{ const char *get = "kmgtpe", *got;
|
||||
if (cp[1]) cp++;
|
||||
else if (*args) cp = *args++;
|
||||
else error_exit(fmtmk(N_fmt(MISSING_args_fmt), ch));
|
||||
if (!(got = strchr(get, tolower(*cp))))
|
||||
error_exit(fmtmk(N_fmt(BAD_memscale_fmt), *cp));
|
||||
if (!(got = strchr(get, tolower(*cp))) || strlen(cp) > 1)
|
||||
error_exit(fmtmk(N_fmt(BAD_memscale_fmt), cp));
|
||||
Rc.summ_mscale = (int)(got - get);
|
||||
} goto bump_cp;
|
||||
} continue;
|
||||
case 'e':
|
||||
{ const char *get = "kmgtp", *got;
|
||||
if (!(got = strchr(get, tolower(*cp))) || strlen(cp) > 1)
|
||||
error_exit(fmtmk(N_fmt(BAD_memscale_fmt), cp));
|
||||
Rc.task_mscale = (int)(got - get);
|
||||
} continue;
|
||||
case 'H':
|
||||
Thread_mode = 1;
|
||||
goto bump_cp;
|
||||
break;
|
||||
case 'h':
|
||||
case 'v':
|
||||
puts(fmtmk(N_fmt(HELP_cmdline_fmt)
|
||||
, PACKAGE_STRING, Myname, N_txt(USAGE_abbrev_txt)));
|
||||
puts(fmtmk(N_fmt(HELP_cmdline_fmt), Myname));
|
||||
bye_bye(NULL);
|
||||
case 'i':
|
||||
TOGw(Curwin, Show_IDLEPS);
|
||||
Curwin->rc.maxtasks = 0;
|
||||
goto bump_cp;
|
||||
break;
|
||||
case 'n':
|
||||
if (cp[1]) cp++;
|
||||
else if (*args) cp = *args++;
|
||||
else error_exit(fmtmk(N_fmt(MISSING_args_fmt), ch));
|
||||
if (!mkfloat(cp, &tmp, 1) || 1.0 > tmp)
|
||||
error_exit(fmtmk(N_fmt(BAD_niterate_fmt), cp));
|
||||
Loops = (int)tmp;
|
||||
break;
|
||||
continue;
|
||||
case 'O':
|
||||
for (i = 0; i < EU_MAXPFLGS; i++)
|
||||
puts(N_col(i));
|
||||
bye_bye(NULL);
|
||||
case 'o':
|
||||
if (cp[1]) cp++;
|
||||
else if (*args) cp = *args++;
|
||||
else error_exit(fmtmk(N_fmt(MISSING_args_fmt), ch));
|
||||
if (*cp == '+') { SETw(Curwin, Qsrt_NORMAL); ++cp; }
|
||||
else if (*cp == '-') { OFFw(Curwin, Qsrt_NORMAL); ++cp; }
|
||||
for (i = 0; i < EU_MAXPFLGS; i++)
|
||||
@ -3892,19 +3892,11 @@ static void parse_args (char **args) {
|
||||
error_exit(fmtmk(N_fmt(XTRA_badflds_fmt), cp));
|
||||
OFFw(Curwin, Show_FOREST);
|
||||
Curwin->rc.sortindx = i;
|
||||
cp += strlen(cp);
|
||||
break;
|
||||
case 'O':
|
||||
for (i = 0; i < EU_MAXPFLGS; i++)
|
||||
puts(N_col(i));
|
||||
bye_bye(NULL);
|
||||
continue;
|
||||
case 'p':
|
||||
{ int pid; char *p;
|
||||
if (Curwin->usrseltyp) error_exit(N_txt(SELECT_clash_txt));
|
||||
do {
|
||||
if (cp[1]) cp++;
|
||||
else if (*args) cp = *args++;
|
||||
else error_exit(fmtmk(N_fmt(MISSING_args_fmt), ch));
|
||||
if (Monpidsidx >= MONPIDMAX)
|
||||
error_exit(fmtmk(N_fmt(LIMIT_exceed_fmt), MONPIDMAX));
|
||||
if (1 != sscanf(cp, "%d", &pid)
|
||||
@ -3916,50 +3908,41 @@ static void parse_args (char **args) {
|
||||
Monpids[Monpidsidx++] = pid;
|
||||
next_pid:
|
||||
if (!(p = strchr(cp, ','))) break;
|
||||
cp = p;
|
||||
cp = p + 1;
|
||||
} while (*cp);
|
||||
} break;
|
||||
case 's':
|
||||
Secure_mode = 1;
|
||||
goto bump_cp;
|
||||
} continue;
|
||||
case 'S':
|
||||
TOGw(Curwin, Show_CTIMES);
|
||||
goto bump_cp;
|
||||
case 'u':
|
||||
break;
|
||||
case 's':
|
||||
Secure_mode = 1;
|
||||
break;
|
||||
case 'U':
|
||||
case 'u':
|
||||
{ const char *errmsg;
|
||||
if (Monpidsidx || Curwin->usrseltyp) error_exit(N_txt(SELECT_clash_txt));
|
||||
if (cp[1]) cp++;
|
||||
else if (*args) cp = *args++;
|
||||
else error_exit(fmtmk(N_fmt(MISSING_args_fmt), ch));
|
||||
if ((errmsg = user_certify(Curwin, cp, ch))) error_exit(errmsg);
|
||||
cp += strlen(cp);
|
||||
} break;
|
||||
case 'w':
|
||||
{ const char *pn = NULL;
|
||||
int ai = 0, ci = 0;
|
||||
tmp = -1;
|
||||
if (cp[1]) pn = &cp[1];
|
||||
else if (*args) { pn = *args; ai = 1; }
|
||||
if (pn && !(ci = strspn(pn, numbs_str))) { ai = 0; pn = NULL; }
|
||||
if (pn && (!mkfloat(pn, &tmp, 1) || tmp < W_MIN_COL || tmp > SCREENMAX))
|
||||
error_exit(fmtmk(N_fmt(BAD_widtharg_fmt), pn));
|
||||
Width_mode = (int)tmp;
|
||||
cp++;
|
||||
args += ai;
|
||||
if (pn) cp = pn + ci;
|
||||
} continue;
|
||||
default :
|
||||
error_exit(fmtmk(N_fmt(UNKNOWN_opts_fmt)
|
||||
, *cp, Myname, N_txt(USAGE_abbrev_txt)));
|
||||
} // end: switch (*cp)
|
||||
case 'V':
|
||||
puts(fmtmk(N_fmt(VERSION_opts_fmt), Myname, PACKAGE_STRING));
|
||||
bye_bye(NULL);
|
||||
case 'w':
|
||||
tmp = -1;
|
||||
if (cp && (!mkfloat(cp, &tmp, 1) || tmp < W_MIN_COL || tmp > SCREENMAX))
|
||||
error_exit(fmtmk(N_fmt(BAD_widtharg_fmt), cp));
|
||||
Width_mode = (int)tmp;
|
||||
continue;
|
||||
default:
|
||||
// we'll rely on getopt for any error message ...
|
||||
bye_bye(NULL);
|
||||
} // end: switch (ch)
|
||||
#ifndef GETOPTFIX_NO
|
||||
if (cp) error_exit(fmtmk(N_fmt(UNKNOWN_opts_fmt), cp));
|
||||
#endif
|
||||
} // end: while getopt_long
|
||||
|
||||
// advance cp and jump over any numerical args used above
|
||||
if (*cp) cp += strspn(&cp[1], numbs_str);
|
||||
bump_cp:
|
||||
if (*cp) ++cp;
|
||||
} // end: while (*cp)
|
||||
} // end: while (*args)
|
||||
if (optind < argc)
|
||||
error_exit(fmtmk(N_fmt(UNKNOWN_opts_fmt), argv[optind]));
|
||||
|
||||
// fixup delay time, maybe...
|
||||
if (FLT_MAX > tmp_delay) {
|
||||
@ -6320,13 +6303,12 @@ static void frame_make (void) {
|
||||
|
||||
/*
|
||||
* duh... */
|
||||
int main (int dont_care_argc, char **argv) {
|
||||
(void)dont_care_argc;
|
||||
int main (int argc, char *argv[]) {
|
||||
before(*argv);
|
||||
// +-------------+
|
||||
wins_stage_1(); // top (sic) slice
|
||||
configs_reads(); // > spread etc, <
|
||||
parse_args(&argv[1]); // > lean stuff, <
|
||||
parse_args(argc, argv); // > lean stuff, <
|
||||
whack_terminal(); // > onions etc. <
|
||||
wins_stage_2(); // as bottom slice
|
||||
// +-------------+
|
||||
|
@ -29,6 +29,7 @@
|
||||
//#define CASEUP_HEXES /* show any hex values in upper case */
|
||||
//#define CASEUP_SUFIX /* show time/mem/cnts suffix in upper case */
|
||||
//#define EQUCOLHDRYES /* yes, do equalize column header lengths */
|
||||
//#define GETOPTFIX_NO /* do not address getopt_long deficiencies */
|
||||
//#define INSP_JUSTNOT /* don't smooth unprintable right margins */
|
||||
//#define INSP_OFFDEMO /* disable demo screens, issue msg instead */
|
||||
//#define INSP_SAVEBUF /* preserve 'Insp_buf' contents in a file */
|
||||
@ -656,7 +657,7 @@ typedef struct WIN_t {
|
||||
//atic const char *configs_file (FILE *fp, const char *name, float *delay);
|
||||
//atic int configs_path (const char *const fmts, ...);
|
||||
//atic void configs_reads (void);
|
||||
//atic void parse_args (char **args);
|
||||
//atic void parse_args (int argc, char **argv);
|
||||
//atic void whack_terminal (void);
|
||||
/*------ Windows/Field Groups support ----------------------------------*/
|
||||
//atic void win_names (WIN_t *q, const char *name);
|
||||
@ -699,7 +700,7 @@ typedef struct WIN_t {
|
||||
/*------ Entry point plus two ------------------------------------------*/
|
||||
//atic void frame_hlp (int wix, int max);
|
||||
//atic void frame_make (void);
|
||||
// int main (int dont_care_argc, char **argv);
|
||||
// int main (int argc, char *argv[]);
|
||||
|
||||
#endif /* _Itop */
|
||||
|
||||
|
@ -375,25 +375,45 @@ static void build_norm_nlstab (void) {
|
||||
Norm_nlstab[WRONG_switch_fmt] = _(""
|
||||
"inappropriate '%s'\n"
|
||||
"Usage:\n %s%s");
|
||||
Norm_nlstab[HELP_cmdline_fmt] = _(""
|
||||
" %s\n"
|
||||
"Usage:\n %s%s");
|
||||
Norm_nlstab[HELP_cmdline_fmt] = _("\n"
|
||||
"Usage:\n"
|
||||
" %s [options]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -b, --batch-mode run in non-iteractive batch mode\n"
|
||||
" -c, --cmdline-toggle reverse last remembered 'c' state\n"
|
||||
" -d, --delay=SECONDS delay time between updates\n"
|
||||
" -E, --scale-summary-mem=SCALE scale memory as: k,m,g,t,p or e\n"
|
||||
" -e, --scale-task-mem=SCALE scale memory as: k,m,g,t or p\n"
|
||||
" -H, --threads-show display individual threads\n"
|
||||
" -i, --idle-toggle reverse last remembered 'i' state\n"
|
||||
" -n, --iterations=NUMBER maximum number of iterations\n"
|
||||
" -O, --list-fields output field names and exit\n"
|
||||
" -o, --sort-override=FIELD force sorting on FIELD name\n"
|
||||
" -p, --pid=PIDLIST monitor only specified process IDs\n"
|
||||
" -S, --accum-time-toggle reverse last remembered 'S' state\n"
|
||||
" -s, --secure-mode force secure mode operation\n"
|
||||
" -U, --filter-any-user=USER show only processes owned by USER\n"
|
||||
" -u, --filter-only-euser=USER show only processes owned by USER\n"
|
||||
" -w, --width [=COLUMNS] override terminal width\n"
|
||||
" -1, --single-cpu-toggle reverse last remembered '1' state\n"
|
||||
"\n"
|
||||
" -h, --help display this help and exit\n"
|
||||
" -V, --version output version information and exit\n"
|
||||
"\n"
|
||||
"For more details see top(1).");
|
||||
Norm_nlstab[BAD_delayint_fmt] = _("bad delay interval '%s'");
|
||||
Norm_nlstab[BAD_niterate_fmt] = _("bad iterations argument '%s'");
|
||||
Norm_nlstab[LIMIT_exceed_fmt] = _("pid limit (%d) exceeded");
|
||||
Norm_nlstab[BAD_mon_pids_fmt] = _("bad pid '%s'");
|
||||
Norm_nlstab[MISSING_args_fmt] = _("-%c requires argument");
|
||||
Norm_nlstab[MISSING_args_fmt] = _("-%c argument missing");
|
||||
Norm_nlstab[BAD_widtharg_fmt] = _("bad width arg '%s'");
|
||||
Norm_nlstab[UNKNOWN_opts_fmt] = _(""
|
||||
"unknown option '%c'\n"
|
||||
"Usage:\n %s%s");
|
||||
Norm_nlstab[UNKNOWN_opts_fmt] = _("unknown option '%s'");
|
||||
Norm_nlstab[DELAY_secure_txt] = _("-d disallowed in \"secure\" mode");
|
||||
Norm_nlstab[DELAY_badarg_txt] = _("-d requires positive argument");
|
||||
Norm_nlstab[ON_word_only_txt] = _("On");
|
||||
Norm_nlstab[OFF_one_word_txt] = _("Off");
|
||||
/* Translation Hint: Only the following words should be translated
|
||||
. secs (seconds), max (maximum), user, field, cols (columns)*/
|
||||
Norm_nlstab[USAGE_abbrev_txt] = _(" -hv | -bcEeHiOSs1 -d secs -n max -u|U user -p pid(s) -o field -w [cols]");
|
||||
Norm_nlstab[VERSION_opts_fmt] = _("%s from %s");
|
||||
Norm_nlstab[FOREST_modes_fmt] = _("Forest mode %s");
|
||||
Norm_nlstab[FAIL_tty_get_txt] = _("failed tty get");
|
||||
Norm_nlstab[FAIL_tty_set_fmt] = _("failed tty set: %s");
|
||||
@ -544,7 +564,7 @@ static void build_norm_nlstab (void) {
|
||||
Norm_nlstab[LIB_errormem_fmt] = _("library failed memory statistics, at %d: %s");
|
||||
Norm_nlstab[LIB_errorcpu_fmt] = _("library failed cpu statistics, at %d: %s");
|
||||
Norm_nlstab[LIB_errorpid_fmt] = _("library failed pids statistics, at %d: %s");
|
||||
Norm_nlstab[BAD_memscale_fmt] = _("bad memory scaling arg '%c'");
|
||||
Norm_nlstab[BAD_memscale_fmt] = _("bad memory scaling arg '%s'");
|
||||
Norm_nlstab[XTRA_vforest_fmt] = _("PID to collapse/expand [default pid = %d]");
|
||||
Norm_nlstab[XTRA_size2up_txt] = _("terminal is not wide enough");
|
||||
Norm_nlstab[XTRA_modebad_txt] = _("wrong mode, command inactive");
|
||||
|
@ -78,7 +78,7 @@ enum norm_nls {
|
||||
OSEL_casenot_txt, OSEL_caseyes_txt, OSEL_errdelm_fmt, OSEL_errdups_txt,
|
||||
OSEL_errvalu_fmt, OSEL_prompts_fmt, OSEL_statlin_fmt, RC_bad_entry_fmt,
|
||||
RC_bad_files_fmt, SCROLL_coord_fmt, SELECT_clash_txt, THREADS_show_fmt,
|
||||
TIME_accumed_fmt, UNKNOWN_cmds_txt, UNKNOWN_opts_fmt, USAGE_abbrev_txt,
|
||||
TIME_accumed_fmt, UNKNOWN_cmds_txt, UNKNOWN_opts_fmt, VERSION_opts_fmt,
|
||||
WORD_abv_mem_txt, WORD_abv_swp_txt, WORD_allcpus_txt, WORD_another_txt,
|
||||
WORD_eachcpu_fmt, WORD_exclude_txt, WORD_include_txt, WORD_noneone_txt,
|
||||
WORD_process_txt, WORD_threads_txt, WRITE_rcfile_fmt, WRONG_switch_fmt,
|
||||
|
Loading…
Reference in New Issue
Block a user