/src/krb5/src/util/profile/prof_parse.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
2 | | #include "prof_int.h" |
3 | | |
4 | | #include <sys/types.h> |
5 | | #include <stdio.h> |
6 | | #include <string.h> |
7 | | #ifdef HAVE_STDLIB_H |
8 | | #include <stdlib.h> |
9 | | #endif |
10 | | #include <errno.h> |
11 | | #include <ctype.h> |
12 | | #ifndef _WIN32 |
13 | | #include <dirent.h> |
14 | | #endif |
15 | | |
16 | | #define SECTION_SEP_CHAR '/' |
17 | | |
18 | 33 | #define STATE_INIT_COMMENT 1 |
19 | 0 | #define STATE_STD_LINE 2 |
20 | 0 | #define STATE_GET_OBRACE 3 |
21 | | |
22 | | struct parse_state { |
23 | | int state; |
24 | | int group_level; |
25 | | struct profile_node *root_section; |
26 | | struct profile_node *current_section; |
27 | | }; |
28 | | |
29 | | static errcode_t parse_file(FILE *f, struct parse_state *state, |
30 | | char **ret_modspec); |
31 | | |
32 | | static char *skip_over_blanks(char *cp) |
33 | 0 | { |
34 | 0 | while (*cp && isspace((int) (*cp))) |
35 | 0 | cp++; |
36 | 0 | return cp; |
37 | 0 | } |
38 | | |
39 | | static void strip_line(char *line) |
40 | 0 | { |
41 | 0 | char *p = line + strlen(line); |
42 | 0 | while (p > line && (p[-1] == '\n' || p[-1] == '\r')) |
43 | 0 | *--p = 0; |
44 | 0 | } |
45 | | |
46 | | static void parse_quoted_string(char *str) |
47 | 0 | { |
48 | 0 | char *to, *from; |
49 | |
|
50 | 0 | for (to = from = str; *from && *from != '"'; to++, from++) { |
51 | 0 | if (*from == '\\' && *(from + 1) != '\0') { |
52 | 0 | from++; |
53 | 0 | switch (*from) { |
54 | 0 | case 'n': |
55 | 0 | *to = '\n'; |
56 | 0 | break; |
57 | 0 | case 't': |
58 | 0 | *to = '\t'; |
59 | 0 | break; |
60 | 0 | case 'b': |
61 | 0 | *to = '\b'; |
62 | 0 | break; |
63 | 0 | default: |
64 | 0 | *to = *from; |
65 | 0 | } |
66 | 0 | continue; |
67 | 0 | } |
68 | 0 | *to = *from; |
69 | 0 | } |
70 | 0 | *to = '\0'; |
71 | 0 | } |
72 | | |
73 | | |
74 | | static errcode_t parse_std_line(char *line, struct parse_state *state) |
75 | 0 | { |
76 | 0 | char *cp, ch, *tag, *value; |
77 | 0 | char *p; |
78 | 0 | errcode_t retval; |
79 | 0 | struct profile_node *node; |
80 | 0 | int do_subsection = 0; |
81 | 0 | void *iter = 0; |
82 | |
|
83 | 0 | if (*line == 0) |
84 | 0 | return 0; |
85 | 0 | cp = skip_over_blanks(line); |
86 | 0 | if (cp[0] == ';' || cp[0] == '#') |
87 | 0 | return 0; |
88 | 0 | strip_line(cp); |
89 | 0 | ch = *cp; |
90 | 0 | if (ch == 0) |
91 | 0 | return 0; |
92 | 0 | if (ch == '[') { |
93 | 0 | if (state->group_level > 0) |
94 | 0 | return PROF_SECTION_NOTOP; |
95 | 0 | cp++; |
96 | 0 | p = strchr(cp, ']'); |
97 | 0 | if (p == NULL) |
98 | 0 | return PROF_SECTION_SYNTAX; |
99 | 0 | *p = '\0'; |
100 | 0 | retval = profile_find_node_subsection(state->root_section, |
101 | 0 | cp, &iter, 0, |
102 | 0 | &state->current_section); |
103 | 0 | if (retval == PROF_NO_SECTION) { |
104 | 0 | retval = profile_add_node(state->root_section, |
105 | 0 | cp, 0, |
106 | 0 | &state->current_section); |
107 | 0 | if (retval) |
108 | 0 | return retval; |
109 | 0 | } else if (retval) |
110 | 0 | return retval; |
111 | | |
112 | | /* |
113 | | * Finish off the rest of the line. |
114 | | */ |
115 | 0 | cp = p+1; |
116 | 0 | if (*cp == '*') { |
117 | 0 | profile_make_node_final(state->current_section); |
118 | 0 | cp++; |
119 | 0 | } |
120 | | /* |
121 | | * A space after ']' should not be fatal |
122 | | */ |
123 | 0 | cp = skip_over_blanks(cp); |
124 | 0 | if (*cp) |
125 | 0 | return PROF_SECTION_SYNTAX; |
126 | 0 | return 0; |
127 | 0 | } |
128 | 0 | if (ch == '}') { |
129 | 0 | if (state->group_level == 0) |
130 | 0 | return PROF_EXTRA_CBRACE; |
131 | 0 | if (*(cp+1) == '*') |
132 | 0 | profile_make_node_final(state->current_section); |
133 | 0 | retval = profile_get_node_parent(state->current_section, |
134 | 0 | &state->current_section); |
135 | 0 | if (retval) |
136 | 0 | return retval; |
137 | 0 | state->group_level--; |
138 | 0 | return 0; |
139 | 0 | } |
140 | | /* |
141 | | * Parse the relations |
142 | | */ |
143 | 0 | tag = cp; |
144 | 0 | cp = strchr(cp, '='); |
145 | 0 | if (!cp) |
146 | 0 | return PROF_RELATION_SYNTAX; |
147 | 0 | if (cp == tag) |
148 | 0 | return PROF_RELATION_SYNTAX; |
149 | 0 | *cp = '\0'; |
150 | 0 | p = tag; |
151 | | /* Look for whitespace on left-hand side. */ |
152 | 0 | while (p < cp && !isspace((int)*p)) |
153 | 0 | p++; |
154 | 0 | if (p < cp) { |
155 | | /* Found some sort of whitespace. */ |
156 | 0 | *p++ = 0; |
157 | | /* If we have more non-whitespace, it's an error. */ |
158 | 0 | while (p < cp) { |
159 | 0 | if (!isspace((int)*p)) |
160 | 0 | return PROF_RELATION_SYNTAX; |
161 | 0 | p++; |
162 | 0 | } |
163 | 0 | } |
164 | 0 | cp = skip_over_blanks(cp+1); |
165 | 0 | value = cp; |
166 | 0 | if (value[0] == '"') { |
167 | 0 | value++; |
168 | 0 | parse_quoted_string(value); |
169 | 0 | } else if (value[0] == 0) { |
170 | 0 | do_subsection++; |
171 | 0 | state->state = STATE_GET_OBRACE; |
172 | 0 | } else if (value[0] == '{' && *(skip_over_blanks(value+1)) == 0) |
173 | 0 | do_subsection++; |
174 | 0 | else { |
175 | 0 | cp = value + strlen(value) - 1; |
176 | 0 | while ((cp > value) && isspace((int) (*cp))) |
177 | 0 | *cp-- = 0; |
178 | 0 | } |
179 | 0 | if (do_subsection) { |
180 | 0 | p = strchr(tag, '*'); |
181 | 0 | if (p) |
182 | 0 | *p = '\0'; |
183 | 0 | retval = profile_add_node(state->current_section, |
184 | 0 | tag, 0, &state->current_section); |
185 | 0 | if (retval) |
186 | 0 | return retval; |
187 | 0 | if (p) |
188 | 0 | profile_make_node_final(state->current_section); |
189 | 0 | state->group_level++; |
190 | 0 | return 0; |
191 | 0 | } |
192 | 0 | p = strchr(tag, '*'); |
193 | 0 | if (p) |
194 | 0 | *p = '\0'; |
195 | 0 | profile_add_node(state->current_section, tag, value, &node); |
196 | 0 | if (p) |
197 | 0 | profile_make_node_final(node); |
198 | 0 | return 0; |
199 | 0 | } |
200 | | |
201 | | /* Open and parse an included profile file. */ |
202 | | static errcode_t parse_include_file(const char *filename, |
203 | | struct profile_node *root_section) |
204 | 0 | { |
205 | 0 | FILE *fp; |
206 | 0 | errcode_t retval = 0; |
207 | 0 | struct parse_state state; |
208 | | |
209 | | /* Create a new state so that fragments are syntactically independent but |
210 | | * share a root section. */ |
211 | 0 | state.state = STATE_INIT_COMMENT; |
212 | 0 | state.group_level = 0; |
213 | 0 | state.root_section = root_section; |
214 | 0 | state.current_section = NULL; |
215 | |
|
216 | 0 | fp = fopen(filename, "r"); |
217 | 0 | if (fp == NULL) |
218 | 0 | return PROF_FAIL_INCLUDE_FILE; |
219 | 0 | retval = parse_file(fp, &state, NULL); |
220 | 0 | fclose(fp); |
221 | 0 | return retval; |
222 | 0 | } |
223 | | |
224 | | /* Return non-zero if filename contains only alphanumeric characters, dashes, |
225 | | * and underscores, or if the filename ends in ".conf" and is not a dotfile. */ |
226 | | static int valid_name(const char *filename) |
227 | 0 | { |
228 | 0 | const char *p; |
229 | 0 | size_t len = strlen(filename); |
230 | | |
231 | | /* Ignore dotfiles, which might be editor or filesystem artifacts. */ |
232 | 0 | if (*filename == '.') |
233 | 0 | return 0; |
234 | | |
235 | 0 | if (len >= 5 && !strcmp(filename + len - 5, ".conf")) |
236 | 0 | return 1; |
237 | | |
238 | 0 | for (p = filename; *p != '\0'; p++) { |
239 | 0 | if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_') |
240 | 0 | return 0; |
241 | 0 | } |
242 | 0 | return 1; |
243 | 0 | } |
244 | | |
245 | | /* |
246 | | * Include files within dirname. Only files with names ending in ".conf", or |
247 | | * consisting entirely of alphanumeric characters, dashes, and underscores are |
248 | | * included. This restriction avoids including editor backup files, .rpmsave |
249 | | * files, and the like. Files are processed in alphanumeric order. |
250 | | */ |
251 | | static errcode_t parse_include_dir(const char *dirname, |
252 | | struct profile_node *root_section) |
253 | 0 | { |
254 | 0 | errcode_t retval = 0; |
255 | 0 | char **fnames, *pathname; |
256 | 0 | int i; |
257 | |
|
258 | 0 | if (k5_dir_filenames(dirname, &fnames) != 0) |
259 | 0 | return PROF_FAIL_INCLUDE_DIR; |
260 | | |
261 | 0 | for (i = 0; fnames != NULL && fnames[i] != NULL; i++) { |
262 | 0 | if (!valid_name(fnames[i])) |
263 | 0 | continue; |
264 | 0 | if (asprintf(&pathname, "%s/%s", dirname, fnames[i]) < 0) { |
265 | 0 | retval = ENOMEM; |
266 | 0 | break; |
267 | 0 | } |
268 | 0 | retval = parse_include_file(pathname, root_section); |
269 | 0 | free(pathname); |
270 | 0 | if (retval) |
271 | 0 | break; |
272 | 0 | } |
273 | 0 | k5_free_filenames(fnames); |
274 | 0 | return retval; |
275 | 0 | } |
276 | | |
277 | | static errcode_t parse_line(char *line, struct parse_state *state, |
278 | | char **ret_modspec) |
279 | 0 | { |
280 | 0 | char *cp; |
281 | |
|
282 | 0 | if (strncmp(line, "include", 7) == 0 && isspace(line[7])) { |
283 | 0 | cp = skip_over_blanks(line + 7); |
284 | 0 | strip_line(cp); |
285 | 0 | return parse_include_file(cp, state->root_section); |
286 | 0 | } |
287 | 0 | if (strncmp(line, "includedir", 10) == 0 && isspace(line[10])) { |
288 | 0 | cp = skip_over_blanks(line + 10); |
289 | 0 | strip_line(cp); |
290 | 0 | return parse_include_dir(cp, state->root_section); |
291 | 0 | } |
292 | 0 | switch (state->state) { |
293 | 0 | case STATE_INIT_COMMENT: |
294 | 0 | if (strncmp(line, "module", 6) == 0 && isspace(line[6])) { |
295 | | /* |
296 | | * If we are expecting a module declaration, fill in *ret_modspec |
297 | | * and return PROF_MODULE, which will cause parsing to abort and |
298 | | * the module to be loaded instead. If we aren't expecting a |
299 | | * module declaration, return PROF_MODULE without filling in |
300 | | * *ret_modspec, which will be treated as an ordinary error. |
301 | | */ |
302 | 0 | if (ret_modspec) { |
303 | 0 | cp = skip_over_blanks(line + 6); |
304 | 0 | strip_line(cp); |
305 | 0 | *ret_modspec = strdup(cp); |
306 | 0 | if (!*ret_modspec) |
307 | 0 | return ENOMEM; |
308 | 0 | } |
309 | 0 | return PROF_MODULE; |
310 | 0 | } |
311 | 0 | if (line[0] != '[') |
312 | 0 | return 0; |
313 | 0 | state->state = STATE_STD_LINE; |
314 | 0 | case STATE_STD_LINE: |
315 | 0 | return parse_std_line(line, state); |
316 | 0 | case STATE_GET_OBRACE: |
317 | 0 | cp = skip_over_blanks(line); |
318 | 0 | if (*cp != '{') |
319 | 0 | return PROF_MISSING_OBRACE; |
320 | 0 | state->state = STATE_STD_LINE; |
321 | 0 | } |
322 | 0 | return 0; |
323 | 0 | } |
324 | | |
325 | | static errcode_t parse_file(FILE *f, struct parse_state *state, |
326 | | char **ret_modspec) |
327 | 33 | { |
328 | 66 | #define BUF_SIZE 2048 |
329 | 33 | char *bptr; |
330 | 33 | errcode_t retval; |
331 | | |
332 | 33 | bptr = malloc (BUF_SIZE); |
333 | 33 | if (!bptr) |
334 | 0 | return ENOMEM; |
335 | | |
336 | 33 | while (!feof(f)) { |
337 | 33 | if (fgets(bptr, BUF_SIZE, f) == NULL) |
338 | 33 | break; |
339 | 0 | #ifndef PROFILE_SUPPORTS_FOREIGN_NEWLINES |
340 | 0 | retval = parse_line(bptr, state, ret_modspec); |
341 | 0 | if (retval) { |
342 | 0 | free (bptr); |
343 | 0 | return retval; |
344 | 0 | } |
345 | | #else |
346 | | { |
347 | | char *p, *end; |
348 | | |
349 | | if (strlen(bptr) >= BUF_SIZE - 1) { |
350 | | /* The string may have foreign newlines and |
351 | | gotten chopped off on a non-newline |
352 | | boundary. Seek backwards to the last known |
353 | | newline. */ |
354 | | long offset; |
355 | | char *c = bptr + strlen (bptr); |
356 | | for (offset = 0; offset > -BUF_SIZE; offset--) { |
357 | | if (*c == '\r' || *c == '\n') { |
358 | | *c = '\0'; |
359 | | fseek (f, offset, SEEK_CUR); |
360 | | break; |
361 | | } |
362 | | c--; |
363 | | } |
364 | | } |
365 | | |
366 | | /* First change all newlines to \n */ |
367 | | for (p = bptr; *p != '\0'; p++) { |
368 | | if (*p == '\r') |
369 | | *p = '\n'; |
370 | | } |
371 | | /* Then parse all lines */ |
372 | | p = bptr; |
373 | | end = bptr + strlen (bptr); |
374 | | while (p < end) { |
375 | | char* newline; |
376 | | char* newp; |
377 | | |
378 | | newline = strchr (p, '\n'); |
379 | | if (newline != NULL) |
380 | | *newline = '\0'; |
381 | | |
382 | | /* parse_line modifies contents of p */ |
383 | | newp = p + strlen (p) + 1; |
384 | | retval = parse_line (p, state, ret_modspec); |
385 | | if (retval) { |
386 | | free (bptr); |
387 | | return retval; |
388 | | } |
389 | | |
390 | | p = newp; |
391 | | } |
392 | | } |
393 | | #endif |
394 | 0 | } |
395 | | |
396 | 33 | free (bptr); |
397 | 33 | return 0; |
398 | 33 | } |
399 | | |
400 | | errcode_t profile_parse_file(FILE *f, struct profile_node **root, |
401 | | char **ret_modspec) |
402 | 33 | { |
403 | 33 | struct parse_state state; |
404 | 33 | errcode_t retval; |
405 | | |
406 | 33 | *root = NULL; |
407 | | |
408 | | /* Initialize parsing state with a new root node. */ |
409 | 33 | state.state = STATE_INIT_COMMENT; |
410 | 33 | state.group_level = 0; |
411 | 33 | state.current_section = NULL; |
412 | 33 | retval = profile_create_node("(root)", 0, &state.root_section); |
413 | 33 | if (retval) |
414 | 0 | return retval; |
415 | | |
416 | 33 | retval = parse_file(f, &state, ret_modspec); |
417 | 33 | if (retval) { |
418 | 0 | profile_free_node(state.root_section); |
419 | 0 | return retval; |
420 | 0 | } |
421 | 33 | *root = state.root_section; |
422 | 33 | return 0; |
423 | 33 | } |
424 | | |
425 | | errcode_t profile_process_directory(const char *dirname, |
426 | | struct profile_node **root) |
427 | 0 | { |
428 | 0 | errcode_t retval; |
429 | 0 | struct profile_node *node; |
430 | |
|
431 | 0 | *root = NULL; |
432 | 0 | retval = profile_create_node("(root)", 0, &node); |
433 | 0 | if (retval) |
434 | 0 | return retval; |
435 | 0 | retval = parse_include_dir(dirname, node); |
436 | 0 | if (retval) { |
437 | 0 | profile_free_node(node); |
438 | 0 | return retval; |
439 | 0 | } |
440 | 0 | *root = node; |
441 | 0 | return 0; |
442 | 0 | } |
443 | | |
444 | | /* |
445 | | * Return TRUE if the string begins or ends with whitespace |
446 | | */ |
447 | | static int need_double_quotes(char *str) |
448 | 0 | { |
449 | 0 | if (!str) |
450 | 0 | return 0; |
451 | 0 | if (str[0] == '\0') |
452 | 0 | return 1; |
453 | 0 | if (isspace((int) (*str)) ||isspace((int) (*(str + strlen(str) - 1)))) |
454 | 0 | return 1; |
455 | 0 | if (strchr(str, '\n') || strchr(str, '\t') || strchr(str, '\b')) |
456 | 0 | return 1; |
457 | 0 | return 0; |
458 | 0 | } |
459 | | |
460 | | /* |
461 | | * Output a string with double quotes, doing appropriate backquoting |
462 | | * of characters as necessary. |
463 | | */ |
464 | | static void output_quoted_string(char *str, void (*cb)(const char *,void *), |
465 | | void *data) |
466 | 0 | { |
467 | 0 | char ch; |
468 | 0 | char buf[2]; |
469 | |
|
470 | 0 | cb("\"", data); |
471 | 0 | if (!str) { |
472 | 0 | cb("\"", data); |
473 | 0 | return; |
474 | 0 | } |
475 | 0 | buf[1] = 0; |
476 | 0 | while ((ch = *str++)) { |
477 | 0 | switch (ch) { |
478 | 0 | case '\\': |
479 | 0 | cb("\\\\", data); |
480 | 0 | break; |
481 | 0 | case '\n': |
482 | 0 | cb("\\n", data); |
483 | 0 | break; |
484 | 0 | case '\t': |
485 | 0 | cb("\\t", data); |
486 | 0 | break; |
487 | 0 | case '\b': |
488 | 0 | cb("\\b", data); |
489 | 0 | break; |
490 | 0 | default: |
491 | | /* This would be a lot faster if we scanned |
492 | | forward for the next "interesting" |
493 | | character. */ |
494 | 0 | buf[0] = ch; |
495 | 0 | cb(buf, data); |
496 | 0 | break; |
497 | 0 | } |
498 | 0 | } |
499 | 0 | cb("\"", data); |
500 | 0 | } |
501 | | |
502 | | |
503 | | |
504 | | #if defined(_WIN32) |
505 | | #define EOL "\r\n" |
506 | | #endif |
507 | | |
508 | | #ifndef EOL |
509 | 0 | #define EOL "\n" |
510 | | #endif |
511 | | |
512 | | /* Errors should be returned, not ignored! */ |
513 | | static void dump_profile(struct profile_node *root, int level, |
514 | | void (*cb)(const char *, void *), void *data) |
515 | 0 | { |
516 | 0 | int i; |
517 | 0 | struct profile_node *p; |
518 | 0 | void *iter; |
519 | 0 | long retval; |
520 | 0 | char *name, *value; |
521 | |
|
522 | 0 | iter = 0; |
523 | 0 | do { |
524 | 0 | retval = profile_find_node_relation(root, 0, &iter, |
525 | 0 | &name, &value); |
526 | 0 | if (retval) |
527 | 0 | break; |
528 | 0 | for (i=0; i < level; i++) |
529 | 0 | cb("\t", data); |
530 | 0 | if (need_double_quotes(value)) { |
531 | 0 | cb(name, data); |
532 | 0 | cb(" = ", data); |
533 | 0 | output_quoted_string(value, cb, data); |
534 | 0 | cb(EOL, data); |
535 | 0 | } else { |
536 | 0 | cb(name, data); |
537 | 0 | cb(" = ", data); |
538 | 0 | cb(value, data); |
539 | 0 | cb(EOL, data); |
540 | 0 | } |
541 | 0 | } while (iter != 0); |
542 | | |
543 | 0 | iter = 0; |
544 | 0 | do { |
545 | 0 | retval = profile_find_node_subsection(root, 0, &iter, |
546 | 0 | &name, &p); |
547 | 0 | if (retval) |
548 | 0 | break; |
549 | 0 | if (level == 0) { /* [xxx] */ |
550 | 0 | cb("[", data); |
551 | 0 | cb(name, data); |
552 | 0 | cb("]", data); |
553 | 0 | cb(profile_is_node_final(p) ? "*" : "", data); |
554 | 0 | cb(EOL, data); |
555 | 0 | dump_profile(p, level+1, cb, data); |
556 | 0 | cb(EOL, data); |
557 | 0 | } else { /* xxx = { ... } */ |
558 | 0 | for (i=0; i < level; i++) |
559 | 0 | cb("\t", data); |
560 | 0 | cb(name, data); |
561 | 0 | cb(" = {", data); |
562 | 0 | cb(EOL, data); |
563 | 0 | dump_profile(p, level+1, cb, data); |
564 | 0 | for (i=0; i < level; i++) |
565 | 0 | cb("\t", data); |
566 | 0 | cb("}", data); |
567 | 0 | cb(profile_is_node_final(p) ? "*" : "", data); |
568 | 0 | cb(EOL, data); |
569 | 0 | } |
570 | 0 | } while (iter != 0); |
571 | 0 | } |
572 | | |
573 | | static void dump_profile_to_file_cb(const char *str, void *data) |
574 | 0 | { |
575 | 0 | fputs(str, data); |
576 | 0 | } |
577 | | |
578 | | errcode_t profile_write_tree_file(struct profile_node *root, FILE *dstfile) |
579 | 0 | { |
580 | 0 | dump_profile(root, 0, dump_profile_to_file_cb, dstfile); |
581 | 0 | return 0; |
582 | 0 | } |
583 | | |
584 | | struct prof_buf { |
585 | | char *base; |
586 | | size_t cur, max; |
587 | | int err; |
588 | | }; |
589 | | |
590 | | static void add_data_to_buffer(struct prof_buf *b, const void *d, size_t len) |
591 | 0 | { |
592 | 0 | if (b->err) |
593 | 0 | return; |
594 | 0 | if (b->max - b->cur < len) { |
595 | 0 | size_t newsize; |
596 | 0 | char *newptr; |
597 | |
|
598 | 0 | newsize = b->max + (b->max >> 1) + len + 1024; |
599 | 0 | newptr = realloc(b->base, newsize); |
600 | 0 | if (newptr == NULL) { |
601 | 0 | b->err = 1; |
602 | 0 | return; |
603 | 0 | } |
604 | 0 | b->base = newptr; |
605 | 0 | b->max = newsize; |
606 | 0 | } |
607 | 0 | memcpy(b->base + b->cur, d, len); |
608 | 0 | b->cur += len; /* ignore overflow */ |
609 | 0 | } |
610 | | |
611 | | static void dump_profile_to_buffer_cb(const char *str, void *data) |
612 | 0 | { |
613 | 0 | add_data_to_buffer((struct prof_buf *)data, str, strlen(str)); |
614 | 0 | } |
615 | | |
616 | | errcode_t profile_write_tree_to_buffer(struct profile_node *root, |
617 | | char **buf) |
618 | 0 | { |
619 | 0 | struct prof_buf prof_buf = { 0, 0, 0, 0 }; |
620 | |
|
621 | 0 | dump_profile(root, 0, dump_profile_to_buffer_cb, &prof_buf); |
622 | 0 | if (prof_buf.err) { |
623 | 0 | *buf = NULL; |
624 | 0 | return ENOMEM; |
625 | 0 | } |
626 | 0 | add_data_to_buffer(&prof_buf, "", 1); /* append nul */ |
627 | 0 | if (prof_buf.max - prof_buf.cur > (prof_buf.max >> 3)) { |
628 | 0 | char *newptr = realloc(prof_buf.base, prof_buf.cur); |
629 | 0 | if (newptr) |
630 | 0 | prof_buf.base = newptr; |
631 | 0 | } |
632 | 0 | *buf = prof_buf.base; |
633 | 0 | return 0; |
634 | 0 | } |