##// END OF EJS Templates
Merge with crew-stable
Merge with crew-stable

File last commit:

r9353:3ac42ca1 default
r10180:a9f60014 merge default
Show More
osutil.c
534 lines | 12.1 KiB | text/x-c | CLexer
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396 /*
osutil.c - native operating system services
Copyright 2007 Matt Mackall and others
This software may be used and distributed according to the terms of
the GNU General Public License, incorporated herein by reference.
*/
Bryan O'Sullivan
osutil: use fdopendir instead of dirfd
r5463 #define _ATFILE_SOURCE
Alexis S. L. Carvalho
osutil.c: include Python.h before the other headers...
r5397 #include <Python.h>
Bryan O'Sullivan
Windows: improve performance via buffered I/O...
r8330 #include <fcntl.h>
#include <stdio.h>
#include <string.h>
Petr Kodl
osutil: implementation for Win32...
r7056 #ifdef _WIN32
Bryan O'Sullivan
Windows: improve performance via buffered I/O...
r8330 # include <windows.h>
# include <io.h>
Petr Kodl
osutil: implementation for Win32...
r7056 #else
Bryan O'Sullivan
Windows: improve performance via buffered I/O...
r8330 # include <dirent.h>
# include <sys/stat.h>
# include <sys/types.h>
# include <unistd.h>
Petr Kodl
osutil: implementation for Win32...
r7056 #endif
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396
Sebastien Binet
osutil: fix compilation with -ansi
r9353 /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
Arne Babenhauserheide
Some platforms lack the PATH_MAX definition (eg. GNU/Hurd)....
r8722 #ifndef PATH_MAX
#define PATH_MAX 4096
#endif
Petr Kodl
osutil: implementation for Win32...
r7056 #ifdef _WIN32
/*
stat struct compatible with hg expectations
Mercurial only uses st_mode, st_size and st_mtime
the rest is kept to minimize changes between implementations
*/
struct hg_stat {
int st_dev;
int st_mode;
int st_nlink;
__int64 st_size;
int st_mtime;
int st_ctime;
};
struct listdir_stat {
PyObject_HEAD
struct hg_stat st;
};
#else
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396 struct listdir_stat {
Matt Mackall
osutil: cleanups...
r5421 PyObject_HEAD
struct stat st;
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396 };
Petr Kodl
osutil: implementation for Win32...
r7056 #endif
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396
#define listdir_slot(name) \
Thomas Arendsen Hein
Some additional space/tab cleanups
r7190 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
{ \
return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
}
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396
Bryan O'Sullivan
Fix build error with Sun C compiler.
r5431 listdir_slot(st_dev)
listdir_slot(st_mode)
listdir_slot(st_nlink)
Petr Kodl
osutil: implementation for Win32...
r7056 #ifdef _WIN32
static PyObject *listdir_stat_st_size(PyObject *self, void *x)
{
return PyLong_FromLongLong(
(PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
}
#else
Bryan O'Sullivan
Fix build error with Sun C compiler.
r5431 listdir_slot(st_size)
Petr Kodl
osutil: implementation for Win32...
r7056 #endif
Bryan O'Sullivan
Fix build error with Sun C compiler.
r5431 listdir_slot(st_mtime)
listdir_slot(st_ctime)
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396
static struct PyGetSetDef listdir_stat_getsets[] = {
Matt Mackall
osutil: cleanups...
r5421 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
{"st_mode", listdir_stat_st_mode, 0, 0, 0},
{"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
{"st_size", listdir_stat_st_size, 0, 0, 0},
{"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
{"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
{0, 0, 0, 0, 0}
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396 };
static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
{
Matt Mackall
osutil: cleanups...
r5421 return t->tp_alloc(t, 0);
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396 }
static void listdir_stat_dealloc(PyObject *o)
{
Matt Mackall
osutil: cleanups...
r5421 o->ob_type->tp_free(o);
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396 }
static PyTypeObject listdir_stat_type = {
Matt Mackall
osutil: cleanups...
r5421 PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"osutil.stat", /*tp_name*/
sizeof(struct listdir_stat), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)listdir_stat_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
"stat objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
listdir_stat_getsets, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
listdir_stat_new, /* tp_new */
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396 };
Petr Kodl
osutil: implementation for Win32...
r7056 #ifdef _WIN32
static int to_python_time(const FILETIME *tm)
{
/* number of seconds between epoch and January 1 1601 */
const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
/* conversion factor from 100ns to 1s */
const __int64 a1 = 10000000;
/* explicit (int) cast to suspend compiler warnings */
return (int)((((__int64)tm->dwHighDateTime << 32)
+ tm->dwLowDateTime) / a1 - a0);
}
static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
{
PyObject *py_st;
struct hg_stat *stp;
int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
? _S_IFDIR : _S_IFREG;
if (!wantstat)
return Py_BuildValue("si", fd->cFileName, kind);
py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
if (!py_st)
return NULL;
stp = &((struct listdir_stat *)py_st)->st;
/*
use kind as st_mode
rwx bits on Win32 are meaningless
and Hg does not use them anyway
*/
stp->st_mode = kind;
stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
stp->st_ctime = to_python_time(&fd->ftCreationTime);
if (kind == _S_IFREG)
stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
+ fd->nFileSizeLow;
return Py_BuildValue("siN", fd->cFileName,
kind, py_st);
}
Benoit Boissinot
osutil.c: refactor argument parsing, allow skip=None being passed
r7098 static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
Petr Kodl
osutil: implementation for Win32...
r7056 {
PyObject *rval = NULL; /* initialize - return value */
PyObject *list;
HANDLE fh;
WIN32_FIND_DATAA fd;
Benoit Boissinot
osutil.c: refactor argument parsing, allow skip=None being passed
r7098 char *pattern;
Petr Kodl
osutil: implementation for Win32...
r7056
/* build the path + \* pattern string */
pattern = malloc(plen+3); /* path + \* + \0 */
if (!pattern) {
PyErr_NoMemory();
Benoit Boissinot
osutil.c: refactor argument parsing, allow skip=None being passed
r7098 goto error_nomem;
Petr Kodl
osutil: implementation for Win32...
r7056 }
strcpy(pattern, path);
if (plen > 0) {
char c = path[plen-1];
if (c != ':' && c != '/' && c != '\\')
pattern[plen++] = '\\';
}
strcpy(pattern + plen, "*");
fh = FindFirstFileA(pattern, &fd);
if (fh == INVALID_HANDLE_VALUE) {
Petr Kodl
Improve error handling in osutil.c...
r7059 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
Petr Kodl
osutil: implementation for Win32...
r7056 goto error_file;
}
list = PyList_New(0);
if (!list)
goto error_list;
do {
PyObject *item;
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (!strcmp(fd.cFileName, ".")
|| !strcmp(fd.cFileName, ".."))
continue;
if (skip && !strcmp(fd.cFileName, skip)) {
rval = PyList_New(0);
goto error;
}
}
item = make_item(&fd, wantstat);
if (!item)
goto error;
if (PyList_Append(list, item)) {
Py_XDECREF(item);
goto error;
}
Py_XDECREF(item);
} while (FindNextFileA(fh, &fd));
if (GetLastError() != ERROR_NO_MORE_FILES) {
Petr Kodl
Improve error handling in osutil.c...
r7059 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
Petr Kodl
osutil: implementation for Win32...
r7056 goto error;
}
rval = list;
Py_XINCREF(rval);
error:
Py_XDECREF(list);
error_list:
FindClose(fh);
error_file:
free(pattern);
Benoit Boissinot
osutil.c: refactor argument parsing, allow skip=None being passed
r7098 error_nomem:
Petr Kodl
osutil: implementation for Win32...
r7056 return rval;
}
#else
Matt Mackall
osutil: major listdir cleanup
r7031 int entkind(struct dirent *ent)
Matt Mackall
osutils: pull file stat loop into its own function
r5425 {
Matt Mackall
osutil: major listdir cleanup
r7031 #ifdef DT_REG
switch (ent->d_type) {
case DT_REG: return S_IFREG;
case DT_DIR: return S_IFDIR;
case DT_LNK: return S_IFLNK;
case DT_BLK: return S_IFBLK;
case DT_CHR: return S_IFCHR;
case DT_FIFO: return S_IFIFO;
case DT_SOCK: return S_IFSOCK;
}
Bryan O'Sullivan
osutil: use fdopendir instead of dirfd
r5463 #endif
Matt Mackall
osutil: fix some braindamage...
r7033 return -1;
Matt Mackall
osutils: pull file stat loop into its own function
r5425 }
Benoit Boissinot
osutil.c: refactor argument parsing, allow skip=None being passed
r7098 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396 {
Benoit Boissinot
osutil.c: refactor argument parsing, allow skip=None being passed
r7098 PyObject *list, *elem, *stat, *ret = NULL;
char fullpath[PATH_MAX + 10];
Brendan Cully
_listdir only uses dfd if AT_SYMLINK_NOFOLLOW is defined
r7136 int kind, err;
Matt Mackall
osutil: major listdir cleanup
r7031 struct stat st;
struct dirent *ent;
DIR *dir;
Brendan Cully
_listdir only uses dfd if AT_SYMLINK_NOFOLLOW is defined
r7136 #ifdef AT_SYMLINK_NOFOLLOW
int dfd = -1;
#endif
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396
Petr Kodl
Improve error handling in osutil.c...
r7059 if (pathlen >= PATH_MAX) {
PyErr_SetString(PyExc_ValueError, "path too long");
Benoit Boissinot
osutil.c: refactor argument parsing, allow skip=None being passed
r7098 goto error_value;
Petr Kodl
Improve error handling in osutil.c...
r7059 }
Matt Mackall
osutil: major listdir cleanup
r7031 strncpy(fullpath, path, PATH_MAX);
fullpath[pathlen] = '/';
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396
Matt Mackall
osutil: fix some braindamage...
r7033 #ifdef AT_SYMLINK_NOFOLLOW
dfd = open(path, O_RDONLY);
if (dfd == -1) {
PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
Benoit Boissinot
osutil.c: refactor argument parsing, allow skip=None being passed
r7098 goto error_value;
Matt Mackall
osutil: fix some braindamage...
r7033 }
dir = fdopendir(dfd);
#else
dir = opendir(path);
#endif
Matt Mackall
osutil: cleanups...
r5421 if (!dir) {
Matt Mackall
osutil: major listdir cleanup
r7031 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
goto error_dir;
}
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396
Matt Mackall
osutil: cleanups...
r5421 list = PyList_New(0);
Matt Mackall
osutil: major listdir cleanup
r7031 if (!list)
goto error_list;
while ((ent = readdir(dir))) {
if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
continue;
Giorgos Keramidas
osutil.c: style fix - delete trailing end-of-line spaces
r5416
Matt Mackall
osutil: major listdir cleanup
r7031 kind = entkind(ent);
if (kind == -1 || keepstat) {
Matt Mackall
osutil: fix some braindamage...
r7033 #ifdef AT_SYMLINK_NOFOLLOW
err = fstatat(dfd, ent->d_name, &st,
AT_SYMLINK_NOFOLLOW);
#else
strncpy(fullpath + pathlen + 1, ent->d_name,
PATH_MAX - pathlen);
fullpath[PATH_MAX] = 0;
err = lstat(fullpath, &st);
#endif
Matt Mackall
osutil: major listdir cleanup
r7031 if (err == -1) {
strncpy(fullpath + pathlen + 1, ent->d_name,
PATH_MAX - pathlen);
fullpath[PATH_MAX] = 0;
PyErr_SetFromErrnoWithFilename(PyExc_OSError,
fullpath);
goto error;
}
kind = st.st_mode & S_IFMT;
}
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396
Matt Mackall
listdir: add support for aborting if a certain path is found...
r7034 /* quit early? */
if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
ret = PyList_New(0);
goto error;
}
Matt Mackall
osutil: major listdir cleanup
r7031 if (keepstat) {
stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
if (!stat)
goto error;
memcpy(&((struct listdir_stat *)stat)->st, &st, sizeof(st));
elem = Py_BuildValue("siN", ent->d_name, kind, stat);
} else
elem = Py_BuildValue("si", ent->d_name, kind);
if (!elem)
goto error;
PyList_Append(list, elem);
Py_DECREF(elem);
}
Matt Mackall
osutil: cleanups...
r5421
Matt Mackall
osutil: major listdir cleanup
r7031 ret = list;
Py_INCREF(ret);
Matt Mackall
osutil: cleanups...
r5421
Matt Mackall
osutil: major listdir cleanup
r7031 error:
Py_DECREF(list);
error_list:
closedir(dir);
error_dir:
Matt Mackall
osutil: fix some braindamage...
r7033 #ifdef AT_SYMLINK_NOFOLLOW
close(dfd);
#endif
Benoit Boissinot
osutil.c: refactor argument parsing, allow skip=None being passed
r7098 error_value:
Matt Mackall
osutil: major listdir cleanup
r7031 return ret;
Matt Mackall
osutil: cleanups...
r5421 }
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396
Petr Kodl
osutil: implementation for Win32...
r7056 #endif /* ndef _WIN32 */
Benoit Boissinot
osutil.c: refactor argument parsing, allow skip=None being passed
r7098 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
{
PyObject *statobj = NULL; /* initialize - optional arg */
PyObject *skipobj = NULL; /* initialize - optional arg */
char *path, *skip = NULL;
int wantstat, plen;
static char *kwlist[] = {"path", "stat", "skip", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
kwlist, &path, &plen, &statobj, &skipobj))
return NULL;
wantstat = statobj && PyObject_IsTrue(statobj);
if (skipobj && skipobj != Py_None) {
skip = PyString_AsString(skipobj);
if (!skip)
return NULL;
}
return _listdir(path, plen, wantstat, skip);
}
Bryan O'Sullivan
Windows: improve performance via buffered I/O...
r8330 #ifdef _WIN32
static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"name", "mode", "buffering", NULL};
PyObject *file_obj = NULL;
char *name = NULL;
char *mode = "rb";
Patrick Mezard
osutil: silence uninitialized variable warning
r8597 DWORD access = 0;
Bryan O'Sullivan
Windows: improve performance via buffered I/O...
r8330 DWORD creation;
HANDLE handle;
int fd, flags = 0;
int bufsize = -1;
char m0, m1, m2;
char fpmode[4];
int fppos = 0;
int plus;
FILE *fp;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
Py_FileSystemDefaultEncoding,
&name, &mode, &bufsize))
return NULL;
m0 = mode[0];
m1 = m0 ? mode[1] : '\0';
m2 = m1 ? mode[2] : '\0';
plus = m1 == '+' || m2 == '+';
fpmode[fppos++] = m0;
if (m1 == 'b' || m2 == 'b') {
flags = _O_BINARY;
fpmode[fppos++] = 'b';
}
else
flags = _O_TEXT;
if (plus) {
flags |= _O_RDWR;
access = GENERIC_READ | GENERIC_WRITE;
fpmode[fppos++] = '+';
}
fpmode[fppos++] = '\0';
switch (m0) {
case 'r':
creation = OPEN_EXISTING;
if (!plus) {
flags |= _O_RDONLY;
access = GENERIC_READ;
}
break;
case 'w':
creation = CREATE_ALWAYS;
if (!plus) {
access = GENERIC_WRITE;
flags |= _O_WRONLY;
}
break;
case 'a':
creation = OPEN_ALWAYS;
flags |= _O_APPEND;
if (!plus) {
flags |= _O_WRONLY;
access = GENERIC_WRITE;
}
break;
default:
PyErr_Format(PyExc_ValueError,
"mode string must begin with one of 'r', 'w', "
"or 'a', not '%c'", m0);
goto bail;
}
handle = CreateFile(name, access,
FILE_SHARE_READ | FILE_SHARE_WRITE |
FILE_SHARE_DELETE,
NULL,
creation,
FILE_ATTRIBUTE_NORMAL,
0);
if (handle == INVALID_HANDLE_VALUE) {
PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
goto bail;
}
fd = _open_osfhandle((intptr_t) handle, flags);
if (fd == -1) {
CloseHandle(handle);
PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
goto bail;
}
fp = _fdopen(fd, fpmode);
if (fp == NULL) {
_close(fd);
PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
goto bail;
}
file_obj = PyFile_FromFile(fp, name, mode, fclose);
if (file_obj == NULL) {
fclose(fp);
goto bail;
}
PyFile_SetBufSize(file_obj, bufsize);
bail:
PyMem_Free(name);
return file_obj;
}
#endif
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396 static char osutil_doc[] = "Native operating system services.";
static PyMethodDef methods[] = {
Matt Mackall
osutil: cleanups...
r5421 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
"list a directory\n"},
Bryan O'Sullivan
Windows: improve performance via buffered I/O...
r8330 #ifdef _WIN32
{"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
"Open a file with POSIX-like semantics.\n"
"On error, this function may raise either a WindowsError or an IOError."},
#endif
Matt Mackall
osutil: cleanups...
r5421 {NULL, NULL}
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396 };
PyMODINIT_FUNC initosutil(void)
{
Matt Mackall
osutil: cleanups...
r5421 if (PyType_Ready(&listdir_stat_type) == -1)
return;
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396
Matt Mackall
osutil: cleanups...
r5421 Py_InitModule3("osutil", methods, osutil_doc);
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396 }