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