1/* bashgetopt.c -- `getopt' for use by the builtins. */ 2 3/* Copyright (C) 1992-2002 Free Software Foundation, Inc. 4 5 This file is part of GNU Bash, the Bourne Again SHell. 6 7 Bash is free software: you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation, either version 3 of the License, or 10 (at your option) any later version. 11 12 Bash is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Bash. If not, see <http://www.gnu.org/licenses/>. 19*/ 20 21#include <config.h> 22 23#if defined (HAVE_UNISTD_H) 24# include <unistd.h> 25#endif 26 27#include "../bashansi.h" 28#include <chartypes.h> 29#include <errno.h> 30 31#include "../shell.h" 32#include "common.h" 33 34#define ISOPT(s) (((*(s) == '-') || (plus && *(s) == '+')) && (s)[1]) 35#define NOTOPT(s) (((*(s) != '-') && (!plus || *(s) != '+')) || (s)[1] == '\0') 36 37static int sp; 38 39char *list_optarg; 40int list_optopt; 41int list_opttype; 42 43static WORD_LIST *lhead = (WORD_LIST *)NULL; 44WORD_LIST *lcurrent = (WORD_LIST *)NULL; 45WORD_LIST *loptend; /* Points to the first non-option argument in the list */ 46 47int 48internal_getopt(list, opts) 49WORD_LIST *list; 50char *opts; 51{ 52 register int c; 53 register char *cp; 54 int plus; /* nonzero means to handle +option */ 55 static char errstr[3] = { '-', '\0', '\0' }; 56 57 plus = *opts == '+'; 58 if (plus) 59 opts++; 60 61 if (list == 0) { 62 list_optarg = (char *)NULL; 63 loptend = (WORD_LIST *)NULL; /* No non-option arguments */ 64 return -1; 65 } 66 67 if (list != lhead || lhead == 0) { 68 /* Hmmm.... called with a different word list. Reset. */ 69 sp = 1; 70 lcurrent = lhead = list; 71 loptend = (WORD_LIST *)NULL; 72 } 73 74 if (sp == 1) { 75 if (lcurrent == 0 || NOTOPT(lcurrent->word->word)) { 76 lhead = (WORD_LIST *)NULL; 77 loptend = lcurrent; 78 return(-1); 79 } else if (lcurrent->word->word[0] == '-' && 80 lcurrent->word->word[1] == '-' && 81 lcurrent->word->word[2] == 0) { 82 lhead = (WORD_LIST *)NULL; 83 loptend = lcurrent->next; 84 return(-1); 85 } 86 errstr[0] = list_opttype = lcurrent->word->word[0]; 87 } 88 89 list_optopt = c = lcurrent->word->word[sp]; 90 91 if (c == ':' || (cp = strchr(opts, c)) == NULL) { 92 errstr[1] = c; 93 sh_invalidopt (errstr); 94 if (lcurrent->word->word[++sp] == '\0') { 95 lcurrent = lcurrent->next; 96 sp = 1; 97 } 98 list_optarg = NULL; 99 if (lcurrent) 100 loptend = lcurrent->next; 101 return('?'); 102 } 103 104 if (*++cp == ':' || *cp == ';') { 105 /* `:': Option requires an argument. */ 106 /* `;': option argument may be missing */ 107 /* We allow -l2 as equivalent to -l 2 */ 108 if (lcurrent->word->word[sp+1]) { 109 list_optarg = lcurrent->word->word + sp + 1; 110 lcurrent = lcurrent->next; 111 /* If the specifier is `;', don't set optarg if the next 112 argument looks like another option. */ 113#if 0 114 } else if (lcurrent->next && (*cp == ':' || lcurrent->next->word->word[0] != '-')) { 115#else 116 } else if (lcurrent->next && (*cp == ':' || NOTOPT(lcurrent->next->word->word))) { 117#endif 118 lcurrent = lcurrent->next; 119 list_optarg = lcurrent->word->word; 120 lcurrent = lcurrent->next; 121 } else if (*cp == ';') { 122 list_optarg = (char *)NULL; 123 lcurrent = lcurrent->next; 124 } else { /* lcurrent->next == NULL */ 125 errstr[1] = c; 126 sh_needarg (errstr); 127 sp = 1; 128 list_optarg = (char *)NULL; 129 return('?'); 130 } 131 sp = 1; 132 } else if (*cp == '#') { 133 /* option requires a numeric argument */ 134 if (lcurrent->word->word[sp+1]) { 135 if (DIGIT(lcurrent->word->word[sp+1])) { 136 list_optarg = lcurrent->word->word + sp + 1; 137 lcurrent = lcurrent->next; 138 } else 139 list_optarg = (char *)NULL; 140 } else { 141 if (lcurrent->next && legal_number(lcurrent->next->word->word, (intmax_t *)0)) { 142 lcurrent = lcurrent->next; 143 list_optarg = lcurrent->word->word; 144 lcurrent = lcurrent->next; 145 } else { 146 errstr[1] = c; 147 sh_neednumarg (errstr); 148 sp = 1; 149 list_optarg = (char *)NULL; 150 return ('?'); 151 } 152 } 153 154 } else { 155 /* No argument, just return the option. */ 156 if (lcurrent->word->word[++sp] == '\0') { 157 sp = 1; 158 lcurrent = lcurrent->next; 159 } 160 list_optarg = (char *)NULL; 161 } 162 163 return(c); 164} 165 166/* 167 * reset_internal_getopt -- force the in[ft]ernal getopt to reset 168 */ 169 170void 171reset_internal_getopt () 172{ 173 lhead = lcurrent = loptend = (WORD_LIST *)NULL; 174 sp = 1; 175} 176