bool w_getopt()

in watchman/Options.cpp [281:432]


bool w_getopt(
    const OptDesc* opts,
    int* argcp,
    char*** argvp,
    char*** daemon_argvp) {
  int num_opts, i;
  char* nextshort;
  int argc = *argcp;
  char** argv = *argvp;
  int long_pos = -1;
  int res;
  int num_daemon = 0;

  /* first build up the getopt_long bits that we need */
  for (num_opts = 0; opts[num_opts].optname; num_opts++) {
    ;
  }

  /* to hold the args we pass to the daemon */
  auto daemon_argv = (char**)calloc(num_opts + 1, sizeof(char*));
  if (!daemon_argv) {
    log(FATAL, "calloc daemon opts\n");
  }
  *daemon_argvp = daemon_argv;

  /* something to hold the long options */
  auto long_opts = (option*)calloc(num_opts + 1, sizeof(struct option));
  if (!long_opts) {
    log(FATAL, "calloc struct option\n");
  }

  /* and the short options */
  auto shortopts = (char*)malloc((1 + num_opts) * 2);
  if (!shortopts) {
    log(FATAL, "malloc shortopts\n");
  }
  nextshort = shortopts;
  nextshort[0] = ':';
  nextshort++;

  /* now transfer information into the space we made */
  for (i = 0; i < num_opts; i++) {
    long_opts[i].name = (char*)opts[i].optname;
    long_opts[i].val = opts[i].shortopt;
    switch (opts[i].argtype) {
      case OPT_NONE:
        long_opts[i].has_arg = no_argument;
        break;
      case REQ_STRING:
      case REQ_INT:
        long_opts[i].has_arg = required_argument;
        break;
    }

    if (opts[i].shortopt) {
      nextshort[0] = (char)opts[i].shortopt;
      nextshort++;

      if (long_opts[i].has_arg != no_argument) {
        nextshort[0] = ':';
        nextshort++;
      }
    }
  }

  nextshort[0] = 0;

  while ((res = getopt_long(argc, argv, shortopts, long_opts, &long_pos)) !=
         -1) {
    const OptDesc* o;

    switch (res) {
      case ':':
        /* missing option argument.
         * Check to see if it was actually optional */
        for (long_pos = 0; long_pos < num_opts; long_pos++) {
          if (opts[long_pos].shortopt == optopt) {
            if (IS_REQUIRED(opts[long_pos].argtype)) {
              fprintf(
                  stderr,
                  "--%s (-%c) requires an argument",
                  opts[long_pos].optname,
                  opts[long_pos].shortopt);
              return false;
            }
          }
        }
        break;

      case '?':
        /* unknown option */
        fprintf(stderr, "Unknown or invalid option! %s\n", argv[optind - 1]);
        usage(opts, stderr);
        return false;

      default:
        if (res == 0) {
          /* we got a long option */
          o = &opts[long_pos];
        } else {
          /* map short option to the real thing */
          o = NULL;
          for (long_pos = 0; long_pos < num_opts; long_pos++) {
            if (opts[long_pos].shortopt == res) {
              o = &opts[long_pos];
              break;
            }
          }
        }

        if (o->is_daemon) {
          auto value = folly::to<std::string>(
              "--", o->optname, "=", optarg ? optarg : "");
          // we deliberately leak this value to the caller
          daemon_argv[num_daemon++] = strdup(value.c_str());
        }

        /* store the argument if we found one */
        if (o->argtype != OPT_NONE && o->val && optarg) {
          switch (o->argtype) {
            case REQ_INT: {
              auto ival = atoi(optarg);
              *(int*)o->val = ival;
              cfg_set_arg(o->optname, json_integer(ival));
              break;
            }
            case REQ_STRING: {
              auto sval = typed_string_to_json(optarg, W_STRING_UNICODE);
              *(std::string*)o->val = optarg;
              cfg_set_arg(o->optname, sval);
              break;
            }
            case OPT_NONE:;
          }
        }
        if (o->argtype == OPT_NONE && o->val) {
          auto bval = json_true();
          *(int*)o->val = 1;
          cfg_set_arg(o->optname, bval);
        }
    }

    long_pos = -1;
  }

  free(long_opts);
  free(shortopts);

  *argcp = argc - optind;
  *argvp = argv + optind;
  return true;
}