1 #include <stdio.h> 2 3 #include "cmdopt.h" 4 5 #ifdef __cplusplus 6 extern "C" { 7 #endif // __cplusplus 8 9 // Moves `optind' to the end and shifts other arguments. 10 static void cmdopt_shift(cmdopt_t *h) { 11 int i; 12 char *tmp; 13 14 tmp = h->argv[h->optind]; 15 for (i = h->optind; i < h->argc - 1; i++) { 16 h->argv[i] = h->argv[i + 1]; 17 } 18 h->argv[i] = tmp; 19 20 h->nextchar = NULL; 21 h->optnum--; 22 } 23 24 // Moves to the next argument. 25 static void cmdopt_next(cmdopt_t *h) { 26 h->optind++; 27 h->nextchar = NULL; 28 } 29 30 // Checks if the current argument is an option or not. 31 static int cmdopt_check(cmdopt_t *h) { 32 int ret = 1; 33 const char *arg = h->argv[h->optind]; 34 35 if (*arg++ != '-') { 36 return 0; 37 } 38 39 if (*arg == '-') { 40 arg++; 41 ret++; 42 } 43 44 return ret - (*arg == '\0'); 45 } 46 47 // Gets an argument of the current option. 48 static void cmdopt_getopt(cmdopt_t *h) { 49 // Moves to the next argument if the current argument has no more characters. 50 if (*h->nextchar == '\0') { 51 cmdopt_next(h); 52 h->nextchar = h->argv[h->optind]; 53 } 54 55 // Checks whether the current option has an argument or not. 56 if (h->optind < h->optnum) { 57 h->optarg = h->nextchar; 58 cmdopt_next(h); 59 } else { 60 h->optarg = NULL; 61 } 62 } 63 64 // Searches an option. 65 static int cmdopt_search(cmdopt_t *h) { 66 const char *ptr; 67 68 // Updates an option character. 69 h->optopt = *h->nextchar++; 70 71 for (ptr = h->optstring; *ptr != '\0'; ptr++) { 72 if (*ptr == h->optopt) { 73 // Gets an option argument if required. 74 if (ptr[1] == ':') { 75 cmdopt_getopt(h); 76 77 // Returns ':' if there is no argument. 78 if (h->optarg == NULL && ptr[2] != ':') { 79 return ':'; 80 } 81 } 82 return h->optopt; 83 } 84 } 85 86 if (h->optopt == '-') { 87 cmdopt_next(h); 88 while (h->optind < h->optnum) { 89 cmdopt_shift(h); 90 } 91 return -1; 92 } 93 94 // Returns '?' if the option character is undefined. 95 return '?'; 96 } 97 98 // Compares a long option with an argument and returns the length of the 99 // matched prefix. 100 static int cmdopt_match_len(const char *opt, const char *arg) { 101 int len = 0; 102 103 // Returns 0 if there is a mismatch. 104 while ((*arg != '\0') && (*arg != '=')) { 105 if (*arg++ != *opt++) { 106 return 0; 107 } 108 len++; 109 } 110 111 // Returns a negative value in case of a perfect match. 112 if ((*arg == '\0') || (*arg == '=')) { 113 return -len; 114 } 115 116 return len; 117 } 118 119 // Checks long options. 120 static int cmdopt_match(cmdopt_t *h) { 121 int i, len; 122 int max = 0, max_optind = -1; 123 124 // Returns -1 if there are no long options. 125 if (h->longopts == NULL) { 126 return max_optind; 127 } 128 129 for (i = 0; h->longopts[i].name != NULL; i++) { 130 len = cmdopt_match_len(h->longopts[i].name, h->nextchar); 131 if (len < 0) { 132 // In case of a perfect match. 133 h->nextchar -= len; 134 return i; 135 } else if (len > max) { 136 // In case of a prefix match. 137 max = len; 138 max_optind = i; 139 } else if (len == max) { 140 // There are other candidates. 141 max_optind = -1; 142 } 143 } 144 145 // If there is no perfect match, adopts the longest one. 146 h->nextchar += max; 147 return max_optind; 148 } 149 150 // Gets an argument of a long option. 151 static void cmdopt_getopt_long(cmdopt_t *h) { 152 if (*h->nextchar == '=') { 153 h->optarg = h->nextchar + 1; 154 cmdopt_next(h); 155 } else { 156 cmdopt_next(h); 157 158 // Checks whether there are more options or not. 159 if (h->optind < h->optnum) { 160 h->optarg = h->argv[h->optind]; 161 cmdopt_next(h); 162 } else { 163 h->optarg = NULL; 164 } 165 } 166 } 167 168 // Searches long options. 169 static int cmdopt_search_long(cmdopt_t *h) { 170 const cmdopt_option *option; 171 172 // Keeps the long option. 173 h->optlong = h->argv[h->optind]; 174 175 // Gets the next option. 176 h->longindex = cmdopt_match(h); 177 if (h->longindex < 0) { 178 cmdopt_next(h); 179 return '?'; 180 } 181 182 // Gets an argument if required. 183 option = h->longopts + h->longindex; 184 if (option->has_arg) { 185 cmdopt_getopt_long(h); 186 187 // Return ':' if there are no more arguments. 188 if (h->optarg == NULL) { 189 return ':'; 190 } 191 } else if (*h->nextchar == '=') { 192 // Returns '?' for an extra option argument. 193 cmdopt_getopt_long(h); 194 return '?'; 195 } 196 197 // Overwrites a variable if specified in settings. 198 if (option->flag != NULL) { 199 *option->flag = option->val; 200 return 0; 201 } 202 203 return option->val; 204 } 205 206 // Analyze command line option. 207 static int cmdopt_main(cmdopt_t *h) { 208 int type; 209 210 // Initializes the internal state. 211 h->optopt = 0; 212 h->optlong = NULL; 213 h->optarg = NULL; 214 h->longindex = 0; 215 216 while (h->optind < h->optnum) { 217 if (h->nextchar == NULL) { 218 // Checks whether the next argument is an option or not. 219 type = cmdopt_check(h); 220 if (type == 0) { 221 cmdopt_shift(h); 222 } else { 223 h->nextchar = h->argv[h->optind] + type; 224 if (type == 2) { 225 return cmdopt_search_long(h); 226 } 227 } 228 } else { 229 if (*h->nextchar == '\0') { 230 cmdopt_next(h); 231 continue; 232 } 233 // Searches an option string. 234 return cmdopt_search(h); 235 } 236 } 237 238 return -1; 239 } 240 241 // cmdopt_init() initializes a cmdopt_t for successive cmdopt_get()s. 242 void cmdopt_init(cmdopt_t *h, int argc, char **argv, 243 const char *optstring, const cmdopt_option *longopts) { 244 static const char empty_optstring[] = ""; 245 246 h->argc = argc; 247 h->argv = argv; 248 h->optnum = h->argc; 249 250 h->longopts = longopts; 251 h->optstring = (optstring != NULL) ? optstring : empty_optstring; 252 253 h->optind = 1; 254 h->nextchar = NULL; 255 h->optarg = NULL; 256 h->optopt = 0; 257 h->optlong = NULL; 258 h->opterr = 1; 259 h->longindex = 0; 260 } 261 262 // cmdopt_get() analyzes command line arguments and gets the next option. 263 int cmdopt_get(cmdopt_t *h) { 264 int value = cmdopt_main(h); 265 266 // Prints a warning to the standard error stream if enabled. 267 if (h->opterr) { 268 if (value == ':') { 269 // Warning for a lack of an option argument. 270 if (h->optlong == NULL) { 271 fprintf(stderr, "option requires an argument -- %c\n", h->optopt); 272 } else { 273 fprintf(stderr, "option `--%s' requires an argument\n", 274 h->longopts[h->longindex].name); 275 } 276 } else if (value == '?') { 277 // Warning for an invalid option. 278 if (h->optlong == NULL) { 279 fprintf(stderr, "invalid option -- %c\n", h->optopt); 280 } else { 281 fprintf(stderr, "unrecognized option `%s'\n", h->optlong); 282 } 283 } else if ((value != -1) && (h->opterr == 2)) { 284 // Actually this is not for warning, but for debugging. 285 if (h->optlong == NULL) { 286 fprintf(stderr, "option with `%s' -- %c\n", h->optarg, h->optopt); 287 } else { 288 fprintf(stderr, "option `--%s' with `%s'\n", 289 h->longopts[h->longindex].name, h->optarg); 290 } 291 } 292 } 293 return value; 294 } 295 296 #ifdef __cplusplus 297 } // extern "C" 298 #endif // __cplusplus 299