You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
		
		
		
		
		
			
		
			
				
					
					
						
							230 lines
						
					
					
						
							7.9 KiB
						
					
					
				
			
		
		
	
	
							230 lines
						
					
					
						
							7.9 KiB
						
					
					
				| /* Copyright (c) 2012, Kim Gräsman | |
|  * All rights reserved. | |
|  * | |
|  * Redistribution and use in source and binary forms, with or without | |
|  * modification, are permitted provided that the following conditions are met: | |
|  *  * Redistributions of source code must retain the above copyright notice, | |
|  *    this list of conditions and the following disclaimer. | |
|  *  * Redistributions in binary form must reproduce the above copyright notice, | |
|  *    this list of conditions and the following disclaimer in the documentation | |
|  *    and/or other materials provided with the distribution. | |
|  *  * Neither the name of Kim Gräsman nor the names of contributors may be used | |
|  *    to endorse or promote products derived from this software without specific | |
|  *    prior written permission. | |
|  * | |
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
|  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
|  * ARE DISCLAIMED. IN NO EVENT SHALL KIM GRÄSMAN BE LIABLE FOR ANY DIRECT, | |
|  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
|  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
|  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
|  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
|  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
|  */ | |
| 
 | |
| #include "getopt.h" | |
|  | |
| #include <stddef.h> | |
| #include <string.h> | |
|  | |
| const int no_argument = 0; | |
| const int required_argument = 1; | |
| const int optional_argument = 2; | |
| 
 | |
| char* optarg; | |
| int optopt; | |
| /* The variable optind [...] shall be initialized to 1 by the system. */ | |
| int optind = 1; | |
| int opterr; | |
| 
 | |
| static char* optcursor = NULL; | |
| 
 | |
| /* Implemented based on [1] and [2] for optional arguments. | |
|    optopt is handled FreeBSD-style, per [3]. | |
|    Other GNU and FreeBSD extensions are purely accidental. | |
|  | |
| [1] http://pubs.opengroup.org/onlinepubs/000095399/functions/getopt.html | |
| [2] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html | |
| [3] http://www.freebsd.org/cgi/man.cgi?query=getopt&sektion=3&manpath=FreeBSD+9.0-RELEASE | |
| */ | |
| int getopt(int argc, char* const argv[], const char* optstring) { | |
|   int optchar = -1; | |
|   const char* optdecl = NULL; | |
| 
 | |
|   optarg = NULL; | |
|   opterr = 0; | |
|   optopt = 0; | |
| 
 | |
|   /* Unspecified, but we need it to avoid overrunning the argv bounds. */ | |
|   if (optind >= argc) | |
|     goto no_more_optchars; | |
| 
 | |
|   /* If, when getopt() is called argv[optind] is a null pointer, getopt() | |
|      shall return -1 without changing optind. */ | |
|   if (argv[optind] == NULL) | |
|     goto no_more_optchars; | |
| 
 | |
|   /* If, when getopt() is called *argv[optind]  is not the character '-', | |
|      getopt() shall return -1 without changing optind. */ | |
|   if (*argv[optind] != '-') | |
|     goto no_more_optchars; | |
| 
 | |
|   /* If, when getopt() is called argv[optind] points to the string "-", | |
|      getopt() shall return -1 without changing optind. */ | |
|   if (strcmp(argv[optind], "-") == 0) | |
|     goto no_more_optchars; | |
| 
 | |
|   /* If, when getopt() is called argv[optind] points to the string "--", | |
|      getopt() shall return -1 after incrementing optind. */ | |
|   if (strcmp(argv[optind], "--") == 0) { | |
|     ++optind; | |
|     goto no_more_optchars; | |
|   } | |
| 
 | |
|   if (optcursor == NULL || *optcursor == '\0') | |
|     optcursor = argv[optind] + 1; | |
| 
 | |
|   optchar = *optcursor; | |
| 
 | |
|   /* FreeBSD: The variable optopt saves the last known option character | |
|      returned by getopt(). */ | |
|   optopt = optchar; | |
| 
 | |
|   /* The getopt() function shall return the next option character (if one is | |
|      found) from argv that matches a character in optstring, if there is | |
|      one that matches. */ | |
|   optdecl = strchr(optstring, optchar); | |
|   if (optdecl) { | |
|     /* [I]f a character is followed by a colon, the option takes an | |
|        argument. */ | |
|     if (optdecl[1] == ':') { | |
|       optarg = ++optcursor; | |
|       if (*optarg == '\0') { | |
|         /* GNU extension: Two colons mean an option takes an | |
|            optional arg; if there is text in the current argv-element | |
|            (i.e., in the same word as the option name itself, for example, | |
|            "-oarg"), then it is returned in optarg, otherwise optarg is set | |
|            to zero. */ | |
|         if (optdecl[2] != ':') { | |
|           /* If the option was the last character in the string pointed to by | |
|              an element of argv, then optarg shall contain the next element | |
|              of argv, and optind shall be incremented by 2. If the resulting | |
|              value of optind is greater than argc, this indicates a missing | |
|              option-argument, and getopt() shall return an error indication. | |
|  | |
|              Otherwise, optarg shall point to the string following the | |
|              option character in that element of argv, and optind shall be | |
|              incremented by 1. | |
|           */ | |
|           if (++optind < argc) { | |
|             optarg = argv[optind]; | |
|           } else { | |
|             /* If it detects a missing option-argument, it shall return the | |
|                colon character ( ':' ) if the first character of optstring | |
|                was a colon, or a question-mark character ( '?' ) otherwise. | |
|             */ | |
|             optarg = NULL; | |
|             optchar = (optstring[0] == ':') ? ':' : '?'; | |
|           } | |
|         } else { | |
|           optarg = NULL; | |
|         } | |
|       } | |
| 
 | |
|       optcursor = NULL; | |
|     } | |
|   } else { | |
|     /* If getopt() encounters an option character that is not contained in | |
|        optstring, it shall return the question-mark ( '?' ) character. */ | |
|     optchar = '?'; | |
|   } | |
| 
 | |
|   if (optcursor == NULL || *++optcursor == '\0') | |
|     ++optind; | |
| 
 | |
|   return optchar; | |
| 
 | |
| no_more_optchars: | |
|   optcursor = NULL; | |
|   return -1; | |
| } | |
| 
 | |
