// // Lol Engine // // Copyright © 2002—2018 Sam Hocevar // // Lol Engine is free software. It comes without any warranty, to // the extent permitted by applicable law. You can redistribute it // and/or modify it under the terms of the Do What the Fuck You Want // to Public License, Version 2, as published by the WTFPL Task Force. // See http://www.wtfpl.net/ for more details. // #include #if HAVE_GETOPT_H # include #endif #if HAVE_UNISTD_H # include #endif namespace lol { struct getopt_private { getopt_private(int argc, char * const * _argv) : m_argc(argc), m_argv(_argv) {} #if HAVE_GETOPT_LONG typedef option optdesc; #else struct optdesc { char const *name; int has_arg; int *flag; int val; }; #endif int m_argc; char * const *m_argv; std::string m_optstring; array m_opts; }; getopt::getopt(int argc, char ** _argv) : index(1), arg(nullptr), m_private(new getopt_private(argc, _argv)) { } getopt::getopt(int argc, char * const * _argv) : index(1), arg(nullptr), m_private(new getopt_private(argc, _argv)) { } getopt::~getopt() { } void getopt::add_opt(int short_opt, char const *long_opt, bool has_arg) { getopt_private::optdesc o { long_opt, has_arg, nullptr, short_opt }; m_private->m_opts.push(o); /* “The standards require that the argument [to isalnum()] is either * EOF or a value that is representable in the type unsigned char.” */ if ((int)(unsigned char)short_opt == short_opt && isalnum(short_opt)) { m_private->m_optstring += (char)short_opt; if (has_arg) m_private->m_optstring += ':'; } } int getopt::parse() { int longindex = 0; // FIXME: what is this? #if HAVE_GETOPT_LONG int ret; optind = this->index; optarg = this->arg; m_private->m_opts.push(getopt_private::optdesc { nullptr, 0, nullptr, 0 }); ret = getopt_long(m_private->m_argc, m_private->m_argv, m_private->m_optstring.c_str(), (option const *)m_private->m_opts.data(), &longindex); this->index = optind; this->arg = optarg; return ret; #else /* XXX: this getopt_long implementation should not be trusted for other * applications without any serious peer reviewing. It “just works” with * zzuf and a few libcaca programs but may fail miserably in other * programs. */ char **argv = (char **)(uintptr_t)m_private->m_argv; char *flag; if (this->index >= m_private->m_argc) return -1; flag = argv[this->index]; if (flag[0] == '-' && flag[1] != '-') { char const *tmp; int ret = flag[1]; if (ret == '\0') return -1; tmp = strchr(m_private->m_optstring.c_str(), ret); if (!tmp || ret == ':') return '?'; ++this->index; if (tmp[1] == ':') { if (flag[2] != '\0') this->arg = flag + 2; else { if (this->index >= m_private->m_argc) goto too_few; this->arg = argv[this->index++]; } return ret; } if (flag[2] != '\0') { flag[1] = '-'; --this->index; ++argv[this->index]; } return ret; } if (flag[0] == '-' && flag[1] == '-') { if (flag[2] == '\0') return -1; for (int i = 0; m_private->m_opts[i].name; ++i) { size_t l = strlen(m_private->m_opts[i].name); if (strncmp(flag + 2, m_private->m_opts[i].name, l)) continue; switch (flag[2 + l]) { case '=': if (!m_private->m_opts[i].has_arg) goto bad_opt; longindex = i; ++this->index; this->arg = flag + 2 + l + 1; return m_private->m_opts[i].val; case '\0': longindex = i; ++this->index; if (m_private->m_opts[i].has_arg) { if (this->index >= m_private->m_argc) goto too_few; this->arg = argv[this->index++]; } return m_private->m_opts[i].val; default: break; } } bad_opt: fprintf(stderr, "%s: unrecognized option `%s'\n", argv[0], flag); return '?'; too_few: fprintf(stderr, "%s: option `%s' requires an argument\n", argv[0], flag); return '?'; } return -1; #endif } } // namespace lol