libopkg: do not rely on getline()
[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 int
108 pkg_parse_line(void *ptr, const char *line, uint mask)
109 {
110         pkg_t *pkg = (pkg_t *) ptr;
111
112         /* these flags are a bit hackish... */
113         static int reading_conffiles = 0, reading_description = 0;
114         int ret = 0;
115
116         /* Exclude globally masked fields. */
117         mask |= conf->pfm;
118
119         /* Flip the semantics of the mask. */
120         mask ^= PFM_ALL;
121
122         switch (*line) {
123         case 'A':
124                 if ((mask & PFM_ARCHITECTURE ) && is_field("Architecture", line)) {
125                         pkg->architecture = parse_simple("Architecture", line);
126                         pkg->arch_priority = get_arch_priority(pkg->architecture);
127                 } else if ((mask & PFM_AUTO_INSTALLED) && is_field("Auto-Installed", line)) {
128                         char *tmp = parse_simple("Auto-Installed", line);
129                         if (strcmp(tmp, "yes") == 0)
130                             pkg->auto_installed = 1;
131                         free(tmp);
132                 }
133                 break;
134
135         case 'C':
136                 if ((mask & PFM_CONFFILES) && is_field("Conffiles", line)) {
137                         reading_conffiles = 1;
138                         reading_description = 0;
139                         goto dont_reset_flags;
140                 }
141                 else if ((mask & PFM_CONFLICTS) && is_field("Conflicts", line))
142                         pkg->conflicts_str = parse_list(line, &pkg->conflicts_count, ',', 0);
143                 break;
144
145         case 'D':
146                 if ((mask & PFM_DESCRIPTION) && is_field("Description", line)) {
147                         pkg->description = parse_simple("Description", line);
148                         reading_conffiles = 0;
149                         reading_description = 1;
150                         goto dont_reset_flags;
151                 } else if ((mask & PFM_DEPENDS) && is_field("Depends", line))
152                         pkg->depends_str = parse_list(line, &pkg->depends_count, ',', 0);
153                 break;
154
155         case 'E':
156                 if((mask & PFM_ESSENTIAL) && is_field("Essential", line)) {
157                         char *tmp = parse_simple("Essential", line);
158                         if (strcmp(tmp, "yes") == 0)
159                                 pkg->essential = 1;
160                         free(tmp);
161                 }
162                 break;
163
164         case 'F':
165                 if((mask & PFM_FILENAME) && is_field("Filename", line))
166                         pkg->filename = parse_simple("Filename", line);
167                 break;
168
169         case 'I':
170                 if ((mask & PFM_INSTALLED_SIZE) && is_field("Installed-Size", line)) {
171                         char *tmp = parse_simple("Installed-Size", line);
172                         pkg->installed_size = strtoul(tmp, NULL, 0);
173                         free (tmp);
174                 } else if ((mask & PFM_INSTALLED_TIME) && is_field("Installed-Time", line)) {
175                         char *tmp = parse_simple("Installed-Time", line);
176                         pkg->installed_time = strtoul(tmp, NULL, 0);
177                         free (tmp);
178                 }
179                 break;
180
181         case 'M':
182                 if ((mask & PFM_MD5SUM) && is_field("MD5sum:", line))
183                         pkg->md5sum = parse_simple("MD5sum", line);
184                         /* The old opkg wrote out status files with the wrong
185                         * case for MD5sum, let's parse it either way */
186                 else if ((mask & PFM_MD5SUM) && is_field("MD5Sum:", line)) 
187                         pkg->md5sum = parse_simple("MD5Sum", line);
188                 else if((mask & PFM_MAINTAINER) && is_field("Maintainer", line))
189                         pkg->maintainer = parse_simple("Maintainer", line);
190                 break;
191
192         case 'P':
193                 if ((mask & PFM_PACKAGE) && is_field("Package", line))
194                         pkg->name = parse_simple("Package", line);
195                 else if ((mask & PFM_PRIORITY) && is_field("Priority", line))
196                         pkg->priority = parse_simple("Priority", line);
197                 else if ((mask & PFM_PROVIDES) && is_field("Provides", line))
198                         pkg->provides_str = parse_list(line, &pkg->provides_count, ',', 0);
199                 else if ((mask & PFM_PRE_DEPENDS) && is_field("Pre-Depends", line))
200                         pkg->pre_depends_str = parse_list(line, &pkg->pre_depends_count, ',', 0);
201                 break;
202
203         case 'R':
204                 if ((mask & PFM_RECOMMENDS) && is_field("Recommends", line))
205                         pkg->recommends_str = parse_list(line, &pkg->recommends_count, ',', 0);
206                 else if ((mask & PFM_REPLACES) && is_field("Replaces", line))
207                         pkg->replaces_str = parse_list(line, &pkg->replaces_count, ',', 0);
208
209                 break;
210
211         case 'S':
212                 if ((mask & PFM_SECTION) && is_field("Section", line))
213                         pkg->section = parse_simple("Section", line);
214 #ifdef HAVE_SHA256
215                 else if ((mask & PFM_SHA256SUM) && is_field("SHA256sum", line))
216                         pkg->sha256sum = parse_simple("SHA256sum", line);
217 #endif
218                 else if ((mask & PFM_SIZE) && is_field("Size", line)) {
219                         char *tmp = parse_simple("Size", line);
220                         pkg->size = strtoul(tmp, NULL, 0);
221                         free (tmp);
222                 } else if ((mask & PFM_SOURCE) && is_field("Source", line))
223                         pkg->source = parse_simple("Source", line);
224                 else if ((mask & PFM_STATUS) && is_field("Status", line))
225                         parse_status(pkg, line);
226                 else if ((mask & PFM_SUGGESTS) && is_field("Suggests", line))
227                         pkg->suggests_str = parse_list(line, &pkg->suggests_count, ',', 0);
228                 break;
229
230         case 'T':
231                 if ((mask & PFM_TAGS) && is_field("Tags", line))
232                         pkg->tags = parse_simple("Tags", line);
233                 break;
234
235         case 'V':
236                 if ((mask & PFM_VERSION) && is_field("Version", line))
237                         parse_version(pkg, line);
238                 break;
239
240         case ' ':
241                 if ((mask & PFM_DESCRIPTION) && reading_description) {
242                         pkg->description = xrealloc(pkg->description,
243                                                 strlen(pkg->description)
244                                                 + 1 + strlen(line) + 1);
245                         strcat(pkg->description, "\n");
246                         strcat(pkg->description, (line));
247                         goto dont_reset_flags;
248                 } else if ((mask & PFM_CONFFILES) && reading_conffiles) {
249                         parse_conffiles(pkg, line);
250                         goto dont_reset_flags;
251                 }
252
253                 /* FALLTHROUGH */
254         default:
255                 /* For package lists, signifies end of package. */
256                 if(line_is_blank(line)) {
257                         ret = 1;
258                         break;
259                 }
260         }
261
262         reading_description = 0;
263         reading_conffiles = 0;
264
265 dont_reset_flags:
266
267         return ret;
268 }
269
270 int
271 pkg_parse_from_stream(pkg_t *pkg, FILE *fp, uint mask)
272 {
273         int ret;
274         char *buf;
275         const size_t len = 4096;
276
277         buf = xmalloc(len);
278         ret = parse_from_stream_nomalloc(pkg_parse_line, pkg, fp, mask, &buf, len);
279         free(buf);
280
281         if (pkg->name == NULL) {
282                 /* probably just a blank line */
283                 ret = 1;
284         }
285
286         return ret;
287 }