Move important information up in -Si output
[pacman-ng.git] / src / pacman / conf.c
blobf47b92dc2feccc4bf6dada1f08b7c49fe7413978
1 /*
2 * conf.c
4 * Copyright (c) 2006-2012 Pacman Development Team <pacman-dev@archlinux.org>
5 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include <errno.h>
22 #include <glob.h>
23 #include <limits.h>
24 #include <fcntl.h> /* open */
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h> /* strdup */
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/utsname.h> /* uname */
31 #include <unistd.h>
33 /* pacman */
34 #include "conf.h"
35 #include "util.h"
36 #include "pacman.h"
37 #include "callback.h"
39 /* global config variable */
40 config_t *config = NULL;
42 config_t *config_new(void)
44 config_t *newconfig = calloc(1, sizeof(config_t));
45 if(!newconfig) {
46 pm_printf(ALPM_LOG_ERROR,
47 _("malloc failure: could not allocate %zd bytes\n"),
48 sizeof(config_t));
49 return NULL;
51 /* defaults which may get overridden later */
52 newconfig->op = PM_OP_MAIN;
53 newconfig->logmask = ALPM_LOG_ERROR | ALPM_LOG_WARNING;
54 newconfig->configfile = strdup(CONFFILE);
55 newconfig->deltaratio = 0.0;
56 if(alpm_capabilities() & ALPM_CAPABILITY_SIGNATURES) {
57 newconfig->siglevel = ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL |
58 ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL;
61 return newconfig;
64 int config_free(config_t *oldconfig)
66 if(oldconfig == NULL) {
67 return -1;
70 alpm_list_free(oldconfig->explicit_adds);
71 alpm_list_free(oldconfig->explicit_removes);
73 FREELIST(oldconfig->holdpkg);
74 FREELIST(oldconfig->ignorepkg);
75 FREELIST(oldconfig->ignoregrp);
76 FREELIST(oldconfig->noupgrade);
77 FREELIST(oldconfig->noextract);
78 free(oldconfig->configfile);
79 free(oldconfig->rootdir);
80 free(oldconfig->dbpath);
81 free(oldconfig->logfile);
82 free(oldconfig->gpgdir);
83 FREELIST(oldconfig->cachedirs);
84 free(oldconfig->xfercommand);
85 free(oldconfig->print_format);
86 free(oldconfig->arch);
87 free(oldconfig);
89 return 0;
92 /** Helper function for download_with_xfercommand() */
93 static char *get_filename(const char *url) {
94 char *filename = strrchr(url, '/');
95 if(filename != NULL) {
96 filename++;
98 return filename;
101 /** Helper function for download_with_xfercommand() */
102 static char *get_destfile(const char *path, const char *filename) {
103 char *destfile;
104 /* len = localpath len + filename len + null */
105 size_t len = strlen(path) + strlen(filename) + 1;
106 destfile = calloc(len, sizeof(char));
107 snprintf(destfile, len, "%s%s", path, filename);
109 return destfile;
112 /** Helper function for download_with_xfercommand() */
113 static char *get_tempfile(const char *path, const char *filename) {
114 char *tempfile;
115 /* len = localpath len + filename len + '.part' len + null */
116 size_t len = strlen(path) + strlen(filename) + 6;
117 tempfile = calloc(len, sizeof(char));
118 snprintf(tempfile, len, "%s%s.part", path, filename);
120 return tempfile;
123 /** External fetch callback */
124 static int download_with_xfercommand(const char *url, const char *localpath,
125 int force) {
126 int ret = 0, retval;
127 int usepart = 0;
128 int cwdfd;
129 struct stat st;
130 char *parsedcmd,*tempcmd;
131 char *destfile, *tempfile, *filename;
133 if(!config->xfercommand) {
134 return -1;
137 filename = get_filename(url);
138 if(!filename) {
139 return -1;
141 destfile = get_destfile(localpath, filename);
142 tempfile = get_tempfile(localpath, filename);
144 if(force && stat(tempfile, &st) == 0) {
145 unlink(tempfile);
147 if(force && stat(destfile, &st) == 0) {
148 unlink(destfile);
151 tempcmd = strdup(config->xfercommand);
152 /* replace all occurrences of %o with fn.part */
153 if(strstr(tempcmd, "%o")) {
154 usepart = 1;
155 parsedcmd = strreplace(tempcmd, "%o", tempfile);
156 free(tempcmd);
157 tempcmd = parsedcmd;
159 /* replace all occurrences of %u with the download URL */
160 parsedcmd = strreplace(tempcmd, "%u", url);
161 free(tempcmd);
163 /* save the cwd so we can restore it later */
164 do {
165 cwdfd = open(".", O_RDONLY);
166 } while(cwdfd == -1 && errno == EINTR);
167 if(cwdfd < 0) {
168 pm_printf(ALPM_LOG_ERROR, _("could not get current working directory\n"));
171 /* cwd to the download directory */
172 if(chdir(localpath)) {
173 pm_printf(ALPM_LOG_WARNING, _("could not chdir to download directory %s\n"), localpath);
174 ret = -1;
175 goto cleanup;
177 /* execute the parsed command via /bin/sh -c */
178 pm_printf(ALPM_LOG_DEBUG, "running command: %s\n", parsedcmd);
179 retval = system(parsedcmd);
181 if(retval == -1) {
182 pm_printf(ALPM_LOG_WARNING, _("running XferCommand: fork failed!\n"));
183 ret = -1;
184 } else if(retval != 0) {
185 /* download failed */
186 pm_printf(ALPM_LOG_DEBUG, "XferCommand command returned non-zero status "
187 "code (%d)\n", retval);
188 ret = -1;
189 } else {
190 /* download was successful */
191 ret = 0;
192 if(usepart) {
193 if(rename(tempfile, destfile)) {
194 pm_printf(ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"),
195 tempfile, destfile, strerror(errno));
196 ret = -1;
201 cleanup:
202 /* restore the old cwd if we have it */
203 if(cwdfd >= 0) {
204 int close_ret;
205 if(fchdir(cwdfd) != 0) {
206 pm_printf(ALPM_LOG_ERROR, _("could not restore working directory (%s)\n"),
207 strerror(errno));
209 do {
210 close_ret = close(cwdfd);
211 } while(close_ret == -1 && errno == EINTR);
214 if(ret == -1) {
215 /* hack to let an user the time to cancel a download */
216 sleep(2);
218 free(destfile);
219 free(tempfile);
220 free(parsedcmd);
222 return ret;
226 int config_set_arch(const char *arch)
228 if(strcmp(arch, "auto") == 0) {
229 struct utsname un;
230 uname(&un);
231 config->arch = strdup(un.machine);
232 } else {
233 config->arch = strdup(arch);
235 pm_printf(ALPM_LOG_DEBUG, "config: arch: %s\n", config->arch);
236 return 0;
240 * Parse a signature verification level line.
241 * @param values the list of parsed option values
242 * @param storage location to store the derived signature level; any existing
243 * value here is used as a starting point
244 * @param file path to the config file
245 * @param linenum current line number in file
246 * @return 0 on success, 1 on any parsing error
248 static int process_siglevel(alpm_list_t *values, alpm_siglevel_t *storage,
249 const char *file, int linenum)
251 alpm_siglevel_t level = *storage;
252 alpm_list_t *i;
253 int ret = 0;
255 /* Collapse the option names into a single bitmasked value */
256 for(i = values; i; i = alpm_list_next(i)) {
257 const char *original = i->data, *value;
258 int package = 0, database = 0;
260 if(strncmp(original, "Package", strlen("Package")) == 0) {
261 /* only packages are affected, don't flip flags for databases */
262 value = original + strlen("Package");
263 package = 1;
264 } else if(strncmp(original, "Database", strlen("Database")) == 0) {
265 /* only databases are affected, don't flip flags for packages */
266 value = original + strlen("Database");
267 database = 1;
268 } else {
269 /* no prefix, so anything found will affect both packages and dbs */
270 value = original;
271 package = database = 1;
274 /* now parse out and store actual flag if it is valid */
275 if(strcmp(value, "Never") == 0) {
276 if(package) {
277 level &= ~ALPM_SIG_PACKAGE;
279 if(database) {
280 level &= ~ALPM_SIG_DATABASE;
282 } else if(strcmp(value, "Optional") == 0) {
283 if(package) {
284 level |= ALPM_SIG_PACKAGE;
285 level |= ALPM_SIG_PACKAGE_OPTIONAL;
287 if(database) {
288 level |= ALPM_SIG_DATABASE;
289 level |= ALPM_SIG_DATABASE_OPTIONAL;
291 } else if(strcmp(value, "Required") == 0) {
292 if(package) {
293 level |= ALPM_SIG_PACKAGE;
294 level &= ~ALPM_SIG_PACKAGE_OPTIONAL;
296 if(database) {
297 level |= ALPM_SIG_DATABASE;
298 level &= ~ALPM_SIG_DATABASE_OPTIONAL;
300 } else if(strcmp(value, "TrustedOnly") == 0) {
301 if(package) {
302 level &= ~ALPM_SIG_PACKAGE_MARGINAL_OK;
303 level &= ~ALPM_SIG_PACKAGE_UNKNOWN_OK;
305 if(database) {
306 level &= ~ALPM_SIG_DATABASE_MARGINAL_OK;
307 level &= ~ALPM_SIG_DATABASE_UNKNOWN_OK;
309 } else if(strcmp(value, "TrustAll") == 0) {
310 if(package) {
311 level |= ALPM_SIG_PACKAGE_MARGINAL_OK;
312 level |= ALPM_SIG_PACKAGE_UNKNOWN_OK;
314 if(database) {
315 level |= ALPM_SIG_DATABASE_MARGINAL_OK;
316 level |= ALPM_SIG_DATABASE_UNKNOWN_OK;
318 } else {
319 pm_printf(ALPM_LOG_ERROR,
320 _("config file %s, line %d: invalid value for '%s' : '%s'\n"),
321 file, linenum, "SigLevel", original);
322 ret = 1;
324 level &= ~ALPM_SIG_USE_DEFAULT;
327 /* ensure we have sig checking ability and are actually turning it on */
328 if(!(alpm_capabilities() & ALPM_CAPABILITY_SIGNATURES) &&
329 level & (ALPM_SIG_PACKAGE | ALPM_SIG_DATABASE)) {
330 pm_printf(ALPM_LOG_ERROR,
331 _("config file %s, line %d: '%s' option invalid, no signature support\n"),
332 file, linenum, "SigLevel");
333 ret = 1;
336 if(!ret) {
337 *storage = level;
339 return ret;
342 static int process_cleanmethods(alpm_list_t *values,
343 const char *file, int linenum)
345 alpm_list_t *i;
346 for(i = values; i; i = alpm_list_next(i)) {
347 const char *value = i->data;
348 if(strcmp(value, "KeepInstalled") == 0) {
349 config->cleanmethod |= PM_CLEAN_KEEPINST;
350 } else if(strcmp(value, "KeepCurrent") == 0) {
351 config->cleanmethod |= PM_CLEAN_KEEPCUR;
352 } else {
353 pm_printf(ALPM_LOG_ERROR,
354 _("config file %s, line %d: invalid value for '%s' : '%s'\n"),
355 file, linenum, "CleanMethod", value);
356 return 1;
359 return 0;
362 /** Add repeating options such as NoExtract, NoUpgrade, etc to libalpm
363 * settings. Refactored out of the parseconfig code since all of them did
364 * the exact same thing and duplicated code.
365 * @param ptr a pointer to the start of the multiple options
366 * @param option the string (friendly) name of the option, used for messages
367 * @param list the list to add the option to
369 static void setrepeatingoption(char *ptr, const char *option,
370 alpm_list_t **list)
372 char *q;
374 while((q = strchr(ptr, ' '))) {
375 *q = '\0';
376 *list = alpm_list_add(*list, strdup(ptr));
377 pm_printf(ALPM_LOG_DEBUG, "config: %s: %s\n", option, ptr);
378 ptr = q;
379 ptr++;
381 *list = alpm_list_add(*list, strdup(ptr));
382 pm_printf(ALPM_LOG_DEBUG, "config: %s: %s\n", option, ptr);
385 static int _parse_options(const char *key, char *value,
386 const char *file, int linenum)
388 if(value == NULL) {
389 /* options without settings */
390 if(strcmp(key, "UseSyslog") == 0) {
391 config->usesyslog = 1;
392 pm_printf(ALPM_LOG_DEBUG, "config: usesyslog\n");
393 } else if(strcmp(key, "ILoveCandy") == 0) {
394 config->chomp = 1;
395 pm_printf(ALPM_LOG_DEBUG, "config: chomp\n");
396 } else if(strcmp(key, "VerbosePkgLists") == 0) {
397 config->verbosepkglists = 1;
398 pm_printf(ALPM_LOG_DEBUG, "config: verbosepkglists\n");
399 } else if(strcmp(key, "UseDelta") == 0) {
400 config->deltaratio = 0.7;
401 pm_printf(ALPM_LOG_DEBUG, "config: usedelta (default 0.7)\n");
402 } else if(strcmp(key, "TotalDownload") == 0) {
403 config->totaldownload = 1;
404 pm_printf(ALPM_LOG_DEBUG, "config: totaldownload\n");
405 } else if(strcmp(key, "CheckSpace") == 0) {
406 config->checkspace = 1;
407 } else {
408 pm_printf(ALPM_LOG_WARNING,
409 _("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"),
410 file, linenum, key, "options");
412 } else {
413 /* options with settings */
414 if(strcmp(key, "NoUpgrade") == 0) {
415 setrepeatingoption(value, "NoUpgrade", &(config->noupgrade));
416 } else if(strcmp(key, "NoExtract") == 0) {
417 setrepeatingoption(value, "NoExtract", &(config->noextract));
418 } else if(strcmp(key, "IgnorePkg") == 0) {
419 setrepeatingoption(value, "IgnorePkg", &(config->ignorepkg));
420 } else if(strcmp(key, "IgnoreGroup") == 0) {
421 setrepeatingoption(value, "IgnoreGroup", &(config->ignoregrp));
422 } else if(strcmp(key, "HoldPkg") == 0) {
423 setrepeatingoption(value, "HoldPkg", &(config->holdpkg));
424 } else if(strcmp(key, "CacheDir") == 0) {
425 setrepeatingoption(value, "CacheDir", &(config->cachedirs));
426 } else if(strcmp(key, "Architecture") == 0) {
427 if(!config->arch) {
428 config_set_arch(value);
430 } else if(strcmp(key, "UseDelta") == 0) {
431 double ratio;
432 char *endptr;
433 ratio = strtod(value, &endptr);
434 if(*endptr != '\0' || ratio < 0.0 || ratio > 2.0) {
435 pm_printf(ALPM_LOG_ERROR,
436 _("config file %s, line %d: invalid value for '%s' : '%s'\n"),
437 file, linenum, "UseDelta", value);
438 return 1;
440 config->deltaratio = ratio;
441 pm_printf(ALPM_LOG_DEBUG, "config: usedelta = %f\n", ratio);
442 } else if(strcmp(key, "DBPath") == 0) {
443 /* don't overwrite a path specified on the command line */
444 if(!config->dbpath) {
445 config->dbpath = strdup(value);
446 pm_printf(ALPM_LOG_DEBUG, "config: dbpath: %s\n", value);
448 } else if(strcmp(key, "RootDir") == 0) {
449 /* don't overwrite a path specified on the command line */
450 if(!config->rootdir) {
451 config->rootdir = strdup(value);
452 pm_printf(ALPM_LOG_DEBUG, "config: rootdir: %s\n", value);
454 } else if(strcmp(key, "GPGDir") == 0) {
455 if(!config->gpgdir) {
456 config->gpgdir = strdup(value);
457 pm_printf(ALPM_LOG_DEBUG, "config: gpgdir: %s\n", value);
459 } else if(strcmp(key, "LogFile") == 0) {
460 if(!config->logfile) {
461 config->logfile = strdup(value);
462 pm_printf(ALPM_LOG_DEBUG, "config: logfile: %s\n", value);
464 } else if(strcmp(key, "XferCommand") == 0) {
465 config->xfercommand = strdup(value);
466 pm_printf(ALPM_LOG_DEBUG, "config: xfercommand: %s\n", value);
467 } else if(strcmp(key, "CleanMethod") == 0) {
468 alpm_list_t *methods = NULL;
469 setrepeatingoption(value, "CleanMethod", &methods);
470 if(process_cleanmethods(methods, file, linenum)) {
471 FREELIST(methods);
472 return 1;
474 FREELIST(methods);
475 } else if(strcmp(key, "SigLevel") == 0) {
476 alpm_list_t *values = NULL;
477 setrepeatingoption(value, "SigLevel", &values);
478 if(process_siglevel(values, &config->siglevel, file, linenum)) {
479 FREELIST(values);
480 return 1;
482 FREELIST(values);
483 } else {
484 pm_printf(ALPM_LOG_WARNING,
485 _("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"),
486 file, linenum, key, "options");
490 return 0;
493 static int _add_mirror(alpm_db_t *db, char *value)
495 const char *dbname = alpm_db_get_name(db);
496 /* let's attempt a replacement for the current repo */
497 char *temp = strreplace(value, "$repo", dbname);
498 /* let's attempt a replacement for the arch */
499 const char *arch = config->arch;
500 char *server;
501 if(arch) {
502 server = strreplace(temp, "$arch", arch);
503 free(temp);
504 } else {
505 if(strstr(temp, "$arch")) {
506 free(temp);
507 pm_printf(ALPM_LOG_ERROR,
508 _("mirror '%s' contains the '%s' variable, but no '%s' is defined.\n"),
509 value, "$arch", "Architecture");
510 return 1;
512 server = temp;
515 if(alpm_db_add_server(db, server) != 0) {
516 /* pm_errno is set by alpm_db_setserver */
517 pm_printf(ALPM_LOG_ERROR, _("could not add server URL to database '%s': %s (%s)\n"),
518 dbname, server, alpm_strerror(alpm_errno(config->handle)));
519 free(server);
520 return 1;
523 free(server);
524 return 0;
527 /** Sets up libalpm global stuff in one go. Called after the command line
528 * and inital config file parsing. Once this is complete, we can see if any
529 * paths were defined. If a rootdir was defined and nothing else, we want all
530 * of our paths to live under the rootdir that was specified. Safe to call
531 * multiple times (will only do anything the first time).
533 static int setup_libalpm(void)
535 int ret = 0;
536 alpm_errno_t err;
537 alpm_handle_t *handle;
539 pm_printf(ALPM_LOG_DEBUG, "setup_libalpm called\n");
541 /* Configure root path first. If it is set and dbpath/logfile were not
542 * set, then set those as well to reside under the root. */
543 if(config->rootdir) {
544 char path[PATH_MAX];
545 if(!config->dbpath) {
546 snprintf(path, PATH_MAX, "%s/%s", config->rootdir, DBPATH + 1);
547 config->dbpath = strdup(path);
549 if(!config->logfile) {
550 snprintf(path, PATH_MAX, "%s/%s", config->rootdir, LOGFILE + 1);
551 config->logfile = strdup(path);
553 } else {
554 config->rootdir = strdup(ROOTDIR);
555 if(!config->dbpath) {
556 config->dbpath = strdup(DBPATH);
560 /* initialize library */
561 handle = alpm_initialize(config->rootdir, config->dbpath, &err);
562 if(!handle) {
563 pm_printf(ALPM_LOG_ERROR, _("failed to initialize alpm library (%s)\n"),
564 alpm_strerror(err));
565 if(err == ALPM_ERR_DB_VERSION) {
566 pm_printf(ALPM_LOG_ERROR, _(" try running pacman-db-upgrade\n"));
568 return -1;
570 config->handle = handle;
572 alpm_option_set_logcb(handle, cb_log);
573 alpm_option_set_dlcb(handle, cb_dl_progress);
574 alpm_option_set_eventcb(handle, cb_event);
575 alpm_option_set_questioncb(handle, cb_question);
576 alpm_option_set_progresscb(handle, cb_progress);
578 config->logfile = config->logfile ? config->logfile : strdup(LOGFILE);
579 ret = alpm_option_set_logfile(handle, config->logfile);
580 if(ret != 0) {
581 pm_printf(ALPM_LOG_ERROR, _("problem setting logfile '%s' (%s)\n"),
582 config->logfile, alpm_strerror(alpm_errno(handle)));
583 return ret;
586 /* Set GnuPG's home directory. This is not relative to rootdir, even if
587 * rootdir is defined. Reasoning: gpgdir contains configuration data. */
588 config->gpgdir = config->gpgdir ? config->gpgdir : strdup(GPGDIR);
589 ret = alpm_option_set_gpgdir(handle, config->gpgdir);
590 if(ret != 0) {
591 pm_printf(ALPM_LOG_ERROR, _("problem setting gpgdir '%s' (%s)\n"),
592 config->gpgdir, alpm_strerror(alpm_errno(handle)));
593 return ret;
596 /* add a default cachedir if one wasn't specified */
597 if(config->cachedirs == NULL) {
598 alpm_option_add_cachedir(handle, CACHEDIR);
599 } else {
600 alpm_option_set_cachedirs(handle, config->cachedirs);
603 alpm_option_set_default_siglevel(handle, config->siglevel);
605 if(config->xfercommand) {
606 alpm_option_set_fetchcb(handle, download_with_xfercommand);
607 } else if(!(alpm_capabilities() & ALPM_CAPABILITY_DOWNLOADER)) {
608 pm_printf(ALPM_LOG_WARNING, _("no '%s' configured"), "XferCommand");
611 if(config->totaldownload) {
612 alpm_option_set_totaldlcb(handle, cb_dl_total);
615 alpm_option_set_arch(handle, config->arch);
616 alpm_option_set_checkspace(handle, config->checkspace);
617 alpm_option_set_usesyslog(handle, config->usesyslog);
618 alpm_option_set_deltaratio(handle, config->deltaratio);
620 alpm_option_set_ignorepkgs(handle, config->ignorepkg);
621 alpm_option_set_ignoregroups(handle, config->ignoregrp);
622 alpm_option_set_noupgrades(handle, config->noupgrade);
623 alpm_option_set_noextracts(handle, config->noextract);
625 return 0;
629 * Allows parsing in advance of an entire config section before we start
630 * calling library methods.
632 struct section_t {
633 /* useful for all sections */
634 char *name;
635 int is_options;
636 /* db section option gathering */
637 alpm_siglevel_t siglevel;
638 alpm_list_t *servers;
642 * Wrap up a section once we have reached the end of it. This should be called
643 * when a subsequent section is encountered, or when we have reached the end of
644 * the root config file. Once called, all existing saved config pieces on the
645 * section struct are freed.
646 * @param section the current parsed and saved section data
647 * @param parse_options whether we are parsing options or repo data
648 * @return 0 on success, 1 on failure
650 static int finish_section(struct section_t *section, int parse_options)
652 int ret = 0;
653 alpm_list_t *i;
654 alpm_db_t *db;
656 pm_printf(ALPM_LOG_DEBUG, "config: finish section '%s'\n", section->name);
658 /* parsing options (or nothing)- nothing to do except free the pieces */
659 if(!section->name || parse_options || section->is_options) {
660 goto cleanup;
663 /* if we are not looking at options sections only, register a db */
664 db = alpm_register_syncdb(config->handle, section->name, section->siglevel);
665 if(db == NULL) {
666 pm_printf(ALPM_LOG_ERROR, _("could not register '%s' database (%s)\n"),
667 section->name, alpm_strerror(alpm_errno(config->handle)));
668 ret = 1;
669 goto cleanup;
672 for(i = section->servers; i; i = alpm_list_next(i)) {
673 char *value = i->data;
674 if(_add_mirror(db, value) != 0) {
675 pm_printf(ALPM_LOG_ERROR,
676 _("could not add mirror '%s' to database '%s' (%s)\n"),
677 value, section->name, alpm_strerror(alpm_errno(config->handle)));
678 ret = 1;
679 goto cleanup;
681 free(value);
684 cleanup:
685 alpm_list_free(section->servers);
686 section->servers = NULL;
687 section->siglevel = ALPM_SIG_USE_DEFAULT;
688 free(section->name);
689 section->name = NULL;
690 return ret;
693 /** The "real" parseconfig. Each "Include" directive will recall this method so
694 * recursion and stack depth are limited to 10 levels. The publicly visible
695 * parseconfig calls this with a NULL section argument so we can recall from
696 * within ourself on an include.
697 * @param file path to the config file
698 * @param section the current active section
699 * @param parse_options whether to parse and call methods for the options
700 * section; if 0, parse and call methods for the repos sections
701 * @param depth the current recursion depth
702 * @return 0 on success, 1 on failure
704 static int _parseconfig(const char *file, struct section_t *section,
705 int parse_options, int depth)
707 FILE *fp = NULL;
708 char line[PATH_MAX];
709 int linenum = 0;
710 int ret = 0;
711 const int max_depth = 10;
713 if(depth >= max_depth) {
714 pm_printf(ALPM_LOG_ERROR,
715 _("config parsing exceeded max recursion depth of %d.\n"), max_depth);
716 ret = 1;
717 goto cleanup;
720 pm_printf(ALPM_LOG_DEBUG, "config: attempting to read file %s\n", file);
721 fp = fopen(file, "r");
722 if(fp == NULL) {
723 pm_printf(ALPM_LOG_ERROR, _("config file %s could not be read: %s\n"),
724 file, strerror(errno));
725 ret = 1;
726 goto cleanup;
729 while(fgets(line, PATH_MAX, fp)) {
730 char *key, *value, *ptr;
731 size_t line_len;
733 linenum++;
735 /* ignore whole line and end of line comments */
736 if((ptr = strchr(line, '#'))) {
737 *ptr = '\0';
740 line_len = strtrim(line);
742 if(line_len == 0) {
743 continue;
746 if(line[0] == '[' && line[line_len - 1] == ']') {
747 char *name;
748 /* only possibility here is a line == '[]' */
749 if(line_len <= 2) {
750 pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: bad section name.\n"),
751 file, linenum);
752 ret = 1;
753 goto cleanup;
755 /* new config section, skip the '[' */
756 name = strdup(line + 1);
757 name[line_len - 2] = '\0';
758 /* we're at a new section; perform any post-actions for the prior */
759 if(finish_section(section, parse_options)) {
760 ret = 1;
761 goto cleanup;
763 pm_printf(ALPM_LOG_DEBUG, "config: new section '%s'\n", name);
764 section->name = name;
765 section->is_options = (strcmp(name, "options") == 0);
766 continue;
769 /* directive */
770 /* strsep modifies the 'line' string: 'key \0 value' */
771 key = line;
772 value = line;
773 strsep(&value, "=");
774 strtrim(key);
775 strtrim(value);
777 if(key == NULL) {
778 pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: syntax error in config file- missing key.\n"),
779 file, linenum);
780 ret = 1;
781 goto cleanup;
783 /* For each directive, compare to the camelcase string. */
784 if(section->name == NULL) {
785 pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: All directives must belong to a section.\n"),
786 file, linenum);
787 ret = 1;
788 goto cleanup;
790 /* Include is allowed in both options and repo sections */
791 if(strcmp(key, "Include") == 0) {
792 glob_t globbuf;
793 int globret;
794 size_t gindex;
796 if(value == NULL) {
797 pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: directive '%s' needs a value\n"),
798 file, linenum, key);
799 ret = 1;
800 goto cleanup;
802 /* Ignore include failures... assume non-critical */
803 globret = glob(value, GLOB_NOCHECK, NULL, &globbuf);
804 switch(globret) {
805 case GLOB_NOSPACE:
806 pm_printf(ALPM_LOG_DEBUG,
807 "config file %s, line %d: include globbing out of space\n",
808 file, linenum);
809 break;
810 case GLOB_ABORTED:
811 pm_printf(ALPM_LOG_DEBUG,
812 "config file %s, line %d: include globbing read error for %s\n",
813 file, linenum, value);
814 break;
815 case GLOB_NOMATCH:
816 pm_printf(ALPM_LOG_DEBUG,
817 "config file %s, line %d: no include found for %s\n",
818 file, linenum, value);
819 break;
820 default:
821 for(gindex = 0; gindex < globbuf.gl_pathc; gindex++) {
822 pm_printf(ALPM_LOG_DEBUG, "config file %s, line %d: including %s\n",
823 file, linenum, globbuf.gl_pathv[gindex]);
824 _parseconfig(globbuf.gl_pathv[gindex], section, parse_options, depth + 1);
826 break;
828 globfree(&globbuf);
829 continue;
831 if(parse_options && section->is_options) {
832 /* we are either in options ... */
833 if((ret = _parse_options(key, value, file, linenum)) != 0) {
834 goto cleanup;
836 } else if(!parse_options && !section->is_options) {
837 /* ... or in a repo section */
838 if(strcmp(key, "Server") == 0) {
839 if(value == NULL) {
840 pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: directive '%s' needs a value\n"),
841 file, linenum, key);
842 ret = 1;
843 goto cleanup;
845 section->servers = alpm_list_add(section->servers, strdup(value));
846 } else if(strcmp(key, "SigLevel") == 0) {
847 alpm_list_t *values = NULL;
848 setrepeatingoption(value, "SigLevel", &values);
849 if(values) {
850 if(section->siglevel == ALPM_SIG_USE_DEFAULT) {
851 section->siglevel = config->siglevel;
853 if(process_siglevel(values, &section->siglevel, file, linenum)) {
854 FREELIST(values);
855 ret = 1;
856 goto cleanup;
858 FREELIST(values);
860 } else {
861 pm_printf(ALPM_LOG_WARNING,
862 _("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"),
863 file, linenum, key, section->name);
868 if(depth == 0) {
869 ret = finish_section(section, parse_options);
872 cleanup:
873 if(fp) {
874 fclose(fp);
876 pm_printf(ALPM_LOG_DEBUG, "config: finished parsing %s\n", file);
877 return ret;
880 /** Parse a configuration file.
881 * @param file path to the config file
882 * @return 0 on success, non-zero on error
884 int parseconfig(const char *file)
886 int ret;
887 struct section_t section;
888 memset(&section, 0, sizeof(struct section_t));
889 section.siglevel = ALPM_SIG_USE_DEFAULT;
890 /* the config parse is a two-pass affair. We first parse the entire thing for
891 * the [options] section so we can get all default and path options set.
892 * Next, we go back and parse everything but [options]. */
894 /* call the real parseconfig function with a null section & db argument */
895 pm_printf(ALPM_LOG_DEBUG, "parseconfig: options pass\n");
896 if((ret = _parseconfig(file, &section, 1, 0))) {
897 return ret;
899 if((ret = setup_libalpm())) {
900 return ret;
902 /* second pass, repo section parsing */
903 pm_printf(ALPM_LOG_DEBUG, "parseconfig: repo pass\n");
904 return _parseconfig(file, &section, 0, 0);
907 /* vim: set ts=2 sw=2 noet: */