linux/tools/perf/util/config.c
<<
>>
Prefs
   1/*
   2 * GIT - The information manager from hell
   3 *
   4 * Copyright (C) Linus Torvalds, 2005
   5 * Copyright (C) Johannes Schindelin, 2005
   6 *
   7 */
   8#include "util.h"
   9#include "cache.h"
  10#include "exec_cmd.h"
  11
  12#define MAXNAME (256)
  13
  14static FILE *config_file;
  15static const char *config_file_name;
  16static int config_linenr;
  17static int config_file_eof;
  18
  19const char *config_exclusive_filename = NULL;
  20
  21static int get_next_char(void)
  22{
  23        int c;
  24        FILE *f;
  25
  26        c = '\n';
  27        if ((f = config_file) != NULL) {
  28                c = fgetc(f);
  29                if (c == '\r') {
  30                        /* DOS like systems */
  31                        c = fgetc(f);
  32                        if (c != '\n') {
  33                                ungetc(c, f);
  34                                c = '\r';
  35                        }
  36                }
  37                if (c == '\n')
  38                        config_linenr++;
  39                if (c == EOF) {
  40                        config_file_eof = 1;
  41                        c = '\n';
  42                }
  43        }
  44        return c;
  45}
  46
  47static char *parse_value(void)
  48{
  49        static char value[1024];
  50        int quote = 0, comment = 0, space = 0;
  51        size_t len = 0;
  52
  53        for (;;) {
  54                int c = get_next_char();
  55
  56                if (len >= sizeof(value) - 1)
  57                        return NULL;
  58                if (c == '\n') {
  59                        if (quote)
  60                                return NULL;
  61                        value[len] = 0;
  62                        return value;
  63                }
  64                if (comment)
  65                        continue;
  66                if (isspace(c) && !quote) {
  67                        space = 1;
  68                        continue;
  69                }
  70                if (!quote) {
  71                        if (c == ';' || c == '#') {
  72                                comment = 1;
  73                                continue;
  74                        }
  75                }
  76                if (space) {
  77                        if (len)
  78                                value[len++] = ' ';
  79                        space = 0;
  80                }
  81                if (c == '\\') {
  82                        c = get_next_char();
  83                        switch (c) {
  84                        case '\n':
  85                                continue;
  86                        case 't':
  87                                c = '\t';
  88                                break;
  89                        case 'b':
  90                                c = '\b';
  91                                break;
  92                        case 'n':
  93                                c = '\n';
  94                                break;
  95                        /* Some characters escape as themselves */
  96                        case '\\': case '"':
  97                                break;
  98                        /* Reject unknown escape sequences */
  99                        default:
 100                                return NULL;
 101                        }
 102                        value[len++] = c;
 103                        continue;
 104                }
 105                if (c == '"') {
 106                        quote = 1-quote;
 107                        continue;
 108                }
 109                value[len++] = c;
 110        }
 111}
 112
 113static inline int iskeychar(int c)
 114{
 115        return isalnum(c) || c == '-';
 116}
 117
 118static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
 119{
 120        int c;
 121        char *value;
 122
 123        /* Get the full name */
 124        for (;;) {
 125                c = get_next_char();
 126                if (config_file_eof)
 127                        break;
 128                if (!iskeychar(c))
 129                        break;
 130                name[len++] = tolower(c);
 131                if (len >= MAXNAME)
 132                        return -1;
 133        }
 134        name[len] = 0;
 135        while (c == ' ' || c == '\t')
 136                c = get_next_char();
 137
 138        value = NULL;
 139        if (c != '\n') {
 140                if (c != '=')
 141                        return -1;
 142                value = parse_value();
 143                if (!value)
 144                        return -1;
 145        }
 146        return fn(name, value, data);
 147}
 148
 149static int get_extended_base_var(char *name, int baselen, int c)
 150{
 151        do {
 152                if (c == '\n')
 153                        return -1;
 154                c = get_next_char();
 155        } while (isspace(c));
 156
 157        /* We require the format to be '[base "extension"]' */
 158        if (c != '"')
 159                return -1;
 160        name[baselen++] = '.';
 161
 162        for (;;) {
 163                int ch = get_next_char();
 164
 165                if (ch == '\n')
 166                        return -1;
 167                if (ch == '"')
 168                        break;
 169                if (ch == '\\') {
 170                        ch = get_next_char();
 171                        if (ch == '\n')
 172                                return -1;
 173                }
 174                name[baselen++] = ch;
 175                if (baselen > MAXNAME / 2)
 176                        return -1;
 177        }
 178
 179        /* Final ']' */
 180        if (get_next_char() != ']')
 181                return -1;
 182        return baselen;
 183}
 184
 185static int get_base_var(char *name)
 186{
 187        int baselen = 0;
 188
 189        for (;;) {
 190                int c = get_next_char();
 191                if (config_file_eof)
 192                        return -1;
 193                if (c == ']')
 194                        return baselen;
 195                if (isspace(c))
 196                        return get_extended_base_var(name, baselen, c);
 197                if (!iskeychar(c) && c != '.')
 198                        return -1;
 199                if (baselen > MAXNAME / 2)
 200                        return -1;
 201                name[baselen++] = tolower(c);
 202        }
 203}
 204
 205static int perf_parse_file(config_fn_t fn, void *data)
 206{
 207        int comment = 0;
 208        int baselen = 0;
 209        static char var[MAXNAME];
 210
 211        /* U+FEFF Byte Order Mark in UTF8 */
 212        static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
 213        const unsigned char *bomptr = utf8_bom;
 214
 215        for (;;) {
 216                int c = get_next_char();
 217                if (bomptr && *bomptr) {
 218                        /* We are at the file beginning; skip UTF8-encoded BOM
 219                         * if present. Sane editors won't put this in on their
 220                         * own, but e.g. Windows Notepad will do it happily. */
 221                        if ((unsigned char) c == *bomptr) {
 222                                bomptr++;
 223                                continue;
 224                        } else {
 225                                /* Do not tolerate partial BOM. */
 226                                if (bomptr != utf8_bom)
 227                                        break;
 228                                /* No BOM at file beginning. Cool. */
 229                                bomptr = NULL;
 230                        }
 231                }
 232                if (c == '\n') {
 233                        if (config_file_eof)
 234                                return 0;
 235                        comment = 0;
 236                        continue;
 237                }
 238                if (comment || isspace(c))
 239                        continue;
 240                if (c == '#' || c == ';') {
 241                        comment = 1;
 242                        continue;
 243                }
 244                if (c == '[') {
 245                        baselen = get_base_var(var);
 246                        if (baselen <= 0)
 247                                break;
 248                        var[baselen++] = '.';
 249                        var[baselen] = 0;
 250                        continue;
 251                }
 252                if (!isalpha(c))
 253                        break;
 254                var[baselen] = tolower(c);
 255                if (get_value(fn, data, var, baselen+1) < 0)
 256                        break;
 257        }
 258        die("bad config file line %d in %s", config_linenr, config_file_name);
 259}
 260
 261static int parse_unit_factor(const char *end, unsigned long *val)
 262{
 263        if (!*end)
 264                return 1;
 265        else if (!strcasecmp(end, "k")) {
 266                *val *= 1024;
 267                return 1;
 268        }
 269        else if (!strcasecmp(end, "m")) {
 270                *val *= 1024 * 1024;
 271                return 1;
 272        }
 273        else if (!strcasecmp(end, "g")) {
 274                *val *= 1024 * 1024 * 1024;
 275                return 1;
 276        }
 277        return 0;
 278}
 279
 280static int perf_parse_long(const char *value, long *ret)
 281{
 282        if (value && *value) {
 283                char *end;
 284                long val = strtol(value, &end, 0);
 285                unsigned long factor = 1;
 286                if (!parse_unit_factor(end, &factor))
 287                        return 0;
 288                *ret = val * factor;
 289                return 1;
 290        }
 291        return 0;
 292}
 293
 294int perf_parse_ulong(const char *value, unsigned long *ret)
 295{
 296        if (value && *value) {
 297                char *end;
 298                unsigned long val = strtoul(value, &end, 0);
 299                if (!parse_unit_factor(end, &val))
 300                        return 0;
 301                *ret = val;
 302                return 1;
 303        }
 304        return 0;
 305}
 306
 307static void die_bad_config(const char *name)
 308{
 309        if (config_file_name)
 310                die("bad config value for '%s' in %s", name, config_file_name);
 311        die("bad config value for '%s'", name);
 312}
 313
 314int perf_config_int(const char *name, const char *value)
 315{
 316        long ret = 0;
 317        if (!perf_parse_long(value, &ret))
 318                die_bad_config(name);
 319        return ret;
 320}
 321
 322unsigned long perf_config_ulong(const char *name, const char *value)
 323{
 324        unsigned long ret;
 325        if (!perf_parse_ulong(value, &ret))
 326                die_bad_config(name);
 327        return ret;
 328}
 329
 330int perf_config_bool_or_int(const char *name, const char *value, int *is_bool)
 331{
 332        *is_bool = 1;
 333        if (!value)
 334                return 1;
 335        if (!*value)
 336                return 0;
 337        if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))
 338                return 1;
 339        if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))
 340                return 0;
 341        *is_bool = 0;
 342        return perf_config_int(name, value);
 343}
 344
 345int perf_config_bool(const char *name, const char *value)
 346{
 347        int discard;
 348        return !!perf_config_bool_or_int(name, value, &discard);
 349}
 350
 351int perf_config_string(const char **dest, const char *var, const char *value)
 352{
 353        if (!value)
 354                return config_error_nonbool(var);
 355        *dest = strdup(value);
 356        return 0;
 357}
 358
 359static int perf_default_core_config(const char *var __used, const char *value __used)
 360{
 361        /* Add other config variables here and to Documentation/config.txt. */
 362        return 0;
 363}
 364
 365int perf_default_config(const char *var, const char *value, void *dummy __used)
 366{
 367        if (!prefixcmp(var, "core."))
 368                return perf_default_core_config(var, value);
 369
 370        /* Add other config variables here and to Documentation/config.txt. */
 371        return 0;
 372}
 373
 374int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
 375{
 376        int ret;
 377        FILE *f = fopen(filename, "r");
 378
 379        ret = -1;
 380        if (f) {
 381                config_file = f;
 382                config_file_name = filename;
 383                config_linenr = 1;
 384                config_file_eof = 0;
 385                ret = perf_parse_file(fn, data);
 386                fclose(f);
 387                config_file_name = NULL;
 388        }
 389        return ret;
 390}
 391
 392const char *perf_etc_perfconfig(void)
 393{
 394        static const char *system_wide;
 395        if (!system_wide)
 396                system_wide = system_path(ETC_PERFCONFIG);
 397        return system_wide;
 398}
 399
 400static int perf_env_bool(const char *k, int def)
 401{
 402        const char *v = getenv(k);
 403        return v ? perf_config_bool(k, v) : def;
 404}
 405
 406int perf_config_system(void)
 407{
 408        return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0);
 409}
 410
 411int perf_config_global(void)
 412{
 413        return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0);
 414}
 415
 416int perf_config(config_fn_t fn, void *data)
 417{
 418        int ret = 0, found = 0;
 419        char *repo_config = NULL;
 420        const char *home = NULL;
 421
 422        /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */
 423        if (config_exclusive_filename)
 424                return perf_config_from_file(fn, config_exclusive_filename, data);
 425        if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) {
 426                ret += perf_config_from_file(fn, perf_etc_perfconfig(),
 427                                            data);
 428                found += 1;
 429        }
 430
 431        home = getenv("HOME");
 432        if (perf_config_global() && home) {
 433                char *user_config = strdup(mkpath("%s/.perfconfig", home));
 434                if (!access(user_config, R_OK)) {
 435                        ret += perf_config_from_file(fn, user_config, data);
 436                        found += 1;
 437                }
 438                free(user_config);
 439        }
 440
 441        repo_config = perf_pathdup("config");
 442        if (!access(repo_config, R_OK)) {
 443                ret += perf_config_from_file(fn, repo_config, data);
 444                found += 1;
 445        }
 446        free(repo_config);
 447        if (found == 0)
 448                return -1;
 449        return ret;
 450}
 451
 452/*
 453 * Find all the stuff for perf_config_set() below.
 454 */
 455
 456#define MAX_MATCHES 512
 457
 458static struct {
 459        int baselen;
 460        char* key;
 461        int do_not_match;
 462        regex_t* value_regex;
 463        int multi_replace;
 464        size_t offset[MAX_MATCHES];
 465        enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
 466        int seen;
 467} store;
 468
 469static int matches(const char* key, const char* value)
 470{
 471        return !strcmp(key, store.key) &&
 472                (store.value_regex == NULL ||
 473                 (store.do_not_match ^
 474                  !regexec(store.value_regex, value, 0, NULL, 0)));
 475}
 476
 477static int store_aux(const char* key, const char* value, void *cb __used)
 478{
 479        int section_len;
 480        const char *ep;
 481
 482        switch (store.state) {
 483        case KEY_SEEN:
 484                if (matches(key, value)) {
 485                        if (store.seen == 1 && store.multi_replace == 0) {
 486                                warning("%s has multiple values", key);
 487                        } else if (store.seen >= MAX_MATCHES) {
 488                                error("too many matches for %s", key);
 489                                return 1;
 490                        }
 491
 492                        store.offset[store.seen] = ftell(config_file);
 493                        store.seen++;
 494                }
 495                break;
 496        case SECTION_SEEN:
 497                /*
 498                 * What we are looking for is in store.key (both
 499                 * section and var), and its section part is baselen
 500                 * long.  We found key (again, both section and var).
 501                 * We would want to know if this key is in the same
 502                 * section as what we are looking for.  We already
 503                 * know we are in the same section as what should
 504                 * hold store.key.
 505                 */
 506                ep = strrchr(key, '.');
 507                section_len = ep - key;
 508
 509                if ((section_len != store.baselen) ||
 510                    memcmp(key, store.key, section_len+1)) {
 511                        store.state = SECTION_END_SEEN;
 512                        break;
 513                }
 514
 515                /*
 516                 * Do not increment matches: this is no match, but we
 517                 * just made sure we are in the desired section.
 518                 */
 519                store.offset[store.seen] = ftell(config_file);
 520                /* fallthru */
 521        case SECTION_END_SEEN:
 522        case START:
 523                if (matches(key, value)) {
 524                        store.offset[store.seen] = ftell(config_file);
 525                        store.state = KEY_SEEN;
 526                        store.seen++;
 527                } else {
 528                        if (strrchr(key, '.') - key == store.baselen &&
 529                              !strncmp(key, store.key, store.baselen)) {
 530                                        store.state = SECTION_SEEN;
 531                                        store.offset[store.seen] = ftell(config_file);
 532                        }
 533                }
 534        default:
 535                break;
 536        }
 537        return 0;
 538}
 539
 540static int store_write_section(int fd, const char* key)
 541{
 542        const char *dot;
 543        int i, success;
 544        struct strbuf sb = STRBUF_INIT;
 545
 546        dot = memchr(key, '.', store.baselen);
 547        if (dot) {
 548                strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key);
 549                for (i = dot - key + 1; i < store.baselen; i++) {
 550                        if (key[i] == '"' || key[i] == '\\')
 551                                strbuf_addch(&sb, '\\');
 552                        strbuf_addch(&sb, key[i]);
 553                }
 554                strbuf_addstr(&sb, "\"]\n");
 555        } else {
 556                strbuf_addf(&sb, "[%.*s]\n", store.baselen, key);
 557        }
 558
 559        success = (write_in_full(fd, sb.buf, sb.len) == (ssize_t)sb.len);
 560        strbuf_release(&sb);
 561
 562        return success;
 563}
 564
 565static int store_write_pair(int fd, const char* key, const char* value)
 566{
 567        int i, success;
 568        int length = strlen(key + store.baselen + 1);
 569        const char *quote = "";
 570        struct strbuf sb = STRBUF_INIT;
 571
 572        /*
 573         * Check to see if the value needs to be surrounded with a dq pair.
 574         * Note that problematic characters are always backslash-quoted; this
 575         * check is about not losing leading or trailing SP and strings that
 576         * follow beginning-of-comment characters (i.e. ';' and '#') by the
 577         * configuration parser.
 578         */
 579        if (value[0] == ' ')
 580                quote = "\"";
 581        for (i = 0; value[i]; i++)
 582                if (value[i] == ';' || value[i] == '#')
 583                        quote = "\"";
 584        if (i && value[i - 1] == ' ')
 585                quote = "\"";
 586
 587        strbuf_addf(&sb, "\t%.*s = %s",
 588                    length, key + store.baselen + 1, quote);
 589
 590        for (i = 0; value[i]; i++)
 591                switch (value[i]) {
 592                case '\n':
 593                        strbuf_addstr(&sb, "\\n");
 594                        break;
 595                case '\t':
 596                        strbuf_addstr(&sb, "\\t");
 597                        break;
 598                case '"':
 599                case '\\':
 600                        strbuf_addch(&sb, '\\');
 601                default:
 602                        strbuf_addch(&sb, value[i]);
 603                        break;
 604                }
 605        strbuf_addf(&sb, "%s\n", quote);
 606
 607        success = (write_in_full(fd, sb.buf, sb.len) == (ssize_t)sb.len);
 608        strbuf_release(&sb);
 609
 610        return success;
 611}
 612
 613static ssize_t find_beginning_of_line(const char* contents, size_t size,
 614        size_t offset_, int* found_bracket)
 615{
 616        size_t equal_offset = size, bracket_offset = size;
 617        ssize_t offset;
 618
 619contline:
 620        for (offset = offset_-2; offset > 0
 621                        && contents[offset] != '\n'; offset--)
 622                switch (contents[offset]) {
 623                        case '=': equal_offset = offset; break;
 624                        case ']': bracket_offset = offset; break;
 625                        default: break;
 626                }
 627        if (offset > 0 && contents[offset-1] == '\\') {
 628                offset_ = offset;
 629                goto contline;
 630        }
 631        if (bracket_offset < equal_offset) {
 632                *found_bracket = 1;
 633                offset = bracket_offset+1;
 634        } else
 635                offset++;
 636
 637        return offset;
 638}
 639
 640int perf_config_set(const char* key, const char* value)
 641{
 642        return perf_config_set_multivar(key, value, NULL, 0);
 643}
 644
 645/*
 646 * If value==NULL, unset in (remove from) config,
 647 * if value_regex!=NULL, disregard key/value pairs where value does not match.
 648 * if multi_replace==0, nothing, or only one matching key/value is replaced,
 649 *     else all matching key/values (regardless how many) are removed,
 650 *     before the new pair is written.
 651 *
 652 * Returns 0 on success.
 653 *
 654 * This function does this:
 655 *
 656 * - it locks the config file by creating ".perf/config.lock"
 657 *
 658 * - it then parses the config using store_aux() as validator to find
 659 *   the position on the key/value pair to replace. If it is to be unset,
 660 *   it must be found exactly once.
 661 *
 662 * - the config file is mmap()ed and the part before the match (if any) is
 663 *   written to the lock file, then the changed part and the rest.
 664 *
 665 * - the config file is removed and the lock file rename()d to it.
 666 *
 667 */
 668int perf_config_set_multivar(const char* key, const char* value,
 669        const char* value_regex, int multi_replace)
 670{
 671        int i, dot;
 672        int fd = -1, in_fd;
 673        int ret = 0;
 674        char* config_filename;
 675        const char* last_dot = strrchr(key, '.');
 676
 677        if (config_exclusive_filename)
 678                config_filename = strdup(config_exclusive_filename);
 679        else
 680                config_filename = perf_pathdup("config");
 681
 682        /*
 683         * Since "key" actually contains the section name and the real
 684         * key name separated by a dot, we have to know where the dot is.
 685         */
 686
 687        if (last_dot == NULL) {
 688                error("key does not contain a section: %s", key);
 689                ret = 2;
 690                goto out_free;
 691        }
 692        store.baselen = last_dot - key;
 693
 694        store.multi_replace = multi_replace;
 695
 696        /*
 697         * Validate the key and while at it, lower case it for matching.
 698         */
 699        store.key = malloc(strlen(key) + 1);
 700        dot = 0;
 701        for (i = 0; key[i]; i++) {
 702                unsigned char c = key[i];
 703                if (c == '.')
 704                        dot = 1;
 705                /* Leave the extended basename untouched.. */
 706                if (!dot || i > store.baselen) {
 707                        if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
 708                                error("invalid key: %s", key);
 709                                free(store.key);
 710                                ret = 1;
 711                                goto out_free;
 712                        }
 713                        c = tolower(c);
 714                } else if (c == '\n') {
 715                        error("invalid key (newline): %s", key);
 716                        free(store.key);
 717                        ret = 1;
 718                        goto out_free;
 719                }
 720                store.key[i] = c;
 721        }
 722        store.key[i] = 0;
 723
 724        /*
 725         * If .perf/config does not exist yet, write a minimal version.
 726         */
 727        in_fd = open(config_filename, O_RDONLY);
 728        if ( in_fd < 0 ) {
 729                free(store.key);
 730
 731                if ( ENOENT != errno ) {
 732                        error("opening %s: %s", config_filename,
 733                              strerror(errno));
 734                        ret = 3; /* same as "invalid config file" */
 735                        goto out_free;
 736                }
 737                /* if nothing to unset, error out */
 738                if (value == NULL) {
 739                        ret = 5;
 740                        goto out_free;
 741                }
 742
 743                store.key = (char*)key;
 744                if (!store_write_section(fd, key) ||
 745                    !store_write_pair(fd, key, value))
 746                        goto write_err_out;
 747        } else {
 748                struct stat st;
 749                char *contents;
 750                ssize_t contents_sz, copy_begin, copy_end;
 751                int new_line = 0;
 752
 753                if (value_regex == NULL)
 754                        store.value_regex = NULL;
 755                else {
 756                        if (value_regex[0] == '!') {
 757                                store.do_not_match = 1;
 758                                value_regex++;
 759                        } else
 760                                store.do_not_match = 0;
 761
 762                        store.value_regex = (regex_t*)malloc(sizeof(regex_t));
 763                        if (regcomp(store.value_regex, value_regex,
 764                                        REG_EXTENDED)) {
 765                                error("invalid pattern: %s", value_regex);
 766                                free(store.value_regex);
 767                                ret = 6;
 768                                goto out_free;
 769                        }
 770                }
 771
 772                store.offset[0] = 0;
 773                store.state = START;
 774                store.seen = 0;
 775
 776                /*
 777                 * After this, store.offset will contain the *end* offset
 778                 * of the last match, or remain at 0 if no match was found.
 779                 * As a side effect, we make sure to transform only a valid
 780                 * existing config file.
 781                 */
 782                if (perf_config_from_file(store_aux, config_filename, NULL)) {
 783                        error("invalid config file %s", config_filename);
 784                        free(store.key);
 785                        if (store.value_regex != NULL) {
 786                                regfree(store.value_regex);
 787                                free(store.value_regex);
 788                        }
 789                        ret = 3;
 790                        goto out_free;
 791                }
 792
 793                free(store.key);
 794                if (store.value_regex != NULL) {
 795                        regfree(store.value_regex);
 796                        free(store.value_regex);
 797                }
 798
 799                /* if nothing to unset, or too many matches, error out */
 800                if ((store.seen == 0 && value == NULL) ||
 801                                (store.seen > 1 && multi_replace == 0)) {
 802                        ret = 5;
 803                        goto out_free;
 804                }
 805
 806                fstat(in_fd, &st);
 807                contents_sz = xsize_t(st.st_size);
 808                contents = mmap(NULL, contents_sz, PROT_READ,
 809                        MAP_PRIVATE, in_fd, 0);
 810                close(in_fd);
 811
 812                if (store.seen == 0)
 813                        store.seen = 1;
 814
 815                for (i = 0, copy_begin = 0; i < store.seen; i++) {
 816                        if (store.offset[i] == 0) {
 817                                store.offset[i] = copy_end = contents_sz;
 818                        } else if (store.state != KEY_SEEN) {
 819                                copy_end = store.offset[i];
 820                        } else
 821                                copy_end = find_beginning_of_line(
 822                                        contents, contents_sz,
 823                                        store.offset[i]-2, &new_line);
 824
 825                        if (copy_end > 0 && contents[copy_end-1] != '\n')
 826                                new_line = 1;
 827
 828                        /* write the first part of the config */
 829                        if (copy_end > copy_begin) {
 830                                if (write_in_full(fd, contents + copy_begin,
 831                                                  copy_end - copy_begin) <
 832                                    copy_end - copy_begin)
 833                                        goto write_err_out;
 834                                if (new_line &&
 835                                    write_in_full(fd, "\n", 1) != 1)
 836                                        goto write_err_out;
 837                        }
 838                        copy_begin = store.offset[i];
 839                }
 840
 841                /* write the pair (value == NULL means unset) */
 842                if (value != NULL) {
 843                        if (store.state == START) {
 844                                if (!store_write_section(fd, key))
 845                                        goto write_err_out;
 846                        }
 847                        if (!store_write_pair(fd, key, value))
 848                                goto write_err_out;
 849                }
 850
 851                /* write the rest of the config */
 852                if (copy_begin < contents_sz)
 853                        if (write_in_full(fd, contents + copy_begin,
 854                                          contents_sz - copy_begin) <
 855                            contents_sz - copy_begin)
 856                                goto write_err_out;
 857
 858                munmap(contents, contents_sz);
 859        }
 860
 861        ret = 0;
 862
 863out_free:
 864        free(config_filename);
 865        return ret;
 866
 867write_err_out:
 868        goto out_free;
 869
 870}
 871
 872/*
 873 * Call this to report error for your variable that should not
 874 * get a boolean value (i.e. "[my] var" means "true").
 875 */
 876int config_error_nonbool(const char *var)
 877{
 878        return error("Missing value for '%s'", var);
 879}
 880