mercurial/osutil.c
changeset 5396 5105b119edd2
child 5397 11caa374f497
equal deleted inserted replaced
5395:e73a83af7926 5396:5105b119edd2
       
     1 /*
       
     2  osutil.c - native operating system services
       
     3 
       
     4  Copyright 2007 Matt Mackall and others
       
     5 
       
     6  This software may be used and distributed according to the terms of
       
     7  the GNU General Public License, incorporated herein by reference.
       
     8 */
       
     9 
       
    10 #define _ATFILE_SOURCE
       
    11 #define _LARGEFILE64_SOURCE
       
    12 #include <alloca.h>
       
    13 #include <dirent.h>
       
    14 #include <fcntl.h>
       
    15 #include <string.h>
       
    16 #include <sys/stat.h>
       
    17 #include <sys/types.h>
       
    18 #include <unistd.h>
       
    19 
       
    20 #include "Python.h"
       
    21 
       
    22 struct listdir_stat {
       
    23     PyObject_HEAD
       
    24     struct stat st;
       
    25 };
       
    26 
       
    27 #define listdir_slot(name) \
       
    28     static PyObject *listdir_stat_##name(PyObject *self, void *x) \
       
    29     { \
       
    30         return PyInt_FromLong(((struct listdir_stat *) self)->st.name); \
       
    31     }
       
    32 
       
    33 listdir_slot(st_dev);
       
    34 listdir_slot(st_mode);
       
    35 listdir_slot(st_nlink);
       
    36 listdir_slot(st_size);
       
    37 listdir_slot(st_mtime);
       
    38 listdir_slot(st_ctime);
       
    39 
       
    40 static struct PyGetSetDef listdir_stat_getsets[] = {
       
    41   {"st_dev", listdir_stat_st_dev, 0, 0, 0},
       
    42   {"st_mode", listdir_stat_st_mode, 0, 0, 0},
       
    43   {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
       
    44   {"st_size", listdir_stat_st_size, 0, 0, 0},
       
    45   {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
       
    46   {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
       
    47   {0, 0, 0, 0, 0}
       
    48 };
       
    49 
       
    50 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
       
    51 {
       
    52     return (*t->tp_alloc)(t, 0);
       
    53 }
       
    54 
       
    55 static void listdir_stat_dealloc(PyObject *o)
       
    56 {
       
    57     (*o->ob_type->tp_free)(o);
       
    58 }
       
    59 
       
    60 static PyTypeObject listdir_stat_type = {
       
    61     PyObject_HEAD_INIT(NULL)
       
    62     0,                         /*ob_size*/
       
    63     "osutil.stat",             /*tp_name*/
       
    64     sizeof(struct listdir_stat), /*tp_basicsize*/
       
    65     0,                         /*tp_itemsize*/
       
    66     (destructor)listdir_stat_dealloc, /*tp_dealloc*/
       
    67     0,                         /*tp_print*/
       
    68     0,                         /*tp_getattr*/
       
    69     0,                         /*tp_setattr*/
       
    70     0,                         /*tp_compare*/
       
    71     0,                         /*tp_repr*/
       
    72     0,                         /*tp_as_number*/
       
    73     0,                         /*tp_as_sequence*/
       
    74     0,                         /*tp_as_mapping*/
       
    75     0,                         /*tp_hash */
       
    76     0,                         /*tp_call*/
       
    77     0,                         /*tp_str*/
       
    78     0,                         /*tp_getattro*/
       
    79     0,                         /*tp_setattro*/
       
    80     0,                         /*tp_as_buffer*/
       
    81     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
       
    82     "stat objects",           /* tp_doc */
       
    83     0,                         /* tp_traverse */
       
    84     0,                         /* tp_clear */
       
    85     0,                         /* tp_richcompare */
       
    86     0,                         /* tp_weaklistoffset */
       
    87     0,                         /* tp_iter */
       
    88     0,                         /* tp_iternext */
       
    89     0,                         /* tp_methods */
       
    90     0,                         /* tp_members */
       
    91     listdir_stat_getsets,      /* tp_getset */
       
    92     0,                         /* tp_base */
       
    93     0,                         /* tp_dict */
       
    94     0,                         /* tp_descr_get */
       
    95     0,                         /* tp_descr_set */
       
    96     0,                         /* tp_dictoffset */
       
    97     0,                         /* tp_init */
       
    98     0,                         /* tp_alloc */
       
    99     listdir_stat_new,          /* tp_new */
       
   100 };
       
   101 
       
   102 static inline int mode_to_kind(int mode)
       
   103 {
       
   104     if (S_ISREG(mode)) return S_IFREG;
       
   105     if (S_ISDIR(mode)) return S_IFDIR;
       
   106     if (S_ISLNK(mode)) return S_IFLNK;
       
   107     if (S_ISBLK(mode)) return S_IFBLK;
       
   108     if (S_ISCHR(mode)) return S_IFCHR;
       
   109     if (S_ISFIFO(mode)) return S_IFIFO;
       
   110     if (S_ISSOCK(mode)) return S_IFSOCK;
       
   111     return mode;
       
   112 }
       
   113 
       
   114 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
       
   115 {
       
   116     DIR *dir = NULL;
       
   117     struct dirent64 *ent;
       
   118     PyObject *list = NULL;
       
   119     PyObject *ctor_args = NULL;
       
   120     int all_kinds = 1;
       
   121     char *full_path;
       
   122     int path_len;
       
   123     int do_stat;
       
   124     char *path;
       
   125     int ret;
       
   126     
       
   127     {
       
   128 	static char *kwlist[] = { "path", "stat", NULL };    
       
   129 	PyObject *statobj = NULL;
       
   130 
       
   131 	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|O:listdir", kwlist,
       
   132 					 &path, &path_len, &statobj))
       
   133 	    goto bail;
       
   134 
       
   135 	do_stat = statobj && PyObject_IsTrue(statobj);
       
   136     }
       
   137     
       
   138     if ((dir = opendir(path)) == NULL) {
       
   139 	list = PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
       
   140         goto bail;
       
   141     }
       
   142 
       
   143     if ((list = PyList_New(0)) == NULL)
       
   144         goto bail;
       
   145 
       
   146     full_path = alloca(path_len + PATH_MAX + 2);
       
   147     memcpy(full_path, path, path_len);
       
   148     full_path[path_len] = '/';
       
   149 
       
   150     while ((ent = readdir64(dir))) {
       
   151         PyObject *name = NULL;
       
   152         PyObject *py_kind = NULL;
       
   153         PyObject *val = NULL;
       
   154         unsigned char d_type;
       
   155         int kind = -1;
       
   156 
       
   157         if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
       
   158             continue;
       
   159 
       
   160 #ifdef DT_REG
       
   161 	if (do_stat)
       
   162 	    d_type = 0;
       
   163 	else
       
   164 	    d_type = ent->d_type;
       
   165 #else
       
   166         d_type = 0;
       
   167 #endif
       
   168 
       
   169         switch (d_type) {
       
   170 #ifdef DT_REG
       
   171         case DT_REG: kind = S_IFREG; break;
       
   172         case DT_DIR: kind = S_IFDIR; break;
       
   173         case DT_LNK: kind = S_IFLNK; break;
       
   174         case DT_BLK: kind = S_IFBLK; break;
       
   175         case DT_CHR: kind = S_IFCHR; break;
       
   176         case DT_FIFO: kind = S_IFIFO; break;
       
   177         case DT_SOCK: kind = S_IFSOCK; break;
       
   178 #endif
       
   179 	default:
       
   180 	    if (all_kinds)
       
   181 		all_kinds = 0;
       
   182 	    break;
       
   183 	}
       
   184 
       
   185         name = PyString_FromString(ent->d_name);
       
   186 	if (kind != -1)
       
   187 	    py_kind = PyInt_FromLong(kind);
       
   188 	else {
       
   189 	    py_kind = Py_None;
       
   190 	    Py_INCREF(Py_None);
       
   191 	}
       
   192 		
       
   193         val = PyTuple_New(do_stat ? 3 : 2);
       
   194 	
       
   195         if (name == NULL || py_kind == NULL || val == NULL) {
       
   196             Py_XDECREF(name);
       
   197             Py_XDECREF(py_kind);
       
   198             Py_XDECREF(val);
       
   199 
       
   200             goto bail;
       
   201 	}
       
   202 
       
   203         PyTuple_SET_ITEM(val, 0, name);
       
   204         PyTuple_SET_ITEM(val, 1, py_kind);
       
   205 	if (do_stat) {
       
   206 	    PyTuple_SET_ITEM(val, 2, Py_None);
       
   207 	    Py_INCREF(Py_None);
       
   208 	}
       
   209 
       
   210         PyList_Append(list, val);
       
   211         Py_DECREF(val);
       
   212     }
       
   213     
       
   214     PyList_Sort(list);
       
   215 
       
   216     if (do_stat || !all_kinds) {
       
   217 	ssize_t size = PyList_Size(list);
       
   218 	ssize_t i;
       
   219 #ifdef AT_SYMLINK_NOFOLLOW
       
   220 	int dfd = dirfd(dir);
       
   221 #endif
       
   222 
       
   223 	for (i = 0; i < size; i++) {
       
   224 	    PyObject *elt = PyList_GetItem(list, i);
       
   225 	    char *name = PyString_AsString(PyTuple_GET_ITEM(elt, 0));
       
   226 	    PyObject *py_st = NULL;
       
   227 	    PyObject *py_kind = PyTuple_GET_ITEM(elt, 1);
       
   228 	    int kind;
       
   229 
       
   230 	    kind = py_kind == Py_None ? -1 : PyInt_AsLong(py_kind);
       
   231 	    
       
   232 	    if (kind != -1 && !do_stat)
       
   233 		continue;
       
   234 
       
   235             strcpy(full_path + path_len + 1, name);
       
   236 
       
   237 	    if (do_stat) {
       
   238 		struct listdir_stat *st;
       
   239 
       
   240 		if (ctor_args == NULL) {
       
   241 		    ctor_args = PyTuple_New(0);
       
   242 		    if (ctor_args == NULL)
       
   243 			goto bail;
       
   244 		}
       
   245 		
       
   246 		st = (struct listdir_stat *)
       
   247 		    PyObject_CallObject((PyObject *) &listdir_stat_type,
       
   248 					ctor_args);
       
   249 		if (st == NULL)
       
   250 		    goto bail;
       
   251 #ifdef AT_SYMLINK_NOFOLLOW
       
   252 		ret = fstatat(dfd, name, &st->st, AT_SYMLINK_NOFOLLOW);
       
   253 #else
       
   254 		ret = lstat(full_path, &st->st);
       
   255 #endif
       
   256 		if (ret == -1) {
       
   257 		    list = PyErr_SetFromErrnoWithFilename(PyExc_OSError,
       
   258 							  full_path);
       
   259 		    goto bail;
       
   260 		}
       
   261 		if (kind == -1)
       
   262 		    kind = mode_to_kind(st->st.st_mode);
       
   263 		py_st = (PyObject *) st;
       
   264 	    } else {
       
   265 		struct stat buf;
       
   266 #ifdef AT_SYMLINK_NOFOLLOW
       
   267 		ret = fstatat(dfd, ent->d_name, &buf, AT_SYMLINK_NOFOLLOW);
       
   268 #else
       
   269 		ret = lstat(full_path, &buf);
       
   270 #endif
       
   271 		if (ret == -1) {
       
   272 		    list = PyErr_SetFromErrnoWithFilename(PyExc_OSError,
       
   273 							  full_path);
       
   274 		    goto bail;
       
   275 		}
       
   276 		if (kind == -1)
       
   277 		    kind = mode_to_kind(buf.st_mode);
       
   278 	    }
       
   279 
       
   280 	    if (py_kind == Py_None && kind != -1) {
       
   281 		py_kind = PyInt_FromLong(kind);
       
   282 		if (py_kind == NULL)
       
   283 		    goto bail;
       
   284 		Py_XDECREF(Py_None);
       
   285 		PyTuple_SET_ITEM(elt, 1, py_kind);
       
   286 	    }
       
   287 
       
   288 	    if (do_stat) {
       
   289 		if (py_st == NULL) {
       
   290 		    py_st = Py_None;
       
   291 		    Py_INCREF(Py_None);
       
   292 		}
       
   293 		PyTuple_SET_ITEM(elt, 2, py_st);
       
   294 	    }
       
   295         }
       
   296     }
       
   297 
       
   298     goto done;
       
   299 
       
   300 bail:
       
   301     Py_XDECREF(list);
       
   302 
       
   303 done:
       
   304     Py_XDECREF(ctor_args);
       
   305     if (dir)
       
   306         closedir(dir);
       
   307 
       
   308     return list;
       
   309 }
       
   310 
       
   311 static char osutil_doc[] = "Native operating system services.";
       
   312 
       
   313 static PyMethodDef methods[] = {
       
   314     {"listdir", (PyCFunction) listdir, METH_VARARGS | METH_KEYWORDS,
       
   315      "list a directory\n"},
       
   316     {NULL, NULL}
       
   317 };
       
   318 
       
   319 PyMODINIT_FUNC initosutil(void)
       
   320 {
       
   321     if (PyType_Ready(&listdir_stat_type) == -1)
       
   322         return;
       
   323 
       
   324     Py_InitModule3("osutil", methods, osutil_doc);
       
   325 }