diff options
Diffstat (limited to 'libctf/ctf-archive.c')
-rw-r--r-- | libctf/ctf-archive.c | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/libctf/ctf-archive.c b/libctf/ctf-archive.c index 42e9024..54d3d0a 100644 --- a/libctf/ctf-archive.c +++ b/libctf/ctf-archive.c @@ -732,6 +732,113 @@ ctf_archive_iter (const ctf_archive_t *arc, ctf_archive_member_f *func, return func (arc->ctfi_file, _CTF_SECTION, data); } +/* Iterate over all CTF files in an archive, returning each dict in turn as a + ctf_file_t, and NULL on error or end of iteration. It is the caller's + responsibility to close it. Parent dicts may be skipped. Regardless of + whether they are skipped or not, the caller must ctf_import the parent if + need be. + + We identify parents by name rather than by flag value: for now, with the + linker only emitting parents named _CTF_SECTION, this works well enough. */ + +ctf_file_t * +ctf_archive_next (const ctf_archive_t *wrapper, ctf_next_t **it, const char **name, + int skip_parent, int *errp) +{ + ctf_file_t *f; + ctf_next_t *i = *it; + struct ctf_archive *arc; + struct ctf_archive_modent *modent; + const char *nametbl; + const char *name_; + + if (!i) + { + if ((i = ctf_next_create()) == NULL) + { + if (errp) + *errp = ENOMEM; + return NULL; + } + i->cu.ctn_arc = wrapper; + i->ctn_iter_fun = (void (*) (void)) ctf_archive_next; + *it = i; + } + + if ((void (*) (void)) ctf_archive_next != i->ctn_iter_fun) + { + if (errp) + *errp = ECTF_NEXT_WRONGFUN; + return NULL; + } + + if (wrapper != i->cu.ctn_arc) + { + if (errp) + *errp = ECTF_NEXT_WRONGFP; + return NULL; + } + + /* Iteration is made a bit more complex by the need to handle ctf_file_t's + transparently wrapped in a single-member archive. These are parents: if + skip_parent is on, they are skipped and the iterator terminates + immediately. */ + + if (!wrapper->ctfi_is_archive && i->ctn_n == 0) + { + i->ctn_n++; + if (!skip_parent) + { + wrapper->ctfi_file->ctf_refcnt++; + return wrapper->ctfi_file; + } + } + + arc = wrapper->ctfi_archive; + + /* The loop keeps going when skip_parent is on as long as the member we find + is the parent (i.e. at most two iterations, but possibly an early return if + *all* we have is a parent). */ + + const ctf_sect_t *symsect; + const ctf_sect_t *strsect; + + do + { + if ((!wrapper->ctfi_is_archive) || (i->ctn_n >= le64toh (arc->ctfa_nfiles))) + { + ctf_next_destroy (i); + *it = NULL; + if (errp) + *errp = ECTF_NEXT_END; + return NULL; + } + + symsect = &wrapper->ctfi_symsect; + strsect = &wrapper->ctfi_strsect; + + if (symsect->cts_name == NULL) + symsect = NULL; + if (strsect->cts_name == NULL) + strsect = NULL; + + modent = (ctf_archive_modent_t *) ((char *) arc + + sizeof (struct ctf_archive)); + nametbl = (((const char *) arc) + le64toh (arc->ctfa_names)); + + name_ = &nametbl[le64toh (modent[i->ctn_n].name_offset)]; + i->ctn_n++; + } while (skip_parent && strcmp (name_, _CTF_SECTION) == 0); + + if (name) + *name = name_; + + f = ctf_arc_open_by_name_internal (arc, symsect, strsect, + name_, errp); + f->ctf_archive = (ctf_archive_t *) wrapper; + return f; +} + #ifdef HAVE_MMAP /* Map the header in. Only used on new, empty files. */ static void *arc_mmap_header (int fd, size_t headersz) |