/src/krb5/src/util/profile/prof_parse.c
Line | Count | Source |
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 | 2.49k | #define STATE_INIT_COMMENT 1 |
19 | 24.4k | #define STATE_STD_LINE 2 |
20 | 1.20k | #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 | 56.1k | { |
35 | 57.5k | while (*cp && isspace((int) (*cp))) |
36 | 1.45k | cp++; |
37 | 56.1k | return cp; |
38 | 56.1k | } |
39 | | |
40 | | static void strip_line(char *line) |
41 | 22.4k | { |
42 | 22.4k | char *p = line + strlen(line); |
43 | 43.8k | while (p > line && (p[-1] == '\n' || p[-1] == '\r')) |
44 | 21.4k | *--p = 0; |
45 | 22.4k | } |
46 | | |
47 | | static void parse_quoted_string(char *str) |
48 | 1.69k | { |
49 | 1.69k | char *to, *from; |
50 | | |
51 | 4.58k | for (to = from = str; *from && *from != '"'; to++, from++) { |
52 | 2.88k | if (*from == '\\' && *(from + 1) != '\0') { |
53 | 986 | from++; |
54 | 986 | switch (*from) { |
55 | 304 | case 'n': |
56 | 304 | *to = '\n'; |
57 | 304 | break; |
58 | 210 | case 't': |
59 | 210 | *to = '\t'; |
60 | 210 | break; |
61 | 194 | case 'b': |
62 | 194 | *to = '\b'; |
63 | 194 | break; |
64 | 278 | default: |
65 | 278 | *to = *from; |
66 | 986 | } |
67 | 986 | continue; |
68 | 986 | } |
69 | 1.89k | *to = *from; |
70 | 1.89k | } |
71 | 1.69k | *to = '\0'; |
72 | 1.69k | } |
73 | | |
74 | | |
75 | | static errcode_t parse_std_line(char *line, struct parse_state *state) |
76 | 22.9k | { |
77 | 22.9k | char *cp, ch, *tag, *value; |
78 | 22.9k | char *p; |
79 | 22.9k | errcode_t retval; |
80 | 22.9k | struct profile_node *node; |
81 | 22.9k | int do_subsection = 0; |
82 | | |
83 | 22.9k | if (*line == 0) |
84 | 199 | return 0; |
85 | 22.8k | cp = skip_over_blanks(line); |
86 | 22.8k | if (cp[0] == ';' || cp[0] == '#') |
87 | 392 | return 0; |
88 | 22.4k | strip_line(cp); |
89 | 22.4k | ch = *cp; |
90 | 22.4k | if (ch == 0) |
91 | 405 | return 0; |
92 | 22.0k | if (ch == '[') { |
93 | 2.69k | if (state->group_level > 1) |
94 | 7 | return PROF_SECTION_NOTOP; |
95 | 2.69k | cp++; |
96 | 2.69k | p = strchr(cp, ']'); |
97 | 2.69k | if (p == NULL) |
98 | 34 | return PROF_SECTION_SYNTAX; |
99 | 2.65k | *p = '\0'; |
100 | 2.65k | retval = profile_add_node(state->root_section, cp, NULL, 0, |
101 | 2.65k | &state->current_section); |
102 | 2.65k | if (retval) |
103 | 0 | return retval; |
104 | 2.65k | 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 | 2.65k | state->discard = profile_is_node_final(state->current_section) ? |
108 | 2.44k | 1 : 0; |
109 | | |
110 | | /* |
111 | | * Finish off the rest of the line. |
112 | | */ |
113 | 2.65k | cp = p+1; |
114 | 2.65k | if (*cp == '*') { |
115 | 228 | profile_make_node_final(state->current_section); |
116 | 228 | cp++; |
117 | 228 | } |
118 | | /* |
119 | | * A space after ']' should not be fatal |
120 | | */ |
121 | 2.65k | cp = skip_over_blanks(cp); |
122 | 2.65k | if (*cp) |
123 | 17 | return PROF_SECTION_SYNTAX; |
124 | 2.64k | return 0; |
125 | 2.65k | } |
126 | 19.3k | if (ch == '}') { |
127 | 1.49k | if (state->group_level < 2) |
128 | 3 | return PROF_EXTRA_CBRACE; |
129 | 1.49k | if (*(cp+1) == '*') |
130 | 448 | profile_make_node_final(state->current_section); |
131 | 1.49k | state->group_level--; |
132 | | /* Check if we are done discarding values from a subsection. */ |
133 | 1.49k | if (state->group_level < state->discard) |
134 | 698 | 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 | 1.49k | if (!state->discard) { |
138 | 1.22k | retval = profile_get_node_parent(state->current_section, |
139 | 1.22k | &state->current_section); |
140 | 1.22k | if (retval) |
141 | 0 | return retval; |
142 | 1.22k | } |
143 | 1.49k | return 0; |
144 | 1.49k | } |
145 | | /* |
146 | | * Parse the relations |
147 | | */ |
148 | 17.8k | tag = cp; |
149 | 17.8k | cp = strchr(cp, '='); |
150 | 17.8k | if (!cp) |
151 | 36 | return PROF_RELATION_SYNTAX; |
152 | 17.7k | if (cp == tag) |
153 | 1 | return PROF_RELATION_SYNTAX; |
154 | 17.7k | *cp = '\0'; |
155 | 17.7k | p = tag; |
156 | | /* Look for whitespace on left-hand side. */ |
157 | 36.1k | while (p < cp && !isspace((int)*p)) |
158 | 18.3k | p++; |
159 | 17.7k | if (p < cp) { |
160 | | /* Found some sort of whitespace. */ |
161 | 260 | *p++ = 0; |
162 | | /* If we have more non-whitespace, it's an error. */ |
163 | 454 | while (p < cp) { |
164 | 195 | if (!isspace((int)*p)) |
165 | 1 | return PROF_RELATION_SYNTAX; |
166 | 194 | p++; |
167 | 194 | } |
168 | 260 | } |
169 | 17.7k | cp = skip_over_blanks(cp+1); |
170 | 17.7k | value = cp; |
171 | 17.7k | if (value[0] == '"') { |
172 | 1.69k | value++; |
173 | 1.69k | parse_quoted_string(value); |
174 | 16.0k | } else if (value[0] == 0) { |
175 | 718 | do_subsection++; |
176 | 718 | state->state = STATE_GET_OBRACE; |
177 | 15.3k | } else if (value[0] == '{' && *(skip_over_blanks(value+1)) == 0) |
178 | 12.1k | do_subsection++; |
179 | 3.16k | else { |
180 | 3.16k | cp = value + strlen(value) - 1; |
181 | 3.36k | while ((cp > value) && isspace((int) (*cp))) |
182 | 194 | *cp-- = 0; |
183 | 3.16k | } |
184 | 17.7k | if (do_subsection) { |
185 | 12.9k | p = strchr(tag, '*'); |
186 | 12.9k | if (p) |
187 | 5.69k | *p = '\0'; |
188 | 12.9k | state->group_level++; |
189 | 12.9k | if (!state->discard) { |
190 | 12.3k | retval = profile_add_node(state->current_section, tag, NULL, 0, |
191 | 12.3k | &state->current_section); |
192 | 12.3k | 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 | 12.3k | if (profile_is_node_final(state->current_section)) |
197 | 733 | state->discard = state->group_level; |
198 | 12.3k | if (p) |
199 | 5.66k | profile_make_node_final(state->current_section); |
200 | 12.3k | } |
201 | 12.9k | return 0; |
202 | 12.9k | } |
203 | 4.86k | p = strchr(tag, '*'); |
204 | 4.86k | if (p) |
205 | 729 | *p = '\0'; |
206 | 4.86k | if (!state->discard) { |
207 | 4.61k | profile_add_node(state->current_section, tag, value, 1, &node); |
208 | 4.61k | if (p && node) |
209 | 320 | profile_make_node_final(node); |
210 | 4.61k | } |
211 | 4.86k | return 0; |
212 | 17.7k | } |
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 | 23.9k | { |
293 | 23.9k | char *cp; |
294 | | |
295 | | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
296 | | if (strncmp(line, "include", 7) == 0 && isspace(line[7])) { |
297 | | cp = skip_over_blanks(line + 7); |
298 | | strip_line(cp); |
299 | | return parse_include_file(cp, state->root_section); |
300 | | } |
301 | | if (strncmp(line, "includedir", 10) == 0 && isspace(line[10])) { |
302 | | cp = skip_over_blanks(line + 10); |
303 | | strip_line(cp); |
304 | | return parse_include_dir(cp, state->root_section); |
305 | | } |
306 | | #endif |
307 | 23.9k | switch (state->state) { |
308 | 1.44k | case STATE_INIT_COMMENT: |
309 | 1.44k | if (strncmp(line, "module", 6) == 0 && isspace(line[6])) { |
310 | | /* |
311 | | * If we are expecting a module declaration, fill in *ret_modspec |
312 | | * and return PROF_MODULE, which will cause parsing to abort and |
313 | | * the module to be loaded instead. If we aren't expecting a |
314 | | * module declaration, return PROF_MODULE without filling in |
315 | | * *ret_modspec, which will be treated as an ordinary error. |
316 | | */ |
317 | 2 | if (ret_modspec) { |
318 | 0 | cp = skip_over_blanks(line + 6); |
319 | 0 | strip_line(cp); |
320 | 0 | *ret_modspec = strdup(cp); |
321 | 0 | if (!*ret_modspec) |
322 | 0 | return ENOMEM; |
323 | 0 | } |
324 | 2 | return PROF_MODULE; |
325 | 2 | } |
326 | 1.44k | if (line[0] != '[') |
327 | 474 | return 0; |
328 | 971 | state->state = STATE_STD_LINE; |
329 | 22.9k | case STATE_STD_LINE: |
330 | 22.9k | return parse_std_line(line, state); |
331 | 484 | case STATE_GET_OBRACE: |
332 | 484 | cp = skip_over_blanks(line); |
333 | 484 | if (*cp != '{') |
334 | 30 | return PROF_MISSING_OBRACE; |
335 | 454 | state->state = STATE_STD_LINE; |
336 | 23.9k | } |
337 | 454 | return 0; |
338 | 23.9k | } |
339 | | |
340 | | static errcode_t parse_file(FILE *f, struct parse_state *state, |
341 | | char **ret_modspec) |
342 | 1.04k | { |
343 | 25.0k | #define BUF_SIZE 2048 |
344 | 1.04k | char *bptr; |
345 | 1.04k | errcode_t retval; |
346 | | |
347 | 1.04k | bptr = malloc (BUF_SIZE); |
348 | 1.04k | if (!bptr) |
349 | 0 | return ENOMEM; |
350 | | |
351 | 24.8k | while (!feof(f)) { |
352 | 23.9k | if (fgets(bptr, BUF_SIZE, f) == NULL) |
353 | 11 | break; |
354 | 23.9k | #ifndef PROFILE_SUPPORTS_FOREIGN_NEWLINES |
355 | 23.9k | retval = parse_line(bptr, state, ret_modspec); |
356 | 23.9k | if (retval) { |
357 | 131 | free (bptr); |
358 | 131 | return retval; |
359 | 131 | } |
360 | | #else |
361 | | { |
362 | | char *p, *end; |
363 | | |
364 | | if (strlen(bptr) >= BUF_SIZE - 1) { |
365 | | /* The string may have foreign newlines and |
366 | | gotten chopped off on a non-newline |
367 | | boundary. Seek backwards to the last known |
368 | | newline. */ |
369 | | long offset; |
370 | | char *c = bptr + strlen (bptr); |
371 | | for (offset = 0; offset > -BUF_SIZE; offset--) { |
372 | | if (*c == '\r' || *c == '\n') { |
373 | | *c = '\0'; |
374 | | fseek (f, offset, SEEK_CUR); |
375 | | break; |
376 | | } |
377 | | c--; |
378 | | } |
379 | | } |
380 | | |
381 | | /* First change all newlines to \n */ |
382 | | for (p = bptr; *p != '\0'; p++) { |
383 | | if (*p == '\r') |
384 | | *p = '\n'; |
385 | | } |
386 | | /* Then parse all lines */ |
387 | | p = bptr; |
388 | | end = bptr + strlen (bptr); |
389 | | while (p < end) { |
390 | | char* newline; |
391 | | char* newp; |
392 | | |
393 | | newline = strchr (p, '\n'); |
394 | | if (newline != NULL) |
395 | | *newline = '\0'; |
396 | | |
397 | | /* parse_line modifies contents of p */ |
398 | | newp = p + strlen (p) + 1; |
399 | | retval = parse_line (p, state, ret_modspec); |
400 | | if (retval) { |
401 | | free (bptr); |
402 | | return retval; |
403 | | } |
404 | | |
405 | | p = newp; |
406 | | } |
407 | | } |
408 | | #endif |
409 | 23.9k | } |
410 | | |
411 | 913 | free (bptr); |
412 | 913 | return 0; |
413 | 1.04k | } |
414 | | |
415 | | errcode_t profile_parse_file(FILE *f, struct profile_node **root, |
416 | | char **ret_modspec) |
417 | 1.04k | { |
418 | 1.04k | struct parse_state state; |
419 | 1.04k | errcode_t retval; |
420 | | |
421 | 1.04k | *root = NULL; |
422 | | |
423 | | /* Initialize parsing state with a new root node. */ |
424 | 1.04k | state.state = STATE_INIT_COMMENT; |
425 | 1.04k | state.group_level = state.discard = 0; |
426 | 1.04k | state.current_section = NULL; |
427 | 1.04k | retval = profile_create_node("(root)", 0, &state.root_section); |
428 | 1.04k | if (retval) |
429 | 0 | return retval; |
430 | | |
431 | 1.04k | retval = parse_file(f, &state, ret_modspec); |
432 | 1.04k | if (retval) { |
433 | 131 | profile_free_node(state.root_section); |
434 | 131 | return retval; |
435 | 131 | } |
436 | 913 | *root = state.root_section; |
437 | 913 | return 0; |
438 | 1.04k | } |
439 | | |
440 | | errcode_t profile_process_directory(const char *dirname, |
441 | | struct profile_node **root) |
442 | 0 | { |
443 | 0 | errcode_t retval; |
444 | 0 | struct profile_node *node; |
445 | |
|
446 | 0 | *root = NULL; |
447 | 0 | retval = profile_create_node("(root)", 0, &node); |
448 | 0 | if (retval) |
449 | 0 | return retval; |
450 | 0 | retval = parse_include_dir(dirname, node); |
451 | 0 | if (retval) { |
452 | 0 | profile_free_node(node); |
453 | 0 | return retval; |
454 | 0 | } |
455 | 0 | *root = node; |
456 | 0 | return 0; |
457 | 0 | } |
458 | | |
459 | | /* |
460 | | * Return TRUE if the string begins or ends with whitespace |
461 | | */ |
462 | | static int need_double_quotes(char *str) |
463 | 4.21k | { |
464 | 4.21k | if (!str) |
465 | 0 | return 0; |
466 | 4.21k | if (str[0] == '\0') |
467 | 774 | return 1; |
468 | 3.44k | if (isspace((int) (*str)) ||isspace((int) (*(str + strlen(str) - 1)))) |
469 | 468 | return 1; |
470 | 2.97k | if (strchr(str, '\n') || strchr(str, '\t') || strchr(str, '\b')) |
471 | 1.24k | return 1; |
472 | 1.72k | return 0; |
473 | 2.97k | } |
474 | | |
475 | | /* |
476 | | * Output a string with double quotes, doing appropriate backquoting |
477 | | * of characters as necessary. |
478 | | */ |
479 | | static void output_quoted_string(char *str, void (*cb)(const char *,void *), |
480 | | void *data) |
481 | 2.48k | { |
482 | 2.48k | char ch; |
483 | 2.48k | char buf[2]; |
484 | | |
485 | 2.48k | cb("\"", data); |
486 | 2.48k | if (!str) { |
487 | 0 | cb("\"", data); |
488 | 0 | return; |
489 | 0 | } |
490 | 2.48k | buf[1] = 0; |
491 | 6.78k | while ((ch = *str++)) { |
492 | 4.29k | switch (ch) { |
493 | 336 | case '\\': |
494 | 336 | cb("\\\\", data); |
495 | 336 | break; |
496 | 292 | case '\n': |
497 | 292 | cb("\\n", data); |
498 | 292 | break; |
499 | 815 | case '\t': |
500 | 815 | cb("\\t", data); |
501 | 815 | break; |
502 | 1.33k | case '\b': |
503 | 1.33k | cb("\\b", data); |
504 | 1.33k | break; |
505 | 1.52k | default: |
506 | | /* This would be a lot faster if we scanned |
507 | | forward for the next "interesting" |
508 | | character. */ |
509 | 1.52k | buf[0] = ch; |
510 | 1.52k | cb(buf, data); |
511 | 1.52k | break; |
512 | 4.29k | } |
513 | 4.29k | } |
514 | 2.48k | cb("\"", data); |
515 | 2.48k | } |
516 | | |
517 | | |
518 | | |
519 | | #if defined(_WIN32) |
520 | | #define EOL "\r\n" |
521 | | #endif |
522 | | |
523 | | #ifndef EOL |
524 | 29.4k | #define EOL "\n" |
525 | | #endif |
526 | | |
527 | | /* Errors should be returned, not ignored! */ |
528 | | static void dump_profile(struct profile_node *root, int level, |
529 | | void (*cb)(const char *, void *), void *data) |
530 | 13.5k | { |
531 | 13.5k | int i, final; |
532 | 13.5k | struct profile_node *p; |
533 | 13.5k | void *iter; |
534 | 13.5k | long retval; |
535 | 13.5k | char *name, *value; |
536 | | |
537 | 13.5k | iter = 0; |
538 | 17.0k | do { |
539 | 17.0k | retval = profile_find_node_relation(root, 0, &iter, |
540 | 17.0k | &name, &value, &final); |
541 | 17.0k | if (retval) |
542 | 12.8k | break; |
543 | 25.5k | for (i=0; i < level; i++) |
544 | 21.3k | cb("\t", data); |
545 | 4.21k | cb(name, data); |
546 | 4.21k | cb(final ? "*" : "", data); |
547 | 4.21k | cb(" = ", data); |
548 | 4.21k | if (need_double_quotes(value)) |
549 | 2.48k | output_quoted_string(value, cb, data); |
550 | 1.72k | else |
551 | 1.72k | cb(value, data); |
552 | 4.21k | cb(EOL, data); |
553 | 4.21k | } while (iter != 0); |
554 | | |
555 | 13.5k | iter = 0; |
556 | 14.7k | do { |
557 | 14.7k | retval = profile_find_node_subsection(root, 0, &iter, |
558 | 14.7k | &name, &p); |
559 | 14.7k | if (retval) |
560 | 2.11k | break; |
561 | 12.6k | if (level == 0) { /* [xxx] */ |
562 | 1.83k | cb("[", data); |
563 | 1.83k | cb(name, data); |
564 | 1.83k | cb("]", data); |
565 | 1.83k | cb(profile_is_node_final(p) ? "*" : "", data); |
566 | 1.83k | cb(EOL, data); |
567 | 1.83k | dump_profile(p, level+1, cb, data); |
568 | 1.83k | cb(EOL, data); |
569 | 10.8k | } else { /* xxx = { ... } */ |
570 | 722k | for (i=0; i < level; i++) |
571 | 712k | cb("\t", data); |
572 | 10.8k | cb(name, data); |
573 | 10.8k | cb(" = {", data); |
574 | 10.8k | cb(EOL, data); |
575 | 10.8k | dump_profile(p, level+1, cb, data); |
576 | 722k | for (i=0; i < level; i++) |
577 | 712k | cb("\t", data); |
578 | 10.8k | cb("}", data); |
579 | 10.8k | cb(profile_is_node_final(p) ? "*" : "", data); |
580 | 10.8k | cb(EOL, data); |
581 | 10.8k | } |
582 | 12.6k | } while (iter != 0); |
583 | 13.5k | } |
584 | | |
585 | | static void dump_profile_to_file_cb(const char *str, void *data) |
586 | 0 | { |
587 | 0 | fputs(str, data); |
588 | 0 | } |
589 | | |
590 | | errcode_t profile_write_tree_file(struct profile_node *root, FILE *dstfile) |
591 | 0 | { |
592 | 0 | dump_profile(root, 0, dump_profile_to_file_cb, dstfile); |
593 | 0 | return 0; |
594 | 0 | } |
595 | | |
596 | | struct prof_buf { |
597 | | char *base; |
598 | | size_t cur, max; |
599 | | int err; |
600 | | }; |
601 | | |
602 | | static void add_data_to_buffer(struct prof_buf *b, const void *d, size_t len) |
603 | 1.55M | { |
604 | 1.55M | if (b->err) |
605 | 0 | return; |
606 | 1.55M | if (b->max - b->cur < len) { |
607 | 1.27k | size_t newsize; |
608 | 1.27k | char *newptr; |
609 | | |
610 | 1.27k | newsize = b->max + (b->max >> 1) + len + 1024; |
611 | 1.27k | newptr = realloc(b->base, newsize); |
612 | 1.27k | if (newptr == NULL) { |
613 | 0 | b->err = 1; |
614 | 0 | return; |
615 | 0 | } |
616 | 1.27k | b->base = newptr; |
617 | 1.27k | b->max = newsize; |
618 | 1.27k | } |
619 | 1.55M | memcpy(b->base + b->cur, d, len); |
620 | 1.55M | b->cur += len; /* ignore overflow */ |
621 | 1.55M | } |
622 | | |
623 | | static void dump_profile_to_buffer_cb(const char *str, void *data) |
624 | 1.54M | { |
625 | 1.54M | add_data_to_buffer((struct prof_buf *)data, str, strlen(str)); |
626 | 1.54M | } |
627 | | |
628 | | errcode_t profile_write_tree_to_buffer(struct profile_node *root, |
629 | | char **buf) |
630 | 913 | { |
631 | 913 | struct prof_buf prof_buf = { 0, 0, 0, 0 }; |
632 | | |
633 | 913 | dump_profile(root, 0, dump_profile_to_buffer_cb, &prof_buf); |
634 | 913 | if (prof_buf.err) { |
635 | 0 | *buf = NULL; |
636 | 0 | return ENOMEM; |
637 | 0 | } |
638 | 913 | add_data_to_buffer(&prof_buf, "", 1); /* append nul */ |
639 | 913 | if (prof_buf.max - prof_buf.cur > (prof_buf.max >> 3)) { |
640 | 883 | char *newptr = realloc(prof_buf.base, prof_buf.cur); |
641 | 883 | if (newptr) |
642 | 883 | prof_buf.base = newptr; |
643 | 883 | } |
644 | 913 | *buf = prof_buf.base; |
645 | 913 | return 0; |
646 | 913 | } |