plgetopt.c (6757B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /* 7 ** File: plgetopt.c 8 ** Description: utilities to parse argc/argv 9 */ 10 11 #include "prmem.h" 12 #include "prlog.h" 13 #include "prerror.h" 14 #include "plstr.h" 15 #include "plgetopt.h" 16 17 #include <string.h> 18 19 static char static_Nul = 0; 20 21 struct PLOptionInternal { 22 const char* options; /* client options list specification */ 23 PRIntn argc; /* original number of arguments */ 24 char** argv; /* vector of pointers to arguments */ 25 PRIntn xargc; /* which one we're processing now */ 26 const char* xargv; /* where within *argv[xargc] */ 27 PRIntn minus; /* do we already have the '-'? */ 28 const PLLongOpt* longOpts; /* Caller's array */ 29 PRBool endOfOpts; /* have reached a "--" argument */ 30 PRIntn optionsLen; /* is strlen(options) */ 31 }; 32 33 /* 34 ** Create the state in which to parse the tokens. 35 ** 36 ** argc the sum of the number of options and their values 37 ** argv the options and their values 38 ** options vector of single character options w/ | w/o ': 39 */ 40 PR_IMPLEMENT(PLOptState*) 41 PL_CreateOptState(PRIntn argc, char** argv, const char* options) { 42 return PL_CreateLongOptState(argc, argv, options, NULL); 43 } /* PL_CreateOptState */ 44 45 PR_IMPLEMENT(PLOptState*) 46 PL_CreateLongOptState(PRIntn argc, char** argv, const char* options, 47 const PLLongOpt* longOpts) { 48 PLOptState* opt = NULL; 49 PLOptionInternal* internal; 50 51 if (NULL == options) { 52 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 53 return opt; 54 } 55 56 opt = PR_NEWZAP(PLOptState); 57 if (NULL == opt) { 58 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 59 return opt; 60 } 61 62 internal = PR_NEW(PLOptionInternal); 63 if (NULL == internal) { 64 PR_DELETE(opt); 65 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 66 return NULL; 67 } 68 69 opt->option = 0; 70 opt->value = NULL; 71 opt->internal = internal; 72 opt->longOption = 0; 73 opt->longOptIndex = -1; 74 75 internal->argc = argc; 76 internal->argv = argv; 77 internal->xargc = 0; 78 internal->xargv = &static_Nul; 79 internal->minus = 0; 80 internal->options = options; 81 internal->longOpts = longOpts; 82 internal->endOfOpts = PR_FALSE; 83 internal->optionsLen = PL_strlen(options); 84 85 return opt; 86 } /* PL_CreateLongOptState */ 87 88 /* 89 ** Destroy object created by CreateOptState() 90 */ 91 PR_IMPLEMENT(void) PL_DestroyOptState(PLOptState* opt) { 92 PR_DELETE(opt->internal); 93 PR_DELETE(opt); 94 } /* PL_DestroyOptState */ 95 96 PR_IMPLEMENT(PLOptStatus) PL_GetNextOpt(PLOptState* opt) { 97 PLOptionInternal* internal = opt->internal; 98 99 opt->longOption = 0; 100 opt->longOptIndex = -1; 101 /* 102 ** If the current xarg points to nul, advance to the next 103 ** element of the argv vector. If the vector index is equal 104 ** to argc, we're out of arguments, so return an EOL. 105 ** Note whether the first character of the new argument is 106 ** a '-' and skip by it if it is. 107 */ 108 while (0 == *internal->xargv) { 109 internal->xargc += 1; 110 if (internal->xargc >= internal->argc) { 111 opt->option = 0; 112 opt->value = NULL; 113 return PL_OPT_EOL; 114 } 115 internal->xargv = internal->argv[internal->xargc]; 116 internal->minus = 0; 117 if (!internal->endOfOpts && ('-' == *internal->xargv)) { 118 internal->minus++; 119 internal->xargv++; /* and consume */ 120 if ('-' == *internal->xargv && internal->longOpts) { 121 internal->minus++; 122 internal->xargv++; 123 if (0 == *internal->xargv) { 124 internal->endOfOpts = PR_TRUE; 125 } 126 } 127 } 128 } 129 130 /* 131 ** If we already have a '-' or '--' in hand, xargv points to the next 132 ** option. See if we can find a match in the list of possible 133 ** options supplied. 134 */ 135 if (internal->minus == 2) { 136 char* foundEqual = strchr(internal->xargv, '='); 137 PRIntn optNameLen = 138 foundEqual ? (foundEqual - internal->xargv) : strlen(internal->xargv); 139 const PLLongOpt* longOpt = internal->longOpts; 140 PLOptStatus result = PL_OPT_BAD; 141 142 opt->option = 0; 143 opt->value = NULL; 144 145 for (; longOpt->longOptName; ++longOpt) { 146 if (strncmp(longOpt->longOptName, internal->xargv, optNameLen)) { 147 continue; /* not a possible match */ 148 } 149 if (strlen(longOpt->longOptName) != optNameLen) { 150 continue; /* not a match */ 151 } 152 /* option name match */ 153 opt->longOptIndex = longOpt - internal->longOpts; 154 opt->longOption = longOpt->longOption; 155 /* value is part of the current argv[] element if = was found */ 156 /* note: this sets value even for long options that do not 157 * require option if specified as --long=value */ 158 if (foundEqual) { 159 opt->value = foundEqual + 1; 160 } else if (longOpt->valueRequired) { 161 /* value is the next argv[] element, if any */ 162 if (internal->xargc + 1 < internal->argc) { 163 opt->value = internal->argv[++(internal->xargc)]; 164 } 165 /* missing value */ 166 else { 167 break; /* return PL_OPT_BAD */ 168 } 169 } 170 result = PL_OPT_OK; 171 break; 172 } 173 internal->xargv = &static_Nul; /* consume this */ 174 return result; 175 } 176 if (internal->minus) { 177 PRIntn cop; 178 PRIntn eoo = internal->optionsLen; 179 for (cop = 0; cop < eoo; ++cop) { 180 if (internal->options[cop] == *internal->xargv) { 181 opt->option = *internal->xargv++; 182 opt->longOption = opt->option & 0xff; 183 /* 184 ** if options indicates that there's an associated 185 ** value, it must be provided, either as part of this 186 ** argv[] element or as the next one 187 */ 188 if (':' == internal->options[cop + 1]) { 189 /* value is part of the current argv[] element */ 190 if (0 != *internal->xargv) { 191 opt->value = internal->xargv; 192 } 193 /* value is the next argv[] element, if any */ 194 else if (internal->xargc + 1 < internal->argc) { 195 opt->value = internal->argv[++(internal->xargc)]; 196 } 197 /* missing value */ 198 else { 199 return PL_OPT_BAD; 200 } 201 202 internal->xargv = &static_Nul; 203 internal->minus = 0; 204 } else { 205 opt->value = NULL; 206 } 207 return PL_OPT_OK; 208 } 209 } 210 internal->xargv += 1; /* consume that option */ 211 return PL_OPT_BAD; 212 } 213 214 /* 215 ** No '-', so it must be a standalone value. The option is nul. 216 */ 217 opt->value = internal->argv[internal->xargc]; 218 internal->xargv = &static_Nul; 219 opt->option = 0; 220 return PL_OPT_OK; 221 } /* PL_GetNextOpt */ 222 223 /* plgetopt.c */