|
- //
- // Lol Engine
- //
- // Copyright © 2002—2018 Sam Hocevar <sam@hocevar.net>
- //
- // 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 <lol/engine-internal.h>
-
- #if HAVE_GETOPT_H
- # include <getopt.h>
- #endif
- #if HAVE_UNISTD_H
- # include <unistd.h>
- #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<optdesc> 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
|