Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions Include/internal/pycore_getopt.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@
# error "this header requires Py_BUILD_CORE define"
#endif

extern int _PyOS_opterr;
extern Py_ssize_t _PyOS_optind;
extern const wchar_t *_PyOS_optarg;
struct _PyOS_GetOpt {
int error; // generate error messages
Py_ssize_t index; // index into argv array
const wchar_t *arg; // optional argument
const wchar_t *ptr;
Py_ssize_t argc;
wchar_t * const *argv;
};

extern void _PyOS_ResetGetOpt(void);
extern void _PyOS_GetOpt_Init(
struct _PyOS_GetOpt *getopt,
Py_ssize_t argc,
wchar_t * const *argv);

typedef struct {
const wchar_t *name;
int has_arg;
int val;
} _PyOS_LongOption;

extern int _PyOS_GetOpt(Py_ssize_t argc, wchar_t * const *argv, int *longindex);
extern int _PyOS_GetOpt(
struct _PyOS_GetOpt *getopt);

#endif /* !Py_INTERNAL_PYGETOPT_H */
117 changes: 62 additions & 55 deletions Python/getopt.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,19 @@
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include "pycore_getopt.h"
#include "pycore_getopt.h" // struct _PyOS_GetOpt

int _PyOS_opterr = 1; /* generate error messages */
Py_ssize_t _PyOS_optind = 1; /* index into argv array */
const wchar_t *_PyOS_optarg = NULL; /* optional argument */

static const wchar_t *opt_ptr = L"";

/* Python command line short and long options */

#define SHORT_OPTS L"bBc:dEhiIm:OPqRsStuvVW:xX:?"

typedef struct {
const wchar_t *name;
int has_arg;
int val;
} _PyOS_LongOption;

