root/releases/pkgcore/0.2.4/src/cpv.c @ ferringb%2540gmail.com-20070212033114-bnsgqv0v5n9y98kg

Revision ferringb%2540gmail.com-20070212033114-bnsgqv0v5n9y98kg, 26.6 KB (checked in by Brian Harring <ferringb@…>, 2 years ago)

bit of cleanup of the code; still needs a full walk since majority of it is quickies, but tiz a start.

Line 
1/*
2 * Copyright: 2006 Brian Harring <ferringb@gmail.com>
3 * License: GPL2
4 *
5 * C version of cpv class for 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 <string.h>
18
19
20// dev-util/diffball-cvs.2006.0_alpha1_alpha2
21// dev-util/diffball
22
23#define ISDIGIT(c) ('0' <= (c) && '9' >= (c))
24#define ISALPHA(c) (('a' <= (c) && 'z' >= (c)) || ('A' <= (c) && 'Z' >= (c)))
25#define ISLOWER(c) ('a' <= (c) && 'z' >= (c))
26#define ISALNUM(c) (ISALPHA(c) || ISDIGIT(c))
27
28typedef enum { SUF_ALPHA=0, SUF_BETA, SUF_PRE, SUF_RC, SUF_NORM, SUF_P }
29    version_suffixes;
30const char * const version_suffixes_str[] = \
31    {"alpha", "beta", "pre", "rc", "", "p", NULL};
32
33struct suffix_ver {
34    const char *str;
35    int str_len;
36    long val;
37};
38
39static struct suffix_ver pkgcore_ebuild_suffixes[] = {
40    {"alpha", 5, 0},
41    {"beta", 4, 1},
42    {"pre", 3, 2},
43    {"rc", 2, 3},
44    // note we skipped 4.  4 is the default.
45    {"p", 1, 5},
46    {NULL, 0, 6},
47};
48
49static const unsigned long pkgcore_ebuild_default_suffixes[] = {4, 0};
50#define PKGCORE_EBUILD_SUFFIX_DEFAULT_SUF 4
51#define PKGCORE_EBUILD_SUFFIX_DEFAULT_VAL 0
52
53typedef struct {
54    PyObject_HEAD
55    PyObject *category;
56    PyObject *package;
57    PyObject *key;
58    PyObject *fullver;
59    PyObject *version;
60    PyObject *revision;
61    unsigned long *suffixes;
62    long hash_val;
63    int cvs;
64} pkgcore_cpv;
65
66static PyObject *pkgcore_InvalidCPV_Exc = NULL;
67
68
69static int
70pkgcore_cpv_set_cpvstr(pkgcore_cpv *self, PyObject *v, void *closure)
71{
72    PyErr_SetString(PyExc_AttributeError, "cpvstr is immutable");
73    return -1;
74}
75
76static PyObject *
77pkgcore_cpv_get_cpvstr(pkgcore_cpv *self, void *closure)
78{
79    if (!self->category || !self->package) {
80        Py_RETURN_NONE;
81    }
82    if (!self->fullver) {
83        return PyString_FromFormat("%s/%s",
84            PyString_AsString(self->category),
85            PyString_AsString(self->package));
86    }
87    return PyString_FromFormat("%s/%s-%s",
88        PyString_AsString(self->category),
89        PyString_AsString(self->package),
90        PyString_AsString(self->fullver));
91}
92
93
94static PyGetSetDef pkgcore_cpv_getsetters[] = {
95PKGCORE_GETSET(pkgcore_cpv, "cpvstr", cpvstr),
96    {NULL}
97};
98
99static PyMemberDef pkgcore_cpv_members[] = {
100    {"category", T_OBJECT, offsetof(pkgcore_cpv, category), READONLY},
101    {"package", T_OBJECT, offsetof(pkgcore_cpv, package), READONLY},
102    {"key", T_OBJECT, offsetof(pkgcore_cpv, key), READONLY},
103    {"fullver", T_OBJECT, offsetof(pkgcore_cpv, fullver), READONLY},
104    {"version", T_OBJECT, offsetof(pkgcore_cpv, version), READONLY},
105    {"revision", T_OBJECT, offsetof(pkgcore_cpv, revision), READONLY},
106    {NULL}
107};
108
109static char *
110pkgcore_cpv_parse_category(const char *start, int null_is_end)
111{
112    char *p = (char *)start;
113    if(NULL == start)
114        return NULL;
115    if(!null_is_end) {
116        char *end = NULL;
117        /* first char must be alnum, after that it's opened up. */
118        while('\0' != *p) {
119            if(!ISALNUM(*p))
120                return NULL;
121            p++;
122            while(ISALNUM(*p) || '+' == *p || '-' == *p || '.' == *p \
123                || '_' == *p)
124                p++;
125            if('/' == *p) {
126                end = p;
127                p++;
128                if('/' == *p)
129                    return NULL;
130            } else {
131                break;
132            }
133        }
134        if(end) {
135            p = end;
136        } else if (!end) {
137            // no '/', must be '\0'
138            if('\0' != *p)
139                return NULL;
140       }
141    } else {
142        for (;;) {
143            if(!ISALNUM(*p))
144                return NULL;
145            p++;
146            while('\0' != *p && (ISALNUM(*p) || '+' == *p || '-' == *p \
147                || '.' == *p || '_' == *p))
148                p++;
149            if('/' == *p) {
150                p++;
151                if('/' == *p)
152                    return NULL;
153            } else if('\0' == *p)
154                break;
155            else
156                return NULL;
157        }
158    }
159    if(p == start)
160       return NULL;
161    return p;
162}
163
164
165static char *
166pkgcore_cpv_parse_package(const char *start)
167{
168    // yay- need to eat the pkg next
169    // allowed [a-zA-Z0-9](?:[-_+a-zA-Z0-9]*?[+a-zA-Z0-9])??)
170    // ver-  "(?:-(?P<fullver>(?P<version>(?:cvs\\.)?(?:\\d+)(?:\\.\\d+)*[a-z]?(?:_(p(?:re)?|beta|alpha|rc)\\d*)*)" +
171    // "(?:-r(?P<revision>\\d+))?))?$")
172    // note that pkg regex is non-greedy.
173    char *p = (char *)start;
174    char *ver_start;
175    if(NULL == start)
176        return NULL;
177    start = p;
178    p = strchr(start, '-');
179    while(NULL != p) {
180        ++p;
181        ver_start = p;
182        if('\0' == *p)
183             return NULL;
184        if(0 == strncmp(p, "cvs.", 4)) {
185            // we've got it.
186            break;
187        }
188        while(ISDIGIT(*p))
189            p++;
190        if(p == ver_start) {
191            p = strchr(ver_start + 1, '-');
192            continue;
193        }
194        if('\0' == *p)
195            break;
196           
197        // ok.  so, either it's a period, _, or a *single* [a-z].
198        if('\0' == *p || '.' == *p || '_' == *p || '-' == *p) {
199            break;
200        } else if(ISLOWER(*p)) {
201            p++;
202            if('\0' == *p || '.' == *p || '_' == *p || '-' == *p)
203                break;
204        }
205        p = strchr(p, '-');
206    }
207    // do verification of pkg for *both* branches
208    if (!p) {
209        // no pkg detected, find end, verification happens outside
210        // the block
211        p = (char *)start;
212        while('\0' != *p)
213            p++;
214        ver_start = p;
215    }
216    if('\0' != *ver_start)
217        ver_start--;
218    return ver_start;
219}
220
221
222static int
223pkgcore_cpv_parse_version(pkgcore_cpv *self, char *ver_start,
224    char **ver_end)
225{
226    // version parsing.
227    // "(?:-(?P<fullver>(?P<version>(?:cvs\\.)?(?:\\d+)(?:\\.\\d+)*[a-z]?(?:_(p(?:re)?|beta|alpha|rc)\\d*)*)" +
228    // "(?:-r(?P<revision>\\d+))?))?$")
229    char *p = ver_start;
230
231    // suffixes _have_ to have versions; do it now to avoid
232    if('_' == *p)
233        return 1;
234
235    // grab cvs chunk
236    if(0 == strncmp(ver_start, "cvs.", 4)) {
237        self->cvs = 1;
238        p += 4;
239        if('\0' == *p)
240            return 1;
241    }
242    // (\d+)(\.\d+)*[a-z]?
243    for(;;) {
244        while(ISDIGIT(*p))
245            p++;
246        // safe due to our checks from above, but just in case...
247        if(ver_start == p || '.' == p[-1]) {
248            return 1;
249        }
250        if(ISALPHA(*p)) {
251            p++;
252            if('\0' != *p && '_' != *p && '-' != *p)
253                return 1;
254            break;
255        } else if('.' == *p) {
256            p++;
257        } else if('\0' == *p || '_' == *p || '-' == *p) {
258            break;
259        } else {
260            return 1;
261        }
262    }
263    if('_' == *p) {
264        // suffixes.  yay.
265        char *orig_p = (char *)p;
266        unsigned int suffix_count = 0;
267        unsigned int pos;
268        unsigned new_long;
269        struct suffix_ver *sv;
270        do {
271            suffix_count++;
272            p = strchr(p + 1, '_');
273        } while(NULL != p);
274       
275        // trailing is 0 0
276       
277        p = orig_p;
278        self->suffixes = PyObject_Malloc(sizeof(long) * (suffix_count + 1) * 2);
279        if(NULL == self->suffixes) {
280            // wanker.
281            PyErr_NoMemory();
282            return -2;
283        }
284        suffix_count *= 2;
285        for(pos = 0; pos < suffix_count; pos += 2) {
286            p += 1; // skip the leading _
287            if('\0' == *p)
288                return 1;
289            for(sv = pkgcore_ebuild_suffixes; NULL != sv->str; sv++) {
290                if(0 == strncmp(p, sv->str, sv->str_len)) {
291                    self->suffixes[pos] = sv->val;
292                    p += sv->str_len;
293                    new_long = 0;
294                    while(ISDIGIT(*p)) {
295                        new_long = (new_long * 10) + *p - '0';
296                        p++;
297                    }
298                    if('\0' != *p && '_' != *p && '-'  != *p)
299                        return 1;
300                    self->suffixes[pos + 1] = new_long;
301                    break;
302                }
303            }
304            if(NULL == sv->str) {
305                // that means it didn't find the suffix.
306                return 1;
307            }
308        }
309        self->suffixes[pos] = PKGCORE_EBUILD_SUFFIX_DEFAULT_SUF;
310        self->suffixes[pos + 1] = PKGCORE_EBUILD_SUFFIX_DEFAULT_VAL;
311    } else {
312        self->suffixes = (unsigned long *)pkgcore_ebuild_default_suffixes;
313    }
314    *ver_end = p;
315    return 0;
316}
317
318static int
319pkgcore_cpv_init(pkgcore_cpv *self, PyObject *args, PyObject *kwds)
320{
321    int result = 0;
322    char *ver_end = NULL;
323    char *p = NULL, *s1 = NULL, *s2 = NULL;
324    char *cpv_char = NULL;
325    char *cpv_pos = NULL;
326    PyObject *tmp = NULL, *tmp2 = NULL, *cpvstr = NULL, *category = NULL,
327        *package = NULL, *fullver = NULL;
328
329    if(!PyArg_UnpackTuple(args, "CPV", 1, 3, &category, &package, &fullver))
330        return -1;
331
332    if(kwds && PyObject_IsTrue(kwds)) {
333        PyErr_SetString(PyExc_TypeError,
334            "cpv accepts either 1 arg (cpvstr), or 3 (category, package, "
335            "version); all must be strings, and no keywords accepted; got kwds");
336        goto cleanup;
337    }
338
339    if(package) {
340        if(!fullver || !PyString_CheckExact(category) ||
341            !PyString_CheckExact(package) || !PyString_CheckExact(fullver)) {
342            PyObject *err_msg = PyString_FromString(
343                "cpv accepts either 1 arg (cpvstr), or 3 (category, package, "
344                "version); all must be strings: got %r");
345            if(err_msg) {
346                PyObject *new_args = PyTuple_Pack(1, args);
347                if(new_args) {
348                    PyObject *s = PyString_Format(err_msg, new_args);
349                    if(s) {
350                        PyErr_SetString(PyExc_TypeError, PyString_AsString(s));
351                        Py_CLEAR(s);
352                    }
353                    Py_CLEAR(new_args);
354                }
355                Py_CLEAR(err_msg);
356            }
357            goto cleanup;
358        }
359    } else {
360        if (!PyString_CheckExact(category)) {
361            PyObject *err_msg = PyString_FromString(
362                "cpv accepts either 1 arg (cpvstr), or 3 (category, package, "
363                "version); all must be strings: got extra arg %r");
364            if(err_msg) {
365                PyObject *new_args = PyTuple_Pack(1, args);
366                if(new_args) {
367                    PyObject *s = PyString_Format(err_msg, new_args);
368                    if(s) {
369                        PyErr_SetString(PyExc_TypeError, PyString_AsString(s));
370                        Py_CLEAR(s);
371                    }
372                    Py_CLEAR(new_args);
373                }
374                Py_CLEAR(err_msg);
375            }
376            goto cleanup;
377        }
378        cpvstr = category;
379        category = NULL;
380    }
381
382    self->hash_val = -1;
383
384    if(!category) {
385        cpv_char = PyString_AsString(cpvstr);
386        cpv_pos = pkgcore_cpv_parse_category(cpv_char, 0);
387        if(!cpv_pos || '/' != *cpv_pos)
388            goto parse_error;
389        category = PyString_FromStringAndSize(cpv_char, cpv_pos - cpv_char);
390        if(!category)
391            goto cleanup;
392        cpv_pos++;
393
394    } else {
395        p = PyString_AsString(category);
396        p = pkgcore_cpv_parse_category(p, 1);
397        if(!p || '\0' != *p)
398            goto parse_error;
399        Py_INCREF(category);
400    }
401    tmp = self->category;
402    self->category = category;
403    Py_XDECREF(tmp);
404
405    if(!package) {
406        p = pkgcore_cpv_parse_package(cpv_pos);
407        if(!p || ('\0' != *p && '-' != *p))
408            goto parse_error;
409        if(NULL == (package = PyString_FromStringAndSize(cpv_pos, p - cpv_pos)))
410            goto cleanup;
411        cpv_pos = p;
412    } else {
413        p = pkgcore_cpv_parse_package(PyString_AsString(package));
414        if(!p || '\0' != *p)
415            goto parse_error;
416        Py_INCREF(package);
417    }
418    tmp = self->package;
419    self->package = package;
420    Py_XDECREF(tmp);
421
422    // package verification
423    s1 = PyString_AsString(self->package);
424    if(!s1)
425        goto parse_error;
426    s2 = s1;
427    if(!ISALNUM(*s2))
428        goto parse_error;
429    s2++;
430    while (ISALNUM(*s2) || '_' == *s2 || '+' == *s2)
431        s2++;
432    while('-' == *s2) {
433        s2++;
434        if('\0' == *s2)
435            goto parse_error;
436        if(ISDIGIT(*s2)) {
437            s2++;
438            while(ISDIGIT(*s2))
439                s2++;
440            if(!ISALPHA(*s2) && '+' != *s2)
441                goto parse_error;
442            s2++;
443            if(!ISALPHA(*s2) && '+' != *s2)
444                goto parse_error;
445            while(ISALNUM(*s2) || '+' == *s2 || '_' == *s2)
446                s2++;
447        } else if(ISALPHA(*s2) || '+' == *s2) {
448            s2++;
449            while(ISALNUM(*s2) || '+' == *s2 || '_' == *s2)
450                s2++;
451        } else {
452            goto parse_error;
453        }
454    }
455    if('\0' != *s2)
456        goto parse_error;       
457
458    if(!fullver) {
459        if('\0' != *p)
460            cpv_pos++;
461        p = cpv_pos;
462    } else {
463        p = PyString_AsString(fullver);
464        if(!p)
465            goto cleanup;
466    }
467    if('\0' != *p) {
468        result = pkgcore_cpv_parse_version(self, p, &ver_end);
469        if(result < 0)
470            goto cleanup;
471        else if(result > 0)
472            goto parse_error;
473        // doesn't look right.
474        if('\0' == *ver_end) {
475            if(fullver) {
476                // no rev; set version to fullver
477                Py_INCREF(fullver);
478            } else {
479                if(NULL ==
480                    (fullver = PyString_FromStringAndSize(cpv_pos, ver_end - p)))
481                    goto cleanup;
482            }
483            tmp = self->version;
484            self->version = fullver;
485            Py_XDECREF(tmp);
486            Py_CLEAR(self->revision);
487            Py_INCREF(fullver);
488        } else if('-' == *ver_end) {
489            if(NULL == (tmp = PyString_FromStringAndSize(p, ver_end - p)))
490                goto cleanup;
491            tmp2 = self->version;
492            self->version = tmp;
493            Py_XDECREF(tmp2);
494            unsigned long revision = 0;
495            // ok, revision.
496            p = ver_end;
497            p++;
498            if('r' != *p)
499                goto parse_error;
500            p++;
501            while(ISDIGIT(*p)) {
502                revision = (revision * 10) + *p - '0';
503                p++;
504            }
505            if('\0' != *p || 'r' == p[-1])
506                goto parse_error;
507            tmp = PyInt_FromLong(revision);
508            if(!tmp) {
509                result = -1;
510                goto cleanup;
511            }
512            tmp2 = self->revision;
513            self->revision = tmp;
514            Py_XDECREF(tmp2);
515            if(!fullver) {
516                if(NULL == (fullver = PyString_FromStringAndSize(cpv_pos,
517                    p - cpv_pos)))
518                    goto cleanup;
519            } else {
520                Py_INCREF(fullver);
521            }
522        } else {
523            goto parse_error;
524        }
525        tmp = self->fullver;
526        self->fullver = fullver;
527        Py_XDECREF(tmp);
528    } else {
529        Py_CLEAR(self->fullver);
530        Py_CLEAR(self->version);
531        Py_CLEAR(self->revision);
532    }
533
534
535    // by now, category, package, version, revision, and fullver should
536    // be initialized.  key, and cpvstr now.
537
538    tmp = NULL;
539    if(cpvstr) {
540        self->hash_val = PyObject_Hash(cpvstr);
541        if(self->hash_val == -1)
542            goto cleanup;
543        if(!self->fullver) {
544            Py_INCREF(cpvstr);
545            tmp = cpvstr;
546        }
547    }
548    if(!tmp) {
549        tmp = PyString_FromFormat("%s/%s", PyString_AsString(self->category),
550            PyString_AsString(self->package));
551        if(!tmp)
552            goto cleanup;
553    }
554    tmp2 = self->key;
555    self->key = tmp;
556    Py_XDECREF(tmp2);
557    return 0;
558
559parse_error:
560    // yay.  well, set an exception.
561    // if an error from trying to call, let it propagate.  meanwhile, we
562    // cleanup our own
563    if(!cpvstr) {
564        if(PySequence_Length(fullver) != 0) {
565            cpvstr = PyString_FromFormat("%s/%s-%s", PyString_AsString(category),
566                PyString_AsString(package), PyString_AsString(fullver));
567        } else {
568            cpvstr = PyString_FromFormat("%s/%s", PyString_AsString(category),
569                PyString_AsString(package));
570        }
571        if(!cpvstr)
572            goto cleanup;
573    }
574    tmp = PyObject_CallFunction(pkgcore_InvalidCPV_Exc, "O", cpvstr);
575    if(NULL != tmp) {
576        PyErr_SetObject(pkgcore_InvalidCPV_Exc, tmp);
577        Py_DECREF(tmp);
578    }
579cleanup:
580
581    Py_CLEAR(self->category);
582    Py_CLEAR(self->package);
583    Py_CLEAR(self->key);
584    Py_CLEAR(self->version);
585    Py_CLEAR(self->revision);
586    Py_CLEAR(self->fullver);
587
588    if(NULL != self->suffixes) {
589        // if we're not using the communal val...
590        if(PKGCORE_EBUILD_SUFFIX_DEFAULT_SUF != self->suffixes[0]) {
591            PyObject_Free(self->suffixes);
592        }
593        self->suffixes = NULL;
594    }
595    return -1;
596}
597
598
599static void
600pkgcore_cpv_dealloc(pkgcore_cpv *self)
601{
602    Py_CLEAR(self->category);
603    Py_CLEAR(self->package);
604    Py_CLEAR(self->key);
605    Py_CLEAR(self->version);
606    Py_CLEAR(self->revision);
607    Py_CLEAR(self->fullver);
608
609    if(NULL != self->suffixes) {
610        if(PKGCORE_EBUILD_SUFFIX_DEFAULT_SUF != self->suffixes[0]) {
611            PyObject_Free(self->suffixes);
612        }
613        self->suffixes = NULL;
614    }
615    self->ob_type->tp_free((PyObject *)self);
616}
617
618
619static int
620pkgcore_nullsafe_compare(PyObject *this, PyObject *other)
621{
622    if ((this == NULL || this == Py_None) &&
623        (other == NULL || other == Py_None)) {
624        return 0;
625    }
626    if (this == NULL || this == Py_None) {
627        return -1;
628    }
629    if (other == NULL || other == Py_None) {
630        return +1;
631    }
632    return PyObject_Compare(this, other);
633}
634
635
636static int
637pkgcore_cpv_compare(pkgcore_cpv *self, pkgcore_cpv *other)
638{
639    int c;
640    c = pkgcore_nullsafe_compare(self->category, other->category);
641    if(PyErr_Occurred())
642        return -1;
643    if(c != 0)
644        return c;
645    c = pkgcore_nullsafe_compare(self->package, other->package);
646    if(PyErr_Occurred())
647        return -1;
648    if(c != 0)
649        return c;
650    if(self->version == NULL)
651        return other->version == NULL ? 0 : -1;
652    if(other->version == NULL)
653        return 1;
654
655    if(self->cvs != other->cvs)
656        return self->cvs ? +1 : -1;
657
658    char *s1, *o1;
659    s1 = PyString_AsString(self->version);
660    if(!s1)
661        return -1;
662    o1 = PyString_AsString(other->version);
663    if (!o1)
664        return -1;
665
666    if(self->cvs) {
667        s1 += 4; // "cvs."
668        o1 += 4;
669    }
670    while('_' != *s1 && '\0' != *s1 && '_' != *o1 && '\0' != *o1) {
671        if('0' == *s1 || '0' == *o1) {
672            // float comparison rules.
673            do {
674                if(*s1 > *o1)
675                    return 1;
676                else if (*s1 < *o1)
677                    return -1;
678                s1++; o1++;
679            } while (ISDIGIT(*s1) && ISDIGIT(*o1));
680
681            while(ISDIGIT(*s1)) {
682                if('0' != *s1)
683                    return +1;
684                s1++;
685            }
686            while(ISDIGIT(*o1)) {
687                if('0' != *o1)
688                    return -1;
689                o1++;
690            }
691        } else {
692            // int comparison rules.
693            char *s_start = s1, *o_start = o1;
694
695            while(ISDIGIT(*s1))
696                s1++;
697            while(ISDIGIT(*o1))
698                o1++;
699
700            if((s1 - s_start) < (o1 - o_start))
701                return -1;
702            else if((s1 - s_start) > (o1 - o_start))
703                return 1;
704           
705            char *s_end = s1;
706
707            for(s1 = s_start, o1 = o_start; s1 != s_end; s1++, o1++) {
708                if(*s1 < *o1)
709                    return -1;
710                else if (*s1 > *o1)
711                    return 1;
712            }
713        }
714        if(ISALPHA(*s1)) {
715            if(ISALPHA(*o1)) {
716                if(*s1 < *o1)
717                    return -1;
718                else if(*s1 > *o1)
719                    return 1;
720                o1++;
721            } else
722                return 1;
723            s1++;
724        } else if ISALPHA(*o1) {
725            return -1;
726        }
727        if('.' == *s1)
728            s1++;
729        if('.' == *o1)
730            o1++;
731        // hokay.  no resolution there.
732    }
733    // ok.  one of the two just ran out of vers; test on suffixes
734    if(ISDIGIT(*s1)) {
735        return +1;
736    } else if(ISDIGIT(*o1)) {
737        return -1;
738    }
739    // bugger.  exact same version string up to suffix pt.
740    int x;
741    for(x=0;;) {
742        // cmp suffix type.
743        if(self->suffixes[x] < other->suffixes[x])
744            return -1;
745        else if(self->suffixes[x] > other->suffixes[x])
746            return +1;
747        else if(PKGCORE_EBUILD_SUFFIX_DEFAULT_SUF == self->suffixes[x]) {
748            // terminator.  one remaining element, but little point in testing
749            // it.  to have hit here requires them to be the same also (for
750            // those wondering why we're not testing)
751            break;
752        }
753        x++;
754        // cmp suffix val
755        if(self->suffixes[x] < other->suffixes[x])
756            return -1;
757        else if(self->suffixes[x] > other->suffixes[x])
758            return +1;
759        x++;
760    }
761    // all that remains is revision.
762    return pkgcore_nullsafe_compare(self->revision, other->revision);
763}
764   
765
766
767static long
768pkgcore_cpv_hash(pkgcore_cpv *self)
769{
770    if (self->hash_val == -1) {
771        PyObject *s = PyObject_GetAttrString((PyObject *)self, "cpvstr");
772        if(!s)
773            return -1;
774        self->hash_val = PyObject_Hash(s);
775        Py_DECREF(s);
776    }
777    return self->hash_val;
778}
779
780
781static PyObject *
782pkgcore_cpv_str(pkgcore_cpv *self)
783{
784    PyObject *s = PyObject_GetAttrString((PyObject *)self, "cpvstr");
785    if(!s)
786        return NULL;
787    if(s != Py_None) {
788        return s;
789    }
790    PyObject *s2 = PyObject_Str(s);
791    Py_DECREF(s);
792    return s2;
793}
794
795
796static PyObject *
797pkgcore_cpv_repr(pkgcore_cpv *self)
798{
799    PyObject *s, *cpv;
800    cpv = PyObject_GetAttrString((PyObject *)self, "cpvstr");
801    if(!cpv)
802        return NULL;
803    s = PyObject_Repr(cpv);
804    Py_DECREF(cpv);
805    if(!s)
806        return NULL;
807    char *str = PyString_AsString(s);
808    if(!s) {
809        Py_DECREF(s);
810        return NULL;
811    }
812    PyObject *s2 = PyString_FromFormat("CPV(%s)", str);
813    Py_DECREF(s);
814    return s2;
815}
816       
817static PyTypeObject pkgcore_cpvType = {
818    PyObject_HEAD_INIT(NULL)
819    0,                                /* ob_size */
820    "CPV",
821    sizeof(pkgcore_cpv),              /* tp_basicsize */
822    0,                            /* tp_itemsize */
823    (destructor)pkgcore_cpv_dealloc,  /* tp_dealloc */
824    0,                                /* tp_print */
825    0,                                /* tp_getattr */
826    0,                                /* tp_setattr */
827    (cmpfunc)pkgcore_cpv_compare,     /* tp_compare */
828    (reprfunc)pkgcore_cpv_repr,       /* tp_repr */
829    0,                                /* tp_as_number */
830    0,                                /* tp_as_sequence */
831    0,                                /* tp_as_mapping */
832    (hashfunc)pkgcore_cpv_hash,       /* tp_hash */
833    0,                                /* tp_call */
834    (reprfunc)pkgcore_cpv_str,        /* tp_str */
835    0,                                /* tp_getattro */
836    0,                                /* tp_setattro */
837    0,                                /* tp_as_buffer */
838    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
839    0,                                /* tp_doc */
840    0,                                /* tp_traverse */
841    0,                                /* tp_clear */
842    0,                                /* tp_richcompare */
843    0,                                /* tp_weaklistoffset */
844    0,                                /* tp_iter */
845    0,                                /* tp_iternext */
846    0,                                /* tp_methods */
847    pkgcore_cpv_members,              /* tp_members */
848    pkgcore_cpv_getsetters,           /* tp_getset */
849    0,                                /* tp_base */
850    0,                                /* tp_dict */
851    0,                                /* tp_descr_get */
852    0,                                /* tp_descr_set */
853    0,                                /* tp_dictoffset */
854    (initproc)pkgcore_cpv_init,       /* tp_init */
855    0,                                /* tp_alloc */
856    PyType_GenericNew,                /* tp_new */
857};
858
859PyDoc_STRVAR(
860    pkgcore_cpv_documentation,
861    "C reimplementation of pkgcore.ebuild.cpv.");
862
863/* Copied from stdtypes.c in guppy */
864#define VISIT(SLOT) \
865    if (SLOT) { \
866        err = visit((PyObject *)(SLOT), arg); \
867        if (err) \
868            return err; \
869    }
870
871#define ATTR(name) \
872    if ((PyObject *)v->name == r->tgt &&                                \
873        (r->visit(NYHR_ATTRIBUTE, PyString_FromString(#name), r)))      \
874        return 1;
875
876static int
877pkgcore_cpv_heapytraverse(NyHeapTraverse* traverse)
878{
879    pkgcore_cpv *cpv = (pkgcore_cpv*)traverse->obj;
880    void *arg = traverse->arg;
881    visitproc visit = traverse->visit;
882    int err;
883    VISIT(cpv->category);
884    VISIT(cpv->package);
885    VISIT(cpv->key);
886    VISIT(cpv->fullver);
887    VISIT(cpv->version);
888    VISIT(cpv->revision);
889    return 0;
890}
891
892static int
893pkgcore_cpv_heapyrelate(NyHeapRelate *r)
894{
895    pkgcore_cpv *v = (pkgcore_cpv*)r->src;
896    ATTR(category);
897    ATTR(package);
898    ATTR(key);
899    ATTR(fullver);
900    ATTR(version);
901    ATTR(revision);
902    return 0;
903}
904
905static NyHeapDef pkgcore_cpv_heapdefs[] = {
906    {
907        0,                            /* flags */
908        &pkgcore_cpvType,             /* type */
909        0,                            /* size */
910        pkgcore_cpv_heapytraverse,    /* traverse */
911        pkgcore_cpv_heapyrelate       /* relate */
912    },
913    {0}
914};
915
916
917PyMODINIT_FUNC
918init_cpv(void)
919{
920    PyObject *m, *s, *errors;
921
922    m = Py_InitModule3("_cpv", NULL, pkgcore_cpv_documentation);
923    if (!m)
924        return;
925
926    // this may be redundant; do this so __builtins__["__import__"] is used.
927    s = PyString_FromString("pkgcore.ebuild.errors");
928    if (!s)
929        return;
930
931    errors = PyImport_Import(s);
932    Py_DECREF(s);
933    if (!errors)
934        return;
935
936    pkgcore_InvalidCPV_Exc = PyObject_GetAttrString(errors, "InvalidCPV");
937    if (!pkgcore_InvalidCPV_Exc)
938        return;
939
940    pkgcore_cpvType.ob_type = &PyType_Type;
941
942    if (PyType_Ready(&pkgcore_cpvType) < 0)
943        return;
944
945    Py_INCREF(&pkgcore_cpvType);
946    if (PyModule_AddObject(m, "CPV", (PyObject *)&pkgcore_cpvType) == -1)
947        return;
948
949    PyObject *cobject = PyCObject_FromVoidPtrAndDesc(
950        &pkgcore_cpv_heapdefs, "NyHeapDef[] v1.0", 0);
951    if (!cobject)
952        return;
953
954    if (PyModule_AddObject(m, "_NyHeapDefs_", cobject) == -1)
955        return;
956
957    /* Success! */
958}
Note: See TracBrowser for help on using the browser.