libctf, dump: migrate towards dumping errors rather than truncation
[deliverable/binutils-gdb.git] / libctf / ctf-dump.c
1 /* Textual dumping of CTF data.
2 Copyright (C) 2019-2020 Free Software Foundation, Inc.
3
4 This file is part of libctf.
5
6 libctf is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 3, or (at your option) any later
9 version.
10
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 See the GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; see the file COPYING. If not see
18 <http://www.gnu.org/licenses/>. */
19
20 #include <ctf-impl.h>
21 #include <string.h>
22
23 #define str_append(s, a) ctf_str_append_noerr (s, a)
24
25 /* One item to be dumped, in string form. */
26
27 typedef struct ctf_dump_item
28 {
29 ctf_list_t cdi_list;
30 char *cdi_item;
31 } ctf_dump_item_t;
32
33 /* Cross-call state for dumping. Basically just enough to track the section in
34 use and a list of return strings. */
35
36 struct ctf_dump_state
37 {
38 ctf_sect_names_t cds_sect;
39 ctf_file_t *cds_fp;
40 ctf_dump_item_t *cds_current;
41 ctf_list_t cds_items;
42 };
43
44 /* Cross-call state for ctf_dump_member. */
45
46 typedef struct ctf_dump_membstate
47 {
48 char **cdm_str;
49 ctf_file_t *cdm_fp;
50 } ctf_dump_membstate_t;
51
52 static int
53 ctf_dump_append (ctf_dump_state_t *state, char *str)
54 {
55 ctf_dump_item_t *cdi;
56
57 if ((cdi = malloc (sizeof (struct ctf_dump_item))) == NULL)
58 return (ctf_set_errno (state->cds_fp, ENOMEM));
59
60 cdi->cdi_item = str;
61 ctf_list_append (&state->cds_items, cdi);
62 return 0;
63 }
64
65 static void
66 ctf_dump_free (ctf_dump_state_t *state)
67 {
68 ctf_dump_item_t *cdi, *next_cdi;
69
70 if (state == NULL)
71 return;
72
73 for (cdi = ctf_list_next (&state->cds_items); cdi != NULL;
74 cdi = next_cdi)
75 {
76 free (cdi->cdi_item);
77 next_cdi = ctf_list_next (cdi);
78 free (cdi);
79 }
80 }
81
82 /* Slices need special handling to distinguish them from their referenced
83 type. */
84
85 static int
86 ctf_is_slice (ctf_file_t *fp, ctf_id_t id, ctf_encoding_t *enc)
87 {
88 int kind = ctf_type_kind (fp, id);
89
90 return (((kind == CTF_K_INTEGER) || (kind == CTF_K_ENUM)
91 || (kind == CTF_K_FLOAT))
92 && ctf_type_reference (fp, id) != CTF_ERR
93 && ctf_type_encoding (fp, id, enc) == 0);
94 }
95
96 /* Return a dump for a single type, without member info: but do show the
97 type's references. */
98
99 static char *
100 ctf_dump_format_type (ctf_file_t *fp, ctf_id_t id, int flag)
101 {
102 ctf_id_t new_id;
103 char *str = NULL, *bit = NULL, *buf = NULL;
104
105 new_id = id;
106 do
107 {
108 ctf_encoding_t enc;
109 const char *nonroot_leader = "";
110 const char *nonroot_trailer = "";
111
112 id = new_id;
113 if (flag == CTF_ADD_NONROOT)
114 {
115 nonroot_leader = "{";
116 nonroot_trailer = "}";
117 }
118
119 buf = ctf_type_aname (fp, id);
120 if (!buf)
121 {
122 if (id == 0 || ctf_errno (fp) == ECTF_NONREPRESENTABLE)
123 {
124 str = str_append (str, " (type not represented in CTF)");
125 ctf_set_errno (fp, ECTF_NOTREF);
126 break;
127 }
128
129 goto err;
130 }
131
132 /* Slices get a different print representation. */
133
134 if (ctf_is_slice (fp, id, &enc))
135 {
136 ctf_type_encoding (fp, id, &enc);
137 if (asprintf (&bit, " %s%lx: [slice 0x%x:0x%x]%s",
138 nonroot_leader, id, enc.cte_offset, enc.cte_bits,
139 nonroot_trailer) < 0)
140 goto oom;
141 }
142 else
143 {
144 if (asprintf (&bit, " %s%lx: %s (size 0x%lx)%s", nonroot_leader,
145 id, buf[0] == '\0' ? "(nameless)" : buf,
146 (unsigned long) ctf_type_size (fp, id),
147 nonroot_trailer) < 0)
148 goto oom;
149 }
150 free (buf);
151 buf = NULL;
152 str = str_append (str, bit);
153 free (bit);
154 bit = NULL;
155
156 new_id = ctf_type_reference (fp, id);
157 if (new_id != CTF_ERR)
158 str = str_append (str, " ->");
159 } while (new_id != CTF_ERR);
160
161 if (ctf_errno (fp) != ECTF_NOTREF)
162 {
163 free (str);
164 return NULL;
165 }
166
167 return str;
168
169 oom:
170 ctf_set_errno (fp, errno);
171 err:
172 ctf_err_warn (fp, 1, "Cannot format name dumping type 0x%lx: %s", id,
173 ctf_errmsg (ctf_errno (fp)));
174 free (buf);
175 free (str);
176 free (bit);
177 return NULL;
178 }
179
180 /* Dump one string field from the file header into the cds_items. */
181 static int
182 ctf_dump_header_strfield (ctf_file_t *fp, ctf_dump_state_t *state,
183 const char *name, uint32_t value)
184 {
185 char *str;
186 if (value)
187 {
188 if (asprintf (&str, "%s: %s\n", name, ctf_strptr (fp, value)) < 0)
189 goto err;
190 ctf_dump_append (state, str);
191 }
192 return 0;
193
194 err:
195 return (ctf_set_errno (fp, errno));
196 }
197
198 /* Dump one section-offset field from the file header into the cds_items. */
199 static int
200 ctf_dump_header_sectfield (ctf_file_t *fp, ctf_dump_state_t *state,
201 const char *sect, uint32_t off, uint32_t nextoff)
202 {
203 char *str;
204 if (nextoff - off)
205 {
206 if (asprintf (&str, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect,
207 (unsigned long) off, (unsigned long) (nextoff - 1),
208 (unsigned long) (nextoff - off)) < 0)
209 goto err;
210 ctf_dump_append (state, str);
211 }
212 return 0;
213
214 err:
215 return (ctf_set_errno (fp, errno));
216 }
217
218 /* Dump the file header into the cds_items. */
219 static int
220 ctf_dump_header (ctf_file_t *fp, ctf_dump_state_t *state)
221 {
222 char *str;
223 const ctf_header_t *hp = fp->ctf_header;
224 const char *vertab[] =
225 {
226 NULL, "CTF_VERSION_1",
227 "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type "
228 "boundaries)",
229 "CTF_VERSION_2",
230 "CTF_VERSION_3", NULL
231 };
232 const char *verstr = NULL;
233
234 if (asprintf (&str, "Magic number: %x\n", hp->cth_magic) < 0)
235 goto err;
236 ctf_dump_append (state, str);
237
238 if (hp->cth_version <= CTF_VERSION)
239 verstr = vertab[hp->cth_version];
240
241 if (verstr == NULL)
242 verstr = "(not a valid version)";
243
244 if (asprintf (&str, "Version: %i (%s)\n", hp->cth_version,
245 verstr) < 0)
246 goto err;
247 ctf_dump_append (state, str);
248
249 /* Everything else is only printed if present. */
250
251 /* The flags are unusual in that they represent the ctf_file_t *in memory*:
252 flags representing compression, etc, are turned off as the file is
253 decompressed. So we store a copy of the flags before they are changed, for
254 the dumper. */
255
256 if (fp->ctf_openflags > 0)
257 {
258 if (fp->ctf_openflags)
259 if (asprintf (&str, "Flags: 0x%x (%s)", fp->ctf_openflags,
260 fp->ctf_openflags & CTF_F_COMPRESS ? "CTF_F_COMPRESS"
261 : "") < 0)
262 goto err;
263 ctf_dump_append (state, str);
264 }
265
266 if (ctf_dump_header_strfield (fp, state, "Parent label",
267 hp->cth_parlabel) < 0)
268 goto err;
269
270 if (ctf_dump_header_strfield (fp, state, "Parent name", hp->cth_parname) < 0)
271 goto err;
272
273 if (ctf_dump_header_strfield (fp, state, "Compilation unit name",
274 hp->cth_cuname) < 0)
275 goto err;
276
277 if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff,
278 hp->cth_objtoff) < 0)
279 goto err;
280
281 if (ctf_dump_header_sectfield (fp, state, "Data object section",
282 hp->cth_objtoff, hp->cth_funcoff) < 0)
283 goto err;
284
285 if (ctf_dump_header_sectfield (fp, state, "Function info section",
286 hp->cth_funcoff, hp->cth_varoff) < 0)
287 goto err;
288
289 if (ctf_dump_header_sectfield (fp, state, "Variable section",
290 hp->cth_varoff, hp->cth_typeoff) < 0)
291 goto err;
292
293 if (ctf_dump_header_sectfield (fp, state, "Type section",
294 hp->cth_typeoff, hp->cth_stroff) < 0)
295 goto err;
296
297 if (ctf_dump_header_sectfield (fp, state, "String section", hp->cth_stroff,
298 hp->cth_stroff + hp->cth_strlen + 1) < 0)
299 goto err;
300
301 return 0;
302 err:
303 return (ctf_set_errno (fp, errno));
304 }
305
306 /* Dump a single label into the cds_items. */
307
308 static int
309 ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
310 void *arg)
311 {
312 char *str;
313 char *typestr;
314 ctf_dump_state_t *state = arg;
315
316 if (asprintf (&str, "%s -> ", name) < 0)
317 return (ctf_set_errno (state->cds_fp, errno));
318
319 if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type,
320 CTF_ADD_ROOT)) == NULL)
321 {
322 free (str);
323 return 0; /* Swallow the error. */
324 }
325
326 str = str_append (str, typestr);
327 free (typestr);
328
329 ctf_dump_append (state, str);
330 return 0;
331 }
332
333 /* Dump all the object entries into the cds_items. (There is no iterator for
334 this section, so we just do it in a loop, and this function handles all of
335 them, rather than only one. */
336
337 static int
338 ctf_dump_objts (ctf_file_t *fp, ctf_dump_state_t *state)
339 {
340 size_t i;
341
342 for (i = 0; i < fp->ctf_nsyms; i++)
343 {
344 char *str;
345 char *typestr;
346 const char *sym_name;
347 ctf_id_t type;
348
349 if ((type = ctf_lookup_by_symbol (state->cds_fp, i)) == CTF_ERR)
350 switch (ctf_errno (state->cds_fp))
351 {
352 /* Most errors are just an indication that this symbol is not a data
353 symbol, but this one indicates that we were called wrong, on a
354 CTF file with no associated symbol table. */
355 case ECTF_NOSYMTAB:
356 return -1;
357 case ECTF_NOTDATA:
358 case ECTF_NOTYPEDAT:
359 continue;
360 }
361
362 /* Variable name. */
363 sym_name = ctf_lookup_symbol_name (fp, i);
364 if (sym_name[0] == '\0')
365 {
366 if (asprintf (&str, "%lx -> ", (unsigned long) i) < 0)
367 return (ctf_set_errno (fp, errno));
368 }
369 else
370 {
371 if (asprintf (&str, "%s (%lx) -> ", sym_name, (unsigned long) i) < 0)
372 return (ctf_set_errno (fp, errno));
373 }
374
375 /* Variable type. */
376 if ((typestr = ctf_dump_format_type (state->cds_fp, type,
377 CTF_ADD_ROOT)) == NULL)
378 {
379 free (str);
380 return 0; /* Swallow the error. */
381 }
382
383 str = str_append (str, typestr);
384 free (typestr);
385
386 ctf_dump_append (state, str);
387 }
388 return 0;
389 }
390
391 /* Dump all the function entries into the cds_items. (As above, there is no
392 iterator for this section.) */
393
394 static int
395 ctf_dump_funcs (ctf_file_t *fp, ctf_dump_state_t *state)
396 {
397 size_t i;
398
399 for (i = 0; i < fp->ctf_nsyms; i++)
400 {
401 char *str;
402 char *bit = NULL;
403 const char *err;
404 const char *sym_name;
405 ctf_funcinfo_t fi;
406 ctf_id_t type;
407
408 if ((type = ctf_func_info (state->cds_fp, i, &fi)) == CTF_ERR)
409 switch (ctf_errno (state->cds_fp))
410 {
411 /* Most errors are just an indication that this symbol is not a data
412 symbol, but this one indicates that we were called wrong, on a
413 CTF file with no associated symbol table. */
414 case ECTF_NOSYMTAB:
415 return -1;
416 case ECTF_NOTDATA:
417 case ECTF_NOTFUNC:
418 case ECTF_NOFUNCDAT:
419 continue;
420 }
421
422 /* Return type and all args. */
423 if ((bit = ctf_type_aname (state->cds_fp, type)) == NULL)
424 {
425 err = "look up return type";
426 goto err;
427 }
428
429 /* Replace in the returned string, dropping in the function name. */
430
431 sym_name = ctf_lookup_symbol_name (fp, i);
432 if (sym_name[0] != '\0')
433 {
434 char *retstar;
435 char *new_bit;
436 char *walk;
437
438 new_bit = malloc (strlen (bit) + 1 + strlen (sym_name));
439 if (!new_bit)
440 goto oom;
441
442 /* See ctf_type_aname. */
443 retstar = strstr (bit, "(*) (");
444 if (!ctf_assert (fp, retstar))
445 goto assert_err;
446 retstar += 2; /* After the '*' */
447
448 /* C is not good at search-and-replace. */
449 walk = new_bit;
450 memcpy (walk, bit, retstar - bit);
451 walk += (retstar - bit);
452 strcpy (walk, sym_name);
453 walk += strlen (sym_name);
454 strcpy (walk, retstar);
455
456 free (bit);
457 bit = new_bit;
458 }
459
460 if (asprintf (&str, "Symbol 0x%lx: %s", (unsigned long) i, bit) < 0)
461 goto oom;
462 free (bit);
463
464 ctf_dump_append (state, str);
465 continue;
466
467 err:
468 ctf_err_warn (fp, 1, "Cannot %s dumping function type for "
469 "symbol 0x%li: %s", err, (unsigned long) i,
470 ctf_errmsg (ctf_errno (state->cds_fp)));
471 free (bit);
472 return -1; /* errno is set for us. */
473
474 oom:
475 free (bit);
476 return (ctf_set_errno (fp, errno));
477
478 assert_err:
479 free (bit);
480 return -1; /* errno is set for us. */
481 }
482 return 0;
483 }
484
485 /* Dump a single variable into the cds_items. */
486 static int
487 ctf_dump_var (const char *name, ctf_id_t type, void *arg)
488 {
489 char *str;
490 char *typestr;
491 ctf_dump_state_t *state = arg;
492
493 if (asprintf (&str, "%s -> ", name) < 0)
494 return (ctf_set_errno (state->cds_fp, errno));
495
496 if ((typestr = ctf_dump_format_type (state->cds_fp, type,
497 CTF_ADD_ROOT)) == NULL)
498 {
499 free (str);
500 return 0; /* Swallow the error. */
501 }
502
503 str = str_append (str, typestr);
504 free (typestr);
505
506 ctf_dump_append (state, str);
507 return 0;
508 }
509
510 /* Dump a single member into the string in the membstate. */
511 static int
512 ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
513 int depth, void *arg)
514 {
515 ctf_dump_membstate_t *state = arg;
516 char *typestr = NULL;
517 char *bit = NULL;
518 ctf_encoding_t ep;
519 ssize_t i;
520
521 for (i = 0; i < depth; i++)
522 *state->cdm_str = str_append (*state->cdm_str, " ");
523
524 if ((typestr = ctf_type_aname (state->cdm_fp, id)) == NULL)
525 {
526 if (id == 0 || ctf_errno (state->cdm_fp) == ECTF_NONREPRESENTABLE)
527 {
528 if (asprintf (&bit, " [0x%lx] (type not represented in CTF)",
529 offset) < 0)
530 goto oom;
531
532 *state->cdm_str = str_append (*state->cdm_str, bit);
533 free (typestr);
534 free (bit);
535 return 0;
536 }
537
538 goto oom;
539 }
540
541 if (asprintf (&bit, " [0x%lx] (ID 0x%lx) (kind %i) %s %s (aligned at 0x%lx",
542 offset, id, ctf_type_kind (state->cdm_fp, id), typestr, name,
543 (unsigned long) ctf_type_align (state->cdm_fp, id)) < 0)
544 goto oom;
545 *state->cdm_str = str_append (*state->cdm_str, bit);
546 free (typestr);
547 free (bit);
548 typestr = NULL;
549 bit = NULL;
550
551 if ((ctf_type_kind (state->cdm_fp, id) == CTF_K_INTEGER)
552 || (ctf_type_kind (state->cdm_fp, id) == CTF_K_FLOAT)
553 || (ctf_is_slice (state->cdm_fp, id, &ep) == CTF_K_ENUM))
554 {
555 ctf_type_encoding (state->cdm_fp, id, &ep);
556 if (asprintf (&bit, ", format 0x%x, offset:bits 0x%x:0x%x", ep.cte_format,
557 ep.cte_offset, ep.cte_bits) < 0)
558 goto oom;
559 *state->cdm_str = str_append (*state->cdm_str, bit);
560 free (bit);
561 bit = NULL;
562 }
563
564 *state->cdm_str = str_append (*state->cdm_str, ")\n");
565 return 0;
566
567 oom:
568 free (typestr);
569 free (bit);
570 return (ctf_set_errno (state->cdm_fp, errno));
571 }
572
573 /* Dump a single type into the cds_items. */
574 static int
575 ctf_dump_type (ctf_id_t id, int flag, void *arg)
576 {
577 char *str;
578 const char *err;
579 ctf_dump_state_t *state = arg;
580 ctf_dump_membstate_t membstate = { &str, state->cds_fp };
581 size_t len;
582
583 if ((str = ctf_dump_format_type (state->cds_fp, id, flag)) == NULL)
584 goto err_nomsg; /* Error already logged for us. */
585
586 str = str_append (str, "\n");
587 if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
588 {
589 if (id == 0 || ctf_errno (state->cds_fp) == ECTF_NONREPRESENTABLE)
590 {
591 ctf_dump_append (state, str);
592 return 0;
593 }
594 err = "visit members";
595 goto err;
596 }
597
598 /* Trim off the last linefeed added by ctf_dump_member(). */
599 len = strlen (str);
600 if (str[len-1] == '\n')
601 str[len-1] = '\0';
602
603 ctf_dump_append (state, str);
604 return 0;
605
606 err:
607 ctf_err_warn (state->cds_fp, 1, "Cannot %s dumping type 0x%lx: %s",
608 err, id, ctf_errmsg (ctf_errno (state->cds_fp)));
609 err_nomsg:
610 free (str);
611 return 0; /* Swallow the error. */
612 }
613
614 /* Dump the string table into the cds_items. */
615
616 static int
617 ctf_dump_str (ctf_file_t *fp, ctf_dump_state_t *state)
618 {
619 const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs;
620
621 for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs +
622 fp->ctf_str[CTF_STRTAB_0].cts_len;)
623 {
624 char *str;
625 if (asprintf (&str, "%lx: %s",
626 (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
627 s) < 0)
628 return (ctf_set_errno (fp, errno));
629 ctf_dump_append (state, str);
630 s += strlen (s) + 1;
631 }
632
633 return 0;
634 }
635
636 /* Dump a particular section of a CTF file, in textual form. Call with a
637 pointer to a NULL STATE: each call emits a dynamically allocated string
638 containing a description of one entity in the specified section, in order.
639 Only the first call (with a NULL state) may vary SECT. Once the CTF section
640 has been entirely dumped, the call returns NULL and frees and annuls the
641 STATE, ready for another section to be dumped. The returned textual content
642 may span multiple lines: between each call the FUNC is called with one
643 textual line at a time, and should return a suitably decorated line (it can
644 allocate a new one and return it if it likes). */
645
646 char *
647 ctf_dump (ctf_file_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
648 ctf_dump_decorate_f *func, void *arg)
649 {
650 char *str;
651 char *line;
652 ctf_dump_state_t *state = NULL;
653
654 if (*statep == NULL)
655 {
656 /* Data collection. Transforming a call-at-a-time iterator into a
657 return-at-a-time iterator in a language without call/cc is annoying. It
658 is easiest to simply collect everything at once and then return it bit
659 by bit. The first call will take (much) longer than otherwise, but the
660 amortized time needed is the same. */
661
662 if ((*statep = malloc (sizeof (struct ctf_dump_state))) == NULL)
663 {
664 ctf_set_errno (fp, ENOMEM);
665 goto end;
666 }
667 state = *statep;
668
669 memset (state, 0, sizeof (struct ctf_dump_state));
670 state->cds_fp = fp;
671 state->cds_sect = sect;
672
673 switch (sect)
674 {
675 case CTF_SECT_HEADER:
676 ctf_dump_header (fp, state);
677 break;
678 case CTF_SECT_LABEL:
679 if (ctf_label_iter (fp, ctf_dump_label, state) < 0)
680 {
681 if (ctf_errno (fp) != ECTF_NOLABELDATA)
682 goto end; /* errno is set for us. */
683 ctf_set_errno (fp, 0);
684 }
685 break;
686 case CTF_SECT_OBJT:
687 if (ctf_dump_objts (fp, state) < 0)
688 goto end; /* errno is set for us. */
689 break;
690 case CTF_SECT_FUNC:
691 if (ctf_dump_funcs (fp, state) < 0)
692 goto end; /* errno is set for us. */
693 break;
694 case CTF_SECT_VAR:
695 if (ctf_variable_iter (fp, ctf_dump_var, state) < 0)
696 goto end; /* errno is set for us. */
697 break;
698 case CTF_SECT_TYPE:
699 if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0)
700 goto end; /* errno is set for us. */
701 break;
702 case CTF_SECT_STR:
703 ctf_dump_str (fp, state);
704 break;
705 default:
706 ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN);
707 goto end;
708 }
709 }
710 else
711 {
712 state = *statep;
713
714 if (state->cds_sect != sect)
715 {
716 ctf_set_errno (fp, ECTF_DUMPSECTCHANGED);
717 goto end;
718 }
719 }
720
721 if (state->cds_current == NULL)
722 state->cds_current = ctf_list_next (&state->cds_items);
723 else
724 state->cds_current = ctf_list_next (state->cds_current);
725
726 if (state->cds_current == NULL)
727 goto end;
728
729 /* Hookery. There is some extra complexity to preserve linefeeds within each
730 item while removing linefeeds at the end. */
731 if (func)
732 {
733 size_t len;
734
735 str = NULL;
736 for (line = state->cds_current->cdi_item; line && *line; )
737 {
738 char *nline = line;
739 char *ret;
740
741 nline = strchr (line, '\n');
742 if (nline)
743 nline[0] = '\0';
744
745 ret = func (sect, line, arg);
746 str = str_append (str, ret);
747 str = str_append (str, "\n");
748 if (ret != line)
749 free (ret);
750
751 if (nline)
752 {
753 nline[0] = '\n';
754 nline++;
755 }
756
757 line = nline;
758 }
759
760 len = strlen (str);
761
762 if (str[len-1] == '\n')
763 str[len-1] = '\0';
764 }
765 else
766 {
767 str = strdup (state->cds_current->cdi_item);
768 if (!str)
769 {
770 ctf_set_errno (fp, ENOMEM);
771 return str;
772 }
773 }
774
775 ctf_set_errno (fp, 0);
776 return str;
777
778 end:
779 ctf_dump_free (state);
780 free (state);
781 ctf_set_errno (fp, 0);
782 *statep = NULL;
783 return NULL;
784 }
This page took 0.048414 seconds and 5 git commands to generate.