perf tools: Make target to generate self contained source tarball
[deliverable/linux.git] / tools / perf / util / config.c
CommitLineData
07800601
IM
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;
07800601 18
a41794cd 19static const char *config_exclusive_filename;
07800601
IM
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];
f37a291c
IM
50 int quote = 0, comment = 0, space = 0;
51 size_t len = 0;
07800601
IM
52
53 for (;;) {
54 int c = get_next_char();
f37a291c 55
07800601
IM
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 (;;) {
83a0944f
IM
163 int ch = get_next_char();
164
165 if (ch == '\n')
07800601 166 return -1;
83a0944f 167 if (ch == '"')
07800601 168 break;
83a0944f
IM
169 if (ch == '\\') {
170 ch = get_next_char();
171 if (ch == '\n')
07800601
IM
172 return -1;
173 }
83a0944f 174 name[baselen++] = ch;
07800601
IM
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
07800601
IM
294static 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
301int 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
a41794cd 309static int perf_config_bool_or_int(const char *name, const char *value, int *is_bool)
07800601
IM
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
324int 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
f37a291c 330static int perf_default_core_config(const char *var __used, const char *value __used)
07800601
IM
331{
332 /* Add other config variables here and to Documentation/config.txt. */
333 return 0;
334}
335
f37a291c 336int perf_default_config(const char *var, const char *value, void *dummy __used)
07800601
IM
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
a41794cd 345static int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
07800601
IM
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
a41794cd 363static const char *perf_etc_perfconfig(void)
07800601
IM
364{
365 static const char *system_wide;
366 if (!system_wide)
367 system_wide = system_path(ETC_PERFCONFIG);
368 return system_wide;
369}
370
371static 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
a41794cd 377static int perf_config_system(void)
07800601
IM
378{
379 return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0);
380}
381
a41794cd 382static int perf_config_global(void)
07800601
IM
383{
384 return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0);
385}
386
387int 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
07800601
IM
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 */
427int config_error_nonbool(const char *var)
428{
429 return error("Missing value for '%s'", var);
430}
This page took 0.091679 seconds and 5 git commands to generate.