This commit is contained in:
parent
faf91e75ab
commit
cfe6f2af3c
|
@ -398,6 +398,268 @@ static int win32_can_symlink = 0;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* De-vararg'd PyArg_ParseTupleAndKeywords()
|
||||||
|
*/
|
||||||
|
typedef struct argument_path_s {
|
||||||
|
struct argument_path_s *next;
|
||||||
|
PyObject *object;
|
||||||
|
char *format;
|
||||||
|
wchar_t **wide;
|
||||||
|
char **narrow;
|
||||||
|
Py_ssize_t *length;
|
||||||
|
} argument_path_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject *args;
|
||||||
|
PyObject *kwargs;
|
||||||
|
char format[32];
|
||||||
|
char *format_trace;
|
||||||
|
char *keywords[8];
|
||||||
|
int keyword_count;
|
||||||
|
PyObject **varargs[16];
|
||||||
|
int vararg_count;
|
||||||
|
const char *function_name;
|
||||||
|
argument_path_t *path_head;
|
||||||
|
} argument_parser_table_t;
|
||||||
|
|
||||||
|
#define APT_DECLARE(apt, fname) \
|
||||||
|
argument_parser_table_t apt; \
|
||||||
|
memset(&apt, 0, sizeof(apt)); \
|
||||||
|
apt.function_name = fname; \
|
||||||
|
apt.args = args; \
|
||||||
|
apt.kwargs = kwargs; \
|
||||||
|
apt.format_trace = apt.format
|
||||||
|
|
||||||
|
#define _APT_ARGUMENT_OBJECT(apt, name, variable, format) \
|
||||||
|
*apt.format_trace++ = (format); \
|
||||||
|
apt.keywords[apt.keyword_count++] = (name); \
|
||||||
|
apt.varargs[apt.vararg_count++] = (PyObject **)(variable); \
|
||||||
|
|
||||||
|
#define APT_ARGUMENT_OBJECT(apt, name, variable) \
|
||||||
|
_APT_ARGUMENT_OBJECT(apt, name, variable, 'O')
|
||||||
|
|
||||||
|
#define APT_ARGUMENT_OBJECT_WITH_CONVERTER(apt, name, variable, converter) \
|
||||||
|
apt.varargs[apt.vararg_count++] = (PyObject **)converter; \
|
||||||
|
APT_ARGUMENT_OBJECT(apt, name, variable); \
|
||||||
|
*apt.format_trace++ = '&'; \
|
||||||
|
|
||||||
|
#define APT_ARGUMENT_UNICODE(apt, name, variable) \
|
||||||
|
_APT_ARGUMENT_OBJECT(apt, name, variable, 'U')
|
||||||
|
|
||||||
|
#define APT_ARGUMENT_BYTES(apt, name, variable) \
|
||||||
|
_APT_ARGUMENT_OBJECT(apt, name, variable, 'y')
|
||||||
|
|
||||||
|
#define APT_ARGUMENT_INT(apt, name, variable) \
|
||||||
|
_APT_ARGUMENT_OBJECT(apt, name, variable, 'i')
|
||||||
|
|
||||||
|
static int
|
||||||
|
bool_converter(PyObject *object, void *address) {
|
||||||
|
int *output = (int *)address;
|
||||||
|
int value = PyObject_IsTrue(object);
|
||||||
|
if (value == -1)
|
||||||
|
return 0;
|
||||||
|
*output = value;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define APT_ARGUMENT_BOOL(apt, name, variable) \
|
||||||
|
_APT_ARGUMENT_OBJECT(apt, name, variable, 'p')
|
||||||
|
|
||||||
|
#if !MS_WINDOWS
|
||||||
|
#define _APT_DEFAULT_PATH_TYPE 'U'
|
||||||
|
#define _APT_PATH_BEFORE ;
|
||||||
|
#define _APT_PATH_AFTER ;
|
||||||
|
#else
|
||||||
|
#define _APT_DEFAULT_PATH_TYPE 'O'
|
||||||
|
#define _APT_PATH_BEFORE apt.varargs[apt.vararg_count++] = (PyObject **)PyUnicode_FSConverter;
|
||||||
|
#define _APT_PATH_AFTER *apt.format_trace++ = '&';
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define APT_ARGUMENT_PATH(apt, name, w, n, l) \
|
||||||
|
{ \
|
||||||
|
argument_path_t *_path_ = (argument_path_t *)alloca(sizeof(argument_path_t)); \
|
||||||
|
_APT_PATH_BEFORE; \
|
||||||
|
memset(_path_, 0, sizeof(*_path_)); \
|
||||||
|
_path_->wide = w; \
|
||||||
|
_path_->narrow = n; \
|
||||||
|
_path_->length = l; \
|
||||||
|
_path_->next = apt.path_head; \
|
||||||
|
apt.path_head = _path_; \
|
||||||
|
_APT_ARGUMENT_OBJECT(apt, name, &_path_->object, _APT_DEFAULT_PATH_TYPE); \
|
||||||
|
_APT_PATH_AFTER; \
|
||||||
|
} \
|
||||||
|
|
||||||
|
#define APT_OPTIONAL(apt) \
|
||||||
|
*apt.format_trace++ = '|' \
|
||||||
|
|
||||||
|
#define APT_KEYWORD_ONLY(apt) \
|
||||||
|
*apt.format_trace++ = '$' \
|
||||||
|
|
||||||
|
|
||||||
|
static int apt_parse(argument_parser_table_t *apt) {
|
||||||
|
argument_path_t *trace;
|
||||||
|
|
||||||
|
*apt->format_trace++ = ':';
|
||||||
|
strcpy(apt->format_trace, apt->function_name);
|
||||||
|
apt->keywords[apt->keyword_count] = NULL;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
int result;
|
||||||
|
|
||||||
|
switch (apt->vararg_count) {
|
||||||
|
case 0:
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "suspiciously too few arguments for apt_parse");
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#define APT_PREAMBLE \
|
||||||
|
result = PyArg_ParseTupleAndKeywords(apt->args, apt->kwargs, apt->format, apt->keywords, \
|
||||||
|
|
||||||
|
#define APT_POSTSCRIPT \
|
||||||
|
); \
|
||||||
|
break; \
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
APT_PREAMBLE
|
||||||
|
apt->varargs[0]
|
||||||
|
APT_POSTSCRIPT
|
||||||
|
case 2:
|
||||||
|
APT_PREAMBLE
|
||||||
|
apt->varargs[0],
|
||||||
|
apt->varargs[1]
|
||||||
|
APT_POSTSCRIPT
|
||||||
|
case 3:
|
||||||
|
APT_PREAMBLE
|
||||||
|
apt->varargs[0],
|
||||||
|
apt->varargs[1],
|
||||||
|
apt->varargs[2]
|
||||||
|
APT_POSTSCRIPT
|
||||||
|
case 4:
|
||||||
|
APT_PREAMBLE
|
||||||
|
apt->varargs[0],
|
||||||
|
apt->varargs[1],
|
||||||
|
apt->varargs[2],
|
||||||
|
apt->varargs[3]
|
||||||
|
APT_POSTSCRIPT
|
||||||
|
case 5:
|
||||||
|
APT_PREAMBLE
|
||||||
|
apt->varargs[0],
|
||||||
|
apt->varargs[1],
|
||||||
|
apt->varargs[2],
|
||||||
|
apt->varargs[3],
|
||||||
|
apt->varargs[4]
|
||||||
|
APT_POSTSCRIPT
|
||||||
|
case 6:
|
||||||
|
APT_PREAMBLE
|
||||||
|
apt->varargs[0],
|
||||||
|
apt->varargs[1],
|
||||||
|
apt->varargs[2],
|
||||||
|
apt->varargs[3],
|
||||||
|
apt->varargs[4],
|
||||||
|
apt->varargs[5]
|
||||||
|
APT_POSTSCRIPT
|
||||||
|
case 7:
|
||||||
|
APT_PREAMBLE
|
||||||
|
apt->varargs[0],
|
||||||
|
apt->varargs[1],
|
||||||
|
apt->varargs[2],
|
||||||
|
apt->varargs[3],
|
||||||
|
apt->varargs[4],
|
||||||
|
apt->varargs[5],
|
||||||
|
apt->varargs[6]
|
||||||
|
APT_POSTSCRIPT
|
||||||
|
case 8:
|
||||||
|
APT_PREAMBLE
|
||||||
|
apt->varargs[0],
|
||||||
|
apt->varargs[1],
|
||||||
|
apt->varargs[2],
|
||||||
|
apt->varargs[3],
|
||||||
|
apt->varargs[4],
|
||||||
|
apt->varargs[5],
|
||||||
|
apt->varargs[6],
|
||||||
|
apt->varargs[7]
|
||||||
|
APT_POSTSCRIPT
|
||||||
|
default:
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "too many arguments for apt_parse");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
for (trace = apt->path_head; trace != NULL; trace = trace->next) {
|
||||||
|
#if MS_WINDOWS
|
||||||
|
switch (*trace->format) {
|
||||||
|
case 'U':
|
||||||
|
*trace->wide = PyUnicode_AsUnicode(trace->object);
|
||||||
|
if (trace->length)
|
||||||
|
*trace->length = PyUnicode_GET_SIZE(trace->object);
|
||||||
|
break;
|
||||||
|
case 'y'
|
||||||
|
if (win32_warn_bytes_api())
|
||||||
|
return 0;
|
||||||
|
*trace->narrow = trace->object;
|
||||||
|
if (trace->length)
|
||||||
|
*trace->length = strlen((char *)trace->object);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "unexpected format character in apt_parse");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
assert(*trace->format == 'O');
|
||||||
|
*trace->narrow = PyBytes_AsString(trace->object);
|
||||||
|
if (trace->length)
|
||||||
|
*trace->length = PyBytes_GET_SIZE(trace->object);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !MS_WINDOWS
|
||||||
|
break;
|
||||||
|
#else
|
||||||
|
for (trace = apt->path_head; trace != NULL; trace = trace->next) {
|
||||||
|
/*
|
||||||
|
* try flipping paths between wide and narrow.
|
||||||
|
*
|
||||||
|
* each element always started with wide.
|
||||||
|
* so:
|
||||||
|
* * if we see a wide, flip it to narrow and stop.
|
||||||
|
* * if we see a narrow, flip it to wide and move on to the next field.
|
||||||
|
* * if we run out of fields, we have exhausted all possibilities. fail.
|
||||||
|
*
|
||||||
|
* (conceptually it helps to think of the fields as a binary number
|
||||||
|
* with as many digits as there are entries in the path list.
|
||||||
|
* wide is 0, narrow is 1. we keep incrementing the number
|
||||||
|
* until we wrap around to 0 again.)
|
||||||
|
*/
|
||||||
|
if (*trace->format == 'U') {
|
||||||
|
*trace->format = 'y';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (*trace->format == 'y') {
|
||||||
|
*trace->format = 'U';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "unexpected format character in apt_parse");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!trace)
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void apt_cleanup(argument_parser_table_t *apt) {
|
||||||
|
#if !MS_WINDOWS
|
||||||
|
argument_path_t *trace;
|
||||||
|
for (trace = apt->path_head; trace != NULL; trace = trace->next) {
|
||||||
|
Py_XDECREF(trace->object);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* A helper used by a number of POSIX-only functions */
|
/* A helper used by a number of POSIX-only functions */
|
||||||
#ifndef MS_WINDOWS
|
#ifndef MS_WINDOWS
|
||||||
static int
|
static int
|
||||||
|
@ -1942,6 +2204,12 @@ posix_do_stat(PyObject *self, PyObject *args,
|
||||||
|
|
||||||
/* POSIX methods */
|
/* POSIX methods */
|
||||||
|
|
||||||
|
#ifdef AT_FDCWD
|
||||||
|
#define DEFAULT_DIR_FD AT_FDCWD
|
||||||
|
#else
|
||||||
|
#define DEFAULT_DIR_FD (-100)
|
||||||
|
#endif
|
||||||
|
|
||||||
PyDoc_STRVAR(posix_access__doc__,
|
PyDoc_STRVAR(posix_access__doc__,
|
||||||
"access(path, mode) -> True if granted, False otherwise\n\n\
|
"access(path, mode) -> True if granted, False otherwise\n\n\
|
||||||
Use the real uid/gid to test for access to a path. Note that most\n\
|
Use the real uid/gid to test for access to a path. Note that most\n\
|
||||||
|
@ -1951,34 +2219,54 @@ specified access to the path. The mode argument can be F_OK to test\n\
|
||||||
existence, or the inclusive-OR of R_OK, W_OK, and X_OK.");
|
existence, or the inclusive-OR of R_OK, W_OK, and X_OK.");
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
posix_access(PyObject *self, PyObject *args)
|
posix_access(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||||
{
|
{
|
||||||
const char *path;
|
|
||||||
int mode;
|
int mode;
|
||||||
|
int dir_fd = DEFAULT_DIR_FD;
|
||||||
|
wchar_t *wide_path;
|
||||||
|
char *path;
|
||||||
|
int follow_symlinks = 1;
|
||||||
|
int effective_ids = 0;
|
||||||
|
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
DWORD attr;
|
DWORD attr;
|
||||||
PyObject *po;
|
#else
|
||||||
if (PyArg_ParseTuple(args, "Ui:access", &po, &mode)) {
|
int res;
|
||||||
wchar_t* wpath = PyUnicode_AsUnicode(po);
|
#endif
|
||||||
if (wpath == NULL)
|
|
||||||
return NULL;
|
APT_DECLARE(apt, "access");
|
||||||
Py_BEGIN_ALLOW_THREADS
|
APT_ARGUMENT_PATH(apt, "path", &wide_path, &path, NULL);
|
||||||
attr = GetFileAttributesW(wpath);
|
APT_ARGUMENT_INT(apt, "mode", &mode);
|
||||||
Py_END_ALLOW_THREADS
|
APT_OPTIONAL(apt);
|
||||||
goto finish;
|
APT_KEYWORD_ONLY(apt);
|
||||||
}
|
APT_ARGUMENT_INT(apt, "dir_fd", &dir_fd);
|
||||||
/* Drop the argument parsing error as narrow strings
|
APT_ARGUMENT_BOOL(apt, "follow_symlinks", &follow_symlinks);
|
||||||
are also valid. */
|
APT_ARGUMENT_BOOL(apt, "effective_ids", &effective_ids);
|
||||||
PyErr_Clear();
|
|
||||||
if (!PyArg_ParseTuple(args, "yi:access", &path, &mode))
|
#define ACCESS_FAIL_IF_KEYWORD_USED \
|
||||||
return NULL;
|
if (dir_fd != DEFAULT_DIR_FD) \
|
||||||
if (win32_warn_bytes_api())
|
PyErr_SetString(PyExc_NotImplementedError, "access: dir_fd unavailable on this platform"); \
|
||||||
|
if (!follow_symlinks) \
|
||||||
|
PyErr_SetString(PyExc_NotImplementedError, "access: follow_symlinks unavailable on this platform"); \
|
||||||
|
if (effective_ids) \
|
||||||
|
PyErr_SetString(PyExc_NotImplementedError, "access: effective_ids unavailable on this platform")
|
||||||
|
|
||||||
|
if (!apt_parse(&apt))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
ACCESS_FAIL_IF_KEYWORD_USED;
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
attr = GetFileAttributesA(path);
|
if (wide != NULL)
|
||||||
|
attr = GetFileAttributesW(wide_path);
|
||||||
|
else
|
||||||
|
attr = GetFileAttributesA(path);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
finish:
|
|
||||||
|
apt_cleanup(&apt);
|
||||||
|
|
||||||
if (attr == 0xFFFFFFFF)
|
if (attr == 0xFFFFFFFF)
|
||||||
/* File does not exist, or cannot read attributes */
|
/* File does not exist, or cannot read attributes */
|
||||||
return PyBool_FromLong(0);
|
return PyBool_FromLong(0);
|
||||||
|
@ -1989,16 +2277,28 @@ finish:
|
||||||
|| !(attr & FILE_ATTRIBUTE_READONLY)
|
|| !(attr & FILE_ATTRIBUTE_READONLY)
|
||||||
|| (attr & FILE_ATTRIBUTE_DIRECTORY));
|
|| (attr & FILE_ATTRIBUTE_DIRECTORY));
|
||||||
#else
|
#else
|
||||||
PyObject *opath;
|
|
||||||
int res;
|
if ((dir_fd != DEFAULT_DIR_FD)
|
||||||
if (!PyArg_ParseTuple(args, "O&i:access",
|
|| effective_ids || !follow_symlinks) {
|
||||||
PyUnicode_FSConverter, &opath, &mode))
|
#ifdef HAVE_FACCESSAT
|
||||||
return NULL;
|
int flags = 0;
|
||||||
path = PyBytes_AsString(opath);
|
if (!follow_symlinks)
|
||||||
Py_BEGIN_ALLOW_THREADS
|
flags |= AT_SYMLINK_NOFOLLOW;
|
||||||
res = access(path, mode);
|
if (effective_ids)
|
||||||
Py_END_ALLOW_THREADS
|
flags |= AT_EACCESS;
|
||||||
Py_DECREF(opath);
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
res = faccessat(dir_fd, path, mode, flags);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
#else
|
||||||
|
ACCESS_FAIL_IF_KEYWORD_USED;
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
res = access(path, mode);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
}
|
||||||
|
|
||||||
|
apt_cleanup(&apt);
|
||||||
return PyBool_FromLong(res == 0);
|
return PyBool_FromLong(res == 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -10596,7 +10896,9 @@ get_terminal_size(PyObject *self, PyObject *args)
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef posix_methods[] = {
|
static PyMethodDef posix_methods[] = {
|
||||||
{"access", posix_access, METH_VARARGS, posix_access__doc__},
|
{"access", (PyCFunction)posix_access,
|
||||||
|
METH_VARARGS | METH_KEYWORDS,
|
||||||
|
posix_access__doc__},
|
||||||
#ifdef HAVE_TTYNAME
|
#ifdef HAVE_TTYNAME
|
||||||
{"ttyname", posix_ttyname, METH_VARARGS, posix_ttyname__doc__},
|
{"ttyname", posix_ttyname, METH_VARARGS, posix_ttyname__doc__},
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue