diff options
author | Lorry <lorry@roadtrain.codethink.co.uk> | 2012-08-22 14:49:51 +0100 |
---|---|---|
committer | Lorry <lorry@roadtrain.codethink.co.uk> | 2012-08-22 14:49:51 +0100 |
commit | a498da43c7fdb9f24b73680c02a4a3588cc62d9a (patch) | |
tree | daf8119dae1749b5165b68033a1b23a7375ce9ce /mercurial/osutil.c | |
download | mercurial-tarball-a498da43c7fdb9f24b73680c02a4a3588cc62d9a.tar.gz |
Tarball conversion
Diffstat (limited to 'mercurial/osutil.c')
-rw-r--r-- | mercurial/osutil.c | 581 |
1 files changed, 581 insertions, 0 deletions
diff --git a/mercurial/osutil.c b/mercurial/osutil.c new file mode 100644 index 0000000..5c3efb7 --- /dev/null +++ b/mercurial/osutil.c @@ -0,0 +1,581 @@ +/* + 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. +*/ + +#define _ATFILE_SOURCE +#include <Python.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#ifdef _WIN32 +#include <windows.h> +#include <io.h> +#else +#include <dirent.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#endif + +#include "util.h" + +/* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */ +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +#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 +struct listdir_stat { + PyObject_HEAD + struct stat st; +}; +#endif + +#define listdir_slot(name) \ + static PyObject *listdir_stat_##name(PyObject *self, void *x) \ + { \ + return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \ + } + +listdir_slot(st_dev) +listdir_slot(st_mode) +listdir_slot(st_nlink) +#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 +listdir_slot(st_size) +#endif +listdir_slot(st_mtime) +listdir_slot(st_ctime) + +static struct PyGetSetDef listdir_stat_getsets[] = { + {"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} +}; + +static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k) +{ + return t->tp_alloc(t, 0); +} + +static void listdir_stat_dealloc(PyObject *o) +{ + o->ob_type->tp_free(o); +} + +static PyTypeObject listdir_stat_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "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 */ +}; + +#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); +} + +static PyObject *_listdir(char *path, int plen, int wantstat, char *skip) +{ + PyObject *rval = NULL; /* initialize - return value */ + PyObject *list; + HANDLE fh; + WIN32_FIND_DATAA fd; + char *pattern; + + /* build the path + \* pattern string */ + pattern = malloc(plen + 3); /* path + \* + \0 */ + if (!pattern) { + PyErr_NoMemory(); + goto error_nomem; + } + 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) { + PyErr_SetFromWindowsErrWithFilename(GetLastError(), path); + 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) { + PyErr_SetFromWindowsErrWithFilename(GetLastError(), path); + goto error; + } + + rval = list; + Py_XINCREF(rval); +error: + Py_XDECREF(list); +error_list: + FindClose(fh); +error_file: + free(pattern); +error_nomem: + return rval; +} + +#else + +int entkind(struct dirent *ent) +{ +#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; + } +#endif + return -1; +} + +static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip) +{ + PyObject *list, *elem, *stat, *ret = NULL; + char fullpath[PATH_MAX + 10]; + int kind, err; + struct stat st; + struct dirent *ent; + DIR *dir; +#ifdef AT_SYMLINK_NOFOLLOW + int dfd = -1; +#endif + + if (pathlen >= PATH_MAX) { + errno = ENAMETOOLONG; + PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); + goto error_value; + } + strncpy(fullpath, path, PATH_MAX); + fullpath[pathlen] = '/'; + +#ifdef AT_SYMLINK_NOFOLLOW + dfd = open(path, O_RDONLY); + if (dfd == -1) { + PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); + goto error_value; + } + dir = fdopendir(dfd); +#else + dir = opendir(path); +#endif + if (!dir) { + PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); + goto error_dir; + } + + list = PyList_New(0); + if (!list) + goto error_list; + + while ((ent = readdir(dir))) { + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) + continue; + + kind = entkind(ent); + if (kind == -1 || keepstat) { +#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 + if (err == -1) { + /* race with file deletion? */ + if (errno == ENOENT) + continue; + 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; + } + + /* quit early? */ + if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) { + ret = PyList_New(0); + goto error; + } + + 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); + } + + ret = list; + Py_INCREF(ret); + +error: + Py_DECREF(list); +error_list: + closedir(dir); +error_dir: +#ifdef AT_SYMLINK_NOFOLLOW + close(dfd); +#endif +error_value: + return ret; +} + +#endif /* ndef _WIN32 */ + +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 = PyBytes_AsString(skipobj); + if (!skip) + return NULL; + } + + return _listdir(path, plen, wantstat, skip); +} + +#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"; + DWORD access = 0; + 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 (m0 == 'r' && !plus) { + flags |= _O_RDONLY; + access = GENERIC_READ; + } else { + /* + work around http://support.microsoft.com/kb/899149 and + set _O_RDWR for 'w' and 'a', even if mode has no '+' + */ + flags |= _O_RDWR; + access = GENERIC_READ | GENERIC_WRITE; + fpmode[fppos++] = '+'; + } + fpmode[fppos++] = '\0'; + + switch (m0) { + case 'r': + creation = OPEN_EXISTING; + break; + case 'w': + creation = CREATE_ALWAYS; + break; + case 'a': + creation = OPEN_ALWAYS; + flags |= _O_APPEND; + 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; + } +#ifndef IS_PY3K + 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); +#else + file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1); + if (file_obj == NULL) + goto bail; +#endif +bail: + PyMem_Free(name); + return file_obj; +} +#endif + +#ifdef __APPLE__ +#include <ApplicationServices/ApplicationServices.h> + +static PyObject *isgui(PyObject *self) +{ + CFDictionaryRef dict = CGSessionCopyCurrentDictionary(); + + if (dict != NULL) { + CFRelease(dict); + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} +#endif + +static char osutil_doc[] = "Native operating system services."; + +static PyMethodDef methods[] = { + {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS, + "list a directory\n"}, +#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 +#ifdef __APPLE__ + { + "isgui", (PyCFunction)isgui, METH_NOARGS, + "Is a CoreGraphics session available?" + }, +#endif + {NULL, NULL} +}; + +#ifdef IS_PY3K +static struct PyModuleDef osutil_module = { + PyModuleDef_HEAD_INIT, + "osutil", + osutil_doc, + -1, + methods +}; + +PyMODINIT_FUNC PyInit_osutil(void) +{ + if (PyType_Ready(&listdir_stat_type) < 0) + return NULL; + + return PyModule_Create(&osutil_module); +} +#else +PyMODINIT_FUNC initosutil(void) +{ + if (PyType_Ready(&listdir_stat_type) == -1) + return; + + Py_InitModule3("osutil", methods, osutil_doc); +} +#endif |