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 static FILE *config_file; 15 static const char *config_file_name; 16 static int config_linenr; 17 static int config_file_eof; 18 19 static const char *config_exclusive_filename; 20 21 static 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 47 static 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 113 static inline int iskeychar(int c) 114 { 115 return isalnum(c) || c == '-'; 116 } 117 118 static 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 149 static 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 185 static 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 205 static 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 261 static 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 280 static 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 294 static void die_bad_config(const char *name) 295 { 296 if (config_file_name) 297 die("bad config value for '%s' in %s", name, config_file_name); 298 die("bad config value for '%s'", name); 299 } 300 301 int perf_config_int(const char *name, const char *value) 302 { 303 long ret = 0; 304 if (!perf_parse_long(value, &ret)) 305 die_bad_config(name); 306 return ret; 307 } 308 309 static int perf_config_bool_or_int(const char *name, const char *value, int *is_bool) 310 { 311 *is_bool = 1; 312 if (!value) 313 return 1; 314 if (!*value) 315 return 0; 316 if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on")) 317 return 1; 318 if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off")) 319 return 0; 320 *is_bool = 0; 321 return perf_config_int(name, value); 322 } 323 324 int perf_config_bool(const char *name, const char *value) 325 { 326 int discard; 327 return !!perf_config_bool_or_int(name, value, &discard); 328 } 329 330 static int perf_default_core_config(const char *var __used, const char *value __used) 331 { 332 /* Add other config variables here and to Documentation/config.txt. */ 333 return 0; 334 } 335 336 int perf_default_config(const char *var, const char *value, void *dummy __used) 337 { 338 if (!prefixcmp(var, "core.")) 339 return perf_default_core_config(var, value); 340 341 /* Add other config variables here and to Documentation/config.txt. */ 342 return 0; 343 } 344 345 static int perf_config_from_file(config_fn_t fn, const char *filename, void *data) 346 { 347 int ret; 348 FILE *f = fopen(filename, "r"); 349 350 ret = -1; 351 if (f) { 352 config_file = f; 353 config_file_name = filename; 354 config_linenr = 1; 355 config_file_eof = 0; 356 ret = perf_parse_file(fn, data); 357 fclose(f); 358 config_file_name = NULL; 359 } 360 return ret; 361 } 362 363 static const char *perf_etc_perfconfig(void) 364 { 365 static const char *system_wide; 366 if (!system_wide) 367 system_wide = system_path(ETC_PERFCONFIG); 368 return system_wide; 369 } 370 371 static int perf_env_bool(const char *k, int def) 372 { 373 const char *v = getenv(k); 374 return v ? perf_config_bool(k, v) : def; 375 } 376 377 static int perf_config_system(void) 378 { 379 return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0); 380 } 381 382 static int perf_config_global(void) 383 { 384 return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0); 385 } 386 387 int perf_config(config_fn_t fn, void *data) 388 { 389 int ret = 0, found = 0; 390 char *repo_config = NULL; 391 const char *home = NULL; 392 393 /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */ 394 if (config_exclusive_filename) 395 return perf_config_from_file(fn, config_exclusive_filename, data); 396 if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) { 397 ret += perf_config_from_file(fn, perf_etc_perfconfig(), 398 data); 399 found += 1; 400 } 401 402 home = getenv("HOME"); 403 if (perf_config_global() && home) { 404 char *user_config = strdup(mkpath("%s/.perfconfig", home)); 405 if (!access(user_config, R_OK)) { 406 ret += perf_config_from_file(fn, user_config, data); 407 found += 1; 408 } 409 free(user_config); 410 } 411 412 repo_config = perf_pathdup("config"); 413 if (!access(repo_config, R_OK)) { 414 ret += perf_config_from_file(fn, repo_config, data); 415 found += 1; 416 } 417 free(repo_config); 418 if (found == 0) 419 return -1; 420 return ret; 421 } 422 423 /* 424 * Call this to report error for your variable that should not 425 * get a boolean value (i.e. "[my] var" means "true"). 426 */ 427 int config_error_nonbool(const char *var) 428 { 429 return error("Missing value for '%s'", var); 430 } 431