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