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
  14#define DEBUG_CACHE_DIR ".debug"
  15
  16
  17char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */
  18
  19static FILE *config_file;
  20static const char *config_file_name;
  21static int config_linenr;
  22static int config_file_eof;
  23
  24static const char *config_exclusive_filename;
  25
  26static int get_next_char(void)
  27{
  28        int c;
  29        FILE *f;
  30
  31        c = '\n';
  32        if ((f = config_file) != NULL) {
  33                c = fgetc(f);
  34                if (c == '\r') {
  35                        /* DOS like systems */
  36                        c = fgetc(f);
  37                        if (c != '\n') {
  38                                ungetc(c, f);
  39                                c = '\r';
  40                        }
  41                }
  42                if (c == '\n')
  43                        config_linenr++;
  44                if (c == EOF) {
  45                        config_file_eof = 1;
  46                        c = '\n';
  47                }
  48        }
  49        return c;
  50}
  51
  52static char *parse_value(void)
  53{
  54        static char value[1024];
  55        int quote = 0, comment = 0, space = 0;
  56        size_t len = 0;
  57
  58        for (;;) {
  59                int c = get_next_char();
  60
  61                if (len >= sizeof(value) - 1)
  62                        return NULL;
  63                if (c == '\n') {
  64                        if (quote)
  65                                return NULL;
  66                        value[len] = 0;
  67                        return value;
  68                }
  69                if (comment)
  70                        continue;
  71                if (isspace(c) && !quote) {
  72                        space = 1;
  73                        continue;
  74                }
  75                if (!quote) {
  76                        if (c == ';' || c == '#') {
  77                                comment = 1;
  78                                continue;
  79                        }
  80                }
  81                if (space) {
  82                        if (len)
  83                                value[len++] = ' ';
  84                        space = 0;
  85                }
  86                if (c == '\\') {
  87                        c = get_next_char();
  88                        switch (c) {
  89                        case '\n':
  90                                continue;
  91                        case 't':
  92                                c = '\t';
  93                                break;
  94                        case 'b':
  95                                c = '\b';
  96                                break;
  97                        case 'n':
  98                                c = '\n';
  99                                break;
 100                        /* Some characters escape as themselves */
 101                        case '\\': case '"':
 102                                break;
 103                        /* Reject unknown escape sequences */
 104                        default:
 105                                return NULL;
 106                        }
 107                        value[len++] = c;
 108                        continue;
 109                }
 110                if (c == '"') {
 111                        quote = 1-quote;
 112                        continue;
 113                }
 114                value[len++] = c;
 115        }
 116}
 117
 118static inline int iskeychar(int c)
 119{
 120        return isalnum(c) || c == '-';
 121}
 122
 123static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
 124{
 125        int c;
 126        char *value;
 127
 128        /* Get the full name */
 129        for (;;) {
 130                c = get_next_char();
 131                if (config_file_eof)
 132                        break;
 133                if (!iskeychar(c))
 134                        break;
 135                name[len++] = c;
 136                if (len >= MAXNAME)
 137                        return -1;
 138        }
 139        name[len] = 0;
 140        while (c == ' ' || c == '\t')
 141                c = get_next_char();
 142
 143        value = NULL;
 144        if (c != '\n') {
 145                if (c != '=')
 146                        return -1;
 147                value = parse_value();
 148                if (!value)
 149                        return -1;
 150        }
 151        return fn(name, value, data);
 152}
 153
 154static int get_extended_base_var(char *name, int baselen, int c)
 155{
 156        do {
 157                if (c == '\n')
 158                        return -1;
 159                c = get_next_char();
 160        } while (isspace(c));
 161
 162        /* We require the format to be '[base "extension"]' */
 163        if (c != '"')
 164                return -1;
 165        name[baselen++] = '.';
 166
 167        for (;;) {
 168                int ch = get_next_char();
 169
 170                if (ch == '\n')
 171                        return -1;
 172                if (ch == '"')
 173                        break;
 174                if (ch == '\\') {
 175                        ch = get_next_char();
 176                        if (ch == '\n')
 177                                return -1;
 178                }
 179                name[baselen++] = ch;
 180                if (baselen > MAXNAME / 2)
 181                        return -1;
 182        }
 183
 184        /* Final ']' */
 185        if (get_next_char() != ']')
 186                return -1;
 187        return baselen;
 188}
 189
 190static int get_base_var(char *name)
 191{
 192        int baselen = 0;
 193
 194        for (;;) {
 195                int c = get_next_char();
 196                if (config_file_eof)
 197                        return -1;
 198                if (c == ']')
 199                        return baselen;
 200                if (isspace(c))
 201                        return get_extended_base_var(name, baselen, c);
 202                if (!iskeychar(c) && c != '.')
 203                        return -1;
 204                if (baselen > MAXNAME / 2)
 205                        return -1;
 206                name[baselen++] = tolower(c);
 207        }
 208}
 209
 210static int perf_parse_file(config_fn_t fn, void *data)
 211{
 212        int comment = 0;
 213        int baselen = 0;
 214        static char var[MAXNAME];
 215
 216        /* U+FEFF Byte Order Mark in UTF8 */
 217        static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
 218        const unsigned char *bomptr = utf8_bom;
 219
 220        for (;;) {
 221                int c = get_next_char();
 222                if (bomptr && *bomptr) {
 223                        /* We are at the file beginning; skip UTF8-encoded BOM
 224                         * if present. Sane editors won't put this in on their
 225                         * own, but e.g. Windows Notepad will do it happily. */
 226                        if ((unsigned char) c == *bomptr) {
 227                                bomptr++;
 228                                continue;
 229                        } else {
 230                                /* Do not tolerate partial BOM. */
 231                                if (bomptr != utf8_bom)
 232                                        break;
 233                                /* No BOM at file beginning. Cool. */
 234                                bomptr = NULL;
 235                        }
 236                }
 237                if (c == '\n') {
 238                        if (config_file_eof)
 239                                return 0;
 240                        comment = 0;
 241                        continue;
 242                }
 243                if (comment || isspace(c))
 244                        continue;
 245                if (c == '#' || c == ';') {
 246                        comment = 1;
 247                        continue;
 248                }
 249                if (c == '[') {
 250                        baselen = get_base_var(var);
 251                        if (baselen <= 0)
 252                                break;
 253                        var[baselen++] = '.';
 254                        var[baselen] = 0;
 255                        continue;
 256                }
 257                if (!isalpha(c))
 258                        break;
 259                var[baselen] = tolower(c);
 260                if (get_value(fn, data, var, baselen+1) < 0)
 261                        break;
 262        }
 263        die("bad config file line %d in %s", config_linenr, config_file_name);
 264}
 265
 266static int parse_unit_factor(const char *end, unsigned long *val)
 267{
 268        if (!*end)
 269                return 1;
 270        else if (!strcasecmp(end, "k")) {
 271                *val *= 1024;
 272                return 1;
 273        }
 274        else if (!strcasecmp(end, "m")) {
 275                *val *= 1024 * 1024;
 276                return 1;
 277        }
 278        else if (!strcasecmp(end, "g")) {
 279                *val *= 1024 * 1024 * 1024;
 280                return 1;
 281        }
 282        return 0;
 283}
 284
 285static int perf_parse_long(const char *value, long *ret)
 286{
 287        if (value && *value) {
 288                char *end;
 289                long val = strtol(value, &end, 0);
 290                unsigned long factor = 1;
 291                if (!parse_unit_factor(end, &factor))
 292                        return 0;
 293                *ret = val * factor;
 294                return 1;
 295        }
 296        return 0;
 297}
 298
 299static void die_bad_config(const char *name)
 300{
 301        if (config_file_name)
 302                die("bad config value for '%s' in %s", name, config_file_name);
 303        die("bad config value for '%s'", name);
 304}
 305
 306int perf_config_int(const char *name, const char *value)
 307{
 308        long ret = 0;
 309        if (!perf_parse_long(value, &ret))
 310                die_bad_config(name);
 311        return ret;
 312}
 313
 314static int perf_config_bool_or_int(const char *name, const char *value, int *is_bool)
 315{
 316        *is_bool = 1;
 317        if (!value)
 318                return 1;
 319        if (!*value)
 320                return 0;
 321        if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))
 322                return 1;
 323        if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))
 324                return 0;
 325        *is_bool = 0;
 326        return perf_config_int(name, value);
 327}
 328
 329int perf_config_bool(const char *name, const char *value)
 330{
 331        int discard;
 332        return !!perf_config_bool_or_int(name, value, &discard);
 333}
 334
 335const char *perf_config_dirname(const char *name, const char *value)
 336{
 337        if (!name)
 338                return NULL;
 339        return value;
 340}
 341
 342static int perf_default_core_config(const char *var __used, const char *value __used)
 343{
 344        /* Add other config variables here and to Documentation/config.txt. */
 345        return 0;
 346}
 347
 348int perf_default_config(const char *var, const char *value, void *dummy __used)
 349{
 350        if (!prefixcmp(var, "core."))
 351                return perf_default_core_config(var, value);
 352
 353        /* Add other config variables here and to Documentation/config.txt. */
 354        return 0;
 355}
 356
 357static int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
 358{
 359        int ret;
 360        FILE *f = fopen(filename, "r");
 361
 362        ret = -1;
 363        if (f) {
 364                config_file = f;
 365                config_file_name = filename;
 366                config_linenr = 1;
 367                config_file_eof = 0;
 368                ret = perf_parse_file(fn, data);
 369                fclose(f);
 370                config_file_name = NULL;
 371        }
 372        return ret;
 373}
 374
 375static const char *perf_etc_perfconfig(void)
 376{
 377        static const char *system_wide;
 378        if (!system_wide)
 379                system_wide = system_path(ETC_PERFCONFIG);
 380        return system_wide;
 381}
 382
 383static int perf_env_bool(const char *k, int def)
 384{
 385        const char *v = getenv(k);
 386        return v ? perf_config_bool(k, v) : def;
 387}
 388
 389static int perf_config_system(void)
 390{
 391        return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0);
 392}
 393
 394static int perf_config_global(void)
 395{
 396        return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0);
 397}
 398
 399int perf_config(config_fn_t fn, void *data)
 400{
 401        int ret = 0, found = 0;
 402        char *repo_config = NULL;
 403        const char *home = NULL;
 404
 405        /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */
 406        if (config_exclusive_filename)
 407                return perf_config_from_file(fn, config_exclusive_filename, data);
 408        if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) {
 409                ret += perf_config_from_file(fn, perf_etc_perfconfig(),
 410                                            data);
 411                found += 1;
 412        }
 413
 414        home = getenv("HOME");
 415        if (perf_config_global() && home) {
 416                char *user_config = strdup(mkpath("%s/.perfconfig", home));
 417                if (!access(user_config, R_OK)) {
 418                        ret += perf_config_from_file(fn, user_config, data);
 419                        found += 1;
 420                }
 421                free(user_config);
 422        }
 423
 424        repo_config = perf_pathdup("config");
 425        if (!access(repo_config, R_OK)) {
 426                ret += perf_config_from_file(fn, repo_config, data);
 427                found += 1;
 428        }
 429        free(repo_config);
 430        if (found == 0)
 431                return -1;
 432        return ret;
 433}
 434
 435/*
 436 * Call this to report error for your variable that should not
 437 * get a boolean value (i.e. "[my] var" means "true").
 438 */
 439int config_error_nonbool(const char *var)
 440{
 441        return error("Missing value for '%s'", var);
 442}
 443
 444struct buildid_dir_config {
 445        char *dir;
 446};
 447
 448static int buildid_dir_command_config(const char *var, const char *value,
 449                                      void *data)
 450{
 451        struct buildid_dir_config *c = data;
 452        const char *v;
 453
 454        /* same dir for all commands */
 455        if (!prefixcmp(var, "buildid.") && !strcmp(var + 8, "dir")) {
 456                v = perf_config_dirname(var, value);
 457                if (!v)
 458                        return -1;
 459                strncpy(c->dir, v, MAXPATHLEN-1);
 460                c->dir[MAXPATHLEN-1] = '\0';
 461        }
 462        return 0;
 463}
 464
 465static void check_buildid_dir_config(void)
 466{
 467        struct buildid_dir_config c;
 468        c.dir = buildid_dir;
 469        perf_config(buildid_dir_command_config, &c);
 470}
 471
 472void set_buildid_dir(void)
 473{
 474        buildid_dir[0] = '\0';
 475
 476        /* try config file */
 477        check_buildid_dir_config();
 478
 479        /* default to $HOME/.debug */
 480        if (buildid_dir[0] == '\0') {
 481                char *v = getenv("HOME");
 482                if (v) {
 483                        snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s",
 484                                 v, DEBUG_CACHE_DIR);
 485                } else {
 486                        strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1);
 487                }
 488                buildid_dir[MAXPATHLEN-1] = '\0';
 489        }
 490        /* for communicating with external commands */
 491        setenv("PERF_BUILDID_DIR", buildid_dir, 1);
 492}
 493