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