dpkg (1.18.25) stretch; urgency=medium
[dpkg] / lib / dpkg / options.c
CommitLineData
1479465f
GJ
1/*
2 * libdpkg - Debian packaging suite library routines
3 * options.c - option parsing functions
4 *
5 * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk>
6 * Copyright © 2000,2002 Wichert Akkerman <wichert@deephackmode.org>
7 * Copyright © 2008-2015 Guillem Jover <guillem@debian.org>
8 *
9 * This is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23#include <config.h>
24#include <compat.h>
25
26#include <errno.h>
27#include <limits.h>
28#include <string.h>
29#include <dirent.h>
30#include <stdarg.h>
31#include <stdlib.h>
32
33#include <dpkg/i18n.h>
34#include <dpkg/c-ctype.h>
35#include <dpkg/dpkg.h>
36#include <dpkg/string.h>
37#include <dpkg/options.h>
38
39static const char *printforhelp;
40
41void
42badusage(const char *fmt, ...)
43{
44 char *buf = NULL;
45 va_list args;
46
47 va_start(args, fmt);
48 m_vasprintf(&buf, fmt, args);
49 va_end(args);
50
51 ohshit("%s\n\n%s", buf, gettext(printforhelp));
52}
53
54static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(3)
55config_error(const char *file_name, int line_num, const char *fmt, ...)
56{
57 char *buf = NULL;
58 va_list args;
59
60 va_start(args, fmt);
61 m_vasprintf(&buf, fmt, args);
62 va_end(args);
63
64 ohshit(_("configuration error: %s:%d: %s"), file_name, line_num, buf);
65}
66
67static void
68dpkg_options_load_file(const char *fn, const struct cmdinfo *cmdinfos)
69{
70 FILE *file;
71 int line_num = 0;
72 char linebuf[MAX_CONFIG_LINE];
73
74 file= fopen(fn, "r");
75 if (!file) {
76 if (errno==ENOENT)
77 return;
78 warning(_("failed to open configuration file '%.255s' for reading: %s"),
79 fn, strerror(errno));
80 return;
81 }
82
83 while (fgets(linebuf, sizeof(linebuf), file)) {
84 char *opt;
85 const struct cmdinfo *cip;
86 int l;
87
88 line_num++;
89
90 l = strlen(linebuf);
91 while (l && c_isspace(linebuf[l - 1]))
92 l--;
93 linebuf[l] = '\0';
94
95 if ((linebuf[0] == '#') || (linebuf[0] == '\0'))
96 continue;
97 for (opt = linebuf; c_isalnum(*opt) || *opt == '-'; opt++) ;
98 if (*opt == '\0')
99 opt=NULL;
100 else {
101 *opt++ = '\0';
102 if (*opt=='=') opt++;
103 while (c_isspace(*opt))
104 opt++;
105
106 opt = str_strip_quotes(opt);
107 if (opt == NULL)
108 config_error(fn, line_num, _("unbalanced quotes in '%s'"), linebuf);
109 }
110
111 for (cip=cmdinfos; cip->olong || cip->oshort; cip++) {
112 if (!cip->olong) continue;
113 if (strcmp(cip->olong, linebuf) == 0)
114 break;
115 l=strlen(cip->olong);
116 if ((cip->takesvalue==2) && (linebuf[l]=='-') &&
117 !opt && strncmp(linebuf, cip->olong, l) == 0) {
118 opt=linebuf+l+1;
119 break;
120 }
121 }
122
123 if (!cip->olong)
124 config_error(fn, line_num, _("unknown option '%s'"), linebuf);
125
126 if (cip->takesvalue) {
127 if (!opt)
128 config_error(fn, line_num, _("'%s' needs a value"), linebuf);
129 if (cip->call) cip->call(cip,opt);
130 else
131 *cip->sassignto = m_strdup(opt);
132 } else {
133 if (opt)
134 config_error(fn, line_num, _("'%s' does not take a value"), linebuf);
135 if (cip->call) cip->call(cip,NULL);
136 else
137 *cip->iassignto = cip->arg_int;
138 }
139 }
140 if (ferror(file))
141 ohshite(_("read error in configuration file '%.255s'"), fn);
142 if (fclose(file))
143 ohshite(_("error closing configuration file '%.255s'"), fn);
144}
145
146static int
147valid_config_filename(const struct dirent *dent)
148{
149 const char *c;
150
151 if (dent->d_name[0] == '.')
152 return 0;
153
154 for (c = dent->d_name; *c; c++)
155 if (!c_isalnum(*c) && *c != '_' && *c != '-')
156 return 0;
157
158 if (*c == '\0')
159 return 1;
160 else
161 return 0;
162}
163
164static void
165dpkg_options_load_dir(const char *prog, const struct cmdinfo *cmdinfos)
166{
167 char *dirname;
168 struct dirent **dlist;
169 int dlist_n, i;
170
171 dirname = str_fmt("%s/%s.cfg.d", CONFIGDIR, prog);
172
173 dlist_n = scandir(dirname, &dlist, valid_config_filename, alphasort);
174 if (dlist_n < 0) {
175 if (errno == ENOENT) {
176 free(dirname);
177 return;
178 } else
179 ohshite(_("error opening configuration directory '%s'"), dirname);
180 }
181
182 for (i = 0; i < dlist_n; i++) {
183 char *filename;
184
185 filename = str_fmt("%s/%s", dirname, dlist[i]->d_name);
186 dpkg_options_load_file(filename, cmdinfos);
187
188 free(dlist[i]);
189 free(filename);
190 }
191
192 free(dirname);
193 free(dlist);
194}
195
196void
197dpkg_options_load(const char *prog, const struct cmdinfo *cmdinfos)
198{
199 char *home, *file;
200
201 dpkg_options_load_dir(prog, cmdinfos);
202
203 file = str_fmt("%s/%s.cfg", CONFIGDIR, prog);
204 dpkg_options_load_file(file, cmdinfos);
205 free(file);
206
207 home = getenv("HOME");
208 if (home != NULL) {
209 file = str_fmt("%s/.%s.cfg", home, prog);
210 dpkg_options_load_file(file, cmdinfos);
211 free(file);
212 }
213}
214
215void
216dpkg_options_parse(const char *const **argvp, const struct cmdinfo *cmdinfos,
217 const char *help_str)
218{
219 const struct cmdinfo *cip;
220 const char *p, *value;
221 int l;
222
223 printforhelp = help_str;
224
225 ++(*argvp);
226 while ((p = **argvp) && p[0] == '-' && p[1] != '\0') {
227 ++(*argvp);
228 if (strcmp(p, "--") == 0)
229 break;
230 if (*++p == '-') {
231 ++p; value=NULL;
232 for (cip= cmdinfos;
233 cip->olong || cip->oshort;
234 cip++) {
235 if (!cip->olong) continue;
236 if (strcmp(p, cip->olong) == 0)
237 break;
238 l= strlen(cip->olong);
239 if (strncmp(p, cip->olong, l) == 0 &&
240 (p[l]== ((cip->takesvalue==2) ? '-' : '='))) { value=p+l+1; break; }
241 }
242 if (!cip->olong) badusage(_("unknown option --%s"),p);
243 if (cip->takesvalue) {
244 if (!value) {
245 value= *(*argvp)++;
246 if (!value) badusage(_("--%s option takes a value"),cip->olong);
247 }
248 if (cip->call) cip->call(cip,value);
249 else *cip->sassignto= value;
250 } else {
251 if (value) badusage(_("--%s option does not take a value"),cip->olong);
252 if (cip->call) cip->call(cip,NULL);
253 else
254 *cip->iassignto = cip->arg_int;
255 }
256 } else {
257 while (*p) {
258 for (cip= cmdinfos; (cip->olong || cip->oshort) && *p != cip->oshort; cip++);
259 if (!cip->oshort) badusage(_("unknown option -%c"),*p);
260 p++;
261 if (cip->takesvalue) {
262 if (!*p) {
263 value= *(*argvp)++;
264 if (!value) badusage(_("-%c option takes a value"),cip->oshort);
265 } else {
266 value= p; p="";
267 if (*value == '=') value++;
268 }
269 if (cip->call) cip->call(cip,value);
270 else *cip->sassignto= value;
271 } else {
272 if (*p == '=') badusage(_("-%c option does not take a value"),cip->oshort);
273 if (cip->call) cip->call(cip,NULL);
274 else
275 *cip->iassignto = cip->arg_int;
276 }
277 }
278 }
279 }
280}
281
282long
283dpkg_options_parse_arg_int(const struct cmdinfo *cmd, const char *str)
284{
285 long value;
286 char *end;
287
288 errno = 0;
289 value = strtol(str, &end, 0);
290 if (str == end || *end || value < 0 || value > INT_MAX || errno != 0) {
291 if (cmd->olong)
292 badusage(_("invalid integer for --%s: '%.250s'"), cmd->olong, str);
293 else
294 badusage(_("invalid integer for -%c: '%.250s'"), cmd->oshort, str);
295 }
296
297 return value;
298}
299
300void
301setobsolete(const struct cmdinfo *cip, const char *value)
302{
303 warning(_("obsolete option '--%s'"), cip->olong);
304}
305
306const struct cmdinfo *cipaction = NULL;
307
308/* XXX: This function is a hack. */
309static inline int
310option_short(int c)
311{
312 return c ? c : '\b';
313}
314
315void
316setaction(const struct cmdinfo *cip, const char *value)
317{
318 if (cipaction && cip)
319 badusage(_("conflicting actions -%c (--%s) and -%c (--%s)"),
320 option_short(cip->oshort), cip->olong,
321 option_short(cipaction->oshort), cipaction->olong);
322 cipaction = cip;
323}