e0d7fce11f8ada0892cbb03b08bcb97d20bc8420
[oweals/opkg-lede.git] / libopkg / pkg_parse.c
1 /* pkg_parse.c - the opkg package management system
2
3    Copyright (C) 2009 Ubiq Technologies <graham.gower@gmail.com>
4
5    Steven M. Ayer
6    Copyright (C) 2002 Compaq Computer Corporation
7
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2, or (at
11    your option) any later version.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17 */
18
19 #include "config.h"
20
21 #include <stdio.h>
22 #include <ctype.h>
23
24 #include "pkg.h"
25 #include "opkg_utils.h"
26 #include "pkg_parse.h"
27 #include "libbb/libbb.h"
28
29 #include "parse_util.h"
30
31 static void
32 parse_status(pkg_t *pkg, const char *sstr)
33 {
34         char sw_str[64], sf_str[64], ss_str[64];
35
36         if (sscanf(sstr, "Status: %63s %63s %63s",
37                                 sw_str, sf_str, ss_str) != 3) {
38                 opkg_msg(ERROR, "Failed to parse Status line for %s\n",
39                                 pkg->name);
40                 return;
41         }
42
43         pkg->state_want = pkg_state_want_from_str(sw_str);
44         pkg->state_flag = pkg_state_flag_from_str(sf_str);
45         pkg->state_status = pkg_state_status_from_str(ss_str);
46 }
47
48 static void
49 parse_conffiles(pkg_t *pkg, const char *cstr)
50 {
51         char file_name[1024], md5sum[35];
52
53         if (sscanf(cstr, "%1023s %34s", file_name, md5sum) != 2) {
54                 opkg_msg(ERROR, "Failed to parse Conffiles line for %s\n",
55                                 pkg->name);
56                 return;
57         }
58
59         conffile_list_append(&pkg->conffiles, file_name, md5sum);
60 }
61
62 int
63 parse_version(pkg_t *pkg, const char *vstr)
64 {
65         char *colon;
66
67         if (strncmp(vstr, "Version:", 8) == 0)
68                 vstr += 8;
69
70         while (*vstr && isspace(*vstr))
71                 vstr++;
72
73         colon = strchr(vstr, ':');
74         if (colon) {
75                 errno = 0;
76                 pkg->epoch = strtoul(vstr, NULL, 10);
77                 if (errno) {
78                         opkg_perror(ERROR, "%s: invalid epoch", pkg->name);
79                 }
80                 vstr = ++colon;
81         } else {
82                 pkg->epoch= 0;
83         }
84
85         pkg->version = xstrdup(vstr);
86         pkg->revision = strrchr(pkg->version,'-');
87
88         if (pkg->revision)
89                 *pkg->revision++ = '\0';
90
91         return 0;
92 }
93
94 static int
95 get_arch_priority(const char *arch)
96 {
97         nv_pair_list_elt_t *l;
98
99         list_for_each_entry(l , &conf->arch_list.head, node) {
100                 nv_pair_t *nv = (nv_pair_t *)l->data;
101                 if (strcmp(nv->name, arch) == 0)
102                         return strtol(nv->value, NULL, 0);
103         }
104         return 0;
105 }
106
107 static int
108 pkg_parse_line(pkg_t *pkg, const char *line, uint mask)
109 {
110         /* these flags are a bit hackish... */
111         static int reading_conffiles = 0, reading_description = 0;
112         int ret = 0;
113
114         /* Exclude globally masked fields. */
115         mask |= conf->pfm;
116
117         /* Flip the semantics of the mask. */
118         mask ^= PFM_ALL;
119
120         switch (*line) {
121         case 'A':
122                 if ((mask & PFM_ARCHITECTURE ) && is_field("Architecture", line)) {
123                         pkg->architecture = parse_simple("Architecture", line);
124                         pkg->arch_priority = get_arch_priority(pkg->architecture);
125                 } else if ((mask & PFM_AUTO_INSTALLED) && is_field("Auto-Installed", line)) {
126                         char *tmp = parse_simple("Auto-Installed", line);
127                         if (strcmp(tmp, "yes") == 0)
128                             pkg->auto_installed = 1;
129                         free(tmp);
130                 }
131                 break;
132
133         case 'C':
134                 if ((mask & PFM_CONFFILES) && is_field("Conffiles", line)) {
135                         reading_conffiles = 1;
136                         reading_description = 0;
137                         goto dont_reset_flags;
138                 }
139                 else if ((mask & PFM_CONFLICTS) && is_field("Conflicts", line))
140                         pkg->conflicts_str = parse_list(line, &pkg->conflicts_count, ',', 0);
141                 break;
142
143         case 'D':
144                 if ((mask & PFM_DESCRIPTION) && is_field("Description", line)) {
145                         pkg->description = parse_simple("Description", line);
146                         reading_conffiles = 0;
147                         reading_description = 1;
148                         goto dont_reset_flags;
149                 } else if ((mask & PFM_DEPENDS) && is_field("Depends", line))
150                         pkg->depends_str = parse_list(line, &pkg->depends_count, ',', 0);
151                 break;
152
153         case 'E':
154                 if((mask & PFM_ESSENTIAL) && is_field("Essential", line)) {
155                         char *tmp = parse_simple("Essential", line);
156                         if (strcmp(tmp, "yes") == 0)
157                                 pkg->essential = 1;
158                         free(tmp);
159                 }
160                 break;
161
162         case 'F':
163                 if((mask & PFM_FILENAME) && is_field("Filename", line))
164                         pkg->filename = parse_simple("Filename", line);
165                 break;
166
167         case 'I':
168                 if ((mask & PFM_INSTALLED_SIZE) && is_field("Installed-Size", line)) {
169                         char *tmp = parse_simple("Installed-Size", line);
170                         pkg->installed_size = strtoul(tmp, NULL, 0);
171                         free (tmp);
172                 } else if ((mask & PFM_INSTALLED_TIME) && is_field("Installed-Time", line)) {
173                         char *tmp = parse_simple("Installed-Time", line);
174                         pkg->installed_time = strtoul(tmp, NULL, 0);
175                         free (tmp);
176                 }
177                 break;
178
179         case 'M':
180                 if ((mask & PFM_MD5SUM) && is_field("MD5sum:", line))
181                         pkg->md5sum = parse_simple("MD5sum", line);
182                         /* The old opkg wrote out status files with the wrong
183                         * case for MD5sum, let's parse it either way */
184                 else if ((mask & PFM_MD5SUM) && is_field("MD5Sum:", line)) 
185                         pkg->md5sum = parse_simple("MD5Sum", line);
186                 else if((mask & PFM_MAINTAINER) && is_field("Maintainer", line))
187                         pkg->maintainer = parse_simple("Maintainer", line);
188                 break;
189
190         case 'P':
191                 if ((mask & PFM_PACKAGE) && is_field("Package", line))
192                         pkg->name = parse_simple("Package", line);
193                 else if ((mask & PFM_PRIORITY) && is_field("Priority", line))
194                         pkg->priority = parse_simple("Priority", line);
195                 else if ((mask & PFM_PROVIDES) && is_field("Provides", line))
196                         pkg->provides_str = parse_list(line, &pkg->provides_count, ',', 0);
197                 else if ((mask & PFM_PRE_DEPENDS) && is_field("Pre-Depends", line))
198                         pkg->pre_depends_str = parse_list(line, &pkg->pre_depends_count, ',', 0);
199                 break;
200
201         case 'R':
202                 if ((mask & PFM_RECOMMENDS) && is_field("Recommends", line))
203                         pkg->recommends_str = parse_list(line, &pkg->recommends_count, ',', 0);
204                 else if ((mask & PFM_REPLACES) && is_field("Replaces", line))
205                         pkg->replaces_str = parse_list(line, &pkg->replaces_count, ',', 0);
206
207                 break;
208
209         case 'S':
210                 if ((mask & PFM_SECTION) && is_field("Section", line))
211                         pkg->section = parse_simple("Section", line);
212 #ifdef HAVE_SHA256
213                 else if ((mask & PFM_SHA256SUM) && is_field("SHA256sum", line))
214                         pkg->sha256sum = parse_simple("SHA256sum", line);
215 #endif
216                 else if ((mask & PFM_SIZE) && is_field("Size", line)) {
217                         char *tmp = parse_simple("Size", line);
218                         pkg->size = strtoul(tmp, NULL, 0);
219                         free (tmp);
220                 } else if ((mask & PFM_SOURCE) && is_field("Source", line))
221                         pkg->source = parse_simple("Source", line);
222                 else if ((mask & PFM_STATUS) && is_field("Status", line))
223                         parse_status(pkg, line);
224                 else if ((mask & PFM_SUGGESTS) && is_field("Suggests", line))
225                         pkg->suggests_str = parse_list(line, &pkg->suggests_count, ',', 0);
226                 break;
227
228         case 'T':
229                 if ((mask & PFM_TAGS) && is_field("Tags", line))
230                         pkg->tags = parse_simple("Tags", line);
231                 break;
232
233         case 'V':
234                 if ((mask & PFM_VERSION) && is_field("Version", line))
235                         parse_version(pkg, line);
236                 break;
237
238         case ' ':
239                 if ((mask & PFM_DESCRIPTION) && reading_description) {
240                         pkg->description = xrealloc(pkg->description,
241                                                 strlen(pkg->description)
242                                                 + 1 + strlen(line) + 1);
243                         strcat(pkg->description, "\n");
244                         strcat(pkg->description, (line));
245                         goto dont_reset_flags;
246                 } else if ((mask & PFM_CONFFILES) && reading_conffiles) {
247                         parse_conffiles(pkg, line);
248                         goto dont_reset_flags;
249                 }
250
251                 /* FALLTHROUGH */
252         default:
253                 /* For package lists, signifies end of package. */
254                 if(line_is_blank(line)) {
255                         ret = 1;
256                         break;
257                 }
258         }
259
260         reading_description = 0;
261         reading_conffiles = 0;
262
263 dont_reset_flags:
264
265         return ret;
266 }
267
268 int
269 pkg_parse_from_stream_nomalloc(pkg_t *pkg, FILE *fp, uint mask,
270                                                 char **buf0, size_t buf0len)
271 {
272         int ret, lineno;
273         char *buf, *nl;
274         size_t buflen;
275
276         lineno = 1;
277         ret = 0;
278
279         buflen = buf0len;
280         buf = *buf0;
281         buf[0] = '\0';
282
283         while (1) {
284                 if (fgets(buf, (int)buflen, fp) == NULL) {
285                         if (ferror(fp)) {
286                                 opkg_perror(ERROR, "fgets");
287                                 ret = -1;
288                         } else if (strlen(*buf0) == buf0len-1) {
289                                 opkg_msg(ERROR, "Missing new line character"
290                                                 " at end of file!\n");
291                                 pkg_parse_line(pkg, *buf0, mask);
292                         }
293                         break;
294                 }
295
296                 nl = strchr(buf, '\n');
297                 if (nl == NULL) {
298                         if (strlen(buf) < buflen-1) {
299                                 /*
300                                  * Line could be exactly buflen-1 long and
301                                  * missing a newline, but we won't know until
302                                  * fgets fails to read more data.
303                                  */
304                                 opkg_msg(ERROR, "Missing new line character"
305                                                 " at end of file!\n");
306                                 pkg_parse_line(pkg, *buf0, mask);
307                                 break;
308                         }
309                         if (buf0len >= EXCESSIVE_LINE_LEN) {
310                                 opkg_msg(ERROR, "Excessively long line at "
311                                         "%d. Corrupt file?\n",
312                                         lineno);
313                                 ret = -1;
314                                 break;
315                         }
316
317                         /*
318                          * Realloc and point buf past the data already read,
319                          * at the NULL terminator inserted by fgets.
320                          * |<--------------- buf0len ----------------->|
321                          * |                     |<------- buflen ---->|
322                          * |---------------------|---------------------|
323                          * buf0                   buf
324                          */
325                         buflen = buf0len +1;
326                         buf0len *= 2;
327                         *buf0 = xrealloc(*buf0, buf0len);
328                         buf = *buf0 + buflen -2;
329
330                         continue;
331                 }
332
333                 *nl = '\0';
334
335                 lineno++;
336
337                 if (pkg_parse_line(pkg, *buf0, mask))
338                         break;
339
340                 buf = *buf0;
341                 buflen = buf0len;
342                 buf[0] = '\0';
343         }
344
345         if (pkg->name == NULL) {
346                 /* probably just a blank line */
347                 ret = 1;
348         }
349
350         return ret;
351 }
352
353 int
354 pkg_parse_from_stream(pkg_t *pkg, FILE *fp, uint mask)
355 {
356         int ret;
357         char *buf;
358         const size_t len = 4096;
359
360         buf = xmalloc(len);
361         ret = pkg_parse_from_stream_nomalloc(pkg, fp, mask, &buf, len);
362         free(buf);
363
364         return ret;
365 }