static const _PyOS_LongOption longopts[] = {
/* name, has_arg, val (used in switch in initconfig.c) */
{L"check-hash-based-pycs", 1, 1},
Expand All @@ -49,113 +50,119 @@ static const _PyOS_LongOption longopts[] = {
};


void _PyOS_ResetGetOpt(void)
void
_PyOS_GetOpt_Init(struct _PyOS_GetOpt *getopt,
Py_ssize_t argc, wchar_t * const *argv)
{
_PyOS_opterr = 1;
_PyOS_optind = 1;
_PyOS_optarg = NULL;
opt_ptr = L"";
getopt->error = 1;
getopt->index = 1;
getopt->arg = NULL;
getopt->ptr = L"";
getopt->argc = argc;
getopt->argv = argv;
}

int _PyOS_GetOpt(Py_ssize_t argc, wchar_t * const *argv, int *longindex)
int
_PyOS_GetOpt(struct _PyOS_GetOpt *getopt)
{
wchar_t *ptr;
wchar_t option;

if (*opt_ptr == '\0') {
if (*getopt->ptr == '\0') {
if (getopt->index >= getopt->argc) {
return -1;
}

if (_PyOS_optind >= argc)
const wchar_t *arg = getopt->argv[getopt->index];
if (arg[0] != L'-' || arg[1] == L'\0' /* lone dash */ ) {
return -1;
}

#ifdef MS_WINDOWS
else if (wcscmp(argv[_PyOS_optind], L"/?") == 0) {
++_PyOS_optind;
if (wcscmp(arg, L"/?") == 0) {
++getopt->index;
return 'h';
}
#endif

else if (argv[_PyOS_optind][0] != L'-' ||
argv[_PyOS_optind][1] == L'\0' /* lone dash */ )
return -1;

else if (wcscmp(argv[_PyOS_optind], L"--") == 0) {
++_PyOS_optind;
if (wcscmp(arg, L"--") == 0) {
++getopt->index;
return -1;
}

else if (wcscmp(argv[_PyOS_optind], L"--help") == 0) {
++_PyOS_optind;
if (wcscmp(arg, L"--help") == 0) {
++getopt->index;
return 'h';
}

else if (wcscmp(argv[_PyOS_optind], L"--version") == 0) {
++_PyOS_optind;
if (wcscmp(arg, L"--version") == 0) {
++getopt->index;
return 'V';
}

opt_ptr = &argv[_PyOS_optind++][1];
getopt->ptr = &getopt->argv[getopt->index++][1];
}

if ((option = *opt_ptr++) == L'\0')
wchar_t option = *getopt->ptr++;
if (option == L'\0') {
return -1;
}

if (option == L'-') {
// Parse long option.
if (*opt_ptr == L'\0') {
if (_PyOS_opterr) {
if (*getopt->ptr == L'\0') {
if (getopt->error) {
fprintf(stderr, "Expected long option\n");
}
return -1;
}
*longindex = 0;
int longindex = 0;
const _PyOS_LongOption *opt;
for (opt = &longopts[*longindex]; opt->name; opt = &longopts[++(*longindex)]) {
if (!wcscmp(opt->name, opt_ptr))
for (opt = &longopts[longindex]; opt->name; opt = &longopts[++longindex]) {
if (wcscmp(opt->name, getopt->ptr) == 0) {
break;
}
}

if (!opt->name) {
if (_PyOS_opterr) {
fprintf(stderr, "Unknown option: %ls\n", argv[_PyOS_optind - 1]);
if (getopt->error) {
fprintf(stderr, "Unknown option: %ls\n", getopt->argv[getopt->index - 1]);
}
return '_';
}
opt_ptr = L"";

getopt->ptr = L"";
if (!opt->has_arg) {
return opt->val;
}
if (_PyOS_optind >= argc) {
if (_PyOS_opterr) {
if (getopt->index >= getopt->argc) {
if (getopt->error) {
fprintf(stderr, "Argument expected for the %ls options\n",
argv[_PyOS_optind - 1]);
getopt->argv[getopt->index - 1]);
}
return '_';
}
_PyOS_optarg = argv[_PyOS_optind++];
getopt->arg = getopt->argv[getopt->index++];
return opt->val;
}

if ((ptr = wcschr(SHORT_OPTS, option)) == NULL) {
if (_PyOS_opterr) {
wchar_t *ptr = wcschr(SHORT_OPTS, option);
if (ptr == NULL) {
if (getopt->error) {
fprintf(stderr, "Unknown option: -%c\n", (char)option);
}
return '_';
}

if (*(ptr + 1) == L':') {
if (*opt_ptr != L'\0') {
_PyOS_optarg = opt_ptr;
opt_ptr = L"";
if (*getopt->ptr != L'\0') {
getopt->arg = getopt->ptr;
getopt->ptr = L"";
}

else {
if (_PyOS_optind >= argc) {
if (_PyOS_opterr) {
if (getopt->index >= getopt->argc) {
if (getopt->error) {
fprintf(stderr,
"Argument expected for the -%c option\n", (char)option);
}
return '_';
}

_PyOS_optarg = argv[_PyOS_optind++];
getopt->arg = getopt->argv[getopt->index++];
}
}

Expand Down
34 changes: 17 additions & 17 deletions Python/initconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -3009,10 +3009,10 @@ config_parse_cmdline(PyConfig *config, PyWideStringList *warnoptions,
const PyWideStringList *argv = &config->argv;
int print_version = 0;

_PyOS_ResetGetOpt();
struct _PyOS_GetOpt getopt;
_PyOS_GetOpt_Init(&getopt, argv->length, argv->items);
do {
int longindex = -1;
int c = _PyOS_GetOpt(argv->length, argv->items, &longindex);
int c = _PyOS_GetOpt(&getopt);
if (c == EOF) {
break;
}
Expand All @@ -3022,12 +3022,12 @@ config_parse_cmdline(PyConfig *config, PyWideStringList *warnoptions,
/* -c is the last option; following arguments
that look like options are left for the
command to interpret. */
size_t len = wcslen(_PyOS_optarg) + 1 + 1;
size_t len = wcslen(getopt.arg) + 1 + 1;
wchar_t *command = PyMem_RawMalloc(sizeof(wchar_t) * len);
if (command == NULL) {
return _PyStatus_NO_MEMORY();
}
memcpy(command, _PyOS_optarg, (len - 2) * sizeof(wchar_t));
memcpy(command, getopt.arg, (len - 2) * sizeof(wchar_t));
command[len - 2] = '\n';
command[len - 1] = 0;
config->run_command = command;
Expand All @@ -3040,7 +3040,7 @@ config_parse_cmdline(PyConfig *config, PyWideStringList *warnoptions,
that look like options are left for the
module to interpret. */
if (config->run_module == NULL) {
config->run_module = _PyMem_RawWcsdup(_PyOS_optarg);
config->run_module = _PyMem_RawWcsdup(getopt.arg);
if (config->run_module == NULL) {
return _PyStatus_NO_MEMORY();
}
Expand All @@ -3052,12 +3052,12 @@ config_parse_cmdline(PyConfig *config, PyWideStringList *warnoptions,
// Integers represent long options, see Python/getopt.c
case 1:
// check-hash-based-pycs
if (wcscmp(_PyOS_optarg, L"always") == 0
|| wcscmp(_PyOS_optarg, L"never") == 0
|| wcscmp(_PyOS_optarg, L"default") == 0)
if (wcscmp(getopt.arg, L"always") == 0
|| wcscmp(getopt.arg, L"never") == 0
|| wcscmp(getopt.arg, L"default") == 0)
{
status = PyConfig_SetString(config, &config->check_hash_pycs_mode,
_PyOS_optarg);
getopt.arg);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
Expand Down Expand Up @@ -3146,7 +3146,7 @@ config_parse_cmdline(PyConfig *config, PyWideStringList *warnoptions,
break;

case 'W':
status = PyWideStringList_Append(warnoptions, _PyOS_optarg);
status = PyWideStringList_Append(warnoptions, getopt.arg);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
Expand Down Expand Up @@ -3177,22 +3177,22 @@ config_parse_cmdline(PyConfig *config, PyWideStringList *warnoptions,
}

if (config->run_command == NULL && config->run_module == NULL
&& _PyOS_optind < argv->length
&& wcscmp(argv->items[_PyOS_optind], L"-") != 0
&& getopt.index < argv->length
&& wcscmp(argv->items[getopt.index], L"-") != 0
&& config->run_filename == NULL)
{
config->run_filename = _PyMem_RawWcsdup(argv->items[_PyOS_optind]);
config->run_filename = _PyMem_RawWcsdup(argv->items[getopt.index]);
if (config->run_filename == NULL) {
return _PyStatus_NO_MEMORY();
}
}

if (config->run_command != NULL || config->run_module != NULL) {
/* Backup _PyOS_optind */
_PyOS_optind--;
/* Backup getopt.index */
getopt.index--;
}

*opt_index = _PyOS_optind;
*opt_index = getopt.index;

return _PyStatus_OK();

Expand Down
10 changes: 5 additions & 5 deletions Python/preconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,13 @@ precmdline_parse_cmdline(_PyPreCmdline *cmdline)
{
const PyWideStringList *argv = &cmdline->argv;

_PyOS_ResetGetOpt();
struct _PyOS_GetOpt getopt;
_PyOS_GetOpt_Init(&getopt, argv->length, argv->items);
/* Don't log parsing errors into stderr here: PyConfig_Read()
is responsible for that */
_PyOS_opterr = 0;
getopt.error = 0;
do {
int longindex = -1;
int c = _PyOS_GetOpt(argv->length, argv->items, &longindex);
int c = _PyOS_GetOpt(&getopt);

if (c == EOF || c == 'c' || c == 'm') {
break;
Expand All @@ -211,7 +211,7 @@ precmdline_parse_cmdline(_PyPreCmdline *cmdline)
case 'X':
{
PyStatus status = PyWideStringList_Append(&cmdline->xoptions,
_PyOS_optarg);
getopt.arg);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
Expand Down
Loading