home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_100 / 181_01 / cforum.2_3 < prev    next >
Text File  |  1986-01-09  |  11KB  |  262 lines

  1. Reprinted from: Micro/Systems Journal, Volume 2. No. 2. March/April 1986
  2. -----------------------------------------------------------------
  3. Copy of back issue may be obtained for $4.50 (foreign $6) from:
  4. Subscriptions are $20/yr, $35/2yrs domestic (published bimonthly)
  5. Micro/Systems Journal
  6. Box 1192
  7. Mountainside NJ 07092
  8. -----------------------------------------------------------------
  9. Copyright 1986
  10. Micro/Systems Journal, Box 1192, Mountainside NJ 07092
  11. This software is released into the public domain for
  12.  non-commercial use only.
  13. -----------------------------------------------------------------
  14.  
  15. March '86 C Forum
  16.  
  17. GETOPT - A Subroutine For Parsing Command-Line Arguments
  18.  
  19.      One of the stumbling blocks of every C programmer is 
  20. figuring out how to get the command-line arguments for a C 
  21. program so that they are usable inside the program.
  22.      Most MS-DOS C compilers follow the UNIX convention that the 
  23. main program has the parameters argc and argv.  argc and argv are 
  24. a strange representation of what was on the command line that 
  25. invoked the program.
  26.      argv is an array of string pointers, each string being one 
  27. of the tokens of the command that the main was invoked with.  (A 
  28. token is a group of characters delimited by "white space".)  
  29. Since the first token is just the name of program, argv[0] is not 
  30. usually examined (and in fact, is hard to get from MS-DOS).  argc 
  31. is the number of pointers in argv.  This layout provides the 
  32. ability to pass a variable number of arguments to a program.  
  33.      However, this layout is unwieldy when one considers it in 
  34. relation to how "command-line arguments" are used in a typical 
  35. program.  
  36.      Command-line arguments are usually single-character flags 
  37. that may or may not have a following value which may or may not 
  38. be separated by white space.  Ugh.  Flags with no values may 
  39. appear consecutively in one argv string.  Some programs are 
  40. generous and allow all of these argument passing styles and more.  
  41. Other programs are more idiosyncratic and will only accept, for 
  42. example, arguments that are separated by white space.  Worst of 
  43. all are programs that insist on having the arguments in a 
  44. particular order.
  45.      Writing argument parsing routines is always a headache 
  46. because you have to think of all the ways a user could reasonably 
  47. enter the input and expect it to be accepted.  Archetypal argv 
  48. parsing code consists of a while loop to look at each argument, 
  49. enclosing a switch which actually determines what code is 
  50. executed for the different argument flags.  Finally, there may be 
  51. a loop to pick up a number of files to be processed.  (Files 
  52. usually appear after flags.)  Such archetypal code also usually 
  53. has bugs in it, since it is confusing writing code that can 
  54. handle all of these possibilities.  
  55.      There have been several efforts to clean up the user 
  56. interface.  Some of them are quite innovative (and would break 
  57. existing programs).  For example, one suggestion has been to use 
  58. + as well as - as a flag introducer.  For example, to compile 
  59. with optimization, you might then say "cc +opt" and to compile 
  60. with no optimization, "cc -opt".  
  61.      Others are not as earth-shattering but useful nonetheless.  
  62. Included in this month's column is a the source code to getopt, 
  63. an argument parser.  This code was first introduced in UNIX III 
  64. and later put in the public-domain by AT&T as an encouragement 
  65. for uniformity and consistency in the way arguments are formed 
  66. from one UNIX command to another.
  67.      getopt is available directly from AT&T via their Toolchest 
  68. system [1].  It is also available via Usenet or Arpanet by 
  69. copying the file ~ftp/pub/mod.std.unix.v3 on sally.UTEXAS.EDU.  I 
  70. have seen numerous versions of getopt, although this one is based 
  71. on the version given out at the 1985 UNIFORUM conference in 
  72. Dallas.  I have made minor modifications to it, including adding 
  73. some comments.  However, it still conforms to the AT&T standard.  
  74. Oddly, the page describing getopt in the System V Release 2 
  75. manual is licensed, so I will simply describe in my own words how 
  76. to use it.  
  77.      getopt is very simple to use and will make your programs 
  78. simpler to use; the interfaces simpler to write.  Further, it is 
  79. an example of a well-written program that is portable among any C 
  80. compiler that uses these argc/argv conventions.  
  81.      Each time getopt is called, it returns the next argument 
  82. from the command-line.  Additionally, it sets a number of 
  83. external variables (such as the argument value).  If you need to 
  84. reference these, be sure to include extern declarations for them.  
  85.      getopt takes three arguments.  The first two are the same 
  86. argc and argv arguments in main.  The third argument is a string 
  87. containing the characters that are used as argument flags.  A 
  88. character followed immediately by a colon indicates that the flag 
  89. takes a value.  (Hence, ":" can not be used as a flag character.)  
  90. For example, 
  91.  
  92. getopt(argc,argv,"i:n:ab")
  93.  
  94. indicates that the program takes flag arguments of i, n, a and b 
  95. and that the i and n flags will have values immediately after 
  96. them.  Flags are restricted to one character.  For example, 
  97.  
  98. program -i 12 -n hello -a
  99.  
  100. would be validly parsed by the above getopt call.  12 is the 
  101. argument to the i flag.  "hello" is the argument to the n flag.  
  102. a has no argument.  b is not used in this call.  Flags need not 
  103. have any space separating them, as long as the first character of 
  104. the token is a hyphen.  For example, we could rewrite the above 
  105. call as 
  106.  
  107. program -ai 12 -n hello
  108.  
  109. An excerpt of the getopt calling code is as follows:
  110.  
  111. int getopt();
  112. extern char *optarg; /* current argv string */
  113. extern int optind;   /* current argv index */
  114. extern char optchar; /* option character */
  115.  
  116. ...
  117. {
  118.     ....
  119.     while (EOF != (optchar =
  120. getopt(argc,argv,"i:n:ab"))) {
  121. switch (optchar) {
  122.     case 'i':
  123. i_flag = TRUE;
  124. i = atoi(optarg);
  125. break;
  126.     case 'n':
  127. n_flag = TRUE;
  128. n = optval;
  129. break;
  130.     case 'a':
  131. a_flag = TRUE;
  132. break;
  133.     case 'b':
  134. b_flag = TRUE;
  135. break;
  136. }
  137.  
  138.     /* rest of arguments are */
  139.     /* files, so pick them up, too */
  140.     for (;optind < argc; optind++) {
  141. if (stat(argv[optind]) ...
  142. ...
  143.     }
  144. }
  145.  
  146.      Notice that the flags that have values pick up their string 
  147. values in "optarg".  For the i flag to convert it to an integer, 
  148. all this requires is the call atoi(optarg), to convert the string 
  149. to an integer.
  150.      To complete the documentation of getopt, here are some other 
  151. things you should know about it.
  152.      When all the flags have been scanned, getopt returns EOF.  
  153. The token "--" may also be used to terminate getopt processing.  
  154. It is not necessary to supply this as a flag, although it could 
  155. be useful when you want to specify a file named, say, "-f"!  (It 
  156. is not a very smart idea to have file names that look like 
  157. flags.)  When "--" is scanned, getopt returns EOF.  (For this 
  158. reason, it is impossible to have a hyphen by itself as an 
  159. argument.)
  160.      After EOF is returned there may be more data on the command 
  161. line (such as files).  optind is the index of the next argv 
  162. string after the last token successfully read by getopt.  
  163.      "?"  is returned when an unknown flag is encountered, or a 
  164. flag that requires a value does not have one.  Thus, you should 
  165. include a case for "?"  (or "default:"  will do) that prints out 
  166. the standard calling sequence to indicate that the user has 
  167. called your program incorrectly.  It is possible to get the 
  168. actual incorrect flag the user specified by examining the 
  169. character variable optopt.  
  170.      When "?" is returned, getopt will print error messages about 
  171. unexpected arguments or missing argument values if the value of 
  172. "opterr" is 1.  If it is 0, no messages will be printed.  
  173.      This code assumes the use of strchr.  Some systems call this 
  174. index.  If you don't have either, you can easily write it 
  175. yourself.  strchr (or index) takes arguments of a string and a 
  176. character, in that order.  It returns a pointer to the first 
  177. occurrence of the character in the string.  If the character does 
  178. not appear in the string, NULL is returned.  
  179.  
  180. References:
  181. [1] "Experiences with Electronic Software Distribution", Brooks, 
  182. Catherine A., USENIX Association 1985 Summer Conference 
  183. Proceedings, Portland, Oregon, pp.  433-436.  
  184.  
  185.  
  186.  
  187. /* optarg - parse command-line arguments */
  188. /* Author: AT&T */
  189.  
  190. #define NULL0
  191. #define EOF(-1)
  192. #define ERR(s, c)if(opterr){\
  193. extern int strlen(), write();\
  194. char errbuf[2];\
  195. errbuf[0] = c; errbuf[1] = '\n';\
  196. (void) write(2, argv[0], (unsigned)strlen(argv[0]));\
  197. (void) write(2, s, (unsigned)strlen(s));\
  198. (void) write(2, errbuf, 2);}
  199.  
  200. extern int strcmp();
  201. extern char *strchr();
  202.  
  203. intopterr = 1;  /* getopt prints errors if this is on */
  204. intoptind = 1;  /* token pointer */
  205. intoptopt;     /* option character passed back to user */
  206. char*optarg;     /* flag argument (or value) */
  207.  
  208. int  /* return option character, EOF if no more or ? if problem */
  209. getopt(argc, argv, opts)
  210. intargc;
  211. char**argv;
  212. char*opts;    /* option string */
  213. {
  214. static int sp = 1;  /* character index in current token */
  215. register char *cp;  /* pointer into current token */
  216.  
  217. if(sp == 1)
  218.      /* check for more flag-like tokens */
  219.      if(optind >= argc ||
  220. argv[optind][0] != '-' || argv[optind][1] == '\0')
  221.      return(EOF);
  222.      else if(strcmp(argv[optind], "--") == 0) {
  223.      optind++;
  224.      return(EOF);
  225. }
  226. optopt = argv[optind][sp];
  227. if(optopt == ':' || (cp=strchr(opts, optopt)) == NULL) {
  228. ERR(": illegal option -- ", optopt);
  229. /* if no chars left in this token, */
  230.                 /* move to next token */
  231. if(argv[optind][++sp] == '\0') {
  232. optind++;
  233. sp = 1;
  234. }
  235. return('?');
  236. }
  237.  
  238. if(*++cp == ':') {   /* if a value is expected, get it */
  239. if(argv[optind][sp+1] != '\0')
  240.      /* flag value is rest of current token */
  241.      optarg = &argv[optind++][sp+1];
  242. else if(++optind >= argc) {
  243.      ERR(": option requires an argument -- ", optopt);
  244.      sp = 1;
  245.      return('?');
  246. } else
  247.      /* flag value is next token */
  248.      optarg = argv[optind++];
  249. sp = 1;
  250. } else {
  251. /* set up to look at next char in token, next time */
  252. if(argv[optind][++sp] == '\0') {
  253. /* no more in current token, */
  254.                         /* so setup next token */
  255. sp = 1;
  256. optind++;
  257. }
  258. optarg = NULL;
  259. }
  260. return(optopt);/* return current flag character found */
  261. }
  262.