help-gengetopt
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[help-gengetopt] Feature suggestion and patch


From: Philip W. L. Fong
Subject: [help-gengetopt] Feature suggestion and patch
Date: Wed, 20 Feb 2002 00:30:07 -0800 (PST)

Hello,

I am working on a project in which I want to generate --help text that
gives better description to the command line arguments (i.e. the
unamed options) than a mere "[FILES]..." on the usage line.
For example, this is what I want my --help text to look like:

  aegisvm 0.0.1

  Purpose:
    Wrapper of libaegisvm, the Aegis Virtual Machine

! Usage: aegisvm [OPTIONS]... MAIN-CLASS [ARGUMENTS]...
+  MAIN-CLASS     The class in which the main method is found
+  ARGUMENTS      Arguments passed to the main method
+  OPTIONS:
     -h  --help      Print help and exit
     -V  --version   Print version and exit
     ......

In my case, the command line arguments are not file names.  I want
at least one command line argument to be supplied, with the first being
the name of a Java class containing the main method, and the optional
trailing list of command line arguments to be passed to the main
method.  Not only is the "[FILES]..." provided by gengetopt too 
brief, it is actually an incorrect description of the nature of 
the arguments in this case.

I want to be able to specify the the above using an .ggo file like
the following:

  package "aegisvm"
  version "0.0.1"
  purpose "Wrapper of libaegisvm, the Aegis Virtual Machine"
  option ......
  option ......
  ......
+ argument "MAIN-CLASS" "The class in which the main method is found"
+ rest     "ARGUMENTS"  "Arguments passed to the main method"

Basically, the .ggo file may contain any number of "argument" clause,
listed in the order of appearance of the command line arguments,
and at most one "rest" clause, which specifies an optional list of
trailing arguments.

It would also be nice for gengetopt to generate code that checks if
the required number of arguments are supplied by the user.  In my
example, aegisvm should complain if the user does not supply any
argument.  In general, the user should supply a number of argument
at least as large as the number of "argument" clauses in the .ggo
file, and in the case when no "rest" clause is supplied, the two
numbers should match exactly.

This feature obviously interacts with the --unamed-opts option.  A
possible resolution I would suggest is the following.  If the .ggo
file contains "argument" or "rest" clauses, then gengetopt behaves as
if --unamed-opts is given.  Otherwise, if no "argument" or "rest"
clause is specified in the .ggo file, but --unamed-opts is specified,
then gengetopt pretends that the following "rest" clause is in 
the .ggo file:

   rest "FILES" "An optional list of filename arguments"


Attached is a patch that implements this feature on gengetopt 2.5.
(It also contains a fix of a very minor memory leak problem in 
gengetopt.c.)  Here is a summary of modifications I have made:

* scanner.l
  - introduced tokens "argument" and "rest"
* parser.y
  - added syntax for argument line and rest line
  - checks to make sure that rest is not redefined
* ggos.h
  - struct to hold argument definition
* gengetopt.h/c
  - gengetopt_define_rest() to record the parameters of the rest clause
  - gengetopt_add_argument() to record the parameters of the 
    argument clause
  - check_rest() to test if rest redefines an argument
  - modified gengetopt_free() to fix a memory leaking bug 
    (forgotten to free gengetopt_version and gengetopt_purpose)
  - modified gengetopt_free() to free the memory allocated 
    for arguments and rest
  - pretend that --unamed-opts is given when .ggo contains
    argument/rest clause
  - pretend that there is a default rest clause when --unamed-opts 
    is given but no argument/rest clause is found
* gm.c
  - generate usage line that include arguments and rest
  - generate description texts for each argument/rest
  - generate checking code to make sure user supplies enough number of
    arguments

Please let me know what you think of this.

Regards,
Philip Fong


=====
* Philip W. L. Fong
* BSMT - 3400 Bentinck Place, Richmond, B.C., Canada V7C 4H4
* Home: (604)277-9666 Mobile: (604)719-2333
* address@hidden
* http://www.cs.sfu.ca/~pwfong

__________________________________________________
Do You Yahoo!?
Yahoo! Sports - Coverage of the 2002 Olympic Games
http://sports.yahoo.com
diff -r -C3 -w -x*~ -x*.html -xparser.h -xparser.c -xscanner.c 
gengetopt-2.5/src/gengetopt.c gengetopt-2.5-hacked/src/gengetopt.c
*** gengetopt-2.5/src/gengetopt.c       Mon May 14 12:41:34 2001
--- gengetopt-2.5-hacked/src/gengetopt.c        Tue Feb 19 16:06:00 2002
***************
*** 53,58 ****
--- 53,62 ----
  char * gengetopt_version = NULL;
  char * gengetopt_purpose = NULL;
  int gengetopt_count_line = 1;
