| From: D. Hugh Redelmeier <[EMAIL PROTECTED]>

| It would be good if more of the flags were documented.
| 
| Also: getopt makes it easy to add a long option name as a synonym for
| a one-character option.  I think that you can use this feature to help
| make options clearer.  For example, --force could be added as a
| synonym for -f at the cost of one more line initializing long_options.

While I was looking at this, I found some interesting issues with -h
and --help.

There is separate and different code to implement --help and -h and
the undocumented -H flag.  In fact, -h is implemented twice: once in
main.c:main() case 'h' and once in the default case!  --help uses
program_usage(), -h case 'h' and -H use cmd_usage (slightly
differently), and -h default uses program_usage.

One difference is that the -H and one implementation of -h must have
an argument, the other -h must not, and neither must the --help.
Very confusing.  -h and --help should optionally take an argument and
-H should not exist.

I would also claim that -h or --help should exit with status 0 but
currently they exit with status 1.

I am enclosing a patch that fixes these anomalies and does a small
amount of tidying.  The resulting code is simpler and easier to
understand, at least in my eyes.

In help.c:

- Disentangle the code for program_usage(LONG_FORM) and
  program_usage(SHORT_FORM).

  The code for these two cases was pretty different but intermingled.
  This change makes them simpler.

  It makes sense that LONG_FORM exits with status 0 and that
  SHORT_FORM exits with status 1 since the first is requested by the
  user while the second is a response to a user error.

  Note that LONG_FORM unconditionally uses less(1).  This is
  questionable.  I didn't change this.

  These two cases should probably be separate routines.  After my
  changes to main.c only one call with LONG_FORM remains.


In help.c cmd_usage():

- make sure that cmd_usage does not call gdb if gdb isn't initialized
  (in trying to give -h help for an unrecognized command)

- add a flag MUST_HELP to cmd_usage to specify that if help isn't
  found a diagnostic should be printed.  This used to be the case if
  the command calling cmd_usage was help but not if -h called it.

- this command still unconditionally uses less(1) for anything but a
  synopsis.  This should be changed.


In main.c, the long_options array:

- use the name "required_argument" in place of the magic number 1.

- make --help a synonym of 'h'

- specify that --help has an optional_argument.


In main.c main():

- in the call of getopt_long: add another ':' for -h.  This means
  that the argument is optional.  This, in turn, means that the switch
  default no longer handles -h at the end of an argument list.

- I added code to detect and report an unhandled long option.  It is
  easy to get the long_option table out of sync with the handlers.
  Mostly this involves adding "else" a lot of places in case 0.

- ditch the code to implement --help since it is now handled by the
  code for -h

- ditch the code to implement -h in the switch default since it is now
  handled in case 'h'

- the case 'h' code now has to handle the fact that the -h / --help
  argument is optional.

- removed -H flag which wasn't documented and is now redundant.

===================================================================
RCS file: RCS/defs.h,v
retrieving revision 1.1
diff -u -r1.1 defs.h
--- defs.h      2007/07/07 17:56:15     1.1
+++ defs.h      2007/07/16 21:34:50
@@ -2630,6 +2630,7 @@
 #define SYNOPSIS      (0x1)
 #define COMPLETE_HELP (0x2)
 #define PIPE_TO_LESS  (0x4)
+#define MUST_HELP     (0x8)
 
 #define LEFT_JUSTIFY   (1)
 #define RIGHT_JUSTIFY  (2)
