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