| /* Implementation based on [1]. | |
|  | |
| [1] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html | |
| */ | |
| int getopt_long(int argc, char* const argv[], const char* optstring, | |
|   const struct option* longopts, int* longindex) { | |
|   const struct option* o = longopts; | |
|   const struct option* match = NULL; | |
|   int num_matches = 0; | |
|   size_t argument_name_length = 0; | |
|   const char* current_argument = NULL; | |
|   int retval = -1; | |
| 
 | |
|   optarg = NULL; | |
|   optopt = 0; | |
| 
 | |
|   if (optind >= argc) | |
|     return -1; | |
| 
 | |
|   if (strlen(argv[optind]) < 3 || strncmp(argv[optind], "--", 2) != 0) | |
|     return getopt(argc, argv, optstring); | |
| 
 | |
|   /* It's an option; starts with -- and is longer than two chars. */ | |
|   current_argument = argv[optind] + 2; | |
|   argument_name_length = strcspn(current_argument, "="); | |
|   for (; o->name; ++o) { | |
|     if (strncmp(o->name, current_argument, argument_name_length) == 0) { | |
|       match = o; | |
|       ++num_matches; | |
|     } | |
|   } | |
| 
 | |
|   if (num_matches == 1) { | |
|     /* If longindex is not NULL, it points to a variable which is set to the | |
|        index of the long option relative to longopts. */ | |
|     if (longindex) | |
|       *longindex = (int) (match - longopts); | |
| 
 | |
|     /* If flag is NULL, then getopt_long() shall return val. | |
|        Otherwise, getopt_long() returns 0, and flag shall point to a variable | |
|        which shall be set to val if the option is found, but left unchanged if | |
|        the option is not found. */ | |
|     if (match->flag) | |
|       *(match->flag) = match->val; | |
| 
 | |
|     retval = match->flag ? 0 : match->val; | |
| 
 | |
|     if (match->has_arg != no_argument) { | |
|       optarg = strchr(argv[optind], '='); | |
|       if (optarg != NULL) | |
|         ++optarg; | |
| 
 | |
|       if (match->has_arg == required_argument) { | |
|         /* Only scan the next argv for required arguments. Behavior is not | |
|            specified, but has been observed with Ubuntu and Mac OSX. */ | |
|         if (optarg == NULL && ++optind < argc) { | |
|           optarg = argv[optind]; | |
|         } | |
| 
 | |
|         if (optarg == NULL) | |
|           retval = ':'; | |
|       } | |
|     } else if (strchr(argv[optind], '=')) { | |
|       /* An argument was provided to a non-argument option. | |
|          I haven't seen this specified explicitly, but both GNU and BSD-based | |
|          implementations show this behavior. | |
|       */ | |
|       retval = '?'; | |
|     } | |
|   } else { | |
|     /* Unknown option or ambiguous match. */ | |
|     retval = '?'; | |
|   } | |
| 
 | |
|   ++optind; | |
|   return retval; | |
| }
 | |
| 
 |