===================================================================
RCS file: RCS/help.c,v
retrieving revision 1.1
diff -u -r1.1 help.c
--- help.c      2007/07/16 17:54:15     1.1
+++ help.c      2007/07/16 21:57:40
@@ -105,34 +105,27 @@
 void
 program_usage(int form)
 {
-       int i;
-       char **p;
-       FILE *less;
-
-       if (form == LONG_FORM)
-               less = popen("/usr/bin/less", "w");
-       else
-               less = NULL;
-
-       p = program_usage_info;
+       if (form == SHORT_FORM) {
+               fprintf(fp, program_usage_info[0], pc->program_name);
+               fprintf(fp, "\nEnter \"%s -h\" for details.\n",
+                       pc->program_name);
+               clean_exit(1);
+       } else {
+               char **p = program_usage_info;
+               FILE *less = popen("/usr/bin/less", "w");
 
-       if (form == LONG_FORM) {
                if (less)
                        fp = less;
-               for (i = 0; program_usage_info[i]; i++, p++) {
-                       fprintf(fp, *p, pc->program_name);
+               for (; *p; p++) {
+                       fprintf(fp, *p, pc->program_name);
                        fprintf(fp, "\n");
                }
-       } else {
-                       fprintf(fp, *p, pc->program_name);
-               fprintf(fp, "\nEnter \"%s -h\" for details.\n",
-                       pc->program_name);
-       }
-       fflush(fp);
-       if (less)
-               pclose(less);
+               fflush(fp);
+               if (less)
+                       pclose(less);
 
-       clean_exit(1);
+               clean_exit(0);
+       }
 }
 
 
@@ -397,7 +390,7 @@
                if (oflag) 
                        dump_offset_table(args[optind], FALSE);
                else    
-                       cmd_usage(args[optind], COMPLETE_HELP);
+                       cmd_usage(args[optind], COMPLETE_HELP|MUST_HELP);
                optind++;
         } while (args[optind]);
 }
