Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | /* |
2 | * libdpkg - Debian packaging suite library routines | |
3 | * parsehelp.c - helpful routines for parsing and writing | |
4 | * | |
5 | * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> | |
6 | * Copyright © 2006-2012 Guillem Jover <guillem@debian.org> | |
7 | * | |
8 | * This is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | * | |
13 | * This is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
20 | */ | |
21 | ||
22 | #include <config.h> | |
23 | #include <compat.h> | |
24 | ||
25 | #include <errno.h> | |
26 | #include <limits.h> | |
27 | #include <string.h> | |
28 | #include <stdlib.h> | |
29 | #include <stdio.h> | |
30 | ||
31 | #include <dpkg/i18n.h> | |
32 | #include <dpkg/c-ctype.h> | |
33 | #include <dpkg/dpkg.h> | |
34 | #include <dpkg/dpkg-db.h> | |
35 | #include <dpkg/string.h> | |
36 | #include <dpkg/error.h> | |
37 | #include <dpkg/parsedump.h> | |
38 | ||
39 | static const char * | |
40 | parse_error_msg(struct parsedb_state *ps, const char *fmt) | |
41 | { | |
42 | static char msg[1024]; | |
43 | char filename[256]; | |
44 | ||
45 | str_escape_fmt(filename, ps->filename, sizeof(filename)); | |
46 | ||
47 | if (ps->pkg && ps->pkg->set->name) { | |
48 | char pkgname[256]; | |
49 | ||
50 | str_escape_fmt(pkgname, pkgbin_name(ps->pkg, ps->pkgbin, pnaw_nonambig), | |
51 | sizeof(pkgname)); | |
52 | sprintf(msg, _("parsing file '%.255s' near line %d package '%.255s':\n" | |
53 | " %.255s"), filename, ps->lno, pkgname, fmt); | |
54 | } else | |
55 | sprintf(msg, _("parsing file '%.255s' near line %d:\n" | |
56 | " %.255s"), filename, ps->lno, fmt); | |
57 | ||
58 | return msg; | |
59 | } | |
60 | ||
61 | void | |
62 | parse_error(struct parsedb_state *ps, const char *fmt, ...) | |
63 | { | |
64 | va_list args; | |
65 | ||
66 | va_start(args, fmt); | |
67 | ohshitv(parse_error_msg(ps, fmt), args); | |
68 | } | |
69 | ||
70 | void | |
71 | parse_warn(struct parsedb_state *ps, const char *fmt, ...) | |
72 | { | |
73 | va_list args; | |
74 | ||
75 | va_start(args, fmt); | |
76 | warningv(parse_error_msg(ps, fmt), args); | |
77 | va_end(args); | |
78 | } | |
79 | ||
80 | const struct fieldinfo * | |
81 | find_field_info(const struct fieldinfo *fields, const char *fieldname) | |
82 | { | |
83 | const struct fieldinfo *field; | |
84 | ||
85 | for (field = fields; field->name; field++) | |
86 | if (strcasecmp(field->name, fieldname) == 0) | |
87 | return field; | |
88 | ||
89 | return NULL; | |
90 | } | |
91 | ||
92 | const struct arbitraryfield * | |
93 | find_arbfield_info(const struct arbitraryfield *arbs, const char *fieldname) | |
94 | { | |
95 | const struct arbitraryfield *arbfield; | |
96 | ||
97 | for (arbfield = arbs; arbfield; arbfield = arbfield->next) | |
98 | if (strcasecmp(arbfield->name, fieldname) == 0) | |
99 | return arbfield; | |
100 | ||
101 | return NULL; | |
102 | } | |
103 | ||
104 | const char * | |
105 | pkg_name_is_illegal(const char *p) | |
106 | { | |
107 | /* FIXME: _ is deprecated, remove sometime. */ | |
108 | static const char alsoallowed[] = "-+._"; | |
109 | static char buf[150]; | |
110 | int c; | |
111 | ||
112 | if (!*p) return _("may not be empty string"); | |
113 | if (!c_isalnum(*p)) | |
114 | return _("must start with an alphanumeric character"); | |
115 | while ((c = *p++) != '\0') | |
116 | if (!c_isalnum(c) && !strchr(alsoallowed, c)) | |
117 | break; | |
118 | if (!c) return NULL; | |
119 | ||
120 | snprintf(buf, sizeof(buf), _( | |
121 | "character '%c' not allowed (only letters, digits and characters '%s')"), | |
122 | c, alsoallowed); | |
123 | return buf; | |
124 | } | |
125 | ||
126 | void varbufversion | |
127 | (struct varbuf *vb, | |
128 | const struct dpkg_version *version, | |
129 | enum versiondisplayepochwhen vdew) | |
130 | { | |
131 | switch (vdew) { | |
132 | case vdew_never: | |
133 | break; | |
134 | case vdew_nonambig: | |
135 | if (!version->epoch && | |
136 | (!version->version || !strchr(version->version,':')) && | |
137 | (!version->revision || !strchr(version->revision,':'))) break; | |
138 | /* Fall through. */ | |
139 | case vdew_always: | |
140 | varbuf_printf(vb, "%u:", version->epoch); | |
141 | break; | |
142 | default: | |
143 | internerr("unknown versiondisplayepochwhen '%d'", vdew); | |
144 | } | |
145 | if (version->version) | |
146 | varbuf_add_str(vb, version->version); | |
147 | if (str_is_set(version->revision)) { | |
148 | varbuf_add_char(vb, '-'); | |
149 | varbuf_add_str(vb, version->revision); | |
150 | } | |
151 | } | |
152 | ||
153 | const char *versiondescribe | |
154 | (const struct dpkg_version *version, | |
155 | enum versiondisplayepochwhen vdew) | |
156 | { | |
157 | static struct varbuf bufs[10]; | |
158 | static int bufnum=0; | |
159 | ||
160 | struct varbuf *vb; | |
161 | ||
162 | if (!dpkg_version_is_informative(version)) | |
163 | return C_("version", "<none>"); | |
164 | ||
165 | vb= &bufs[bufnum]; bufnum++; if (bufnum == 10) bufnum= 0; | |
166 | varbuf_reset(vb); | |
167 | varbufversion(vb,version,vdew); | |
168 | varbuf_end_str(vb); | |
169 | ||
170 | return vb->buf; | |
171 | } | |
172 | ||
173 | /** | |
174 | * Parse a version string and check for invalid syntax. | |
175 | * | |
176 | * Distinguish between lax (warnings) and strict (error) parsing. | |
177 | * | |
178 | * @param rversion The parsed version. | |
179 | * @param string The version string to parse. | |
180 | * @param err The warning or error message if any. | |
181 | * | |
182 | * @retval 0 On success. | |
183 | * @retval -1 On failure, and err is set accordingly. | |
184 | */ | |
185 | int | |
186 | parseversion(struct dpkg_version *rversion, const char *string, | |
187 | struct dpkg_error *err) | |
188 | { | |
189 | char *hyphen, *colon, *eepochcolon; | |
190 | const char *end, *ptr; | |
191 | ||
192 | /* Trim leading and trailing space. */ | |
193 | while (*string && c_isblank(*string)) | |
194 | string++; | |
195 | ||
196 | if (!*string) | |
197 | return dpkg_put_error(err, _("version string is empty")); | |
198 | ||
199 | /* String now points to the first non-whitespace char. */ | |
200 | end = string; | |
201 | /* Find either the end of the string, or a whitespace char. */ | |
202 | while (*end && !c_isblank(*end)) | |
203 | end++; | |
204 | /* Check for extra chars after trailing space. */ | |
205 | ptr = end; | |
206 | while (*ptr && c_isblank(*ptr)) | |
207 | ptr++; | |
208 | if (*ptr) | |
209 | return dpkg_put_error(err, _("version string has embedded spaces")); | |
210 | ||
211 | colon= strchr(string,':'); | |
212 | if (colon) { | |
213 | long epoch; | |
214 | ||
215 | errno = 0; | |
216 | epoch = strtol(string, &eepochcolon, 10); | |
217 | if (string == eepochcolon) | |
218 | return dpkg_put_error(err, _("epoch in version is empty")); | |
219 | if (colon != eepochcolon) | |
220 | return dpkg_put_error(err, _("epoch in version is not number")); | |
221 | if (epoch < 0) | |
222 | return dpkg_put_error(err, _("epoch in version is negative")); | |
223 | if (epoch > INT_MAX || errno == ERANGE) | |
224 | return dpkg_put_error(err, _("epoch in version is too big")); | |
225 | if (!*++colon) | |
226 | return dpkg_put_error(err, _("nothing after colon in version number")); | |
227 | string= colon; | |
228 | rversion->epoch= epoch; | |
229 | } else { | |
230 | rversion->epoch= 0; | |
231 | } | |
232 | rversion->version= nfstrnsave(string,end-string); | |
233 | hyphen= strrchr(rversion->version,'-'); | |
234 | if (hyphen) { | |
235 | *hyphen++ = '\0'; | |
236 | ||
237 | if (*hyphen == '\0') | |
238 | return dpkg_put_error(err, _("revision number is empty")); | |
239 | } | |
240 | rversion->revision= hyphen ? hyphen : ""; | |
241 | ||
242 | /* XXX: Would be faster to use something like cisversion and cisrevision. */ | |
243 | ptr = rversion->version; | |
244 | if (!*ptr) | |
245 | return dpkg_put_error(err, _("version number is empty")); | |
246 | if (*ptr && !c_isdigit(*ptr++)) | |
247 | return dpkg_put_warn(err, _("version number does not start with digit")); | |
248 | for (; *ptr; ptr++) { | |
249 | if (!c_isdigit(*ptr) && !c_isalpha(*ptr) && strchr(".-+~:", *ptr) == NULL) | |
250 | return dpkg_put_warn(err, _("invalid character in version number")); | |
251 | } | |
252 | for (ptr = rversion->revision; *ptr; ptr++) { | |
253 | if (!c_isdigit(*ptr) && !c_isalpha(*ptr) && strchr(".+~", *ptr) == NULL) | |
254 | return dpkg_put_warn(err, _("invalid character in revision number")); | |
255 | } | |
256 | ||
257 | return 0; | |
258 | } | |
259 | ||
260 | /** | |
261 | * Parse a version string coming from a database file. | |
262 | * | |
263 | * It parses a version string, and prints a warning or an error depending | |
264 | * on the parse options. | |
265 | * | |
266 | * @param ps The parsedb state. | |
267 | * @param version The version to parse into. | |
268 | * @param value The version string to parse from. | |
269 | * @param fmt The error format string. | |
270 | */ | |
271 | void | |
272 | parse_db_version(struct parsedb_state *ps, struct dpkg_version *version, | |
273 | const char *value, const char *fmt, ...) | |
274 | { | |
275 | struct dpkg_error err; | |
276 | va_list args; | |
277 | char buf[1000]; | |
278 | ||
279 | if (parseversion(version, value, &err) == 0) | |
280 | return; | |
281 | ||
282 | va_start(args, fmt); | |
283 | vsnprintf(buf, sizeof(buf), fmt, args); | |
284 | va_end(args); | |
285 | ||
286 | if (err.type == DPKG_MSG_WARN && (ps->flags & pdb_lax_version_parser)) | |
287 | parse_warn(ps, "%s: %.250s", buf, err.str); | |
288 | else | |
289 | parse_error(ps, "%s: %.250s", buf, err.str); | |
290 | ||
291 | dpkg_error_destroy(&err); | |
292 | } | |
293 | ||
294 | void | |
295 | parse_must_have_field(struct parsedb_state *ps, | |
296 | const char *value, const char *what) | |
297 | { | |
298 | if (!value) | |
299 | parse_error(ps, _("missing %s"), what); | |
300 | else if (!*value) | |
301 | parse_error(ps, _("empty value for %s"), what); | |
302 | } | |
303 | ||
304 | void | |
305 | parse_ensure_have_field(struct parsedb_state *ps, | |
306 | const char **value, const char *what) | |
307 | { | |
308 | static const char empty[] = ""; | |
309 | ||
310 | if (!*value) { | |
311 | parse_warn(ps, _("missing %s"), what); | |
312 | *value = empty; | |
313 | } else if (!**value) { | |
314 | parse_warn(ps, _("empty value for %s"), what); | |
315 | } | |
316 | } |