Added some code to peer into a data structure in Maemian/Schedule.pm. Also added the
[maemian] / nokia-lintian / depcheck / package.py
1 # Copyright (C) 1998 Richard Braakman
2 #
3 # This program is free software.  It is distributed under the terms of
4 # the GNU General Public License as published by the Free Software
5 # Foundation; either version 2 of the License, or (at your option) any
6 # later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program.  If not, you can find it on the World Wide
15 # Web at http://www.gnu.org/copyleft/gpl.html, or write to the Free
16 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
17 # MA 02110-1301, USA.
18
19 import string
20 import regex
21
22 import version
23 import relation
24
25 bad_field = 'Bad field value'
26
27 defaults = {'depends': [], 'recommends': [], 'suggests': [], 'pre-depends': [],
28             'conflicts': [], 'replaces': [], 'provides': [],
29             'essential': 0, 'distribution': 'main', 'architecture': 'all',
30             'description': '', 'synopsis': ''}
31
32 relationships = ['depends', 'recommends', 'suggests', 'pre-depends',
33                  'conflicts', 'replaces']
34
35 # The Package class models a read-only dictionary that is initialized
36 # by feeding it a paragraph of control information.
37 # Some translation is done on the field names:
38 # 'package'     ->  'name'
39 # 'source'      ->  'sourcepackage' and 'sourceversion'
40 # 'description' ->  'synopsis' and 'description'
41 # 'section'     ->  'distribution' and 'section'
42 class Package:
43     def __init__(self):
44         self.fields = {}
45
46     def __len__(self):
47         return len(self.fields)
48
49     # Look up a field in this package.
50     def __getitem__(self, key):
51         if self.fields.has_key(key):
52             return self.fields[key]
53         # If it is not defined, return the default value for that field.
54         if defaults.has_key(key):
55             return defaults[key]
56         # Special defaults
57         if key == 'sourcepackage':
58             return self['name']
59         if key == 'sourceversion':
60             return self['version']
61         # If there is no default, try again with a lowercase version
62         # of the field name.
63         lcase = string.lower(key)
64         if lcase != key:
65             return self[lcase]
66         raise KeyError, key
67
68     # Define some standard dictionary methods
69     def keys(self):   return self.fields.keys()
70     def items(self):  return self.fields.items()
71     def values(self): return self.fields.values()
72     def has_key(self, key):  return self.fields.has_key(key)
73
74     def parsefield(self, field, fieldval):
75         # Perform translations on field and fieldval
76         if field == 'package':
77             field = 'name'
78         elif field == 'version':
79             fieldval = version.make(fieldval)
80         elif field == 'architecture':
81             fieldval = string.split(fieldval)
82             if len(fieldval) == 1:
83                 fieldval = fieldval[0]
84         elif field == 'source':
85             field = 'sourcepackage'
86             splitsource = string.split(fieldval)
87             if (len(splitsource) > 1):
88                 if splitsource[1][0] != '(' \
89                    or splitsource[1][-1] != ')':
90                     raise ValueError, fieldval
91                 fieldval = splitsource[0]
92                 self.fields['sourceversion'] = version.make(splitsource[1][1:-1])
93         elif field in relationships:
94             fieldval = map(relation.parse, string.split(fieldval, ','))
95         elif field == 'provides':
96             # I will assume that the alternates construct is
97             # not allowed in the Provides: header.
98             fieldval = string.split(fieldval, ', ')
99         elif field == 'description':
100             i = string.find(fieldval, '\n')
101             if i >= 0:
102                 self.fields['description'] = fieldval[i+1:]
103                 fieldval = string.strip(fieldval[:i])
104         elif field == 'essential':
105             if fieldval == 'yes':
106                 fieldval = 1
107             elif fieldval != 'no':
108                 raise ValueError, fieldval
109             else:
110                 fieldval = 0 
111         elif field == 'section':
112             i = string.find(fieldval, '/')
113             if i >= 0:
114                 self.fields['distribution'] = fieldval[:i]
115                 fieldval = fieldval[i+1:]
116         elif field == 'installed-size':
117             fieldval = string.atoi(fieldval)
118         elif field == 'size':
119             fieldval = string.atoi(fieldval)
120
121         self.fields[field] = fieldval
122
123     # This function accepts a list of "field: value" strings, 
124     # with continuation lines already folded into the values.
125     # "filter" is an array of header fields (lowercase) to parse.
126     # If it is None, parse all fields.
127     def parseparagraph(self, lines, filter=None):
128         for line in lines:
129             idx = string.find(line, ':')
130             if idx < 0:
131                 raise bad_field, line
132             field = string.lower(line[:idx])
133             if not filter or field in filter:
134                 try:
135                     self.parsefield(field, string.strip(line[idx+1:]))
136                 except:
137                      raise bad_field, line
138
139 def parsepackages(infile, filter=None):
140     packages = {}
141     paragraph = []
142     while 1:
143         line = infile.readline()
144         if len(line) == 0:
145             break 
146         elif line[0] == ' ' or line[0] == '\t':
147             paragraph[-1] = paragraph[-1] + line
148         elif line[0] == '\n':
149             pk = Package()
150             pk.parseparagraph(paragraph, filter)
151             packages[pk['name']] = pk
152             paragraph = []
153         else:
154             paragraph.append(line)
155     return packages