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