#include <errno.h>
#include <glob.h>
#include <limits.h>
+#include <locale.h> /* setlocale */
#include <fcntl.h> /* open */
#include <stdlib.h>
#include <stdio.h>
/* global config variable */
config_t *config = NULL;
+#define NOCOLOR "\033[0m"
+
+#define BOLD "\033[0;1m"
+
+#define BLACK "\033[0;30m"
+#define RED "\033[0;31m"
+#define GREEN "\033[0;32m"
+#define YELLOW "\033[0;33m"
+#define BLUE "\033[0;34m"
+#define MAGENTA "\033[0;35m"
+#define CYAN "\033[0;36m"
+#define WHITE "\033[0;37m"
+
+#define BOLDBLACK "\033[1;30m"
+#define BOLDRED "\033[1;31m"
+#define BOLDGREEN "\033[1;32m"
+#define BOLDYELLOW "\033[1;33m"
+#define BOLDBLUE "\033[1;34m"
+#define BOLDMAGENTA "\033[1;35m"
+#define BOLDCYAN "\033[1;36m"
+#define BOLDWHITE "\033[1;37m"
+
+void enable_colors(int colors)
+{
+ colstr_t *colstr = &config->colstr;
+
+ if(colors == PM_COLOR_ON) {
+ colstr->colon = BOLDBLUE "::" BOLD " ";
+ colstr->title = BOLD;
+ colstr->repo = BOLDMAGENTA;
+ colstr->version = BOLDGREEN;
+ colstr->groups = BOLDBLUE;
+ colstr->meta = BOLDCYAN;
+ colstr->warn = BOLDYELLOW;
+ colstr->err = BOLDRED;
+ colstr->nocolor = NOCOLOR;
+ }
+}
+
config_t *config_new(void)
{
config_t *newconfig = calloc(1, sizeof(config_t));
if(alpm_capabilities() & ALPM_CAPABILITY_SIGNATURES) {
newconfig->siglevel = ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL |
ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL;
+ newconfig->localfilesiglevel = ALPM_SIG_USE_DEFAULT;
+ newconfig->remotefilesiglevel = ALPM_SIG_USE_DEFAULT;
}
+ newconfig->colstr.colon = ":: ";
+ newconfig->colstr.title = "";
+ newconfig->colstr.repo = "";
+ newconfig->colstr.version = "";
+ newconfig->colstr.groups = "";
+ newconfig->colstr.meta = "";
+ newconfig->colstr.warn = "";
+ newconfig->colstr.err = "";
+ newconfig->colstr.nocolor = "";
+
return newconfig;
}
}
/** Helper function for download_with_xfercommand() */
-static char *get_filename(const char *url) {
+static char *get_filename(const char *url)
+{
char *filename = strrchr(url, '/');
if(filename != NULL) {
filename++;
}
/** Helper function for download_with_xfercommand() */
-static char *get_destfile(const char *path, const char *filename) {
+static char *get_destfile(const char *path, const char *filename)
+{
char *destfile;
/* len = localpath len + filename len + null */
size_t len = strlen(path) + strlen(filename) + 1;
}
/** Helper function for download_with_xfercommand() */
-static char *get_tempfile(const char *path, const char *filename) {
+static char *get_tempfile(const char *path, const char *filename)
+{
char *tempfile;
/* len = localpath len + filename len + '.part' len + null */
size_t len = strlen(path) + strlen(filename) + 6;
/** External fetch callback */
static int download_with_xfercommand(const char *url, const char *localpath,
- int force) {
+ int force)
+{
int ret = 0, retval;
int usepart = 0;
int cwdfd;
cleanup:
/* restore the old cwd if we have it */
if(cwdfd >= 0) {
- int close_ret;
if(fchdir(cwdfd) != 0) {
pm_printf(ALPM_LOG_ERROR, _("could not restore working directory (%s)\n"),
strerror(errno));
}
- do {
- close_ret = close(cwdfd);
- } while(close_ret == -1 && errno == EINTR);
+ close(cwdfd);
}
if(ret == -1) {
if(strcmp(value, "Never") == 0) {
if(package) {
level &= ~ALPM_SIG_PACKAGE;
+ level |= ALPM_SIG_PACKAGE_SET;
}
if(database) {
level &= ~ALPM_SIG_DATABASE;
if(package) {
level |= ALPM_SIG_PACKAGE;
level |= ALPM_SIG_PACKAGE_OPTIONAL;
+ level |= ALPM_SIG_PACKAGE_SET;
}
if(database) {
level |= ALPM_SIG_DATABASE;
if(package) {
level |= ALPM_SIG_PACKAGE;
level &= ~ALPM_SIG_PACKAGE_OPTIONAL;
+ level |= ALPM_SIG_PACKAGE_SET;
}
if(database) {
level |= ALPM_SIG_DATABASE;
if(package) {
level &= ~ALPM_SIG_PACKAGE_MARGINAL_OK;
level &= ~ALPM_SIG_PACKAGE_UNKNOWN_OK;
+ level |= ALPM_SIG_PACKAGE_TRUST_SET;
}
if(database) {
level &= ~ALPM_SIG_DATABASE_MARGINAL_OK;
if(package) {
level |= ALPM_SIG_PACKAGE_MARGINAL_OK;
level |= ALPM_SIG_PACKAGE_UNKNOWN_OK;
+ level |= ALPM_SIG_PACKAGE_TRUST_SET;
}
if(database) {
level |= ALPM_SIG_DATABASE_MARGINAL_OK;
return ret;
}
+/**
+ * Merge the package entires of two signature verification levels.
+ * @param base initial siglevel
+ * @param over overridden siglevel, derived value is stored here
+ */
+static void merge_siglevel(alpm_siglevel_t *base, alpm_siglevel_t *over)
+{
+ alpm_siglevel_t level = *over;
+ if(level & ALPM_SIG_USE_DEFAULT) {
+ level = *base;
+ } else {
+ if(!(level & ALPM_SIG_PACKAGE_SET)) {
+ level |= *base & ALPM_SIG_PACKAGE;
+ level |= *base & ALPM_SIG_PACKAGE_OPTIONAL;
+ }
+ if(!(level & ALPM_SIG_PACKAGE_TRUST_SET)) {
+ level |= *base & ALPM_SIG_PACKAGE_MARGINAL_OK;
+ level |= *base & ALPM_SIG_PACKAGE_UNKNOWN_OK;
+ }
+ }
+
+ *over = level;
+}
+
static int process_cleanmethods(alpm_list_t *values,
const char *file, int linenum)
{
static void setrepeatingoption(char *ptr, const char *option,
alpm_list_t **list)
{
- char *q;
+ char *val, *saveptr;
- while((q = strchr(ptr, ' '))) {
- *q = '\0';
- *list = alpm_list_add(*list, strdup(ptr));
- pm_printf(ALPM_LOG_DEBUG, "config: %s: %s\n", option, ptr);
- ptr = q;
- ptr++;
+ val = strtok_r(ptr, " ", &saveptr);
+ while(val) {
+ *list = alpm_list_add(*list, strdup(val));
+ pm_printf(ALPM_LOG_DEBUG, "config: %s: %s\n", option, val);
+ val = strtok_r(NULL, " ", &saveptr);
}
- *list = alpm_list_add(*list, strdup(ptr));
- pm_printf(ALPM_LOG_DEBUG, "config: %s: %s\n", option, ptr);
}
static int _parse_options(const char *key, char *value,
pm_printf(ALPM_LOG_DEBUG, "config: totaldownload\n");
} else if(strcmp(key, "CheckSpace") == 0) {
config->checkspace = 1;
+ } else if(strcmp(key, "Color") == 0) {
+ if(config->color == PM_COLOR_UNSET) {
+ config->color = isatty(fileno(stdout)) ? PM_COLOR_ON : PM_COLOR_OFF;
+ enable_colors(config->color);
+ }
} else {
pm_printf(ALPM_LOG_WARNING,
_("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"),
} else if(strcmp(key, "UseDelta") == 0) {
double ratio;
char *endptr;
+ const char *oldlocale;
+
+ /* set the locale to 'C' for consistent decimal parsing (0.7 and never
+ * 0,7) from config files, then restore old setting when we are done */
+ oldlocale = setlocale(LC_NUMERIC, NULL);
+ setlocale(LC_NUMERIC, "C");
ratio = strtod(value, &endptr);
+ setlocale(LC_NUMERIC, oldlocale);
+
if(*endptr != '\0' || ratio < 0.0 || ratio > 2.0) {
pm_printf(ALPM_LOG_ERROR,
_("config file %s, line %d: invalid value for '%s' : '%s'\n"),
return 1;
}
FREELIST(values);
+ } else if(strcmp(key, "LocalFileSigLevel") == 0) {
+ alpm_list_t *values = NULL;
+ setrepeatingoption(value, "LocalFileSigLevel", &values);
+ if(process_siglevel(values, &config->localfilesiglevel, file, linenum)) {
+ FREELIST(values);
+ return 1;
+ }
+ FREELIST(values);
+ } else if(strcmp(key, "RemoteFileSigLevel") == 0) {
+ alpm_list_t *values = NULL;
+ setrepeatingoption(value, "RemoteFileSigLevel", &values);
+ if(process_siglevel(values, &config->remotefilesiglevel, file, linenum)) {
+ FREELIST(values);
+ return 1;
+ }
+ FREELIST(values);
} else {
pm_printf(ALPM_LOG_WARNING,
_("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"),
}
/** Sets up libalpm global stuff in one go. Called after the command line
- * and inital config file parsing. Once this is complete, we can see if any
+ * and initial config file parsing. Once this is complete, we can see if any
* paths were defined. If a rootdir was defined and nothing else, we want all
* of our paths to live under the rootdir that was specified. Safe to call
* multiple times (will only do anything the first time).
/* initialize library */
handle = alpm_initialize(config->rootdir, config->dbpath, &err);
if(!handle) {
- pm_printf(ALPM_LOG_ERROR, _("failed to initialize alpm library (%s)\n"),
- alpm_strerror(err));
+ pm_printf(ALPM_LOG_ERROR, _("failed to initialize alpm library\n(%s: %s)\n"),
+ alpm_strerror(err), config->dbpath);
if(err == ALPM_ERR_DB_VERSION) {
pm_printf(ALPM_LOG_ERROR, _(" try running pacman-db-upgrade\n"));
}
alpm_option_set_default_siglevel(handle, config->siglevel);
+ merge_siglevel(&config->siglevel, &config->localfilesiglevel);
+ merge_siglevel(&config->siglevel, &config->remotefilesiglevel);
+ alpm_option_set_local_file_siglevel(handle, config->localfilesiglevel);
+ alpm_option_set_remote_file_siglevel(handle, config->remotefilesiglevel);
+
if(config->xfercommand) {
alpm_option_set_fetchcb(handle, download_with_xfercommand);
} else if(!(alpm_capabilities() & ALPM_CAPABILITY_DOWNLOADER)) {
*/
struct section_t {
/* useful for all sections */
- char *name;
+ const char *name;
int is_options;
+ int parse_options;
/* db section option gathering */
alpm_siglevel_t siglevel;
alpm_list_t *servers;
};
+static int _parse_repo(const char *key, char *value, const char *file,
+ int line, struct section_t *section)
+{
+ int ret = 0;
+
+ if(strcmp(key, "Server") == 0) {
+ if(!value) {
+ pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: directive '%s' needs a value\n"),
+ file, line, key);
+ ret = 1;
+ } else {
+ section->servers = alpm_list_add(section->servers, strdup(value));
+ }
+ } else if(strcmp(key, "SigLevel") == 0) {
+ if(!value) {
+ pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: directive '%s' needs a value\n"),
+ file, line, key);
+ } else {
+ alpm_list_t *values = NULL;
+ setrepeatingoption(value, "SigLevel", &values);
+ if(values) {
+ if(section->siglevel == ALPM_SIG_USE_DEFAULT) {
+ section->siglevel = config->siglevel;
+ }
+ ret = process_siglevel(values, §ion->siglevel, file, line);
+ FREELIST(values);
+ }
+ }
+ } else {
+ pm_printf(ALPM_LOG_WARNING,
+ _("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"),
+ file, line, key, section->name);
+ }
+
+ return ret;
+}
+
/**
* Wrap up a section once we have reached the end of it. This should be called
* when a subsequent section is encountered, or when we have reached the end of
* @param parse_options whether we are parsing options or repo data
* @return 0 on success, 1 on failure
*/
-static int finish_section(struct section_t *section, int parse_options)
+static int finish_section(struct section_t *section)
{
int ret = 0;
alpm_list_t *i;
pm_printf(ALPM_LOG_DEBUG, "config: finish section '%s'\n", section->name);
/* parsing options (or nothing)- nothing to do except free the pieces */
- if(!section->name || parse_options || section->is_options) {
+ if(!section->name || section->parse_options || section->is_options) {
goto cleanup;
}
alpm_list_free(section->servers);
section->servers = NULL;
section->siglevel = ALPM_SIG_USE_DEFAULT;
- free(section->name);
section->name = NULL;
return ret;
}
+static int _parse_directive(const char *file, int linenum, const char *name,
+ char *key, char *value, struct section_t *section)
+{
+ if(!key && !value) {
+ int ret = finish_section(section);
+ pm_printf(ALPM_LOG_DEBUG, "config: new section '%s'\n", name);
+ section->name = name;
+ if(name && strcmp(name, "options") == 0) {
+ section->is_options = 1;
+ } else {
+ section->is_options = 0;
+ }
+ return ret;
+ }
+
+ if(section->name == NULL) {
+ pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: All directives must belong to a section.\n"),
+ file, linenum);
+ return 1;
+ }
+
+ if(section->parse_options && section->is_options) {
+ /* we are either in options ... */
+ return _parse_options(key, value, file, linenum);
+ } else if(!section->parse_options && !section->is_options) {
+ /* ... or in a repo section */
+ return _parse_repo(key, value, file, linenum, section);
+ }
+
+ return 0;
+}
+
/** The "real" parseconfig. Each "Include" directive will recall this method so
* recursion and stack depth are limited to 10 levels. The publicly visible
* parseconfig calls this with a NULL section argument so we can recall from
* within ourself on an include.
* @param file path to the config file
* @param section the current active section
- * @param parse_options whether to parse and call methods for the options
- * section; if 0, parse and call methods for the repos sections
* @param depth the current recursion depth
* @return 0 on success, 1 on failure
*/
static int _parseconfig(const char *file, struct section_t *section,
- int parse_options, int depth)
+ char **section_name, int depth)
{
FILE *fp = NULL;
char line[PATH_MAX];
/* new config section, skip the '[' */
name = strdup(line + 1);
name[line_len - 2] = '\0';
- /* we're at a new section; perform any post-actions for the prior */
- if(finish_section(section, parse_options)) {
- ret = 1;
+
+ ret = _parse_directive(file, linenum, name, NULL, NULL, section);
+ free(*section_name);
+ *section_name = name;
+
+ if(ret) {
goto cleanup;
}
- pm_printf(ALPM_LOG_DEBUG, "config: new section '%s'\n", name);
- section->name = name;
- section->is_options = (strcmp(name, "options") == 0);
continue;
}
ret = 1;
goto cleanup;
}
- /* For each directive, compare to the camelcase string. */
- if(section->name == NULL) {
- pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: All directives must belong to a section.\n"),
- file, linenum);
- ret = 1;
- goto cleanup;
- }
/* Include is allowed in both options and repo sections */
if(strcmp(key, "Include") == 0) {
glob_t globbuf;
for(gindex = 0; gindex < globbuf.gl_pathc; gindex++) {
pm_printf(ALPM_LOG_DEBUG, "config file %s, line %d: including %s\n",
file, linenum, globbuf.gl_pathv[gindex]);
- _parseconfig(globbuf.gl_pathv[gindex], section, parse_options, depth + 1);
+ _parseconfig(globbuf.gl_pathv[gindex], section,
+ section_name, depth + 1);
}
break;
}
globfree(&globbuf);
continue;
}
- if(parse_options && section->is_options) {
- /* we are either in options ... */
- if((ret = _parse_options(key, value, file, linenum)) != 0) {
- goto cleanup;
- }
- } else if(!parse_options && !section->is_options) {
- /* ... or in a repo section */
- if(strcmp(key, "Server") == 0) {
- if(value == NULL) {
- pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: directive '%s' needs a value\n"),
- file, linenum, key);
- ret = 1;
- goto cleanup;
- }
- section->servers = alpm_list_add(section->servers, strdup(value));
- } else if(strcmp(key, "SigLevel") == 0) {
- alpm_list_t *values = NULL;
- setrepeatingoption(value, "SigLevel", &values);
- if(values) {
- if(section->siglevel == ALPM_SIG_USE_DEFAULT) {
- section->siglevel = config->siglevel;
- }
- if(process_siglevel(values, §ion->siglevel, file, linenum)) {
- FREELIST(values);
- ret = 1;
- goto cleanup;
- }
- FREELIST(values);
- }
- } else {
- pm_printf(ALPM_LOG_WARNING,
- _("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"),
- file, linenum, key, section->name);
- }
+ if((ret = _parse_directive(file, linenum, *section_name, key, value, section)) != 0) {
+ goto cleanup;
}
}
if(depth == 0) {
- ret = finish_section(section, parse_options);
+ ret = _parse_directive(NULL, 0, NULL, NULL, NULL, section);
}
cleanup:
if(fp) {
fclose(fp);
}
+ if(depth == 0) {
+ free(*section_name);
+ *section_name = NULL;
+ }
pm_printf(ALPM_LOG_DEBUG, "config: finished parsing %s\n", file);
return ret;
}
{
int ret;
struct section_t section;
+ char *section_name = NULL;
memset(§ion, 0, sizeof(struct section_t));
section.siglevel = ALPM_SIG_USE_DEFAULT;
/* the config parse is a two-pass affair. We first parse the entire thing for
/* call the real parseconfig function with a null section & db argument */
pm_printf(ALPM_LOG_DEBUG, "parseconfig: options pass\n");
- if((ret = _parseconfig(file, §ion, 1, 0))) {
+ section.parse_options = 1;
+ if((ret = _parseconfig(file, §ion, §ion_name, 0))) {
return ret;
}
if((ret = setup_libalpm())) {
}
/* second pass, repo section parsing */
pm_printf(ALPM_LOG_DEBUG, "parseconfig: repo pass\n");
- return _parseconfig(file, §ion, 0, 0);
+ section.parse_options = 0;
+ return _parseconfig(file, §ion, §ion_name, 0);
}
/* vim: set ts=2 sw=2 noet: */