home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / editor / sed / getopt.c < prev    next >
C/C++ Source or Header  |  1994-01-31  |  14KB  |  505 lines

  1. /*
  2.  * GNU getopt, hacked for MSDOS and Microsoft C 5.1 and QuickC.
  3.  *
  4.  * Barry Schwartz, Dec. 1989
  5.  *
  6.  * New compile-time defs:
  7.  *   MSDOS            Defined by Microsoft compilers
  8.  *   MSDOS_FLAGS        Allow "/" as a synonym for "-"
  9.  *                and "/-" as a synonym for "--"
  10.  *   PROTO            ANSI-style parameters
  11.  */
  12.  
  13. #define PROTO
  14. /*#define MSDOS_FLAGS*/
  15.  
  16. /*
  17.  * This version of `getopt' appears to the caller like standard Unix
  18.  * `getopt' but it behaves differently for the user, since it allows the
  19.  * user to intersperse the options with the other arguments.
  20.  *
  21.  * As `getopt' works, it permutes the elements of `argv' so that, when it is
  22.  * done, all the options precede everything else.  Thus all application
  23.  * programs are extended to handle flexible argument order.
  24.  *
  25.  * Setting the environment variable _POSIX_OPTION_ORDER disables permutation.
  26.  * Then the behavior is completely standard.
  27.  *
  28.  * GNU application programs can use a third alternative mode in which they
  29.  * can distinguish the relative order of options and other arguments.
  30.  */
  31.  
  32. #include <stdio.h>
  33. #if defined(MSDOS)
  34. #include <malloc.h>
  35. #include <string.h>
  36. extern char *getenv(char *);
  37. #endif
  38.  
  39. #if defined(sparc)
  40. #include <alloca.h>
  41. #endif
  42. #if defined(USG) || defined(MSDOS)
  43. #define bcopy(s, d, l)    memcpy((d), (s), (l))
  44. #define index        strchr
  45. #endif
  46.  
  47. /*
  48.  * For communication from `getopt' to the caller. When `getopt' finds an
  49.  * option that takes an argument, the argument value is returned here.
  50.  * Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element
  51.  * is returned here.
  52.  */
  53.  
  54. char           *optarg = 0;
  55.  
  56. /*
  57.  * Index in ARGV of the next element to be scanned. This is used for
  58.  * communication to and from the caller and for communication between
  59.  * successive calls to `getopt'.
  60.  *
  61.  * On entry to `getopt', zero means this is the first call; initialize.
  62.  *
  63.  * When `getopt' returns EOF, this is the index of the first of the
  64.  * non-option elements that the caller should itself scan.
  65.  *
  66.  * Otherwise, `optind' communicates from one call to the next how much of
  67.  * ARGV has been scanned so far.
  68.  */
  69.  
  70. int             optind = 0;
  71.  
  72. /*
  73.  * The next char to be scanned in the option-element in which the last
  74.  * option character we returned was found. This allows us to pick up the
  75.  * scan where we left off.
  76.  *
  77.  * If this is zero, or a null string, it means resume the scan by advancing
  78.  * to the next ARGV-element.
  79.  */
  80.  
  81. static char    *nextchar;
  82.  
  83. /*
  84.  * Callers store zero here to inhibit the error message for unrecognized
  85.  * options.
  86.  */
  87.  
  88. int             opterr = 1;
  89.  
  90. /*
  91.  * Describe how to deal with options that follow non-option ARGV-elements.
  92.  *
  93.  * UNSPECIFIED means the caller did not specify anything; the default is then
  94.  * REQUIRE_ORDER if the environment variable _OPTIONS_FIRST is defined,
  95.  * PERMUTE otherwise.
  96.  *
  97.  * REQUIRE_ORDER means don't recognize them as options. Stop option
  98.  * processing when the first non-option is seen. This is what Unix does.
  99.  *
  100.  * PERMUTE is the default.  We permute the contents of `argv' as we scan, so
  101.  * that eventually all the options are at the end.  This allows options to
  102.  * be given in any order, even with programs that were not written to
  103.  * expect this.
  104.  *
  105.  * RETURN_IN_ORDER is an option available to programs that were written to
  106.  * expect options and other ARGV-elements in any order and that care about
  107.  * the ordering of the two.  We describe each non-option ARGV-element as
  108.  * if it were the argument of an option with character code zero. Using
  109.  * `-' as the first character of the list of option characters requests
  110.  * this mode of operation.
  111.  *
  112.  * The special argument `--' forces an end of option-scanning regardless of
  113.  * the value of `ordering'.  In the case of RETURN_IN_ORDER, only `--' can
  114.  * cause `getopt' to return EOF with `optind' != ARGC.
  115.  */
  116.  
  117. static enum
  118. {
  119.     REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
  120. }               ordering;
  121.  
  122.  
  123. /* Handle permutation of arguments.  */
  124.  
  125. /*
  126.  * Describe the part of ARGV that contains non-options that have been
  127.  * skipped.  `first_nonopt' is the index in ARGV of the first of them;
  128.  * `last_nonopt' is the index after the last of them.
  129.  */
  130.  
  131. static int      first_nonopt;
  132. static int      last_nonopt;
  133.  
  134. /*
  135.  * Exchange two adjacent subsequences of ARGV. One subsequence is elements
  136.  * [first_nonopt,last_nonopt) which contains all the non-options that have
  137.  * been skipped so far. The other is elements [last_nonopt,optind), which
  138.  * contains all the options processed since those non-options were
  139.  * skipped.
  140.  *
  141.  * `first_nonopt' and `last_nonopt' are relocated so that they describe the
  142.  * new indices of the non-options in ARGV after they are moved.
  143.  */
  144.  
  145. #if defined(PROTO)
  146.  
  147. static void
  148. exchange(char **argv)
  149.  
  150. #else
  151.  
  152. static void
  153. exchange(argv)
  154. char          **argv;
  155.  
  156. #endif
  157. {
  158.     int             nonopts_size
  159.     = (last_nonopt - first_nonopt) * sizeof(char *);
  160.     char          **temp = (char **) alloca(nonopts_size);
  161.  
  162.     /* Interchange the two blocks of data in argv.  */
  163.  
  164.     bcopy(&argv[first_nonopt], temp, nonopts_size);
  165.     bcopy(&argv[last_nonopt], &argv[first_nonopt],
  166.       (optind - last_nonopt) * sizeof(char *));
  167.     bcopy(temp, &argv[first_nonopt + optind - last_nonopt],
  168.       nonopts_size);
  169.  
  170.     /* Update records for the slots the non-options now occupy.  */
  171.  
  172.     first_nonopt += (optind - last_nonopt);
  173.     last_nonopt = optind;
  174. }
  175.  
  176.  
  177. /*
  178.  * Scan elements of ARGV (whose length is ARGC) for option characters
  179.  * given in OPTSTRING.
  180.  *
  181.  * If an element of ARGV starts with '-', and is not exactly "-" or "--",
  182.  * then it is an option element.  The characters of this element (aside
  183.  * from the initial '-') are option characters.  If `getopt' is called
  184.  * repeatedly, it returns successively each of theoption characters from
  185.  * each of the option elements.
  186.  *
  187.  * If `getopt' finds another option character, it returns that character,
  188.  * updating `optind' and `nextchar' so that the next call to `getopt' can
  189.  * resume the scan with the following option character or ARGV-element.
  190.  *
  191.  * If there are no more option characters, `getopt' returns `EOF'. Then
  192.  * `optind' is the index in ARGV of the first ARGV-element that is not an
  193.  * option.  (The ARGV-elements have been permuted so that those that are
  194.  * not options now come last.)
  195.  *
  196.  * OPTSTRING is a string containing the legitimate option characters. A colon
  197.  * in OPTSTRING means that the previous character is an option that wants
  198.  * an argument.  The argument is taken from the rest of the current
  199.  * ARGV-element, or from the following ARGV-element, and returned in
  200.  * `optarg'.
  201.  *
  202.  * If an option character is seen that is not listed in OPTSTRING, return '?'
  203.  * after printing an error message.  If you set `opterr' to zero, the
  204.  * error message is suppressed but we still return '?'.
  205.  *
  206.  * If a char in OPTSTRING is followed by a colon, that means it wants an arg,
  207.  * so the following text in the same ARGV-element, or the text of the
  208.  * following ARGV-element, is returned in `optarg.  Two colons mean an
  209.  * option that wants an optional arg; if there is text in the current
  210.  * ARGV-element, it is returned in `optarg'.
  211.  *
  212.  * If OPTSTRING starts with `-', it requests a different method of handling
  213.  * the non-option ARGV-elements.  See the comments about RETURN_IN_ORDER,
  214.  * above.
  215.  */
  216.  
  217. #if defined(PROTO)
  218.  
  219. int
  220. getopt(int argc, char **argv, char *optstring)
  221.  
  222. #else
  223.  
  224. int
  225. getopt(argc, argv, optstring)
  226. int             argc;
  227. char          **argv;
  228. char           *optstring;
  229.  
  230. #endif
  231. {
  232.     /*
  233.      * Initialize the internal data when the first call is made. Start
  234.      * processing options with ARGV-element 1 (since ARGV-element 0 is the
  235.      * program name); the sequence of previously skipped non-option
  236.      * ARGV-elements is empty.
  237.      */
  238.  
  239.     if (optind == 0)
  240.     {
  241.     first_nonopt = last_nonopt = optind = 1;
  242.  
  243.     nextchar = 0;
  244.  
  245.     /*
  246.      * Determine how to handle the ordering of options and nonoptions.
  247.      */
  248.  
  249.     if (optstring[0] == '-')
  250.         ordering = RETURN_IN_ORDER;
  251.     else
  252.     if (getenv("_POSIX_OPTION_ORDER") != 0)
  253.         ordering = REQUIRE_ORDER;
  254.     else
  255.         ordering = PERMUTE;
  256.     }
  257.  
  258.     if (nextchar == 0 || *nextchar == 0)
  259.     {
  260.     if (ordering == PERMUTE)
  261.     {
  262.         /*
  263.          * If we have just processed some options following some
  264.          * non-options, exchange them so that the options come first.
  265.          */
  266.  
  267.         if (first_nonopt != last_nonopt && last_nonopt != optind)
  268.         exchange(argv);
  269.         else
  270.         if (last_nonopt != optind)
  271.         first_nonopt = optind;
  272.  
  273.         /*
  274.          * Now skip any additional non-options and extend the range of
  275.          * non-options previously skipped.
  276.          */
  277.  
  278.         while (optind < argc
  279. #if !defined(MSDOS_FLAGS)
  280.            && (argv[optind][0] != '-'
  281. #else
  282.            && ((argv[optind][0] != '-' && argv[optind][0] != '/')
  283. #endif
  284.                || argv[optind][1] == 0))
  285.         optind++;
  286.         last_nonopt = optind;
  287.     }
  288.  
  289.     /*
  290.      * Special ARGV-element `--' means premature end of options. Skip
  291.      * it like a null option, then exchange with previous non-options
  292.      * as if it were an option, then skip everything else like a
  293.      * non-option.
  294.      */
  295.  
  296. #if !defined(MSDOS_FLAGS)
  297.     if (optind != argc && !strcmp(argv[optind], "--"))
  298. #else
  299.     if (optind != argc &&
  300.         !(strcmp(argv[optind], "--") && strcmp(argv[optind], "/-")))
  301. #endif
  302.     {
  303.         optind++;
  304.  
  305.         if (first_nonopt != last_nonopt && last_nonopt != optind)
  306.         exchange(argv);
  307.         else
  308.         if (first_nonopt == last_nonopt)
  309.         first_nonopt = optind;
  310.         last_nonopt = argc;
  311.  
  312.         optind = argc;
  313.     }
  314.  
  315.     /*
  316.      * If we have done all the ARGV-elements, stop the scan and back
  317.      * over any non-options that we skipped and permuted.
  318.      */
  319.  
  320.     if (optind == argc)
  321.     {
  322.         /*
  323.          * Set the next-arg-index to point at the non-options that we
  324.          * previously skipped, so the caller will digest them.
  325.          */
  326.         if (first_nonopt != last_nonopt)
  327.         optind = first_nonopt;
  328.         return EOF;
  329.     }
  330.  
  331.     /*
  332.      * If we have come to a non-option and did not permute it, either
  333.      * stop the scan or describe it to the caller and pass it by.
  334.      */
  335.  
  336. #if !defined(MSDOS_FLAGS)
  337.     if (argv[optind][0] != '-' || argv[optind][1] == 0)
  338. #else
  339.     if ((argv[optind][0] != '-' && argv[optind][0] != '/')
  340.         || argv[optind][1] == 0)
  341. #endif
  342.     {
  343.         if (ordering == REQUIRE_ORDER)
  344.         return EOF;
  345.         optarg = argv[optind++];
  346.         return 0;
  347.     }
  348.  
  349.     /*
  350.      * We have found another option-ARGV-element. Start decoding its
  351.      * characters.
  352.      */
  353.  
  354.     nextchar = argv[optind] + 1;
  355.     }
  356.  
  357.     /* Look at and handle the next option-character.  */
  358.  
  359.     {
  360.     char            c = *nextchar++;
  361.     char           *temp = (char *) index(optstring, c);
  362.  
  363.     /*
  364.      * Increment `optind' when we start to process its last character.
  365.      */
  366.     if (*nextchar == 0)
  367.         optind++;
  368.  
  369.     if (temp == 0 || c == ':')
  370.     {
  371.         if (opterr != 0)
  372.         {
  373.         if (c < 040 || c >= 0177)
  374.                     fprintf(stderr, "\n%s: unrecognized option, character code 0%o\n",
  375.                 argv[0], c);
  376.         else
  377.                     fprintf(stderr, "\n%s: unrecognized option `-%c'\n",
  378.                 argv[0], c);
  379.         }
  380.         return '?';
  381.     }
  382.     if (temp[1] == ':')
  383.     {
  384.         if (temp[2] == ':')
  385.         {
  386.         /*
  387.          * This is an option that accepts an argument optionally.
  388.          */
  389.         if (*nextchar != 0)
  390.         {
  391.             optarg = nextchar;
  392.             optind++;
  393.         }
  394.         else
  395.             optarg = 0;
  396.         nextchar = 0;
  397.         }
  398.         else
  399.         {
  400.         /*
  401.          * This is an option that requires an argument.
  402.          */
  403.         if (*nextchar != 0)
  404.         {
  405.             optarg = nextchar;
  406.             /*
  407.              * If we end this ARGV-element by taking the rest as
  408.              * an arg, we must advance to the next element now.
  409.              */
  410.             optind++;
  411.         }
  412.         else
  413.         if (optind == argc)
  414.         {
  415.             if (opterr != 0)
  416.                         fprintf(stderr, "\n%s: no argument for `-%c' option\n",
  417.                 argv[0], c);
  418.             c = '?';
  419.         }
  420.         else
  421.             /*
  422.              * We already incremented `optind' once; increment it
  423.              * again when taking next ARGV-elt as argument.
  424.              */
  425.             optarg = argv[optind++];
  426.         nextchar = 0;
  427.         }
  428.     }
  429.     return c;
  430.     }
  431. }
  432.  
  433.  
  434. #if defined(TEST)
  435.  
  436. /*
  437.  * Compile with -DTEST to make an executable for use in testing the above
  438.  * definition of `getopt'.
  439.  */
  440.  
  441. int
  442. main(argc, argv)
  443. int             argc;
  444. char          **argv;
  445. {
  446.     char            c;
  447.     int             digit_optind = 0;
  448.  
  449.     while (1)
  450.     {
  451.     int             this_option_optind = optind;
  452.     if ((c = getopt(argc, argv, "abc:d:0123456789")) == EOF)
  453.         break;
  454.  
  455.     switch (c)
  456.     {
  457.     case '0':
  458.     case '1':
  459.     case '2':
  460.     case '3':
  461.     case '4':
  462.     case '5':
  463.     case '6':
  464.     case '7':
  465.     case '8':
  466.     case '9':
  467.         if (digit_optind != 0 && digit_optind != this_option_optind)
  468.         printf("digits occur in two different argv-elements.\n");
  469.         digit_optind = this_option_optind;
  470.         printf("option %c\n", c);
  471.         break;
  472.  
  473.     case 'a':
  474.         printf("option a\n");
  475.         break;
  476.  
  477.     case 'b':
  478.         printf("option b\n");
  479.         break;
  480.  
  481.     case 'c':
  482.         printf("option c with value `%s'\n", optarg);
  483.         break;
  484.  
  485.     case '?':
  486.         break;
  487.  
  488.     default:
  489.         printf("?? getopt returned character code 0%o ??\n", c);
  490.     }
  491.     }
  492.  
  493.     if (optind < argc)
  494.     {
  495.     printf("non-option ARGV-elements: ");
  496.     while (optind < argc)
  497.         printf("%s ", argv[optind++]);
  498.     printf("\n");
  499.     }
  500.  
  501.     return 0;
  502. }
  503.  
  504. #endif
  505.