+ struct gengetopt_argument * gengetopt_arguments = NULL; /* list of arguments 
*/
+ int gengetopt_arguments_count = 0; /* number of required arguments */
+ char * gengetopt_rest_name = NULL; /* name of rest */
+ char * gengetopt_rest_desc = NULL; /* description text of rest */
  
  int canonize_vars (void);
  
***************
*** 63,68 ****
--- 67,73 ----
  
  static void print_copyright();
  static void print_reportbugs();
+ static int check_rest();
  
  int
  main (int argc, char **argv)
***************
*** 142,147 ****
--- 147,171 ----
        return 1;
    }
  
+   /* Default file arguments if unamed_opts is given but no argument/rest
+      clause is specified in input file */
+   if (args_info.unamed_opts_given &&
+       gengetopt_arguments != NULL &&
+       gengetopt_rest_name != NULL) {
+     if (gengetopt_define_rest ("FILES", "List of file arguments")) {
+       fprintf (stderr, "gengetopt: not enough memory\n");
+       gengetopt_free ();
+       return 1;
+     }
+   }
+ 
+   /* Check if rest clause redefines any argument */
+   if (check_rest ()) {
+     gengetopt_free ();
+     fprintf(stderr, "gengetopt: argument redefined\n");
+     return 1;
+   }
+ 
    if (canonize_vars ()) {
        gengetopt_free ();
        return 1;
***************
*** 162,168 ****
    comment[2] = NULL ;
  
    if (generate_cmdline_parser (cmdline_parser_name,
!                                args_info.unamed_opts_given,
                                 cmdline_filename,
                                 DEFAULT_HEADER_EXT,
                                 DEFAULT_C_EXT,
--- 186,196 ----
    comment[2] = NULL ;
  
    if (generate_cmdline_parser (cmdline_parser_name,
!                              /* force processing of unamed opts if 
!                                 argument/rest are specified */
!                                (args_info.unamed_opts_given ||
!                               gengetopt_arguments != NULL ||
!                               gengetopt_rest_name != NULL),
                                 cmdline_filename,
                                 DEFAULT_HEADER_EXT,
                                 DEFAULT_C_EXT,
***************
*** 264,276 ****
--- 292,380 ----
        return 0;
  }
  
+ int
+ gengetopt_define_rest (char * name, char * desc)
+ {
+   gengetopt_rest_name = strdup (name);
+   if (gengetopt_rest_name == NULL)
+     return 1;
+   gengetopt_rest_desc = strdup (desc);
+   if (gengetopt_rest_desc == NULL) {
+     free (gengetopt_rest_name);
+     gengetopt_rest_name = NULL;
+     return 1;
+   }
+   return 0;
+ }
+ 
+ int
+ gengetopt_add_argument (char * name, char * desc)
+ {
+   /* Pointer to the next field of a node's predecessor:
+      we traverse the list of arguments in such convoluted way because
+      we want to add new argument to the END of the list. This is 
+      different from gengetopt_add_option, which treats the global
+      list of options as a stack */
+   struct gengetopt_argument ** n;
+   struct gengetopt_argument *p;
+ 
+   /* check if name and desc are given */
+   if (name == NULL || desc == NULL)
+     return 3;
+ 
+   /* search for collisions */
+   for (n = &gengetopt_arguments; *n != NULL; n = &((*n)->next))
+     if (strcmp(name, (*n)->name) == 0)
+       return 2;
+     
+   /* allocate memeory for argument */
+   p = malloc (sizeof(struct gengetopt_argument));
+   if (p == NULL)
+     return 1;
+   p->name = strdup(name);
+   if (p->name == NULL) {
+     free (p);
+     return 1;
+   }
+   p->desc = strdup(desc);
+   if (p->desc == NULL) {
+     free (p->name);
+     free (p);
+     return 1;
+   }
+   
+   /* append argument node to end of arguments list */
+   p->next = NULL;
+   *n = p;
+   ++gengetopt_arguments_count;
+ 
+   return 0;
+ }
+ 
+ int
+ check_rest (void)
+ {
+   struct gengetopt_argument *p;
+   for (p = gengetopt_arguments; p != NULL; p = p->next)
+     if (strcmp(p->name, gengetopt_rest_name) == 0)
+       return 1;
+   return 0;
+ }
  
  void
  gengetopt_free (void)
  {
    struct gengetopt_option *p, *pnext;
+   struct gengetopt_argument *q, *qnext;
  
    if (gengetopt_package != NULL) free (gengetopt_package);
+   if (gengetopt_version != NULL) free (gengetopt_version);
+   if (gengetopt_purpose != NULL) free (gengetopt_purpose);
+   if (gengetopt_rest_name != NULL)
+   {
+     free (gengetopt_rest_name);
+     free (gengetopt_rest_desc);
+   }
    for (p = gengetopt_options; p != NULL; p = pnext)
    {
        pnext = p->next;
***************
*** 278,283 ****
--- 382,394 ----
        if (p->desc != NULL) free (p->desc);
        if (p->var_arg != NULL) free (p->var_arg);
        free (p);
+   }
+   for (q = gengetopt_arguments; q != NULL; q = qnext)
+   {
+     qnext = q->next;
+     free (q->name);
+     free (q->desc);
+     free (q);
    }
  }
  
diff -r -C3 -w -x*~ -x*.html -xparser.h -xparser.c -xscanner.c 
gengetopt-2.5/src/gengetopt.h gengetopt-2.5-hacked/src/gengetopt.h
*** gengetopt-2.5/src/gengetopt.h       Mon May 14 12:41:34 2001
--- gengetopt-2.5-hacked/src/gengetopt.h        Tue Feb 19 13:16:37 2002
***************
*** 26,30 ****
--- 26,32 ----
  int gengetopt_define_purpose (char * s) ;
  int gengetopt_add_option (char * long_opt, char short_opt, char * desc, 
                          int type, int flagstat, int required);
+ int gengetopt_add_argument (char * name, char * desc);
+ int gengetopt_define_rest (char * name, char *desc);
  
  #endif /* _GENGETOPT_H */
diff -r -C3 -w -x*~ -x*.html -xparser.h -xparser.c -xscanner.c 
gengetopt-2.5/src/ggos.h gengetopt-2.5-hacked/src/ggos.h
*** gengetopt-2.5/src/ggos.h    Sun Jul 23 10:51:59 2000
--- gengetopt-2.5-hacked/src/ggos.h     Tue Feb 19 13:22:39 2002
***************
*** 22,25 ****
--- 22,31 ----
    char * var_arg; /* canonized long_opt + "_arg" = argument var */
  };
  
+ struct gengetopt_argument {
+   char * name; /* name of argument */
+   char * desc; /* description of what the argument is for */
+   struct gengetopt_argument * next; /* points to next item in argument list */
+ };
+ 
  #endif /* _GENGETOPT_GGOS_H */
diff -r -C3 -w -x*~ -x*.html -xparser.h -xparser.c -xscanner.c 
gengetopt-2.5/src/gm.c gengetopt-2.5-hacked/src/gm.c
*** gengetopt-2.5/src/gm.c      Mon Dec 17 13:28:02 2001
--- gengetopt-2.5-hacked/src/gm.c       Tue Feb 19 15:53:08 2002
***************
*** 32,37 ****
--- 32,41 ----
  extern char * gengetopt_package;
  extern char * gengetopt_version;
  extern char * gengetopt_purpose;
+ extern struct gengetopt_argument * gengetopt_arguments;
+ extern int gengetopt_arguments_count;
+ extern char * gengetopt_rest_name;
+ extern char * gengetopt_rest_desc;
  extern int gengetopt_strdup_text_length;
  extern char *gengetopt_strdup_text[];
  
***************
*** 478,487 ****
      printf ("[OPTIONS]...");    
    }
  
!   if ( unamed_options )
!       printf (" [FILES]...");
  
!   printf ("\\n\", PACKAGE);\n");
    /* calculate columns */
    max_long = max_short = 0;
    foropt {
--- 482,500 ----
      printf ("[OPTIONS]...");    
    }
  
!   if ( unamed_options ) {
!     struct gengetopt_argument *p;
!     for (p = gengetopt_arguments; p != NULL; p = p->next)
!       printf (" %s", p->name);
!     if (gengetopt_rest_name != NULL)
!       printf (" [%s]...\"\n", gengetopt_rest_name);
!     for (p = gengetopt_arguments; p != NULL; p = p->next)
!       printf ("\"\\n %s\t%s\"\n", p->name, p->desc);
!     if (gengetopt_rest_name != NULL)
!       printf ("\"\\n %s\t%s\"\n", gengetopt_rest_name, gengetopt_rest_desc);
!   }
  
!   printf ("\"\\n OPTIONS:\\n\", PACKAGE);\n");
    /* calculate columns */
    max_long = max_short = 0;
    foropt {
***************
*** 827,832 ****
--- 840,869 ----
    /* now handle unamed options */
    if ( unamed_options )
      {
+       /* check if the number of unamed options is consistent with 
+        the specification of the argument/rest clauses */
+       indent();
+       if (gengetopt_rest_name != 0) {
+       /* if a rest clause is specified, then user must supply
+        at least as many arguments as specified by the argument
+        clauses */
+       printf("if (argc - optind < %d) {\n", gengetopt_arguments_count);
+       } else {
+       /* if a rest clause is not specified, then suer must supply
+          exactly the same number of arguments as the number of
+          argument clauses */
+       printf ("if (argc - optind != %d) {\n", gengetopt_arguments_count);
+       }
+       inc_indent();
+       indent();
+       printf("fprintf(stderr, \"%%s: "
+            "incorrect number of arguments\\n\", PACKAGE);\n");
+       indent();
+       printf("exit(1);\n");
+       dec_indent();
+       indent();
+       printf("}\n\n");
+ 
        indent (); 
        printf ("if (optind < argc)\n");
        inc_indent ();
diff -r -C3 -w -x*~ -x*.html -xparser.h -xparser.c -xscanner.c 
gengetopt-2.5/src/parser.y gengetopt-2.5-hacked/src/parser.y
*** gengetopt-2.5/src/parser.y  Mon May 14 12:41:34 2001
--- gengetopt-2.5-hacked/src/parser.y   Tue Feb 19 14:31:02 2002
***************
*** 32,37 ****
--- 32,38 ----
  static int gengetopt_package_given = 0;
  static int gengetopt_version_given = 0;
  static int gengetopt_purpose_given = 0;
+ static int gengetopt_rest_given = 0;
  
  extern void yyerror ( char *error ) ;
  extern int yylex () ;
***************
*** 55,60 ****
--- 56,63 ----
  %token           TOK_NO
  %token           TOK_FLAG
  %token           TOK_PURPOSE
+ %token           TOK_ARGUMENT
+ %token           TOK_REST
  %token <bool>    TOK_ONOFF
  %token <str>     TOK_STRING
  %token <str>     TOK_MLSTRING
***************
*** 113,118 ****
--- 116,124 ----
  exp: TOK_OPTION TOK_STRING TOK_CHAR exp_str TOK_ARGTYPE exp_yesno { int o = 
gengetopt_add_option ($2, $3, $4, $5, 0, $6); check_result; }
  ;
  
+ exp: TOK_ARGUMENT TOK_STRING exp_str { int o = gengetopt_add_argument ($2, 
$3); if (o) { switch (o) { case 1: yyerror ("not enough memory"); break; case 
2: yyerror ("argument redefined"); break; case 3: yyerror ("bug found!!"); 
break; } YYERROR; } }
+ 
+ exp: TOK_REST TOK_STRING exp_str { if (gengetopt_rest_given) { yyerror ("rest 
redefined"); YYERROR; } else { gengetopt_rest_given = 1; if 
(gengetopt_define_rest ($2, $3)) { yyerror ("not enough memory"); YYERROR; } } }
  
  %%
  
diff -r -C3 -w -x*~ -x*.html -xparser.h -xparser.c -xscanner.c 
gengetopt-2.5/src/scanner.l gengetopt-2.5-hacked/src/scanner.l
*** gengetopt-2.5/src/scanner.l Sun Jun 24 05:25:48 2001
--- gengetopt-2.5-hacked/src/scanner.l  Tue Feb 19 12:39:20 2002
***************
*** 31,36 ****
--- 31,38 ----
  [Pp][Aa][Cc][Kk][Aa][Gg][Ee]             return TOK_PACKAGE;
  [Vv][Ee][Rr][Ss][Ii][Oo][Nn]             return TOK_VERSION;
  [Oo][Pp][Tt][Ii][Oo][Nn]                 return TOK_OPTION;
+ [Aa][Rr][Gg][Uu][Mm][Ee][Nn][Tt]           return TOK_ARGUMENT;
+ [Rr][Ee][Ss][Tt]                           return TOK_REST;
  [Ss][Tt][Rr][Ii][Nn][Gg]      yylval.argtype = ARG_STRING; return TOK_ARGTYPE;
  [Ii][Nn][Tt]                  yylval.argtype = ARG_INT; return TOK_ARGTYPE;
  [Ss][Hh][Oo][Rr][Tt]          yylval.argtype = ARG_SHORT; return TOK_ARGTYPE;

reply via email to

[Prev in Thread] Current Thread [Next in Thread]