9b43754e04a6e51dcb305f4a6dc5a65c6cf91703
[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
142   if (!pkg->version)
143   {
144   pkg->version= malloc(strlen(raw)+1);
145   if ( pkg->version == NULL ) {
146      fprintf(stderr, "%s: out of memory \n", __FUNCTION__);
147      return ENOMEM;
148   }
149   strcpy(pkg->version, raw);
150   }
151
152   hyphen= strrchr(pkg->version,'-');
153
154   if (hyphen) {
155     *hyphen++= 0;
156       pkg->revision = hyphen;
157   }
158
159   return 0;
160 }
161
162
163 /* This code is needed to insert in first position the keyword for the aligning bug */
164
165 int alterProvidesLine(char *raw, char *temp)
166 {
167
168
169   if (!*raw) {
170       fprintf(stderr, "%s: ERROR: Provides string is empty", __FUNCTION__);
171       return -EINVAL;
172   }
173
174   if ( temp == NULL ) {
175      fprintf(stderr, "%s: out of memory \n", __FUNCTION__);
176      return -ENOMEM;
177   }
178
179   if (strncmp(raw, "Provides:", 9) == 0) {
180       raw += 9;
181   }
182   while (*raw && isspace(*raw)) {
183       raw++;
184   }      
185   
186   snprintf ( temp, 35, "Provides: opkg_internal_use_only, ");           /* First part of the line */
187   while (*raw) {
188      strncat( temp, raw++, 1);
189   }
190   return 0;
191  
192 }
193
194 /* Some random thoughts from Carl:
195
196    This function could be considerably simplified if we just kept
197    an array of all the generic string-valued field names, and looped
198    through those looking for a match. Also, these fields could perhaps
199    be stored in the package as an array as well, (or, probably better,
200    as an nv_pair_list_t).
201
202    Fields which require special parsing or storage, (such as Depends:
203    and Status:) could be handled as they are now. 
204 */
205 /* XXX: FEATURE: The Suggests: field needs to be changed from a string
206    to a dependency list. And, since we already have
207    Depends/Pre-Depends and need to add Conflicts, Recommends, and
208    Enhances, perhaps we could generalize all of these and save some
209    code duplication.
210 */
211 int pkg_parse_raw(pkg_t *pkg, char ***raw, pkg_src_t *src, pkg_dest_t *dest)
212 {
213     int reading_conffiles, reading_description;
214     int pkg_false_provides=1;
215     char ** lines;
216     char * provide=NULL;
217
218     pkg->src = src;
219     pkg->dest = dest;
220
221     reading_conffiles = reading_description = 0;
222
223     for (lines = *raw; *lines; lines++) {
224         /*      fprintf(stderr, "PARSING %s\n", *lines);*/
225         switch (**lines) {
226         case 'P':
227             if(isGenericFieldType("Package:", *lines)) 
228                 pkg->name = parseGenericFieldType("Package", *lines);
229             else if(isGenericFieldType("Priority:", *lines))
230                 pkg->priority = parseGenericFieldType("Priority", *lines);
231             else if(isGenericFieldType("Provides", *lines)){
232 /* Here we add the internal_use to align the off by one problem between provides_str and provides */
233                 provide = (char * ) malloc(strlen(*lines)+ 35 ); /* Preparing the space for the new opkg_internal_use_only */
234                 if ( alterProvidesLine(*lines,provide) ){
235                     return EINVAL;
236                 }
237                 pkg->provides_str = parseDependsString( provide, &pkg->provides_count);
238 /* Let's try to hack a bit here.
239    The idea is that if a package has no Provides, we would add one generic, to permit the check of dependencies
240    in alot of other places. We will remove it before writing down the status database */
241                 pkg_false_provides=0;
242                 free(provide);
243             } 
244             else if(isGenericFieldType("Pre-Depends", *lines))
245                 pkg->pre_depends_str = parseDependsString(*lines, &pkg->pre_depends_count);
246             break;
247
248         case 'A':
249             if(isGenericFieldType("Architecture:", *lines))
250                 pkg->architecture = parseGenericFieldType("Architecture", *lines);
251             else if(isGenericFieldType("Auto-Installed:", *lines)) {
252                 char *auto_installed_value;
253                 auto_installed_value = parseGenericFieldType("Auto-Installed:", *lines);
254                 if (strcmp(auto_installed_value, "yes") == 0) {
255                     pkg->auto_installed = 1;
256                 }
257                 free(auto_installed_value);
258             }
259             break;
260
261         case 'F':
262             if(isGenericFieldType("Filename:", *lines))
263                 pkg->filename = parseGenericFieldType("Filename", *lines);
264             break;
265
266         case 'S':
267             if(isGenericFieldType("Section:", *lines))
268                 pkg->section = parseGenericFieldType("Section", *lines);
269             else if(isGenericFieldType("Size:", *lines))
270                 pkg->size = parseGenericFieldType("Size", *lines);
271             else if(isGenericFieldType("Source:", *lines))
272                 pkg->source = parseGenericFieldType("Source", *lines);
273             else if(isGenericFieldType("Status", *lines))
274                 parseStatus(pkg, *lines);
275             else if(isGenericFieldType("Suggests", *lines))
276                 pkg->suggests_str = parseDependsString(*lines, &pkg->suggests_count);
277             break;
278
279         case 'T':
280             if(isGenericFieldType("Tags:", *lines))
281                 pkg->tags = parseGenericFieldType("Tags", *lines);
282             break;
283
284         case 'M':
285             if(isGenericFieldType("MD5sum:", *lines))
286                 pkg->md5sum = parseGenericFieldType("MD5sum", *lines);
287             /* The old opkg wrote out status files with the wrong case for MD5sum,
288                 let's parse it either way */
289             else if(isGenericFieldType("MD5Sum:", *lines))
290                 pkg->md5sum = parseGenericFieldType("MD5Sum", *lines);
291             else if(isGenericFieldType("Maintainer", *lines))
292                 pkg->maintainer = parseGenericFieldType("Maintainer", *lines);
293             break;
294
295         case 'I':
296             if(isGenericFieldType("Installed-Size:", *lines))
297                 pkg->installed_size = parseGenericFieldType("Installed-Size", *lines);
298             else if(isGenericFieldType("Installed-Time:", *lines)) {
299                 char *time_str = parseGenericFieldType("Installed-Time", *lines);
300                 pkg->installed_time = strtoul(time_str, NULL, 0);
301                 free (time_str);
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     {
375        pkg->provides_count = 1;
376        pkg->provides_str = malloc (sizeof (char*));
377        pkg->provides_str[0] = strdup ("opkg_internal_use_only");
378     }
379
380     if (pkg->name) {
381         return 0;
382     } else {
383         return EINVAL;
384     }
385 }
386
387 int pkg_valorize_other_field(pkg_t *pkg, char ***raw)
388 {
389     char ** lines;
390
391     for (lines = *raw; *lines; lines++) {
392         if(isGenericFieldType("Essential:", *lines)) {
393             char *essential_value;
394             essential_value = parseGenericFieldType("Essential", *lines);
395             if (strcmp(essential_value, "yes") == 0) {
396                 pkg->essential = 1;
397             }
398             free(essential_value);
399         }
400     }
401     *raw = lines;
402
403     return 0;
404 }