aboutsummaryrefslogtreecommitdiffstats
path: root/Documentation/perf_counter/config.c
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2009-04-20 09:00:56 -0400
committerIngo Molnar <mingo@elte.hu>2009-04-20 11:36:48 -0400
commit0780060124011b94af55830939c86cc0916be0f5 (patch)
tree883a4d0ed69862ab49e6d4bf4e19debafeb5c48c /Documentation/perf_counter/config.c
parentd24e473e5b2ca86d1288b9416227ccc603313d0f (diff)
perf_counter tools: add in basic glue from Git
First very raw version at having a central 'perf' command and a list of subcommands: perf top perf stat perf record perf report ... This is done by picking up Git's collection of utility functions, and hacking them to build fine in this new environment. Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'Documentation/perf_counter/config.c')
-rw-r--r--Documentation/perf_counter/config.c966
1 files changed, 966 insertions, 0 deletions
diff --git a/Documentation/perf_counter/config.c b/Documentation/perf_counter/config.c
new file mode 100644
index 000000000000..672d53959334
--- /dev/null
+++ b/Documentation/perf_counter/config.c
@@ -0,0 +1,966 @@
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;
18static int zlib_compression_seen;
19
20const char *config_exclusive_filename = NULL;
21
22static int get_next_char(void)
23{
24 int c;
25 FILE *f;
26
27 c = '\n';
28 if ((f = config_file) != NULL) {
29 c = fgetc(f);
30 if (c == '\r') {
31 /* DOS like systems */
32 c = fgetc(f);
33 if (c != '\n') {
34 ungetc(c, f);
35 c = '\r';
36 }
37 }
38 if (c == '\n')
39 config_linenr++;
40 if (c == EOF) {
41 config_file_eof = 1;
42 c = '\n';
43 }
44 }
45 return c;
46}
47
48static char *parse_value(void)
49{
50 static char value[1024];
51 int quote = 0, comment = 0, len = 0, space = 0;
52
53 for (;;) {
54 int c = get_next_char();
55 if (len >= sizeof(value) - 1)
56 return NULL;
57 if (c == '\n') {
58 if (quote)
59 return NULL;
60 value[len] = 0;
61 return value;
62 }
63 if (comment)
64 continue;
65 if (isspace(c) && !quote) {
66 space = 1;
67 continue;
68 }
69 if (!quote) {
70 if (c == ';' || c == '#') {
71 comment = 1;
72 continue;
73 }
74 }
75 if (space) {
76 if (len)
77 value[len++] = ' ';
78 space = 0;
79 }
80 if (c == '\\') {
81 c = get_next_char();
82 switch (c) {
83 case '\n':
84 continue;
85 case 't':
86 c = '\t';
87 break;
88 case 'b':
89 c = '\b';
90 break;
91 case 'n':
92 c = '\n';
93 break;
94 /* Some characters escape as themselves */
95 case '\\': case '"':
96 break;
97 /* Reject unknown escape sequences */
98 default:
99 return NULL;
100 }
101 value[len++] = c;
102 continue;
103 }
104 if (c == '"') {
105 quote = 1-quote;
106 continue;
107 }
108 value[len++] = c;
109 }
110}
111
112static inline int iskeychar(int c)
113{
114 return isalnum(c) || c == '-';
115}
116
117static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
118{
119 int c;
120 char *value;
121
122 /* Get the full name */
123 for (;;) {
124 c = get_next_char();
125 if (config_file_eof)
126 break;
127 if (!iskeychar(c))
128 break;
129 name[len++] = tolower(c);
130 if (len >= MAXNAME)
131 return -1;
132 }
133 name[len] = 0;
134 while (c == ' ' || c == '\t')
135 c = get_next_char();
136
137 value = NULL;
138 if (c != '\n') {
139 if (c != '=')
140 return -1;
141 value = parse_value();
142 if (!value)
143 return -1;
144 }
145 return fn(name, value, data);
146}
147
148static int get_extended_base_var(char *name, int baselen, int c)
149{
150 do {
151 if (c == '\n')
152 return -1;
153 c = get_next_char();
154 } while (isspace(c));
155
156 /* We require the format to be '[base "extension"]' */
157 if (c != '"')
158 return -1;
159 name[baselen++] = '.';
160
161 for (;;) {
162 int c = get_next_char();
163 if (c == '\n')
164 return -1;
165 if (c == '"')
166 break;
167 if (c == '\\') {
168 c = get_next_char();
169 if (c == '\n')
170 return -1;
171 }
172 name[baselen++] = c;
173 if (baselen > MAXNAME / 2)
174 return -1;
175 }
176
177 /* Final ']' */
178 if (get_next_char() != ']')
179 return -1;
180 return baselen;
181}
182
183static int get_base_var(char *name)
184{
185 int baselen = 0;
186
187 for (;;) {
188 int c = get_next_char();
189 if (config_file_eof)
190 return -1;
191 if (c == ']')
192 return baselen;
193 if (isspace(c))
194 return get_extended_base_var(name, baselen, c);
195 if (!iskeychar(c) && c != '.')
196 return -1;
197 if (baselen > MAXNAME / 2)
198 return -1;
199 name[baselen++] = tolower(c);
200 }
201}
202
203static int perf_parse_file(config_fn_t fn, void *data)
204{
205 int comment = 0;
206 int baselen = 0;
207 static char var[MAXNAME];
208
209 /* U+FEFF Byte Order Mark in UTF8 */
210 static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
211 const unsigned char *bomptr = utf8_bom;
212
213 for (;;) {
214 int c = get_next_char();
215 if (bomptr && *bomptr) {
216 /* We are at the file beginning; skip UTF8-encoded BOM
217 * if present. Sane editors won't put this in on their
218 * own, but e.g. Windows Notepad will do it happily. */
219 if ((unsigned char) c == *bomptr) {
220 bomptr++;
221 continue;
222 } else {
223 /* Do not tolerate partial BOM. */
224 if (bomptr != utf8_bom)
225 break;
226 /* No BOM at file beginning. Cool. */
227 bomptr = NULL;
228 }
229 }
230 if (c == '\n') {
231 if (config_file_eof)
232 return 0;
233 comment = 0;
234 continue;
235 }
236 if (comment || isspace(c))
237 continue;
238 if (c == '#' || c == ';') {
239 comment = 1;
240 continue;
241 }
242 if (c == '[') {
243 baselen = get_base_var(var);
244 if (baselen <= 0)
245 break;
246 var[baselen++] = '.';
247 var[baselen] = 0;
248 continue;
249 }
250 if (!isalpha(c))
251 break;
252 var[baselen] = tolower(c);
253 if (get_value(fn, data, var, baselen+1) < 0)
254 break;
255 }
256 die("bad config file line %d in %s", config_linenr, config_file_name);
257}
258
259static int parse_unit_factor(const char *end, unsigned long *val)
260{
261 if (!*end)
262 return 1;
263 else if (!strcasecmp(end, "k")) {
264 *val *= 1024;
265 return 1;
266 }
267 else if (!strcasecmp(end, "m")) {
268 *val *= 1024 * 1024;
269 return 1;
270 }
271 else if (!strcasecmp(end, "g")) {
272 *val *= 1024 * 1024 * 1024;
273 return 1;
274 }
275 return 0;
276}
277
278static int perf_parse_long(const char *value, long *ret)
279{
280 if (value && *value) {
281 char *end;
282 long val = strtol(value, &end, 0);
283 unsigned long factor = 1;
284 if (!parse_unit_factor(end, &factor))
285 return 0;
286 *ret = val * factor;
287 return 1;
288 }
289 return 0;
290}
291
292int perf_parse_ulong(const char *value, unsigned long *ret)
293{
294 if (value && *value) {
295 char *end;
296 unsigned long val = strtoul(value, &end, 0);
297 if (!parse_unit_factor(end, &val))
298 return 0;
299 *ret = val;
300 return 1;
301 }
302 return 0;
303}
304
305static void die_bad_config(const char *name)
306{
307 if (config_file_name)
308 die("bad config value for '%s' in %s", name, config_file_name);
309 die("bad config value for '%s'", name);
310}
311
312int perf_config_int(const char *name, const char *value)
313{
314 long ret = 0;
315 if (!perf_parse_long(value, &ret))
316 die_bad_config(name);
317 return ret;
318}
319
320unsigned long perf_config_ulong(const char *name, const char *value)
321{
322 unsigned long ret;
323 if (!perf_parse_ulong(value, &ret))
324 die_bad_config(name);
325 return ret;
326}
327
328int perf_config_bool_or_int(const char *name, const char *value, int *is_bool)
329{
330 *is_bool = 1;
331 if (!value)
332 return 1;
333 if (!*value)
334 return 0;
335 if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))
336 return 1;
337 if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))
338 return 0;
339 *is_bool = 0;
340 return perf_config_int(name, value);
341}
342
343int perf_config_bool(const char *name, const char *value)
344{
345 int discard;
346 return !!perf_config_bool_or_int(name, value, &discard);
347}
348
349int perf_config_string(const char **dest, const char *var, const char *value)
350{
351 if (!value)
352 return config_error_nonbool(var);
353 *dest = strdup(value);
354 return 0;
355}
356
357static int perf_default_core_config(const char *var, const char *value)
358{
359 /* Add other config variables here and to Documentation/config.txt. */
360 return 0;
361}
362
363int perf_default_config(const char *var, const char *value, void *dummy)
364{
365 if (!prefixcmp(var, "core."))
366 return perf_default_core_config(var, value);
367
368 /* Add other config variables here and to Documentation/config.txt. */
369 return 0;
370}
371
372int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
373{
374 int ret;
375 FILE *f = fopen(filename, "r");
376
377 ret = -1;
378 if (f) {
379 config_file = f;
380 config_file_name = filename;
381 config_linenr = 1;
382 config_file_eof = 0;
383 ret = perf_parse_file(fn, data);
384 fclose(f);
385 config_file_name = NULL;
386 }
387 return ret;
388}
389
390const char *perf_etc_perfconfig(void)
391{
392 static const char *system_wide;
393 if (!system_wide)
394 system_wide = system_path(ETC_PERFCONFIG);
395 return system_wide;
396}
397
398static int perf_env_bool(const char *k, int def)
399{
400 const char *v = getenv(k);
401 return v ? perf_config_bool(k, v) : def;
402}
403
404int perf_config_system(void)
405{
406 return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0);
407}
408
409int perf_config_global(void)
410{
411 return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0);
412}
413
414int perf_config(config_fn_t fn, void *data)
415{
416 int ret = 0, found = 0;
417 char *repo_config = NULL;
418 const char *home = NULL;
419
420 /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */
421 if (config_exclusive_filename)
422 return perf_config_from_file(fn, config_exclusive_filename, data);
423 if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) {
424 ret += perf_config_from_file(fn, perf_etc_perfconfig(),
425 data);
426 found += 1;
427 }
428
429 home = getenv("HOME");
430 if (perf_config_global() && home) {
431 char *user_config = strdup(mkpath("%s/.perfconfig", home));
432 if (!access(user_config, R_OK)) {
433 ret += perf_config_from_file(fn, user_config, data);
434 found += 1;
435 }
436 free(user_config);
437 }
438
439 repo_config = perf_pathdup("config");
440 if (!access(repo_config, R_OK)) {
441 ret += perf_config_from_file(fn, repo_config, data);
442 found += 1;
443 }
444 free(repo_config);
445 if (found == 0)
446 return -1;
447 return ret;
448}
449
450/*
451 * Find all the stuff for perf_config_set() below.
452 */
453
454#define MAX_MATCHES 512
455
456static struct {
457 int baselen;
458 char* key;
459 int do_not_match;
460 regex_t* value_regex;
461 int multi_replace;
462 size_t offset[MAX_MATCHES];
463 enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
464 int seen;
465} store;
466
467static int matches(const char* key, const char* value)
468{
469 return !strcmp(key, store.key) &&
470 (store.value_regex == NULL ||
471 (store.do_not_match ^
472 !regexec(store.value_regex, value, 0, NULL, 0)));
473}
474
475static int store_aux(const char* key, const char* value, void *cb)
476{
477 const char *ep;
478 size_t section_len;
479
480 switch (store.state) {
481 case KEY_SEEN:
482 if (matches(key, value)) {
483 if (store.seen == 1 && store.multi_replace == 0) {
484 warning("%s has multiple values", key);
485 } else if (store.seen >= MAX_MATCHES) {
486 error("too many matches for %s", key);
487 return 1;
488 }
489
490 store.offset[store.seen] = ftell(config_file);
491 store.seen++;
492 }
493 break;
494 case SECTION_SEEN:
495 /*
496 * What we are looking for is in store.key (both
497 * section and var), and its section part is baselen
498 * long. We found key (again, both section and var).
499 * We would want to know if this key is in the same
500 * section as what we are looking for. We already
501 * know we are in the same section as what should
502 * hold store.key.
503 */
504 ep = strrchr(key, '.');
505 section_len = ep - key;
506
507 if ((section_len != store.baselen) ||
508 memcmp(key, store.key, section_len+1)) {
509 store.state = SECTION_END_SEEN;
510 break;
511 }
512
513 /*
514 * Do not increment matches: this is no match, but we
515 * just made sure we are in the desired section.
516 */
517 store.offset[store.seen] = ftell(config_file);
518 /* fallthru */
519 case SECTION_END_SEEN:
520 case START:
521 if (matches(key, value)) {
522 store.offset[store.seen] = ftell(config_file);
523 store.state = KEY_SEEN;
524 store.seen++;
525 } else {
526 if (strrchr(key, '.') - key == store.baselen &&
527 !strncmp(key, store.key, store.baselen)) {
528 store.state = SECTION_SEEN;
529 store.offset[store.seen] = ftell(config_file);
530 }
531 }
532 }
533 return 0;
534}
535
536static int write_error(const char *filename)
537{
538 error("failed to write new configuration file %s", filename);
539
540 /* Same error code as "failed to rename". */
541 return 4;
542}
543
544static int store_write_section(int fd, const char* key)
545{
546 const char *dot;
547 int i, success;
548 struct strbuf sb = STRBUF_INIT;
549
550 dot = memchr(key, '.', store.baselen);
551 if (dot) {
552 strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key);
553 for (i = dot - key + 1; i < store.baselen; i++) {
554 if (key[i] == '"' || key[i] == '\\')
555 strbuf_addch(&sb, '\\');
556 strbuf_addch(&sb, key[i]);
557 }
558 strbuf_addstr(&sb, "\"]\n");
559 } else {
560 strbuf_addf(&sb, "[%.*s]\n", store.baselen, key);
561 }
562
563 success = write_in_full(fd, sb.buf, sb.len) == sb.len;
564 strbuf_release(&sb);
565
566 return success;
567}
568
569static int store_write_pair(int fd, const char* key, const char* value)
570{
571 int i, success;
572 int length = strlen(key + store.baselen + 1);
573 const char *quote = "";
574 struct strbuf sb = STRBUF_INIT;
575
576 /*
577 * Check to see if the value needs to be surrounded with a dq pair.
578 * Note that problematic characters are always backslash-quoted; this
579 * check is about not losing leading or trailing SP and strings that
580 * follow beginning-of-comment characters (i.e. ';' and '#') by the
581 * configuration parser.
582 */
583 if (value[0] == ' ')
584 quote = "\"";
585 for (i = 0; value[i]; i++)
586 if (value[i] == ';' || value[i] == '#')
587 quote = "\"";
588 if (i && value[i - 1] == ' ')
589 quote = "\"";
590
591 strbuf_addf(&sb, "\t%.*s = %s",
592 length, key + store.baselen + 1, quote);
593
594 for (i = 0; value[i]; i++)
595 switch (value[i]) {
596 case '\n':
597 strbuf_addstr(&sb, "\\n");
598 break;
599 case '\t':
600 strbuf_addstr(&sb, "\\t");
601 break;
602 case '"':
603 case '\\':
604 strbuf_addch(&sb, '\\');
605 default:
606 strbuf_addch(&sb, value[i]);
607 break;
608 }
609 strbuf_addf(&sb, "%s\n", quote);
610
611 success = write_in_full(fd, sb.buf, sb.len) == sb.len;
612 strbuf_release(&sb);
613
614 return success;
615}
616
617static ssize_t find_beginning_of_line(const char* contents, size_t size,
618 size_t offset_, int* found_bracket)
619{
620 size_t equal_offset = size, bracket_offset = size;
621 ssize_t offset;
622
623contline:
624 for (offset = offset_-2; offset > 0
625 && contents[offset] != '\n'; offset--)
626 switch (contents[offset]) {
627 case '=': equal_offset = offset; break;
628 case ']': bracket_offset = offset; break;
629 }
630 if (offset > 0 && contents[offset-1] == '\\') {
631 offset_ = offset;
632 goto contline;
633 }
634 if (bracket_offset < equal_offset) {
635 *found_bracket = 1;
636 offset = bracket_offset+1;
637 } else
638 offset++;
639
640 return offset;
641}
642
643int perf_config_set(const char* key, const char* value)
644{
645 return perf_config_set_multivar(key, value, NULL, 0);
646}
647
648/*
649 * If value==NULL, unset in (remove from) config,
650 * if value_regex!=NULL, disregard key/value pairs where value does not match.
651 * if multi_replace==0, nothing, or only one matching key/value is replaced,
652 * else all matching key/values (regardless how many) are removed,
653 * before the new pair is written.
654 *
655 * Returns 0 on success.
656 *
657 * This function does this:
658 *
659 * - it locks the config file by creating ".perf/config.lock"
660 *
661 * - it then parses the config using store_aux() as validator to find
662 * the position on the key/value pair to replace. If it is to be unset,
663 * it must be found exactly once.
664 *
665 * - the config file is mmap()ed and the part before the match (if any) is
666 * written to the lock file, then the changed part and the rest.
667 *
668 * - the config file is removed and the lock file rename()d to it.
669 *
670 */
671int perf_config_set_multivar(const char* key, const char* value,
672 const char* value_regex, int multi_replace)
673{
674 int i, dot;
675 int fd = -1, in_fd;
676 int ret;
677 char* config_filename;
678 const char* last_dot = strrchr(key, '.');
679
680 if (config_exclusive_filename)
681 config_filename = strdup(config_exclusive_filename);
682 else
683 config_filename = perf_pathdup("config");
684
685 /*
686 * Since "key" actually contains the section name and the real
687 * key name separated by a dot, we have to know where the dot is.
688 */
689
690 if (last_dot == NULL) {
691 error("key does not contain a section: %s", key);
692 ret = 2;
693 goto out_free;
694 }
695 store.baselen = last_dot - key;
696
697 store.multi_replace = multi_replace;
698
699 /*
700 * Validate the key and while at it, lower case it for matching.
701 */
702 store.key = malloc(strlen(key) + 1);
703 dot = 0;
704 for (i = 0; key[i]; i++) {
705 unsigned char c = key[i];
706 if (c == '.')
707 dot = 1;
708 /* Leave the extended basename untouched.. */
709 if (!dot || i > store.baselen) {
710 if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
711 error("invalid key: %s", key);
712 free(store.key);
713 ret = 1;
714 goto out_free;
715 }
716 c = tolower(c);
717 } else if (c == '\n') {
718 error("invalid key (newline): %s", key);
719 free(store.key);
720 ret = 1;
721 goto out_free;
722 }
723 store.key[i] = c;
724 }
725 store.key[i] = 0;
726
727 /*
728 * If .perf/config does not exist yet, write a minimal version.
729 */
730 in_fd = open(config_filename, O_RDONLY);
731 if ( in_fd < 0 ) {
732 free(store.key);
733
734 if ( ENOENT != errno ) {
735 error("opening %s: %s", config_filename,
736 strerror(errno));
737 ret = 3; /* same as "invalid config file" */
738 goto out_free;
739 }
740 /* if nothing to unset, error out */
741 if (value == NULL) {
742 ret = 5;
743 goto out_free;
744 }
745
746 store.key = (char*)key;
747 if (!store_write_section(fd, key) ||
748 !store_write_pair(fd, key, value))
749 goto write_err_out;
750 } else {
751 struct stat st;
752 char* contents;
753 size_t contents_sz, copy_begin, copy_end;
754 int i, new_line = 0;
755
756 if (value_regex == NULL)
757 store.value_regex = NULL;
758 else {
759 if (value_regex[0] == '!') {
760 store.do_not_match = 1;
761 value_regex++;
762 } else
763 store.do_not_match = 0;
764
765 store.value_regex = (regex_t*)malloc(sizeof(regex_t));
766 if (regcomp(store.value_regex, value_regex,
767 REG_EXTENDED)) {
768 error("invalid pattern: %s", value_regex);
769 free(store.value_regex);
770 ret = 6;
771 goto out_free;
772 }
773 }
774
775 store.offset[0] = 0;
776 store.state = START;
777 store.seen = 0;
778
779 /*
780 * After this, store.offset will contain the *end* offset
781 * of the last match, or remain at 0 if no match was found.
782 * As a side effect, we make sure to transform only a valid
783 * existing config file.
784 */
785 if (perf_config_from_file(store_aux, config_filename, NULL)) {
786 error("invalid config file %s", config_filename);
787 free(store.key);
788 if (store.value_regex != NULL) {
789 regfree(store.value_regex);
790 free(store.value_regex);
791 }
792 ret = 3;
793 goto out_free;
794 }
795
796 free(store.key);
797 if (store.value_regex != NULL) {
798 regfree(store.value_regex);
799 free(store.value_regex);
800 }
801
802 /* if nothing to unset, or too many matches, error out */
803 if ((store.seen == 0 && value == NULL) ||
804 (store.seen > 1 && multi_replace == 0)) {
805 ret = 5;
806 goto out_free;
807 }
808
809 fstat(in_fd, &st);
810 contents_sz = xsize_t(st.st_size);
811 contents = mmap(NULL, contents_sz, PROT_READ,
812 MAP_PRIVATE, in_fd, 0);
813 close(in_fd);
814
815 if (store.seen == 0)
816 store.seen = 1;
817
818 for (i = 0, copy_begin = 0; i < store.seen; i++) {
819 if (store.offset[i] == 0) {
820 store.offset[i] = copy_end = contents_sz;
821 } else if (store.state != KEY_SEEN) {
822 copy_end = store.offset[i];
823 } else
824 copy_end = find_beginning_of_line(
825 contents, contents_sz,
826 store.offset[i]-2, &new_line);
827
828 if (copy_end > 0 && contents[copy_end-1] != '\n')
829 new_line = 1;
830
831 /* write the first part of the config */
832 if (copy_end > copy_begin) {
833 if (write_in_full(fd, contents + copy_begin,
834 copy_end - copy_begin) <
835 copy_end - copy_begin)
836 goto write_err_out;
837 if (new_line &&
838 write_in_full(fd, "\n", 1) != 1)
839 goto write_err_out;
840 }
841 copy_begin = store.offset[i];
842 }
843
844 /* write the pair (value == NULL means unset) */
845 if (value != NULL) {
846 if (store.state == START) {
847 if (!store_write_section(fd, key))
848 goto write_err_out;
849 }
850 if (!store_write_pair(fd, key, value))
851 goto write_err_out;
852 }
853
854 /* write the rest of the config */
855 if (copy_begin < contents_sz)
856 if (write_in_full(fd, contents + copy_begin,
857 contents_sz - copy_begin) <
858 contents_sz - copy_begin)
859 goto write_err_out;
860
861 munmap(contents, contents_sz);
862 }
863
864 ret = 0;
865
866out_free:
867 free(config_filename);
868 return ret;
869
870write_err_out:
871 goto out_free;
872
873}
874
875static int section_name_match (const char *buf, const char *name)
876{
877 int i = 0, j = 0, dot = 0;
878 for (; buf[i] && buf[i] != ']'; i++) {
879 if (!dot && isspace(buf[i])) {
880 dot = 1;
881 if (name[j++] != '.')
882 break;
883 for (i++; isspace(buf[i]); i++)
884 ; /* do nothing */
885 if (buf[i] != '"')
886 break;
887 continue;
888 }
889 if (buf[i] == '\\' && dot)
890 i++;
891 else if (buf[i] == '"' && dot) {
892 for (i++; isspace(buf[i]); i++)
893 ; /* do_nothing */
894 break;
895 }
896 if (buf[i] != name[j++])
897 break;
898 }
899 return (buf[i] == ']' && name[j] == 0);
900}
901
902/* if new_name == NULL, the section is removed instead */
903int perf_config_rename_section(const char *old_name, const char *new_name)
904{
905 int ret = 0, remove = 0;
906 char *config_filename;
907 int out_fd;
908 char buf[1024];
909
910 if (config_exclusive_filename)
911 config_filename = strdup(config_exclusive_filename);
912 else
913 config_filename = perf_pathdup("config");
914 if (out_fd < 0) {
915 ret = error("could not lock config file %s", config_filename);
916 goto out;
917 }
918
919 if (!(config_file = fopen(config_filename, "rb"))) {
920 /* no config file means nothing to rename, no error */
921 goto unlock_and_out;
922 }
923
924 while (fgets(buf, sizeof(buf), config_file)) {
925 int i;
926 int length;
927 for (i = 0; buf[i] && isspace(buf[i]); i++)
928 ; /* do nothing */
929 if (buf[i] == '[') {
930 /* it's a section */
931 if (section_name_match (&buf[i+1], old_name)) {
932 ret++;
933 if (new_name == NULL) {
934 remove = 1;
935 continue;
936 }
937 store.baselen = strlen(new_name);
938 if (!store_write_section(out_fd, new_name)) {
939 goto out;
940 }
941 continue;
942 }
943 remove = 0;
944 }
945 if (remove)
946 continue;
947 length = strlen(buf);
948 if (write_in_full(out_fd, buf, length) != length) {
949 goto out;
950 }
951 }
952 fclose(config_file);
953 unlock_and_out:
954 out:
955 free(config_filename);
956 return ret;
957}
958
959/*
960 * Call this to report error for your variable that should not
961 * get a boolean value (i.e. "[my] var" means "true").
962 */
963int config_error_nonbool(const char *var)
964{
965 return error("Missing value for '%s'", var);
966}