| /* |
| * libdpkg - Debian packaging suite library routines |
| * options.c - option parsing functions |
| * |
| * Copyright © 1994,1995 Ian Jackson <ian@chiark.greenend.org.uk> |
| * Copyright © 2000,2002 Wichert Akkerman <wichert@deephackmode.org> |
| * Copyright © 2008,2009 Guillem Jover <guillem@debian.org> |
| * |
| * This is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include <config.h> |
| #include <compat.h> |
| |
| #include <errno.h> |
| #include <ctype.h> |
| #include <string.h> |
| #include <dirent.h> |
| #include <stdarg.h> |
| #include <stdlib.h> |
| |
| #include <dpkg/i18n.h> |
| #include <dpkg/dpkg.h> |
| #include <dpkg/string.h> |
| #include <dpkg/options.h> |
| |
| static const char *printforhelp; |
| |
| void |
| badusage(const char *fmt, ...) |
| { |
| char buf[1024]; |
| va_list args; |
| |
| va_start(args, fmt); |
| vsnprintf(buf, sizeof(buf), fmt, args); |
| va_end(args); |
| |
| ohshit("%s\n\n%s", buf, gettext(printforhelp)); |
| } |
| |
| static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(3) |
| config_error(const char *file_name, int line_num, const char *fmt, ...) |
| { |
| char buf[1024]; |
| va_list args; |
| |
| va_start(args, fmt); |
| vsnprintf(buf, sizeof(buf), fmt, args); |
| va_end(args); |
| |
| ohshit(_("configuration error: %s:%d: %s"), file_name, line_num, buf); |
| } |
| |
| void myfileopt(const char* fn, const struct cmdinfo* cmdinfos) { |
| FILE* file; |
| int line_num = 0; |
| char linebuf[MAX_CONFIG_LINE]; |
| |
| file= fopen(fn, "r"); |
| if (!file) { |
| if (errno==ENOENT) |
| return; |
| warning(_("failed to open configuration file '%.255s' for reading: %s"), |
| fn, strerror(errno)); |
| return; |
| } |
| |
| while (fgets(linebuf, sizeof(linebuf), file)) { |
| char* opt; |
| const struct cmdinfo *cip; |
| int l; |
| |
| line_num++; |
| |
| if ((linebuf[0] == '#') || (linebuf[0] == '\n') || (linebuf[0] == '\0')) |
| continue; |
| l=strlen(linebuf); |
| if (linebuf[l - 1] == '\n') |
| linebuf[l - 1] = '\0'; |
| for (opt=linebuf;isalnum(*opt)||*opt=='-';opt++) ; |
| if (*opt == '\0') |
| opt=NULL; |
| else { |
| *opt++ = '\0'; |
| if (*opt=='=') opt++; |
| while (isspace(*opt)) opt++; |
| |
| opt = str_strip_quotes(opt); |
| if (opt == NULL) |
| config_error(fn, line_num, _("unbalanced quotes in '%s'"), linebuf); |
| } |
| |
| for (cip=cmdinfos; cip->olong || cip->oshort; cip++) { |
| if (!cip->olong) continue; |
| if (!strcmp(cip->olong,linebuf)) break; |
| l=strlen(cip->olong); |
| if ((cip->takesvalue==2) && (linebuf[l]=='-') && |
| !opt && !strncmp(linebuf,cip->olong,l)) { |
| opt=linebuf+l+1; |
| break; |
| } |
| } |
| |
| if (!cip->olong) |
| config_error(fn, line_num, _("unknown option '%s'"), linebuf); |
| |
| if (cip->takesvalue) { |
| if (!opt) |
| config_error(fn, line_num, _("'%s' needs a value"), linebuf); |
| if (cip->call) cip->call(cip,opt); |
| else |
| *cip->sassignto = m_strdup(opt); |
| } else { |
| if (opt) |
| config_error(fn, line_num, _("'%s' does not take a value"), linebuf); |
| if (cip->call) cip->call(cip,NULL); |
| else |
| *cip->iassignto = cip->arg_int; |
| } |
| } |
| if (ferror(file)) ohshite(_("read error in configuration file `%.255s'"), fn); |
| if (fclose(file)) ohshite(_("error closing configuration file `%.255s'"), fn); |
| } |
| |
| static int |
| valid_config_filename(const struct dirent *dent) |
| { |
| const char *c; |
| |
| if (dent->d_name[0] == '.') |
| return 0; |
| |
| for (c = dent->d_name; *c; c++) |
| if (!isalnum(*c) && *c != '_' && *c != '-') |
| return 0; |
| |
| if (*c == '\0') |
| return 1; |
| else |
| return 0; |
| } |
| |
| static void |
| load_config_dir(const char *prog, const struct cmdinfo* cmdinfos) |
| { |
| char *dirname; |
| struct dirent **dlist; |
| int dlist_n, i; |
| |
| m_asprintf(&dirname, "%s/%s.cfg.d", CONFIGDIR, prog); |
| |
| dlist_n = scandir(dirname, &dlist, valid_config_filename, alphasort); |
| if (dlist_n < 0) { |
| if (errno == ENOENT) { |
| free(dirname); |
| return; |
| } else |
| ohshite(_("error opening configuration directory '%s'"), dirname); |
| } |
| |
| for (i = 0; i < dlist_n; i++) { |
| char *filename; |
| |
| m_asprintf(&filename, "%s/%s", dirname, dlist[i]->d_name); |
| myfileopt(filename, cmdinfos); |
| |
| free(dlist[i]); |
| free(filename); |
| } |
| |
| free(dirname); |
| free(dlist); |
| } |
| |
| void loadcfgfile(const char *prog, const struct cmdinfo* cmdinfos) { |
| char *home, *file; |
| |
| load_config_dir(prog, cmdinfos); |
| |
| m_asprintf(&file, "%s/%s.cfg", CONFIGDIR, prog); |
| myfileopt(file, cmdinfos); |
| free(file); |
| |
| if ((home = getenv("HOME")) != NULL) { |
| m_asprintf(&file, "%s/.%s.cfg", home, prog); |
| myfileopt(file, cmdinfos); |
| free(file); |
| } |
| } |
| |
| void |
| myopt(const char *const **argvp, const struct cmdinfo *cmdinfos, |
| const char *help_str) |
| { |
| const struct cmdinfo *cip; |
| const char *p, *value; |
| int l; |
| |
| printforhelp = help_str; |
| |
| ++(*argvp); |
| while ((p= **argvp) && *p == '-') { |
| ++(*argvp); |
| if (!strcmp(p,"--")) break; |
| if (*++p == '-') { |
| ++p; value=NULL; |
| for (cip= cmdinfos; |
| cip->olong || cip->oshort; |
| cip++) { |
| if (!cip->olong) continue; |
| if (!strcmp(p,cip->olong)) break; |
| l= strlen(cip->olong); |
| if (!strncmp(p,cip->olong,l) && |
| (p[l]== ((cip->takesvalue==2) ? '-' : '='))) { value=p+l+1; break; } |
| } |
| if (!cip->olong) badusage(_("unknown option --%s"),p); |
| if (cip->takesvalue) { |
| if (!value) { |
| value= *(*argvp)++; |
| if (!value) badusage(_("--%s option takes a value"),cip->olong); |
| } |
| if (cip->call) cip->call(cip,value); |
| else *cip->sassignto= value; |
| } else { |
| if (value) badusage(_("--%s option does not take a value"),cip->olong); |
| if (cip->call) cip->call(cip,NULL); |
| else |
| *cip->iassignto = cip->arg_int; |
| } |
| } else { |
| while (*p) { |
| for (cip= cmdinfos; (cip->olong || cip->oshort) && *p != cip->oshort; cip++); |
| if (!cip->oshort) badusage(_("unknown option -%c"),*p); |
| p++; |
| if (cip->takesvalue) { |
| if (!*p) { |
| value= *(*argvp)++; |
| if (!value) badusage(_("-%c option takes a value"),cip->oshort); |
| } else { |
| value= p; p=""; |
| if (*value == '=') value++; |
| } |
| if (cip->call) cip->call(cip,value); |
| else *cip->sassignto= value; |
| } else { |
| if (*p == '=') badusage(_("-%c option does not take a value"),cip->oshort); |
| if (cip->call) cip->call(cip,NULL); |
| else |
| *cip->iassignto = cip->arg_int; |
| } |
| } |
| } |
| } |
| } |
| |
| void |
| setobsolete(const struct cmdinfo *cip, const char *value) |
| { |
| warning(_("obsolete option '--%s'\n"), cip->olong); |
| } |
| |
| const struct cmdinfo *cipaction = NULL; |
| |
| /* XXX: This function is a hack. */ |
| static inline int |
| option_short(int c) |
| { |
| return c ? c : '\b'; |
| } |
| |
| void |
| setaction(const struct cmdinfo *cip, const char *value) |
| { |
| if (cipaction) |
| badusage(_("conflicting actions -%c (--%s) and -%c (--%s)"), |
| option_short(cip->oshort), cip->olong, |
| option_short(cipaction->oshort), cipaction->olong); |
| cipaction = cip; |
| } |