summaryrefslogtreecommitdiff
path: root/portato/eix/parser.pyx
blob: d72e052d7bdfb8fa265b909dd70df7d88801f210 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
# -*- coding: utf-8 -*-
#
# File: portato/eix/parser.pyx
# This file is part of the Portato-Project, a graphical portage-frontend.
#
# Copyright (C) 2006-2010 René 'Necoro' Neumann
# This is free software.  You may redistribute copies of it under the terms of
# the GNU General Public License version 2.
# There is NO WARRANTY, to the extent permitted by law.
#
# Written by René 'Necoro' Neumann <necoro@necoro.net>

"""
The cache file supports different types of data.
In this module (nearly) all of these types have a corresponding function.

For the exact way all the functions work, have a look at the eix format description.
"""

__docformat__ = "restructuredtext"

cdef extern from "stdio.h":
    ctypedef struct FILE

    int fgetc(FILE* stream)
    long ftell(FILE* stream)
    int fseek(FILE* stream, long offset, int whence)
    int fread(void* ptr, int size, int n, FILE* stream)
    
    int EOF
    int SEEK_CUR

cdef extern from "Python.h":
    FILE* PyFile_AsFile(object)

ctypedef unsigned char UChar
ctypedef long long LLong

from cpython.unicode cimport PyUnicode_DecodeUTF8
from cpython.mem cimport PyMem_Malloc, PyMem_Free
from cpython.exc cimport PyErr_NoMemory
from cpython.string cimport PyString_FromStringAndSize

from portato.eix.exceptions import EndOfFileException

#
# Helper
#

cdef int _get_byte (FILE* file) except -1:
    cdef int c = fgetc(file)

    if c == EOF:
        raise EndOfFileException

    return c

#
# Base Types
#

cpdef LLong number (object pfile):
    """
    Returns a number.

    :param file: The file to read from
    :type file: file
    :rtype: int
    """
    
    cdef UChar n
    cdef LLong value
    cdef int i
    
    cdef unsigned short count = 1
    cdef FILE* file = PyFile_AsFile(pfile)

    n = <UChar>_get_byte(file)

    if n < 0xFF:
        return <LLong>n
    else:

        n = <UChar>_get_byte(file)
        while (n == 0xFF):
            count += 1
            n = <UChar>_get_byte(file)

        if n == 0:
            value = <LLong>0xFF # 0xFF is encoded as 0xFF 0x00
            count -= 1
        else:
            value = <LLong>n
        
        for i in range(count):
            value = (value << 8) | <LLong>(_get_byte(file))
        
    return value

cpdef object vector (object file, object get_type, object nelems = None):
    """
    Returns a vector of elements.

    :Parameters:

        file : file
            The file to read from.

        get_type : function(file, bool)
            The function determining type of the elements.

        nelems : int
            Normally the eix-Vector has the number of elements as the first argument.
            If for some reason this is not the case, you can pass it in here.

    :rtype: list
    """

    cdef LLong n
    cdef LLong i

    if nelems is None:
        n = number(file)
    else:
        n = nelems
    
    return [get_type(file) for i in range(n)]

cpdef object string (object pfile, bint unicode = False):
    """
    Returns a string.

    :param pfile: The file to read from
    :type pfile: file
    :param unicode: Return unicode
    :type unicode: bool
    :rtype: str or unicode
    """
    cdef LLong nelems = number(pfile)
    cdef FILE* file = PyFile_AsFile(pfile)
    cdef char* s

    s = <char*>PyMem_Malloc((nelems + 1) * sizeof(char))

    if s is NULL:
        PyErr_NoMemory()

    try:
        if fread(s, sizeof(char), nelems, file) < nelems:
            raise EndOfFileException, pfile.name

        s[nelems] = '\0'

        if unicode:
            return PyUnicode_DecodeUTF8(s, nelems, 'replace')
        else: # simple string, implicitly copied
            return PyString_FromStringAndSize(s, nelems)

    finally:
        PyMem_Free(s)

#
# Complex Types
#

cdef class overlay:
    """
    Represents an overlay object.

    :IVariables:

        path : string
            The path to the overlay

        label : string
            The label/name of the overlay
    """

    cdef readonly object path
    cdef readonly object label

    def __init__ (self, file):
        """
        :param file: The file to read from
        :type file: file
        """
        
        self.path = string(file)
        self.label = string(file)

cdef class header:
    """
    Represents the header of the cache.

    :IVariables:

        version : int
            The version of the cache file.

        ncats : int
            The number of categories.

        overlays : `overlay` []
            The list of overlays.

        provide : string[]
            A list of "PROVIDE" values.

        licenses : string[]
            The list of licenses.

        keywords : string[]
            The list of keywords.
        
        useflags : string[]
            The list of useflags.
        
        slots : string[]
            The list of slots different from "0".

        sets : string[]
            The names of world sets are the names (without leading @) of the world sets stored in /var/lib/portage/world_sets.
            If SAVE_WORLD=false, the list is empty.
    """
    
    cdef readonly object version
    cdef readonly object ncats
    cdef readonly object overlays
    cdef readonly object provide
    cdef readonly object licenses
    cdef readonly object keywords
    cdef readonly object useflags
    cdef readonly object slots
    cdef readonly object sets

    def __init__ (self, file):
        """
        :param file: The file to read from
        :type file: file
        """
        self.version = number(file)
        self.ncats = number(file)
        self.overlays = vector(file, overlay)
        self.provide = vector(file, string)
        self.licenses = vector(file, string)
        self.keywords = vector(file, string)
        self.useflags = vector(file, string)
        self.slots = vector(file, string)
        self.sets = vector(file, string)

cdef class package:
    """
    The representation of one package.

    Currently, version information is not parsed and stored.
    So you can gain general infos only.

    :IVariables:
        
        name : string
            The name of the package.

        description : string
            Description of the package.

        homepage : string
            The homepage of the package.

        provide : int[]
            The indices of `header.provide` representing the PROVIDE value of the package.

        license : int
            The index of `header.licenses` representing the license of the package.

        useflags : int[]
            The indices of `header.useflags` representing the IUSE value of the package.
    """

    cdef LLong _offset
    cdef readonly object name
    cdef readonly object description
    #cdef readonly object provide
    #cdef readonly object homepage
    #cdef readonly object license
    #cdef readonly object useflags

    def __init__ (self, file):
        """
        :param file: The file to read from
        :type file: file
        """
        cdef FILE* cfile = PyFile_AsFile(file)
        cdef long after_offset
        
        self._offset = number(file)
        
        after_offset = ftell(cfile)
        
        self.name = string(file)
        self.description = string(file, True)

        # skip the rest, as it is currently unneeded
        #self.provide = vector(file, number)
        #self.homepage = string(file)
        #self.license = number(file)
        #self.useflags = vector(file, number)
        
        # self.versions = LE(typed_vector(version))
        # for the moment just skip the versions
        fseek(cfile, self._offset - (ftell(cfile) - after_offset), SEEK_CUR)

cdef class category:
    """
    Represents a whole category.

    :IVariables:

        name : string
            The category name.

        packages : `package` []
            All the packages of the category.
    """
    
    cdef readonly object name
    cdef readonly object packages

    def __init__ (self, file):
        """
        :param file: The file to read from
        :type file: file
        """
        self.name = string(file)
        self.packages = vector(file, package)