@@ -4902,12 +4895,9 @@
 void
 cmd_usage(char *cmd, int helpflag)
 {
-       int i;
-        int found;
        char **p;
        struct command_table_entry *cp;
        char buf[BUFSIZE];
-       struct alias_data *ad;
        FILE *less;
 
        if (helpflag & PIPE_TO_LESS) {
@@ -4939,6 +4929,8 @@
         }
 
        if (STREQ(cmd, "all")) {
+               int i;
+
                display_input_info();
                 display_output_info();
                help_init();
@@ -4959,46 +4951,50 @@
                goto done_usage;
        }
 
-       found = FALSE;
-retry:
-       if ((cp = get_command_table_entry(cmd))) {
-               if ((p = cp->help_data))
-                       found = TRUE;
-       }
+       /* look up command, possibly through an alias */
+       for (;;) {
+               struct alias_data *ad;
+
+               cp = get_command_table_entry(cmd);
+               if (cp != NULL)
+                       break;  /* found command */
+
+               /* try for an alias */
+               ad = is_alias(cmd);
+               if (ad == NULL)
+                       break;  /* neither command nor alias */
 
-       /*
-       *  Check for alias names or gdb commands.
-       */
-       if (!found) {
-               if ((ad = is_alias(cmd))) {
-                       cmd = ad->args[0];
-                       goto retry;
-               }
+               cmd = ad->args[0];
+               cp = get_command_table_entry(cmd);
+       }
 
-               if (helpflag == SYNOPSIS) { 
-                       fprintf(fp,
-                         "No usage data for the \"%s\" command is 
available.\n",
+       if (cp == NULL || (p = cp->help_data) == NULL) {
+               if (helpflag & SYNOPSIS) { 
+                       fprintf(fp,
+                               "No usage data for the \"%s\" command"
+                               " is available.\n",
                                cmd);
                        RESTART();
                }
 
-               if (STREQ(pc->curcmd, "help")) {
-                       if (cp)
-                               fprintf(fp,
-                          "No help data for the \"%s\" command is 
available.\n",
+               if (helpflag & MUST_HELP) {
+                       if (cp || !(pc->flags & GDB_INIT))
+                               fprintf(fp,
+                                   "No help data for the \"%s\" command"
+                                   " is available.\n",
                                        cmd);
                        else if (!gdb_pass_through(concat_args(buf, 0, FALSE), 
                                NULL, GNU_RETURN_ON_ERROR))
                                fprintf(fp, 
-                                   "No help data for \"%s\" is available.\n",
-                                       cmd);
+                                       "No help data for \"%s\" is 
available.\n",
+                                       cmd);
                }
                goto done_usage;
         }
 
        p++;
 
-        if (helpflag == SYNOPSIS) {
+        if (helpflag & SYNOPSIS) {
                 p++;
                 fprintf(fp, "Usage: %s ", cmd);
                fprintf(fp, *p, pc->program_name, pc->program_name);
===================================================================
RCS file: RCS/main.c,v
retrieving revision 1.1
diff -u -r1.1 main.c
--- main.c      2007/07/16 17:52:28     1.1
+++ main.c      2007/07/16 21:41:01
@@ -27,12 +27,12 @@
 static void check_xen_hyper(void);
 
 static struct option long_options[] = {
-        {"memory_module", 1, 0, 0},
-        {"memory_device", 1, 0, 0},
+        {"memory_module", required_argument, 0, 0},
+        {"memory_device", required_argument, 0, 0},
         {"no_kallsyms", 0, 0, 0},
         {"no_modules", 0, 0, 0},
         {"no_namelist_gzip", 0, 0, 0},
-        {"help", 0, 0, 0},
+        {"help", optional_argument, 0, 'h'},
        {"data_debug", 0, 0, 0},
        {"no_data_debug", 0, 0, 0},
        {"no_crashrc", 0, 0, 0},
@@ -40,14 +40,14 @@
        {"kmem_cache_delay", 0, 0, 0},
        {"readnow", 0, 0, 0},
        {"smp", 0, 0, 0},
-       {"machdep", 1, 0, 0},
+       {"machdep", required_argument, 0, 0},
        {"version", 0, 0, 0},
        {"buildinfo", 0, 0, 0},
        {"shadow_page_tables", 0, 0, 0},
-        {"cpus", 1, 0, 0},
+        {"cpus", required_argument, 0, 0},
         {"no_ikconfig", 0, 0, 0},
         {"hyper", 0, 0, 0},
-       {"p2m_mfn", 1, 0, 0},
+       {"p2m_mfn", required_argument, 0, 0},
        {"zero_excluded", 0, 0, 0},
        {"no_panic", 0, 0, 0},
         {0, 0, 0, 0}
@@ -65,7 +65,7 @@
         */
        opterr = 0;
        optind = 0;
-       while((c = getopt_long(argc, argv, "LgH:h:e:i:sSvc:d:tfp:m:",
+       while((c = getopt_long(argc, argv, "Lgh::e:i:sSvc:d:tfp:m:",
                        long_options, &option_index)) != -1) {
                switch (c)
                {
@@ -74,60 +74,55 @@
                            "memory_module")) 
                                pc->memory_module = optarg;
 
-                       if (STREQ(long_options[option_index].name, 
+                       else if (STREQ(long_options[option_index].name, 
                            "memory_device")) 
                                pc->memory_device = optarg;
 
-                       if (STREQ(long_options[option_index].name, 
+                       else if (STREQ(long_options[option_index].name, 
                            "no_kallsyms")) 
                                kt->flags |= NO_KALLSYMS;
 
-                       if (STREQ(long_options[option_index].name, 
+                       else if (STREQ(long_options[option_index].name, 
                            "no_modules")) 
                                kt->flags |= NO_MODULE_ACCESS;
 
-                       if (STREQ(long_options[option_index].name, 
+                       else if (STREQ(long_options[option_index].name, 
                            "no_ikconfig")) 
                                kt->flags |= NO_IKCONFIG;
 
-                       if (STREQ(long_options[option_index].name, 
+                       else if (STREQ(long_options[option_index].name, 
                            "no_namelist_gzip")) 
                                pc->flags |= NAMELIST_NO_GZIP;
 
-                       if (STREQ(long_options[option_index].name, "help")) {
-                               program_usage(LONG_FORM);
-                               clean_exit(0);
-                       }
-
-                       if (STREQ(long_options[option_index].name, 
+                       else if (STREQ(long_options[option_index].name, 
                            "data_debug")) 
                                pc->flags |= DATADEBUG;
 
-                       if (STREQ(long_options[option_index].name, 
+                       else if (STREQ(long_options[option_index].name, 
                            "no_data_debug")) 
                                pc->flags &= ~DATADEBUG;
 
-                       if (STREQ(long_options[option_index].name, 
+                       else if (STREQ(long_options[option_index].name, 
                            "no_kmem_cache")) 
                                vt->flags |= KMEM_CACHE_UNAVAIL;
 
-                       if (STREQ(long_options[option_index].name, 
+                       else if (STREQ(long_options[option_index].name, 
                            "kmem_cache_delay")) 
                                vt->flags |= KMEM_CACHE_DELAY;
 
-                       if (STREQ(long_options[option_index].name, 
+                       else if (STREQ(long_options[option_index].name, 
                            "readnow")) 
                                pc->flags |= READNOW;
 
-                       if (STREQ(long_options[option_index].name, 
+                       else if (STREQ(long_options[option_index].name, 
                            "smp")) 
                                kt->flags |= SMP;
 
-                       if (STREQ(long_options[option_index].name, 
+                       else if (STREQ(long_options[option_index].name, 
                            "machdep")) 
                                machdep->cmdline_arg = optarg;
 
-                       if (STREQ(long_options[option_index].name, 
+                       else if (STREQ(long_options[option_index].name, 
                            "version")) { 
                                pc->flags |= VERSION_QUERY;
                                display_version();
@@ -135,30 +130,36 @@
                                clean_exit(0);
                        }
 
-                       if (STREQ(long_options[option_index].name, 
+                       else if (STREQ(long_options[option_index].name, 
                            "buildinfo")) {
                                dump_build_data();
                                clean_exit(0);
                        }
 
-                       if (STREQ(long_options[option_index].name, 
+                       else if (STREQ(long_options[option_index].name, 
                            "shadow_page_tables")) 
                                kt->xen_flags |= SHADOW_PAGE_TABLES;
 
-                       if (STREQ(long_options[option_index].name, "cpus")) 
+                       else if (STREQ(long_options[option_index].name, 
"cpus")) 
                                kt->cpus_override = optarg;
 
-                       if (STREQ(long_options[option_index].name, "hyper"))
+                       else if (STREQ(long_options[option_index].name, 
"hyper"))
                                pc->flags |= XEN_HYPER;
 
-                       if (STREQ(long_options[option_index].name, "p2m_mfn")) 
+                       else if (STREQ(long_options[option_index].name, 
"p2m_mfn")) 
                                xen_kdump_p2m_mfn(optarg);
 
-                       if (STREQ(long_options[option_index].name, 
"zero_excluded")) 
+                       else if (STREQ(long_options[option_index].name, 
"zero_excluded")) 
                                *diskdump_flags |= ZERO_EXCLUDED;
 
-                       if (STREQ(long_options[option_index].name, "no_panic")) 
+                       else if (STREQ(long_options[option_index].name, 
"no_panic")) 
                                tt->flags |= PANIC_TASK_NOT_FOUND;
+
+                       else {
+                               error(INFO, "internal error: option %s 
unhandled\n",
+                                       long_options[option_index].name);
+                               program_usage(SHORT_FORM);
+                       }
                        break;
 
                case 'f':
@@ -169,12 +170,19 @@
                        pc->flags |= KERNEL_DEBUG_QUERY;
                        break;
 
-               case 'H':
-                       cmd_usage(optarg, COMPLETE_HELP);
-                       clean_exit(0);
-
                case 'h':
-                       cmd_usage(optarg, COMPLETE_HELP|PIPE_TO_LESS);
+                       /* note: long_getopt's handling of optional arguments 
is weak.
+                        * To it, an optional argument must be part of the same 
argument
+                        * as the flag itself (eg. --help=commands or 
-hcommands).
+                        * We want to accept "--help commands" or "-h commands".
+                        * So we must do that part ourselves.
+                        */
+                       if (optarg != NULL)
+                               cmd_usage(optarg, 
COMPLETE_HELP|PIPE_TO_LESS|MUST_HELP);
+                       else if (argv[optind] != NULL && argv[optind][0] != '-')
+                               cmd_usage(argv[optind++], 
COMPLETE_HELP|PIPE_TO_LESS|MUST_HELP);
+                       else
+                               program_usage(LONG_FORM);
                        clean_exit(0);
                        
                case 'e':
@@ -238,13 +246,9 @@
                        break;
 
                default:
-                       if (STREQ(argv[optind-1], "-h"))
-                               program_usage(LONG_FORM);
-                       else {
-                               error(INFO, "invalid option: %s\n",
-                                       argv[optind-1]);
-                               program_usage(SHORT_FORM);
-                       }
+                       error(INFO, "invalid option: %s\n",
+                               argv[optind-1]);
+                       program_usage(SHORT_FORM);
                }
        }
        opterr = 1;
================ end ================

--
Crash-utility mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/crash-utility

Reply via email to