root/releases/pkgcore/0.2.13/src/posix.c @ masterdriverz%2540gentoo.org-20070309225812-15j6j9wmp0pek3vw

Revision masterdriverz%2540gentoo.org-20070309225812-15j6j9wmp0pek3vw, 22.0 KB (checked in by Charlie Shepherd <masterdriverz@…>, 22 months ago)

Even more killing of trailing whitespace

Line 
1/*
2 * Copyright: 2006-2007 Brian Harring <ferringb@gmail.com>
3 * License: GPL2
4 *
5 * C version of some of pkgcore (for extra speed).
6 */
7
8/* This does not really do anything since we do not use the "#"
9 * specifier in a PyArg_Parse or similar call, but hey, not using it
10 * means we are Py_ssize_t-clean too!
11 */
12
13#define PY_SSIZE_T_CLEAN
14
15#include "common.h"
16#include <structmember.h>
17#include <sys/mman.h>
18#include <sys/types.h>
19#include <sys/stat.h>
20#include <fcntl.h>
21
22// only 2.5.46 kernels and up have this.
23#ifndef MAP_POPULATE
24#define MAP_POPULATE 0
25#endif
26
27static PyObject *pkgcore_stat_float_times = NULL;
28static PyObject *pkgcore_empty_tuple = NULL;
29static PyObject *pkgcore_readlines_empty_iter_singleton = NULL;
30
31
32#define SKIP_SLASHES(ptr) while('/' == *(ptr)) (ptr)++;
33
34static PyObject *
35pkgcore_normpath(PyObject *self, PyObject *old_path)
36{
37    if(!PyString_CheckExact(old_path)) {
38        PyErr_SetString(PyExc_TypeError,
39            "old_path must be a str");
40        return NULL;
41    }
42    Py_ssize_t len = PyString_Size(old_path);
43    if(!len)
44        return PyString_FromString(".");
45
46    char *oldstart, *oldp, *newstart, *newp, *real_newstart;
47    oldstart = oldp = PyString_AsString(old_path);
48
49    PyObject *new_path = PyString_FromStringAndSize(NULL, len);
50    if(!new_path)
51        return new_path;
52    real_newstart = newstart = newp = PyString_AS_STRING(new_path);
53
54
55    int leading_slash;
56    Py_ssize_t slash_count = 0;
57    // /../ == / , ../foo == ../foo , ../foo/../../ == ../../../
58    if('/' == *oldp) {
59        *newp = '/';
60        newp++;
61        leading_slash = 1;
62        slash_count++;
63        SKIP_SLASHES(oldp);
64        newstart = newp;
65    } else {
66        leading_slash = 0;
67    }
68
69    while('\0' != *oldp) {
70        if('/' == *oldp) {
71            *newp = '/';
72            newp++;
73            slash_count++;
74            SKIP_SLASHES(oldp);
75        }
76        if('.' == *oldp) {
77            oldp++;
78            if('\0' == *oldp)
79                break;
80            if('/' == *oldp) {
81                oldp++;
82                SKIP_SLASHES(oldp);
83                continue;
84            }
85            if(*oldp == '.' && ('/' == oldp[1] || '\0' ==  oldp[1])) {
86                // for newp, ../ == ../ , /../ == /
87                if(leading_slash == slash_count) {
88                    if(!leading_slash) {
89                        // ../ case.
90                        newp[0] = '.';
91                        newp[1] = '.';
92                        newp[2] = '/';
93                        newp += 3;
94                    }
95                } else if (slash_count != 1 || '/' != *newstart) {
96                    // if its /, then the stripping would be ignored.
97                    newp--;
98                    while(newp > newstart && '/' != newp[-1])
99                        newp--;
100                }
101                oldp++;
102                SKIP_SLASHES(oldp);
103                continue;
104            }
105            // funky file name.
106            oldp--;
107        }
108        while('/' != *oldp && '\0' != *oldp) {
109            *newp = *oldp;
110            ++newp;
111            ++oldp;
112        }
113    }
114
115    *newp = '\0';
116    // protect leading slash, but strip trailing.
117    --newp;
118    while(newp > real_newstart && '/' == *newp)
119        newp--;
120
121    // resize it now.
122    _PyString_Resize(&new_path, newp - real_newstart + 1);
123    return new_path;
124}
125
126static PyObject *
127pkgcore_join(PyObject *self, PyObject *args)
128{
129    if(!args) {
130        PyErr_SetString(PyExc_TypeError, "requires at least one path");
131        return NULL;
132    }
133    PyObject *fast = PySequence_Fast(args, "arg must be a sequence");
134    if(!fast)
135        return NULL;
136    Py_ssize_t end = PySequence_Fast_GET_SIZE(fast);
137    if(!end) {
138        PyErr_SetString(PyExc_TypeError,
139            "join takes at least one arguement (0 given)");
140        return NULL;
141    }
142
143    PyObject **items = PySequence_Fast_ITEMS(fast);
144    Py_ssize_t start = 0, len, i = 0;
145    char *s;
146    int leading_slash = 0;
147    // find the right most item with a prefixed '/', else 0.
148    for(; i < end; i++) {
149        if(!PyString_CheckExact(items[i])) {
150            PyErr_SetString(PyExc_TypeError, "all args must be strings");
151            Py_DECREF(fast);
152            return NULL;
153        }
154        s = PyString_AsString(items[i]);
155        if('/' == *s) {
156            leading_slash = 1;
157            start = i;
158        }
159    }
160    // know the relevant slice now; figure out the size.
161    len = 0;
162    char *s_start;
163    for(i = start; i < end; i++) {
164        // this is safe because we're using CheckExact above.
165        s_start = s = PyString_AS_STRING(items[i]);
166        while('\0' != *s)
167            s++;
168        if(s_start == s)
169            continue;
170        len += s - s_start;
171        char *s_end = s;
172        if(i + 1 != end) {
173            // cut the length down for trailing duplicate slashes
174            while(s != s_start && '/' == s[-1])
175                s--;
176            // allocate for a leading slash if needed
177            if(s_end == s && (s_start != s ||
178                (s_end == s_start && i != start))) {
179                len++;
180            } else if(s_start != s) {
181                len -= s_end - s -1;
182            }
183        }
184    }
185
186    // ok... we know the length.  allocate a string, and copy it.
187    PyObject *ret = PyString_FromStringAndSize(NULL, len);
188    if(!ret)
189        return NULL;
190    char *buf = PyString_AS_STRING(ret);
191    if(leading_slash) {
192        *buf = '/';
193        buf++;
194    }
195    for(i = start; i < end; i++) {
196        s_start = s = PyString_AS_STRING(items[i]);
197        if(i == start && leading_slash) {
198            // a slash is inserted anywas, thus we skip one ahead
199            // so it doesn't gain an extra.
200            s_start++;
201            s = s_start;
202        }
203
204       if('\0' == *s)
205            continue;
206        while('\0' != *s) {
207            *buf = *s;
208            buf++;
209            if('/' == *s) {
210                char *tmp_s = s + 1;
211                SKIP_SLASHES(s);
212                if('\0' == *s) {
213                    if(i + 1  != end) {
214                        buf--;
215                    } else {
216                        // copy the cracked out trailing slashes on the
217                        // last item
218                        while(tmp_s < s) {
219                            *buf = '/';
220                            buf++;
221                            tmp_s++;
222                        }
223                    }
224                    break;
225                } else {
226                    // copy the cracked out intermediate slashes.
227                    while(tmp_s < s) {
228                        *buf = '/';
229                        buf++;
230                        tmp_s++;
231                    }
232                }
233            } else
234                s++;
235        }
236        if(i + 1 != end) {
237            *buf = '/';
238            buf++;
239        }
240    }
241    *buf = '\0';
242    Py_DECREF(fast);
243    return ret;
244}
245
246// returns 0 on success opening, 1 on ENOENT but ignore, and -1 on failure
247// if failure condition, appropriate exception is set.
248
249static inline int
250pkgcore_read_open_and_stat(PyObject *path,
251    int *fd, struct stat *st)
252{
253    errno = 0;
254    if((*fd = open(PyString_AsString(path), O_RDONLY)) >= 0) {
255        int ret = fstat(*fd, st);
256        if(!ret) {
257            return 0;
258        }
259    }
260    return 1;
261}
262
263static inline int
264handle_failed_open_stat(int fd, PyObject *path, PyObject *swallow_missing)
265{
266    if(fd < 0) {
267        if(errno == ENOENT) {
268            if(swallow_missing) {
269                if(PyObject_IsTrue(swallow_missing)) {
270                    errno = 0;
271                    return 0;
272                }
273                if(PyErr_Occurred())
274                    return 1;
275            }
276        }
277        PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, path);
278        return 1;
279    }
280    PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path);
281    if(close(fd))
282        PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, path);
283    return 1;
284}
285
286static PyObject *
287pkgcore_readfile(PyObject *self, PyObject *args)
288{
289    PyObject *path, *swallow_missing = NULL;
290    if(!args || !PyArg_ParseTuple(args, "S|O:readfile", &path,
291        &swallow_missing)) {
292        return NULL;
293    }
294//    Py_ssize_t size;
295    int fd;
296    struct stat st;
297    Py_BEGIN_ALLOW_THREADS
298    if(pkgcore_read_open_and_stat(path, &fd, &st)) {
299        Py_BLOCK_THREADS
300        if(handle_failed_open_stat(fd, path, swallow_missing))
301            return NULL;
302        Py_RETURN_NONE;
303    }
304    Py_END_ALLOW_THREADS
305
306    int ret = 0;
307    PyObject *data = PyString_FromStringAndSize(NULL, st.st_size);
308
309    Py_BEGIN_ALLOW_THREADS
310    errno = 0;
311    if(data) {
312        ret = read(fd, PyString_AS_STRING(data), st.st_size) != st.st_size ? 1 : 0;
313    }
314    ret += close(fd);
315    Py_END_ALLOW_THREADS
316
317    if(ret) {
318        Py_CLEAR(data);
319        data = PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path);
320    }
321    return data;
322}
323
324typedef struct {
325    PyObject_HEAD
326} pkgcore_readlines_empty_iter;
327
328static PyObject *
329pkgcore_readlines_empty_iter_get_mtime(pkgcore_readlines_empty_iter *self)
330{
331    Py_RETURN_NONE;
332}
333
334static int
335pkgcore_readlines_empty_iter_set_mtime(pkgcore_readlines_empty_iter *self,
336    PyObject *v, void *closure)
337{
338    PyErr_SetString(PyExc_AttributeError, "mtime is immutable");
339    return -1;
340}
341
342static PyObject *
343pkgcore_readlines_empty_iter_next(pkgcore_readlines_empty_iter *self)
344{
345    PyErr_SetNone(PyExc_StopIteration);
346    return NULL;
347}
348
349struct PyGetSetDef pkgcore_readlines_empty_iter_getsetters[] = {
350    PKGCORE_GETSET(pkgcore_readlines_empty_iter, "mtime", mtime),
351    {NULL}
352};
353
354static PyTypeObject pkgcore_readlines_empty_iter_type = {
355    PyObject_HEAD_INIT(NULL)
356    0,                                               /* ob_size */
357    "readlines.empty_iter",                          /* tp_name */
358    sizeof(pkgcore_readlines_empty_iter),            /* tp_size */
359    0,                                               /* tp_itemsize*/
360    0,                                               /* tp_dealloc*/
361    0,                                               /* tp_print*/
362    0,                                               /* tp_getattr*/
363    0,                                               /* tp_setattr*/
364    0,                                               /* tp_compare*/
365    0,                                               /* tp_repr*/
366    0,                                               /* tp_as_number*/
367    0,                                               /* tp_as_sequence*/
368    0,                                               /* tp_as_mapping*/
369    0,                                               /* tp_hash */
370    (ternaryfunc)0,                                  /* tp_call*/
371    (reprfunc)0,                                     /* tp_str*/
372    0,                                               /* tp_getattro*/
373    0,                                               /* tp_setattro*/
374    0,                                               /* tp_as_buffer*/
375    Py_TPFLAGS_DEFAULT,                              /* tp_flags*/
376    0,                                               /* tp_doc */
377    (traverseproc)0,                                 /* tp_traverse */
378    (inquiry)0,                                      /* tp_clear */
379    (richcmpfunc)0,                                  /* tp_richcompare */
380    0,                                               /* tp_weaklistoffset */
381    (getiterfunc)PyObject_SelfIter,                  /* tp_iter */
382    (iternextfunc)pkgcore_readlines_empty_iter_next, /* tp_iternext */
383    0,                                               /* tp_methods */
384    0,                                               /* tp_members */
385    pkgcore_readlines_empty_iter_getsetters,         /* tp_getset */
386};
387
388typedef struct {
389    PyObject_HEAD
390    char *start;
391    char *end;
392    char *map;
393    int fd;
394    int strip_newlines;
395    time_t mtime;
396    unsigned long mtime_nsec;
397    PyObject *fallback;
398} pkgcore_readlines;
399
400static PyObject *
401pkgcore_readlines_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
402{
403    PyObject *path, *swallow_missing = NULL, *strip_newlines = NULL;
404    PyObject *none_on_missing = NULL;
405    pkgcore_readlines *self = NULL;
406    if(kwargs && PyDict_Size(kwargs)) {
407        PyErr_SetString(PyExc_TypeError,
408            "readlines.__new__ doesn't accept keywords");
409        return NULL;
410    } else if (!PyArg_ParseTuple(args, "S|OOOO:readlines.__new__",
411        &path, &strip_newlines, &swallow_missing, &none_on_missing)) {
412        return NULL;
413    }
414
415    int fd;
416    struct stat st;
417//    Py_ssize_t size;
418    void *ptr = NULL;
419    PyObject *fallback = NULL;
420    Py_BEGIN_ALLOW_THREADS
421    errno = 0;
422    if(pkgcore_read_open_and_stat(path, &fd, &st)) {
423        Py_BLOCK_THREADS
424
425        if(handle_failed_open_stat(fd, path, swallow_missing))
426            return NULL;
427
428        // return an empty tuple, and let them iter over that.
429        if(none_on_missing && PyObject_IsTrue(none_on_missing)) {
430            Py_RETURN_NONE;
431        }
432
433        Py_INCREF(pkgcore_readlines_empty_iter_singleton);
434        return pkgcore_readlines_empty_iter_singleton;
435    }
436    if(st.st_size >= 0x4000) {
437        ptr = (char *)mmap(NULL, st.st_size, PROT_READ,
438            MAP_SHARED|MAP_NORESERVE|MAP_POPULATE, fd, 0);
439        if(ptr == MAP_FAILED)
440            ptr = NULL;
441    } else {
442        Py_BLOCK_THREADS
443        fallback = PyString_FromStringAndSize(NULL, st.st_size);
444        Py_UNBLOCK_THREADS
445        if(fallback) {
446            errno = 0;
447            ptr = (read(fd, PyString_AS_STRING(fallback), st.st_size) != st.st_size) ?
448                MAP_FAILED : NULL;
449        }
450        int ret = close(fd);
451        if(ret) {
452            Py_CLEAR(fallback);
453            PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path);
454            Py_BLOCK_THREADS
455            return NULL;
456        } else if(!fallback) {
457            Py_BLOCK_THREADS
458            return NULL;
459        }
460    }
461    Py_END_ALLOW_THREADS
462
463    if(ptr == MAP_FAILED) {
464        PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path);
465        if(close(fd))
466            PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path);
467        Py_CLEAR(fallback);
468        return NULL;
469    }
470
471    self = (pkgcore_readlines *)type->tp_alloc(type, 0);
472    if(!self) {
473        // you've got to be kidding me...
474        if(ptr) {
475            munmap(ptr, st.st_size);
476            close(fd);
477            errno = 0;
478        } else {
479            Py_DECREF(fallback);
480        }
481        if(self) {
482            Py_DECREF(self);
483        }
484        return NULL;
485    }
486    self->fallback = fallback;
487    self->map = ptr;
488    self->mtime = st.st_mtime;
489#ifdef HAVE_STAT_TV_NSEC
490    self->mtime_nsec = st.st_mtim.tv_nsec;
491#else
492    self->mtime_nsec = 0;
493#endif
494    if (ptr) {
495        self->start = ptr;
496        self->fd = fd;
497    } else {
498        self->start = PyString_AS_STRING(fallback);
499        self->fd = -1;
500    }
501    self->end = self->start + st.st_size;
502
503    if(strip_newlines) {
504        if(strip_newlines == Py_True) {
505            self->strip_newlines = 1;
506        } else if (strip_newlines == Py_False) {
507            self->strip_newlines = 0;
508        } else {
509            self->strip_newlines = PyObject_IsTrue(strip_newlines) ? 1 : 0;
510            if(PyErr_Occurred()) {
511                Py_DECREF(self);
512                return NULL;
513            }
514        }
515    } else
516        self->strip_newlines = 1;
517    return (PyObject *)self;
518}
519
520static void
521pkgcore_readlines_dealloc(pkgcore_readlines *self)
522{
523    if(self->fallback) {
524        Py_DECREF(self->fallback);
525    } else if(self->map) {
526        if(munmap(self->map, self->end - self->map))
527            // swallow it, no way to signal an error
528            errno = 0;
529        if(close(self->fd))
530            // swallow it, no way to signal an error
531            errno = 0;
532    }
533    self->ob_type->tp_free((PyObject *)self);
534}
535
536static PyObject *
537pkgcore_readlines_iternext(pkgcore_readlines *self)
538{
539    if(self->start == self->end) {
540        // at the end, thus return
541        return NULL;
542    }
543    char *p = self->start;
544    assert(self->end);
545    assert(self->start);
546    assert(self->map || self->fallback);
547    assert(self->end > self->start);
548
549    while(p != self->end && '\n' != *p)
550        p++;
551
552    PyObject *ret;
553    if(self->strip_newlines) {
554        ret = PyString_FromStringAndSize(self->start, p - self->start);
555    } else {
556        if(p == self->end)
557            ret = PyString_FromStringAndSize(self->start, p - self->start);
558        else
559            ret = PyString_FromStringAndSize(self->start, p - self->start + 1);
560    }
561    if(p != self->end) {
562        p++;
563    }
564    self->start = p;
565    return ret;
566}
567
568static int
569pkgcore_readlines_set_mtime(pkgcore_readlines *self, PyObject *v,
570    void *closure)
571{
572    PyErr_SetString(PyExc_AttributeError, "mtime is immutable");
573    return -1;
574}
575
576static PyObject *
577pkgcore_readlines_get_mtime(pkgcore_readlines *self)
578{
579    PyObject *ret = PyObject_Call(pkgcore_stat_float_times,
580        pkgcore_empty_tuple, NULL);
581    if(!ret)
582        return NULL;
583    int is_float;
584    if(ret == Py_True) {
585        is_float = 1;
586    } else if (ret == Py_False) {
587        is_float = 0;
588    } else {
589        is_float = PyObject_IsTrue(ret);
590        if(is_float == -1) {
591            Py_DECREF(ret);
592            return NULL;
593        }
594    }
595    Py_DECREF(ret);
596    if(is_float)
597        return PyFloat_FromDouble(self->mtime + 1e-9 * self->mtime_nsec);
598#if SIZEOF_TIME_T > SIZEOF_LONG
599    return PyLong_FromLong((Py_LONG_LONG)self->mtime);
600#else
601    return PyInt_FromLong((long)self->mtime);
602#endif
603}
604
605static PyGetSetDef pkgcore_readlines_getsetters[] = {
606PKGCORE_GETSET(pkgcore_readlines, "mtime", mtime),
607    {NULL}
608};
609
610PyDoc_STRVAR(
611    pkgcore_readlines_documentation,
612    "readline(path [, strip_newlines [, swallow_missing [, none_on_missing]]])"
613    " -> iterable yielding"
614    " each line of a file\n\n"
615    "if strip_newlines is True, the trailing newline is stripped\n"
616    "if swallow_missing is True, for missing files it returns an empty "
617    "iterable\n"
618    "if none_on_missing and the file is missing, return None instead"
619    );
620
621
622static PyTypeObject pkgcore_readlines_type = {
623    PyObject_HEAD_INIT(NULL)
624    0,                                               /* ob_size*/
625    "pkgcore.util.osutils._posix.readlines",         /* tp_name*/
626    sizeof(pkgcore_readlines),                       /* tp_basicsize*/
627    0,                                               /* tp_itemsize*/
628    (destructor)pkgcore_readlines_dealloc,           /* tp_dealloc*/
629    0,                                               /* tp_print*/
630    0,                                               /* tp_getattr*/
631    0,                                               /* tp_setattr*/
632    0,                                               /* tp_compare*/
633    0,                                               /* tp_repr*/
634    0,                                               /* tp_as_number*/
635    0,                                               /* tp_as_sequence*/
636    0,                                               /* tp_as_mapping*/
637    0,                                               /* tp_hash */
638    (ternaryfunc)0,                                  /* tp_call*/
639    (reprfunc)0,                                     /* tp_str*/
640    0,                                               /* tp_getattro*/
641    0,                                               /* tp_setattro*/
642    0,                                               /* tp_as_buffer*/
643    Py_TPFLAGS_DEFAULT,                              /* tp_flags*/
644    pkgcore_readlines_documentation,                 /* tp_doc */
645    (traverseproc)0,                                 /* tp_traverse */
646    (inquiry)0,                                      /* tp_clear */
647    (richcmpfunc)0,                                  /* tp_richcompare */
648    0,                                               /* tp_weaklistoffset */
649    (getiterfunc)PyObject_SelfIter,                  /* tp_iter */
650    (iternextfunc)pkgcore_readlines_iternext,        /* tp_iternext */
651    0,                                               /* tp_methods */
652    0,                                               /* tp_members */
653    pkgcore_readlines_getsetters,                    /* tp_getset */
654    0,                                               /* tp_base */
655    0,                                               /* tp_dict */
656    0,                                               /* tp_descr_get */
657    0,                                               /* tp_descr_set */
658    0,                                               /* tp_dictoffset */
659    (initproc)0,                                     /* tp_init */
660    0,                                               /* tp_alloc */
661    pkgcore_readlines_new,                           /* tp_new */
662};
663
664static PyMethodDef pkgcore_posix_methods[] = {
665    {"normpath", (PyCFunction)pkgcore_normpath, METH_O,
666        "normalize a path entry"},
667    {"join", pkgcore_join, METH_VARARGS,
668        "join multiple path items"},
669    {"readfile", pkgcore_readfile, METH_VARARGS,
670        "fast read of a file: requires a string path, and an optional bool "
671        "indicating whether to swallow ENOENT; defaults to false"},
672    {NULL}
673};
674
675PyDoc_STRVAR(
676    pkgcore_posix_documentation,
677    "cpython posix path functionality");
678
679PyMODINIT_FUNC
680init_posix()
681{
682    PyObject *s = PyString_FromString("os");
683    if(!s)
684        return;
685
686    PyObject *mos = PyImport_Import(s);
687    Py_DECREF(s);
688    if(!mos)
689        return;
690    pkgcore_stat_float_times = PyObject_GetAttrString(mos, "stat_float_times");
691    Py_DECREF(mos);
692    if(!pkgcore_stat_float_times)
693        return;
694
695    pkgcore_empty_tuple = PyTuple_New(0);
696    if(!pkgcore_empty_tuple)
697        return;
698
699    PyObject *m = Py_InitModule3("_posix", pkgcore_posix_methods,
700                                 pkgcore_posix_documentation);
701    if (!m)
702        return;
703
704    if (PyType_Ready(&pkgcore_readlines_type) < 0)
705        return;
706
707    if (PyType_Ready(&pkgcore_readlines_empty_iter_type) < 0)
708        return;
709
710    Py_INCREF(&pkgcore_readlines_empty_iter_type);
711    pkgcore_readlines_empty_iter_singleton = _PyObject_New(
712        &pkgcore_readlines_empty_iter_type);
713
714
715    Py_INCREF(&pkgcore_readlines_type);
716    if (PyModule_AddObject(
717            m, "readlines", (PyObject *)&pkgcore_readlines_type) == -1)
718        return;
719
720    /* Success! */
721}
Note: See TracBrowser for help on using the browser.