a588e180eba67cbea2e4305a68b9645cf2d6e37b
[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
142 if (!pkg->version)
143 {
144 pkg->version= calloc(1, strlen(raw)+1);
145 if ( pkg->version == NULL ) {
146 fprintf(stderr, "%s: out of memory \n", __FUNCTION__);
147 return ENOMEM;
148 }
149 strcpy(pkg->version, raw);
150 }
151
152 hyphen= strrchr(pkg->version,'-');
153
154 if (hyphen) {
155 *hyphen++= 0;
156 pkg->revision = hyphen;
157 }
158
159 return 0;
160 }
161
162
163 /* This code is needed to insert in first position the keyword for the aligning bug */
164
165 int alterProvidesLine(char *raw, char *temp)
166 {
167
168
169 if (!*raw) {
170 fprintf(stderr, "%s: ERROR: Provides string is empty", __FUNCTION__);
171 return -EINVAL;
172 }
173
174 if ( temp == NULL ) {
175 fprintf(stderr, "%s: out of memory \n", __FUNCTION__);
176 return -ENOMEM;
177 }
178
179 if (strncmp(raw, "Provides:", 9) == 0) {
180 raw += 9;
181 }
182 while (*raw && isspace(*raw)) {
183 raw++;
184 }
185
186 snprintf ( temp, 35, "Provides: opkg_internal_use_only, "); /* First part of the line */
187 while (*raw) {
188 strncat( temp, raw++, 1);
189 }
190 return 0;
191
192 }
193
194 /* Some random thoughts from Carl:
195
196 This function could be considerably simplified if we just kept
197 an array of all the generic string-valued field names, and looped
198 through those looking for a match. Also, these fields could perhaps
199 be stored in the package as an array as well, (or, probably better,
200 as an nv_pair_list_t).
201
202 Fields which require special parsing or storage, (such as Depends:
203 and Status:) could be handled as they are now.
204 */
205 /* XXX: FEATURE: The Suggests: field needs to be changed from a string
206 to a dependency list. And, since we already have
207 Depends/Pre-Depends and need to add Conflicts, Recommends, and
208 Enhances, perhaps we could generalize all of these and save some
209 code duplication.
210 */
211 int pkg_parse_raw(pkg_t *pkg, char ***raw, pkg_src_t *src, pkg_dest_t *dest)
212 {
213 int reading_conffiles, reading_description;
214 int pkg_false_provides=1;
215 char ** lines;
216 char * provide=NULL;
217
218 pkg->src = src;
219 pkg->dest = dest;
220
221 reading_conffiles = reading_description = 0;
222
223 for (lines = *raw; *lines; lines++) {
224 /* fprintf(stderr, "PARSING %s\n", *lines);*/
225 switch (**lines) {
226 case 'P':
227 if(isGenericFieldType("Package:", *lines))
228 pkg->name = parseGenericFieldType("Package", *lines);
229 else if(isGenericFieldType("Priority:", *lines))
230 pkg->priority = parseGenericFieldType("Priority", *lines);
231 else if(isGenericFieldType("Provides", *lines)){
232 /* Here we add the internal_use to align the off by one problem between provides_str and provides */
233 provide = (char * ) calloc(1, strlen(*lines)+ 35 ); /* Preparing the space for the new opkg_internal_use_only */
234 if ( alterProvidesLine(*lines,provide) ){
235 return EINVAL;
236 }
237 pkg->provides_str = parseDependsString( provide, &pkg->provides_count);
238 /* Let's try to hack a bit here.
239 The idea is that if a package has no Provides, we would add one generic, to permit the check of dependencies
240 in alot of other places. We will remove it before writing down the status database */
241 pkg_false_provides=0;
242 free(provide);
243 }
244 else if(isGenericFieldType("Pre-Depends", *lines))
245 pkg->pre_depends_str = parseDependsString(*lines, &pkg->pre_depends_count);
246 break;
247
248 case 'A':
249 if(isGenericFieldType("Architecture:", *lines))
250 pkg->architecture = parseGenericFieldType("Architecture", *lines);
251 else if(isGenericFieldType("Auto-Installed:", *lines)) {
252 char *auto_installed_value;
253 auto_installed_value = parseGenericFieldType("Auto-Installed:", *lines);
254 if (strcmp(auto_installed_value, "yes") == 0) {
255 pkg->auto_installed = 1;
256 }
257 free(auto_installed_value);
258 }
259 break;
260
261 case 'F':
262 if(isGenericFieldType("Filename:", *lines))
263 pkg->filename = parseGenericFieldType("Filename", *lines);
264 break;
265
266 case 'S':
267 if(isGenericFieldType("Section:", *lines))
268 pkg->section = parseGenericFieldType("Section", *lines);
269 #ifdef HAVE_SHA256
270 else if(isGenericFieldType("SHA256sum:", *lines))
271 pkg->sha256sum = parseGenericFieldType("SHA256sum", *lines);
272 #endif
273 else if(isGenericFieldType("Size:", *lines))
274 pkg->size = parseGenericFieldType("Size", *lines);
275 else if(isGenericFieldType("Source:", *lines))
276 pkg->source = parseGenericFieldType("Source", *lines);
277 else if(isGenericFieldType("Status", *lines))
278 parseStatus(pkg, *lines);
279 else if(isGenericFieldType("Suggests", *lines))
280 pkg->suggests_str = parseDependsString(*lines, &pkg->suggests_count);
281 break;
282
283 case 'T':
284 if(isGenericFieldType("Tags:", *lines))
285 pkg->tags = parseGenericFieldType("Tags", *lines);
286 break;
287
288 case 'M':
289 if(isGenericFieldType("MD5sum:", *lines))
290 pkg->md5sum = parseGenericFieldType("MD5sum", *lines);
291 /* The old opkg wrote out status files with the wrong case for MD5sum,
292 let's parse it either way */
293 else if(isGenericFieldType("MD5Sum:", *lines))
294 pkg->md5sum = parseGenericFieldType("MD5Sum", *lines);
295 else if(isGenericFieldType("Maintainer", *lines))
296 pkg->maintainer = parseGenericFieldType("Maintainer", *lines);
297 break;
298
299 case 'I':
300 if(isGenericFieldType("Installed-Size:", *lines))
301 pkg->installed_size = parseGenericFieldType("Installed-Size", *lines);
302 else if(isGenericFieldType("Installed-Time:", *lines)) {
303 char *time_str = parseGenericFieldType("Installed-Time", *lines);
304 pkg->installed_time = strtoul(time_str, NULL, 0);
305 free (time_str);
306 }
307 break;
308
309 case 'E':
310 if(isGenericFieldType("Essential:", *lines)) {
311 char *essential_value;
312 essential_value = parseGenericFieldType("Essential", *lines);
313 if (strcmp(essential_value, "yes") == 0) {
314 pkg->essential = 1;
315 }
316 free(essential_value);
317 }
318 break;
319
320 case 'V':
321 if(isGenericFieldType("Version", *lines))
322 parseVersion(pkg, *lines);
323 break;
324
325 case 'C':
326 if(isGenericFieldType("Conffiles", *lines)){
327 parseConffiles(pkg, *lines);
328 reading_conffiles = 1;
329 }
330 else if(isGenericFieldType("Conflicts", *lines))
331 pkg->conflicts_str = parseDependsString(*lines, &pkg->conflicts_count);
332 break;
333
334 case 'D':
335 if(isGenericFieldType("Description", *lines)) {
336 pkg->description = parseGenericFieldType("Description", *lines);
337 reading_conffiles = 0;
338 reading_description = 1;
339 }
340 else if(isGenericFieldType("Depends", *lines))
341 pkg->depends_str = parseDependsString(*lines, &pkg->depends_count);
342 break;
343
344 case 'R':
345 if(isGenericFieldType("Recommends", *lines))
346 pkg->recommends_str = parseDependsString(*lines, &pkg->recommends_count);
347 else if(isGenericFieldType("Replaces", *lines))
348 pkg->replaces_str = parseDependsString(*lines, &pkg->replaces_count);
349
350 break;
351
352 case ' ':
353 if(reading_description) {
354 /* we already know it's not blank, so the rest of description */
355 pkg->description = realloc(pkg->description,
356 strlen(pkg->description)
357 + 1 + strlen(*lines) + 1);
358 strcat(pkg->description, "\n");
359 strcat(pkg->description, (*lines));
360 }
361 else if(reading_conffiles)
362 parseConffiles(pkg, *lines);
363
364 break;
365
366 default:
367 if(line_is_blank(*lines)) {
368 lines++;
369 goto out;
370 }
371 }
372 }
373 out:;
374
375 *raw = lines;
376 /* If the opk has not a Provides line, we insert our false line */
377 if ( pkg_false_provides==1)
378 {
379 pkg->provides_count = 1;
380 pkg->provides_str = calloc (1, sizeof (char*));
381 pkg->provides_str[0] = strdup ("opkg_internal_use_only");
382 }
383
384 if (pkg->name) {
385 return 0;
386 } else {
387 return EINVAL;
388 }
389 }
390
391 int pkg_valorize_other_field(pkg_t *pkg, char ***raw)
392 {
393 char ** lines;
394
395 for (lines = *raw; *lines; lines++) {
396 if(isGenericFieldType("Essential:", *lines)) {
397 char *essential_value;
398 essential_value = parseGenericFieldType("Essential", *lines);
399 if (strcmp(essential_value, "yes") == 0) {
400 pkg->essential = 1;
401 }
402 free(essential_value);
403 }
404 }
405 *raw = lines;
406
407 return 0;
408 }