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