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