123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- /*
- * C Reusables
- * <http://github.com/mity/c-reusables>
- *
- * Copyright (c) 2017-2020 Martin Mitas
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
- #include "cmdline.h"
- #include <stdio.h>
- #include <string.h>
- #ifdef _WIN32
- #define snprintf _snprintf
- #endif
- #define CMDLINE_AUXBUF_SIZE 32
- static int
- cmdline_handle_short_opt_group(const CMDLINE_OPTION* options, const char* arggroup,
- int (*callback)(int /*optval*/, const char* /*arg*/, void* /*userdata*/),
- void* userdata)
- {
- const CMDLINE_OPTION* opt;
- int i;
- int ret = 0;
- for(i = 0; arggroup[i] != '\0'; i++) {
- for(opt = options; opt->id != 0; opt++) {
- if(arggroup[i] == opt->shortname)
- break;
- }
- if(opt->id != 0 && !(opt->flags & CMDLINE_OPTFLAG_REQUIREDARG)) {
- ret = callback(opt->id, NULL, userdata);
- } else {
- /* Unknown option. */
- char badoptname[3];
- badoptname[0] = '-';
- badoptname[1] = arggroup[i];
- badoptname[2] = '\0';
- ret = callback((opt->id != 0 ? CMDLINE_OPTID_MISSINGARG : CMDLINE_OPTID_UNKNOWN),
- badoptname, userdata);
- }
- if(ret != 0)
- break;
- }
- return ret;
- }
- int
- cmdline_read(const CMDLINE_OPTION* options, int argc, char** argv,
- int (*callback)(int /*optval*/, const char* /*arg*/, void* /*userdata*/),
- void* userdata)
- {
- const CMDLINE_OPTION* opt;
- char auxbuf[CMDLINE_AUXBUF_SIZE+1];
- int fast_optarg_decision = 1;
- int after_doubledash = 0;
- int i = 1;
- int ret = 0;
- auxbuf[CMDLINE_AUXBUF_SIZE] = '\0';
- /* Check whether there is any CMDLINE_OPTFLAG_COMPILERLIKE option with
- * a name not starting with '-'. That would imply we can to check for
- * non-option arguments only after refusing all such options. */
- for(opt = options; opt->id != 0; opt++) {
- if((opt->flags & CMDLINE_OPTFLAG_COMPILERLIKE) && opt->longname[0] != '-')
- fast_optarg_decision = 0;
- }
- while(i < argc) {
- if(after_doubledash || strcmp(argv[i], "-") == 0) {
- /* Non-option argument.
- * Standalone "-" usually means "read from stdin" or "write to
- * stdout" so treat it always as a non-option. */
- ret = callback(CMDLINE_OPTID_NONE, argv[i], userdata);
- } else if(strcmp(argv[i], "--") == 0) {
- /* End of options. All the remaining tokens are non-options
- * even if they start with a dash. */
- after_doubledash = 1;
- } else if(fast_optarg_decision && argv[i][0] != '-') {
- /* Non-option argument. */
- ret = callback(CMDLINE_OPTID_NONE, argv[i], userdata);
- } else {
- for(opt = options; opt->id != 0; opt++) {
- if(opt->flags & CMDLINE_OPTFLAG_COMPILERLIKE) {
- size_t len = strlen(opt->longname);
- if(strncmp(argv[i], opt->longname, len) == 0) {
- /* Compiler-like option. */
- if(argv[i][len] != '\0')
- ret = callback(opt->id, argv[i] + len, userdata);
- else if(i+1 < argc)
- ret = callback(opt->id, argv[++i], userdata);
- else
- ret = callback(CMDLINE_OPTID_MISSINGARG, opt->longname, userdata);
- break;
- }
- } else if(opt->longname != NULL && strncmp(argv[i], "--", 2) == 0) {
- size_t len = strlen(opt->longname);
- if(strncmp(argv[i]+2, opt->longname, len) == 0) {
- /* Regular long option. */
- if(argv[i][2+len] == '\0') {
- /* with no argument provided. */
- if(!(opt->flags & CMDLINE_OPTFLAG_REQUIREDARG))
- ret = callback(opt->id, NULL, userdata);
- else
- ret = callback(CMDLINE_OPTID_MISSINGARG, argv[i], userdata);
- break;
- } else if(argv[i][2+len] == '=') {
- /* with an argument provided. */
- if(opt->flags & (CMDLINE_OPTFLAG_OPTIONALARG | CMDLINE_OPTFLAG_REQUIREDARG)) {
- ret = callback(opt->id, argv[i]+2+len+1, userdata);
- } else {
- snprintf(auxbuf, CMDLINE_AUXBUF_SIZE, "--%s", opt->longname);
- ret = callback(CMDLINE_OPTID_BOGUSARG, auxbuf, userdata);
- }
- break;
- } else {
- continue;
- }
- }
- } else if(opt->shortname != '\0' && argv[i][0] == '-') {
- if(argv[i][1] == opt->shortname) {
- /* Regular short option. */
- if(opt->flags & CMDLINE_OPTFLAG_REQUIREDARG) {
- if(argv[i][2] != '\0')
- ret = callback(opt->id, argv[i]+2, userdata);
- else if(i+1 < argc)
- ret = callback(opt->id, argv[++i], userdata);
- else
- ret = callback(CMDLINE_OPTID_MISSINGARG, argv[i], userdata);
- break;
- } else {
- ret = callback(opt->id, NULL, userdata);
- /* There might be more (argument-less) short options
- * grouped together. */
- if(ret == 0 && argv[i][2] != '\0')
- ret = cmdline_handle_short_opt_group(options, argv[i]+2, callback, userdata);
- break;
- }
- }
- }
- }
- if(opt->id == 0) { /* still not handled? */
- if(argv[i][0] != '-') {
- /* Non-option argument. */
- ret = callback(CMDLINE_OPTID_NONE, argv[i], userdata);
- } else {
- /* Unknown option. */
- char* badoptname = argv[i];
- if(strncmp(badoptname, "--", 2) == 0) {
- /* Strip any argument from the long option. */
- char* assignment = strchr(badoptname, '=');
- if(assignment != NULL) {
- size_t len = assignment - badoptname;
- if(len > CMDLINE_AUXBUF_SIZE)
- len = CMDLINE_AUXBUF_SIZE;
- strncpy(auxbuf, badoptname, len);
- auxbuf[len] = '\0';
- badoptname = auxbuf;
- }
- }
- ret = callback(CMDLINE_OPTID_UNKNOWN, badoptname, userdata);
- }
- }
- }
- if(ret != 0)
- return ret;
- i++;
- }
- return ret;
- }
|