d654e5dbad6170cc698e515d143a84cf8c4544b6
[oweals/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                 pkg->architecture = parseGenericFieldType("Auto-Installed", *lines);
278             }
279             break;
280
281         case 'F':
282             if(isGenericFieldType("Filename:", *lines))
283                 pkg->filename = parseGenericFieldType("Filename", *lines);
284             break;
285
286         case 'S':
287             if(isGenericFieldType("Section:", *lines))
288                 pkg->section = parseGenericFieldType("Section", *lines);
289             else if(isGenericFieldType("Size:", *lines))
290                 pkg->size = parseGenericFieldType("Size", *lines);
291             else if(isGenericFieldType("Source:", *lines))
292                 pkg->source = parseGenericFieldType("Source", *lines);
293             else if(isGenericFieldType("Status", *lines))
294                 parseStatus(pkg, *lines);
295             else if(isGenericFieldType("Suggests", *lines))
296                 pkg->suggests_str = parseDependsString(*lines, &pkg->suggests_count);
297             break;
298
299         case 'M':
300             if(isGenericFieldType("MD5sum:", *lines))
301                 pkg->md5sum = parseGenericFieldType("MD5sum", *lines);
302             /* The old opkg wrote out status files with the wrong case for MD5sum,
303                 let's parse it either way */
304             else if(isGenericFieldType("MD5Sum:", *lines))
305                 pkg->md5sum = parseGenericFieldType("MD5Sum", *lines);
306             else if(isGenericFieldType("Maintainer", *lines))
307                 pkg->maintainer = parseGenericFieldType("Maintainer", *lines);
308             break;
309
310         case 'I':
311             if(isGenericFieldType("Installed-Size:", *lines))
312                 pkg->installed_size = parseGenericFieldType("Installed-Size", *lines);
313             else if(isGenericFieldType("Installed-Time:", *lines)) {
314                 char *time_str = parseGenericFieldType("Installed-Time", *lines);
315                 pkg->installed_time = strtoul(time_str, NULL, 0);
316             }       
317             break;
318
319         case 'E':
320             if(isGenericFieldType("Essential:", *lines)) {
321                 char *essential_value;
322                 essential_value = parseGenericFieldType("Essential", *lines);
323                 if (strcmp(essential_value, "yes") == 0) {
324                     pkg->essential = 1;
325                 }
326                 free(essential_value);
327             }
328             break;
329
330         case 'V':
331             if(isGenericFieldType("Version", *lines))
332                 parseVersion(pkg, *lines);
333             break;
334
335         case 'C':
336             if(isGenericFieldType("Conffiles", *lines)){
337                 parseConffiles(pkg, *lines);
338                 reading_conffiles = 1;
339             }
340             else if(isGenericFieldType("Conflicts", *lines))
341                 pkg->conflicts_str = parseDependsString(*lines, &pkg->conflicts_count);
342             break;
343
344         case 'D':
345             if(isGenericFieldType("Description", *lines)) {
346                 pkg->description = parseGenericFieldType("Description", *lines);
347                 reading_conffiles = 0;
348                 reading_description = 1;
349             }
350             else if(isGenericFieldType("Depends", *lines))
351                 pkg->depends_str = parseDependsString(*lines, &pkg->depends_count);
352             break;
353
354         case 'R':
355             if(isGenericFieldType("Recommends", *lines))
356                 pkg->recommends_str = parseDependsString(*lines, &pkg->recommends_count);
357             else if(isGenericFieldType("Replaces", *lines))
358                 pkg->replaces_str = parseDependsString(*lines, &pkg->replaces_count);
359             
360             break;
361
362         case ' ':
363             if(reading_description) {
364                 /* we already know it's not blank, so the rest of description */      
365                 pkg->description = realloc(pkg->description,
366                                            strlen(pkg->description)
367                                            + 1 + strlen(*lines) + 1);
368                 strcat(pkg->description, "\n");
369                 strcat(pkg->description, (*lines));
370             }
371             else if(reading_conffiles)
372                 parseConffiles(pkg, *lines);
373                 
374             break;
375
376         default:
377             if(line_is_blank(*lines)) {
378                 lines++;
379                 goto out;
380             }
381         }
382     }
383 out:;
384     
385     *raw = lines;
386 /* If the ipk has not a Provides line, we insert our false line */ 
387     if ( pkg_false_provides==1)
388        pkg->provides_str = parseDependsString ((char *)"Provides: opkg_internal_use_only ", &pkg->provides_count);
389
390     if (pkg->name) {
391         return 0;
392     } else {
393         return EINVAL;
394     }
395 }
396
397 int pkg_valorize_other_field(pkg_t *pkg, char ***raw)
398 {
399     char ** lines;
400
401     for (lines = *raw; *lines; lines++) {
402         if(isGenericFieldType("Essential:", *lines)) {
403             char *essential_value;
404             essential_value = parseGenericFieldType("Essential", *lines);
405             if (strcmp(essential_value, "yes") == 0) {
406                 pkg->essential = 1;
407             }
408             free(essential_value);
409         }
410     }
411     *raw = lines;
412
413     return 0;
414 }