Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * OpenTyrian: A modern cross-platform port of Tyrian
  3.  * Copyright (C) 2007-2009  The OpenTyrian Development Team
  4.  *
  5.  * This program is free software; you can redistribute it and/or
  6.  * modify it under the terms of the GNU General Public License
  7.  * as published by the Free Software Foundation; either version 2
  8.  * of the License, or (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  18.  */
  19. #include "arg_parse.h"
  20.  
  21. #include "std_support.h"
  22.  
  23. #ifndef _GNU_SOURCE
  24. #define _GNU_SOURCE
  25. #endif
  26.  
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30.  
  31. static void permute( const char *argv[], int *first_nonopt, int *first_opt, int after_opt );
  32.  
  33. static int parse_short_opt( int argc, const char *const argv[], const Options *options, Option *option );
  34. static int parse_long_opt( int argc, const char *const argv[], const Options *options, Option *option );
  35.  
  36. Option parse_args( int argc, const char *argv[], const Options *options )
  37. {
  38.         static int argn = 1;
  39.         static bool no_more_options = false;
  40.        
  41.         static int first_nonopt = 1;
  42.        
  43.         Option option = { NOT_OPTION, NULL, 0 };
  44.         option.argn = first_nonopt;
  45.        
  46.         while (argn < argc)
  47.         {
  48.                 size_t arg_len = strlen(argv[argn]);
  49.                
  50.                 if (!no_more_options &&
  51.                     argv[argn][0] == '-' &&  // first char is '-'
  52.                     arg_len > 1)             // option is not "-"
  53.                 {
  54.                         option.argn = argn;
  55.                        
  56.                         if (argv[argn][1] == '-')  // string begins with "--"
  57.                         {
  58.                                 if (arg_len == 2)  // "--" alone indicates end of options
  59.                                 {
  60.                                         ++argn;
  61.                                         no_more_options = true;
  62.                                 }
  63.                                 else
  64.                                 {
  65.                                         argn = parse_long_opt(argc, argv, options, &option);
  66.                                 }
  67.                         }
  68.                         else
  69.                         {
  70.                                 argn = parse_short_opt(argc, argv, options, &option);
  71.                         }
  72.                        
  73.                         // shift option in front of non-options
  74.                         permute(argv, &first_nonopt, &option.argn, argn);
  75.                        
  76.                         // don't include "--" in non-options
  77.                         if (no_more_options)
  78.                                 ++option.argn;
  79.                         break;
  80.                 }
  81.                 else
  82.                 {
  83.                         // skip non-options, permute later when option encountered
  84.                         ++argn;
  85.                 }
  86.         }
  87.        
  88.         return option;
  89. }
  90.  
  91. static void permute( const char *argv[], int *first_nonopt, int *first_opt, int after_opt )
  92. {
  93.         const int nonopts = *first_opt - *first_nonopt;
  94.        
  95.         // slide each of the options in front of the non-options
  96.         for (int i = *first_opt; i < after_opt; ++i)
  97.         {
  98.                 for (int j = i; j > *first_nonopt; --j)
  99.                 {
  100.                         // swap argv[j] and argv[j - 1]
  101.                         const char *temp = argv[j];
  102.                         argv[j] = argv[j - 1];
  103.                         argv[j - 1] = temp;
  104.                 }
  105.                
  106.                 // position of first non-option shifts right once for each option
  107.                 ++(*first_nonopt);
  108.         }
  109.        
  110.         // position of first option is initial position of first non-option
  111.         *first_opt -= nonopts;
  112. }
  113.  
  114. static int parse_short_opt( int argc, const char *const argv[], const Options *options, Option *option )
  115. {
  116.         static size_t offset = 1;  // ignore the "-"
  117.        
  118.         int argn = option->argn;
  119.        
  120.         const char *arg = argv[argn];
  121.        
  122.         const size_t arg_len = strlen(arg);
  123.        
  124.         const bool arg_attached = (offset + 1 < arg_len),  // possible argument attached?
  125.                    last_in_argv = (argn == argc - 1);
  126.        
  127.         option->value = INVALID_OPTION;
  128.        
  129.         for (; !(options->short_opt == 0 &&
  130.                  options->long_opt == NULL); ++options)
  131.         {
  132.                 if (options->short_opt != 0 &&
  133.                     options->short_opt == arg[offset])
  134.                 {
  135.                         option->value = options->value;
  136.                        
  137.                         if (options->has_arg)
  138.                         {
  139.                                 if (arg_attached)  // arg direclty follows option
  140.                                 {
  141.                                         option->arg = arg + offset + 1;
  142.                                        
  143.                                         offset = arg_len;
  144.                                 }
  145.                                 else if (!last_in_argv)  // arg is next in argv
  146.                                 {
  147.                                         option->arg = argv[++argn];
  148.                                        
  149.                                         offset = arg_len;
  150.                                 }
  151.                                 else
  152.                                 {
  153.                                         option->value = OPTION_MISSING_ARG;
  154.                                         break;
  155.                                 }
  156.                         }
  157.                        
  158.                         break;
  159.                 }
  160.         }
  161.        
  162.         switch (option->value)
  163.         {
  164.         case INVALID_OPTION:
  165.                 fprintf(stderr, "%s: invalid option -- '%c'\n", argv[0], argv[option->argn][offset]);
  166.                 break;
  167.         case OPTION_MISSING_ARG:
  168.                 fprintf(stderr, "%s: option requires an argument -- '%c'\n", argv[0], argv[option->argn][offset]);
  169.                 break;
  170.         }
  171.        
  172.         if (++offset >= arg_len)
  173.         {
  174.                 ++argn;
  175.                 offset = 1;
  176.         }
  177.        
  178.         return argn;  // which arg in argv that parse_args() should examine when called again
  179. }
  180.  
  181. static int parse_long_opt( int argc, const char *const argv[], const Options *options, Option *option )
  182. {
  183.         int argn = option->argn;
  184.        
  185.         const char *arg = argv[argn] + 2;  // ignore the "--"
  186.        
  187.         const size_t arg_len = strlen(arg),
  188.                      arg_opt_len = ot_strchrnul(arg, '=') - arg;  // length before "="
  189.        
  190.         const bool arg_attached = (arg_opt_len < arg_len),  // argument attached using "="?
  191.                    last_in_argv = (argn == argc - 1);
  192.        
  193.         option->value = INVALID_OPTION;
  194.        
  195.         for (; !(options->short_opt == 0 &&
  196.                  options->long_opt == NULL); ++options)
  197.         {
  198.                 if (options->long_opt != NULL &&
  199.                     strncmp(options->long_opt, arg, arg_opt_len) == 0)  // matches (partially, at least)
  200.                 {
  201.                         if (option->value != INVALID_OPTION)  // other match already found
  202.                         {
  203.                                 option->value = AMBIGUOUS_OPTION;
  204.                                 break;
  205.                         }
  206.                        
  207.                         option->value = options->value;
  208.                        
  209.                         if (options->has_arg)
  210.                         {
  211.                                 if (arg_attached)  // arg is after "="
  212.                                 {
  213.                                         option->arg = arg + arg_opt_len + 1;
  214.                                 }
  215.                                 else if (!last_in_argv)  // arg is next in argv
  216.                                 {
  217.                                         option->arg = argv[++argn];
  218.                                 }
  219.                                 else  // arg is missing
  220.                                 {
  221.                                         option->value = OPTION_MISSING_ARG;
  222.                                         // can't break, gotta check for ambiguity
  223.                                 }
  224.                         }
  225.                        
  226.                         if (arg_opt_len == strlen(options->long_opt)) // exact match
  227.                                 break;
  228.                         // can't break for partial match, gotta check for ambiguity
  229.                 }
  230.         }
  231.        
  232.         switch (option->value)
  233.         {
  234.         case INVALID_OPTION:
  235.                 fprintf(stderr, "%s: unrecognized option '%s'\n", argv[0], argv[option->argn]);
  236.                 break;
  237.         case AMBIGUOUS_OPTION:
  238.                 fprintf(stderr, "%s: option '%s' is ambiguous\n", argv[0], argv[option->argn]);
  239.                 break;
  240.         case OPTION_MISSING_ARG:
  241.                 fprintf(stderr, "%s: option '%s' requires an argument\n", argv[0], argv[option->argn]);
  242.                 break;
  243.         }
  244.        
  245.         ++argn;
  246.        
  247.         return argn;  // which arg in argv that parse_args() should examine when called again
  248. }
  249.