opkg: (leak fixing, day 2) lots and lots of memory leaks fixed
[project/opkg-lede.git] / libopkg / pkg_parse.c
1 /* pkg_parse.c - the opkg package management system
2
3 Steven M. Ayer
4
5 Copyright (C) 2002 Compaq Computer Corporation
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2, or (at
10 your option) any later version.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16 */
17
18 #include "includes.h"
19 #include <errno.h>
20 #include <ctype.h>
21
22 #include "pkg.h"
23 #include "opkg_utils.h"
24 #include "pkg_parse.h"
25
26 int isGenericFieldType(char * type, char * line)
27 {
28 if(!strncmp(line, type, strlen(type)))
29 return 1;
30 return 0;
31 }
32
33 char * parseGenericFieldType(char * type, char * raw)
34 {
35 char * field_value = raw + (strlen(type) + 1);
36 return trim_alloc(field_value);
37 }
38
39 void parseStatus(pkg_t *pkg, char * raw)
40 {
41 char sw_str[64], sf_str[64], ss_str[64];
42
43 sscanf(raw, "Status: %s %s %s", sw_str, sf_str, ss_str);
44 pkg->state_want = pkg_state_want_from_str(sw_str);
45 pkg->state_flag = pkg_state_flag_from_str(sf_str);
46 pkg->state_status = pkg_state_status_from_str(ss_str);
47 }
48
49 char ** parseDependsString(char * raw, int * depends_count)
50 {
51 char ** depends = NULL;
52 int line_count = 0;
53 char buff[2048], * dest;
54
55 while(raw && *raw && !isspace(*raw)) {
56 raw++;
57 }
58
59 if(line_is_blank(raw)){
60 *depends_count = line_count;
61 return NULL;
62 }
63 while(raw && *raw){
64 depends = (char **)realloc(depends, sizeof(char *) * (line_count + 1));
65
66 while(isspace(*raw)) raw++;
67
68 dest = buff;
69 while((*raw != ',') && *raw)
70 *dest++ = *raw++;
71
72 *dest = '\0';
73 depends[line_count] = trim_alloc(buff);
74 if(depends[line_count] ==NULL)
75 return NULL;
76 line_count++;
77 if(*raw == ',')
78 raw++;
79 }
80 *depends_count = line_count;
81 return depends;
82 }
83
84 void parseConffiles(pkg_t * pkg, char * raw)
85 {
86 char file_name[1048], md5sum[1048]; /* please tell me there aren't any longer that 1k */
87
88 if(!strncmp(raw, "Conffiles:", 10))
89 raw += strlen("Conffiles:");
90
91 while(*raw && (sscanf(raw, "%s%s", file_name, md5sum) == 2)){
92 conffile_list_append(&pkg->conffiles, file_name, md5sum);
93 /* fprintf(stderr, "%s %s ", file_name, md5sum);*/
94 while (*raw && isspace(*raw)) {
95 raw++;
96 }
97 raw += strlen(file_name);
98 while (*raw && isspace(*raw)) {
99 raw++;
100 }
101 raw += strlen(md5sum);
102 }
103 }
104
105 int parseVersion(pkg_t *pkg, char *raw)
106 {
107 char *colon, *eepochcolon;
108 char *hyphen;
109 unsigned long epoch;
110
111 if (!*raw) {
112 fprintf(stderr, "%s: ERROR: version string is empty", __FUNCTION__);
113 return EINVAL;
114 }
115
116 if (strncmp(raw, "Version:", 8) == 0) {
117 raw += 8;
118 }
119 while (*raw && isspace(*raw)) {
120 raw++;
121 }
122
123 colon= strchr(raw,':');
124 if (colon) {
125 epoch= strtoul(raw,&eepochcolon,10);
126 if (colon != eepochcolon) {
127 fprintf(stderr, "%s: ERROR: epoch in version is not number", __FUNCTION__);
128 return EINVAL;
129 }
130 if (!*++colon) {
131 fprintf(stderr, "%s: ERROR: nothing after colon in version number", __FUNCTION__);
132 return EINVAL;
133 }
134 raw= colon;
135 pkg->epoch= epoch;
136 } else {
137 pkg->epoch= 0;
138 }
139
140 pkg->revision = "";
141 pkg->familiar_revision = "";
142
143 if (!pkg->version)
144 {
145 pkg->version= malloc(strlen(raw)+1);
146 if ( pkg->version == NULL ) {
147 fprintf(stderr, "%s: out of memory \n", __FUNCTION__);
148 return ENOMEM;
149 }
150 strcpy(pkg->version, raw);
151 }
152
153 hyphen= strrchr(pkg->version,'-');
154
155 if (hyphen) {
156 *hyphen++= 0;
157 pkg->revision = hyphen;
158 }
159
160 /*
161 fprintf(stderr,"Parsed version: %lu, %s, %s, %s\n",
162 pkg->epoch,
163 pkg->version,
164 pkg->revision,
165 pkg->familiar_revision);
166 */
167
168 return 0;
169 }
170
171
172 /* This code is needed to insert in first position the keyword for the aligning bug */
173
174 int alterProvidesLine(char *raw, char *temp)
175 {
176
177
178 if (!*raw) {
179 fprintf(stderr, "%s: ERROR: Provides string is empty", __FUNCTION__);
180 return -EINVAL;
181 }
182
183 if ( temp == NULL ) {
184 fprintf(stderr, "%s: out of memory \n", __FUNCTION__);
185 return -ENOMEM;
186 }
187
188 if (strncmp(raw, "Provides:", 9) == 0) {
189 raw += 9;
190 }
191 while (*raw && isspace(*raw)) {
192 raw++;
193 }
194
195 snprintf ( temp, 35, "Provides: opkg_internal_use_only, "); /* First part of the line */
196 while (*raw) {
197 strncat( temp, raw++, 1);
198 }
199 return 0;
200
201 }
202
203 /* Some random thoughts from Carl:
204
205 This function could be considerably simplified if we just kept
206 an array of all the generic string-valued field names, and looped
207 through those looking for a match. Also, these fields could perhaps
208 be stored in the package as an array as well, (or, probably better,
209 as an nv_pair_list_t).
210
211 Fields which require special parsing or storage, (such as Depends:
212 and Status:) could be handled as they are now.
213 */
214 /* XXX: FEATURE: The Suggests: field needs to be changed from a string
215 to a dependency list. And, since we already have
216 Depends/Pre-Depends and need to add Conflicts, Recommends, and
217 Enhances, perhaps we could generalize all of these and save some
218 code duplication.
219 */
220 int pkg_parse_raw(pkg_t *pkg, char ***raw, pkg_src_t *src, pkg_dest_t *dest)
221 {
222 int reading_conffiles, reading_description;
223 int pkg_false_provides=1;
224 char ** lines;
225 char * provide=NULL;
226
227 pkg->src = src;
228 pkg->dest = dest;
229
230 reading_conffiles = reading_description = 0;
231
232 for (lines = *raw; *lines; lines++) {
233 /* fprintf(stderr, "PARSING %s\n", *lines);*/
234 switch (**lines) {
235 case 'P':
236 if(isGenericFieldType("Package:", *lines))
237 pkg->name = parseGenericFieldType("Package", *lines);
238 else if(isGenericFieldType("Priority:", *lines))
239 pkg->priority = parseGenericFieldType("Priority", *lines);
240 else if(isGenericFieldType("Provides", *lines)){
241 /* Here we add the internal_use to align the off by one problem between provides_str and provides */
242 provide = (char * ) malloc(strlen(*lines)+ 35 ); /* Preparing the space for the new opkg_internal_use_only */
243 if ( alterProvidesLine(*lines,provide) ){
244 return EINVAL;
245 }
246 pkg->provides_str = parseDependsString( provide, &pkg->provides_count);
247 /* Let's try to hack a bit here.
248 The idea is that if a package has no Provides, we would add one generic, to permit the check of dependencies
249 in alot of other places. We will remove it before writing down the status database */
250 pkg_false_provides=0;
251 free(provide);
252 }
253 else if(isGenericFieldType("Pre-Depends", *lines))
254 pkg->pre_depends_str = parseDependsString(*lines, &pkg->pre_depends_count);
255 break;
256
257 case 'A':
258 if(isGenericFieldType("Architecture:", *lines))
259 pkg->architecture = parseGenericFieldType("Architecture", *lines);
260 else if(isGenericFieldType("Auto-Installed:", *lines)) {
261 char *auto_installed_value;
262 auto_installed_value = parseGenericFieldType("Auto-Installed:", *lines);
263 if (strcmp(auto_installed_value, "yes") == 0) {
264 pkg->auto_installed = 1;
265 }
266 free(auto_installed_value);
267 }
268 break;
269
270 case 'F':
271 if(isGenericFieldType("Filename:", *lines))
272 pkg->filename = parseGenericFieldType("Filename", *lines);
273 break;
274
275 case 'S':
276 if(isGenericFieldType("Section:", *lines))
277 pkg->section = parseGenericFieldType("Section", *lines);
278 else if(isGenericFieldType("Size:", *lines))
279 pkg->size = parseGenericFieldType("Size", *lines);
280 else if(isGenericFieldType("Source:", *lines))
281 pkg->source = parseGenericFieldType("Source", *lines);
282 else if(isGenericFieldType("Status", *lines))
283 parseStatus(pkg, *lines);
284 else if(isGenericFieldType("Suggests", *lines))
285 pkg->suggests_str = parseDependsString(*lines, &pkg->suggests_count);
286 break;
287
288 case 'T':
289 if(isGenericFieldType("Tags:", *lines))
290 pkg->tags = parseGenericFieldType("Tags", *lines);
291 break;
292
293 case 'M':
294 if(isGenericFieldType("MD5sum:", *lines))
295 pkg->md5sum = parseGenericFieldType("MD5sum", *lines);
296 /* The old opkg wrote out status files with the wrong case for MD5sum,
297 let's parse it either way */
298 else if(isGenericFieldType("MD5Sum:", *lines))
299 pkg->md5sum = parseGenericFieldType("MD5Sum", *lines);
300 else if(isGenericFieldType("Maintainer", *lines))
301 pkg->maintainer = parseGenericFieldType("Maintainer", *lines);
302 break;
303
304 case 'I':
305 if(isGenericFieldType("Installed-Size:", *lines))
306 pkg->installed_size = parseGenericFieldType("Installed-Size", *lines);
307 else if(isGenericFieldType("Installed-Time:", *lines)) {
308 char *time_str = parseGenericFieldType("Installed-Time", *lines);
309 pkg->installed_time = strtoul(time_str, NULL, 0);
310 free (time_str);
311 }
312 break;
313
314 case 'E':
315 if(isGenericFieldType("Essential:", *lines)) {
316 char *essential_value;
317 essential_value = parseGenericFieldType("Essential", *lines);
318 if (strcmp(essential_value, "yes") == 0) {
319 pkg->essential = 1;
320 }
321 free(essential_value);
322 }
323 break;
324
325 case 'V':
326 if(isGenericFieldType("Version", *lines))
327 parseVersion(pkg, *lines);
328 break;
329
330 case 'C':
331 if(isGenericFieldType("Conffiles", *lines)){
332 parseConffiles(pkg, *lines);
333 reading_conffiles = 1;
334 }
335 else if(isGenericFieldType("Conflicts", *lines))
336 pkg->conflicts_str = parseDependsString(*lines, &pkg->conflicts_count);
337 break;
338
339 case 'D':
340 if(isGenericFieldType("Description", *lines)) {
341 pkg->description = parseGenericFieldType("Description", *lines);
342 reading_conffiles = 0;
343 reading_description = 1;
344 }
345 else if(isGenericFieldType("Depends", *lines))
346 pkg->depends_str = parseDependsString(*lines, &pkg->depends_count);
347 break;
348
349 case 'R':
350 if(isGenericFieldType("Recommends", *lines))
351 pkg->recommends_str = parseDependsString(*lines, &pkg->recommends_count);
352 else if(isGenericFieldType("Replaces", *lines))
353 pkg->replaces_str = parseDependsString(*lines, &pkg->replaces_count);
354
355 break;
356
357 case ' ':
358 if(reading_description) {
359 /* we already know it's not blank, so the rest of description */
360 pkg->description = realloc(pkg->description,
361 strlen(pkg->description)
362 + 1 + strlen(*lines) + 1);
363 strcat(pkg->description, "\n");
364 strcat(pkg->description, (*lines));
365 }
366 else if(reading_conffiles)
367 parseConffiles(pkg, *lines);
368
369 break;
370
371 default:
372 if(line_is_blank(*lines)) {
373 lines++;
374 goto out;
375 }
376 }
377 }
378 out:;
379
380 *raw = lines;
381 /* If the ipk has not a Provides line, we insert our false line */
382 if ( pkg_false_provides==1)
383 {
384 pkg->provides_count = 1;
385 pkg->provides_str = malloc (sizeof (char*));
386 pkg->provides_str[0] = strdup ("opkg_internal_use_only");
387 }
388
389 if (pkg->name) {
390 return 0;
391 } else {
392 return EINVAL;
393 }
394 }
395
396 int pkg_valorize_other_field(pkg_t *pkg, char ***raw)
397 {
398 char ** lines;
399
400 for (lines = *raw; *lines; lines++) {
401 if(isGenericFieldType("Essential:", *lines)) {
402 char *essential_value;
403 essential_value = parseGenericFieldType("Essential", *lines);
404 if (strcmp(essential_value, "yes") == 0) {
405 pkg->essential = 1;
406 }
407 free(essential_value);
408 }
409 }
410 *raw = lines;
411
412 return 0;
413 }