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