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