123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830 |
- /* Textual dumping of CTF data.
- Copyright (C) 2019-2022 Free Software Foundation, Inc.
- This file is part of libctf.
- libctf is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 3, or (at your option) any later
- version.
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not see
- <http://www.gnu.org/licenses/>. */
- #include <ctf-impl.h>
- #include <string.h>
- #define str_append(s, a) ctf_str_append_noerr (s, a)
- /* One item to be dumped, in string form. */
- typedef struct ctf_dump_item
- {
- ctf_list_t cdi_list;
- char *cdi_item;
- } ctf_dump_item_t;
- /* Cross-call state for dumping. Basically just enough to track the section in
- use and a list of return strings. */
- struct ctf_dump_state
- {
- ctf_sect_names_t cds_sect;
- ctf_dict_t *cds_fp;
- ctf_dump_item_t *cds_current;
- ctf_list_t cds_items;
- };
- /* Cross-call state for ctf_dump_member. */
- typedef struct ctf_dump_membstate
- {
- char **cdm_str;
- ctf_dict_t *cdm_fp;
- const char *cdm_toplevel_indent;
- } ctf_dump_membstate_t;
- static int
- ctf_dump_append (ctf_dump_state_t *state, char *str)
- {
- ctf_dump_item_t *cdi;
- if ((cdi = malloc (sizeof (struct ctf_dump_item))) == NULL)
- return (ctf_set_errno (state->cds_fp, ENOMEM));
- cdi->cdi_item = str;
- ctf_list_append (&state->cds_items, cdi);
- return 0;
- }
- static void
- ctf_dump_free (ctf_dump_state_t *state)
- {
- ctf_dump_item_t *cdi, *next_cdi;
- if (state == NULL)
- return;
- for (cdi = ctf_list_next (&state->cds_items); cdi != NULL;
- cdi = next_cdi)
- {
- free (cdi->cdi_item);
- next_cdi = ctf_list_next (cdi);
- free (cdi);
- }
- }
- /* Return a dump for a single type, without member info: but do optionally show
- the type's references. */
- #define CTF_FT_REFS 0x2 /* Print referenced types. */
- #define CTF_FT_BITFIELD 0x4 /* Print :BITS if a bitfield. */
- #define CTF_FT_ID 0x8 /* Print "ID: " in front of type IDs. */
- static char *
- ctf_dump_format_type (ctf_dict_t *fp, ctf_id_t id, int flag)
- {
- ctf_id_t new_id;
- char *str = NULL, *bit = NULL, *buf = NULL;
- ctf_set_errno (fp, 0);
- new_id = id;
- do
- {
- ctf_encoding_t ep;
- ctf_arinfo_t ar;
- int kind, unsliced_kind;
- ssize_t size, align;
- const char *nonroot_leader = "";
- const char *nonroot_trailer = "";
- const char *idstr = "";
- id = new_id;
- if (flag == CTF_ADD_NONROOT)
- {
- nonroot_leader = "{";
- nonroot_trailer = "}";
- }
- buf = ctf_type_aname (fp, id);
- if (!buf)
- {
- if (id == 0 || ctf_errno (fp) == ECTF_NONREPRESENTABLE)
- {
- ctf_set_errno (fp, ECTF_NONREPRESENTABLE);
- str = str_append (str, " (type not represented in CTF)");
- return str;
- }
- goto err;
- }
- if (flag & CTF_FT_ID)
- idstr = "ID ";
- if (asprintf (&bit, "%s%s0x%lx: (kind %i) ", nonroot_leader, idstr,
- id, ctf_type_kind (fp, id)) < 0)
- goto oom;
- str = str_append (str, bit);
- free (bit);
- bit = NULL;
- if (buf[0] != '\0')
- str = str_append (str, buf);
- free (buf);
- buf = NULL;
- unsliced_kind = ctf_type_kind_unsliced (fp, id);
- kind = ctf_type_kind (fp, id);
- /* Report encodings of everything with an encoding other than enums:
- base-type enums cannot have a nonzero cte_offset or cte_bits value.
- (Slices of them can, but they are of kind CTF_K_SLICE.) */
- if (unsliced_kind != CTF_K_ENUM && ctf_type_encoding (fp, id, &ep) == 0)
- {
- if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
- && flag & CTF_FT_BITFIELD)
- {
- if (asprintf (&bit, ":%i", ep.cte_bits) < 0)
- goto oom;
- str = str_append (str, bit);
- free (bit);
- bit = NULL;
- }
- if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
- || ep.cte_offset != 0)
- {
- const char *slice = "";
- if (unsliced_kind == CTF_K_SLICE)
- slice = "slice ";
- if (asprintf (&bit, " [%s0x%x:0x%x]",
- slice, ep.cte_offset, ep.cte_bits) < 0)
- goto oom;
- str = str_append (str, bit);
- free (bit);
- bit = NULL;
- }
- if (asprintf (&bit, " (format 0x%x)", ep.cte_format) < 0)
- goto oom;
- str = str_append (str, bit);
- free (bit);
- bit = NULL;
- }
- size = ctf_type_size (fp, id);
- if (kind != CTF_K_FUNCTION && size >= 0)
- {
- if (asprintf (&bit, " (size 0x%lx)", (unsigned long int) size) < 0)
- goto oom;
- str = str_append (str, bit);
- free (bit);
- bit = NULL;
- }
- align = ctf_type_align (fp, id);
- if (align >= 0)
- {
- if (asprintf (&bit, " (aligned at 0x%lx)",
- (unsigned long int) align) < 0)
- goto oom;
- str = str_append (str, bit);
- free (bit);
- bit = NULL;
- }
- if (nonroot_trailer[0] != 0)
- str = str_append (str, nonroot_trailer);
- /* Just exit after one iteration if we are not showing the types this type
- references. */
- if (!(flag & CTF_FT_REFS))
- return str;
- /* Keep going as long as this type references another. We consider arrays
- to "reference" their element type. */
- if (kind == CTF_K_ARRAY)
- {
- if (ctf_array_info (fp, id, &ar) < 0)
- goto err;
- new_id = ar.ctr_contents;
- }
- else
- new_id = ctf_type_reference (fp, id);
- if (new_id != CTF_ERR)
- str = str_append (str, " -> ");
- }
- while (new_id != CTF_ERR);
- if (ctf_errno (fp) != ECTF_NOTREF)
- {
- free (str);
- return NULL;
- }
- return str;
- oom:
- ctf_set_errno (fp, errno);
- err:
- ctf_err_warn (fp, 1, 0, _("cannot format name dumping type 0x%lx"), id);
- free (buf);
- free (str);
- free (bit);
- return NULL;
- }
- /* Dump one string field from the file header into the cds_items. */
- static int
- ctf_dump_header_strfield (ctf_dict_t *fp, ctf_dump_state_t *state,
- const char *name, uint32_t value)
- {
- char *str;
- if (value)
- {
- if (asprintf (&str, "%s: %s\n", name, ctf_strptr (fp, value)) < 0)
- goto err;
- ctf_dump_append (state, str);
- }
- return 0;
- err:
- return (ctf_set_errno (fp, errno));
- }
- /* Dump one section-offset field from the file header into the cds_items. */
- static int
- ctf_dump_header_sectfield (ctf_dict_t *fp, ctf_dump_state_t *state,
- const char *sect, uint32_t off, uint32_t nextoff)
- {
- char *str;
- if (nextoff - off)
- {
- if (asprintf (&str, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect,
- (unsigned long) off, (unsigned long) (nextoff - 1),
- (unsigned long) (nextoff - off)) < 0)
- goto err;
- ctf_dump_append (state, str);
- }
- return 0;
- err:
- return (ctf_set_errno (fp, errno));
- }
- /* Dump the file header into the cds_items. */
- static int
- ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state)
- {
- char *str;
- char *flagstr = NULL;
- const ctf_header_t *hp = fp->ctf_header;
- const char *vertab[] =
- {
- NULL, "CTF_VERSION_1",
- "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type "
- "boundaries)",
- "CTF_VERSION_2",
- "CTF_VERSION_3", NULL
- };
- const char *verstr = NULL;
- if (asprintf (&str, "Magic number: 0x%x\n", hp->cth_magic) < 0)
- goto err;
- ctf_dump_append (state, str);
- if (hp->cth_version <= CTF_VERSION)
- verstr = vertab[hp->cth_version];
- if (verstr == NULL)
- verstr = "(not a valid version)";
- if (asprintf (&str, "Version: %i (%s)\n", hp->cth_version,
- verstr) < 0)
- goto err;
- ctf_dump_append (state, str);
- /* Everything else is only printed if present. */
- /* The flags are unusual in that they represent the ctf_dict_t *in memory*:
- flags representing compression, etc, are turned off as the file is
- decompressed. So we store a copy of the flags before they are changed, for
- the dumper. */
- if (fp->ctf_openflags > 0)
- {
- if (asprintf (&flagstr, "%s%s%s%s%s%s%s",
- fp->ctf_openflags & CTF_F_COMPRESS
- ? "CTF_F_COMPRESS": "",
- (fp->ctf_openflags & CTF_F_COMPRESS)
- && (fp->ctf_openflags & ~CTF_F_COMPRESS)
- ? ", " : "",
- fp->ctf_openflags & CTF_F_NEWFUNCINFO
- ? "CTF_F_NEWFUNCINFO" : "",
- (fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
- && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
- ? ", " : "",
- fp->ctf_openflags & CTF_F_IDXSORTED
- ? "CTF_F_IDXSORTED" : "",
- fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
- | CTF_F_IDXSORTED)
- && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
- | CTF_F_IDXSORTED))
- ? ", " : "",
- fp->ctf_openflags & CTF_F_DYNSTR
- ? "CTF_F_DYNSTR" : "") < 0)
- goto err;
- if (asprintf (&str, "Flags: 0x%x (%s)", fp->ctf_openflags, flagstr) < 0)
- goto err;
- ctf_dump_append (state, str);
- }
- if (ctf_dump_header_strfield (fp, state, "Parent label",
- hp->cth_parlabel) < 0)
- goto err;
- if (ctf_dump_header_strfield (fp, state, "Parent name", hp->cth_parname) < 0)
- goto err;
- if (ctf_dump_header_strfield (fp, state, "Compilation unit name",
- hp->cth_cuname) < 0)
- goto err;
- if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff,
- hp->cth_objtoff) < 0)
- goto err;
- if (ctf_dump_header_sectfield (fp, state, "Data object section",
- hp->cth_objtoff, hp->cth_funcoff) < 0)
- goto err;
- if (ctf_dump_header_sectfield (fp, state, "Function info section",
- hp->cth_funcoff, hp->cth_objtidxoff) < 0)
- goto err;
- if (ctf_dump_header_sectfield (fp, state, "Object index section",
- hp->cth_objtidxoff, hp->cth_funcidxoff) < 0)
- goto err;
- if (ctf_dump_header_sectfield (fp, state, "Function index section",
- hp->cth_funcidxoff, hp->cth_varoff) < 0)
- goto err;
- if (ctf_dump_header_sectfield (fp, state, "Variable section",
- hp->cth_varoff, hp->cth_typeoff) < 0)
- goto err;
- if (ctf_dump_header_sectfield (fp, state, "Type section",
- hp->cth_typeoff, hp->cth_stroff) < 0)
- goto err;
- if (ctf_dump_header_sectfield (fp, state, "String section", hp->cth_stroff,
- hp->cth_stroff + hp->cth_strlen + 1) < 0)
- goto err;
- return 0;
- err:
- free (flagstr);
- return (ctf_set_errno (fp, errno));
- }
- /* Dump a single label into the cds_items. */
- static int
- ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
- void *arg)
- {
- char *str;
- char *typestr;
- ctf_dump_state_t *state = arg;
- if (asprintf (&str, "%s -> ", name) < 0)
- return (ctf_set_errno (state->cds_fp, errno));
- if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type,
- CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
- {
- free (str);
- return 0; /* Swallow the error. */
- }
- str = str_append (str, typestr);
- free (typestr);
- ctf_dump_append (state, str);
- return 0;
- }
- /* Dump all the object or function entries into the cds_items. */
- static int
- ctf_dump_objts (ctf_dict_t *fp, ctf_dump_state_t *state, int functions)
- {
- const char *name;
- ctf_id_t id;
- ctf_next_t *i = NULL;
- char *str = NULL;
- if ((functions && fp->ctf_funcidx_names)
- || (!functions && fp->ctf_objtidx_names))
- str = str_append (str, _("Section is indexed.\n"));
- else if (fp->ctf_symtab.cts_data == NULL)
- str = str_append (str, _("No symbol table.\n"));
- while ((id = ctf_symbol_next (fp, &i, &name, functions)) != CTF_ERR)
- {
- char *typestr = NULL;
- /* Emit the name, if we know it. No trailing space: ctf_dump_format_type
- has a leading one. */
- if (name)
- {
- if (asprintf (&str, "%s -> ", name) < 0)
- goto oom;
- }
- else
- str = xstrdup ("");
- if ((typestr = ctf_dump_format_type (state->cds_fp, id,
- CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
- {
- ctf_dump_append (state, str);
- continue; /* Swallow the error. */
- }
- str = str_append (str, typestr);
- free (typestr);
- ctf_dump_append (state, str);
- continue;
- oom:
- ctf_set_errno (fp, ENOMEM);
- ctf_next_destroy (i);
- return -1;
- }
- return 0;
- }
- /* Dump a single variable into the cds_items. */
- static int
- ctf_dump_var (const char *name, ctf_id_t type, void *arg)
- {
- char *str;
- char *typestr;
- ctf_dump_state_t *state = arg;
- if (asprintf (&str, "%s -> ", name) < 0)
- return (ctf_set_errno (state->cds_fp, errno));
- if ((typestr = ctf_dump_format_type (state->cds_fp, type,
- CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
- {
- free (str);
- return 0; /* Swallow the error. */
- }
- str = str_append (str, typestr);
- free (typestr);
- ctf_dump_append (state, str);
- return 0;
- }
- /* Dump a single struct/union member into the string in the membstate. */
- static int
- ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
- int depth, void *arg)
- {
- ctf_dump_membstate_t *state = arg;
- char *typestr = NULL;
- char *bit = NULL;
- /* The struct/union itself has already been printed. */
- if (depth == 0)
- return 0;
- if (asprintf (&bit, "%s%*s", state->cdm_toplevel_indent, (depth-1)*4, "") < 0)
- goto oom;
- *state->cdm_str = str_append (*state->cdm_str, bit);
- free (bit);
- if ((typestr = ctf_dump_format_type (state->cdm_fp, id,
- CTF_ADD_ROOT | CTF_FT_BITFIELD
- | CTF_FT_ID)) == NULL)
- return -1; /* errno is set for us. */
- if (asprintf (&bit, "[0x%lx] %s: %s\n", offset, name, typestr) < 0)
- goto oom;
- *state->cdm_str = str_append (*state->cdm_str, bit);
- free (typestr);
- free (bit);
- typestr = NULL;
- bit = NULL;
- return 0;
- oom:
- free (typestr);
- free (bit);
- return (ctf_set_errno (state->cdm_fp, errno));
- }
- /* Report the number of digits in the hexadecimal representation of a type
- ID. */
- static int
- type_hex_digits (ctf_id_t id)
- {
- int i = 0;
- if (id == 0)
- return 1;
- for (; id > 0; id >>= 4, i++);
- return i;
- }
- /* Dump a single type into the cds_items. */
- static int
- ctf_dump_type (ctf_id_t id, int flag, void *arg)
- {
- char *str;
- char *indent;
- ctf_dump_state_t *state = arg;
- ctf_dump_membstate_t membstate = { &str, state->cds_fp, NULL };
- /* Indent neatly. */
- if (asprintf (&indent, " %*s", type_hex_digits (id), "") < 0)
- return (ctf_set_errno (state->cds_fp, ENOMEM));
- /* Dump the type itself. */
- if ((str = ctf_dump_format_type (state->cds_fp, id,
- flag | CTF_FT_REFS)) == NULL)
- goto err;
- str = str_append (str, "\n");
- membstate.cdm_toplevel_indent = indent;
- /* Member dumping for structs, unions... */
- if (ctf_type_kind (state->cds_fp, id) == CTF_K_STRUCT
- || ctf_type_kind (state->cds_fp, id) == CTF_K_UNION)
- {
- if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
- {
- if (id == 0 || ctf_errno (state->cds_fp) == ECTF_NONREPRESENTABLE)
- {
- ctf_dump_append (state, str);
- return 0;
- }
- ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
- _("cannot visit members dumping type 0x%lx"), id);
- goto err;
- }
- }
- /* ... and enums, for which we dump the first and last few members and skip
- the ones in the middle. */
- if (ctf_type_kind (state->cds_fp, id) == CTF_K_ENUM)
- {
- int enum_count = ctf_member_count (state->cds_fp, id);
- ctf_next_t *it = NULL;
- int i = 0;
- const char *enumerand;
- char *bit;
- int value;
- while ((enumerand = ctf_enum_next (state->cds_fp, id,
- &it, &value)) != NULL)
- {
- i++;
- if ((i > 5) && (i < enum_count - 4))
- continue;
- str = str_append (str, indent);
- if (asprintf (&bit, "%s: %i\n", enumerand, value) < 0)
- {
- ctf_next_destroy (it);
- goto oom;
- }
- str = str_append (str, bit);
- free (bit);
- if ((i == 5) && (enum_count > 10))
- {
- str = str_append (str, indent);
- str = str_append (str, "...\n");
- }
- }
- if (ctf_errno (state->cds_fp) != ECTF_NEXT_END)
- {
- ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
- _("cannot visit enumerands dumping type 0x%lx"), id);
- goto err;
- }
- }
- ctf_dump_append (state, str);
- free (indent);
- return 0;
- err:
- free (indent);
- free (str);
- /* Swallow the error: don't cause an error in one type to abort all
- type dumping. */
- return 0;
- oom:
- free (indent);
- free (str);
- return ctf_set_errno (state->cds_fp, ENOMEM);
- }
- /* Dump the string table into the cds_items. */
- static int
- ctf_dump_str (ctf_dict_t *fp, ctf_dump_state_t *state)
- {
- const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs;
- for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs +
- fp->ctf_str[CTF_STRTAB_0].cts_len;)
- {
- char *str;
- if (asprintf (&str, "0x%lx: %s",
- (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
- s) < 0)
- return (ctf_set_errno (fp, errno));
- ctf_dump_append (state, str);
- s += strlen (s) + 1;
- }
- return 0;
- }
- /* Dump a particular section of a CTF file, in textual form. Call with a
- pointer to a NULL STATE: each call emits a dynamically allocated string
- containing a description of one entity in the specified section, in order.
- Only the first call (with a NULL state) may vary SECT. Once the CTF section
- has been entirely dumped, the call returns NULL and frees and annuls the
- STATE, ready for another section to be dumped. The returned textual content
- may span multiple lines: between each call the FUNC is called with one
- textual line at a time, and should return a suitably decorated line (it can
- allocate a new one and return it if it likes). */
- char *
- ctf_dump (ctf_dict_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
- ctf_dump_decorate_f *func, void *arg)
- {
- char *str;
- char *line;
- ctf_dump_state_t *state = NULL;
- if (*statep == NULL)
- {
- /* Data collection. Transforming a call-at-a-time iterator into a
- return-at-a-time iterator in a language without call/cc is annoying. It
- is easiest to simply collect everything at once and then return it bit
- by bit. The first call will take (much) longer than otherwise, but the
- amortized time needed is the same. */
- if ((*statep = malloc (sizeof (struct ctf_dump_state))) == NULL)
- {
- ctf_set_errno (fp, ENOMEM);
- goto end;
- }
- state = *statep;
- memset (state, 0, sizeof (struct ctf_dump_state));
- state->cds_fp = fp;
- state->cds_sect = sect;
- switch (sect)
- {
- case CTF_SECT_HEADER:
- ctf_dump_header (fp, state);
- break;
- case CTF_SECT_LABEL:
- if (ctf_label_iter (fp, ctf_dump_label, state) < 0)
- {
- if (ctf_errno (fp) != ECTF_NOLABELDATA)
- goto end; /* errno is set for us. */
- ctf_set_errno (fp, 0);
- }
- break;
- case CTF_SECT_OBJT:
- if (ctf_dump_objts (fp, state, 0) < 0)
- goto end; /* errno is set for us. */
- break;
- case CTF_SECT_FUNC:
- if (ctf_dump_objts (fp, state, 1) < 0)
- goto end; /* errno is set for us. */
- break;
- case CTF_SECT_VAR:
- if (ctf_variable_iter (fp, ctf_dump_var, state) < 0)
- goto end; /* errno is set for us. */
- break;
- case CTF_SECT_TYPE:
- if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0)
- goto end; /* errno is set for us. */
- break;
- case CTF_SECT_STR:
- ctf_dump_str (fp, state);
- break;
- default:
- ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN);
- goto end;
- }
- }
- else
- {
- state = *statep;
- if (state->cds_sect != sect)
- {
- ctf_set_errno (fp, ECTF_DUMPSECTCHANGED);
- goto end;
- }
- }
- if (state->cds_current == NULL)
- state->cds_current = ctf_list_next (&state->cds_items);
- else
- state->cds_current = ctf_list_next (state->cds_current);
- if (state->cds_current == NULL)
- goto end;
- /* Hookery. There is some extra complexity to preserve linefeeds within each
- item while removing linefeeds at the end. */
- if (func)
- {
- size_t len;
- str = NULL;
- for (line = state->cds_current->cdi_item; line && *line; )
- {
- char *nline = line;
- char *ret;
- nline = strchr (line, '\n');
- if (nline)
- nline[0] = '\0';
- ret = func (sect, line, arg);
- str = str_append (str, ret);
- str = str_append (str, "\n");
- if (ret != line)
- free (ret);
- if (nline)
- {
- nline[0] = '\n';
- nline++;
- }
- line = nline;
- }
- len = strlen (str);
- if (str[len-1] == '\n')
- str[len-1] = '\0';
- }
- else
- {
- str = strdup (state->cds_current->cdi_item);
- if (!str)
- {
- ctf_set_errno (fp, ENOMEM);
- return str;
- }
- }
- ctf_set_errno (fp, 0);
- return str;
- end:
- ctf_dump_free (state);
- free (state);
- ctf_set_errno (fp, 0);
- *statep = NULL;
- return NULL;
- }
|