xref: /openbmc/linux/tools/perf/util/config.c (revision 1fa6ac37)
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