next up previous Back to Operating Systems Home Page
Next: Automatic assignment submission program Up: 1997 term messages Previous: Additional Instructions (Assig. 1)

Hop Hop...Getopt

As requested from several students, I put together a sample code fragment
using getopt(3) to parse the command line argument of a command.

Note: it's pointless to read the following code unless you have already
gone through the manpage, and the simple examples shown there. What
follows belongs to the "fairly hairy" category.

The code refers to the processing of the command-line options for the
ar(1V) command. From its manpage (SunOS 4.1) we gather the following
synopsis:

----------------

AR(1V)                   USER COMMANDS                     AR(1V)

NAME
  ar - create library archives, and add or extract files

SYNOPSIS
  ar {-d|m|p|q|r|t|x}[[-clouv][-abi position-name]] archive [ member-file...]

---------------

Meaning:
        1) One and only one of the d,m,p,q,r,t,x options must be specified;
        2) options c,l,o,u,v do not take any arguments, and may be specified
           together; 
        3) if one or more of the c,l.o,u,v options are specified, then one or
           more of the a,b,i options may be specified, each taking a string
           argument.
        4) Other than options, the command take as arguments an "archive"
           name, and a list of "member-file" names.

 
Note that not all options take arguments. Note also that getopt(3) can be used
only to parse the command-line options, while the remaining command-line
arguments (4) must be read directly on argv[].

This synopsis might be parsed by a main like the following:


/* 
// ar.c 
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>


/* usage: print help message and abort. */
void
usage(void) {
    fprintf(stderr, 
            "Usage:\n"
            "ar {- d | m | p | q | r | t | x} [ [ clouv ] \n"
            "[ abi position-name] ] archive [ member-file...  ]\n");
    exit(1);
}
            
    
int
main(int argc, char* argv[]) {
     extern char *optarg;
     extern int optind;
     void usage(void);
     /* Variables used for option-parsing. */
     int c; /* Option character returned by getopt(3). Must be declared as an
               int because at the end of the options getopt returns -1. */

     int cmd=0; /* Used to detect whether one and only one of the compulsory
                   directives d,m,p,q,r,t,x has been passed, and which one. */

     char optformat[]="dmpqrtxclouva:b:i:"; /* Option format string to pass to
                                               getopt: note that options a,b,i
                                               are specified as needing an
                                               argument. */

     int clouv=0; /* True if one or more of the c,l,o,u,v options have been
                     passed. */ 

     int op_c=0, op_l=0, op_o=0, 
         op_u=0, op_v=0; /* True when the corresponding options have been
                            passed. */

     char* posname[3]={NULL, NULL, NULL}; /* Arguments passed to options
                                             a,b,i, in this order. NULL means
                                             option not passed. */

     char *archive=NULL, **member_files=NULL;

     int member_num=0; /* Number of member_files passed. */

     /* Parse command line */
     while (-1 != (c=getopt(argc, argv, optformat))) {
         switch (c) {
           case 'd': /* All compulsory arguments first */
           case 'm':
           case 'p':
           case 'q':
           case 'r':
           case 't':
           case 'x':
             if (cmd) usage(); /* one and only one... */
             else cmd=c;
             break;
           case 'c':
             op_c=clouv=1;
             break;
           case 'l':
             op_l=clouv=1;
             break;
           case 'o':
             op_o=clouv=1;
             break;
           case 'u':
             op_u=clouv=1;
             break;
           case 'v':
             op_v=clouv=1;
             break;
           case 'a':
             /* a,b,i are legal only after c,l,o,u, or v  are passed */
             if (!clouv) usage(); 
             /* Copy option argument by strdup(3).*/
             posname[0]=strdup(optarg); 
             break;
           case 'b':
             if (!clouv) usage(); 
             posname[1]=strdup(optarg);
             break;
           case 'i':
             if (!clouv) usage(); 
             posname[2]=strdup(optarg);
             break;
           default: /* Bad option passed */
             usage();
         }
     }

     /* Now get the archive and member_file arguments 
        After getopt finishes parsing successfully, 
        optind indexes the first non-option argument 
        But we must check whether it's actually there 
        or not, since the "archive"argument is compulsory. */

     if (optind==argc) usage(); /* oops, no archive passed: abort . */
     archive=argv[optind++];

     /* Are there any member_files? */
     if (argc>optind) {
         /* Make room for at least one of them */
         member_files=(char**)malloc(sizeof(char*));
         assert(member_files); /* Abort via assert(3) if allocation failed. */
         while (argc>optind) {
             if (member_num>0) { /* We need more room */
                 member_files=(char**)realloc(member_files, 
                                              member_num*sizeof(char*));
                 assert(member_files);
             }
             member_files[member_num++]=strdup(argv[optind++]);
         }
     }
         
     /* Command line done - now get the job done ... */

     /* THE JOB SHOULD GET DONE HERE, but that's left as an exercise ;-) */
}


\ Franco Callari