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