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