Add ctf-reader prototype
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Mon, 15 Aug 2016 01:09:18 +0000 (21:09 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Sat, 27 May 2017 18:08:47 +0000 (14:08 -0400)
Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
34 files changed:
Makefile.am
configure.ac
ctf-reader-proto/Makefile.am [new file with mode: 0644]
ctf-reader-proto/ctf-btr.gdb [new file with mode: 0644]
ctf-reader-proto/ctf-btr/Makefile.am [new file with mode: 0644]
ctf-reader-proto/ctf-btr/ctf-btr.c [new file with mode: 0644]
ctf-reader-proto/ctf-btr/ctf-btr.h [new file with mode: 0644]
ctf-reader-proto/ctf-btr/print.h [new file with mode: 0644]
ctf-reader-proto/ctf-fs-data-stream.c [new file with mode: 0644]
ctf-reader-proto/ctf-fs-data-stream.h [new file with mode: 0644]
ctf-reader-proto/ctf-fs-file.c [new file with mode: 0644]
ctf-reader-proto/ctf-fs-file.h [new file with mode: 0644]
ctf-reader-proto/ctf-fs-metadata.c [new file with mode: 0644]
ctf-reader-proto/ctf-fs-metadata.h [new file with mode: 0644]
ctf-reader-proto/ctf-fs.c [new file with mode: 0644]
ctf-reader-proto/ctf-fs.h [new file with mode: 0644]
ctf-reader-proto/ctf-notif-iter.gdb [new file with mode: 0644]
ctf-reader-proto/ctf-notif-iter/Makefile.am [new file with mode: 0644]
ctf-reader-proto/ctf-notif-iter/ctf-notif-iter.c [new file with mode: 0644]
ctf-reader-proto/ctf-notif-iter/ctf-notif-iter.h [new file with mode: 0644]
ctf-reader-proto/ctf-notif-iter/print.h [new file with mode: 0644]
ctf-reader-proto/metadata-parsing/Makefile.am [new file with mode: 0644]
ctf-reader-proto/metadata-parsing/ctf-ast.h [new file with mode: 0644]
ctf-reader-proto/metadata-parsing/ctf-lexer.l [new file with mode: 0644]
ctf-reader-proto/metadata-parsing/ctf-parser.y [new file with mode: 0644]
ctf-reader-proto/metadata-parsing/ctf-scanner-symbols.h [new file with mode: 0644]
ctf-reader-proto/metadata-parsing/ctf-scanner.h [new file with mode: 0644]
ctf-reader-proto/metadata-parsing/ctf-visitor-generate-ir.c [new file with mode: 0644]
ctf-reader-proto/metadata-parsing/ctf-visitor-parent-links.c [new file with mode: 0644]
ctf-reader-proto/metadata-parsing/ctf-visitor-semantic-validator.c [new file with mode: 0644]
ctf-reader-proto/metadata-parsing/objstack.c [new file with mode: 0644]
ctf-reader-proto/metadata-parsing/objstack.h [new file with mode: 0644]
ctf-reader-proto/print.h [new file with mode: 0644]
ctf-reader-proto/protorectoral.c [new file with mode: 0644]

index 0544bd1684aaee0d03688d435763fa9edee2e09e..fab48f1947132627e39ef7c00960db5923103ae3 100644 (file)
@@ -2,7 +2,7 @@ AM_CFLAGS = $(PACKAGE_CFLAGS) -I$(top_srcdir)/include
 
 ACLOCAL_AMFLAGS = -I m4
 
-SUBDIRS = include types compat lib formats plugins converter bindings tests doc extras
+SUBDIRS = include types compat lib formats plugins converter bindings tests doc extras ctf-reader-proto
 
 dist_doc_DATA = ChangeLog LICENSE mit-license.txt gpl-2.0.txt \
                std-ext-lib.txt README
index be939a2d4fc0989e2aac2f34ba677e5da0ecced3..c3809bee7e17336dde1e5ebb5e573737711777a1 100644 (file)
@@ -343,6 +343,10 @@ AC_CONFIG_FILES([
        Makefile
        types/Makefile
        compat/Makefile
+       ctf-reader-proto/Makefile
+       ctf-reader-proto/metadata-parsing/Makefile
+       ctf-reader-proto/ctf-notif-iter/Makefile
+       ctf-reader-proto/ctf-btr/Makefile
        formats/Makefile
        formats/ctf/Makefile
        formats/ctf/types/Makefile
diff --git a/ctf-reader-proto/Makefile.am b/ctf-reader-proto/Makefile.am
new file mode 100644 (file)
index 0000000..567114e
--- /dev/null
@@ -0,0 +1,20 @@
+SUBDIRS = metadata-parsing ctf-notif-iter ctf-btr
+
+AM_CFLAGS = $(PACKAGE_CFLAGS)
+AM_CPPFLAGS =  -I$(top_srcdir)/include
+
+noinst_PROGRAMS = protorectoral
+
+protorectoral_SOURCES = \
+               protorectoral.c \
+               ctf-fs.c \
+               ctf-fs-file.c \
+               ctf-fs-metadata.c \
+               ctf-fs-data-stream.c
+protorectoral_LDADD = \
+               metadata-parsing/libctf-parser.la \
+               metadata-parsing/libctf-ast.la \
+               ctf-notif-iter/libctf-notif-iter.la \
+               ctf-btr/libctf-btr.la \
+               $(top_builddir)/lib/libbabeltrace.la \
+               $(top_builddir)/formats/ctf/libbabeltrace-ctf.la
diff --git a/ctf-reader-proto/ctf-btr.gdb b/ctf-reader-proto/ctf-btr.gdb
new file mode 100644 (file)
index 0000000..9b42b1c
--- /dev/null
@@ -0,0 +1,24 @@
+define ctf-btr-show-stack
+    if (stack_empty($arg0))
+        printf "stack is empty!\n"
+    else
+        set $stack_size = stack_size($arg0)
+        set $stack_at = (int) ($stack_size - 1)
+        printf "%3s    %10s   %4s    %3s\n", "pos", "base addr", "blen", "idx"
+
+        while ($stack_at >= 0)
+            set $stack_entry = (struct stack_entry *) g_ptr_array_index($arg0->entries, $stack_at)
+
+            if ($stack_at == $stack_size - 1)
+                printf "%3d    %10p    %3d    %3d  <-- top\n", $stack_at, \
+                    $stack_entry->base_type, $stack_entry->base_len, \
+                    $stack_entry->index
+            else
+                printf "%3d    %10p    %3d    %3d\n", $stack_at, \
+                    $stack_entry->base_type, $stack_entry->base_len, \
+                    $stack_entry->index
+            end
+            set $stack_at = $stack_at - 1
+        end
+    end
+end
diff --git a/ctf-reader-proto/ctf-btr/Makefile.am b/ctf-reader-proto/ctf-btr/Makefile.am
new file mode 100644 (file)
index 0000000..93dd221
--- /dev/null
@@ -0,0 +1,6 @@
+AM_CFLAGS = $(PACKAGE_CFLAGS)
+AM_CPPFLAGS =  -I$(top_srcdir)/include
+
+noinst_LTLIBRARIES = libctf-btr.la
+
+libctf_btr_la_SOURCES = ctf-btr.c
diff --git a/ctf-reader-proto/ctf-btr/ctf-btr.c b/ctf-reader-proto/ctf-btr/ctf-btr.c
new file mode 100644 (file)
index 0000000..cb250f9
--- /dev/null
@@ -0,0 +1,1388 @@
+/*
+ * Babeltrace - CTF binary type reader (BTR)
+ *
+ * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation
+ * Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <string.h>
+#include <babeltrace/bitfield.h>
+#include <babeltrace/ctf-ir/field-types.h>
+#include <babeltrace/ref.h>
+#include <babeltrace/align.h>
+#include <glib.h>
+
+#include "ctf-btr.h"
+
+#define PRINT_ERR_STREAM       btr->err_stream
+#define PRINT_PREFIX           "ctf-btr"
+#include "print.h"
+
+#define DIV8(_x)                       ((_x) >> 3)
+#define BYTES_TO_BITS(_x)              ((_x) * 8)
+#define BITS_TO_BYTES_FLOOR(_x)                DIV8(_x)
+#define BITS_TO_BYTES_CEIL(_x)         DIV8((_x) + 7)
+#define IN_BYTE_OFFSET(_at)            ((_at) & 7)
+
+/* A visit stack entry */
+struct stack_entry {
+       /*
+        * Current type of base field, one of:
+        *
+        *   * Structure
+        *   * Array
+        *   * Sequence
+        *   * Variant
+        *
+        * Owned by this.
+        */
+       struct bt_ctf_field_type *base_type;
+
+       /* Length of base field (always 1 for variant types) */
+       int64_t base_len;
+
+       /* Lndex of next field to read */
+       int64_t index;
+};
+
+/* Visit stack */
+struct stack {
+       /* Entries (struct stack_entry *) (top is last element) */
+       GPtrArray *entries;
+};
+
+/* Reading states */
+enum btr_state {
+       BTR_STATE_NEXT_FIELD,
+       BTR_STATE_ALIGN_BASIC,
+       BTR_STATE_ALIGN_COMPOUND,
+       BTR_STATE_READ_BASIC_BEGIN,
+       BTR_STATE_READ_BASIC_CONTINUE,
+       BTR_STATE_DONE,
+};
+
+/* Binary type reader */
+struct bt_ctf_btr {
+       /* Bisit stack */
+       struct stack *stack;
+
+       /* Error stream */
+       FILE *err_stream;
+
+       /* Current basic field type */
+       struct bt_ctf_field_type *cur_basic_field_type;
+
+       /* Current state */
+       enum btr_state state;
+
+       /*
+        * Last basic field type's byte order.
+        *
+        * This is used to detect errors since two contiguous basic
+        * types for which the common boundary is not the boundary of
+        * a byte cannot have different byte orders.
+        *
+        * This is set to BT_CTF_BYTE_ORDER_UNKNOWN on reset and when
+        * the last basic field type was a string type.
+        */
+       enum bt_ctf_byte_order last_bo;
+
+       /* Current byte order (copied to last_bo after a successful read) */
+       enum bt_ctf_byte_order cur_bo;
+
+       /* Stitch buffer infos */
+       struct {
+               /* Stitch buffer */
+               uint8_t buf[16];
+
+               /* Offset, within stitch buffer, of first bit */
+               size_t offset;
+
+               /* Length (bits) of data in stitch buffer from offset */
+               size_t at;
+       } stitch;
+
+       /* User buffer infos */
+       struct {
+               /* Address */
+               const uint8_t *addr;
+
+               /* Offset of data from address (bits) */
+               size_t offset;
+
+               /* Current position from offset (bits) */
+               size_t at;
+
+               /* Offset of offset within whole packet (bits) */
+               size_t packet_offset;
+
+               /* Data size in buffer (bits) */
+               size_t sz;
+
+               /* Buffer size (bytes) */
+               size_t buf_sz;
+       } buf;
+
+       /* User stuff */
+       struct {
+               /* Callback functions */
+               struct bt_ctf_btr_cbs cbs;
+
+               /* Private data */
+               void *data;
+       } user;
+};
+
+static
+void stack_entry_free_func(gpointer data)
+{
+       struct stack_entry *entry = data;
+
+       BT_PUT(entry->base_type);
+       g_free(entry);
+}
+
+static
+struct stack *stack_new(void)
+{
+       struct stack *stack = NULL;
+
+       stack = g_new0(struct stack, 1);
+       if (!stack) {
+               goto error;
+       }
+
+       stack->entries = g_ptr_array_new_with_free_func(stack_entry_free_func);
+       if (!stack->entries) {
+               goto error;
+       }
+
+       return stack;
+
+error:
+       g_free(stack);
+
+       return NULL;
+}
+
+static
+void stack_destroy(struct stack *stack)
+{
+       if (!stack) {
+               return;
+       }
+
+       g_ptr_array_free(stack->entries, TRUE);
+       g_free(stack);
+}
+
+static inline
+int64_t get_compound_field_type_length(struct bt_ctf_btr *btr,
+       struct bt_ctf_field_type *field_type)
+{
+       int64_t length;
+
+       switch (bt_ctf_field_type_get_type_id(field_type)) {
+       case BT_CTF_TYPE_ID_STRUCT:
+               length = (int64_t) bt_ctf_field_type_structure_get_field_count(
+                       field_type);
+               break;
+       case BT_CTF_TYPE_ID_VARIANT:
+               /* Variant field types always "contain" a single type */
+               length = 1;
+               break;
+       case BT_CTF_TYPE_ID_ARRAY:
+               length = bt_ctf_field_type_array_get_length(field_type);
+               break;
+       case BT_CTF_TYPE_ID_SEQUENCE:
+               length = btr->user.cbs.query.get_sequence_length(field_type,
+                       btr->user.data);
+               break;
+       default:
+               PERR("Cannot get length of field type with type ID %d\n",
+                       bt_ctf_field_type_get_type_id(field_type));
+               length = BT_CTF_BTR_STATUS_ERROR;
+       }
+
+       return length;
+}
+
+static
+int stack_push(struct stack *stack, struct bt_ctf_field_type *base_type,
+       size_t base_len)
+{
+       int ret = 0;
+       struct stack_entry *entry;
+
+       assert(stack);
+       assert(base_type);
+
+       entry = g_new0(struct stack_entry, 1);
+       if (!entry) {
+               ret = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+       entry->base_type = base_type;
+       bt_get(entry->base_type);
+       entry->base_len = base_len;
+       g_ptr_array_add(stack->entries, entry);
+
+end:
+       return ret;
+}
+
+static
+int stack_push_with_len(struct bt_ctf_btr *btr,
+       struct bt_ctf_field_type *base_type)
+{
+       int ret = 0;
+       int64_t base_len = get_compound_field_type_length(btr, base_type);
+
+       if (base_len < 0) {
+               PERR("Failed to get compound field type's length\n");
+               ret = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+       ret = stack_push(btr->stack, base_type, (size_t) base_len);
+
+end:
+       return ret;
+}
+
+static inline
+unsigned int stack_size(struct stack *stack)
+{
+       assert(stack);
+
+       return stack->entries->len;
+}
+
+static
+void stack_pop(struct stack *stack)
+{
+       assert(stack);
+       assert(stack_size(stack));
+       g_ptr_array_remove_index(stack->entries, stack->entries->len - 1);
+}
+
+static inline
+bool stack_empty(struct stack *stack)
+{
+       return stack_size(stack) == 0;
+}
+
+static
+void stack_clear(struct stack *stack)
+{
+       assert(stack);
+
+       if (!stack_empty(stack)) {
+               g_ptr_array_remove_range(stack->entries, 0, stack_size(stack));
+       }
+
+       assert(stack_empty(stack));
+}
+
+static inline
+struct stack_entry *stack_top(struct stack *stack)
+{
+       assert(stack);
+       assert(stack_size(stack));
+
+       return g_ptr_array_index(stack->entries, stack->entries->len - 1);
+}
+
+static inline
+size_t available_bits(struct bt_ctf_btr *btr)
+{
+       return btr->buf.sz - btr->buf.at;
+}
+
+static inline
+void consume_bits(struct bt_ctf_btr *btr, size_t incr)
+{
+       btr->buf.at += incr;
+}
+
+static inline
+bool has_enough_bits(struct bt_ctf_btr *btr, size_t sz)
+{
+       return available_bits(btr) >= sz;
+}
+
+static inline
+bool at_least_one_bit_left(struct bt_ctf_btr *btr)
+{
+       return has_enough_bits(btr, 1);
+}
+
+static inline
+size_t packet_at(struct bt_ctf_btr *btr)
+{
+       return btr->buf.packet_offset + btr->buf.at;
+}
+
+static inline
+size_t buf_at_from_addr(struct bt_ctf_btr *btr)
+{
+       /*
+        * Considering this:
+        *
+        *     ====== offset ===== (17)
+        *
+        *     xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
+        *     ^
+        *     addr (0)           ==== at ==== (12)
+        *
+        * We want this:
+        *
+        *     =============================== (29)
+        */
+       return btr->buf.offset + btr->buf.at;
+}
+
+static inline
+int get_basic_field_type_size(struct bt_ctf_btr *btr,
+       struct bt_ctf_field_type *field_type)
+{
+       int size;
+
+       switch (bt_ctf_field_type_get_type_id(field_type)) {
+       case BT_CTF_TYPE_ID_INTEGER:
+               size = bt_ctf_field_type_integer_get_size(field_type);
+               break;
+       case BT_CTF_TYPE_ID_FLOAT:
+       {
+               int exp_dig, mant_dig;
+
+               exp_dig =
+                       bt_ctf_field_type_floating_point_get_exponent_digits(
+                               field_type);
+               mant_dig =
+                       bt_ctf_field_type_floating_point_get_mantissa_digits(
+                               field_type);
+               if (exp_dig < 0 || mant_dig < 0) {
+                       PERR("Failed to get floating point number type's sizes\n");
+                       size = BT_CTF_BTR_STATUS_ERROR;
+               }
+
+               size = exp_dig + mant_dig;
+               break;
+       }
+       case BT_CTF_TYPE_ID_ENUM:
+       {
+               struct bt_ctf_field_type *int_type;
+
+               int_type = bt_ctf_field_type_enumeration_get_container_type(
+                       field_type);
+               if (!int_type) {
+                       PERR("Failed to get enumeration type's container type\n");
+                       size = BT_CTF_BTR_STATUS_ERROR;
+                       goto end;
+               }
+
+               size = get_basic_field_type_size(btr, int_type);
+               BT_PUT(int_type);
+               break;
+       }
+       default:
+               size = BT_CTF_BTR_STATUS_ERROR;
+               break;
+       }
+
+end:
+       return size;
+}
+
+static
+void stitch_reset(struct bt_ctf_btr *btr)
+{
+       btr->stitch.offset = 0;
+       btr->stitch.at = 0;
+}
+
+static inline
+size_t stitch_at_from_addr(struct bt_ctf_btr *btr)
+{
+       return btr->stitch.offset + btr->stitch.at;
+}
+
+static
+void stitch_append_from_buf(struct bt_ctf_btr *btr, size_t sz)
+{
+       size_t stitch_byte_at;
+       size_t buf_byte_at;
+       size_t nb_bytes;;
+
+       if (sz == 0) {
+               return;
+       }
+
+       stitch_byte_at =
+               BITS_TO_BYTES_FLOOR(stitch_at_from_addr(btr));
+       buf_byte_at = BITS_TO_BYTES_FLOOR(buf_at_from_addr(btr));
+       nb_bytes = BITS_TO_BYTES_CEIL(sz);
+       assert(nb_bytes > 0);
+       memcpy(&btr->stitch.buf[stitch_byte_at], &btr->buf.addr[buf_byte_at],
+               nb_bytes);
+       btr->stitch.at += sz;
+       consume_bits(btr, sz);
+}
+
+static
+void stitch_append_from_remaining_buf(struct bt_ctf_btr *btr)
+{
+       stitch_append_from_buf(btr, available_bits(btr));
+}
+
+static
+void stitch_set_from_remaining_buf(struct bt_ctf_btr *btr)
+{
+       stitch_reset(btr);
+       btr->stitch.offset = IN_BYTE_OFFSET(buf_at_from_addr(btr));
+       stitch_append_from_remaining_buf(btr);
+}
+
+static inline
+enum bt_ctf_btr_status read_unsigned_bitfield(const uint8_t *buf, size_t at,
+       int64_t field_size, enum bt_ctf_byte_order bo, uint64_t *v)
+{
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+
+       switch (bo) {
+       case BT_CTF_BYTE_ORDER_BIG_ENDIAN:
+       case BT_CTF_BYTE_ORDER_NETWORK:
+               bt_bitfield_read_be(buf, uint8_t, at, field_size, v);
+               break;
+       case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN:
+               bt_bitfield_read_le(buf, uint8_t, at, field_size, v);
+               break;
+       default:
+               status = BT_CTF_BTR_STATUS_ERROR;
+       }
+
+       return status;
+}
+
+static inline
+enum bt_ctf_btr_status read_signed_bitfield(const uint8_t *buf, size_t at,
+       int64_t field_size, enum bt_ctf_byte_order bo, int64_t *v)
+{
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+
+       switch (bo) {
+       case BT_CTF_BYTE_ORDER_BIG_ENDIAN:
+       case BT_CTF_BYTE_ORDER_NETWORK:
+               bt_bitfield_read_be(buf, uint8_t, at, field_size, v);
+               break;
+       case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN:
+               bt_bitfield_read_le(buf, uint8_t, at, field_size, v);
+               break;
+       default:
+               status = BT_CTF_BTR_STATUS_ERROR;
+       }
+
+       return status;
+}
+
+typedef enum bt_ctf_btr_status (* read_basic_and_call_cb_t)(struct bt_ctf_btr *,
+       const uint8_t *, size_t);
+
+static inline
+enum bt_ctf_btr_status validate_contiguous_bo(struct bt_ctf_btr *btr,
+       enum bt_ctf_byte_order next_bo)
+{
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+
+       /* Always valid when at a byte boundary */
+       if (packet_at(btr) % 8 == 0) {
+               goto end;
+       }
+
+       /* Always valid if last byte order is unknown */
+       if (btr->last_bo == BT_CTF_BYTE_ORDER_UNKNOWN) {
+               goto end;
+       }
+
+       /* Always valid if next byte order is unknown */
+       if (next_bo == BT_CTF_BYTE_ORDER_UNKNOWN) {
+               goto end;
+       }
+
+       /* Make sure last byte order is compatible with the next byte order */
+       switch (btr->last_bo) {
+       case BT_CTF_BYTE_ORDER_BIG_ENDIAN:
+       case BT_CTF_BYTE_ORDER_NETWORK:
+               if (next_bo != BT_CTF_BYTE_ORDER_BIG_ENDIAN &&
+                               next_bo != BT_CTF_BYTE_ORDER_NETWORK) {
+                       status = BT_CTF_BTR_STATUS_ERROR;
+               }
+               break;
+       case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN:
+               if (next_bo != BT_CTF_BYTE_ORDER_LITTLE_ENDIAN) {
+                       status = BT_CTF_BTR_STATUS_ERROR;
+               }
+               break;
+       default:
+               status = BT_CTF_BTR_STATUS_ERROR;
+       }
+
+end:
+       return status;
+}
+
+static
+enum bt_ctf_btr_status read_basic_float_and_call_cb(struct bt_ctf_btr *btr,
+       const uint8_t *buf, size_t at)
+{
+       int ret;
+       double dblval;
+       int64_t field_size;
+       enum bt_ctf_byte_order bo;
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+
+       field_size = get_basic_field_type_size(btr, btr->cur_basic_field_type);
+       bo = bt_ctf_field_type_get_byte_order(btr->cur_basic_field_type);
+       btr->cur_bo = bo;
+
+       switch (field_size) {
+       case 32:
+       {
+               uint64_t v;
+               union {
+                       uint32_t u;
+                       float f;
+               } f32;
+
+               ret = bt_ctf_field_type_floating_point_get_mantissa_digits(
+                       btr->cur_basic_field_type);
+               assert(ret == 24);
+               ret = bt_ctf_field_type_floating_point_get_exponent_digits(
+                       btr->cur_basic_field_type);
+               assert(ret == 8);
+               status = read_unsigned_bitfield(buf, at, field_size, bo, &v);
+               if (status != BT_CTF_BTR_STATUS_OK) {
+                       PERR("Failed to reader unsigned bitfield\n");
+                       goto end;
+               }
+
+               f32.u = (uint32_t) v;
+               dblval = (double) f32.f;
+               break;
+       }
+       case 64:
+       {
+               union {
+                       uint64_t u;
+                       double d;
+               } f64;
+
+               ret = bt_ctf_field_type_floating_point_get_mantissa_digits(
+                       btr->cur_basic_field_type);
+               assert(ret == 53);
+               ret = bt_ctf_field_type_floating_point_get_exponent_digits(
+                       btr->cur_basic_field_type);
+               assert(ret == 11);
+               status = read_unsigned_bitfield(buf, at, field_size, bo,
+                       &f64.u);
+               if (status != BT_CTF_BTR_STATUS_OK) {
+                       PERR("Failed to reader unsigned bitfield\n");
+                       goto end;
+               }
+
+               dblval = f64.d;
+               break;
+       }
+       default:
+               /* Only 32-bit and 64-bit fields are supported currently */
+               status = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+       if (btr->user.cbs.types.floating_point) {
+               status = btr->user.cbs.types.floating_point(dblval,
+                       btr->cur_basic_field_type, btr->user.data);
+       }
+
+end:
+       return status;
+}
+
+static inline
+enum bt_ctf_btr_status read_basic_int_and_call(struct bt_ctf_btr *btr,
+       const uint8_t *buf, size_t at, struct bt_ctf_field_type *int_type,
+       struct bt_ctf_field_type *orig_type)
+{
+       int signd;
+       int64_t field_size;
+       enum bt_ctf_byte_order bo;
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+
+       signd = bt_ctf_field_type_integer_get_signed(int_type);
+       field_size = get_basic_field_type_size(btr, int_type);
+       if (field_size < 1) {
+               PERR("Failed to get basic field type's size\n");
+               status = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+       bo = bt_ctf_field_type_get_byte_order(int_type);
+
+       /*
+        * Update current byte order now because we could be reading
+        * the integer value of an enumeration type, and thus we know
+        * here the actual supporting integer type's byte order.
+        */
+       btr->cur_bo = bo;
+
+       if (signd) {
+               int64_t v;
+
+               status = read_signed_bitfield(buf, at, field_size, bo, &v);
+               if (status != BT_CTF_BTR_STATUS_OK) {
+                       PERR("Failed to reader signed bitfield\n");
+                       goto end;
+               }
+
+               if (btr->user.cbs.types.signed_int) {
+                       status = btr->user.cbs.types.signed_int(v,
+                               btr->cur_basic_field_type, btr->user.data);
+               }
+       } else {
+               uint64_t v;
+
+               status = read_unsigned_bitfield(buf, at, field_size, bo, &v);
+               if (status != BT_CTF_BTR_STATUS_OK) {
+                       PERR("Failed to reader unsigned bitfield\n");
+                       goto end;
+               }
+
+               if (btr->user.cbs.types.unsigned_int) {
+                       status = btr->user.cbs.types.unsigned_int(v,
+                               btr->cur_basic_field_type, btr->user.data);
+               }
+       }
+
+end:
+       return status;
+}
+
+static
+enum bt_ctf_btr_status read_basic_int_and_call_cb(struct bt_ctf_btr *btr,
+       const uint8_t *buf, size_t at)
+{
+       return read_basic_int_and_call(btr, buf, at, btr->cur_basic_field_type,
+               btr->cur_basic_field_type);
+}
+
+static
+enum bt_ctf_btr_status read_basic_enum_and_call_cb(struct bt_ctf_btr *btr,
+       const uint8_t *buf, size_t at)
+{
+       struct bt_ctf_field_type *int_field_type;
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+
+       int_field_type = bt_ctf_field_type_enumeration_get_container_type(
+               btr->cur_basic_field_type);
+       if (!int_field_type) {
+               PERR("Failed to get enumeration type's container type\n");
+               status = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+       status = read_basic_int_and_call(btr, buf, at,
+               int_field_type, btr->cur_basic_field_type);
+
+end:
+       BT_PUT(int_field_type);
+
+       return status;
+}
+
+static inline
+enum bt_ctf_btr_status read_basic_type_and_call_continue(struct bt_ctf_btr *btr,
+       read_basic_and_call_cb_t read_basic_and_call_cb)
+{
+       size_t available;
+       int64_t field_size;
+       int64_t needed_bits;
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+
+       if (!at_least_one_bit_left(btr)) {
+               status = BT_CTF_BTR_STATUS_EOF;
+               goto end;
+       }
+
+       field_size = get_basic_field_type_size(btr, btr->cur_basic_field_type);
+       if (field_size < 1) {
+               PERR("Failed to get basic field type's size\n");
+               status = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+       available = available_bits(btr);
+       needed_bits = field_size - btr->stitch.at;
+       if (needed_bits <= available) {
+               /* We have all the bits; append to stitch, then decode */
+               stitch_append_from_buf(btr, needed_bits);
+               status = read_basic_and_call_cb(btr, btr->stitch.buf,
+                       btr->stitch.offset);
+               if (status != BT_CTF_BTR_STATUS_OK) {
+                       PERR("Failed to read basic field\n");
+                       goto end;
+               }
+
+               if (stack_empty(btr->stack)) {
+                       /* Root is a basic type */
+                       btr->state = BTR_STATE_DONE;
+               } else {
+                       /* Go to next field */
+                       stack_top(btr->stack)->index++;
+                       btr->state = BTR_STATE_NEXT_FIELD;
+                       btr->last_bo = btr->cur_bo;
+               }
+               goto end;
+       }
+
+       /* We are here; it means we don't have enough data to decode this */
+       stitch_append_from_remaining_buf(btr);
+       status = BT_CTF_BTR_STATUS_EOF;
+
+end:
+       return status;
+}
+
+static inline
+enum bt_ctf_btr_status read_basic_type_and_call_begin(struct bt_ctf_btr *btr,
+       read_basic_and_call_cb_t read_basic_and_call_cb)
+{
+       size_t available;
+       int64_t field_size;
+       enum bt_ctf_byte_order bo;
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+
+       if (!at_least_one_bit_left(btr)) {
+               status = BT_CTF_BTR_STATUS_EOF;
+               goto end;
+       }
+
+       field_size = get_basic_field_type_size(btr, btr->cur_basic_field_type);
+
+       if (field_size < 1) {
+               PERR("Failed to get basic field type's size\n");
+               status = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+       bo = bt_ctf_field_type_get_byte_order(btr->cur_basic_field_type);
+       status = validate_contiguous_bo(btr, bo);
+
+       if (status != BT_CTF_BTR_STATUS_OK) {
+               PERR("Invalid contiguous byte orders\n");
+               goto end;
+       }
+
+       available = available_bits(btr);
+
+       if (field_size <= available) {
+               /* We have all the bits; decode and set now */
+               status = read_basic_and_call_cb(btr, btr->buf.addr,
+                       buf_at_from_addr(btr));
+
+               if (status != BT_CTF_BTR_STATUS_OK) {
+                       PERR("Failed to read basic type\n");
+                       goto end;
+               }
+
+               consume_bits(btr, field_size);
+
+               if (stack_empty(btr->stack)) {
+                       /* Root is a basic type */
+                       btr->state = BTR_STATE_DONE;
+               } else {
+                       /* Go to next field */
+                       stack_top(btr->stack)->index++;
+                       btr->state = BTR_STATE_NEXT_FIELD;
+                       btr->last_bo = btr->cur_bo;
+               }
+
+               goto end;
+       }
+
+       /* We are here; it means we don't have enough data to decode this */
+       stitch_set_from_remaining_buf(btr);
+       btr->state = BTR_STATE_READ_BASIC_CONTINUE;
+       status = BT_CTF_BTR_STATUS_EOF;
+
+end:
+       return status;
+}
+
+static inline
+enum bt_ctf_btr_status read_basic_int_type_and_call_begin(
+       struct bt_ctf_btr *btr)
+{
+       return read_basic_type_and_call_begin(btr, read_basic_int_and_call_cb);
+}
+
+static inline
+enum bt_ctf_btr_status read_basic_int_type_and_call_continue(
+       struct bt_ctf_btr *btr)
+{
+       return read_basic_type_and_call_continue(btr,
+               read_basic_int_and_call_cb);
+}
+
+static inline
+enum bt_ctf_btr_status read_basic_float_type_and_call_begin(
+       struct bt_ctf_btr *btr)
+{
+       return read_basic_type_and_call_begin(btr,
+               read_basic_float_and_call_cb);
+}
+
+static inline
+enum bt_ctf_btr_status read_basic_float_type_and_call_continue(
+       struct bt_ctf_btr *btr)
+{
+       return read_basic_type_and_call_continue(btr,
+               read_basic_float_and_call_cb);
+}
+
+static inline
+enum bt_ctf_btr_status read_basic_enum_type_and_call_begin(
+       struct bt_ctf_btr *btr)
+{
+       return read_basic_type_and_call_begin(btr,
+               read_basic_enum_and_call_cb);
+}
+
+static inline
+enum bt_ctf_btr_status read_basic_enum_type_and_call_continue(
+       struct bt_ctf_btr *btr)
+{
+       return read_basic_type_and_call_continue(btr,
+               read_basic_enum_and_call_cb);
+}
+
+static inline
+enum bt_ctf_btr_status read_basic_string_type_and_call(
+       struct bt_ctf_btr *btr, bool begin)
+{
+       size_t buf_at_bytes;
+       const uint8_t *result;
+       size_t available_bytes;
+       const uint8_t *first_chr;
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+
+       if (!at_least_one_bit_left(btr)) {
+               status = BT_CTF_BTR_STATUS_EOF;
+               goto end;
+       }
+
+       assert(buf_at_from_addr(btr) % 8 == 0);
+       available_bytes = BITS_TO_BYTES_FLOOR(available_bits(btr));
+       buf_at_bytes = BITS_TO_BYTES_FLOOR(buf_at_from_addr(btr));
+       first_chr = &btr->buf.addr[buf_at_bytes];
+       result = memchr(first_chr, '\0', available_bytes);
+
+       if (begin && btr->user.cbs.types.string_begin) {
+               status = btr->user.cbs.types.string_begin(
+                       btr->cur_basic_field_type, btr->user.data);
+
+               if (status != BT_CTF_BTR_STATUS_OK) {
+                       PERR("string_begin() user callback function failed\n");
+                       goto end;
+               }
+       }
+
+       if (!result) {
+               /* No null character yet */
+               if (btr->user.cbs.types.string) {
+                       status = btr->user.cbs.types.string(
+                               (const char *) first_chr,
+                               available_bytes, btr->cur_basic_field_type,
+                               btr->user.data);
+                       if (status != BT_CTF_BTR_STATUS_OK) {
+                               PERR("string() user callback function failed\n");
+                               goto end;
+                       }
+               }
+
+               consume_bits(btr, BYTES_TO_BITS(available_bytes));
+               btr->state = BTR_STATE_READ_BASIC_CONTINUE;
+               status = BT_CTF_BTR_STATUS_EOF;
+       } else {
+               /* Found the null character */
+               size_t result_len = (size_t) (result - first_chr);
+
+               if (btr->user.cbs.types.string && result_len) {
+                       status = btr->user.cbs.types.string(
+                               (const char *) first_chr,
+                               result_len, btr->cur_basic_field_type,
+                               btr->user.data);
+                       if (status != BT_CTF_BTR_STATUS_OK) {
+                               PERR("string() user callback function failed\n");
+                               goto end;
+                       }
+               }
+
+               if (btr->user.cbs.types.string_end) {
+                       status = btr->user.cbs.types.string_end(
+                               btr->cur_basic_field_type, btr->user.data);
+                       if (status != BT_CTF_BTR_STATUS_OK) {
+                               PERR("string_end() user callback function failed\n");
+                               goto end;
+                       }
+               }
+
+               consume_bits(btr, BYTES_TO_BITS(result_len + 1));
+
+               if (stack_empty(btr->stack)) {
+                       /* Root is a basic type */
+                       btr->state = BTR_STATE_DONE;
+               } else {
+                       /* Go to next field */
+                       stack_top(btr->stack)->index++;
+                       btr->state = BTR_STATE_NEXT_FIELD;
+                       btr->last_bo = btr->cur_bo;
+               }
+       }
+
+end:
+       return status;
+}
+
+static inline
+enum bt_ctf_btr_status read_basic_begin_state(struct bt_ctf_btr *btr)
+{
+       enum bt_ctf_btr_status status;
+
+       assert(btr->cur_basic_field_type);
+
+       switch (bt_ctf_field_type_get_type_id(btr->cur_basic_field_type)) {
+       case BT_CTF_TYPE_ID_INTEGER:
+               status = read_basic_int_type_and_call_begin(btr);
+               break;
+       case BT_CTF_TYPE_ID_FLOAT:
+               status = read_basic_float_type_and_call_begin(btr);
+               break;
+       case BT_CTF_TYPE_ID_ENUM:
+               status = read_basic_enum_type_and_call_begin(btr);
+               break;
+       case BT_CTF_TYPE_ID_STRING:
+               status = read_basic_string_type_and_call(btr, true);
+               break;
+       default:
+               assert(false);
+       }
+
+       return status;
+}
+
+static inline
+enum bt_ctf_btr_status read_basic_continue_state(struct bt_ctf_btr *btr)
+{
+       enum bt_ctf_btr_status status;
+
+       assert(btr->cur_basic_field_type);
+
+       switch (bt_ctf_field_type_get_type_id(btr->cur_basic_field_type)) {
+       case BT_CTF_TYPE_ID_INTEGER:
+               status = read_basic_int_type_and_call_continue(btr);
+               break;
+       case BT_CTF_TYPE_ID_FLOAT:
+               status = read_basic_float_type_and_call_continue(btr);
+               break;
+       case BT_CTF_TYPE_ID_ENUM:
+               status = read_basic_enum_type_and_call_continue(btr);
+               break;
+       case BT_CTF_TYPE_ID_STRING:
+               status = read_basic_string_type_and_call(btr, false);
+               break;
+       default:
+               assert(false);
+       }
+
+       return status;
+}
+
+static inline
+size_t bits_to_skip_to_align_to(struct bt_ctf_btr *btr, size_t align)
+{
+       size_t aligned_packet_at;
+
+       aligned_packet_at = ALIGN(packet_at(btr), align);
+
+       return aligned_packet_at - packet_at(btr);
+}
+
+static inline
+enum bt_ctf_btr_status align_type_state(struct bt_ctf_btr *btr,
+       struct bt_ctf_field_type *field_type, enum btr_state next_state)
+{
+       int field_alignment;
+       size_t skip_bits;
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+
+       /* Get field's alignment */
+       field_alignment = bt_ctf_field_type_get_alignment(field_type);
+       if (field_alignment < 0) {
+               PERR("Failed to get type alignment\n");
+               status = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+       /*
+        * 0 means "undefined" for variants; what we really want is 1
+        * (always aligned)
+        */
+       if (field_alignment == 0) {
+               field_alignment = 1;
+       }
+
+       /* Compute how many bits we need to skip */
+       skip_bits = bits_to_skip_to_align_to(btr, field_alignment);
+
+       /* Nothing to skip? aligned */
+       if (skip_bits == 0) {
+               btr->state = next_state;
+               goto end;
+       }
+
+       /* Make sure there's at least one bit left */
+       if (!at_least_one_bit_left(btr)) {
+               status = BT_CTF_BTR_STATUS_EOF;
+               goto end;
+       }
+
+       /* Consume as many bits as possible in what's left */
+       consume_bits(btr, MIN(available_bits(btr), skip_bits));
+
+       /* Are we done now? */
+       skip_bits = bits_to_skip_to_align_to(btr, field_alignment);
+
+       if (skip_bits == 0) {
+               /* Yes: go to next state */
+               btr->state = next_state;
+               goto end;
+       } else {
+               /* No: need more data */
+               status = BT_CTF_BTR_STATUS_EOF;
+       }
+
+end:
+       return status;
+}
+
+static inline
+bool is_compound_type(struct bt_ctf_field_type *field_type)
+{
+       enum bt_ctf_type_id id = bt_ctf_field_type_get_type_id(field_type);
+
+       return id == BT_CTF_TYPE_ID_STRUCT || id == BT_CTF_TYPE_ID_ARRAY ||
+               id == BT_CTF_TYPE_ID_SEQUENCE || id == BT_CTF_TYPE_ID_VARIANT;
+}
+
+static inline
+enum bt_ctf_btr_status next_field_state(struct bt_ctf_btr *btr)
+{
+       int ret;
+       struct stack_entry *top;
+       struct bt_ctf_field_type *next_field_type = NULL;
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+
+       if (stack_empty(btr->stack)) {
+               goto end;
+       }
+
+       top = stack_top(btr->stack);
+
+       /* Are we done with this base type? */
+       while (top->index == top->base_len) {
+               if (btr->user.cbs.types.compound_end) {
+                       status = btr->user.cbs.types.compound_end(
+                               top->base_type, btr->user.data);
+                       if (status != BT_CTF_BTR_STATUS_OK) {
+                               PERR("compound_end() user callback function failed\n");
+                               goto end;
+                       }
+               }
+
+               stack_pop(btr->stack);
+
+               /* Are we done with the root type? */
+               if (stack_empty(btr->stack)) {
+                       btr->state = BTR_STATE_DONE;
+                       goto end;
+               }
+
+               top = stack_top(btr->stack);
+               top->index++;
+       }
+
+       /* Get next field's type */
+       switch (bt_ctf_field_type_get_type_id(top->base_type)) {
+       case BT_CTF_TYPE_ID_STRUCT:
+               ret = bt_ctf_field_type_structure_get_field(
+                       top->base_type, NULL, &next_field_type,
+                       top->index);
+               if (ret) {
+                       next_field_type = NULL;
+               }
+               break;
+       case BT_CTF_TYPE_ID_ARRAY:
+               next_field_type =
+                       bt_ctf_field_type_array_get_element_type(
+                               top->base_type);
+               break;
+       case BT_CTF_TYPE_ID_SEQUENCE:
+               next_field_type =
+                       bt_ctf_field_type_sequence_get_element_type(
+                               top->base_type);
+               break;
+       case BT_CTF_TYPE_ID_VARIANT:
+               /* Variant types are dynamic: query the user, he should know! */
+               next_field_type =
+                       btr->user.cbs.query.get_variant_type(
+                               top->base_type, btr->user.data);
+               break;
+       default:
+               break;
+       }
+
+       if (!next_field_type) {
+               PERR("Failed to get next field's type\n");
+               status = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+       if (is_compound_type(next_field_type)) {
+               if (btr->user.cbs.types.compound_begin) {
+                       status = btr->user.cbs.types.compound_begin(
+                               next_field_type, btr->user.data);
+                       if (status != BT_CTF_BTR_STATUS_OK) {
+                               PERR("compound_begin() user callback function failed\n");
+                               goto end;
+                       }
+               }
+
+               ret = stack_push_with_len(btr, next_field_type);
+               if (ret) {
+                       PERR("Failed to push compound type onto the stack\n");
+                       status = BT_CTF_BTR_STATUS_ERROR;
+                       goto end;
+               }
+
+               /* Next state: align a compound type */
+               btr->state = BTR_STATE_ALIGN_COMPOUND;
+       } else {
+               /* Replace current basic field type */
+               BT_MOVE(btr->cur_basic_field_type, next_field_type);
+
+               /* Next state: align a basic type */
+               btr->state = BTR_STATE_ALIGN_BASIC;
+       }
+
+end:
+       BT_PUT(next_field_type);
+
+       return status;
+}
+
+static inline
+enum bt_ctf_btr_status handle_state(struct bt_ctf_btr *btr)
+{
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+
+       switch (btr->state) {
+       case BTR_STATE_NEXT_FIELD:
+               status = next_field_state(btr);
+               break;
+       case BTR_STATE_ALIGN_BASIC:
+               status = align_type_state(btr, btr->cur_basic_field_type,
+                       BTR_STATE_READ_BASIC_BEGIN);
+               break;
+       case BTR_STATE_ALIGN_COMPOUND:
+               status = align_type_state(btr, stack_top(btr->stack)->base_type,
+                       BTR_STATE_NEXT_FIELD);
+               break;
+       case BTR_STATE_READ_BASIC_BEGIN:
+               status = read_basic_begin_state(btr);
+               break;
+       case BTR_STATE_READ_BASIC_CONTINUE:
+               status = read_basic_continue_state(btr);
+               break;
+       case BTR_STATE_DONE:
+               break;
+       }
+
+       return status;
+}
+
+struct bt_ctf_btr *bt_ctf_btr_create(struct bt_ctf_btr_cbs cbs, void *data,
+       FILE *err_stream)
+{
+       struct bt_ctf_btr *btr;
+
+       btr = g_new0(struct bt_ctf_btr, 1);
+       if (!btr) {
+               PERR("Failed to allocate memory for binary type reader\n");
+               goto end;
+       }
+
+       btr->stack = stack_new();
+       if (!btr->stack) {
+               PERR("Failed to create stack\n");
+               bt_ctf_btr_destroy(btr);
+               btr = NULL;
+               goto end;
+       }
+
+       btr->state = BTR_STATE_NEXT_FIELD;
+       btr->user.cbs = cbs;
+       btr->user.data = data;
+       btr->err_stream = err_stream;
+
+end:
+       return btr;
+}
+
+void bt_ctf_btr_destroy(struct bt_ctf_btr *btr)
+{
+       if (btr->stack) {
+               stack_destroy(btr->stack);
+       }
+
+       BT_PUT(btr->cur_basic_field_type);
+       g_free(btr);
+}
+
+static
+void reset(struct bt_ctf_btr *btr)
+{
+       stack_clear(btr->stack);
+       BT_PUT(btr->cur_basic_field_type);
+       stitch_reset(btr);
+       btr->buf.addr = NULL;
+       btr->last_bo = BT_CTF_BYTE_ORDER_UNKNOWN;
+}
+
+size_t bt_ctf_btr_start(struct bt_ctf_btr *btr,
+       struct bt_ctf_field_type *type, const uint8_t *buf,
+       size_t offset, size_t packet_offset, size_t sz,
+       enum bt_ctf_btr_status *status)
+{
+       assert(btr);
+       assert(buf);
+       assert(sz > 0);
+       assert(BYTES_TO_BITS(sz) > offset);
+       reset(btr);
+       btr->buf.addr = buf;
+       btr->buf.offset = offset;
+       btr->buf.at = 0;
+       btr->buf.packet_offset = packet_offset;
+       btr->buf.buf_sz = sz;
+       btr->buf.sz = BYTES_TO_BITS(sz) - offset;
+       *status = BT_CTF_BTR_STATUS_OK;
+
+       /* Set root type */
+       if (is_compound_type(type)) {
+               /* Compound type: push on visit stack */
+               int stack_ret;
+
+               if (btr->user.cbs.types.compound_begin) {
+                       *status = btr->user.cbs.types.compound_begin(
+                               type, btr->user.data);
+                       if (*status != BT_CTF_BTR_STATUS_OK) {
+                               PERR("compound_begin() user callback function failed\n");
+                               goto end;
+                       }
+               }
+
+               stack_ret = stack_push_with_len(btr, type);
+               if (stack_ret) {
+                       PERR("Failed to push initial compound type onto the stack\n");
+                       *status = BT_CTF_BTR_STATUS_ERROR;
+                       goto end;
+               }
+
+               btr->state = BTR_STATE_ALIGN_COMPOUND;
+       } else {
+               /* Basic type: set as current basic type */
+               btr->cur_basic_field_type = type;
+               bt_get(btr->cur_basic_field_type);
+               btr->state = BTR_STATE_ALIGN_BASIC;
+       }
+
+       /* Run the machine! */
+       while (true) {
+               *status = handle_state(btr);
+               if (*status != BT_CTF_BTR_STATUS_OK) {
+                       break;
+               } else if (btr->state == BTR_STATE_DONE) {
+                       break;
+               }
+       }
+
+       /* Update packet offset for next time */
+       btr->buf.packet_offset += btr->buf.at;
+
+end:
+       return btr->buf.at;
+}
+
+size_t bt_ctf_btr_continue(struct bt_ctf_btr *btr,
+       const uint8_t *buf, size_t sz,
+       enum bt_ctf_btr_status *status)
+{
+       assert(btr);
+       assert(buf);
+       assert(sz > 0);
+       btr->buf.addr = buf;
+       btr->buf.offset = 0;
+       btr->buf.at = 0;
+       btr->buf.buf_sz = sz;
+       btr->buf.sz = BYTES_TO_BITS(sz);
+       *status = BT_CTF_BTR_STATUS_OK;
+
+       /* Continue running the machine */
+       while (true) {
+               *status = handle_state(btr);
+               if (*status != BT_CTF_BTR_STATUS_OK) {
+                       break;
+               } else if (btr->state == BTR_STATE_DONE) {
+                       break;
+               }
+       }
+
+       /* Update packet offset for next time */
+       btr->buf.packet_offset += btr->buf.at;
+
+       return btr->buf.at;
+}
diff --git a/ctf-reader-proto/ctf-btr/ctf-btr.h b/ctf-reader-proto/ctf-btr/ctf-btr.h
new file mode 100644 (file)
index 0000000..18244b0
--- /dev/null
@@ -0,0 +1,352 @@
+#ifndef CTF_BTR_H
+#define CTF_BTR_H
+
+/*
+ * Babeltrace - CTF binary type reader (BTR)
+ *
+ * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation
+ * Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <babeltrace/ctf-ir/field-types.h>
+#include <babeltrace/babeltrace-internal.h>
+
+/**
+ * @file ctf-btr.h
+ *
+ * Event-driven CTF binary type reader (BTR).
+ *
+ * This is a common, internal API used by CTF source plugins. It allows
+ * a binary CTF IR field type to be decoded from user-provided buffers.
+ * As the type is decoded (and, possibly, its nested types), registered
+ * user callback functions are called.
+ *
+ * This API is only concerned with reading one CTF type at a time from
+ * one or more buffer of bytes. It does not know CTF dynamic scopes,
+ * events, or streams. Sequence lengths and selected variant types are
+ * requested to the user when needed.
+ */
+
+/**
+ * Binary type reader API status codes.
+ */
+enum bt_ctf_btr_status {
+       /**
+        * The binary stream reader reached the end of the user-provided
+        * buffer, but data is still needed to finish decoding the
+        * requested type.
+        *
+        * The user needs to call bt_ctf_btr_continue() as long as
+        * #BT_CTF_BTR_STATUS_EOF is returned to complete the decoding
+        * process of a given type.
+        */
+       BT_CTF_BTR_STATUS_EOF =         -4,
+
+       /** Invalid argument. */
+       BT_CTF_BTR_STATUS_INVAL =       -3,
+
+       /** General error. */
+       BT_CTF_BTR_STATUS_ERROR =       -1,
+
+       /** Everything okay. */
+       BT_CTF_BTR_STATUS_OK =          0,
+};
+
+/** Type reader. */
+struct bt_ctf_btr;
+
+/*
+ * Type reader user callback functions.
+ */
+struct bt_ctf_btr_cbs {
+       /**
+        * Type callback functions.
+        *
+        * This CTF binary type reader is event-driven. The following
+        * functions are called during the decoding process, either when
+        * a compound type begins/ends, or when a basic type is
+        * completely decoded (along with its value).
+        *
+        * Each function also receives the CTF IR field type associated
+        * with the call, and user data (registered to the type reader
+        * calling them). This field type is a weak reference; the
+        * callback function must use bt_ctf_field_type_get() to keep
+        * its own reference of it.
+        *
+        * Actual CTF IR fields are \em not created here; this would be
+        * the responsibility of a type reader's user (the provider of
+        * those callback functions).
+        *
+        * All the type callback functions return one of the following
+        * values:
+        *
+        *   - <b>#BT_CTF_BTR_STATUS_OK</b>: Everything is okay;
+        *     continue the decoding process.
+        *   - <b>#BT_CTF_BTR_STATUS_ERROR</b>: General error (reported
+        *     to type reader's user).
+        *
+        * Any member of this structure may be set to \c NULL, should
+        * a specific notification be not needed.
+        */
+       struct {
+               /**
+                * Called when a signed integer type is completely
+                * decoded. This could also be the supporting signed
+                * integer type of an enumeration type (\p type will
+                * indicate this).
+                *
+                * @param value         Signed integer value
+                * @param type          Integer or enumeration type (weak
+                *                      reference)
+                * @param data          User data
+                * @returns             #BT_CTF_BTR_STATUS_OK or
+                *                      #BT_CTF_BTR_STATUS_ERROR
+                */
+               enum bt_ctf_btr_status (* signed_int)(int64_t value,
+                               struct bt_ctf_field_type *type, void *data);
+
+               /**
+                * Called when an unsigned integer type is completely
+                * decoded. This could also be the supporting signed
+                * integer type of an enumeration type (\p type will
+                * indicate this).
+                *
+                * @param value         Unsigned integer value
+                * @param type          Integer or enumeration type (weak
+                *                      reference)
+                * @param data          User data
+                * @returns             #BT_CTF_BTR_STATUS_OK or
+                *                      #BT_CTF_BTR_STATUS_ERROR
+                */
+               enum bt_ctf_btr_status (* unsigned_int)(uint64_t value,
+                               struct bt_ctf_field_type *type, void *data);
+
+               /**
+                * Called when a floating point number type is
+                * completely decoded.
+                *
+                * @param value         Floating point number value
+                * @param type          Floating point number type (weak
+                *                      reference)
+                * @param data          User data
+                * @returns             #BT_CTF_BTR_STATUS_OK or
+                *                      #BT_CTF_BTR_STATUS_ERROR
+                */
+               enum bt_ctf_btr_status (* floating_point)(double value,
+                               struct bt_ctf_field_type *type, void *data);
+
+               /**
+                * Called when a string type begins.
+                *
+                * All the following user callback function calls will
+                * be made to bt_ctf_btr_cbs::types::string(), each of
+                * them providing one substring of the complete string
+                * type's value.
+                *
+                * @param type          Beginning string type (weak reference)
+                * @param data          User data
+                * @returns             #BT_CTF_BTR_STATUS_OK or
+                *                      #BT_CTF_BTR_STATUS_ERROR
+                */
+               enum bt_ctf_btr_status (* string_begin)(
+                               struct bt_ctf_field_type *type, void *data);
+
+               /**
+                * Called when a string type's substring is decoded
+                * (between a call to bt_ctf_btr_cbs::types::string_begin()
+                * and a call to bt_ctf_btr_cbs::types::string_end()).
+                *
+                * @param value         String value (\em not null-terminated)
+                * @param len           String value length
+                * @param type          String type (weak reference)
+                * @param data          User data
+                * @returns             #BT_CTF_BTR_STATUS_OK or
+                *                      #BT_CTF_BTR_STATUS_ERROR
+                */
+               enum bt_ctf_btr_status (* string)(const char *value,
+                               size_t len, struct bt_ctf_field_type *type,
+                               void *data);
+
+               /**
+                * Called when a string type ends.
+                *
+                * @param type          Ending string type (weak reference)
+                * @param data          User data
+                * @returns             #BT_CTF_BTR_STATUS_OK or
+                *                      #BT_CTF_BTR_STATUS_ERROR
+                */
+               enum bt_ctf_btr_status (* string_end)(
+                               struct bt_ctf_field_type *type, void *data);
+
+               /**
+                * Called when a compound type begins.
+                *
+                * All the following type callback function calls will
+                * signal sequential elements of this compound type,
+                * until the next corresponding
+                * bt_ctf_btr_cbs::types::compound_end() is called.
+                *
+                * If \p type is a variant type, then only one type
+                * callback function call will follow before the call to
+                * bt_ctf_btr_cbs::types::compound_end(). This single
+                * call indicates the selected type of this variant
+                * type.
+                *
+                * @param type          Beginning compound type (weak reference)
+                * @param data          User data
+                * @returns             #BT_CTF_BTR_STATUS_OK or
+                *                      #BT_CTF_BTR_STATUS_ERROR
+                */
+               enum bt_ctf_btr_status (* compound_begin)(
+                               struct bt_ctf_field_type *type, void *data);
+
+               /**
+                * Called when a compound type ends.
+                *
+                * @param type          Ending compound type (weak reference)
+                * @param data          User data
+                * @returns             #BT_CTF_BTR_STATUS_OK or
+                *                      #BT_CTF_BTR_STATUS_ERROR
+                */
+               enum bt_ctf_btr_status (* compound_end)(
+                               struct bt_ctf_field_type *type, void *data);
+       } types;
+
+       /**
+        * Query callback functions are used when the type reader needs
+        * dynamic information, i.e. a sequence type's current length
+        * or a variant type's current selected type.
+        *
+        * Both functions need to be set unless it is known that no
+        * sequences or variants will have to be decoded.
+        */
+       struct {
+               /**
+                * Called to query the current length of a given sequence
+                * type.
+                *
+                * @param type          Sequence type (weak reference)
+                * @param data          User data
+                * @returns             Sequence length or
+                *                      #BT_CTF_BTR_STATUS_ERROR on error
+                */
+               int64_t (* get_sequence_length)(struct bt_ctf_field_type *type,
+                               void *data);
+
+               /**
+                * Called to query the current selected type of a given
+                * variant type.
+                *
+                * @param type          Variant type (weak reference)
+                * @param data          User data
+                * @returns             Current selected type (owned by
+                *                      this) or \c NULL on error
+                */
+               struct bt_ctf_field_type * (* get_variant_type)(
+                               struct bt_ctf_field_type *type, void *data);
+       } query;
+};
+
+/**
+ * Creates a CTF binary type reader.
+ *
+ * @param cbs          User callback functions
+ * @param data         User data (passed to user callback functions)
+ * @param err_stream   Error stream (can be \c NULL to disable)
+ * @returns            New binary type reader on success, or \c NULL on error
+ */
+struct bt_ctf_btr *bt_ctf_btr_create(struct bt_ctf_btr_cbs cbs, void *data,
+               FILE *err_stream);
+
+/**
+ * Destroys a CTF binary type reader, freeing all internal resources.
+ *
+ * @param btr  Binary type reader
+ */
+void bt_ctf_btr_destroy(struct bt_ctf_btr *btr);
+
+/**
+ * Decodes a given CTF type from a buffer of bytes.
+ *
+ * The number of \em bits consumed by this function is returned.
+ *
+ * The \p status output parameter is where a status is written, amongst
+ * the following:
+ *
+ *   - <b>#BT_CTF_BTR_STATUS_OK</b>: Decoding is done.
+ *   - <b>#BT_CTF_BTR_STATUS_EOF</b>: The end of the buffer was reached,
+ *     but more data is needed to finish the decoding process of the
+ *     requested type. The user needs to call bt_ctf_btr_continue()
+ *     as long as #BT_CTF_BTR_STATUS_EOF is returned to complete the
+ *     decoding process of the original type.
+ *   - <b>#BT_CTF_BTR_STATUS_INVAL</b>: Invalid argument.
+ *   - <b>#BT_CTF_BTR_STATUS_ERROR</b>: General error.
+ *
+ * Calling this function resets the type reader's internal state. If
+ * #BT_CTF_BTR_STATUS_EOF is returned, bt_ctf_btr_continue() needs to
+ * be called next, \em not bt_ctf_btr_decode().
+ *
+ * @param btr                  Binary type reader
+ * @param type                 Type to decode (weak reference)
+ * @param buf                  Buffer
+ * @param offset               Offset of first bit from \p buf (bits)
+ * @param packet_offset                Offset of \p offset within the CTF
+ *                             binary packet containing \p type (bits)
+ * @param sz                   Size of buffer in bytes (from \p buf)
+ * @param status               Returned status (see description above)
+ * ­@returns                  Number of consumed bits
+ */
+size_t bt_ctf_btr_start(struct bt_ctf_btr *btr,
+               struct bt_ctf_field_type *type, const uint8_t *buf,
+               size_t offset, size_t packet_offset, size_t sz,
+               enum bt_ctf_btr_status *status);
+
+/**
+ * Continues the decoding process a given CTF type.
+ *
+ * The number of bits consumed by this function is returned.
+ *
+ * The \p status output parameter is where a status is placed, amongst
+ * the following:
+ *
+ *   - <b>#BT_CTF_BTR_STATUS_OK</b>: decoding is done.
+ *   - <b>#BT_CTF_BTR_STATUS_EOF</b>: the end of the buffer was reached,
+ *     but more data is needed to finish the decoding process of the
+ *     requested type. The user needs to call bt_ctf_btr_continue()
+ *     as long as #BT_CTF_BTR_STATUS_EOF is returned to complete the
+ *     decoding process of the original type.
+ *   - <b>#BT_CTF_BTR_STATUS_INVAL</b>: invalid argument.
+ *   - <b>#BT_CTF_BTR_STATUS_ERROR</b>: general error.
+ *
+ * @param btr          Binary type reader
+ * @param buf          Buffer
+ * @param sz           Size of buffer in bytes (from \p offset)
+ * @param status       Returned status (see description above)
+ * ­@returns          Number of consumed bits
+ */
+size_t bt_ctf_btr_continue(struct bt_ctf_btr *btr,
+               const uint8_t *buf, size_t sz,
+               enum bt_ctf_btr_status *status);
+
+#endif /* CTF_BTR_H */
diff --git a/ctf-reader-proto/ctf-btr/print.h b/ctf-reader-proto/ctf-btr/print.h
new file mode 100644 (file)
index 0000000..22381dc
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef CTF_BTR_PRINT_H
+#define CTF_BTR_PRINT_H
+
+/*
+ * Define PRINT_PREFIX and PRINT_ERR_STREAM, then include this file.
+ *
+ * Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <babeltrace/babeltrace-internal.h>
+
+#define PERR(fmt, ...)                                                 \
+       do {                                                            \
+               if (PRINT_ERR_STREAM) {                                 \
+                       fprintf(PRINT_ERR_STREAM,                       \
+                               "Error: " PRINT_PREFIX ": " fmt,        \
+                               ##__VA_ARGS__);                         \
+               }                                                       \
+       } while (0)
+
+#define PWARN(fmt, ...)                                                        \
+       do {                                                            \
+               if (PRINT_ERR_STREAM) {                                 \
+                       fprintf(PRINT_ERR_STREAM,                       \
+                               "Warning: " PRINT_PREFIX ": " fmt,      \
+                               ##__VA_ARGS__);                         \
+               }                                                       \
+       } while (0)
+
+#define PDBG(fmt, ...)                                                 \
+       do {                                                            \
+               if (babeltrace_debug) {                                 \
+                       fprintf(stderr,                                 \
+                               "Debug: " PRINT_PREFIX ": " fmt,        \
+                               ##__VA_ARGS__);                         \
+               }                                                       \
+       } while (0)
+
+#endif /* CTF_BTR_PRINT_H */
diff --git a/ctf-reader-proto/ctf-fs-data-stream.c b/ctf-reader-proto/ctf-fs-data-stream.c
new file mode 100644 (file)
index 0000000..c696927
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * Copyright 2016 - Philippe Proulx <pproulx@efficios.com>
+ * Copyright 2010-2011 - EfficiOS Inc. and Linux Foundation
+ *
+ * Some functions are based on older functions written by Mathieu Desnoyers.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+#include <babeltrace/ctf-ir/stream.h>
+
+#define PRINT_ERR_STREAM       ctf_fs->error_fp
+#define PRINT_PREFIX           "ctf-fs-data-stream"
+#include "print.h"
+
+#include "ctf-fs.h"
+#include "ctf-fs-file.h"
+#include "ctf-fs-metadata.h"
+#include "ctf-notif-iter/ctf-notif-iter.h"
+
+static void ctf_fs_stream_destroy(struct ctf_fs_stream *stream)
+{
+       if (stream->file) {
+               ctf_fs_file_destroy(stream->file);
+       }
+
+       if (stream->stream) {
+               BT_PUT(stream->stream);
+       }
+
+       if (stream->notif_iter) {
+               bt_ctf_notif_iter_destroy(stream->notif_iter);
+       }
+
+       g_free(stream);
+}
+
+static size_t remaining_mmap_bytes(struct ctf_fs_stream *stream)
+{
+       return stream->mmap_offset + stream->mmap_len -
+               stream->request_offset;
+}
+
+static int stream_munmap(struct ctf_fs_stream *stream)
+{
+       struct ctf_fs *ctf_fs = stream->file->ctf_fs;
+
+       if (munmap(stream->mmap_addr, stream->mmap_len)) {
+               PERR("Cannot memory-unmap address %p (size %zu) of file \"%s\" (%p): %s\n",
+                       stream->mmap_addr, stream->mmap_len,
+                       stream->file->path->str, stream->file->fp,
+                       strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+static int mmap_next(struct ctf_fs_stream *stream)
+{
+       struct ctf_fs *ctf_fs = stream->file->ctf_fs;
+       int ret = 0;
+
+       /* Unmap old region */
+       if (stream->mmap_addr) {
+               if (stream_munmap(stream)) {
+                       goto error;
+               }
+
+               stream->mmap_offset += stream->mmap_len;
+               stream->request_offset = stream->mmap_offset;
+       }
+
+       /* Map new region */
+       stream->mmap_addr = mmap((void *) 0, stream->mmap_len,
+               PROT_READ, MAP_PRIVATE, fileno(stream->file->fp),
+               stream->mmap_offset);
+       if (stream->mmap_addr == MAP_FAILED) {
+               PERR("Cannot memory-map address (size %zu) of file \"%s\" (%p) at offset %zu: %s\n",
+                       stream->mmap_len, stream->file->path->str,
+                       stream->file->fp, stream->mmap_offset,
+                       strerror(errno));
+               goto error;
+       }
+
+       goto end;
+
+error:
+       stream_munmap(stream);
+       ret = -1;
+
+end:
+       return ret;
+}
+
+static enum bt_ctf_notif_iter_medium_status medop_request_bytes(
+               size_t request_sz, uint8_t **buffer_addr,
+               size_t *buffer_sz, void *data)
+{
+       enum bt_ctf_notif_iter_medium_status status =
+               BT_CTF_NOTIF_ITER_MEDIUM_STATUS_OK;
+       struct ctf_fs_stream *stream = data;
+       struct ctf_fs *ctf_fs = stream->file->ctf_fs;
+
+       if (request_sz == 0) {
+               goto end;
+       }
+
+       /* Check if we need an initial memory map */
+       if (!stream->mmap_addr) {
+               if (mmap_next(stream)) {
+                       PERR("Cannot memory-map initial region of file \"%s\" (%p)\n",
+                               stream->file->path->str, stream->file->fp);
+                       goto error;
+               }
+       }
+
+       /* Check if we have at least one memory-mapped byte left */
+       if (remaining_mmap_bytes(stream) == 0) {
+               /* Are we at the end of the file? */
+               if (stream->request_offset == stream->file->size) {
+                       PDBG("Reached end of file \"%s\" (%p)\n",
+                               stream->file->path->str, stream->file->fp);
+                       status = BT_CTF_NOTIF_ITER_MEDIUM_STATUS_EOF;
+                       goto end;
+               }
+
+               if (mmap_next(stream)) {
+                       PERR("Cannot memory-map next region of file \"%s\" (%p)\n",
+                               stream->file->path->str, stream->file->fp);
+                       goto error;
+               }
+       }
+
+       *buffer_sz = MIN(remaining_mmap_bytes(stream), request_sz);
+       *buffer_addr = ((uint8_t *) stream->mmap_addr) +
+               stream->request_offset - stream->mmap_offset;
+       stream->request_offset += *buffer_sz;
+       goto end;
+
+error:
+       status = BT_CTF_NOTIF_ITER_MEDIUM_STATUS_ERROR;
+
+end:
+       return status;
+}
+
+static struct bt_ctf_stream *medop_get_stream(
+               struct bt_ctf_stream_class *stream_class, void *data)
+{
+       struct ctf_fs_stream *fs_stream = data;
+       struct ctf_fs *ctf_fs = fs_stream->file->ctf_fs;
+
+       if (!fs_stream->stream) {
+               int64_t id = bt_ctf_stream_class_get_id(stream_class);
+
+               PDBG("Creating stream out of stream class %" PRId64 "\n", id);
+               fs_stream->stream = bt_ctf_stream_create(stream_class,
+                       fs_stream->file->path->str);
+               if (!fs_stream->stream) {
+                       PERR("Cannot create stream (stream class %" PRId64 ")\n",
+                               id);
+               }
+       }
+
+       return fs_stream->stream;
+}
+
+static struct bt_ctf_notif_iter_medium_ops medops = {
+       .request_bytes = medop_request_bytes,
+       .get_stream = medop_get_stream,
+};
+
+static struct ctf_fs_stream *ctf_fs_stream_create(struct ctf_fs *ctf_fs,
+               struct ctf_fs_file *file)
+{
+       struct ctf_fs_stream *stream = g_new0(struct ctf_fs_stream, 1);
+
+       if (!stream) {
+               goto error;
+       }
+
+       stream->file = file;
+       stream->notif_iter = bt_ctf_notif_iter_create(ctf_fs->metadata.trace,
+               12, medops, stream, ctf_fs->error_fp);
+       if (!stream->notif_iter) {
+               goto error;
+       }
+       stream->mmap_len = ctf_fs->page_size;
+
+       goto end;
+
+error:
+       /* Do not touch borrowed file */
+       stream->file = NULL;
+       ctf_fs_stream_destroy(stream);
+       stream = NULL;
+
+end:
+       return stream;
+}
+
+int ctf_fs_data_stream_open_streams(struct ctf_fs *ctf_fs)
+{
+       int ret = 0;
+       GError *error = NULL;
+       GDir *dir = g_dir_open(ctf_fs->trace_path->str, 0, &error);
+       const char *name;
+
+       if (!dir) {
+               PERR("Cannot open directory \"%s\": %s (code %d)\n",
+                       ctf_fs->trace_path->str, error->message,
+                       error->code);
+               goto error;
+       }
+
+       while ((name = g_dir_read_name(dir))) {
+               struct ctf_fs_file *file = NULL;
+               struct ctf_fs_stream *stream = NULL;
+
+               if (strcmp(name, CTF_FS_METADATA_FILENAME) == 0) {
+                       /* Ignore the metadata stream */
+                       PDBG("Ignoring metadata file \"%s\"\n",
+                               name);
+                       continue;
+               }
+
+               if (name[0] == '.') {
+                       PDBG("Ignoring hidden file \"%s\"\n",
+                               name);
+                       continue;
+               }
+
+               /* Create the file */
+               file = ctf_fs_file_create(ctf_fs);
+               if (!file) {
+                       PERR("Cannot create stream file object\n");
+                       goto error;
+               }
+
+               /* Create full path string */
+               g_string_append(file->path, ctf_fs->trace_path->str);
+               g_string_append(file->path, "/");
+               g_string_append(file->path, name);
+
+               if (!g_file_test(file->path->str, G_FILE_TEST_IS_REGULAR)) {
+                       PDBG("Ignoring non-regular file \"%s\"\n", name);
+                       ctf_fs_file_destroy(file);
+                       continue;
+               }
+
+               /* Open the file */
+               if (ctf_fs_file_open(ctf_fs, file, "rb")) {
+                       ctf_fs_file_destroy(file);
+                       goto error;
+               }
+
+               /* Create a private stream */
+               stream = ctf_fs_stream_create(ctf_fs, file);
+               if (!stream) {
+                       ctf_fs_file_destroy(file);
+                       goto error;
+               }
+
+               /* Append file to the array of files */
+               g_ptr_array_add(ctf_fs->data_stream.streams, stream);
+       }
+
+       goto end;
+
+error:
+       ret = -1;
+
+end:
+       if (dir) {
+               g_dir_close(dir);
+               dir = NULL;
+       }
+
+       if (error) {
+               g_error_free(error);
+       }
+
+       return ret;
+}
+
+int ctf_fs_data_stream_init(struct ctf_fs *ctf_fs,
+               struct ctf_fs_data_stream *data_stream)
+{
+       int ret = 0;
+
+       data_stream->streams = g_ptr_array_new_with_free_func(
+               (GDestroyNotify) ctf_fs_stream_destroy);
+       if (!data_stream->streams) {
+               PERR("Cannot allocate array of streams\n");
+               goto error;
+       }
+
+       goto end;
+
+error:
+       ret = -1;
+
+end:
+       return ret;
+}
+
+void ctf_fs_data_stream_deinit(struct ctf_fs_data_stream *data_stream)
+{
+       g_ptr_array_free(data_stream->streams, TRUE);
+}
+
+int ctf_fs_data_stream_get_next_notification(
+               struct ctf_fs *ctf_fs,
+               struct bt_ctf_notif_iter_notif **notification)
+{
+       int ret = 0;
+       struct ctf_fs_stream *stream = g_ptr_array_index(
+               ctf_fs->data_stream.streams, 0);
+       enum bt_ctf_notif_iter_status status;
+
+       status = bt_ctf_notif_iter_get_next_notification(
+               stream->notif_iter, notification);
+       if (status != BT_CTF_NOTIF_ITER_STATUS_OK &&
+                       status != BT_CTF_NOTIF_ITER_STATUS_EOF) {
+               goto error;
+       }
+       if (status == BT_CTF_NOTIF_ITER_STATUS_EOF) {
+               *notification = NULL;
+       }
+
+       goto end;
+
+error:
+       ret = -1;
+
+end:
+       return ret;
+}
diff --git a/ctf-reader-proto/ctf-fs-data-stream.h b/ctf-reader-proto/ctf-fs-data-stream.h
new file mode 100644 (file)
index 0000000..d8f6746
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef CTF_FS_DATA_STREAM_H
+#define CTF_FS_DATA_STREAM_H
+
+/*
+ * Copyright 2016 - Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <glib.h>
+#include <babeltrace/babeltrace-internal.h>
+#include <babeltrace/ctf-ir/trace.h>
+
+#include "ctf-notif-iter/ctf-notif-iter.h"
+
+struct ctf_fs_stream {
+       struct ctf_fs_file *file;
+       struct bt_ctf_stream *stream;
+       struct bt_ctf_notif_iter *notif_iter;
+       void *mmap_addr;
+       size_t mmap_len;
+       off_t mmap_offset;
+       off_t request_offset;
+};
+
+struct ctf_fs_data_stream {
+       GPtrArray *streams;
+};
+
+BT_HIDDEN
+int ctf_fs_data_stream_init(struct ctf_fs *ctf_fs,
+               struct ctf_fs_data_stream *data_stream);
+
+BT_HIDDEN
+void ctf_fs_data_stream_deinit(struct ctf_fs_data_stream *data_stream);
+
+BT_HIDDEN
+int ctf_fs_data_stream_open_streams(struct ctf_fs *ctf_fs);
+
+BT_HIDDEN
+int ctf_fs_data_stream_get_next_notification(
+               struct ctf_fs *ctf_fs,
+               struct bt_ctf_notif_iter_notif **notification);
+
+#endif /* CTF_FS_DATA_STREAM_H */
diff --git a/ctf-reader-proto/ctf-fs-file.c b/ctf-reader-proto/ctf-fs-file.c
new file mode 100644 (file)
index 0000000..45819ef
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2016 - Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <glib.h>
+
+#define PRINT_ERR_STREAM       ctf_fs->error_fp
+#define PRINT_PREFIX           "ctf-fs-file"
+#include "print.h"
+
+#include "ctf-fs-file.h"
+
+void ctf_fs_file_destroy(struct ctf_fs_file *file)
+{
+       struct ctf_fs *ctf_fs = file->ctf_fs;
+
+       if (!file) {
+               return;
+       }
+
+       if (file->fp) {
+               PDBG("Closing file \"%s\" (%p)\n", file->path->str, file->fp);
+
+               if (fclose(file->fp)) {
+                       PERR("Cannot close file \"%s\": %s\n", file->path->str,
+                               strerror(errno));
+               }
+       }
+
+       if (file->path) {
+               g_string_free(file->path, TRUE);
+       }
+
+       g_free(file);
+}
+
+struct ctf_fs_file *ctf_fs_file_create(struct ctf_fs *ctf_fs)
+{
+       struct ctf_fs_file *file = g_new0(struct ctf_fs_file, 1);
+
+       if (!file) {
+               goto error;
+       }
+
+       file->ctf_fs = ctf_fs;
+       file->path = g_string_new(NULL);
+       if (!file->path) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       ctf_fs_file_destroy(file);
+       file = NULL;
+
+end:
+       return file;
+}
+
+int ctf_fs_file_open(struct ctf_fs *ctf_fs, struct ctf_fs_file *file,
+               const char *mode)
+{
+       int ret = 0;
+       struct stat stat;
+
+       PDBG("Opening file \"%s\" with mode \"%s\"\n", file->path->str, mode);
+       file->fp = fopen(file->path->str, mode);
+       if (!file->fp) {
+               PERR("Cannot open file \"%s\" with mode \"%s\": %s",
+                       file->path->str, mode, strerror(errno));
+               goto error;
+       }
+
+       PDBG("Opened file: %p\n", file->fp);
+
+       if (fstat(fileno(file->fp), &stat)) {
+               PERR("Cannot get file informations: %s\n", strerror(errno));
+               goto error;
+       }
+
+       file->size = stat.st_size;
+       PDBG("  File is %zu bytes\n", file->size);
+       goto end;
+
+error:
+       ret = -1;
+
+       if (file->fp) {
+               if (fclose(file->fp)) {
+                       PERR("Cannot close file \"%s\": %s\n", file->path->str,
+                               strerror(errno));
+               }
+       }
+
+end:
+       return ret;
+}
diff --git a/ctf-reader-proto/ctf-fs-file.h b/ctf-reader-proto/ctf-fs-file.h
new file mode 100644 (file)
index 0000000..5da082e
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef CTF_FS_FILE_H
+#define CTF_FS_FILE_H
+
+/*
+ * Copyright 2016 - Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <glib.h>
+#include <babeltrace/babeltrace-internal.h>
+
+#include "ctf-fs.h"
+
+struct ctf_fs_file {
+       struct ctf_fs *ctf_fs;
+       GString *path;
+       FILE *fp;
+       off_t size;
+};
+
+BT_HIDDEN
+void ctf_fs_file_destroy(struct ctf_fs_file *file);
+
+BT_HIDDEN
+struct ctf_fs_file *ctf_fs_file_create(struct ctf_fs *ctf_fs);
+
+BT_HIDDEN
+int ctf_fs_file_open(struct ctf_fs *ctf_fs, struct ctf_fs_file *file,
+               const char *mode);
+
+#endif /* CTF_FS_FILE_H */
diff --git a/ctf-reader-proto/ctf-fs-metadata.c b/ctf-reader-proto/ctf-fs-metadata.c
new file mode 100644 (file)
index 0000000..bfe2472
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * Copyright 2016 - Philippe Proulx <pproulx@efficios.com>
+ * Copyright 2010-2011 - EfficiOS Inc. and Linux Foundation
+ *
+ * Some functions are based on older functions written by Mathieu Desnoyers.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <babeltrace/compat/uuid.h>
+#include <babeltrace/compat/memstream.h>
+
+#define PRINT_ERR_STREAM       ctf_fs->error_fp
+#define PRINT_PREFIX           "ctf-fs-metadata"
+#include "print.h"
+
+#include "ctf-fs.h"
+#include "ctf-fs-file.h"
+#include "metadata-parsing/ctf-ast.h"
+#include "metadata-parsing/ctf-scanner.h"
+
+#define TSDL_MAGIC     0x75d11d57
+
+struct packet_header {
+       uint32_t magic;
+       uint8_t  uuid[16];
+       uint32_t checksum;
+       uint32_t content_size;
+       uint32_t packet_size;
+       uint8_t  compression_scheme;
+       uint8_t  encryption_scheme;
+       uint8_t  checksum_scheme;
+       uint8_t  major;
+       uint8_t  minor;
+} __attribute__((__packed__));
+
+static struct ctf_fs_file *get_file(struct ctf_fs *ctf_fs,
+               const char *trace_path)
+{
+       struct ctf_fs_file *file = ctf_fs_file_create(ctf_fs);
+
+       if (!file) {
+               goto error;
+       }
+
+       g_string_append(file->path, trace_path);
+       g_string_append(file->path, "/" CTF_FS_METADATA_FILENAME);
+
+       if (ctf_fs_file_open(ctf_fs, file, "rb")) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       if (file) {
+               ctf_fs_file_destroy(file);
+               file = NULL;
+       }
+
+end:
+       return file;
+}
+
+static
+bool is_packetized(struct ctf_fs *ctf_fs, struct ctf_fs_file *file)
+{
+       uint32_t magic;
+       size_t len;
+       int ret = 0;
+
+       len = fread(&magic, sizeof(magic), 1, file->fp);
+       if (len != 1) {
+               goto end;
+       }
+
+       if (magic == TSDL_MAGIC) {
+               ret = 1;
+               ctf_fs->metadata.bo = BYTE_ORDER;
+       } else if (magic == GUINT32_SWAP_LE_BE(TSDL_MAGIC)) {
+               ret = 1;
+               ctf_fs->metadata.bo = BYTE_ORDER == BIG_ENDIAN ?
+                       LITTLE_ENDIAN : BIG_ENDIAN;
+       }
+
+end:
+       rewind(file->fp);
+
+       return ret;
+}
+
+static
+bool is_version_valid(unsigned int major, unsigned int minor)
+{
+       return major == 1 && minor == 8;
+}
+
+static
+int decode_packet(struct ctf_fs *ctf_fs, FILE *in_fp, FILE *out_fp)
+{
+       struct packet_header header;
+       size_t readlen, writelen, toread;
+       uint8_t buf[512 + 1];   /* + 1 for debug-mode \0 */
+       int ret = 0;
+
+       readlen = fread(&header, sizeof(header), 1, in_fp);
+       if (feof(in_fp) != 0) {
+               goto end;
+       }
+       if (readlen < 1) {
+               goto error;
+       }
+
+       if (ctf_fs->metadata.bo != BYTE_ORDER) {
+               header.magic = GUINT32_SWAP_LE_BE(header.magic);
+               header.checksum = GUINT32_SWAP_LE_BE(header.checksum);
+               header.content_size = GUINT32_SWAP_LE_BE(header.content_size);
+               header.packet_size = GUINT32_SWAP_LE_BE(header.packet_size);
+       }
+
+       if (header.compression_scheme) {
+               PERR("Metadata packet compression not supported yet\n");
+               goto error;
+       }
+
+       if (header.encryption_scheme) {
+               PERR("Metadata packet encryption not supported yet\n");
+               goto error;
+       }
+
+       if (header.checksum || header.checksum_scheme) {
+               PERR("Metadata packet checksum verification not supported yet\n");
+               goto error;
+       }
+
+       if (!is_version_valid(header.major, header.minor)) {
+               PERR("Invalid metadata version: %u.%u\n", header.major,
+                       header.minor);
+               goto error;
+       }
+
+       /* Set expected trace UUID if not set; otherwise validate it */
+       if (!ctf_fs->metadata.is_uuid_set) {
+               memcpy(ctf_fs->metadata.uuid, header.uuid, sizeof(header.uuid));
+               ctf_fs->metadata.is_uuid_set = true;
+       } else if (bt_uuid_compare(header.uuid, ctf_fs->metadata.uuid)) {
+               PERR("Metadata UUID mismatch between packets of the same file\n");
+               goto error;
+       }
+
+       if ((header.content_size / CHAR_BIT) < sizeof(header)) {
+               PERR("Bad metadata packet content size: %u\n",
+                       header.content_size);
+               goto error;
+       }
+
+       toread = header.content_size / CHAR_BIT - sizeof(header);
+
+       for (;;) {
+               readlen = fread(buf, sizeof(uint8_t),
+                       MIN(sizeof(buf) - 1, toread), in_fp);
+               if (ferror(in_fp)) {
+                       PERR("Cannot read metadata packet buffer (at position %ld)\n",
+                               ftell(in_fp));
+                       goto error;
+               }
+
+               writelen = fwrite(buf, sizeof(uint8_t), readlen, out_fp);
+               if (writelen < readlen || ferror(out_fp)) {
+                       PERR("Cannot write decoded metadata text to buffer\n");
+                       goto error;
+               }
+
+               toread -= readlen;
+               if (toread == 0) {
+                       int fseek_ret;
+
+                       /* Read leftover padding */
+                       toread = (header.packet_size - header.content_size) /
+                               CHAR_BIT;
+                       fseek_ret = fseek(in_fp, toread, SEEK_CUR);
+                       if (fseek_ret < 0) {
+                               PWARN("Missing padding at the end of the metadata file\n");
+                       }
+
+                       break;
+               }
+       }
+
+       goto end;
+
+error:
+       ret = -1;
+
+end:
+       return ret;
+}
+
+static
+int packetized_file_to_buf(struct ctf_fs *ctf_fs, struct ctf_fs_file *file,
+               uint8_t **buf)
+{
+       FILE *out_fp;
+       size_t size;
+       int ret = 0;
+       int tret;
+       size_t packet_index = 0;
+
+       out_fp = babeltrace_open_memstream((char **) buf, &size);
+       if (out_fp == NULL) {
+               PERR("Cannot open memory stream: %s\n", strerror(errno));
+               goto error;
+       }
+
+       for (;;) {
+               if (feof(file->fp) != 0) {
+                       break;
+               }
+
+               tret = decode_packet(ctf_fs, file->fp, out_fp);
+               if (tret) {
+                       PERR("Cannot decode packet #%zu\n", packet_index);
+                       goto error;
+               }
+
+               packet_index++;
+       }
+
+       /* Make sure the whole string ends with a null character */
+       tret = fputc('\0', out_fp);
+       if (tret == EOF) {
+               PERR("Cannot append '\\0' to the decoded metadata buffer\n");
+               goto error;
+       }
+
+       /* Close stream, which also flushes the buffer */
+       ret = babeltrace_close_memstream((char **) buf, &size, out_fp);
+       if (ret < 0) {
+               PERR("Cannot close memory stream: %s\n", strerror(errno));
+               goto error;
+       }
+
+       goto end;
+
+error:
+       ret = -1;
+
+       if (out_fp) {
+               babeltrace_close_memstream((char **) buf, &size, out_fp);
+       }
+
+       if (*buf) {
+               free(*buf);
+       }
+
+end:
+       return ret;
+}
+
+void ctf_fs_metadata_set_trace(struct ctf_fs *ctf_fs)
+{
+       int ret = 0;
+       struct ctf_fs_file *file = get_file(ctf_fs, ctf_fs->trace_path->str);
+       struct ctf_scanner *scanner = NULL;
+       uint8_t *buf = NULL;
+       char *cbuf;
+
+       if (!file) {
+               PERR("Cannot create metadata file object\n");
+               goto error;
+       }
+
+       if (babeltrace_debug) {
+               // yydebug = 1;
+       }
+
+       if (is_packetized(ctf_fs, file)) {
+               PDBG("Metadata file \"%s\" is packetized\n", file->path->str);
+               ret = packetized_file_to_buf(ctf_fs, file, &buf);
+               if (ret) {
+                       // log: details
+                       goto error;
+               }
+
+               cbuf = (char *) buf;
+               ctf_fs->metadata.text = (char *) cbuf;
+
+               /* Convert the real file pointer to a memory file pointer */
+               ret = fclose(file->fp);
+               file->fp = NULL;
+               if (ret) {
+                       PERR("Cannot close file \"%s\": %s\n", file->path->str,
+                               strerror(errno));
+                       goto error;
+               }
+
+               file->fp = babeltrace_fmemopen(cbuf, strlen(cbuf), "rb");
+               if (!file->fp) {
+                       PERR("Cannot memory-open metadata buffer: %s\n",
+                               strerror(errno));
+                       goto error;
+               }
+       } else {
+               unsigned int major, minor;
+               ssize_t nr_items;
+
+               PDBG("Metadata file \"%s\" is plain text\n", file->path->str);
+
+               /* Check text-only metadata header and version */
+               nr_items = fscanf(file->fp, "/* CTF %10u.%10u", &major, &minor);
+               if (nr_items < 2) {
+                       PWARN("Ill-shapen or missing \"/* CTF major.minor\" header in plain text metadata file\n");
+               }
+               PDBG("Metadata version: %u.%u\n", major, minor);
+
+               if (!is_version_valid(major, minor)) {
+                       PERR("Invalid metadata version: %u.%u\n", major, minor);
+                       goto error;
+               }
+
+               rewind(file->fp);
+       }
+
+       /* Allocate a scanner and append the metadata text content */
+       scanner = ctf_scanner_alloc();
+       if (!scanner) {
+               PERR("Cannot allocate a metadata lexical scanner\n");
+               goto error;
+       }
+
+       ret = ctf_scanner_append_ast(scanner, file->fp);
+       if (ret) {
+               PERR("Cannot create the metadata AST\n");
+               goto error;
+       }
+
+       ret = ctf_visitor_semantic_check(stderr, 0, &scanner->ast->root);
+       if (ret) {
+               PERR("Metadata semantic validation failed\n");
+               goto error;
+       }
+
+       ret = ctf_visitor_generate_ir(ctf_fs->error_fp, &scanner->ast->root,
+               &ctf_fs->metadata.trace);
+       if (ret) {
+               PERR("Cannot create trace object from metadata AST\n");
+               goto error;
+       }
+
+       goto end;
+
+error:
+       if (ctf_fs->metadata.text) {
+               free(ctf_fs->metadata.text);
+               ctf_fs->metadata.text = NULL;
+       }
+
+end:
+       if (file) {
+               ctf_fs_file_destroy(file);
+       }
+
+       if (scanner) {
+               ctf_scanner_free(scanner);
+       }
+}
+
+int ctf_fs_metadata_init(struct ctf_fs_metadata *metadata)
+{
+       /* Nothing to initialize for the moment */
+       return 0;
+}
+
+void ctf_fs_metadata_deinit(struct ctf_fs_metadata *metadata)
+{
+       if (metadata->text) {
+               free(metadata->text);
+       }
+
+       if (metadata->trace) {
+               BT_PUT(metadata->trace);
+       }
+}
diff --git a/ctf-reader-proto/ctf-fs-metadata.h b/ctf-reader-proto/ctf-fs-metadata.h
new file mode 100644 (file)
index 0000000..ed9b785
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef CTF_FS_METADATA_H
+#define CTF_FS_METADATA_H
+
+/*
+ * Copyright 2016 - Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <glib.h>
+#include <babeltrace/babeltrace-internal.h>
+#include <babeltrace/ctf-ir/trace.h>
+
+#define CTF_FS_METADATA_FILENAME       "metadata"
+
+struct ctf_fs;
+
+struct ctf_fs_metadata {
+       struct bt_ctf_trace *trace;
+       uint8_t uuid[16];
+       bool is_uuid_set;
+       int bo;
+       char *text;
+};
+
+BT_HIDDEN
+int ctf_fs_metadata_init(struct ctf_fs_metadata *metadata);
+
+BT_HIDDEN
+void ctf_fs_metadata_deinit(struct ctf_fs_metadata *metadata);
+
+BT_HIDDEN
+void ctf_fs_metadata_set_trace(struct ctf_fs *ctf_fs);
+
+#endif /* CTF_FS_METADATA_H */
diff --git a/ctf-reader-proto/ctf-fs.c b/ctf-reader-proto/ctf-fs.c
new file mode 100644 (file)
index 0000000..5bb022d
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2016 - Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <glib.h>
+
+#define PRINT_ERR_STREAM       ctf_fs->error_fp
+#define PRINT_PREFIX           "ctf-fs"
+#include "print.h"
+
+#include "ctf-fs.h"
+#include "ctf-fs-file.h"
+#include "ctf-fs-metadata.h"
+#include "ctf-fs-data-stream.h"
+
+bool ctf_fs_debug = false;
+
+static void ctf_fs_destroy(struct ctf_fs *ctf_fs)
+{
+       if (!ctf_fs) {
+               return;
+       }
+
+       if (ctf_fs->trace_path) {
+               g_string_free(ctf_fs->trace_path, TRUE);
+       }
+
+       ctf_fs_metadata_deinit(&ctf_fs->metadata);
+       ctf_fs_data_stream_deinit(&ctf_fs->data_stream);
+       g_free(ctf_fs);
+}
+
+static struct ctf_fs *ctf_fs_create(const char *trace_path)
+{
+       struct ctf_fs *ctf_fs = g_new0(struct ctf_fs, 1);
+
+       if (!ctf_fs) {
+               goto error;
+       }
+
+       ctf_fs->trace_path = g_string_new(trace_path);
+       if (!ctf_fs->trace_path) {
+               goto error;
+       }
+
+       ctf_fs->error_fp = stderr;
+       ctf_fs->page_size = getpagesize();
+
+       if (ctf_fs_metadata_init(&ctf_fs->metadata)) {
+               PERR("Cannot initialize metadata structure\n");
+               goto error;
+       }
+
+       if (ctf_fs_data_stream_init(ctf_fs, &ctf_fs->data_stream)) {
+               PERR("Cannot initialize data stream structure\n");
+               goto error;
+       }
+
+       goto end;
+
+error:
+       ctf_fs_destroy(ctf_fs);
+       ctf_fs = NULL;
+
+end:
+       return ctf_fs;
+}
+
+void ctf_fs_init(void)
+{
+       if (g_strcmp0(getenv("CTF_FS_DEBUG"), "1") == 0) {
+               ctf_fs_debug = true;
+       }
+}
+
+void ctf_fs_test(const char *trace_path)
+{
+       ctf_fs_init();
+
+       struct ctf_fs *ctf_fs = ctf_fs_create(trace_path);
+       struct bt_ctf_notif_iter_notif *notification;
+
+       if (!ctf_fs) {
+               return;
+       }
+
+       /* Set trace from metadata file */
+       ctf_fs_metadata_set_trace(ctf_fs);
+       ctf_fs_data_stream_open_streams(ctf_fs);
+
+       while (true) {
+               int ret = ctf_fs_data_stream_get_next_notification(ctf_fs, &notification);
+               assert(ret == 0);
+
+               if (!notification) {
+                       goto end;
+               }
+
+               switch (notification->type) {
+               case BT_CTF_NOTIF_ITER_NOTIF_NEW_PACKET:
+               {
+                       struct bt_ctf_notif_iter_notif_new_packet *notif =
+                               (struct bt_ctf_notif_iter_notif_new_packet *) notification;
+                       break;
+               }
+               case BT_CTF_NOTIF_ITER_NOTIF_EVENT:
+               {
+                       struct bt_ctf_notif_iter_notif_event *notif =
+                               (struct bt_ctf_notif_iter_notif_event *) notification;
+                       break;
+               }
+               case BT_CTF_NOTIF_ITER_NOTIF_END_OF_PACKET:
+               {
+                       struct bt_ctf_notif_iter_notif_end_of_packet *notif =
+                               (struct bt_ctf_notif_iter_notif_end_of_packet *) notification;
+                       break;
+               }
+               default:
+                       break;
+               }
+
+               bt_ctf_notif_iter_notif_destroy(notification);
+       }
+
+end:
+       ctf_fs_destroy(ctf_fs);
+}
diff --git a/ctf-reader-proto/ctf-fs.h b/ctf-reader-proto/ctf-fs.h
new file mode 100644 (file)
index 0000000..3206e9e
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef CTF_FS_H
+#define CTF_FS_H
+
+/*
+ * Copyright 2016 - Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <babeltrace/babeltrace-internal.h>
+#include <babeltrace/ctf-ir/trace.h>
+
+#include "ctf-fs-metadata.h"
+#include "ctf-fs-data-stream.h"
+
+extern bool ctf_fs_debug;
+
+struct ctf_fs {
+       GString *trace_path;
+       FILE *error_fp;
+       int page_size;
+       struct ctf_fs_metadata metadata;
+       struct ctf_fs_data_stream data_stream;
+};
+
+void ctf_fs_test(const char *trace_path);
+
+#endif /* CTF_FS_H */
diff --git a/ctf-reader-proto/ctf-notif-iter.gdb b/ctf-reader-proto/ctf-notif-iter.gdb
new file mode 100644 (file)
index 0000000..b536943
--- /dev/null
@@ -0,0 +1,22 @@
+define ctf-notif-iter-show-stack
+    if (stack_empty($arg0))
+        printf "stack is empty!\n"
+    else
+        set $stack_size = stack_size($arg0)
+        set $stack_at = (int) ($stack_size - 1)
+        printf "%3s    %10s    %3s\n", "pos", "base addr", "idx"
+
+        while ($stack_at >= 0)
+            set $stack_entry = (struct stack_entry *) g_ptr_array_index($arg0->entries, $stack_at)
+
+            if ($stack_at == $stack_size - 1)
+                printf "%3d    %10p    %3d  <-- top\n", $stack_at, \
+                    $stack_entry->base, $stack_entry->index
+            else
+                printf "%3d    %10p    %3d\n", $stack_at, \
+                    $stack_entry->base, $stack_entry->index
+            end
+            set $stack_at = $stack_at - 1
+        end
+    end
+end
diff --git a/ctf-reader-proto/ctf-notif-iter/Makefile.am b/ctf-reader-proto/ctf-notif-iter/Makefile.am
new file mode 100644 (file)
index 0000000..fe90136
--- /dev/null
@@ -0,0 +1,6 @@
+AM_CFLAGS = $(PACKAGE_CFLAGS)
+AM_CPPFLAGS =  -I$(top_srcdir)/include
+
+noinst_LTLIBRARIES = libctf-notif-iter.la
+
+libctf_notif_iter_la_SOURCES = ctf-notif-iter.c
diff --git a/ctf-reader-proto/ctf-notif-iter/ctf-notif-iter.c b/ctf-reader-proto/ctf-notif-iter/ctf-notif-iter.c
new file mode 100644 (file)
index 0000000..9fefbd8
--- /dev/null
@@ -0,0 +1,1938 @@
+/*
+ * Babeltrace - CTF notification iterator
+ *                  ¯¯¯¯¯        ¯¯¯¯
+ * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation
+ * Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <string.h>
+#include <babeltrace/ctf-ir/field-types.h>
+#include <babeltrace/ctf-ir/field-path.h>
+#include <babeltrace/ctf-ir/fields.h>
+#include <babeltrace/ctf-ir/stream-class.h>
+#include <babeltrace/ctf-ir/packet.h>
+#include <babeltrace/ctf-ir/stream.h>
+#include <babeltrace/ctf-ir/clock.h>
+#include <babeltrace/ctf-ir/event-class.h>
+#include <babeltrace/ref.h>
+#include <glib.h>
+
+#define PRINT_ERR_STREAM       notit->err_stream
+#define PRINT_PREFIX           "ctf-notif-iter"
+#include "print.h"
+
+#include "ctf-notif-iter.h"
+#include "../ctf-btr/ctf-btr.h"
+
+#define BYTES_TO_BITS(x)               ((x) * 8)
+
+struct bt_ctf_notif_iter;
+
+/* A visit stack entry */
+struct stack_entry {
+       /*
+        * Current base field, one of:
+        *
+        *   * string
+        *   * structure
+        *   * array
+        *   * sequence
+        *   * variant
+        *
+        * Field is owned by this.
+        */
+       struct bt_ctf_field *base;
+
+       /* index of next field to set */
+       size_t index;
+};
+
+/* Visit stack */
+struct stack {
+       /* Entries (struct stack_entry *) (top is last element) */
+       GPtrArray *entries;
+
+       /* Link to owner */
+       struct bt_ctf_notif_iter *notit;
+};
+
+/* State */
+enum state {
+       STATE_INIT,
+       STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN,
+       STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE,
+       STATE_AFTER_TRACE_PACKET_HEADER,
+       STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN,
+       STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE,
+       STATE_AFTER_STREAM_PACKET_CONTEXT,
+       STATE_EMIT_NOTIF_NEW_PACKET,
+       STATE_DSCOPE_STREAM_EVENT_HEADER_BEGIN,
+       STATE_DSCOPE_STREAM_EVENT_HEADER_CONTINUE,
+       STATE_AFTER_STREAM_EVENT_HEADER,
+       STATE_DSCOPE_STREAM_EVENT_CONTEXT_BEGIN,
+       STATE_DSCOPE_STREAM_EVENT_CONTEXT_CONTINUE,
+       STATE_DSCOPE_EVENT_CONTEXT_BEGIN,
+       STATE_DSCOPE_EVENT_CONTEXT_CONTINUE,
+       STATE_DSCOPE_EVENT_PAYLOAD_BEGIN,
+       STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE,
+       STATE_EMIT_NOTIF_EVENT,
+       STATE_EMIT_NOTIF_END_OF_PACKET,
+       STATE_SKIP_PACKET_PADDING,
+};
+
+/* CTF notification iterator */
+struct bt_ctf_notif_iter {
+       /* Visit stack */
+       struct stack *stack;
+
+       /* Error stream (may be NULL) */
+       FILE *err_stream;
+
+       /*
+        * Current dynamic scope field pointer.
+        *
+        * This is set when a dynamic scope field is first created by
+        * btr_compound_begin_cb(). It points to one of the fields in
+        * dscopes below.
+        */
+       struct bt_ctf_field **cur_dscope_field;
+
+       /* Trace and classes (owned by this) */
+       struct {
+               struct bt_ctf_trace *trace;
+               struct bt_ctf_stream_class *stream_class;
+               struct bt_ctf_event_class *event_class;
+       } meta;
+
+       /* Current packet (NULL if not created yet) */
+       struct bt_ctf_packet *packet;
+
+       /* Database of current dynamic scopes (owned by this) */
+       struct {
+               struct bt_ctf_field *trace_packet_header;
+               struct bt_ctf_field *stream_packet_context;
+               struct bt_ctf_field *stream_event_header;
+               struct bt_ctf_field *stream_event_context;
+               struct bt_ctf_field *event_context;
+               struct bt_ctf_field *event_payload;
+       } dscopes;
+
+       /* Current state */
+       enum state state;
+
+       /* User buffer stuff */
+       struct {
+               /* Last address provided by medium */
+               const uint8_t *addr;
+
+               /* Buffer size provided by medium (bytes) */
+               size_t sz;
+
+               /* Offset within whole packet of addr (bits) */
+               size_t packet_offset;
+
+               /* Current position from addr (bits) */
+               size_t at;
+       } buf;
+
+       /* Binary type reader */
+       struct bt_ctf_btr *btr;
+
+       /* Medium stuff */
+       struct {
+               struct bt_ctf_notif_iter_medium_ops medops;
+               size_t max_request_sz;
+               void *data;
+       } medium;
+
+       /* Current packet size (bits) (-1 if unknown) */
+       size_t cur_packet_size;
+
+       /* Current content size (bits) (-1 if unknown) */
+       size_t cur_content_size;
+};
+
+static
+void stack_entry_free_func(gpointer data)
+{
+       struct stack_entry *entry = data;
+
+       bt_put(entry->base);
+       g_free(entry);
+}
+
+static
+struct stack *stack_new(struct bt_ctf_notif_iter *notit)
+{
+       struct stack *stack = NULL;
+
+       stack = g_new0(struct stack, 1);
+       if (!stack) {
+               goto error;
+       }
+
+       stack->entries = g_ptr_array_new_with_free_func(stack_entry_free_func);
+       if (!stack->entries) {
+               goto error;
+       }
+
+       stack->notit = notit;
+
+       return stack;
+
+error:
+       g_free(stack);
+
+       return NULL;
+}
+
+static
+void stack_destroy(struct stack *stack)
+{
+       assert(stack);
+       g_ptr_array_free(stack->entries, TRUE);
+       g_free(stack);
+}
+
+static
+int stack_push(struct stack *stack, struct bt_ctf_field *base)
+{
+       int ret = 0;
+       struct stack_entry *entry;
+       struct bt_ctf_notif_iter *notit;
+
+       assert(stack);
+       assert(base);
+       notit = stack->notit;
+       entry = g_new0(struct stack_entry, 1);
+       if (!entry) {
+               PERR("Cannot create new stack entry\n");
+               ret = -1;
+               goto end;
+       }
+
+       entry->base = bt_get(base);
+       g_ptr_array_add(stack->entries, entry);
+
+end:
+       return ret;
+}
+
+static inline
+unsigned int stack_size(struct stack *stack)
+{
+       assert(stack);
+
+       return stack->entries->len;
+}
+
+static
+void stack_pop(struct stack *stack)
+{
+       assert(stack);
+       assert(stack_size(stack));
+       g_ptr_array_remove_index(stack->entries, stack->entries->len - 1);
+}
+
+static inline
+struct stack_entry *stack_top(struct stack *stack)
+{
+       assert(stack);
+       assert(stack_size(stack));
+
+       return g_ptr_array_index(stack->entries, stack->entries->len - 1);
+}
+
+static inline
+bool stack_empty(struct stack *stack)
+{
+       return stack_size(stack) == 0;
+}
+
+static
+void stack_clear(struct stack *stack)
+{
+       assert(stack);
+
+       if (!stack_empty(stack)) {
+               g_ptr_array_remove_range(stack->entries, 0, stack_size(stack));
+       }
+
+       assert(stack_empty(stack));
+}
+
+static inline
+enum bt_ctf_notif_iter_status notif_iter_status_from_m_status(
+               enum bt_ctf_notif_iter_medium_status m_status)
+{
+       return m_status;
+}
+
+static inline
+size_t buf_size_bits(struct bt_ctf_notif_iter *notit)
+{
+       return BYTES_TO_BITS(notit->buf.sz);
+}
+
+static inline
+size_t buf_available_bits(struct bt_ctf_notif_iter *notit)
+{
+       return buf_size_bits(notit) - notit->buf.at;
+}
+
+static inline
+size_t packet_at(struct bt_ctf_notif_iter *notit)
+{
+       return notit->buf.packet_offset + notit->buf.at;
+}
+
+static inline
+size_t remaining_content_bits(struct bt_ctf_notif_iter *notit)
+{
+       if (notit->cur_content_size == -1) {
+               return -1;
+       }
+
+       return notit->cur_content_size - packet_at(notit);
+}
+
+static inline
+size_t remaining_packet_bits(struct bt_ctf_notif_iter *notit)
+{
+       if (notit->cur_packet_size == -1) {
+               return -1;
+       }
+
+       return notit->cur_packet_size - packet_at(notit);
+}
+
+static inline
+void buf_consume_bits(struct bt_ctf_notif_iter *notit, size_t incr)
+{
+       notit->buf.at += incr;
+}
+
+static inline
+bool buf_has_enough_bits(struct bt_ctf_notif_iter *notit, size_t sz)
+{
+       return buf_available_bits(notit) >= sz;
+}
+
+static
+enum bt_ctf_notif_iter_status request_medium_bytes(struct bt_ctf_notif_iter *notit)
+{
+       uint8_t *buffer_addr;
+       size_t buffer_sz;
+       enum bt_ctf_notif_iter_medium_status m_status;
+
+       m_status = notit->medium.medops.request_bytes(
+               notit->medium.max_request_sz, &buffer_addr,
+               &buffer_sz, notit->medium.data);
+       if (m_status == BT_CTF_NOTIF_ITER_MEDIUM_STATUS_OK) {
+               assert(buffer_sz != 0);
+
+               /* New packet offset is old one + old size (in bits) */
+               notit->buf.packet_offset += buf_size_bits(notit);
+
+               /* Restart at the beginning of the new medium buffer */
+               notit->buf.at = 0;
+
+               /* New medium buffer size */
+               notit->buf.sz = buffer_sz;
+
+               /* New medium buffer address */
+               notit->buf.addr = buffer_addr;
+       }
+
+       return notif_iter_status_from_m_status(m_status);
+}
+
+static inline
+enum bt_ctf_notif_iter_status buf_ensure_available_bits(
+               struct bt_ctf_notif_iter *notit)
+{
+       enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
+
+       if (buf_available_bits(notit) == 0) {
+               /*
+                * This _cannot_ return BT_CTF_NOTIF_ITER_STATUS_OK
+                * _and_ no bits.
+                */
+               status = request_medium_bytes(notit);
+       }
+
+       return status;
+}
+
+static
+enum bt_ctf_notif_iter_status read_dscope_begin_state(
+               struct bt_ctf_notif_iter *notit,
+               struct bt_ctf_field_type *dscope_field_type,
+               enum state done_state, enum state continue_state,
+               struct bt_ctf_field **dscope_field)
+{
+       enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
+       enum bt_ctf_btr_status btr_status;
+       size_t consumed_bits;
+
+       status = buf_ensure_available_bits(notit);
+       if (status != BT_CTF_NOTIF_ITER_STATUS_OK) {
+               goto end;
+       }
+
+       bt_put(*dscope_field);
+       notit->cur_dscope_field = dscope_field;
+       consumed_bits = bt_ctf_btr_start(notit->btr, dscope_field_type,
+               notit->buf.addr, notit->buf.at, packet_at(notit),
+               notit->buf.sz, &btr_status);
+
+       switch (btr_status) {
+       case BT_CTF_BTR_STATUS_OK:
+               /* type was read completely */
+               notit->state = done_state;
+               break;
+       case BT_CTF_BTR_STATUS_EOF:
+               notit->state = continue_state;
+               break;
+       default:
+               PERR("Binary type reader failed to start\n");
+               status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+       /* Consume bits now since we know we're not in an error state */
+       buf_consume_bits(notit, consumed_bits);
+
+end:
+       return status;
+}
+
+static
+enum bt_ctf_notif_iter_status read_dscope_continue_state(
+               struct bt_ctf_notif_iter *notit, enum state done_state)
+{
+       enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
+       enum bt_ctf_btr_status btr_status;
+       size_t consumed_bits;
+
+       status = buf_ensure_available_bits(notit);
+       if (status != BT_CTF_NOTIF_ITER_STATUS_OK) {
+               goto end;
+       }
+
+       consumed_bits = bt_ctf_btr_continue(notit->btr, notit->buf.addr,
+               notit->buf.sz, &btr_status);
+
+       switch (btr_status) {
+       case BT_CTF_BTR_STATUS_OK:
+               /* Type was read completely */
+               notit->state = done_state;
+               break;
+       case BT_CTF_BTR_STATUS_EOF:
+               /* Stay in this continue state */
+               break;
+       default:
+               PERR("Binary type reader failed to continue\n");
+               status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+       /* Consume bits now since we know we're not in an error state */
+       buf_consume_bits(notit, consumed_bits);
+
+end:
+       return status;
+}
+
+static
+void put_event_dscopes(struct bt_ctf_notif_iter *notit)
+{
+       BT_PUT(notit->dscopes.stream_event_header);
+       BT_PUT(notit->dscopes.stream_event_context);
+       BT_PUT(notit->dscopes.event_context);
+       BT_PUT(notit->dscopes.event_payload);
+}
+
+static
+void put_all_dscopes(struct bt_ctf_notif_iter *notit)
+{
+       BT_PUT(notit->dscopes.trace_packet_header);
+       BT_PUT(notit->dscopes.stream_packet_context);
+       put_event_dscopes(notit);
+}
+
+static
+enum bt_ctf_notif_iter_status read_packet_header_begin_state(
+               struct bt_ctf_notif_iter *notit)
+{
+       enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
+       struct bt_ctf_field_type *packet_header_type;
+
+       /* Reset all dynamic scopes since we're reading a new packet */
+       put_all_dscopes(notit);
+       BT_PUT(notit->packet);
+       BT_PUT(notit->meta.stream_class);
+       BT_PUT(notit->meta.event_class);
+
+       /* Packet header type is common to the whole trace */
+       packet_header_type = bt_ctf_trace_get_packet_header_type(
+               notit->meta.trace);
+       if (!packet_header_type) {
+               PERR("Failed to retrieve trace's packet header type\n");
+               status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+       status = read_dscope_begin_state(notit, packet_header_type,
+               STATE_AFTER_TRACE_PACKET_HEADER,
+               STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE,
+               &notit->dscopes.trace_packet_header);
+
+end:
+       BT_PUT(packet_header_type);
+
+       return status;
+}
+
+static
+enum bt_ctf_notif_iter_status read_packet_header_continue_state(
+               struct bt_ctf_notif_iter *notit)
+{
+       return read_dscope_continue_state(notit,
+               STATE_AFTER_TRACE_PACKET_HEADER);
+}
+
+static inline
+bool is_struct_type(struct bt_ctf_field_type *field_type)
+{
+       return bt_ctf_field_type_get_type_id(field_type) == BT_CTF_TYPE_ID_STRUCT;
+}
+
+static inline
+bool is_variant_type(struct bt_ctf_field_type *field_type)
+{
+       return bt_ctf_field_type_get_type_id(field_type) == BT_CTF_TYPE_ID_VARIANT;
+}
+
+static inline
+enum bt_ctf_notif_iter_status set_current_stream_class(struct bt_ctf_notif_iter *notit)
+{
+       enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
+       struct bt_ctf_field_type *packet_header_type;
+       struct bt_ctf_field_type *stream_id_field_type = NULL;
+       uint64_t stream_id;
+
+       /* Is there any "stream_id" field in the packet header? */
+       packet_header_type = bt_ctf_trace_get_packet_header_type(
+               notit->meta.trace);
+       if (!packet_header_type) {
+               PERR("Failed to retrieve trace's packet header type\n");
+               status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+       assert(is_struct_type(packet_header_type));
+
+       // TODO: optimalize!
+       stream_id_field_type =
+               bt_ctf_field_type_structure_get_field_type_by_name(
+                       packet_header_type, "stream_id");
+       if (stream_id_field_type) {
+               /* Find appropriate stream class using current stream ID */
+               struct bt_ctf_field *stream_id_field = NULL;
+               int ret;
+
+               assert(notit->dscopes.trace_packet_header);
+
+               // TODO: optimalize!
+               stream_id_field = bt_ctf_field_structure_get_field(
+                       notit->dscopes.trace_packet_header, "stream_id");
+               assert(stream_id_field);
+               ret = bt_ctf_field_unsigned_integer_get_value(
+                       stream_id_field, &stream_id);
+               assert(!ret);
+               BT_PUT(stream_id_field);
+       } else {
+               /* Only one stream: pick the first stream class */
+               assert(bt_ctf_trace_get_stream_class_count(
+                       notit->meta.trace) == 1);
+               stream_id = 0;
+       }
+
+       BT_PUT(notit->meta.stream_class);
+
+       // TODO: get by ID
+       notit->meta.stream_class = bt_ctf_trace_get_stream_class(
+               notit->meta.trace, stream_id);
+       if (!notit->meta.stream_class) {
+               PERR("Cannot find stream class with ID %" PRIu64 "\n",
+                       stream_id);
+               status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+end:
+       BT_PUT(packet_header_type);
+       BT_PUT(stream_id_field_type);
+
+       return status;
+}
+
+static
+enum bt_ctf_notif_iter_status after_packet_header_state(
+               struct bt_ctf_notif_iter *notit)
+{
+       enum bt_ctf_notif_iter_status status;
+
+       status = set_current_stream_class(notit);
+       if (status == BT_CTF_NOTIF_ITER_STATUS_OK) {
+               notit->state = STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN;
+       }
+
+       return status;
+}
+
+static
+enum bt_ctf_notif_iter_status read_packet_context_begin_state(
+               struct bt_ctf_notif_iter *notit)
+{
+       enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
+       struct bt_ctf_field_type *packet_context_type;
+
+       assert(notit->meta.stream_class);
+       packet_context_type = bt_ctf_stream_class_get_packet_context_type(
+               notit->meta.stream_class);
+       if (!packet_context_type) {
+               PERR("Failed to retrieve stream class's packet context\n");
+               status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+       status = read_dscope_begin_state(notit, packet_context_type,
+               STATE_AFTER_STREAM_PACKET_CONTEXT,
+               STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE,
+               &notit->dscopes.stream_packet_context);
+
+end:
+       BT_PUT(packet_context_type);
+
+       return status;
+}
+
+static
+enum bt_ctf_notif_iter_status read_packet_context_continue_state(
+               struct bt_ctf_notif_iter *notit)
+{
+       return read_dscope_continue_state(notit,
+               STATE_AFTER_STREAM_PACKET_CONTEXT);
+}
+
+static inline
+enum bt_ctf_notif_iter_status set_current_packet_content_sizes(
+               struct bt_ctf_notif_iter *notit)
+{
+       enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
+       struct bt_ctf_field *packet_size_field = NULL;
+       struct bt_ctf_field *content_size_field = NULL;
+       uint64_t content_size = -1, packet_size = -1;
+
+       assert(notit->dscopes.stream_packet_context);
+
+       // TODO: optimalize!
+       packet_size_field = bt_ctf_field_structure_get_field(
+               notit->dscopes.stream_packet_context, "packet_size");
+       content_size_field = bt_ctf_field_structure_get_field(
+               notit->dscopes.stream_packet_context, "content_size");
+       if (packet_size_field) {
+               int ret = bt_ctf_field_unsigned_integer_get_value(
+                       packet_size_field, &packet_size);
+               assert(!ret);
+
+               if (packet_size == 0) {
+                       PERR("Decoded packet size is 0\n");
+                       status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+                       goto end;
+               } else if ((packet_size % 8) != 0) {
+                       PERR("Decoded packet size is not a multiple of 8\n");
+                       status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+                       goto end;
+               }
+       }
+       if (content_size_field) {
+               int ret = bt_ctf_field_unsigned_integer_get_value(
+                       content_size_field, &content_size);
+               assert(!ret);
+       } else {
+               content_size = packet_size;
+       }
+
+       notit->cur_packet_size = packet_size;
+       notit->cur_content_size = content_size;
+
+end:
+       BT_PUT(packet_size_field);
+       BT_PUT(content_size_field);
+
+       return status;
+}
+
+static
+enum bt_ctf_notif_iter_status after_packet_context_state(
+               struct bt_ctf_notif_iter *notit)
+{
+       enum bt_ctf_notif_iter_status status;
+
+       status = set_current_packet_content_sizes(notit);
+       if (status == BT_CTF_NOTIF_ITER_STATUS_OK) {
+               notit->state = STATE_EMIT_NOTIF_NEW_PACKET;
+       }
+
+       return status;
+}
+
+static
+enum bt_ctf_notif_iter_status read_event_header_begin_state(
+               struct bt_ctf_notif_iter *notit)
+{
+       enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
+       struct bt_ctf_field_type *event_header_type = NULL;
+
+       /* Check if we have some content left */
+       if (notit->cur_content_size >= 0) {
+               if (packet_at(notit) == notit->cur_content_size) {
+                       /* No more events! */
+                       notit->state = STATE_EMIT_NOTIF_END_OF_PACKET;
+                       goto end;
+               } else if (packet_at(notit) > notit->cur_content_size) {
+                       /* That's not supposed to happen */
+                       PERR("Cursor passed packet's content size:\n");
+                       PERR("  Decoded content size: %zu\n",
+                               notit->cur_content_size);
+                       PERR("  Cursor position:      %zu\n", packet_at(notit));
+                       status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+                       goto end;
+               }
+       }
+
+       event_header_type = bt_ctf_stream_class_get_event_header_type(
+               notit->meta.stream_class);
+       if (!event_header_type) {
+               PERR("Failed to retrieve stream class's event header type\n");
+               status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+       put_event_dscopes(notit);
+       status = read_dscope_begin_state(notit, event_header_type,
+               STATE_AFTER_STREAM_EVENT_HEADER,
+               STATE_DSCOPE_STREAM_EVENT_HEADER_CONTINUE,
+               &notit->dscopes.stream_event_header);
+
+end:
+       BT_PUT(event_header_type);
+
+       return status;
+}
+
+static
+enum bt_ctf_notif_iter_status read_event_header_continue_state(
+               struct bt_ctf_notif_iter *notit)
+{
+       return read_dscope_continue_state(notit,
+               STATE_AFTER_STREAM_EVENT_HEADER);
+}
+
+static inline
+enum bt_ctf_notif_iter_status set_current_event_class(struct bt_ctf_notif_iter *notit)
+{
+       /*
+        * The assert() calls in this function are okay because it is
+        * assumed here that all the metadata objects have been
+        * validated for CTF correctness before decoding actual streams.
+        */
+
+       enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
+       struct bt_ctf_field_type *event_header_type;
+       struct bt_ctf_field_type *id_field_type = NULL;
+       struct bt_ctf_field_type *v_field_type = NULL;
+       uint64_t event_id = -1ULL;
+       int ret;
+
+       event_header_type = bt_ctf_stream_class_get_event_header_type(
+               notit->meta.stream_class);
+       if (!event_header_type) {
+               PERR("Failed to retrieve stream class's event header type\n");
+               status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+       /* Is there any "id"/"v" field in the event header? */
+       assert(is_struct_type(event_header_type));
+       id_field_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               event_header_type, "id");
+       v_field_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               event_header_type, "v");
+       assert(notit->dscopes.stream_event_header);
+       if (v_field_type) {
+               /*
+                *  _   _____ _____
+                * | | |_   _|_   _| __   __ _
+                * | |   | |   | || '_ \ / _` |
+                * | |___| |   | || | | | (_| |  S P E C I A L
+                * |_____|_|   |_||_| |_|\__, |  C A S E ™
+                *                       |___/
+                */
+               struct bt_ctf_field *v_field = NULL;
+               struct bt_ctf_field *v_struct_field = NULL;
+               struct bt_ctf_field *v_struct_id_field = NULL;
+
+               // TODO: optimalize!
+               v_field = bt_ctf_field_structure_get_field(
+                       notit->dscopes.stream_event_header, "v");
+               assert(v_field);
+
+               v_struct_field =
+                       bt_ctf_field_variant_get_current_field(v_field);
+               if (!v_struct_field) {
+                       goto end_v_field_type;
+               }
+
+               // TODO: optimalize!
+               v_struct_id_field =
+                       bt_ctf_field_structure_get_field(v_struct_field, "id");
+               if (!v_struct_id_field) {
+                       goto end_v_field_type;
+               }
+
+               ret = bt_ctf_field_unsigned_integer_get_value(
+                       v_struct_id_field, &event_id);
+               if (ret) {
+                       event_id = -1ULL;
+               }
+
+end_v_field_type:
+               BT_PUT(v_field);
+               BT_PUT(v_struct_field);
+               BT_PUT(v_struct_id_field);
+       }
+
+       if (id_field_type && event_id == -1ULL) {
+               /* Check "id" field */
+               struct bt_ctf_field *id_field = NULL;
+               int ret;
+
+               // TODO: optimalize!
+               id_field = bt_ctf_field_structure_get_field(
+                       notit->dscopes.stream_event_header, "id");
+               assert(id_field);
+               assert(bt_ctf_field_is_integer(id_field) ||
+                       bt_ctf_field_is_enumeration(id_field));
+
+               if (bt_ctf_field_is_integer(id_field)) {
+                       ret = bt_ctf_field_unsigned_integer_get_value(
+                               id_field, &event_id);
+               } else {
+                       struct bt_ctf_field *container;
+
+                       container = bt_ctf_field_enumeration_get_container(
+                               id_field);
+                       assert(container);
+                       ret = bt_ctf_field_unsigned_integer_get_value(
+                               container, &event_id);
+                       BT_PUT(container);
+               }
+               assert(!ret);
+               BT_PUT(id_field);
+       }
+
+       if (event_id == -1ULL) {
+               /* Event ID not found: single event? */
+               assert(bt_ctf_stream_class_get_event_class_count(
+                       notit->meta.stream_class) == 1);
+               event_id = 0;
+       }
+
+       BT_PUT(notit->meta.event_class);
+       notit->meta.event_class = bt_ctf_stream_class_get_event_class_by_id(
+               notit->meta.stream_class, event_id);
+       if (!notit->meta.event_class) {
+               PERR("Cannot find event class with ID %" PRIu64 "\n", event_id);
+               status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+end:
+       BT_PUT(event_header_type);
+       BT_PUT(id_field_type);
+       BT_PUT(v_field_type);
+
+       return status;
+}
+
+static
+enum bt_ctf_notif_iter_status after_event_header_state(
+               struct bt_ctf_notif_iter *notit)
+{
+       enum bt_ctf_notif_iter_status status;
+
+       status = set_current_packet_content_sizes(notit);
+       if (status != BT_CTF_NOTIF_ITER_STATUS_OK) {
+               PERR("Failed to set current packet and content sizes\n");
+               goto end;
+       }
+
+       status = set_current_event_class(notit);
+       if (status != BT_CTF_NOTIF_ITER_STATUS_OK) {
+               PERR("Failed to set current event class\n");
+               goto end;
+       }
+
+       notit->state = STATE_DSCOPE_STREAM_EVENT_CONTEXT_BEGIN;
+
+end:
+       return status;
+}
+
+static
+enum bt_ctf_notif_iter_status read_stream_event_context_begin_state(
+               struct bt_ctf_notif_iter *notit)
+{
+       enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
+       struct bt_ctf_field_type *stream_event_context_type;
+
+       stream_event_context_type = bt_ctf_stream_class_get_event_context_type(
+               notit->meta.stream_class);
+       if (!stream_event_context_type) {
+               PERR("Failed to retrieve stream class's event context type\n");
+               status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+       status = read_dscope_begin_state(notit, stream_event_context_type,
+               STATE_DSCOPE_EVENT_CONTEXT_BEGIN,
+               STATE_DSCOPE_STREAM_EVENT_CONTEXT_CONTINUE,
+               &notit->dscopes.stream_event_context);
+
+end:
+       BT_PUT(stream_event_context_type);
+
+       return status;
+}
+
+static
+enum bt_ctf_notif_iter_status read_stream_event_context_continue_state(
+               struct bt_ctf_notif_iter *notit)
+{
+       return read_dscope_continue_state(notit,
+               STATE_DSCOPE_EVENT_CONTEXT_BEGIN);
+}
+
+static
+enum bt_ctf_notif_iter_status read_event_context_begin_state(
+               struct bt_ctf_notif_iter *notit)
+{
+       enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
+       struct bt_ctf_field_type *event_context_type;
+
+       event_context_type = bt_ctf_event_class_get_context_type(
+               notit->meta.event_class);
+       if (!event_context_type) {
+               PERR("Failed to retrieve event class's context type\n");
+               status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+       status = read_dscope_begin_state(notit, event_context_type,
+               STATE_DSCOPE_EVENT_PAYLOAD_BEGIN,
+               STATE_DSCOPE_EVENT_CONTEXT_CONTINUE,
+               &notit->dscopes.event_context);
+
+end:
+       BT_PUT(event_context_type);
+
+       return status;
+}
+
+static
+enum bt_ctf_notif_iter_status read_event_context_continue_state(
+               struct bt_ctf_notif_iter *notit)
+{
+       return read_dscope_continue_state(notit,
+               STATE_DSCOPE_EVENT_PAYLOAD_BEGIN);
+}
+
+static
+enum bt_ctf_notif_iter_status read_event_payload_begin_state(
+               struct bt_ctf_notif_iter *notit)
+{
+       enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
+       struct bt_ctf_field_type *event_payload_type;
+
+       event_payload_type = bt_ctf_event_class_get_payload_type(
+               notit->meta.event_class);
+       if (!event_payload_type) {
+               PERR("Failed to retrieve event class's payload type\n");
+               status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+       status = read_dscope_begin_state(notit, event_payload_type,
+               STATE_EMIT_NOTIF_EVENT,
+               STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE,
+               &notit->dscopes.event_payload);
+
+end:
+       BT_PUT(event_payload_type);
+
+       return status;
+}
+
+static
+enum bt_ctf_notif_iter_status read_event_payload_continue_state(
+               struct bt_ctf_notif_iter *notit)
+{
+       return read_dscope_continue_state(notit, STATE_EMIT_NOTIF_EVENT);
+}
+
+static
+enum bt_ctf_notif_iter_status skip_packet_padding_state(
+               struct bt_ctf_notif_iter *notit)
+{
+       enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
+       size_t bits_to_skip;
+
+       assert(notit->cur_packet_size > 0);
+       bits_to_skip = notit->cur_packet_size - packet_at(notit);
+       if (bits_to_skip == 0) {
+               notit->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN;
+               goto end;
+       } else {
+               size_t bits_to_consume;
+               status = buf_ensure_available_bits(notit);
+               if (status != BT_CTF_NOTIF_ITER_STATUS_OK) {
+                       goto end;
+               }
+
+               bits_to_consume = MIN(buf_available_bits(notit), bits_to_skip);
+               buf_consume_bits(notit, bits_to_consume);
+               bits_to_skip = notit->cur_packet_size - packet_at(notit);
+               if (bits_to_skip == 0) {
+                       notit->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN;
+                       goto end;
+               }
+       }
+
+end:
+       return status;
+}
+
+static inline
+enum bt_ctf_notif_iter_status handle_state(struct bt_ctf_notif_iter *notit)
+{
+       enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
+
+       PDBG("Handling state %d\n", notit->state);
+
+       // TODO: optimalize!
+       switch (notit->state) {
+       case STATE_INIT:
+               notit->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN;
+               break;
+       case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN:
+               status = read_packet_header_begin_state(notit);
+               break;
+       case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE:
+               status = read_packet_header_continue_state(notit);
+               break;
+       case STATE_AFTER_TRACE_PACKET_HEADER:
+               status = after_packet_header_state(notit);
+               break;
+       case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN:
+               status = read_packet_context_begin_state(notit);
+               break;
+       case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE:
+               status = read_packet_context_continue_state(notit);
+               break;
+       case STATE_AFTER_STREAM_PACKET_CONTEXT:
+               status = after_packet_context_state(notit);
+               break;
+       case STATE_EMIT_NOTIF_NEW_PACKET:
+               notit->state = STATE_DSCOPE_STREAM_EVENT_HEADER_BEGIN;
+               break;
+       case STATE_DSCOPE_STREAM_EVENT_HEADER_BEGIN:
+               status = read_event_header_begin_state(notit);
+               break;
+       case STATE_DSCOPE_STREAM_EVENT_HEADER_CONTINUE:
+               status = read_event_header_continue_state(notit);
+               break;
+       case STATE_AFTER_STREAM_EVENT_HEADER:
+               status = after_event_header_state(notit);
+               break;
+       case STATE_DSCOPE_STREAM_EVENT_CONTEXT_BEGIN:
+               status = read_stream_event_context_begin_state(notit);
+               break;
+       case STATE_DSCOPE_STREAM_EVENT_CONTEXT_CONTINUE:
+               status = read_stream_event_context_continue_state(notit);
+               break;
+       case STATE_DSCOPE_EVENT_CONTEXT_BEGIN:
+               status = read_event_context_begin_state(notit);
+               break;
+       case STATE_DSCOPE_EVENT_CONTEXT_CONTINUE:
+               status = read_event_context_continue_state(notit);
+               break;
+       case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN:
+               status = read_event_payload_begin_state(notit);
+               break;
+       case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE:
+               status = read_event_payload_continue_state(notit);
+               break;
+       case STATE_EMIT_NOTIF_EVENT:
+               notit->state = STATE_DSCOPE_STREAM_EVENT_HEADER_BEGIN;
+               break;
+       case STATE_SKIP_PACKET_PADDING:
+               status = skip_packet_padding_state(notit);
+               break;
+       case STATE_EMIT_NOTIF_END_OF_PACKET:
+               notit->state = STATE_SKIP_PACKET_PADDING;
+               break;
+       }
+
+       return status;
+}
+
+void bt_ctf_notif_iter_reset(struct bt_ctf_notif_iter *notit)
+{
+       assert(notit);
+       stack_clear(notit->stack);
+       BT_PUT(notit->meta.stream_class);
+       BT_PUT(notit->meta.event_class);
+       BT_PUT(notit->packet);
+       put_all_dscopes(notit);
+       notit->buf.addr = NULL;
+       notit->buf.sz = 0;
+       notit->buf.at = 0;
+       notit->buf.packet_offset = 0;
+       notit->state = STATE_INIT;
+       notit->cur_content_size = -1;
+       notit->cur_packet_size = -1;
+}
+
+static
+struct bt_ctf_field *get_next_field(struct bt_ctf_notif_iter *notit)
+{
+       struct bt_ctf_field *next_field = NULL;
+       struct bt_ctf_field *base_field;
+       struct bt_ctf_field_type *base_type;
+       size_t index;
+
+       assert(!stack_empty(notit->stack));
+       index = stack_top(notit->stack)->index;
+       base_field = stack_top(notit->stack)->base;
+       base_type = bt_ctf_field_get_type(base_field);
+       if (!base_type) {
+               PERR("Failed to get base field's type\n");
+               goto end;
+       }
+
+       switch (bt_ctf_field_type_get_type_id(base_type)) {
+       case BT_CTF_TYPE_ID_STRUCT:
+               next_field = bt_ctf_field_structure_get_field_by_index(
+                       base_field, index);
+               break;
+       case BT_CTF_TYPE_ID_ARRAY:
+               next_field = bt_ctf_field_array_get_field(base_field, index);
+               break;
+       case BT_CTF_TYPE_ID_SEQUENCE:
+               next_field = bt_ctf_field_sequence_get_field(base_field, index);
+               break;
+       case BT_CTF_TYPE_ID_VARIANT:
+               next_field = bt_ctf_field_variant_get_current_field(base_field);
+               break;
+       default:
+               assert(false);
+               break;
+       }
+
+end:
+       BT_PUT(base_type);
+
+       return next_field;
+}
+
+static
+enum bt_ctf_btr_status btr_signed_int_cb(int64_t value,
+               struct bt_ctf_field_type *type, void *data)
+{
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+       struct bt_ctf_field *field = NULL;
+       struct bt_ctf_field *int_field = NULL;
+       struct bt_ctf_notif_iter *notit = data;
+       int ret;
+
+       /* create next field */
+       field = get_next_field(notit);
+       if (!field) {
+               PERR("Failed to get next field (signed int)\n");
+               status = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+       switch(bt_ctf_field_type_get_type_id(type)) {
+       case BT_CTF_TYPE_ID_INTEGER:
+               /* Integer field is created field */
+               BT_MOVE(int_field, field);
+               break;
+       case BT_CTF_TYPE_ID_ENUM:
+               int_field = bt_ctf_field_enumeration_get_container(field);
+               break;
+       default:
+               break;
+       }
+
+       if (!int_field) {
+               PERR("Failed to get integer field\n");
+               status = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+       ret = bt_ctf_field_signed_integer_set_value(int_field, value);
+       assert(!ret);
+       stack_top(notit->stack)->index++;
+
+end:
+       BT_PUT(field);
+       BT_PUT(int_field);
+
+       return status;
+}
+
+static
+enum bt_ctf_btr_status btr_unsigned_int_cb(uint64_t value,
+               struct bt_ctf_field_type *type, void *data)
+{
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+       struct bt_ctf_field *field = NULL;
+       struct bt_ctf_field *int_field = NULL;
+       struct bt_ctf_notif_iter *notit = data;
+       int ret;
+
+       /* Create next field */
+       field = get_next_field(notit);
+       if (!field) {
+               PERR("Failed to get next field (unsigned int)\n");
+               status = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+       switch(bt_ctf_field_type_get_type_id(type)) {
+       case BT_CTF_TYPE_ID_INTEGER:
+               /* Integer field is created field */
+               BT_MOVE(int_field, field);
+               break;
+       case BT_CTF_TYPE_ID_ENUM:
+               int_field = bt_ctf_field_enumeration_get_container(field);
+               break;
+       default:
+               break;
+       }
+
+       if (!int_field) {
+               PERR("Failed to get integer field\n");
+               status = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+       ret = bt_ctf_field_unsigned_integer_set_value(int_field, value);
+       assert(!ret);
+       stack_top(notit->stack)->index++;
+
+end:
+       BT_PUT(field);
+       BT_PUT(int_field);
+
+       return status;
+}
+
+static
+enum bt_ctf_btr_status btr_floating_point_cb(double value,
+               struct bt_ctf_field_type *type, void *data)
+{
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+       struct bt_ctf_field *field = NULL;
+       struct bt_ctf_notif_iter *notit = data;
+       int ret;
+
+       /* Create next field */
+       field = get_next_field(notit);
+       if (!field) {
+               PERR("Failed to get next field (floating point number)\n");
+               status = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+       ret = bt_ctf_field_floating_point_set_value(field, value);
+       assert(!ret);
+       stack_top(notit->stack)->index++;
+
+end:
+       BT_PUT(field);
+
+       return status;
+}
+
+static
+enum bt_ctf_btr_status btr_string_begin_cb(
+               struct bt_ctf_field_type *type, void *data)
+{
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+       struct bt_ctf_field *field = NULL;
+       struct bt_ctf_notif_iter *notit = data;
+       int ret;
+
+       /* Create next field */
+       field = get_next_field(notit);
+       if (!field) {
+               PERR("Failed to get next field (string)\n");
+               status = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+       /*
+        * Push on stack. Not a compound type per se, but we know that only
+        * btr_string_cb() may be called between this call and a subsequent
+        * call to btr_string_end_cb().
+        */
+       ret = stack_push(notit->stack, field);
+       if (ret) {
+               PERR("Failed to push string field onto the stack\n");
+               status = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+end:
+       BT_PUT(field);
+
+       return status;
+}
+
+static
+enum bt_ctf_btr_status btr_string_cb(const char *value,
+               size_t len, struct bt_ctf_field_type *type, void *data)
+{
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+       struct bt_ctf_field *field = NULL;
+       struct bt_ctf_notif_iter *notit = data;
+       int ret;
+
+       /* Get string field */
+       field = stack_top(notit->stack)->base;
+       assert(field);
+
+       /* Append current string */
+       ret = bt_ctf_field_string_append_len(field, value, len);
+       if (ret) {
+               PERR("Failed to append a string to a string field\n");
+               status = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+end:
+       return status;
+}
+
+static
+enum bt_ctf_btr_status btr_string_end_cb(
+               struct bt_ctf_field_type *type, void *data)
+{
+       struct bt_ctf_notif_iter *notit = data;
+
+       /* Pop string field */
+       stack_pop(notit->stack);
+
+       /* Go to next field */
+       stack_top(notit->stack)->index++;
+
+       return BT_CTF_BTR_STATUS_OK;
+}
+
+enum bt_ctf_btr_status btr_compound_begin_cb(
+               struct bt_ctf_field_type *type, void *data)
+{
+       enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK;
+       struct bt_ctf_notif_iter *notit = data;
+       struct bt_ctf_field *field;
+       int ret;
+
+       /* Create field */
+       if (stack_empty(notit->stack)) {
+               /* Root: create dynamic scope field */
+               *notit->cur_dscope_field = bt_ctf_field_create(type);
+               field = *notit->cur_dscope_field;
+
+               /*
+                * Field will be put at the end of this function
+                * (stack_push() will take one reference, but this
+                * reference is lost upon the equivalent stack_pop()
+                * later), so also get it for our context to own it.
+                */
+               bt_get(*notit->cur_dscope_field);
+       } else {
+               field = get_next_field(notit);
+       }
+
+       if (!field) {
+               PERR("Failed to get next field or create dynamic scope field\n");
+               status = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+       /* Push field */
+       ret = stack_push(notit->stack, field);
+       if (ret) {
+               PERR("Failed to push compound field onto the stack\n");
+               status = BT_CTF_BTR_STATUS_ERROR;
+               goto end;
+       }
+
+end:
+       BT_PUT(field);
+
+       return status;
+}
+
+enum bt_ctf_btr_status btr_compound_end_cb(
+               struct bt_ctf_field_type *type, void *data)
+{
+       struct bt_ctf_notif_iter *notit = data;
+
+       assert(!stack_empty(notit->stack));
+
+       /* Pop stack */
+       stack_pop(notit->stack);
+
+       /* If the stack is not empty, increment the base's index */
+       if (!stack_empty(notit->stack)) {
+               stack_top(notit->stack)->index++;
+       }
+
+       return BT_CTF_BTR_STATUS_OK;
+}
+
+static
+struct bt_ctf_field *resolve_field(struct bt_ctf_notif_iter *notit,
+               struct bt_ctf_field_path *path)
+{
+       struct bt_ctf_field *field = NULL;
+       unsigned int i;
+
+       switch (bt_ctf_field_path_get_root_scope(path)) {
+       case BT_CTF_SCOPE_TRACE_PACKET_HEADER:
+               field = notit->dscopes.trace_packet_header;
+               break;
+       case BT_CTF_SCOPE_STREAM_PACKET_CONTEXT:
+               field = notit->dscopes.stream_packet_context;
+               break;
+       case BT_CTF_SCOPE_STREAM_EVENT_HEADER:
+               field = notit->dscopes.stream_event_header;
+               break;
+       case BT_CTF_SCOPE_STREAM_EVENT_CONTEXT:
+               field = notit->dscopes.stream_event_context;
+               break;
+       case BT_CTF_SCOPE_EVENT_CONTEXT:
+               field = notit->dscopes.event_context;
+               break;
+       case BT_CTF_SCOPE_EVENT_FIELDS:
+               field = notit->dscopes.event_payload;
+               break;
+       default:
+               break;
+       }
+
+       if (!field) {
+               goto end;
+       }
+
+       bt_get(field);
+
+       for (i = 0; i < bt_ctf_field_path_get_index_count(path); ++i) {
+               struct bt_ctf_field *next_field = NULL;
+               struct bt_ctf_field_type *field_type;
+               int index = bt_ctf_field_path_get_index(path, i);
+
+               field_type = bt_ctf_field_get_type(field);
+               if (!field_type) {
+                       BT_PUT(field);
+                       goto end;
+               }
+
+               if (is_struct_type(field_type)) {
+                       next_field = bt_ctf_field_structure_get_field_by_index(
+                               field, index);
+               } else if (is_variant_type(field_type)) {
+                       next_field =
+                               bt_ctf_field_variant_get_current_field(field);
+               }
+
+               BT_PUT(field);
+               BT_PUT(field_type);
+
+               if (!next_field) {
+                       goto end;
+               }
+
+               /* Move next field -> field */
+               BT_MOVE(field, next_field);
+       }
+
+end:
+       return field;
+}
+
+static
+int64_t btr_get_sequence_length_cb(struct bt_ctf_field_type *type, void *data)
+{
+       int64_t ret = -1;
+       int iret;
+       struct bt_ctf_field_path *field_path;
+       struct bt_ctf_notif_iter *notit = data;
+       struct bt_ctf_field *field = NULL;
+       uint64_t length;
+
+       field_path = bt_ctf_field_type_sequence_get_length_field_path(type);
+       if (!field_path) {
+               goto end;
+       }
+
+       field = resolve_field(notit, field_path);
+       if (!field) {
+               goto end;
+       }
+
+       iret = bt_ctf_field_unsigned_integer_get_value(field, &length);
+       if (iret) {
+               goto end;
+       }
+
+       ret = (int64_t) length;
+
+end:
+       BT_PUT(field);
+       BT_PUT(field_path);
+
+       return ret;
+}
+
+static
+struct bt_ctf_field_type *btr_get_variant_type_cb(
+               struct bt_ctf_field_type *type, void *data)
+{
+       struct bt_ctf_field_path *path;
+       struct bt_ctf_notif_iter *notit = data;
+       struct bt_ctf_field *tag_field = NULL;
+       struct bt_ctf_field *selected_field = NULL;
+       struct bt_ctf_field_type *selected_field_type = NULL;
+
+       path = bt_ctf_field_type_variant_get_tag_field_path(type);
+       if (!path) {
+               goto end;
+       }
+
+       tag_field = resolve_field(notit, path);
+       if (!tag_field) {
+               goto end;
+       }
+
+       /*
+        * We found the enumeration tag field instance which should be
+        * able to select a current field for this variant. This
+        * callback function we're in is called _after_
+        * compound_begin(), so the current stack top's base field is
+        * the variant field in question. We get the selected field here
+        * thanks to this tag field (thus creating the selected field),
+        * which will also provide us with its type. Then, this field
+        * will remain the current selected one until the next callback
+        * function call which is used to fill the current selected
+        * field.
+        */
+       selected_field = bt_ctf_field_variant_get_field(
+               stack_top(notit->stack)->base, tag_field);
+       if (!selected_field) {
+               goto end;
+       }
+
+       selected_field_type = bt_ctf_field_get_type(selected_field);
+
+end:
+       BT_PUT(tag_field);
+       BT_PUT(selected_field);
+
+       return selected_field_type;
+}
+
+static struct bt_ctf_event *create_event(struct bt_ctf_notif_iter *notit)
+{
+       struct bt_ctf_event *event;
+       int ret;
+
+       /* Create event object */
+       event = bt_ctf_event_create(notit->meta.event_class);
+       if (!event) {
+               goto error;
+       }
+
+       /* Set header, stream event context, context, and payload fields */
+       ret = bt_ctf_event_set_header(event,
+               notit->dscopes.stream_event_header);
+       if (ret) {
+               goto error;
+       }
+
+       ret = bt_ctf_event_set_stream_event_context(event,
+               notit->dscopes.stream_event_context);
+       if (ret) {
+               goto error;
+       }
+
+       ret = bt_ctf_event_set_event_context(event,
+               notit->dscopes.event_context);
+       if (ret) {
+               goto error;
+       }
+
+       ret = bt_ctf_event_set_payload_field(event,
+               notit->dscopes.event_payload);
+       if (ret) {
+               goto error;
+       }
+
+       /* Associate with current packet */
+       assert(notit->packet);
+       ret = bt_ctf_event_set_packet(event, notit->packet);
+       if (ret) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       BT_PUT(event);
+
+end:
+       return event;
+}
+
+static void create_packet(struct bt_ctf_notif_iter *notit)
+{
+       int ret;
+       struct bt_ctf_stream *stream = NULL;
+       struct bt_ctf_packet *packet = NULL;
+
+       /* Ask the user for the stream */
+       stream = notit->medium.medops.get_stream(notit->meta.stream_class,
+               notit->medium.data);
+       if (!stream) {
+               goto error;
+       }
+
+       /* Create packet */
+       packet = bt_ctf_packet_create(stream);
+       if (!packet) {
+               goto error;
+       }
+
+       /* Set packet's context and header fields */
+       if (notit->dscopes.trace_packet_header) {
+               ret = bt_ctf_packet_set_header(packet,
+                       notit->dscopes.trace_packet_header);
+               if (ret) {
+                       goto error;
+               }
+       }
+
+       if (notit->dscopes.stream_packet_context) {
+               ret = bt_ctf_packet_set_context(packet,
+                       notit->dscopes.stream_packet_context);
+               if (ret) {
+                       goto error;
+               }
+       }
+
+       goto end;
+
+error:
+       BT_PUT(packet);
+
+end:
+       BT_MOVE(notit->packet, packet);
+}
+
+static void notify_new_packet(struct bt_ctf_notif_iter *notit,
+               struct bt_ctf_notif_iter_notif **notification)
+{
+       struct bt_ctf_notif_iter_notif_new_packet *rnotif;
+
+       rnotif = g_new0(struct bt_ctf_notif_iter_notif_new_packet, 1);
+       if (!rnotif) {
+               goto error;
+       }
+
+       rnotif->base.type = BT_CTF_NOTIF_ITER_NOTIF_NEW_PACKET;
+
+       /* Create packet */
+       create_packet(notit);
+       if (!notit->packet) {
+               goto error;
+       }
+
+       rnotif->packet = bt_get(notit->packet);
+       *notification = (struct bt_ctf_notif_iter_notif *) rnotif;
+       return;
+
+error:
+       bt_ctf_notif_iter_notif_destroy(rnotif);
+}
+
+static void notify_end_of_packet(struct bt_ctf_notif_iter *notit,
+               struct bt_ctf_notif_iter_notif **notification)
+{
+       struct bt_ctf_notif_iter_notif_end_of_packet *rnotif;
+
+       rnotif = g_new0(struct bt_ctf_notif_iter_notif_end_of_packet, 1);
+       if (!rnotif) {
+               goto error;
+       }
+
+       rnotif->base.type = BT_CTF_NOTIF_ITER_NOTIF_END_OF_PACKET;
+
+       /* Create packet */
+       create_packet(notit);
+       if (!notit->packet) {
+               goto error;
+       }
+
+       rnotif->packet = bt_get(notit->packet);
+       *notification = (struct bt_ctf_notif_iter_notif *) rnotif;
+       return;
+
+error:
+       bt_ctf_notif_iter_notif_destroy(rnotif);
+}
+
+static void notify_event(struct bt_ctf_notif_iter *notit,
+               struct bt_ctf_notif_iter_notif **notification)
+{
+       struct bt_ctf_notif_iter_notif_event *rnotif;
+       struct bt_ctf_event *event = NULL;
+
+       rnotif = g_new0(struct bt_ctf_notif_iter_notif_event, 1);
+       if (!rnotif) {
+               goto error;
+       }
+
+       rnotif->base.type = BT_CTF_NOTIF_ITER_NOTIF_EVENT;
+
+       /* Create event */
+       event = create_event(notit);
+       if (!event) {
+               goto error;
+       }
+
+       BT_MOVE(rnotif->event, event);
+       *notification = (struct bt_ctf_notif_iter_notif *) rnotif;
+       return;
+
+error:
+       BT_PUT(event);
+       bt_ctf_notif_iter_notif_destroy(rnotif);
+}
+
+void bt_ctf_notif_iter_notif_destroy(void *vnotif)
+{
+       struct bt_ctf_notif_iter_notif *notif = vnotif;
+
+       switch (notif->type) {
+       case BT_CTF_NOTIF_ITER_NOTIF_NEW_PACKET:
+       {
+               struct bt_ctf_notif_iter_notif_new_packet *rnotif =
+                       (struct bt_ctf_notif_iter_notif_new_packet *) notif;
+
+               BT_PUT(rnotif->packet);
+               break;
+       }
+       case BT_CTF_NOTIF_ITER_NOTIF_END_OF_PACKET:
+       {
+               struct bt_ctf_notif_iter_notif_end_of_packet *rnotif =
+                       (struct bt_ctf_notif_iter_notif_end_of_packet *) notif;
+
+               BT_PUT(rnotif->packet);
+               break;
+       }
+       case BT_CTF_NOTIF_ITER_NOTIF_EVENT:
+       {
+               struct bt_ctf_notif_iter_notif_event *rnotif =
+                       (struct bt_ctf_notif_iter_notif_event *) notif;
+
+               BT_PUT(rnotif->event);
+               break;
+       }
+       default:
+               assert(false);
+       }
+
+       g_free(notif);
+}
+
+struct bt_ctf_notif_iter *bt_ctf_notif_iter_create(struct bt_ctf_trace *trace,
+               size_t max_request_sz,
+               struct bt_ctf_notif_iter_medium_ops medops,
+               void *data, FILE *err_stream)
+{
+       struct bt_ctf_notif_iter *notit = NULL;
+       struct bt_ctf_btr_cbs cbs = {
+               .types = {
+                       .signed_int = btr_signed_int_cb,
+                       .unsigned_int = btr_unsigned_int_cb,
+                       .floating_point = btr_floating_point_cb,
+                       .string_begin = btr_string_begin_cb,
+                       .string = btr_string_cb,
+                       .string_end = btr_string_end_cb,
+                       .compound_begin = btr_compound_begin_cb,
+                       .compound_end = btr_compound_end_cb,
+               },
+               .query = {
+                       .get_sequence_length = btr_get_sequence_length_cb,
+                       .get_variant_type = btr_get_variant_type_cb,
+               },
+       };
+
+       assert(trace);
+       assert(medops.request_bytes);
+       notit = g_new0(struct bt_ctf_notif_iter, 1);
+       if (!notit) {
+               PERR("Failed to allocate memory for CTF notification iterator\n");
+               goto end;
+       }
+
+       notit->meta.trace = trace;
+       bt_get(notit->meta.trace);
+       notit->medium.medops = medops;
+       notit->medium.max_request_sz = max_request_sz;
+       notit->medium.data = data;
+       notit->err_stream = err_stream;
+       notit->stack = stack_new(notit);
+       if (!notit->stack) {
+               PERR("Failed to create stack\n");
+               bt_ctf_notif_iter_destroy(notit);
+               notit = NULL;
+               goto end;
+       }
+
+       notit->btr = bt_ctf_btr_create(cbs, notit, err_stream);
+       if (!notit->btr) {
+               PERR("Failed to create binary type reader\n");
+               bt_ctf_notif_iter_destroy(notit);
+               notit = NULL;
+               goto end;
+       }
+
+       bt_ctf_notif_iter_reset(notit);
+
+end:
+       return notit;
+}
+
+void bt_ctf_notif_iter_destroy(struct bt_ctf_notif_iter *notit)
+{
+       BT_PUT(notit->meta.trace);
+       BT_PUT(notit->meta.stream_class);
+       BT_PUT(notit->meta.event_class);
+       BT_PUT(notit->packet);
+       put_all_dscopes(notit);
+
+       if (notit->stack) {
+               stack_destroy(notit->stack);
+       }
+
+       if (notit->btr) {
+               bt_ctf_btr_destroy(notit->btr);
+       }
+
+       g_free(notit);
+}
+
+enum bt_ctf_notif_iter_status bt_ctf_notif_iter_get_next_notification(
+               struct bt_ctf_notif_iter *notit,
+               struct bt_ctf_notif_iter_notif **notification)
+{
+       enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK;
+
+       assert(notit);
+       assert(notification);
+
+       while (true) {
+               status = handle_state(notit);
+               if (status != BT_CTF_NOTIF_ITER_STATUS_OK) {
+                       if (status == BT_CTF_NOTIF_ITER_STATUS_EOF) {
+                               PDBG("Medium operation reported end of file\n");
+                       } else {
+                               PERR("Failed to handle state:\n");
+                               PERR("  State: %d\n", notit->state);
+                       }
+                       goto end;
+               }
+
+               switch (notit->state) {
+               case STATE_EMIT_NOTIF_NEW_PACKET:
+                       PDBG("Emitting new packet notification\n");
+                       notify_new_packet(notit, notification);
+                       if (!*notification) {
+                               status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+                       }
+                       goto end;
+               case STATE_EMIT_NOTIF_EVENT:
+                       PDBG("Emitting event notification\n");
+                       notify_event(notit, notification);
+                       if (!*notification) {
+                               status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+                       }
+                       goto end;
+               case STATE_EMIT_NOTIF_END_OF_PACKET:
+                       PDBG("Emitting end of packet notification\n");
+                       notify_end_of_packet(notit, notification);
+                       if (!*notification) {
+                               status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
+                       }
+                       goto end;
+               default:
+                       /* Non-emitting state: continue */
+                       break;
+               }
+       }
+
+end:
+       return status;
+}
diff --git a/ctf-reader-proto/ctf-notif-iter/ctf-notif-iter.h b/ctf-reader-proto/ctf-notif-iter/ctf-notif-iter.h
new file mode 100644 (file)
index 0000000..ed1dbbc
--- /dev/null
@@ -0,0 +1,295 @@
+#ifndef CTF_NOTIF_ITER_H
+#define CTF_NOTIF_ITER_H
+
+/*
+ * Babeltrace - CTF notification iterator
+ *                  ¯¯¯¯¯        ¯¯¯¯
+ * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation
+ * Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <babeltrace/ctf-ir/trace.h>
+#include <babeltrace/ctf-ir/fields.h>
+#include <babeltrace/ctf-ir/event.h>
+#include <babeltrace/babeltrace-internal.h>
+
+/**
+ * @file ctf-notif-iter.h
+ *
+ * CTF notification iterator
+ *     ¯¯¯¯¯        ¯¯¯¯
+ * This is a common internal API used by CTF source plugins. It allows
+ * one to get notifications from a user-provided medium.
+ */
+
+/**
+ * Medium operations status codes.
+ */
+enum bt_ctf_notif_iter_medium_status {
+       /**
+        * End of file.
+        *
+        * The medium function called by the notification iterator
+        * function reached the end of the file.
+        */
+       BT_CTF_NOTIF_ITER_MEDIUM_STATUS_EOF =   -4,
+
+       /**
+        * There is no data available right now, try again later.
+        */
+       BT_CTF_NOTIF_ITER_MEDIUM_STATUS_AGAIN = -3,
+
+       /** Invalid argument. */
+       BT_CTF_NOTIF_ITER_MEDIUM_STATUS_INVAL = -2,
+
+       /** General error. */
+       BT_CTF_NOTIF_ITER_MEDIUM_STATUS_ERROR = -1,
+
+       /** Everything okay. */
+       BT_CTF_NOTIF_ITER_MEDIUM_STATUS_OK =            0,
+};
+
+/**
+ * CTF notification iterator API status code.
+ */
+enum bt_ctf_notif_iter_status {
+       /**
+        * End of file.
+        *
+        * The medium function called by the notification iterator
+        * function reached the end of the file.
+        */
+       BT_CTF_NOTIF_ITER_STATUS_EOF =  -4,
+
+       /**
+        * There is no data available right now, try again later.
+        *
+        * Some condition resulted in the
+        * bt_ctf_notif_iter_medium_ops::request_bytes() user function not
+        * having access to any data now. You should retry calling the
+        * last called notification iterator function once the situation
+        * is resolved.
+        */
+       BT_CTF_NOTIF_ITER_STATUS_AGAIN =        -3,
+
+       /** Invalid argument. */
+       BT_CTF_NOTIF_ITER_STATUS_INVAL =        -2,
+
+       /** General error. */
+       BT_CTF_NOTIF_ITER_STATUS_ERROR =        -1,
+
+       /** Everything okay. */
+       BT_CTF_NOTIF_ITER_STATUS_OK =   0,
+};
+
+/**
+ * Medium operations.
+ *
+ * Those user functions are called by the notification iterator
+ * functions to request medium actions.
+ */
+struct bt_ctf_notif_iter_medium_ops {
+       /**
+        * Returns the next byte buffer to be used by the binary file
+        * reader to deserialize binary data.
+        *
+        * This function \em must be defined.
+        *
+        * The purpose of this function is to return a buffer of bytes
+        * to the notification iterator, of a maximum of \p request_sz
+        * bytes. If this function cannot return a buffer of at least
+        * \p request_sz bytes, it may return a smaller buffer. In
+        * either cases, \p buffer_sz must be set to the returned buffer
+        * size (in bytes).
+        *
+        * The returned buffer's ownership remains the medium, in that
+        * it won't be freed by the notification iterator functions. The
+        * returned buffer won't be modified by the notification
+        * iterator functions either.
+        *
+        * When this function is called for the first time for a given
+        * file, the offset within the file is considered to be 0. The
+        * next times this function is called, the returned buffer's
+        * byte offset within the complete file must be the previous
+        * offset plus the last returned value of \p buffer_sz by this
+        * medium.
+        *
+        * This function must return one of the following statuses:
+        *
+        *   - <b>#BT_CTF_NOTIF_ITER_MEDIUM_STATUS_OK</b>: Everything
+        *     is okay, i.e. \p buffer_sz is set to a positive value
+        *     reflecting the number of available bytes in the buffer
+        *     starting at the address written in \p buffer_addr.
+        *   - <b>#BT_CTF_NOTIF_ITER_MEDIUM_STATUS_AGAIN</b>: No data is
+        *     available right now. In this case, the notification
+        *     iterator function called by the user returns
+        *     #BT_CTF_NOTIF_ITER_STATUS_AGAIN, and it is the user's
+        *     responsibility to make sure enough data becomes available
+        *     before calling the \em same notification iterator
+        *     function again to continue the decoding process.
+        *   - <b>#BT_CTF_NOTIF_ITER_MEDIUM_STATUS_EOF</b>: The end of
+        *     the file was reached, and no more data will ever be
+        *     available for this file. In this case, the notification
+        *     iterator function called by the user returns
+        *     #BT_CTF_NOTIF_ITER_STATUS_EOF. This must \em not be
+        *     returned when returning at least one byte of data to the
+        *     caller, i.e. this must be returned when there's
+        *     absolutely nothing left; should the request size be
+        *     larger than what's left in the file, this function must
+        *     return what's left, setting \p buffer_sz to the number of
+        *     remaining bytes, and return
+        *     #BT_CTF_NOTIF_ITER_MEDIUM_STATUS_EOF on the \em following
+        *     call.
+        *   - <b>#BT_CTF_NOTIF_ITER_MEDIUM_STATUS_ERROR</b>: A fatal
+        *     error occured during this operation. In this case, the
+        *     notification iterator function called by the user returns
+        *     #BT_CTF_NOTIF_ITER_STATUS_ERROR.
+        *
+        * If #BT_CTF_NOTIF_ITER_MEDIUM_STATUS_OK is not returned, the
+        * values of \p buffer_sz and \p buffer_addr are \em ignored by
+        * the caller.
+        *
+        * @param request_sz    Requested buffer size (bytes)
+        * @param buffer_addr   Returned buffer address
+        * @param buffer_sz     Returned buffer's size (bytes)
+        * @param data          User data
+        * @returns             Status code (see description above)
+        */
+       enum bt_ctf_notif_iter_medium_status (* request_bytes)(
+                       size_t request_sz, uint8_t **buffer_addr,
+                       size_t *buffer_sz, void *data);
+
+       /**
+        * Returns a stream instance (weak reference) for the given
+        * stream class.
+        *
+        * This is called after a packet header is read, and the
+        * corresponding stream class is found by the notification
+        * iterator.
+        *
+        * @param stream_class  Stream class associated to the stream
+        * @param data          User data
+        * @returns             Stream instance (weak reference) or
+        *                      \c NULL on error
+        */
+       struct bt_ctf_stream * (* get_stream)(
+                       struct bt_ctf_stream_class *stream_class, void *data);
+};
+
+/** CTF notification iterator. */
+struct bt_ctf_notif_iter;
+
+// TODO: Replace by the real thing
+enum bt_ctf_notif_iter_notif_type {
+       BT_CTF_NOTIF_ITER_NOTIF_NEW_PACKET,
+       BT_CTF_NOTIF_ITER_NOTIF_END_OF_PACKET,
+       BT_CTF_NOTIF_ITER_NOTIF_EVENT,
+};
+
+struct bt_ctf_notif_iter_notif {
+       enum bt_ctf_notif_iter_notif_type type;
+};
+
+struct bt_ctf_notif_iter_notif_new_packet {
+       struct bt_ctf_notif_iter_notif base;
+       struct bt_ctf_packet *packet;
+};
+
+struct bt_ctf_notif_iter_notif_end_of_packet {
+       struct bt_ctf_notif_iter_notif base;
+       struct bt_ctf_packet *packet;
+};
+
+struct bt_ctf_notif_iter_notif_event {
+       struct bt_ctf_notif_iter_notif base;
+       struct bt_ctf_event *event;
+};
+
+void bt_ctf_notif_iter_notif_destroy(void *notif);
+
+/**
+ * Creates a CTF notification iterator.
+ *
+ * Upon successful completion, the reference count of \p trace is
+ * incremented.
+ *
+ * @param trace                        Trace to read
+ * @param max_request_sz       Maximum buffer size, in bytes, to
+ *                             request to
+ *                             bt_ctf_notif_iter_medium_ops::request_bytes()
+ *                             at a time
+ * @param medops               Medium operations
+ * @param medops_data          User data (passed to medium operations)
+ * @param err_stream           Error stream (can be \c NULL to disable)
+ * @returns                    New CTF notification iterator on
+ *                             success, or \c NULL on error
+ */
+struct bt_ctf_notif_iter *bt_ctf_notif_iter_create(struct bt_ctf_trace *trace,
+       size_t max_request_sz, struct bt_ctf_notif_iter_medium_ops medops,
+       void *medops_data, FILE *err_stream);
+
+/**
+ * Destroys a CTF notification iterator, freeing all internal resources.
+ *
+ * The registered trace's reference count is decremented.
+ *
+ * @param notif_iter           CTF notification iterator
+ */
+void bt_ctf_notif_iter_destroy(struct bt_ctf_notif_iter *notif_iter);
+
+/**
+ * Resets the internal state of a CTF notification iterator.
+ *
+ * This function can be used when it is desired to seek to the beginning
+ * of another packet. It is expected that the next call to
+ * bt_ctf_notif_iter_medium_ops::request_bytes() made by this
+ * notification iterator will return the \em first bytes of a \em
+ * packet.
+ *
+ * @param notif_iter           CTF notification iterator
+ */
+void bt_ctf_notif_iter_reset(struct bt_ctf_notif_iter *notif_iter);
+
+/**
+ * Returns the next notification from a CTF notification iterator.
+ *
+ * Upon successful completion, #BT_CTF_NOTIF_ITER_STATUS_OK is
+ * returned, and the next notification is written to \p notif.
+ * In this case, the caller is responsible for calling
+ * bt_notification_put() on the returned notification.
+ *
+ * If this function returns #BT_CTF_NOTIF_ITER_STATUS_AGAIN, the caller
+ * should make sure that data becomes available to its medium, and
+ * call this function again, until another status is returned.
+ *
+ * @param notif_iter           CTF notification iterator
+ * @param notification         Returned notification if the function's
+ *                             return value is #BT_CTF_NOTIF_ITER_STATUS_OK
+ * @returns                    One of #bt_ctf_notif_iter_status values
+ */
+enum bt_ctf_notif_iter_status bt_ctf_notif_iter_get_next_notification(
+               struct bt_ctf_notif_iter *notif_iter,
+               struct bt_ctf_notif_iter_notif **notification);
+
+#endif /* CTF_NOTIF_ITER_H */
diff --git a/ctf-reader-proto/ctf-notif-iter/print.h b/ctf-reader-proto/ctf-notif-iter/print.h
new file mode 100644 (file)
index 0000000..bb5c62a
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef CTF_NOTIF_ITER_PRINT_H
+#define CTF_NOTIF_ITER_PRINT_H
+
+/*
+ * Define PRINT_PREFIX and PRINT_ERR_STREAM, then include this file.
+ *
+ * Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <babeltrace/babeltrace-internal.h>
+
+#define PERR(fmt, ...)                                                 \
+       do {                                                            \
+               if (PRINT_ERR_STREAM) {                                 \
+                       fprintf(PRINT_ERR_STREAM,                       \
+                               "Error: " PRINT_PREFIX ": " fmt,        \
+                               ##__VA_ARGS__);                         \
+               }                                                       \
+       } while (0)
+
+#define PWARN(fmt, ...)                                                        \
+       do {                                                            \
+               if (PRINT_ERR_STREAM) {                                 \
+                       fprintf(PRINT_ERR_STREAM,                       \
+                               "Warning: " PRINT_PREFIX ": " fmt,      \
+                               ##__VA_ARGS__);                         \
+               }                                                       \
+       } while (0)
+
+#define PDBG(fmt, ...)                                                 \
+       do {                                                            \
+               if (babeltrace_debug) {                                 \
+                       fprintf(stderr,                                 \
+                               "Debug: " PRINT_PREFIX ": " fmt,        \
+                               ##__VA_ARGS__);                         \
+               }                                                       \
+       } while (0)
+
+#endif /* CTF_NOTIF_ITER_PRINT_H */
diff --git a/ctf-reader-proto/metadata-parsing/Makefile.am b/ctf-reader-proto/metadata-parsing/Makefile.am
new file mode 100644 (file)
index 0000000..2195aff
--- /dev/null
@@ -0,0 +1,38 @@
+AM_CPPFLAGS = $(CPPFLAGS) -I$(top_srcdir)/include -I$(srcdir)
+AM_CFLAGS = $(PACKAGE_CFLAGS)
+BUILT_SOURCES = ctf-parser.h ctf-parser.c ctf-lexer.c
+AM_YFLAGS = -t -d -v
+
+noinst_LTLIBRARIES = libctf-parser.la libctf-ast.la
+
+noinst_HEADERS = \
+       ctf-scanner.h \
+       ctf-ast.h \
+       ctf-scanner-symbols.h
+
+libctf_parser_la_SOURCES = ctf-lexer.l ctf-parser.y objstack.c
+# ctf-scanner-symbols.h is included to prefix generated yy_* symbols
+# with bt_.
+libctf_parser_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir) \
+               -include $(srcdir)/ctf-scanner-symbols.h
+
+libctf_ast_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)
+libctf_ast_la_SOURCES = \
+               ctf-visitor-generate-ir.c \
+               ctf-visitor-semantic-validator.c \
+               ctf-visitor-parent-links.c
+libctf_ast_la_LIBADD = $(top_builddir)/lib/libbabeltrace.la
+
+if BABELTRACE_BUILD_WITH_LIBUUID
+libctf_ast_la_LIBADD += -luuid
+endif
+
+if BABELTRACE_BUILD_WITH_LIBC_UUID
+libctf_ast_la_LIBADD += -lc
+endif
+
+if BABELTRACE_BUILD_WITH_MINGW
+libctf_ast_la_LIBADD += -lrpcrt4 -lintl -liconv -lole32 -lpopt
+endif
+
+CLEANFILES = $(BUILT_SOURCES) ctf-parser.output
diff --git a/ctf-reader-proto/metadata-parsing/ctf-ast.h b/ctf-reader-proto/metadata-parsing/ctf-ast.h
new file mode 100644 (file)
index 0000000..c884327
--- /dev/null
@@ -0,0 +1,321 @@
+#ifndef _CTF_AST_H
+#define _CTF_AST_H
+
+/*
+ * ctf-ast.h
+ *
+ * Copyright 2011-2012 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <glib.h>
+#include <babeltrace/list.h>
+#include <babeltrace/ctf-ir/trace.h>
+
+// the parameter name (of the reentrant 'yyparse' function)
+// data is a pointer to a 'SParserParam' structure
+//#define YYPARSE_PARAM        scanner
+
+struct ctf_node;
+struct ctf_parser;
+
+#define FOREACH_CTF_NODES(F) \
+       F(NODE_UNKNOWN) \
+       F(NODE_ROOT) \
+       F(NODE_ERROR) \
+       F(NODE_EVENT) \
+       F(NODE_STREAM) \
+       F(NODE_ENV) \
+       F(NODE_TRACE) \
+       F(NODE_CLOCK) \
+       F(NODE_CALLSITE) \
+       F(NODE_CTF_EXPRESSION) \
+       F(NODE_UNARY_EXPRESSION) \
+       F(NODE_TYPEDEF) \
+       F(NODE_TYPEALIAS_TARGET) \
+       F(NODE_TYPEALIAS_ALIAS) \
+       F(NODE_TYPEALIAS) \
+       F(NODE_TYPE_SPECIFIER) \
+       F(NODE_TYPE_SPECIFIER_LIST) \
+       F(NODE_POINTER) \
+       F(NODE_TYPE_DECLARATOR) \
+       F(NODE_FLOATING_POINT) \
+       F(NODE_INTEGER) \
+       F(NODE_STRING) \
+       F(NODE_ENUMERATOR) \
+       F(NODE_ENUM) \
+       F(NODE_STRUCT_OR_VARIANT_DECLARATION) \
+       F(NODE_VARIANT) \
+       F(NODE_STRUCT)
+
+enum node_type {
+#define ENTRY(S)       S,
+       FOREACH_CTF_NODES(ENTRY)
+#undef ENTRY
+       NR_NODE_TYPES,
+};
+
+struct ctf_node {
+       /*
+        * Parent node is only set on demand by specific visitor.
+        */
+       struct ctf_node *parent;
+       struct bt_list_head siblings;
+       struct bt_list_head tmp_head;
+       unsigned int lineno;
+       /*
+        * We mark nodes visited in the generate-ir phase (last
+        * phase). We only mark the 1-depth level nodes as visited
+        * (never the root node, and not their sub-nodes). This allows
+        * skipping already visited nodes when doing incremental
+        * metadata append.
+        */
+       int visited;
+
+       enum node_type type;
+       union {
+               struct {
+               } unknown;
+               struct {
+                       /*
+                        * Children nodes are ctf_expression, typedef,
+                        * typealias and type_specifier_list.
+                        */
+                       struct bt_list_head declaration_list;
+                       struct bt_list_head trace;
+                       struct bt_list_head env;
+                       struct bt_list_head stream;
+                       struct bt_list_head event;
+                       struct bt_list_head clock;
+                       struct bt_list_head callsite;
+               } root;
+               struct {
+                       /*
+                        * Children nodes are ctf_expression, typedef,
+                        * typealias and type_specifier_list.
+                        */
+                       struct bt_list_head declaration_list;
+               } event;
+               struct {
+                       /*
+                        * Children nodes are ctf_expression, typedef,
+                        * typealias and type_specifier_list.
+                        */
+                       struct bt_list_head declaration_list;
+               } stream;
+               struct {
+                       /*
+                        * Children nodes are ctf_expression, typedef,
+                        * typealias and type_specifier_list.
+                        */
+                       struct bt_list_head declaration_list;
+               } env;
+               struct {
+                       /*
+                        * Children nodes are ctf_expression, typedef,
+                        * typealias and type_specifier_list.
+                        */
+                       struct bt_list_head declaration_list;
+               } trace;
+               struct {
+                       /*
+                        * Children nodes are ctf_expression, typedef,
+                        * typealias and type_specifier_list.
+                        */
+                       struct bt_list_head declaration_list;
+               } clock;
+               struct {
+                       /*
+                        * Children nodes are ctf_expression, typedef,
+                        * typealias and type_specifier_list.
+                        */
+                       struct bt_list_head declaration_list;
+               } callsite;
+               struct {
+                       struct bt_list_head left;       /* Should be string */
+                       struct bt_list_head right;      /* Unary exp. or type */
+               } ctf_expression;
+               struct {
+                       enum {
+                               UNARY_UNKNOWN = 0,
+                               UNARY_STRING,
+                               UNARY_SIGNED_CONSTANT,
+                               UNARY_UNSIGNED_CONSTANT,
+                               UNARY_SBRAC,
+                       } type;
+                       union {
+                               /*
+                                * string for identifier, id_type, keywords,
+                                * string literals and character constants.
+                                */
+                               char *string;
+                               int64_t signed_constant;
+                               uint64_t unsigned_constant;
+                               struct ctf_node *sbrac_exp;
+                       } u;
+                       enum {
+                               UNARY_LINK_UNKNOWN = 0,
+                               UNARY_DOTLINK,
+                               UNARY_ARROWLINK,
+                               UNARY_DOTDOTDOT,
+                       } link;
+               } unary_expression;
+               struct {
+                       struct ctf_node *type_specifier_list;
+                       struct bt_list_head type_declarators;
+               } _typedef;
+               /* new type is "alias", existing type "target" */
+               struct {
+                       struct ctf_node *type_specifier_list;
+                       struct bt_list_head type_declarators;
+               } typealias_target;
+               struct {
+                       struct ctf_node *type_specifier_list;
+                       struct bt_list_head type_declarators;
+               } typealias_alias;
+               struct {
+                       struct ctf_node *target;
+                       struct ctf_node *alias;
+               } typealias;
+               struct {
+                       enum {
+                               TYPESPEC_UNKNOWN = 0,
+                               TYPESPEC_VOID,
+                               TYPESPEC_CHAR,
+                               TYPESPEC_SHORT,
+                               TYPESPEC_INT,
+                               TYPESPEC_LONG,
+                               TYPESPEC_FLOAT,
+                               TYPESPEC_DOUBLE,
+                               TYPESPEC_SIGNED,
+                               TYPESPEC_UNSIGNED,
+                               TYPESPEC_BOOL,
+                               TYPESPEC_COMPLEX,
+                               TYPESPEC_IMAGINARY,
+                               TYPESPEC_CONST,
+                               TYPESPEC_ID_TYPE,
+                               TYPESPEC_FLOATING_POINT,
+                               TYPESPEC_INTEGER,
+                               TYPESPEC_STRING,
+                               TYPESPEC_STRUCT,
+                               TYPESPEC_VARIANT,
+                               TYPESPEC_ENUM,
+                       } type;
+                       /* For struct, variant and enum */
+                       struct ctf_node *node;
+                       const char *id_type;
+               } type_specifier;
+               struct {
+                       /* list of type_specifier */
+                       struct bt_list_head head;
+               } type_specifier_list;
+               struct {
+                       unsigned int const_qualifier;
+               } pointer;
+               struct {
+                       struct bt_list_head pointers;
+                       enum {
+                               TYPEDEC_UNKNOWN = 0,
+                               TYPEDEC_ID,     /* identifier */
+                               TYPEDEC_NESTED, /* (), array or sequence */
+                       } type;
+                       union {
+                               char *id;
+                               struct {
+                                       /* typedec has no pointer list */
+                                       struct ctf_node *type_declarator;
+                                       /*
+                                        * unary expression (value) or
+                                        * type_specifier_list.
+                                        */
+                                       struct bt_list_head length;
+                                       /* for abstract type declarator */
+                                       unsigned int abstract_array;
+                               } nested;
+                       } u;
+                       struct ctf_node *bitfield_len;
+               } type_declarator;
+               struct {
+                       /* Children nodes are ctf_expression. */
+                       struct bt_list_head expressions;
+               } floating_point;
+               struct {
+                       /* Children nodes are ctf_expression. */
+                       struct bt_list_head expressions;
+               } integer;
+               struct {
+                       /* Children nodes are ctf_expression. */
+                       struct bt_list_head expressions;
+               } string;
+               struct {
+                       char *id;
+                       /*
+                        * Range list or single value node. Contains unary
+                        * expressions.
+                        */
+                       struct bt_list_head values;
+               } enumerator;
+               struct {
+                       char *enum_id;
+                       /*
+                        * Either NULL, or points to unary expression or
+                        * type_specifier_list.
+                        */
+                       struct ctf_node *container_type;
+                       struct bt_list_head enumerator_list;
+                       int has_body;
+               } _enum;
+               struct {
+                       struct ctf_node *type_specifier_list;
+                       struct bt_list_head type_declarators;
+               } struct_or_variant_declaration;
+               struct {
+                       char *name;
+                       char *choice;
+                       /* list of typedef, typealias and declarations */
+                       struct bt_list_head declaration_list;
+                       int has_body;
+               } variant;
+               struct {
+                       char *name;
+                       /* list of typedef, typealias and declarations */
+                       struct bt_list_head declaration_list;
+                       int has_body;
+                       struct bt_list_head min_align;  /* align() attribute */
+               } _struct;
+       } u;
+};
+
+struct ctf_ast {
+       struct ctf_node root;
+};
+
+const char *node_type(struct ctf_node *node);
+
+struct ctf_trace;
+
+BT_HIDDEN
+int ctf_visitor_generate_ir(FILE *efd, struct ctf_node *node,
+               struct bt_ctf_trace **trace);
+
+BT_HIDDEN
+int ctf_visitor_semantic_check(FILE *fd, int depth, struct ctf_node *node);
+
+BT_HIDDEN
+int ctf_visitor_parent_links(FILE *fd, int depth, struct ctf_node *node);
+
+BT_HIDDEN
+int ctf_destroy_metadata(struct ctf_trace *trace);
+
+#endif /* _CTF_AST_H */
diff --git a/ctf-reader-proto/metadata-parsing/ctf-lexer.l b/ctf-reader-proto/metadata-parsing/ctf-lexer.l
new file mode 100644 (file)
index 0000000..6b605a0
--- /dev/null
@@ -0,0 +1,142 @@
+%{
+/*
+ * ctf-lexer.l
+ *
+ * Common Trace Formal Lexer
+ *
+ * Copyright 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <babeltrace/babeltrace-internal.h>
+#include "ctf-scanner.h"
+#include "ctf-parser.h"
+#include "ctf-ast.h"
+
+#define PARSE_INTEGER_LITERAL(base)                                    \
+       do {                                                            \
+               errno = 0;                                              \
+               yylval->ull = strtoull(yytext, NULL, base);             \
+               if (errno) {                                            \
+                       printfl_perror(yylineno, "Integer literal");    \
+                       return ERROR;                                   \
+               }                                                       \
+       } while (0)
+
+BT_HIDDEN
+void setstring(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src);
+
+static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
+       __attribute__((unused));
+static int input (yyscan_t yyscanner) __attribute__((unused));
+
+BT_HIDDEN
+int import_string(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src, char delim);
+
+%}
+
+%x comment_ml comment_sl string_lit char_const
+%option reentrant yylineno noyywrap bison-bridge
+%option extra-type="struct ctf_scanner *"
+       /* bison-locations */
+INTEGER_SUFFIX                 (U|UL|ULL|LU|LLU|Ul|Ull|lU|llU|u|uL|uLL|Lu|LLu|ul|ull|lu|llu)
+DIGIT                          [0-9]
+NONDIGIT                       [a-zA-Z_]
+HEXDIGIT                       [0-9A-Fa-f]
+OCTALDIGIT                     [0-7]
+UCHARLOWERCASE                 \\u{HEXDIGIT}{4}
+UCHARUPPERCASE                 \\U{HEXDIGIT}{8}
+ID_NONDIGIT                    {NONDIGIT}|{UCHARLOWERCASE}|{UCHARUPPERCASE}
+IDENTIFIER                     {ID_NONDIGIT}({ID_NONDIGIT}|{DIGIT})*
+%%
+
+                               /*
+                                * Using start conditions to deal with comments
+                                * and strings.
+                                */ 
+
+"/*"                           BEGIN(comment_ml);
+<comment_ml>[^*\n]*            /* eat anything that's not a '*' */
+<comment_ml>"*"+[^*/\n]*       /* eat up '*'s not followed by '/'s */
+<comment_ml>\n
+<comment_ml>"*"+"/"            BEGIN(INITIAL);
+
+"//"[^\n]*\n                   /* skip comment */
+
+L?\"(\\.|[^\\"])*\"            { if (import_string(yyextra, yylval, yytext, '\"') < 0) return ERROR; else return STRING_LITERAL; }
+L?\'(\\.|[^\\'])*\'            { if (import_string(yyextra, yylval, yytext, '\'') < 0) return ERROR; else return CHARACTER_LITERAL; }
+
+"["                            return LSBRAC;
+"]"                            return RSBRAC;
+"("                            return LPAREN;
+")"                            return RPAREN;
+"{"                            return LBRAC;
+"}"                            return RBRAC;
+"->"                           return RARROW;
+"*"                            return STAR;
+"+"                            return PLUS;
+"-"                            return MINUS;
+"<"                            return LT;
+">"                            return GT;
+:=                             return TYPEASSIGN;
+:                              return COLON;
+;                              return SEMICOLON;
+"..."                          return DOTDOTDOT;
+"."                            return DOT;
+=                              return EQUAL;
+","                            return COMMA;
+align                          setstring(yyextra, yylval, yytext); return TOK_ALIGN;
+const                          setstring(yyextra, yylval, yytext); return CONST;
+char                           setstring(yyextra, yylval, yytext); return CHAR;
+clock                          setstring(yyextra, yylval, yytext); return CLOCK;
+double                         setstring(yyextra, yylval, yytext); return DOUBLE;
+enum                           setstring(yyextra, yylval, yytext); return ENUM;
+env                            setstring(yyextra, yylval, yytext); return ENV;
+event                          setstring(yyextra, yylval, yytext); return EVENT;
+floating_point                 setstring(yyextra, yylval, yytext); return FLOATING_POINT;
+float                          setstring(yyextra, yylval, yytext); return FLOAT;
+integer                                setstring(yyextra, yylval, yytext); return INTEGER;
+int                            setstring(yyextra, yylval, yytext); return INT;
+long                           setstring(yyextra, yylval, yytext); return LONG;
+short                          setstring(yyextra, yylval, yytext); return SHORT;
+signed                         setstring(yyextra, yylval, yytext); return SIGNED;
+stream                         setstring(yyextra, yylval, yytext); return STREAM;
+string                         setstring(yyextra, yylval, yytext); return STRING;
+struct                         setstring(yyextra, yylval, yytext); return STRUCT;
+trace                          setstring(yyextra, yylval, yytext); return TRACE;
+callsite                       setstring(yyextra, yylval, yytext); return CALLSITE;
+typealias                      setstring(yyextra, yylval, yytext); return TYPEALIAS;
+typedef                                setstring(yyextra, yylval, yytext); return TYPEDEF;
+unsigned                       setstring(yyextra, yylval, yytext); return UNSIGNED;
+variant                                setstring(yyextra, yylval, yytext); return VARIANT;
+void                           setstring(yyextra, yylval, yytext); return VOID;
+_Bool                          setstring(yyextra, yylval, yytext); return _BOOL;
+_Complex                       setstring(yyextra, yylval, yytext); return _COMPLEX;
+_Imaginary                     setstring(yyextra, yylval, yytext); return _IMAGINARY;
+[1-9]{DIGIT}*{INTEGER_SUFFIX}? PARSE_INTEGER_LITERAL(10); return INTEGER_LITERAL;
+0{OCTALDIGIT}*{INTEGER_SUFFIX}?        PARSE_INTEGER_LITERAL(8); return INTEGER_LITERAL;
+0[xX]{HEXDIGIT}+{INTEGER_SUFFIX}?      PARSE_INTEGER_LITERAL(16); return INTEGER_LITERAL;
+
+{IDENTIFIER}                   printf_debug("<IDENTIFIER %s>\n", yytext); setstring(yyextra, yylval, yytext); if (is_type(yyextra, yytext)) return ID_TYPE; else return IDENTIFIER;
+[ \t\r\n]                      ; /* ignore */
+.                              printfl_error(yylineno, "invalid character '0x%02X'", yytext[0]);  return ERROR;
+%%
diff --git a/ctf-reader-proto/metadata-parsing/ctf-parser.y b/ctf-reader-proto/metadata-parsing/ctf-parser.y
new file mode 100644 (file)
index 0000000..821f0f7
--- /dev/null
@@ -0,0 +1,2586 @@
+%{
+/*
+ * ctf-parser.y
+ *
+ * Common Trace Format Metadata Grammar.
+ *
+ * Copyright 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <glib.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <babeltrace/list.h>
+#include <babeltrace/babeltrace-internal.h>
+#include "ctf-scanner.h"
+#include "ctf-parser.h"
+#include "ctf-ast.h"
+#include "objstack.h"
+
+BT_HIDDEN
+int yydebug;
+
+/* Join two lists, put "add" at the end of "head".  */
+static inline void
+_bt_list_splice_tail (struct bt_list_head *add, struct bt_list_head *head)
+{
+       /* Do nothing if the list which gets added is empty.  */
+       if (add != add->next) {
+               add->next->prev = head->prev;
+               add->prev->next = head;
+               head->prev->next = add->next;
+               head->prev = add->prev;
+       }
+}
+
+BT_HIDDEN
+int yyparse(struct ctf_scanner *scanner, yyscan_t yyscanner);
+BT_HIDDEN
+int yylex(union YYSTYPE *yyval, yyscan_t yyscanner);
+BT_HIDDEN
+int yylex_init_extra(struct ctf_scanner *scanner, yyscan_t * ptr_yy_globals);
+BT_HIDDEN
+int yylex_destroy(yyscan_t yyscanner);
+BT_HIDDEN
+void yyrestart(FILE * in_str, yyscan_t yyscanner);
+BT_HIDDEN
+int yyget_lineno(yyscan_t yyscanner);
+BT_HIDDEN
+char *yyget_text(yyscan_t yyscanner);
+
+static const char *node_type_to_str[] = {
+#define ENTRY(S)       [S] = #S,
+       FOREACH_CTF_NODES(ENTRY)
+#undef ENTRY
+};
+
+/*
+ * Static node for out of memory errors. Only "type" is used. lineno is
+ * always left at 0. The rest of the node content can be overwritten,
+ * but is never used.
+ */
+static struct ctf_node error_node = {
+       .type = NODE_ERROR,
+};
+
+BT_HIDDEN
+const char *node_type(struct ctf_node *node)
+{
+       if (node->type < NR_NODE_TYPES)
+               return node_type_to_str[node->type];
+       else
+               return NULL;
+}
+
+void setstring(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src)
+{
+       lvalp->s = objstack_alloc(scanner->objstack, strlen(src) + 1);
+       strcpy(lvalp->s, src);
+}
+
+static
+int str_check(size_t str_len, size_t offset, size_t len)
+{
+       /* check overflow */
+       if (offset + len < offset)
+               return -1;
+       if (offset + len > str_len)
+               return -1;
+       return 0;
+}
+
+static
+int bt_isodigit(int c)
+{
+       switch (c) {
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static
+int parse_base_sequence(const char *src, size_t len, size_t pos,
+               char *buffer, size_t *buf_len, int base)
+{
+       const size_t max_char = 3;
+       int nr_char = 0;
+
+       while (!str_check(len, pos, 1) && nr_char < max_char) {
+               char c = src[pos++];
+
+               if (base == 8) {
+                       if (bt_isodigit(c))
+                               buffer[nr_char++] = c;
+                       else
+                               break;
+               } else if (base == 16) {
+                       if (isxdigit(c))
+                               buffer[nr_char++] = c;
+                       else
+                               break;
+
+               } else {
+                       /* Unsupported base */
+                       return -1;
+               }
+       }
+       assert(nr_char > 0);
+       buffer[nr_char] = '\0';
+       *buf_len = nr_char;
+       return 0;
+}
+
+static
+int import_basic_string(struct ctf_scanner *scanner, YYSTYPE *lvalp,
+               size_t len, const char *src, char delim)
+{
+       size_t pos = 0, dpos = 0;
+
+       if (str_check(len, pos, 1))
+               return -1;
+       if (src[pos++] != delim)
+               return -1;
+
+       while (src[pos] != delim) {
+               char c;
+
+               if (str_check(len, pos, 1))
+                       return -1;
+               c = src[pos++];
+               if (c == '\\') {
+                       if (str_check(len, pos, 1))
+                               return -1;
+                       c = src[pos++];
+
+                       switch (c) {
+                       case 'a':
+                               c = '\a';
+                               break;
+                       case 'b':
+                               c = '\b';
+                               break;
+                       case 'f':
+                               c = '\f';
+                               break;
+                       case 'n':
+                               c = '\n';
+                               break;
+                       case 'r':
+                               c = '\r';
+                               break;
+                       case 't':
+                               c = '\t';
+                               break;
+                       case 'v':
+                               c = '\v';
+                               break;
+                       case '\\':
+                               c = '\\';
+                               break;
+                       case '\'':
+                               c = '\'';
+                               break;
+                       case '\"':
+                               c = '\"';
+                               break;
+                       case '?':
+                               c = '?';
+                               break;
+                       case '0':
+                       case '1':
+                       case '2':
+                       case '3':
+                       case '4':
+                       case '5':
+                       case '6':
+                       case '7':
+                       {
+                               char oct_buffer[4];
+                               size_t oct_len;
+
+                               if (parse_base_sequence(src, len, pos - 1,
+                                               oct_buffer, &oct_len, 8))
+                                       return -1;
+                               c = strtoul(&oct_buffer[0], NULL, 8);
+                               pos += oct_len - 1;
+                               break;
+                       }
+                       case 'x':
+                       {
+                               char hex_buffer[4];
+                               size_t hex_len;
+
+                               if (parse_base_sequence(src, len, pos,
+                                               hex_buffer, &hex_len, 16))
+                                       return -1;
+                               c = strtoul(&hex_buffer[0], NULL, 16);
+                               pos += hex_len;
+                               break;
+                       }
+                       default:
+                               return -1;
+                       }
+               }
+               if (str_check(len, dpos, 1))
+                       return -1;
+               lvalp->s[dpos++] = c;
+       }
+
+       if (str_check(len, dpos, 1))
+               return -1;
+       lvalp->s[dpos++] = '\0';
+
+       if (str_check(len, pos, 1))
+               return -1;
+       if (src[pos++] != delim)
+               return -1;
+
+       if (str_check(len, pos, 1))
+               return -1;
+       if (src[pos] != '\0')
+               return -1;
+       return 0;
+}
+
+int import_string(struct ctf_scanner *scanner, YYSTYPE *lvalp,
+               const char *src, char delim)
+{
+       size_t len;
+
+       len = strlen(src) + 1;
+       lvalp->s = objstack_alloc(scanner->objstack, len);
+       if (src[0] == 'L') {
+               // TODO: import wide string
+               printfl_error(yyget_lineno(scanner),
+                       "Wide string not supported yet.");
+               return -1;
+       } else {
+               return import_basic_string(scanner, lvalp, len, src, delim);
+       }
+}
+
+static void init_scope(struct ctf_scanner_scope *scope,
+                      struct ctf_scanner_scope *parent)
+{
+       scope->parent = parent;
+       scope->types = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                            NULL, NULL);
+}
+
+static void finalize_scope(struct ctf_scanner_scope *scope)
+{
+       g_hash_table_destroy(scope->types);
+}
+
+static void push_scope(struct ctf_scanner *scanner)
+{
+       struct ctf_scanner_scope *ns;
+
+       printf_debug("push scope\n");
+       ns = malloc(sizeof(struct ctf_scanner_scope));
+       init_scope(ns, scanner->cs);
+       scanner->cs = ns;
+}
+
+static void pop_scope(struct ctf_scanner *scanner)
+{
+       struct ctf_scanner_scope *os;
+
+       printf_debug("pop scope\n");
+       os = scanner->cs;
+       scanner->cs = os->parent;
+       finalize_scope(os);
+       free(os);
+}
+
+static int lookup_type(struct ctf_scanner_scope *s, const char *id)
+{
+       int ret;
+
+       ret = (int) (long) g_hash_table_lookup(s->types, id);
+       printf_debug("lookup %p %s %d\n", s, id, ret);
+       return ret;
+}
+
+BT_HIDDEN
+int is_type(struct ctf_scanner *scanner, const char *id)
+{
+       struct ctf_scanner_scope *it;
+       int ret = 0;
+
+       for (it = scanner->cs; it != NULL; it = it->parent) {
+               if (lookup_type(it, id)) {
+                       ret = 1;
+                       break;
+               }
+       }
+       printf_debug("is type %s %d\n", id, ret);
+       return ret;
+}
+
+static void add_type(struct ctf_scanner *scanner, char *id)
+{
+       printf_debug("add type %s\n", id);
+       if (lookup_type(scanner->cs, id))
+               return;
+       g_hash_table_insert(scanner->cs->types, id, id);
+}
+
+static struct ctf_node *make_node(struct ctf_scanner *scanner,
+                                 enum node_type type)
+{
+       struct ctf_node *node;
+
+       node = objstack_alloc(scanner->objstack, sizeof(*node));
+       if (!node) {
+               printfl_fatal(yyget_lineno(scanner->scanner), "out of memory");
+               return &error_node;
+       }
+       node->type = type;
+       node->lineno = yyget_lineno(scanner->scanner);
+       BT_INIT_LIST_HEAD(&node->tmp_head);
+       bt_list_add(&node->siblings, &node->tmp_head);
+
+       switch (type) {
+       case NODE_ROOT:
+               node->type = NODE_ERROR;
+               printfn_fatal(node, "trying to create root node");
+               break;
+
+       case NODE_EVENT:
+               BT_INIT_LIST_HEAD(&node->u.event.declaration_list);
+               break;
+       case NODE_STREAM:
+               BT_INIT_LIST_HEAD(&node->u.stream.declaration_list);
+               break;
+       case NODE_ENV:
+               BT_INIT_LIST_HEAD(&node->u.env.declaration_list);
+               break;
+       case NODE_TRACE:
+               BT_INIT_LIST_HEAD(&node->u.trace.declaration_list);
+               break;
+       case NODE_CLOCK:
+               BT_INIT_LIST_HEAD(&node->u.clock.declaration_list);
+               break;
+       case NODE_CALLSITE:
+               BT_INIT_LIST_HEAD(&node->u.callsite.declaration_list);
+               break;
+
+       case NODE_CTF_EXPRESSION:
+               BT_INIT_LIST_HEAD(&node->u.ctf_expression.left);
+               BT_INIT_LIST_HEAD(&node->u.ctf_expression.right);
+               break;
+       case NODE_UNARY_EXPRESSION:
+               break;
+
+       case NODE_TYPEDEF:
+               BT_INIT_LIST_HEAD(&node->u._typedef.type_declarators);
+               break;
+       case NODE_TYPEALIAS_TARGET:
+               BT_INIT_LIST_HEAD(&node->u.typealias_target.type_declarators);
+               break;
+       case NODE_TYPEALIAS_ALIAS:
+               BT_INIT_LIST_HEAD(&node->u.typealias_alias.type_declarators);
+               break;
+       case NODE_TYPEALIAS:
+               break;
+
+       case NODE_TYPE_SPECIFIER:
+               break;
+       case NODE_TYPE_SPECIFIER_LIST:
+               BT_INIT_LIST_HEAD(&node->u.type_specifier_list.head);
+               break;
+       case NODE_POINTER:
+               break;
+       case NODE_TYPE_DECLARATOR:
+               BT_INIT_LIST_HEAD(&node->u.type_declarator.pointers);
+               break;
+
+       case NODE_FLOATING_POINT:
+               BT_INIT_LIST_HEAD(&node->u.floating_point.expressions);
+               break;
+       case NODE_INTEGER:
+               BT_INIT_LIST_HEAD(&node->u.integer.expressions);
+               break;
+       case NODE_STRING:
+               BT_INIT_LIST_HEAD(&node->u.string.expressions);
+               break;
+       case NODE_ENUMERATOR:
+               BT_INIT_LIST_HEAD(&node->u.enumerator.values);
+               break;
+       case NODE_ENUM:
+               BT_INIT_LIST_HEAD(&node->u._enum.enumerator_list);
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               BT_INIT_LIST_HEAD(&node->u.struct_or_variant_declaration.type_declarators);
+               break;
+       case NODE_VARIANT:
+               BT_INIT_LIST_HEAD(&node->u.variant.declaration_list);
+               break;
+       case NODE_STRUCT:
+               BT_INIT_LIST_HEAD(&node->u._struct.declaration_list);
+               BT_INIT_LIST_HEAD(&node->u._struct.min_align);
+               break;
+
+       case NODE_UNKNOWN:
+       default:
+               node->type = NODE_ERROR;
+               printfn_fatal(node, "unknown node type '%d'", (int) type);
+               break;
+       }
+
+       return node;
+}
+
+static int reparent_ctf_expression(struct ctf_node *node,
+                                  struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_EVENT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list);
+               break;
+       case NODE_STREAM:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list);
+               break;
+       case NODE_ENV:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list);
+               break;
+       case NODE_TRACE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list);
+               break;
+       case NODE_CLOCK:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list);
+               break;
+       case NODE_CALLSITE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list);
+               break;
+       case NODE_FLOATING_POINT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.floating_point.expressions);
+               break;
+       case NODE_INTEGER:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.integer.expressions);
+               break;
+       case NODE_STRING:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.string.expressions);
+               break;
+
+       case NODE_ROOT:
+       case NODE_CTF_EXPRESSION:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_TYPEALIAS:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_TYPE_SPECIFIER_LIST:
+       case NODE_POINTER:
+       case NODE_TYPE_DECLARATOR:
+       case NODE_ENUMERATOR:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               printfn_fatal(node, "unknown node type '%d'", (int) parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int reparent_typedef(struct ctf_node *node, struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_ROOT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.root.declaration_list);
+               break;
+       case NODE_EVENT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list);
+               break;
+       case NODE_STREAM:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list);
+               break;
+       case NODE_ENV:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list);
+               break;
+       case NODE_TRACE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list);
+               break;
+       case NODE_CLOCK:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list);
+               break;
+       case NODE_CALLSITE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list);
+               break;
+       case NODE_VARIANT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list);
+               break;
+       case NODE_STRUCT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list);
+               break;
+
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_CTF_EXPRESSION:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_TYPEALIAS:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_TYPE_SPECIFIER_LIST:
+       case NODE_POINTER:
+       case NODE_TYPE_DECLARATOR:
+       case NODE_ENUMERATOR:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               printfn_fatal(node, "unknown node type %d", parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int reparent_typealias(struct ctf_node *node, struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_ROOT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.root.declaration_list);
+               break;
+       case NODE_EVENT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list);
+               break;
+       case NODE_STREAM:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list);
+               break;
+       case NODE_ENV:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list);
+               break;
+       case NODE_TRACE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list);
+               break;
+       case NODE_CLOCK:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list);
+               break;
+       case NODE_CALLSITE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list);
+               break;
+       case NODE_VARIANT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list);
+               break;
+       case NODE_STRUCT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list);
+               break;
+
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_CTF_EXPRESSION:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_TYPEALIAS:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_TYPE_SPECIFIER_LIST:
+       case NODE_POINTER:
+       case NODE_TYPE_DECLARATOR:
+       case NODE_ENUMERATOR:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               printfn_fatal(node, "unknown node type '%d'", (int) parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int reparent_type_specifier(struct ctf_node *node,
+                                  struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_TYPE_SPECIFIER_LIST:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.type_specifier_list.head);
+               break;
+
+       case NODE_TYPE_SPECIFIER:
+       case NODE_EVENT:
+       case NODE_STREAM:
+       case NODE_ENV:
+       case NODE_TRACE:
+       case NODE_CLOCK:
+       case NODE_CALLSITE:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_TYPE_DECLARATOR:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_TYPEALIAS:
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_CTF_EXPRESSION:
+       case NODE_POINTER:
+       case NODE_ENUMERATOR:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               printfn_fatal(node, "unknown node type '%d'", (int) parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int reparent_type_specifier_list(struct ctf_node *node,
+                                       struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_ROOT:
+               bt_list_add_tail(&node->siblings, &parent->u.root.declaration_list);
+               break;
+       case NODE_EVENT:
+               bt_list_add_tail(&node->siblings, &parent->u.event.declaration_list);
+               break;
+       case NODE_STREAM:
+               bt_list_add_tail(&node->siblings, &parent->u.stream.declaration_list);
+               break;
+       case NODE_ENV:
+               bt_list_add_tail(&node->siblings, &parent->u.env.declaration_list);
+               break;
+       case NODE_TRACE:
+               bt_list_add_tail(&node->siblings, &parent->u.trace.declaration_list);
+               break;
+       case NODE_CLOCK:
+               bt_list_add_tail(&node->siblings, &parent->u.clock.declaration_list);
+               break;
+       case NODE_CALLSITE:
+               bt_list_add_tail(&node->siblings, &parent->u.callsite.declaration_list);
+               break;
+       case NODE_VARIANT:
+               bt_list_add_tail(&node->siblings, &parent->u.variant.declaration_list);
+               break;
+       case NODE_STRUCT:
+               bt_list_add_tail(&node->siblings, &parent->u._struct.declaration_list);
+               break;
+       case NODE_TYPEDEF:
+               parent->u._typedef.type_specifier_list = node;
+               break;
+       case NODE_TYPEALIAS_TARGET:
+               parent->u.typealias_target.type_specifier_list = node;
+               break;
+       case NODE_TYPEALIAS_ALIAS:
+               parent->u.typealias_alias.type_specifier_list = node;
+               break;
+       case NODE_ENUM:
+               parent->u._enum.container_type = node;
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               parent->u.struct_or_variant_declaration.type_specifier_list = node;
+               break;
+       case NODE_TYPE_DECLARATOR:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_TYPEALIAS:
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_CTF_EXPRESSION:
+       case NODE_POINTER:
+       case NODE_ENUMERATOR:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               printfn_fatal(node, "unknown node type '%d'", (int) parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int reparent_type_declarator(struct ctf_node *node,
+                                   struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_TYPE_DECLARATOR:
+               parent->u.type_declarator.type = TYPEDEC_NESTED;
+               parent->u.type_declarator.u.nested.type_declarator = node;
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.struct_or_variant_declaration.type_declarators);
+               break;
+       case NODE_TYPEDEF:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u._typedef.type_declarators);
+               break;
+       case NODE_TYPEALIAS_TARGET:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.typealias_target.type_declarators);
+               break;
+       case NODE_TYPEALIAS_ALIAS:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.typealias_alias.type_declarators);
+               break;
+
+       case NODE_ROOT:
+       case NODE_EVENT:
+       case NODE_STREAM:
+       case NODE_ENV:
+       case NODE_TRACE:
+       case NODE_CLOCK:
+       case NODE_CALLSITE:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+       case NODE_TYPEALIAS:
+       case NODE_ENUM:
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_CTF_EXPRESSION:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_TYPE_SPECIFIER_LIST:
+       case NODE_POINTER:
+       case NODE_ENUMERATOR:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               printfn_fatal(node, "unknown node type '%d'", (int) parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/*
+ * set_parent_node
+ *
+ * Link node to parent. Returns 0 on success, -EPERM if it is not permitted to
+ * create the link declared by the input, -ENOENT if node or parent is NULL,
+ * -EINVAL if there is an internal structure problem.
+ */
+static int set_parent_node(struct ctf_node *node,
+                        struct ctf_node *parent)
+{
+       if (!node || !parent)
+               return -ENOENT;
+
+       /* Note: Linking to parent will be done only by an external visitor */
+
+       switch (node->type) {
+       case NODE_ROOT:
+               printfn_fatal(node, "trying to reparent root node");
+               return -EINVAL;
+
+       case NODE_EVENT:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.event);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_STREAM:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.stream);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_ENV:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.env);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_TRACE:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.trace);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_CLOCK:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.clock);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_CALLSITE:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.callsite);
+               } else {
+                       return -EPERM;
+               }
+               break;
+
+       case NODE_CTF_EXPRESSION:
+               return reparent_ctf_expression(node, parent);
+       case NODE_UNARY_EXPRESSION:
+               if (parent->type == NODE_TYPE_DECLARATOR)
+                       parent->u.type_declarator.bitfield_len = node;
+               else
+                       return -EPERM;
+               break;
+
+       case NODE_TYPEDEF:
+               return reparent_typedef(node, parent);
+       case NODE_TYPEALIAS_TARGET:
+               if (parent->type == NODE_TYPEALIAS)
+                       parent->u.typealias.target = node;
+               else
+                       return -EINVAL;
+       case NODE_TYPEALIAS_ALIAS:
+               if (parent->type == NODE_TYPEALIAS)
+                       parent->u.typealias.alias = node;
+               else
+                       return -EINVAL;
+       case NODE_TYPEALIAS:
+               return reparent_typealias(node, parent);
+
+       case NODE_POINTER:
+               if (parent->type == NODE_TYPE_DECLARATOR) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.type_declarator.pointers);
+               } else
+                       return -EPERM;
+               break;
+       case NODE_TYPE_DECLARATOR:
+               return reparent_type_declarator(node, parent);
+
+       case NODE_TYPE_SPECIFIER_LIST:
+               return reparent_type_specifier_list(node, parent);
+
+       case NODE_TYPE_SPECIFIER:
+               return reparent_type_specifier(node, parent);
+
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_ENUM:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+               return -EINVAL; /* Dealt with internally within grammar */
+
+       case NODE_ENUMERATOR:
+               if (parent->type == NODE_ENUM) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u._enum.enumerator_list);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               switch (parent->type) {
+               case NODE_STRUCT:
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list);
+                       break;
+               case NODE_VARIANT:
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+
+       case NODE_UNKNOWN:
+       default:
+               printfn_fatal(node, "unknown node type '%d'", (int) parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+BT_HIDDEN
+void yyerror(struct ctf_scanner *scanner, yyscan_t yyscanner, const char *str)
+{
+       printfl_error(yyget_lineno(scanner->scanner),
+               "token \"%s\": %s\n",
+               yyget_text(scanner->scanner), str);
+}
+BT_HIDDEN
+int yywrap(void)
+{
+       return 1;
+} 
+
+#define reparent_error(scanner, str)                           \
+do {                                                           \
+       yyerror(scanner, scanner->scanner, YY_("reparent_error: " str)); \
+       YYERROR;                                                \
+} while (0)
+
+static struct ctf_ast *ctf_ast_alloc(struct ctf_scanner *scanner)
+{
+       struct ctf_ast *ast;
+
+       ast = objstack_alloc(scanner->objstack, sizeof(*ast));
+       if (!ast)
+               return NULL;
+       ast->root.type = NODE_ROOT;
+       BT_INIT_LIST_HEAD(&ast->root.tmp_head);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.declaration_list);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.trace);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.env);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.stream);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.event);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.clock);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.callsite);
+       return ast;
+}
+
+int ctf_scanner_append_ast(struct ctf_scanner *scanner, FILE *input)
+{
+       /* Start processing new stream */
+       yyrestart(input, scanner->scanner);
+       if (yydebug)
+               fprintf(stdout, "Scanner input is a%s.\n",
+                       isatty(fileno(input)) ? "n interactive tty" :
+                                               " noninteractive file");
+       return yyparse(scanner, scanner->scanner);
+}
+
+struct ctf_scanner *ctf_scanner_alloc(void)
+{
+       struct ctf_scanner *scanner;
+       int ret;
+
+       yydebug = babeltrace_debug;
+
+       scanner = malloc(sizeof(*scanner));
+       if (!scanner)
+               return NULL;
+       memset(scanner, 0, sizeof(*scanner));
+       ret = yylex_init_extra(scanner, &scanner->scanner);
+       if (ret) {
+               printf_fatal("yylex_init error");
+               goto cleanup_scanner;
+       }
+       scanner->objstack = objstack_create();
+       if (!scanner->objstack)
+               goto cleanup_lexer;
+       scanner->ast = ctf_ast_alloc(scanner);
+       if (!scanner->ast)
+               goto cleanup_objstack;
+       init_scope(&scanner->root_scope, NULL);
+       scanner->cs = &scanner->root_scope;
+
+       return scanner;
+
+cleanup_objstack:
+       objstack_destroy(scanner->objstack);
+cleanup_lexer:
+       ret = yylex_destroy(scanner->scanner);
+       if (!ret)
+               printf_fatal("yylex_destroy error");
+cleanup_scanner:
+       free(scanner);
+       return NULL;
+}
+
+void ctf_scanner_free(struct ctf_scanner *scanner)
+{
+       int ret;
+
+       if (!scanner)
+               return;
+       finalize_scope(&scanner->root_scope);
+       objstack_destroy(scanner->objstack);
+       ret = yylex_destroy(scanner->scanner);
+       if (ret)
+               printf_error("yylex_destroy error");
+       free(scanner);
+}
+
+%}
+
+%define api.pure
+       /* %locations */
+%error-verbose
+%parse-param {struct ctf_scanner *scanner}
+%parse-param {yyscan_t yyscanner}
+%lex-param {yyscan_t yyscanner}
+/*
+ * Expect two shift-reduce conflicts. Caused by enum name-opt : type {}
+ * vs struct { int :value; } (unnamed bit-field). The default is to
+ * shift, so whenever we encounter an enumeration, we are doing the
+ * proper thing (shift). It is illegal to declare an enumeration
+ * "bit-field", so it is OK if this situation ends up in a parsing
+ * error.
+ */
+%expect 2
+%start file
+%token INTEGER_LITERAL STRING_LITERAL CHARACTER_LITERAL LSBRAC RSBRAC LPAREN RPAREN LBRAC RBRAC RARROW STAR PLUS MINUS LT GT TYPEASSIGN COLON SEMICOLON DOTDOTDOT DOT EQUAL COMMA CONST CHAR DOUBLE ENUM ENV EVENT FLOATING_POINT FLOAT INTEGER INT LONG SHORT SIGNED STREAM STRING STRUCT TRACE CALLSITE CLOCK TYPEALIAS TYPEDEF UNSIGNED VARIANT VOID _BOOL _COMPLEX _IMAGINARY TOK_ALIGN
+%token <s> IDENTIFIER ID_TYPE
+%token ERROR
+%union
+{
+       long long ll;
+       unsigned long long ull;
+       char c;
+       char *s;
+       struct ctf_node *n;
+}
+
+%type <s> STRING_LITERAL CHARACTER_LITERAL
+
+%type <s> keywords
+
+%type <ull> INTEGER_LITERAL
+%type <n> postfix_expression unary_expression unary_expression_or_range
+
+%type <n> declaration
+%type <n> event_declaration
+%type <n> stream_declaration
+%type <n> env_declaration
+%type <n> trace_declaration
+%type <n> clock_declaration
+%type <n> callsite_declaration
+%type <n> integer_declaration_specifiers
+%type <n> declaration_specifiers
+%type <n> alias_declaration_specifiers
+
+%type <n> type_declarator_list
+%type <n> integer_type_specifier
+%type <n> type_specifier
+%type <n> struct_type_specifier
+%type <n> variant_type_specifier
+%type <n> enum_type_specifier
+%type <n> struct_or_variant_declaration_list
+%type <n> struct_or_variant_declaration
+%type <n> struct_or_variant_declarator_list
+%type <n> struct_or_variant_declarator
+%type <n> enumerator_list
+%type <n> enumerator
+%type <n> abstract_declarator_list
+%type <n> abstract_declarator
+%type <n> direct_abstract_declarator
+%type <n> alias_abstract_declarator_list
+%type <n> alias_abstract_declarator
+%type <n> direct_alias_abstract_declarator
+%type <n> declarator
+%type <n> direct_declarator
+%type <n> type_declarator
+%type <n> direct_type_declarator
+%type <n> pointer      
+%type <n> ctf_assignment_expression_list
+%type <n> ctf_assignment_expression
+
+%%
+
+file:
+               declaration
+               {
+                       if (set_parent_node($1, &ctf_scanner_get_ast(scanner)->root))
+                               reparent_error(scanner, "error reparenting to root");
+               }
+       |       file declaration
+               {
+                       if (set_parent_node($2, &ctf_scanner_get_ast(scanner)->root))
+                               reparent_error(scanner, "error reparenting to root");
+               }
+       ;
+
+keywords:
+               VOID
+               {       $$ = yylval.s;          }
+       |       CHAR
+               {       $$ = yylval.s;          }
+       |       SHORT
+               {       $$ = yylval.s;          }
+       |       INT
+               {       $$ = yylval.s;          }
+       |       LONG
+               {       $$ = yylval.s;          }
+       |       FLOAT
+               {       $$ = yylval.s;          }
+       |       DOUBLE
+               {       $$ = yylval.s;          }
+       |       SIGNED
+               {       $$ = yylval.s;          }
+       |       UNSIGNED
+               {       $$ = yylval.s;          }
+       |       _BOOL
+               {       $$ = yylval.s;          }
+       |       _COMPLEX
+               {       $$ = yylval.s;          }
+       |       _IMAGINARY
+               {       $$ = yylval.s;          }
+       |       FLOATING_POINT
+               {       $$ = yylval.s;          }
+       |       INTEGER
+               {       $$ = yylval.s;          }
+       |       STRING
+               {       $$ = yylval.s;          }
+       |       ENUM
+               {       $$ = yylval.s;          }
+       |       VARIANT
+               {       $$ = yylval.s;          }
+       |       STRUCT
+               {       $$ = yylval.s;          }
+       |       CONST
+               {       $$ = yylval.s;          }
+       |       TYPEDEF
+               {       $$ = yylval.s;          }
+       |       EVENT
+               {       $$ = yylval.s;          }
+       |       STREAM
+               {       $$ = yylval.s;          }
+       |       ENV
+               {       $$ = yylval.s;          }
+       |       TRACE
+               {       $$ = yylval.s;          }
+       |       CLOCK
+               {       $$ = yylval.s;          }
+       |       CALLSITE
+               {       $$ = yylval.s;          }
+       |       TOK_ALIGN
+               {       $$ = yylval.s;          }
+       ;
+
+
+/* 2: Phrase structure grammar */
+
+postfix_expression:
+               IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+               }
+       |       keywords
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+               }
+       |       INTEGER_LITERAL
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_UNSIGNED_CONSTANT;
+                       $$->u.unary_expression.u.unsigned_constant = $1;
+               }
+       |       STRING_LITERAL
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = $1;
+               }
+       |       CHARACTER_LITERAL
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = $1;
+               }
+       |       LPAREN unary_expression RPAREN
+               {
+                       $$ = $2;
+               }
+       |       postfix_expression LSBRAC unary_expression RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_SBRAC;
+                       $$->u.unary_expression.u.sbrac_exp = $3;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       |       postfix_expression DOT IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+                       $$->u.unary_expression.link = UNARY_DOTLINK;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       |       postfix_expression DOT ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+                       $$->u.unary_expression.link = UNARY_DOTLINK;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       |       postfix_expression DOT keywords
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+                       $$->u.unary_expression.link = UNARY_DOTLINK;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       |       postfix_expression RARROW IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+                       $$->u.unary_expression.link = UNARY_ARROWLINK;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       |       postfix_expression RARROW ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+                       $$->u.unary_expression.link = UNARY_ARROWLINK;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+unary_expression:
+               postfix_expression
+               {       $$ = $1;                                }
+       |       PLUS postfix_expression
+               {
+                       $$ = $2;
+                       if ($$->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT
+                               && $$->u.unary_expression.type != UNARY_SIGNED_CONSTANT) {
+                               reparent_error(scanner, "expecting numeric constant");
+                       }
+               }
+       |       MINUS postfix_expression
+               {
+                       $$ = $2;
+                       if ($$->u.unary_expression.type == UNARY_UNSIGNED_CONSTANT) {
+                               $$->u.unary_expression.type = UNARY_SIGNED_CONSTANT;
+                               $$->u.unary_expression.u.signed_constant =
+                                       -($$->u.unary_expression.u.unsigned_constant);
+                       } else if ($$->u.unary_expression.type == UNARY_UNSIGNED_CONSTANT) {
+                               $$->u.unary_expression.u.signed_constant =
+                                       -($$->u.unary_expression.u.signed_constant);
+                       } else {
+                               reparent_error(scanner, "expecting numeric constant");
+                       }
+               }
+       ;
+
+unary_expression_or_range:
+               unary_expression DOTDOTDOT unary_expression
+               {
+                       $$ = $1;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->tmp_head);
+                       $3->u.unary_expression.link = UNARY_DOTDOTDOT;
+               }
+       |       unary_expression
+               {       $$ = $1;                }
+       ;
+
+/* 2.2: Declarations */
+
+declaration:
+               declaration_specifiers SEMICOLON
+               {       $$ = $1;        }
+       |       event_declaration
+               {       $$ = $1;        }
+       |       stream_declaration
+               {       $$ = $1;        }
+       |       env_declaration
+               {       $$ = $1;        }
+       |       trace_declaration
+               {       $$ = $1;        }
+       |       clock_declaration
+               {       $$ = $1;        }
+       |       callsite_declaration
+               {       $$ = $1;        }
+       |       declaration_specifiers TYPEDEF declaration_specifiers type_declarator_list SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u._typedef.type_specifier_list = list;
+                       _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._typedef.type_declarators);
+               }
+       |       TYPEDEF declaration_specifiers type_declarator_list SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u._typedef.type_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators);
+               }
+       |       declaration_specifiers TYPEDEF type_declarator_list SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u._typedef.type_specifier_list = list;
+                       _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators);
+               }
+       |       TYPEALIAS declaration_specifiers abstract_declarator_list TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEALIAS);
+                       $$->u.typealias.target = make_node(scanner, NODE_TYPEALIAS_TARGET);
+                       $$->u.typealias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.typealias.target->u.typealias_target.type_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.typealias.target->u.typealias_target.type_declarators);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.typealias.alias->u.typealias_alias.type_specifier_list = list;
+                       _bt_list_splice_tail(&($5)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.typealias.alias->u.typealias_alias.type_declarators);
+               }
+       ;
+
+event_declaration:
+               event_declaration_begin event_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_EVENT);
+               }
+       |       event_declaration_begin ctf_assignment_expression_list event_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_EVENT);
+                       if (set_parent_node($2, $$))
+                               reparent_error(scanner, "event_declaration");
+               }
+       ;
+
+event_declaration_begin:
+               EVENT LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+event_declaration_end:
+               RBRAC SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+
+stream_declaration:
+               stream_declaration_begin stream_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_STREAM);
+               }
+       |       stream_declaration_begin ctf_assignment_expression_list stream_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_STREAM);
+                       if (set_parent_node($2, $$))
+                               reparent_error(scanner, "stream_declaration");
+               }
+       ;
+
+stream_declaration_begin:
+               STREAM LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+stream_declaration_end:
+               RBRAC SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+env_declaration:
+               env_declaration_begin env_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_ENV);
+               }
+       |       env_declaration_begin ctf_assignment_expression_list env_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_ENV);
+                       if (set_parent_node($2, $$))
+                               reparent_error(scanner, "env declaration");
+               }
+       ;
+
+env_declaration_begin:
+               ENV LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+env_declaration_end:
+               RBRAC SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+trace_declaration:
+               trace_declaration_begin trace_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_TRACE);
+               }
+       |       trace_declaration_begin ctf_assignment_expression_list trace_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_TRACE);
+                       if (set_parent_node($2, $$))
+                               reparent_error(scanner, "trace_declaration");
+               }
+       ;
+
+trace_declaration_begin:
+               TRACE LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+trace_declaration_end:
+               RBRAC SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+clock_declaration:
+               CLOCK clock_declaration_begin clock_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_CLOCK);
+               }
+       |       CLOCK clock_declaration_begin ctf_assignment_expression_list clock_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_CLOCK);
+                       if (set_parent_node($3, $$))
+                               reparent_error(scanner, "trace_declaration");
+               }
+       ;
+
+clock_declaration_begin:
+               LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+clock_declaration_end:
+               RBRAC SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+callsite_declaration:
+               CALLSITE callsite_declaration_begin callsite_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_CALLSITE);
+               }
+       |       CALLSITE callsite_declaration_begin ctf_assignment_expression_list callsite_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_CALLSITE);
+                       if (set_parent_node($3, $$))
+                               reparent_error(scanner, "trace_declaration");
+               }
+       ;
+
+callsite_declaration_begin:
+               LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+callsite_declaration_end:
+               RBRAC SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+integer_declaration_specifiers:
+               CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.type_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head);
+               }
+       |       integer_type_specifier
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = $1;
+                       bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head);
+               }
+       |       integer_declaration_specifiers CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = $1;
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.type_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head);
+               }
+       |       integer_declaration_specifiers integer_type_specifier
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($2)->siblings, &($$)->u.type_specifier_list.head);
+               }
+       ;
+
+declaration_specifiers:
+               CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.type_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head);
+               }
+       |       type_specifier
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = $1;
+                       bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head);
+               }
+       |       declaration_specifiers CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = $1;
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.type_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head);
+               }
+       |       declaration_specifiers type_specifier
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($2)->siblings, &($$)->u.type_specifier_list.head);
+               }
+       ;
+
+type_declarator_list:
+               type_declarator
+               {       $$ = $1;        }
+       |       type_declarator_list COMMA type_declarator
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+integer_type_specifier:
+               CHAR
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_CHAR;
+               }
+       |       SHORT
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_SHORT;
+               }
+       |       INT
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_INT;
+               }
+       |       LONG
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_LONG;
+               }
+       |       SIGNED
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_SIGNED;
+               }
+       |       UNSIGNED
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_UNSIGNED;
+               }
+       |       _BOOL
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_BOOL;
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_ID_TYPE;
+                       $$->u.type_specifier.id_type = yylval.s;
+               }
+       |       INTEGER LBRAC RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_INTEGER;
+                       $$->u.type_specifier.node = make_node(scanner, NODE_INTEGER);
+               }
+       |       INTEGER LBRAC ctf_assignment_expression_list RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_INTEGER;
+                       $$->u.type_specifier.node = make_node(scanner, NODE_INTEGER);
+                       if (set_parent_node($3, $$->u.type_specifier.node))
+                               reparent_error(scanner, "integer reparent error");
+               }
+       ;
+
+type_specifier:
+               VOID
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_VOID;
+               }
+       |       CHAR
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_CHAR;
+               }
+       |       SHORT
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_SHORT;
+               }
+       |       INT
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_INT;
+               }
+       |       LONG
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_LONG;
+               }
+       |       FLOAT
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_FLOAT;
+               }
+       |       DOUBLE
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_DOUBLE;
+               }
+       |       SIGNED
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_SIGNED;
+               }
+       |       UNSIGNED
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_UNSIGNED;
+               }
+       |       _BOOL
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_BOOL;
+               }
+       |       _COMPLEX
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_COMPLEX;
+               }
+       |       _IMAGINARY
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_IMAGINARY;
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_ID_TYPE;
+                       $$->u.type_specifier.id_type = yylval.s;
+               }
+       |       FLOATING_POINT LBRAC RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_FLOATING_POINT;
+                       $$->u.type_specifier.node = make_node(scanner, NODE_FLOATING_POINT);
+               }
+       |       FLOATING_POINT LBRAC ctf_assignment_expression_list RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_FLOATING_POINT;
+                       $$->u.type_specifier.node = make_node(scanner, NODE_FLOATING_POINT);
+                       if (set_parent_node($3, $$->u.type_specifier.node))
+                               reparent_error(scanner, "floating point reparent error");
+               }
+       |       INTEGER LBRAC RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_INTEGER;
+                       $$->u.type_specifier.node = make_node(scanner, NODE_INTEGER);
+               }
+       |       INTEGER LBRAC ctf_assignment_expression_list RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_INTEGER;
+                       $$->u.type_specifier.node = make_node(scanner, NODE_INTEGER);
+                       if (set_parent_node($3, $$->u.type_specifier.node))
+                               reparent_error(scanner, "integer reparent error");
+               }
+       |       STRING
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_STRING;
+                       $$->u.type_specifier.node = make_node(scanner, NODE_STRING);
+               }
+       |       STRING LBRAC RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_STRING;
+                       $$->u.type_specifier.node = make_node(scanner, NODE_STRING);
+               }
+       |       STRING LBRAC ctf_assignment_expression_list RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_STRING;
+                       $$->u.type_specifier.node = make_node(scanner, NODE_STRING);
+                       if (set_parent_node($3, $$->u.type_specifier.node))
+                               reparent_error(scanner, "string reparent error");
+               }
+       |       ENUM enum_type_specifier
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_ENUM;
+                       $$->u.type_specifier.node = $2;
+               }
+       |       VARIANT variant_type_specifier
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_VARIANT;
+                       $$->u.type_specifier.node = $2;
+               }
+       |       STRUCT struct_type_specifier
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.type_specifier.type = TYPESPEC_STRUCT;
+                       $$->u.type_specifier.node = $2;
+               }
+       ;
+
+struct_type_specifier:
+               struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       if ($2 && set_parent_node($2, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       |       IDENTIFIER struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       $$->u._struct.name = $1;
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       |       ID_TYPE struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       $$->u._struct.name = $1;
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       |       IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 0;
+                       $$->u._struct.name = $1;
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 0;
+                       $$->u._struct.name = $1;
+               }
+       |       struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end TOK_ALIGN LPAREN unary_expression RPAREN
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       bt_list_add_tail(&($6)->siblings, &$$->u._struct.min_align);
+                       if ($2 && set_parent_node($2, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       |       IDENTIFIER struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end TOK_ALIGN LPAREN unary_expression RPAREN
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       $$->u._struct.name = $1;
+                       bt_list_add_tail(&($7)->siblings, &$$->u._struct.min_align);
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       |       ID_TYPE struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end TOK_ALIGN LPAREN unary_expression RPAREN
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       $$->u._struct.name = $1;
+                       bt_list_add_tail(&($7)->siblings, &$$->u._struct.min_align);
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       ;
+
+struct_declaration_begin:
+               LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+struct_declaration_end:
+               RBRAC
+               {       pop_scope(scanner);     }
+       ;
+
+variant_type_specifier:
+               variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       if ($2 && set_parent_node($2, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       LT IDENTIFIER GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.choice = $2;
+                       if ($5 && set_parent_node($5, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       LT ID_TYPE GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.choice = $2;
+                       if ($5 && set_parent_node($5, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       IDENTIFIER variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       IDENTIFIER LT IDENTIFIER GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+                       if ($6 && set_parent_node($6, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       IDENTIFIER LT IDENTIFIER GT
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 0;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+               }
+       |       IDENTIFIER LT ID_TYPE GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+                       if ($6 && set_parent_node($6, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       IDENTIFIER LT ID_TYPE GT
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 0;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+               }
+       |       ID_TYPE variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       ID_TYPE LT IDENTIFIER GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+                       if ($6 && set_parent_node($6, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       ID_TYPE LT IDENTIFIER GT
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 0;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+               }
+       |       ID_TYPE LT ID_TYPE GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+                       if ($6 && set_parent_node($6, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       ID_TYPE LT ID_TYPE GT
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 0;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+               }
+       ;
+
+variant_declaration_begin:
+               LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+variant_declaration_end:
+               RBRAC
+               {       pop_scope(scanner);     }
+       ;
+
+enum_type_specifier:
+               LBRAC enumerator_list RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       COLON integer_declaration_specifiers LBRAC enumerator_list RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       ($$)->u._enum.container_type = $2;
+                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       IDENTIFIER LBRAC enumerator_list RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       IDENTIFIER COLON integer_declaration_specifiers LBRAC enumerator_list RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       ($$)->u._enum.container_type = $3;
+                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       ID_TYPE LBRAC enumerator_list RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       ID_TYPE COLON integer_declaration_specifiers LBRAC enumerator_list RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       ($$)->u._enum.container_type = $3;
+                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       LBRAC enumerator_list COMMA RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       COLON integer_declaration_specifiers LBRAC enumerator_list COMMA RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       ($$)->u._enum.container_type = $2;
+                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       IDENTIFIER LBRAC enumerator_list COMMA RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       IDENTIFIER COLON integer_declaration_specifiers LBRAC enumerator_list COMMA RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       ($$)->u._enum.container_type = $3;
+                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 0;
+                       $$->u._enum.enum_id = $1;
+               }
+       |       ID_TYPE LBRAC enumerator_list COMMA RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       ID_TYPE COLON integer_declaration_specifiers LBRAC enumerator_list COMMA RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       ($$)->u._enum.container_type = $3;
+                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 0;
+                       $$->u._enum.enum_id = $1;
+               }
+       ;
+
+struct_or_variant_declaration_list:
+               /* empty */
+               {       $$ = NULL;      }
+       |       struct_or_variant_declaration_list struct_or_variant_declaration
+               {
+                       if ($1) {
+                               $$ = $1;
+                               bt_list_add_tail(&($2)->siblings, &($$)->tmp_head);
+                       } else {
+                               $$ = $2;
+                               bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+                       }
+               }
+       ;
+
+struct_or_variant_declaration:
+               declaration_specifiers struct_or_variant_declarator_list SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       $$ = make_node(scanner, NODE_STRUCT_OR_VARIANT_DECLARATION);
+                       ($$)->u.struct_or_variant_declaration.type_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->tmp_head, &($$)->u.struct_or_variant_declaration.type_declarators);
+               }
+       |       declaration_specifiers TYPEDEF declaration_specifiers type_declarator_list SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u._typedef.type_specifier_list = list;
+                       _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._typedef.type_declarators);
+               }
+       |       TYPEDEF declaration_specifiers type_declarator_list SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u._typedef.type_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators);
+               }
+       |       declaration_specifiers TYPEDEF type_declarator_list SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       ($$)->u.struct_or_variant_declaration.type_specifier_list = list;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators);
+               }
+       |       TYPEALIAS declaration_specifiers abstract_declarator_list TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEALIAS);
+                       $$->u.typealias.target = make_node(scanner, NODE_TYPEALIAS_TARGET);
+                       $$->u.typealias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.typealias.target->u.typealias_target.type_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.typealias.target->u.typealias_target.type_declarators);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.typealias.alias->u.typealias_alias.type_specifier_list = list;
+                       _bt_list_splice_tail(&($5)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.typealias.alias->u.typealias_alias.type_declarators);
+               }
+       ;
+
+alias_declaration_specifiers:
+               CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.type_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head);
+               }
+       |       type_specifier
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = $1;
+                       bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head);
+               }
+       |       IDENTIFIER
+               {
+                       struct ctf_node *node;
+
+                       add_type(scanner, $1);
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.type_specifier.type = TYPESPEC_ID_TYPE;
+                       node->u.type_specifier.id_type = yylval.s;
+                       bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head);
+               }
+       |       alias_declaration_specifiers CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = $1;
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.type_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head);
+               }
+       |       alias_declaration_specifiers type_specifier
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($2)->siblings, &($$)->u.type_specifier_list.head);
+               }
+       |       alias_declaration_specifiers IDENTIFIER
+               {
+                       struct ctf_node *node;
+
+                       add_type(scanner, $2);
+                       $$ = $1;
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.type_specifier.type = TYPESPEC_ID_TYPE;
+                       node->u.type_specifier.id_type = yylval.s;
+                       bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head);
+               }
+       ;
+
+struct_or_variant_declarator_list:
+               struct_or_variant_declarator
+               {       $$ = $1;        }
+       |       struct_or_variant_declarator_list COMMA struct_or_variant_declarator
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+struct_or_variant_declarator:
+               declarator
+               {       $$ = $1;        }
+       |       COLON unary_expression
+               {       $$ = $2;        }
+       |       declarator COLON unary_expression
+               {
+                       $$ = $1;
+                       if (set_parent_node($3, $1))
+                               reparent_error(scanner, "struct_or_variant_declarator");
+               }
+       ;
+
+enumerator_list:
+               enumerator
+               {       $$ = $1;        }
+       |       enumerator_list COMMA enumerator
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+enumerator:
+               IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+               }
+       |       keywords
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+               }
+       |       STRING_LITERAL
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+               }
+       |       IDENTIFIER EQUAL unary_expression_or_range
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
+               }
+       |       ID_TYPE EQUAL unary_expression_or_range
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
+               }
+       |       keywords EQUAL unary_expression_or_range
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
+               }
+       |       STRING_LITERAL EQUAL unary_expression_or_range
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
+               }
+       ;
+
+abstract_declarator_list:
+               abstract_declarator
+               {       $$ = $1;        }
+       |       abstract_declarator_list COMMA abstract_declarator
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+abstract_declarator:
+               direct_abstract_declarator
+               {       $$ = $1;        }
+       |       pointer direct_abstract_declarator
+               {
+                       $$ = $2;
+                       bt_list_splice(&($1)->tmp_head, &($$)->u.type_declarator.pointers);
+               }
+       ;
+
+direct_abstract_declarator:
+               /* empty */
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                        $$->u.type_declarator.type = TYPEDEC_ID;
+                       /* id is NULL */
+               }
+       |       IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.type_declarator.type = TYPEDEC_ID;
+                       $$->u.type_declarator.u.id = $1;
+               }
+       |       LPAREN abstract_declarator RPAREN
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.type_declarator.type = TYPEDEC_NESTED;
+                       $$->u.type_declarator.u.nested.type_declarator = $2;
+               }
+       |       direct_abstract_declarator LSBRAC unary_expression RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.type_declarator.type = TYPEDEC_NESTED;
+                       $$->u.type_declarator.u.nested.type_declarator = $1;
+                       BT_INIT_LIST_HEAD(&($$)->u.type_declarator.u.nested.length);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.type_declarator.u.nested.length);
+               }
+       |       direct_abstract_declarator LSBRAC RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.type_declarator.type = TYPEDEC_NESTED;
+                       $$->u.type_declarator.u.nested.type_declarator = $1;
+                       $$->u.type_declarator.u.nested.abstract_array = 1;
+               }
+       ;
+
+alias_abstract_declarator_list:
+               alias_abstract_declarator
+               {       $$ = $1;        }
+       |       alias_abstract_declarator_list COMMA alias_abstract_declarator
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+alias_abstract_declarator:
+               direct_alias_abstract_declarator
+               {       $$ = $1;        }
+       |       pointer direct_alias_abstract_declarator
+               {
+                       $$ = $2;
+                       bt_list_splice(&($1)->tmp_head, &($$)->u.type_declarator.pointers);
+               }
+       ;
+
+direct_alias_abstract_declarator:
+               /* empty */
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                        $$->u.type_declarator.type = TYPEDEC_ID;
+                       /* id is NULL */
+               }
+       |       LPAREN alias_abstract_declarator RPAREN
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.type_declarator.type = TYPEDEC_NESTED;
+                       $$->u.type_declarator.u.nested.type_declarator = $2;
+               }
+       |       direct_alias_abstract_declarator LSBRAC unary_expression RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.type_declarator.type = TYPEDEC_NESTED;
+                       $$->u.type_declarator.u.nested.type_declarator = $1;
+                       BT_INIT_LIST_HEAD(&($$)->u.type_declarator.u.nested.length);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.type_declarator.u.nested.length);
+               }
+       |       direct_alias_abstract_declarator LSBRAC RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.type_declarator.type = TYPEDEC_NESTED;
+                       $$->u.type_declarator.u.nested.type_declarator = $1;
+                       $$->u.type_declarator.u.nested.abstract_array = 1;
+               }
+       ;
+
+declarator:
+               direct_declarator
+               {       $$ = $1;        }
+       |       pointer direct_declarator
+               {
+                       $$ = $2;
+                       bt_list_splice(&($1)->tmp_head, &($$)->u.type_declarator.pointers);
+               }
+       ;
+
+direct_declarator:
+               IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.type_declarator.type = TYPEDEC_ID;
+                       $$->u.type_declarator.u.id = $1;
+               }
+       |       LPAREN declarator RPAREN
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.type_declarator.type = TYPEDEC_NESTED;
+                       $$->u.type_declarator.u.nested.type_declarator = $2;
+               }
+       |       direct_declarator LSBRAC unary_expression RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.type_declarator.type = TYPEDEC_NESTED;
+                       $$->u.type_declarator.u.nested.type_declarator = $1;
+                       BT_INIT_LIST_HEAD(&($$)->u.type_declarator.u.nested.length);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.type_declarator.u.nested.length);
+               }
+       ;
+
+type_declarator:
+               direct_type_declarator
+               {       $$ = $1;        }
+       |       pointer direct_type_declarator
+               {
+                       $$ = $2;
+                       bt_list_splice(&($1)->tmp_head, &($$)->u.type_declarator.pointers);
+               }
+       ;
+
+direct_type_declarator:
+               IDENTIFIER
+               {
+                       add_type(scanner, $1);
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.type_declarator.type = TYPEDEC_ID;
+                       $$->u.type_declarator.u.id = $1;
+               }
+       |       LPAREN type_declarator RPAREN
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.type_declarator.type = TYPEDEC_NESTED;
+                       $$->u.type_declarator.u.nested.type_declarator = $2;
+               }
+       |       direct_type_declarator LSBRAC unary_expression RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.type_declarator.type = TYPEDEC_NESTED;
+                       $$->u.type_declarator.u.nested.type_declarator = $1;
+                       BT_INIT_LIST_HEAD(&($$)->u.type_declarator.u.nested.length);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.type_declarator.u.nested.length);
+               }
+       ;
+
+pointer:       
+               STAR
+               {
+                       $$ = make_node(scanner, NODE_POINTER);
+               }
+       |       STAR pointer
+               {
+                       $$ = make_node(scanner, NODE_POINTER);
+                       bt_list_splice(&($2)->tmp_head, &($$)->tmp_head);
+               }
+       |       STAR type_qualifier_list pointer
+               {
+                       $$ = make_node(scanner, NODE_POINTER);
+                       $$->u.pointer.const_qualifier = 1;
+                       bt_list_splice(&($3)->tmp_head, &($$)->tmp_head);
+               }
+       ;
+
+type_qualifier_list:
+               /* pointer assumes only const type qualifier */
+               CONST
+       |       type_qualifier_list CONST
+       ;
+
+/* 2.3: CTF-specific declarations */
+
+ctf_assignment_expression_list:
+               ctf_assignment_expression SEMICOLON
+               {       $$ = $1;        }
+       |       ctf_assignment_expression_list ctf_assignment_expression SEMICOLON
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($2)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+ctf_assignment_expression:
+               unary_expression EQUAL unary_expression
+               {
+                       /*
+                        * Because we have left and right, cannot use
+                        * set_parent_node.
+                        */
+                       $$ = make_node(scanner, NODE_CTF_EXPRESSION);
+                       _bt_list_splice_tail(&($1)->tmp_head, &($$)->u.ctf_expression.left);
+                       if ($1->u.unary_expression.type != UNARY_STRING)
+                               reparent_error(scanner, "ctf_assignment_expression left expects string");
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.ctf_expression.right);
+               }
+       |       unary_expression TYPEASSIGN declaration_specifiers      /* Only allow struct */
+               {
+                       /*
+                        * Because we have left and right, cannot use
+                        * set_parent_node.
+                        */
+                       $$ = make_node(scanner, NODE_CTF_EXPRESSION);
+                       _bt_list_splice_tail(&($1)->tmp_head, &($$)->u.ctf_expression.left);
+                       if ($1->u.unary_expression.type != UNARY_STRING)
+                               reparent_error(scanner, "ctf_assignment_expression left expects string");
+                       bt_list_add_tail(&($3)->siblings, &($$)->u.ctf_expression.right);
+               }
+       |       declaration_specifiers TYPEDEF declaration_specifiers type_declarator_list
+               {
+                       struct ctf_node *list;
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       ($$)->u.struct_or_variant_declaration.type_specifier_list = list;
+                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._typedef.type_declarators);
+               }
+       |       TYPEDEF declaration_specifiers type_declarator_list
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u._typedef.type_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators);
+               }
+       |       declaration_specifiers TYPEDEF type_declarator_list
+               {
+                       struct ctf_node *list;
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       ($$)->u.struct_or_variant_declaration.type_specifier_list = list;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators);
+               }
+       |       TYPEALIAS declaration_specifiers abstract_declarator_list TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEALIAS);
+                       $$->u.typealias.target = make_node(scanner, NODE_TYPEALIAS_TARGET);
+                       $$->u.typealias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.typealias.target->u.typealias_target.type_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.typealias.target->u.typealias_target.type_declarators);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.typealias.alias->u.typealias_alias.type_specifier_list = list;
+                       _bt_list_splice_tail(&($5)->u.type_specifier_list.head, &list->u.type_specifier_list.head);
+                       _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.typealias.alias->u.typealias_alias.type_declarators);
+               }
+       ;
diff --git a/ctf-reader-proto/metadata-parsing/ctf-scanner-symbols.h b/ctf-reader-proto/metadata-parsing/ctf-scanner-symbols.h
new file mode 100644 (file)
index 0000000..9b9e363
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef _CTF_SCANNER_SYMBOLS
+#define _CTF_SCANNER_SYMBOLS
+
+/*
+ * ctf-scanner-symbols.h
+ *
+ * Copyright 2011-2012 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ */
+
+#define yy_create_buffer bt_yy_create_buffer
+#define yy_delete_buffer bt_yy_delete_buffer
+#define yy_flush_buffer bt_yy_flush_buffer
+#define yy_scan_buffer bt_yy_scan_buffer
+#define yy_scan_bytes bt_yy_scan_bytes
+#define yy_scan_string bt_yy_scan_string
+#define yy_switch_to_buffer bt_yy_switch_to_buffer
+#define yyalloc bt_yyalloc
+#define yyfree bt_yyfree
+#define yyget_column bt_yyget_column
+#define yyget_debug bt_yyget_debug
+#define yyget_extra bt_yyget_extra
+#define yyget_in bt_yyget_in
+#define yyget_leng bt_yyget_leng
+#define yyget_lineno bt_yyget_lineno
+#define yyget_lval bt_yyget_lval
+#define yyget_out bt_yyget_out
+#define yyget_text bt_yyget_text
+#define yylex_init bt_yylex_init
+#define yypop_buffer_state bt_yypop_buffer_state
+#define yypush_buffer_state bt_yypush_buffer_state
+#define yyrealloc bt_yyrealloc
+#define yyset_column bt_yyset_column
+#define yyset_debug bt_yyset_debug
+#define yyset_extra bt_yyset_extra
+#define yyset_in bt_yyset_in
+#define yyset_lineno bt_yyset_lineno
+#define yyset_lval bt_yyset_lval
+#define yyset_out bt_yyset_out
+
+#endif /* _CTF_SCANNER_SYMBOLS */
diff --git a/ctf-reader-proto/metadata-parsing/ctf-scanner.h b/ctf-reader-proto/metadata-parsing/ctf-scanner.h
new file mode 100644 (file)
index 0000000..51484a1
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef _CTF_SCANNER_H
+#define _CTF_SCANNER_H
+
+/*
+ * ctf-scanner.h
+ *
+ * Copyright 2011-2012 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ */
+
+#include <stdio.h>
+#include "ctf-ast.h"
+
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+struct ctf_scanner_scope;
+struct ctf_scanner_scope {
+       struct ctf_scanner_scope *parent;
+       GHashTable *types;
+};
+
+struct ctf_scanner {
+       yyscan_t scanner;
+       struct ctf_ast *ast;
+       struct ctf_scanner_scope root_scope;
+       struct ctf_scanner_scope *cs;
+       struct objstack *objstack;
+};
+
+struct ctf_scanner *ctf_scanner_alloc(void);
+void ctf_scanner_free(struct ctf_scanner *scanner);
+int ctf_scanner_append_ast(struct ctf_scanner *scanner, FILE *input);
+
+static inline
+struct ctf_ast *ctf_scanner_get_ast(struct ctf_scanner *scanner)
+{
+       return scanner->ast;
+}
+
+BT_HIDDEN
+int is_type(struct ctf_scanner *scanner, const char *id);
+
+#endif /* _CTF_SCANNER_H */
diff --git a/ctf-reader-proto/metadata-parsing/ctf-visitor-generate-ir.c b/ctf-reader-proto/metadata-parsing/ctf-visitor-generate-ir.c
new file mode 100644 (file)
index 0000000..714a452
--- /dev/null
@@ -0,0 +1,4662 @@
+/*
+ * ctf-visitor-generate-ir.c
+ *
+ * Common Trace Format metadata visitor (generates CTF IR objects).
+ *
+ * Based on older ctf-visitor-generate-io-struct.c.
+ *
+ * Copyright 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright 2015-2016 - Philippe Proulx <philippe.proulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <assert.h>
+#include <glib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <babeltrace/ctf/metadata.h>
+#include <babeltrace/compat/uuid.h>
+#include <babeltrace/endian.h>
+#include <babeltrace/ref.h>
+#include <babeltrace/ctf/events-internal.h>
+#include <babeltrace/ctf-ir/trace.h>
+#include <babeltrace/ctf-ir/stream-class.h>
+#include <babeltrace/ctf-ir/event.h>
+#include <babeltrace/ctf-ir/event-class.h>
+#include <babeltrace/ctf-ir/field-types.h>
+#include <babeltrace/ctf-ir/field-types-internal.h>
+#include <babeltrace/ctf-ir/clock.h>
+
+#include "ctf-scanner.h"
+#include "ctf-parser.h"
+#include "ctf-ast.h"
+
+/* Bit value (left shift) */
+#define _BV(_val)              (1 << (_val))
+
+/* Bit is set in a set of bits */
+#define _IS_SET(_set, _mask)   (*(_set) & (_mask))
+
+/* Set bit in a set of bits */
+#define _SET(_set, _mask)      (*(_set) |= (_mask))
+
+/* Bits for verifying existing attributes in various declarations */
+enum {
+       _CLOCK_NAME_SET =               _BV(0),
+       _CLOCK_UUID_SET =               _BV(1),
+       _CLOCK_FREQ_SET =               _BV(2),
+       _CLOCK_PRECISION_SET =          _BV(3),
+       _CLOCK_OFFSET_S_SET =           _BV(4),
+       _CLOCK_OFFSET_SET =             _BV(5),
+       _CLOCK_ABSOLUTE_SET =           _BV(6),
+       _CLOCK_DESCRIPTION_SET =        _BV(7),
+};
+
+enum {
+       _INTEGER_ALIGN_SET =            _BV(0),
+       _INTEGER_SIZE_SET =             _BV(1),
+       _INTEGER_BASE_SET =             _BV(2),
+       _INTEGER_ENCODING_SET =         _BV(3),
+       _INTEGER_BYTE_ORDER_SET =       _BV(4),
+       _INTEGER_SIGNED_SET =           _BV(5),
+       _INTEGER_MAP_SET =              _BV(6),
+};
+
+enum {
+       _FLOAT_ALIGN_SET =              _BV(0),
+       _FLOAT_MANT_DIG_SET =           _BV(1),
+       _FLOAT_EXP_DIG_SET =            _BV(2),
+       _FLOAT_BYTE_ORDER_SET =         _BV(3),
+};
+
+enum {
+       _STRING_ENCODING_SET =          _BV(0),
+};
+
+enum {
+       _TRACE_MINOR_SET =              _BV(0),
+       _TRACE_MAJOR_SET =              _BV(1),
+       _TRACE_BYTE_ORDER_SET =         _BV(2),
+       _TRACE_UUID_SET =               _BV(3),
+       _TRACE_PACKET_HEADER_SET =      _BV(4),
+};
+
+enum {
+       _STREAM_ID_SET =                _BV(0),
+       _STREAM_PACKET_CONTEXT_SET =    _BV(1),
+       _STREAM_EVENT_HEADER_SET =      _BV(2),
+       _STREAM_EVENT_CONTEXT_SET =     _BV(3),
+};
+
+enum {
+       _EVENT_NAME_SET =               _BV(0),
+       _EVENT_ID_SET =                 _BV(1),
+       _EVENT_MODEL_EMF_URI_SET =      _BV(2),
+       _EVENT_STREAM_ID_SET =          _BV(3),
+       _EVENT_LOGLEVEL_SET =           _BV(4),
+       _EVENT_CONTEXT_SET =            _BV(5),
+       _EVENT_FIELDS_SET =             _BV(6),
+};
+
+/* Prefixes of type aliases */
+#define _PREFIX_ALIAS                  'a'
+#define _PREFIX_ENUM                   'e'
+#define _PREFIX_STRUCT                 's'
+#define _PREFIX_VARIANT                        'v'
+
+/* First entry in a BT list */
+#define _BT_LIST_FIRST_ENTRY(_ptr, _type, _member)     \
+       bt_list_entry((_ptr)->next, _type, _member)
+
+#define _BT_CTF_FIELD_TYPE_INIT(_name) struct bt_ctf_field_type *_name = NULL;
+
+/* Error printing wrappers */
+#define _PERROR(_fmt, ...)                                     \
+       do {                                                    \
+               fprintf(ctx->efd, "[error] %s: " _fmt "\n",     \
+                       __func__, __VA_ARGS__);                 \
+       } while (0)
+
+#define _PWARNING(_fmt, ...)                                   \
+       do {                                                    \
+               fprintf(ctx->efd, "[warning] %s: " _fmt "\n",   \
+                       __func__, __VA_ARGS__);                 \
+       } while (0)
+
+#define _FPERROR(_stream, _fmt, ...)                           \
+       do {                                                    \
+               fprintf(_stream, "[error] %s: " _fmt "\n",      \
+                       __func__, __VA_ARGS__);                 \
+       } while (0)
+
+#define _FPWARNING(_stream, _fmt, ...)                         \
+       do {                                                    \
+               fprintf(_stream, "[warning] %s: " _fmt "\n",    \
+                       __func__, __VA_ARGS__);                 \
+       } while (0)
+
+#define _PERROR_DUP_ATTR(_attr, _entity)                       \
+       do {                                                    \
+               fprintf(ctx->efd,                               \
+                       "[error] %s: duplicate attribute \""    \
+                       _attr "\" in " _entity "\n", __func__); \
+       } while (0)
+
+/*
+ * Declaration scope of a visitor context. This represents a TSDL
+ * lexical scope, so that aliases and named structures, variants,
+ * and enumerations may be registered and looked up hierarchically.
+ */
+struct ctx_decl_scope {
+       /*
+        * Alias name to field type.
+        *
+        * GQuark -> struct bt_ctf_field_type *
+        */
+       GHashTable *decl_map;
+
+       /* Parent scope; NULL if this is the root declaration scope */
+       struct ctx_decl_scope *parent_scope;
+};
+
+/*
+ * Visitor context.
+ */
+struct ctx {
+       /* Trace being filled (weak ref.) */
+       struct bt_ctf_trace *trace;
+
+       /* Error stream to use during visit */
+       FILE *efd;
+
+       /* Current declaration scope (top of the stack) */
+       struct ctx_decl_scope *current_scope;
+
+       /* 1 if trace declaration is visited */
+       int is_trace_visited;
+
+       /* Trace attributes */
+       uint64_t trace_major;
+       uint64_t trace_minor;
+       unsigned char trace_uuid[BABELTRACE_UUID_LEN];
+
+       /*
+        * Stream IDs to stream classes.
+        *
+        * int64_t -> struct bt_ctf_stream_class *
+        */
+       GHashTable *stream_classes;
+};
+
+/**
+ * Creates a new declaration scope.
+ *
+ * @param par_scope    Parent scope (NULL if creating a root scope)
+ * @returns            New declaration scope, or NULL on error
+ */
+static
+struct ctx_decl_scope *ctx_decl_scope_create(struct ctx_decl_scope *par_scope)
+{
+       struct ctx_decl_scope *scope;
+
+       scope = g_new(struct ctx_decl_scope, 1);
+       if (!scope) {
+               goto end;
+       }
+
+       scope->decl_map = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+               NULL, (GDestroyNotify) bt_ctf_field_type_put);
+       scope->parent_scope = par_scope;
+
+end:
+       return scope;
+}
+
+/**
+ * Destroys a declaration scope.
+ *
+ * This function does not destroy the parent scope.
+ *
+ * @param scope        Scope to destroy
+ */
+static
+void ctx_decl_scope_destroy(struct ctx_decl_scope *scope)
+{
+       if (!scope) {
+               goto end;
+       }
+
+       g_hash_table_destroy(scope->decl_map);
+       g_free(scope);
+
+end:
+       return;
+}
+
+/**
+ * Returns the GQuark of a prefixed alias.
+ *
+ * @param prefix       Prefix character
+ * @param name         Name
+ * @returns            Associated GQuark, or 0 on error
+ */
+static
+GQuark get_prefixed_named_quark(char prefix, const char *name)
+{
+       GQuark qname = 0;
+
+       assert(name);
+
+       /* Prefix character + original string + '\0' */
+       char *prname = g_new(char, strlen(name) + 2);
+       if (!prname) {
+               goto end;
+       }
+
+       sprintf(prname, "%c%s", prefix, name);
+       qname = g_quark_from_string(prname);
+       g_free(prname);
+
+end:
+       return qname;
+}
+
+/**
+ * Looks up a prefixed type alias within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param prefix       Prefix character
+ * @param name         Alias name
+ * @param level                Number of levels to dig (-1 means infinite)
+ * @returns            Declaration, or NULL if not found
+ */
+static
+struct bt_ctf_field_type *ctx_decl_scope_lookup_prefix_alias(
+       struct ctx_decl_scope *scope, char prefix,
+       const char *name, int levels)
+{
+       GQuark qname = 0;
+       int cur_levels = 0;
+       _BT_CTF_FIELD_TYPE_INIT(decl);
+       struct ctx_decl_scope *cur_scope = scope;
+
+       assert(scope);
+       assert(name);
+       qname = get_prefixed_named_quark(prefix, name);
+       if (!qname) {
+               goto error;
+       }
+
+       if (levels < 0) {
+               levels = INT_MAX;
+       }
+
+       while (cur_scope && cur_levels < levels) {
+               decl = g_hash_table_lookup(cur_scope->decl_map,
+                       (gconstpointer) (unsigned long) qname);
+               if (decl) {
+                       /* Caller's reference */
+                       bt_get(decl);
+                       break;
+               }
+
+               cur_scope = cur_scope->parent_scope;
+               cur_levels++;
+       }
+
+       return decl;
+
+error:
+       return NULL;
+}
+
+/**
+ * Looks up a type alias within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param name         Alias name
+ * @param level                Number of levels to dig (-1 means infinite)
+ * @returns            Declaration, or NULL if not found
+ */
+static
+struct bt_ctf_field_type *ctx_decl_scope_lookup_alias(
+       struct ctx_decl_scope *scope, const char *name, int levels)
+{
+       return ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_ALIAS,
+               name, levels);
+}
+
+/**
+ * Looks up an enumeration within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param name         Enumeration name
+ * @param level                Number of levels to dig (-1 means infinite)
+ * @returns            Declaration, or NULL if not found
+ */
+static
+struct bt_ctf_field_type *ctx_decl_scope_lookup_enum(
+       struct ctx_decl_scope *scope, const char *name, int levels)
+{
+       return ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_ENUM,
+               name, levels);
+}
+
+/**
+ * Looks up a structure within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param name         Structure name
+ * @param level                Number of levels to dig (-1 means infinite)
+ * @returns            Declaration, or NULL if not found
+ */
+static
+struct bt_ctf_field_type *ctx_decl_scope_lookup_struct(
+       struct ctx_decl_scope *scope, const char *name, int levels)
+{
+       return ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_STRUCT,
+               name, levels);
+}
+
+/**
+ * Looks up a variant within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param name         Variant name
+ * @param level                Number of levels to dig (-1 means infinite)
+ * @returns            Declaration, or NULL if not found
+ */
+static
+struct bt_ctf_field_type *ctx_decl_scope_lookup_variant(
+       struct ctx_decl_scope *scope, const char *name, int levels)
+{
+       return ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_VARIANT,
+               name, levels);
+}
+
+/**
+ * Registers a prefixed type alias within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param prefix       Prefix character
+ * @param name         Alias name (non-NULL)
+ * @param decl         Declaration to register
+ * @returns            0 if registration went okay, negative value otherwise
+ */
+static
+int ctx_decl_scope_register_prefix_alias(struct ctx_decl_scope *scope,
+       char prefix, const char *name, struct bt_ctf_field_type *decl)
+{
+       int ret = 0;
+       GQuark qname = 0;
+       _BT_CTF_FIELD_TYPE_INIT(edecl);
+
+       assert(scope);
+       assert(name);
+       assert(decl);
+       qname = get_prefixed_named_quark(prefix, name);
+       if (!qname) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       /* Make sure alias does not exist in local scope */
+       edecl = ctx_decl_scope_lookup_prefix_alias(scope, prefix, name, 1);
+       if (edecl) {
+               BT_PUT(edecl);
+               ret = -EEXIST;
+               goto error;
+       }
+
+       g_hash_table_insert(scope->decl_map,
+               (gpointer) (unsigned long) qname, decl);
+
+       /* Hash table's reference */
+       bt_get(decl);
+
+       return 0;
+
+error:
+       return ret;
+}
+
+/**
+ * Registers a type alias within a declaration scope.
+ *
+ * @param scope        Declaration scope
+ * @param name Alias name (non-NULL)
+ * @param decl Declaration to register
+ * @returns    0 if registration went okay, negative value otherwise
+ */
+static
+int ctx_decl_scope_register_alias(struct ctx_decl_scope *scope,
+       const char *name, struct bt_ctf_field_type *decl)
+{
+       return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_ALIAS,
+               name, decl);
+}
+
+/**
+ * Registers an enumeration declaration within a declaration scope.
+ *
+ * @param scope        Declaration scope
+ * @param name Enumeration name (non-NULL)
+ * @param decl Enumeration declaration to register
+ * @returns    0 if registration went okay, negative value otherwise
+ */
+static
+int ctx_decl_scope_register_enum(struct ctx_decl_scope *scope,
+       const char *name, struct bt_ctf_field_type *decl)
+{
+       return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_ENUM,
+               name, decl);
+}
+
+/**
+ * Registers a structure declaration within a declaration scope.
+ *
+ * @param scope        Declaration scope
+ * @param name Structure name (non-NULL)
+ * @param decl Structure declaration to register
+ * @returns    0 if registration went okay, negative value otherwise
+ */
+static
+int ctx_decl_scope_register_struct(struct ctx_decl_scope *scope,
+       const char *name, struct bt_ctf_field_type *decl)
+{
+       return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_STRUCT,
+               name, decl);
+}
+
+/**
+ * Registers a variant declaration within a declaration scope.
+ *
+ * @param scope        Declaration scope
+ * @param name Variant name (non-NULL)
+ * @param decl Variant declaration to register
+ * @returns    0 if registration went okay, negative value otherwise
+ */
+static
+int ctx_decl_scope_register_variant(struct ctx_decl_scope *scope,
+       const char *name, struct bt_ctf_field_type *decl)
+{
+       return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_VARIANT,
+               name, decl);
+}
+
+/**
+ * Creates a new visitor context.
+ *
+ * @param trace        Associated trace
+ * @param efd  Error stream
+ * @returns    New visitor context, or NULL on error
+ */
+static
+struct ctx *ctx_create(struct bt_ctf_trace *trace, FILE *efd)
+{
+       struct ctx *ctx = NULL;
+       struct ctx_decl_scope *scope = NULL;
+
+       ctx = g_new(struct ctx, 1);
+       if (!ctx) {
+               goto error;
+       }
+
+       /* Root declaration scope */
+       scope = ctx_decl_scope_create(NULL);
+       if (!scope) {
+               goto error;
+       }
+
+       ctx->stream_classes = g_hash_table_new_full(g_direct_hash,
+               g_direct_equal, NULL, (GDestroyNotify) bt_put);
+       if (!ctx->stream_classes) {
+               goto error;
+       }
+
+       ctx->trace = trace;
+       ctx->efd = efd;
+       ctx->current_scope = scope;
+       ctx->is_trace_visited = FALSE;
+
+       return ctx;
+
+error:
+       g_free(ctx);
+       ctx_decl_scope_destroy(scope);
+
+       return NULL;
+}
+
+/**
+ * Destroys a visitor context.
+ *
+ * @param ctx  Visitor context to destroy
+ */
+static
+void ctx_destroy(struct ctx *ctx)
+{
+       struct ctx_decl_scope *scope;
+       /*
+        * Destroy all scopes, from current one to the root scope.
+        */
+
+       if (!ctx) {
+               goto end;
+       }
+
+       scope = ctx->current_scope;
+
+       while (scope) {
+               struct ctx_decl_scope *parent_scope = scope->parent_scope;
+
+               ctx_decl_scope_destroy(scope);
+               scope = parent_scope;
+       }
+
+       g_hash_table_destroy(ctx->stream_classes);
+       g_free(ctx);
+
+end:
+       return;
+}
+
+/**
+ * Pushes a new declaration scope on top of a visitor context's
+ * declaration scope stack.
+ *
+ * @param ctx  Visitor context
+ * @returns    0 on success, or a negative value on error
+ */
+static
+int ctx_push_scope(struct ctx *ctx)
+{
+       int ret = 0;
+       struct ctx_decl_scope *new_scope;
+
+       assert(ctx);
+       new_scope = ctx_decl_scope_create(ctx->current_scope);
+       if (!new_scope) {
+               ret = -ENOMEM;
+               goto end;
+       }
+
+       ctx->current_scope = new_scope;
+
+end:
+       return ret;
+}
+
+static
+void ctx_pop_scope(struct ctx *ctx)
+{
+       struct ctx_decl_scope *parent_scope = NULL;
+
+       assert(ctx);
+
+       if (!ctx->current_scope) {
+               goto end;
+       }
+
+       parent_scope = ctx->current_scope->parent_scope;
+       ctx_decl_scope_destroy(ctx->current_scope);
+       ctx->current_scope = parent_scope;
+
+end:
+       return;
+}
+
+static
+int visit_type_specifier_list(struct ctx *ctx, struct ctf_node *ts_list,
+       struct bt_ctf_field_type **decl);
+
+static
+int is_unary_string(struct bt_list_head *head)
+{
+       int ret = TRUE;
+       struct ctf_node *node;
+
+       bt_list_for_each_entry(node, head, siblings) {
+               if (node->type != NODE_UNARY_EXPRESSION) {
+                       ret = FALSE;
+               }
+
+               if (node->u.unary_expression.type != UNARY_STRING) {
+                       ret = FALSE;
+               }
+       }
+
+       return ret;
+}
+
+static
+char *concatenate_unary_strings(struct bt_list_head *head)
+{
+       int i = 0;
+       GString *str;
+       struct ctf_node *node;
+
+       str = g_string_new(NULL);
+
+       bt_list_for_each_entry(node, head, siblings) {
+               char *src_string;
+
+               if (
+                       node->type != NODE_UNARY_EXPRESSION ||
+                       node->u.unary_expression.type != UNARY_STRING ||
+                       !(
+                               (
+                                       node->u.unary_expression.link !=
+                                       UNARY_LINK_UNKNOWN
+                               ) ^ (i == 0)
+                       )
+               ) {
+                       goto error;
+               }
+
+               switch (node->u.unary_expression.link) {
+               case UNARY_DOTLINK:
+                       g_string_append(str, ".");
+                       break;
+               case UNARY_ARROWLINK:
+                       g_string_append(str, "->");
+                       break;
+               case UNARY_DOTDOTDOT:
+                       g_string_append(str, "...");
+                       break;
+               default:
+                       break;
+               }
+
+               src_string = node->u.unary_expression.u.string;
+               g_string_append(str, src_string);
+               i++;
+       }
+
+       /* Destroys the container, returns the underlying string */
+       return g_string_free(str, FALSE);
+
+error:
+       /* This always returns NULL */
+       return g_string_free(str, TRUE);
+}
+
+static
+const char *get_map_clock_name_value(struct bt_list_head *head)
+{
+       int i = 0;
+       struct ctf_node *node;
+       const char *name = NULL;
+
+       bt_list_for_each_entry(node, head, siblings) {
+               char *src_string;
+               int uexpr_type = node->u.unary_expression.type;
+               int uexpr_link = node->u.unary_expression.link;
+               int cond = node->type != NODE_UNARY_EXPRESSION ||
+                       uexpr_type != UNARY_STRING ||
+                       !((uexpr_link != UNARY_LINK_UNKNOWN) ^ (i == 0));
+               if (cond) {
+                       goto error;
+               }
+
+               /* Needs to be chained with . */
+               switch (node->u.unary_expression.link) {
+               case UNARY_DOTLINK:
+                       break;
+               case UNARY_ARROWLINK:
+               case UNARY_DOTDOTDOT:
+                       goto error;
+               default:
+                       break;
+               }
+
+               src_string = node->u.unary_expression.u.string;
+
+               switch (i) {
+               case 0:
+                       if (strcmp("clock", src_string)) {
+                               goto error;
+                       }
+                       break;
+               case 1:
+                       name = src_string;
+                       break;
+               case 2:
+                       if (strcmp("value", src_string)) {
+                               goto error;
+                       }
+                       break;
+               default:
+                       /* Extra identifier, unknown */
+                       goto error;
+               }
+
+               i++;
+       }
+
+       return name;
+
+error:
+       return NULL;
+}
+
+static
+int is_unary_unsigned(struct bt_list_head *head)
+{
+       int ret = TRUE;
+       struct ctf_node *node;
+
+       bt_list_for_each_entry(node, head, siblings) {
+               if (node->type != NODE_UNARY_EXPRESSION) {
+                       ret = FALSE;
+               }
+
+               if (node->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) {
+                       ret = FALSE;
+               }
+       }
+
+       return ret;
+}
+
+static
+int get_unary_unsigned(struct bt_list_head *head, uint64_t *value)
+{
+       int i = 0;
+       int ret = 0;
+       struct ctf_node *node;
+
+       bt_list_for_each_entry(node, head, siblings) {
+               int uexpr_type = node->u.unary_expression.type;
+               int uexpr_link = node->u.unary_expression.link;
+               int cond = node->type != NODE_UNARY_EXPRESSION ||
+                       uexpr_type != UNARY_UNSIGNED_CONSTANT ||
+                       uexpr_link != UNARY_LINK_UNKNOWN || i != 0;
+               if (cond) {
+                       ret = -EINVAL;
+                       goto end;
+               }
+
+               *value = node->u.unary_expression.u.unsigned_constant;
+               i++;
+       }
+
+end:
+       return ret;
+}
+
+static
+int is_unary_signed(struct bt_list_head *head)
+{
+       int ret = TRUE;
+       struct ctf_node *node;
+
+       bt_list_for_each_entry(node, head, siblings) {
+               if (node->type != NODE_UNARY_EXPRESSION) {
+                       ret = FALSE;
+               }
+
+               if (node->u.unary_expression.type != UNARY_SIGNED_CONSTANT) {
+                       ret = FALSE;
+               }
+       }
+
+       return ret;
+}
+
+static
+int get_unary_signed(struct bt_list_head *head, int64_t *value)
+{
+       int i = 0;
+       int ret = 0;
+       struct ctf_node *node;
+
+       bt_list_for_each_entry(node, head, siblings) {
+               int uexpr_type = node->u.unary_expression.type;
+               int uexpr_link = node->u.unary_expression.link;
+               int cond = node->type != NODE_UNARY_EXPRESSION ||
+                       (uexpr_type != UNARY_UNSIGNED_CONSTANT) ||
+                       (uexpr_type != UNARY_UNSIGNED_CONSTANT &&
+                               uexpr_type != UNARY_SIGNED_CONSTANT) ||
+                       uexpr_link != UNARY_LINK_UNKNOWN || i != 0;
+               if (cond) {
+                       ret = -EINVAL;
+                       goto end;
+               }
+
+               switch (node->u.unary_expression.type) {
+               case UNARY_UNSIGNED_CONSTANT:
+                       *value = (int64_t)
+                               node->u.unary_expression.u.unsigned_constant;
+                       break;
+               case UNARY_SIGNED_CONSTANT:
+                       *value = node->u.unary_expression.u.signed_constant;
+                       break;
+               default:
+                       ret = -EINVAL;
+                       goto end;
+               }
+
+               i++;
+       }
+
+end:
+       return ret;
+}
+
+static
+int get_unary_uuid(struct bt_list_head *head, unsigned char *uuid)
+{
+       int i = 0;
+       int ret = 0;
+       struct ctf_node *node;
+
+       bt_list_for_each_entry(node, head, siblings) {
+               int uexpr_type = node->u.unary_expression.type;
+               int uexpr_link = node->u.unary_expression.link;
+               const char *src_string;
+
+               if (node->type != NODE_UNARY_EXPRESSION ||
+                               uexpr_type != UNARY_STRING ||
+                               uexpr_link != UNARY_LINK_UNKNOWN ||
+                               i != 0) {
+                       ret = -EINVAL;
+                       goto end;
+               }
+
+               src_string = node->u.unary_expression.u.string;
+               ret = bt_uuid_parse(src_string, uuid);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+static
+int get_boolean(FILE *efd, struct ctf_node *unary_expr)
+{
+       int ret = 0;
+
+       if (unary_expr->type != NODE_UNARY_EXPRESSION) {
+               _FPERROR(efd, "%s", "expecting unary expression");
+               ret = -EINVAL;
+               goto end;
+       }
+
+       switch (unary_expr->u.unary_expression.type) {
+       case UNARY_UNSIGNED_CONSTANT:
+               ret = (unary_expr->u.unary_expression.u.unsigned_constant != 0);
+               break;
+       case UNARY_SIGNED_CONSTANT:
+               ret = (unary_expr->u.unary_expression.u.signed_constant != 0);
+               break;
+       case UNARY_STRING:
+       {
+               const char *str = unary_expr->u.unary_expression.u.string;
+
+               if (!strcmp(str, "true") || !strcmp(str, "TRUE")) {
+                       ret = TRUE;
+               } else if (!strcmp(str, "false") || !strcmp(str, "FALSE")) {
+                       ret = FALSE;
+               } else {
+                       _FPERROR(efd, "unexpected string \"%s\"", str);
+                       ret = -EINVAL;
+                       goto end;
+               }
+               break;
+       }
+       default:
+               _FPERROR(efd, "%s", "unexpected unary expression type");
+               ret = -EINVAL;
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static
+enum bt_ctf_byte_order byte_order_from_unary_expr(FILE *efd,
+       struct ctf_node *unary_expr)
+{
+       const char *str;
+       enum bt_ctf_byte_order bo = BT_CTF_BYTE_ORDER_UNKNOWN;
+
+       if (unary_expr->u.unary_expression.type != UNARY_STRING) {
+               _FPERROR(efd, "%s",
+                       "\"byte_order\" attribute: expecting string");
+               goto end;
+       }
+
+       str = unary_expr->u.unary_expression.u.string;
+
+       if (!strcmp(str, "be") || !strcmp(str, "network")) {
+               bo = BT_CTF_BYTE_ORDER_BIG_ENDIAN;
+       } else if (!strcmp(str, "le")) {
+               bo = BT_CTF_BYTE_ORDER_LITTLE_ENDIAN;
+       } else if (!strcmp(str, "native")) {
+               bo = BT_CTF_BYTE_ORDER_NATIVE;
+       } else {
+               _FPERROR(efd, "unexpected \"byte_order\" attribute value \"%s\"; should be \"be\", \"le\", \"network\", or \"native\"",
+                       str);
+               goto end;
+       }
+
+end:
+       return bo;
+}
+
+static
+enum bt_ctf_byte_order get_real_byte_order(struct ctx *ctx,
+       struct ctf_node *uexpr)
+{
+       enum bt_ctf_byte_order bo = byte_order_from_unary_expr(ctx->efd, uexpr);
+
+       if (bo == BT_CTF_BYTE_ORDER_NATIVE) {
+               bo = bt_ctf_trace_get_byte_order(ctx->trace);
+       }
+
+       return bo;
+}
+
+static
+int is_align_valid(uint64_t align)
+{
+       return (align != 0) && !(align & (align - 1));
+}
+
+static
+int get_type_specifier_name(struct ctx *ctx, struct ctf_node *type_specifier,
+       GString *str)
+{
+       int ret = 0;
+
+       if (type_specifier->type != NODE_TYPE_SPECIFIER) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       switch (type_specifier->u.type_specifier.type) {
+       case TYPESPEC_VOID:
+               g_string_append(str, "void");
+               break;
+       case TYPESPEC_CHAR:
+               g_string_append(str, "char");
+               break;
+       case TYPESPEC_SHORT:
+               g_string_append(str, "short");
+               break;
+       case TYPESPEC_INT:
+               g_string_append(str, "int");
+               break;
+       case TYPESPEC_LONG:
+               g_string_append(str, "long");
+               break;
+       case TYPESPEC_FLOAT:
+               g_string_append(str, "float");
+               break;
+       case TYPESPEC_DOUBLE:
+               g_string_append(str, "double");
+               break;
+       case TYPESPEC_SIGNED:
+               g_string_append(str, "signed");
+               break;
+       case TYPESPEC_UNSIGNED:
+               g_string_append(str, "unsigned");
+               break;
+       case TYPESPEC_BOOL:
+               g_string_append(str, "bool");
+               break;
+       case TYPESPEC_COMPLEX:
+               g_string_append(str, "_Complex");
+               break;
+       case TYPESPEC_IMAGINARY:
+               g_string_append(str, "_Imaginary");
+               break;
+       case TYPESPEC_CONST:
+               g_string_append(str, "const");
+               break;
+       case TYPESPEC_ID_TYPE:
+               if (type_specifier->u.type_specifier.id_type) {
+                       g_string_append(str,
+                               type_specifier->u.type_specifier.id_type);
+               }
+               break;
+       case TYPESPEC_STRUCT:
+       {
+               struct ctf_node *node = type_specifier->u.type_specifier.node;
+
+               if (!node->u._struct.name) {
+                       _PERROR("%s", "unexpected empty structure name");
+                       ret = -EINVAL;
+                       goto end;
+               }
+
+               g_string_append(str, "struct ");
+               g_string_append(str, node->u._struct.name);
+               break;
+       }
+       case TYPESPEC_VARIANT:
+       {
+               struct ctf_node *node = type_specifier->u.type_specifier.node;
+
+               if (!node->u.variant.name) {
+                       _PERROR("%s", "unexpected empty variant name");
+                       ret = -EINVAL;
+                       goto end;
+               }
+
+               g_string_append(str, "variant ");
+               g_string_append(str, node->u.variant.name);
+               break;
+       }
+       case TYPESPEC_ENUM:
+       {
+               struct ctf_node *node = type_specifier->u.type_specifier.node;
+
+               if (!node->u._enum.enum_id) {
+                       _PERROR("%s", "unexpected empty enum name");
+                       ret = -EINVAL;
+                       goto end;
+               }
+
+               g_string_append(str, "enum ");
+               g_string_append(str, node->u._enum.enum_id);
+               break;
+       }
+       case TYPESPEC_FLOATING_POINT:
+       case TYPESPEC_INTEGER:
+       case TYPESPEC_STRING:
+       default:
+               _PERROR("%s", "unknown specifier");
+               ret = -EINVAL;
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static
+int get_type_specifier_list_name(struct ctx *ctx,
+       struct ctf_node *type_specifier_list, GString *str)
+{
+       int ret;
+       struct ctf_node *iter;
+       int alias_item_nr = 0;
+       struct bt_list_head *head =
+               &type_specifier_list->u.type_specifier_list.head;
+
+       bt_list_for_each_entry(iter, head, siblings) {
+               if (alias_item_nr != 0) {
+                       g_string_append(str, " ");
+               }
+
+               alias_item_nr++;
+               ret = get_type_specifier_name(ctx, iter, str);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+static
+GQuark create_typealias_identifier(struct ctx *ctx,
+       struct ctf_node *type_specifier_list,
+       struct ctf_node *node_type_declarator)
+{
+       int ret;
+       char *str_c;
+       GString *str;
+       GQuark qalias = 0;
+       struct ctf_node *iter;
+       struct bt_list_head *pointers =
+               &node_type_declarator->u.type_declarator.pointers;
+
+       str = g_string_new("");
+       ret = get_type_specifier_list_name(ctx, type_specifier_list, str);
+       if (ret) {
+               g_string_free(str, TRUE);
+               goto end;
+       }
+
+       bt_list_for_each_entry(iter, pointers, siblings) {
+               g_string_append(str, " *");
+
+               if (iter->u.pointer.const_qualifier) {
+                       g_string_append(str, " const");
+               }
+       }
+
+       str_c = g_string_free(str, FALSE);
+       qalias = g_quark_from_string(str_c);
+       g_free(str_c);
+
+end:
+       return qalias;
+}
+
+static
+int visit_type_declarator(struct ctx *ctx, struct ctf_node *type_specifier_list,
+       GQuark *field_name, struct ctf_node *node_type_declarator,
+       struct bt_ctf_field_type **field_decl,
+       struct bt_ctf_field_type *nested_decl)
+{
+       /*
+        * During this whole function, nested_decl is always OURS,
+        * whereas field_decl is an output which we create, but
+        * belongs to the caller (it is moved).
+        */
+
+       int ret = 0;
+       *field_decl = NULL;
+
+       /* Validate type declarator node */
+       if (node_type_declarator) {
+               if (node_type_declarator->u.type_declarator.type ==
+                               TYPEDEC_UNKNOWN) {
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               /* TODO: GCC bitfields not supported yet */
+               if (node_type_declarator->u.type_declarator.bitfield_len !=
+                               NULL) {
+                       _PERROR("%s", "GCC bitfields are not supported as of this version");
+                       ret = -EPERM;
+                       goto error;
+               }
+       }
+
+       /* Find the right nested declaration if not provided */
+       if (!nested_decl) {
+               struct bt_list_head *pointers =
+                       &node_type_declarator->u.type_declarator.pointers;
+
+               if (node_type_declarator && !bt_list_empty(pointers)) {
+                       GQuark qalias;
+                       _BT_CTF_FIELD_TYPE_INIT(nested_decl_copy);
+
+                       /*
+                        * If we have a pointer declarator, it HAS to
+                        * be present in the typealiases (else fail).
+                        */
+                       qalias = create_typealias_identifier(ctx,
+                               type_specifier_list, node_type_declarator);
+                       nested_decl =
+                               ctx_decl_scope_lookup_alias(ctx->current_scope,
+                                       g_quark_to_string(qalias), -1);
+                       if (!nested_decl) {
+                               _PERROR("cannot find typealias \"%s\"",
+                                       g_quark_to_string(qalias));
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       /* Make a copy of it */
+                       nested_decl_copy = bt_ctf_field_type_copy(nested_decl);
+                       BT_PUT(nested_decl);
+                       if (!nested_decl_copy) {
+                               _PERROR("%s", "cannot copy nested declaration");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       BT_MOVE(nested_decl, nested_decl_copy);
+
+                       /* Force integer's base to 16 since it's a pointer */
+                       if (bt_ctf_field_type_is_integer(nested_decl)) {
+                               bt_ctf_field_type_integer_set_base(nested_decl,
+                                       BT_CTF_INTEGER_BASE_HEXADECIMAL);
+                       }
+               } else {
+                       ret = visit_type_specifier_list(ctx,
+                               type_specifier_list, &nested_decl);
+                       if (ret) {
+                               assert(!nested_decl);
+                               goto error;
+                       }
+               }
+       }
+
+       assert(nested_decl);
+
+       if (!node_type_declarator) {
+               BT_MOVE(*field_decl, nested_decl);
+               goto end;
+       }
+
+       if (node_type_declarator->u.type_declarator.type == TYPEDEC_ID) {
+               if (node_type_declarator->u.type_declarator.u.id) {
+                       const char *id =
+                               node_type_declarator->u.type_declarator.u.id;
+
+                       *field_name = g_quark_from_string(id);
+               } else {
+                       *field_name = 0;
+               }
+
+               BT_MOVE(*field_decl, nested_decl);
+               goto end;
+       } else {
+               struct ctf_node *first;
+               _BT_CTF_FIELD_TYPE_INIT(decl);
+               _BT_CTF_FIELD_TYPE_INIT(outer_field_decl);
+               struct bt_list_head *length =
+                       &node_type_declarator->
+                               u.type_declarator.u.nested.length;
+
+               /* Create array/sequence, pass nested_decl as child */
+               if (bt_list_empty(length)) {
+                       _PERROR("%s",
+                               "expecting length field reference or value");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               first = _BT_LIST_FIRST_ENTRY(length, struct ctf_node, siblings);
+               if (first->type != NODE_UNARY_EXPRESSION) {
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               switch (first->u.unary_expression.type) {
+               case UNARY_UNSIGNED_CONSTANT:
+               {
+                       size_t len;
+                       _BT_CTF_FIELD_TYPE_INIT(array_decl);
+
+                       len = first->u.unary_expression.u.unsigned_constant;
+                       array_decl = bt_ctf_field_type_array_create(nested_decl,
+                               len);
+                       BT_PUT(nested_decl);
+                       if (!array_decl) {
+                               _PERROR("%s",
+                                       "cannot create array declaration");
+                               ret = -ENOMEM;
+                               goto error;
+                       }
+
+                       BT_MOVE(decl, array_decl);
+                       break;
+               }
+               case UNARY_STRING:
+               {
+                       /* Lookup unsigned integer definition, create seq. */
+                       _BT_CTF_FIELD_TYPE_INIT(seq_decl);
+                       char *length_name = concatenate_unary_strings(length);
+
+                       if (!length_name) {
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       seq_decl = bt_ctf_field_type_sequence_create(
+                               nested_decl, length_name);
+                       g_free(length_name);
+                       BT_PUT(nested_decl);
+                       if (!seq_decl) {
+                               _PERROR("%s",
+                                       "cannot create sequence declaration");
+                               ret = -ENOMEM;
+                               goto error;
+                       }
+
+                       BT_MOVE(decl, seq_decl);
+                       break;
+               }
+               default:
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               assert(!nested_decl);
+               assert(decl);
+               assert(!*field_decl);
+
+               /*
+                * At this point, we found the next nested declaration.
+                * We currently own this (and lost the ownership of
+                * nested_decl in the meantime). Pass this next
+                * nested declaration as the content of the outer
+                * container, MOVING its ownership.
+                */
+               ret = visit_type_declarator(ctx, type_specifier_list,
+                       field_name,
+                       node_type_declarator->
+                               u.type_declarator.u.nested.type_declarator,
+                       &outer_field_decl, decl);
+               decl = NULL;
+               if (ret) {
+                       assert(!outer_field_decl);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               assert(outer_field_decl);
+               BT_MOVE(*field_decl, outer_field_decl);
+       }
+
+end:
+       BT_PUT(nested_decl);
+       assert(*field_decl);
+
+       return 0;
+
+error:
+       BT_PUT(nested_decl);
+       BT_PUT(*field_decl);
+
+       return ret;
+}
+
+static
+int visit_struct_decl_field(struct ctx *ctx,
+       struct bt_ctf_field_type *struct_decl,
+       struct ctf_node *type_specifier_list,
+       struct bt_list_head *type_declarators)
+{
+       int ret = 0;
+       struct ctf_node *iter;
+       _BT_CTF_FIELD_TYPE_INIT(field_decl);
+
+       bt_list_for_each_entry(iter, type_declarators, siblings) {
+               field_decl = NULL;
+               GQuark qfield_name;
+               const char *field_name;
+               _BT_CTF_FIELD_TYPE_INIT(efield_decl);
+
+               ret = visit_type_declarator(ctx, type_specifier_list,
+                       &qfield_name, iter, &field_decl, NULL);
+               if (ret) {
+                       assert(!field_decl);
+                       _PERROR("%s", "unable to find structure field declaration type");
+                       goto error;
+               }
+
+               assert(field_decl);
+               field_name = g_quark_to_string(qfield_name);
+
+               /* Check if field with same name already exists */
+               efield_decl =
+                       bt_ctf_field_type_structure_get_field_type_by_name(
+                               struct_decl, field_name);
+               if (efield_decl) {
+                       BT_PUT(efield_decl);
+                       _PERROR("duplicate field \"%s\" in structure",
+                               field_name);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               /* Add field to structure */
+               ret = bt_ctf_field_type_structure_add_field(struct_decl,
+                       field_decl, field_name);
+               BT_PUT(field_decl);
+               if (ret) {
+                       _PERROR("cannot add field \"%s\" to structure",
+                               g_quark_to_string(qfield_name));
+                       goto error;
+               }
+       }
+
+       return 0;
+
+error:
+       BT_PUT(field_decl);
+
+       return ret;
+}
+
+static
+int visit_variant_decl_field(struct ctx *ctx,
+       struct bt_ctf_field_type *variant_decl,
+       struct ctf_node *type_specifier_list,
+       struct bt_list_head *type_declarators)
+{
+       int ret = 0;
+       struct ctf_node *iter;
+       _BT_CTF_FIELD_TYPE_INIT(field_decl);
+
+       bt_list_for_each_entry(iter, type_declarators, siblings) {
+               field_decl = NULL;
+               GQuark qfield_name;
+               const char *field_name;
+               _BT_CTF_FIELD_TYPE_INIT(efield_decl);
+
+               ret = visit_type_declarator(ctx, type_specifier_list,
+                       &qfield_name, iter, &field_decl, NULL);
+               if (ret) {
+                       assert(!field_decl);
+                       _PERROR("%s",
+                               "unable to find variant field declaration type");
+                       goto error;
+               }
+
+               assert(field_decl);
+               field_name = g_quark_to_string(qfield_name);
+
+               /* Check if field with same name already exists */
+               efield_decl =
+                       bt_ctf_field_type_variant_get_field_type_by_name(
+                               variant_decl, field_name);
+               if (efield_decl) {
+                       BT_PUT(efield_decl);
+                       _PERROR("duplicate field \"%s\" in variant",
+                               field_name);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               /* Add field to structure */
+               ret = bt_ctf_field_type_variant_add_field(variant_decl,
+                       field_decl, field_name);
+               BT_PUT(field_decl);
+               if (ret) {
+                       _PERROR("cannot add field \"%s\" to variant",
+                               g_quark_to_string(qfield_name));
+                       goto error;
+               }
+       }
+
+       return 0;
+
+error:
+       BT_PUT(field_decl);
+
+       return ret;
+}
+
+static
+int visit_typedef(struct ctx *ctx, struct ctf_node *type_specifier_list,
+       struct bt_list_head *type_declarators)
+{
+       int ret = 0;
+       GQuark qidentifier;
+       struct ctf_node *iter;
+       _BT_CTF_FIELD_TYPE_INIT(type_decl);
+
+       bt_list_for_each_entry(iter, type_declarators, siblings) {
+               ret = visit_type_declarator(ctx, type_specifier_list,
+                       &qidentifier, iter, &type_decl, NULL);
+               if (ret) {
+                       _PERROR("%s", "problem creating type declaration");
+                       ret = -EINVAL;
+                       goto end;
+               }
+
+               /* Do not allow typedef and typealias of untagged variants */
+               if (bt_ctf_field_type_is_variant(type_decl)) {
+                       if (bt_ctf_field_type_variant_get_tag_name(type_decl)) {
+                               _PERROR("%s", "typedef of untagged variant is not allowed");
+                               ret = -EPERM;
+                               goto end;
+                       }
+               }
+
+               ret = ctx_decl_scope_register_alias(ctx->current_scope,
+                       g_quark_to_string(qidentifier), type_decl);
+               if (ret) {
+                       _PERROR("cannot register typedef \"%s\"",
+                               g_quark_to_string(qidentifier));
+                       goto end;
+               }
+       }
+
+end:
+       BT_PUT(type_decl);
+
+       return ret;
+}
+
+static
+int visit_typealias(struct ctx *ctx, struct ctf_node *target,
+       struct ctf_node *alias)
+{
+       int ret = 0;
+       GQuark qalias;
+       struct ctf_node *node;
+       GQuark qdummy_field_name;
+       _BT_CTF_FIELD_TYPE_INIT(type_decl);
+
+       /* Create target type declaration */
+       if (bt_list_empty(&target->u.typealias_target.type_declarators)) {
+               node = NULL;
+       } else {
+               node = _BT_LIST_FIRST_ENTRY(
+                       &target->u.typealias_target.type_declarators,
+                       struct ctf_node, siblings);
+       }
+
+       ret = visit_type_declarator(ctx,
+               target->u.typealias_target.type_specifier_list,
+               &qdummy_field_name, node, &type_decl, NULL);
+       if (ret) {
+               assert(!type_decl);
+               _PERROR("%s", "problem creating type declaration");
+               goto end;
+       }
+
+       /* Do not allow typedef and typealias of untagged variants */
+       if (bt_ctf_field_type_is_variant(type_decl)) {
+               if (bt_ctf_field_type_variant_get_tag_name(type_decl)) {
+                       _PERROR("%s",
+                               "typealias of untagged variant is not allowed");
+                       ret = -EPERM;
+                       goto end;
+               }
+       }
+
+       /*
+        * The semantic validator does not check whether the target is
+        * abstract or not (if it has an identifier). Check it here.
+        */
+       if (qdummy_field_name != 0) {
+               _PERROR("%s", "expecting empty identifier");
+               ret = -EINVAL;
+               goto end;
+       }
+
+       /* Create alias identifier */
+       node = _BT_LIST_FIRST_ENTRY(&alias->u.typealias_alias.type_declarators,
+               struct ctf_node, siblings);
+       qalias = create_typealias_identifier(ctx,
+               alias->u.typealias_alias.type_specifier_list, node);
+       ret = ctx_decl_scope_register_alias(ctx->current_scope,
+               g_quark_to_string(qalias), type_decl);
+       if (ret) {
+               _PERROR("cannot register typealias \"%s\"",
+                       g_quark_to_string(qalias));
+               goto end;
+       }
+
+end:
+       BT_PUT(type_decl);
+
+       return ret;
+}
+
+static
+int visit_struct_decl_entry(struct ctx *ctx, struct ctf_node *entry_node,
+       struct bt_ctf_field_type *struct_decl)
+{
+       int ret = 0;
+
+       switch (entry_node->type) {
+       case NODE_TYPEDEF:
+               ret = visit_typedef(ctx,
+                       entry_node->u._typedef.type_specifier_list,
+                       &entry_node->u._typedef.type_declarators);
+               if (ret) {
+                       _PERROR("%s",
+                               "cannot add typedef in \"struct\" declaration");
+                       goto end;
+               }
+               break;
+       case NODE_TYPEALIAS:
+               ret = visit_typealias(ctx, entry_node->u.typealias.target,
+                       entry_node->u.typealias.alias);
+               if (ret) {
+                       _PERROR("%s",
+                               "cannot add typealias in \"struct\" declaration");
+                       goto end;
+               }
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               /* Field */
+               ret = visit_struct_decl_field(ctx, struct_decl,
+                       entry_node->u.struct_or_variant_declaration.
+                               type_specifier_list,
+                       &entry_node->u.struct_or_variant_declaration.
+                               type_declarators);
+               if (ret) {
+                       goto end;
+               }
+               break;
+       default:
+               _PERROR("unexpected node type: %d", (int) entry_node->type);
+               ret = -EINVAL;
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static
+int visit_variant_decl_entry(struct ctx *ctx, struct ctf_node *entry_node,
+       struct bt_ctf_field_type *variant_decl)
+{
+       int ret = 0;
+
+       switch (entry_node->type) {
+       case NODE_TYPEDEF:
+               ret = visit_typedef(ctx,
+                       entry_node->u._typedef.type_specifier_list,
+                       &entry_node->u._typedef.type_declarators);
+               if (ret) {
+                       _PERROR("%s",
+                               "cannot add typedef in \"variant\" declaration");
+                       goto end;
+               }
+               break;
+       case NODE_TYPEALIAS:
+               ret = visit_typealias(ctx, entry_node->u.typealias.target,
+                       entry_node->u.typealias.alias);
+               if (ret) {
+                       _PERROR("%s",
+                               "cannot add typealias in \"variant\" declaration");
+                       goto end;
+               }
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               /* Field */
+               ret = visit_variant_decl_field(ctx, variant_decl,
+                       entry_node->u.struct_or_variant_declaration.
+                               type_specifier_list,
+                       &entry_node->u.struct_or_variant_declaration.
+                               type_declarators);
+               if (ret) {
+                       goto end;
+               }
+               break;
+       default:
+               _PERROR("unexpected node type: %d", (int) entry_node->type);
+               ret = -EINVAL;
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static
+int visit_struct_decl(struct ctx *ctx, const char *name,
+       struct bt_list_head *decl_list, int has_body,
+       struct bt_list_head *min_align,
+       struct bt_ctf_field_type **struct_decl)
+{
+       int ret = 0;
+
+       *struct_decl = NULL;
+
+       /* For named struct (without body), lookup in declaration scope */
+       if (!has_body) {
+               _BT_CTF_FIELD_TYPE_INIT(struct_decl_copy);
+
+               if (!name) {
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               *struct_decl = ctx_decl_scope_lookup_struct(ctx->current_scope,
+                       name, -1);
+               if (!*struct_decl) {
+                       _PERROR("cannot find \"struct %s\"", name);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               /* Make a copy of it */
+               struct_decl_copy = bt_ctf_field_type_copy(*struct_decl);
+               if (!struct_decl_copy) {
+                       _PERROR("%s",
+                               "cannot create copy of structure declaration");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               BT_MOVE(*struct_decl, struct_decl_copy);
+       } else {
+               struct ctf_node *entry_node;
+               uint64_t min_align_value = 0;
+
+               if (name) {
+                       _BT_CTF_FIELD_TYPE_INIT(estruct_decl);
+
+                       estruct_decl = ctx_decl_scope_lookup_struct(
+                               ctx->current_scope, name, 1);
+                       if (estruct_decl) {
+                               BT_PUT(estruct_decl);
+                               _PERROR("\"struct %s\" already declared in local scope",
+                                       name);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+               }
+
+               if (!bt_list_empty(min_align)) {
+                       ret = get_unary_unsigned(min_align, &min_align_value);
+                       if (ret) {
+                               _PERROR("%s", "unexpected unary expression for structure declaration's \"align\" attribute");
+                               goto error;
+                       }
+               }
+
+               *struct_decl = bt_ctf_field_type_structure_create();
+               if (!*struct_decl) {
+                       _PERROR("%s", "cannot create structure declaration");
+                       ret = -ENOMEM;
+                       goto error;
+               }
+
+               ret = ctx_push_scope(ctx);
+               if (ret) {
+                       _PERROR("%s", "cannot push scope");
+                       goto error;
+               }
+
+               bt_list_for_each_entry(entry_node, decl_list, siblings) {
+                       ret = visit_struct_decl_entry(ctx, entry_node,
+                               *struct_decl);
+                       if (ret) {
+                               ctx_pop_scope(ctx);
+                               goto error;
+                       }
+               }
+
+               ctx_pop_scope(ctx);
+
+               if (name) {
+                       ret = ctx_decl_scope_register_struct(ctx->current_scope,
+                               name, *struct_decl);
+                       if (ret) {
+                               _PERROR("cannot register \"struct %s\" in declaration scope",
+                                       name);
+                               goto error;
+                       }
+               }
+       }
+
+       return 0;
+
+error:
+       BT_PUT(*struct_decl);
+
+       return ret;
+}
+
+static
+int visit_variant_decl(struct ctx *ctx, const char *name,
+       const char *tag, struct bt_list_head *decl_list,
+       int has_body, struct bt_ctf_field_type **variant_decl)
+{
+       int ret = 0;
+       _BT_CTF_FIELD_TYPE_INIT(untagged_variant_decl);
+
+       *variant_decl = NULL;
+
+       /* For named variant (without body), lookup in declaration scope */
+       if (!has_body) {
+               _BT_CTF_FIELD_TYPE_INIT(variant_decl_copy);
+
+               if (!name) {
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               untagged_variant_decl =
+                       ctx_decl_scope_lookup_variant(ctx->current_scope,
+                               name, -1);
+               if (!untagged_variant_decl) {
+                       _PERROR("cannot find \"variant %s\"", name);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               /* Make a copy of it */
+               variant_decl_copy = bt_ctf_field_type_copy(
+                       untagged_variant_decl);
+               if (!variant_decl_copy) {
+                       _PERROR("%s",
+                               "cannot create copy of structure declaration");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               BT_MOVE(untagged_variant_decl, variant_decl_copy);
+       } else {
+               struct ctf_node *entry_node;
+
+               if (name) {
+                       struct bt_ctf_field_type *evariant_decl =
+                               ctx_decl_scope_lookup_struct(ctx->current_scope,
+                                       name, 1);
+
+                       if (evariant_decl) {
+                               BT_PUT(evariant_decl);
+                               _PERROR("\"variant %s\" already declared in local scope",
+                                       name);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+               }
+
+               untagged_variant_decl = bt_ctf_field_type_variant_create(NULL,
+                       NULL);
+               if (!untagged_variant_decl) {
+                       _PERROR("%s", "cannot create variant declaration");
+                       ret = -ENOMEM;
+                       goto error;
+               }
+
+               ret = ctx_push_scope(ctx);
+               if (ret) {
+                       _PERROR("%s", "cannot push scope");
+                       goto error;
+               }
+
+               bt_list_for_each_entry(entry_node, decl_list, siblings) {
+                       ret = visit_variant_decl_entry(ctx, entry_node,
+                               untagged_variant_decl);
+                       if (ret) {
+                               ctx_pop_scope(ctx);
+                               goto error;
+                       }
+               }
+
+               ctx_pop_scope(ctx);
+
+               if (name) {
+                       ret = ctx_decl_scope_register_variant(
+                               ctx->current_scope, name,
+                               untagged_variant_decl);
+                       if (ret) {
+                               _PERROR("cannot register \"variant %s\" in declaration scope",
+                                       name);
+                               goto error;
+                       }
+               }
+       }
+
+       /*
+        * If tagged, create tagged variant and return; otherwise
+        * return untagged variant.
+        */
+       if (!tag) {
+               BT_MOVE(*variant_decl, untagged_variant_decl);
+       } else {
+               /*
+                * At this point, we have a fresh untagged variant; nobody
+                * else owns it. Set its tag now.
+                */
+               ret = bt_ctf_field_type_variant_set_tag_name(
+                       untagged_variant_decl, tag);
+               if (ret) {
+                       goto error;
+               }
+
+               BT_MOVE(*variant_decl, untagged_variant_decl);
+       }
+
+       assert(!untagged_variant_decl);
+       assert(*variant_decl);
+
+       return 0;
+
+error:
+       BT_PUT(untagged_variant_decl);
+       BT_PUT(*variant_decl);
+
+       return ret;
+}
+
+static
+int visit_enum_decl_entry(struct ctx *ctx, struct ctf_node *enumerator,
+       struct bt_ctf_field_type *enum_decl, int64_t *last, int is_signed)
+{
+       int ret = 0;
+       int nr_vals = 0;
+       struct ctf_node *iter;
+       int64_t start = 0, end = 0;
+       const char *label = enumerator->u.enumerator.id;
+       struct bt_list_head *values = &enumerator->u.enumerator.values;
+
+       bt_list_for_each_entry(iter, values, siblings) {
+               int64_t *target;
+
+               if (iter->type != NODE_UNARY_EXPRESSION) {
+                       _PERROR("wrong unary expression for enumeration label \"%s\"",
+                               label);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (nr_vals == 0) {
+                       target = &start;
+               } else {
+                       target = &end;
+               }
+
+               switch (iter->u.unary_expression.type) {
+               case UNARY_SIGNED_CONSTANT:
+                       *target = iter->u.unary_expression.u.signed_constant;
+                       break;
+               case UNARY_UNSIGNED_CONSTANT:
+                       *target = (int64_t)
+                               iter->u.unary_expression.u.unsigned_constant;
+                       break;
+               default:
+                       _PERROR("invalid enumeration entry: \"%s\"",
+                               label);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (nr_vals > 1) {
+                       _PERROR("invalid enumeration entry: \"%s\"",
+                               label);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               nr_vals++;
+       }
+
+       if (nr_vals == 0) {
+               start = *last;
+       }
+
+       if (nr_vals <= 1) {
+               end = start;
+       }
+
+       *last = end + 1;
+
+       if (is_signed) {
+               ret = bt_ctf_field_type_enumeration_add_mapping(enum_decl, label,
+                       start, end);
+       } else {
+               ret = bt_ctf_field_type_enumeration_add_mapping_unsigned(enum_decl,
+                       label, (uint64_t) start, (uint64_t) end);
+       }
+       if (ret) {
+               _PERROR("cannot add mapping to enumeration for label \"%s\"",
+                       label);
+               goto error;
+       }
+
+       return 0;
+
+error:
+       return ret;
+}
+
+static
+int visit_enum_decl(struct ctx *ctx, const char *name,
+       struct ctf_node *container_type,
+       struct bt_list_head *enumerator_list,
+       int has_body,
+       struct bt_ctf_field_type **enum_decl)
+{
+       int ret = 0;
+       GQuark qdummy_id;
+       _BT_CTF_FIELD_TYPE_INIT(integer_decl);
+
+       *enum_decl = NULL;
+
+       /* For named enum (without body), lookup in declaration scope */
+       if (!has_body) {
+               _BT_CTF_FIELD_TYPE_INIT(enum_decl_copy);
+
+               if (!name) {
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               *enum_decl = ctx_decl_scope_lookup_enum(ctx->current_scope,
+                       name, -1);
+               if (!*enum_decl) {
+                       _PERROR("cannot find \"enum %s\"", name);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               /* Make a copy of it */
+               enum_decl_copy = bt_ctf_field_type_copy(*enum_decl);
+               if (!enum_decl_copy) {
+                       _PERROR("%s",
+                               "cannot create copy of enumeration declaration");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               BT_PUT(*enum_decl);
+               BT_MOVE(*enum_decl, enum_decl_copy);
+       } else {
+               struct ctf_node *iter;
+               int64_t last_value = 0;
+
+               if (name) {
+                       _BT_CTF_FIELD_TYPE_INIT(eenum_decl);
+
+                       eenum_decl = ctx_decl_scope_lookup_enum(
+                               ctx->current_scope, name, 1);
+                       if (eenum_decl) {
+                               BT_PUT(eenum_decl);
+                               _PERROR("\"enum %s\" already declared in local scope",
+                                       name);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+               }
+
+               if (!container_type) {
+                       integer_decl = ctx_decl_scope_lookup_alias(
+                               ctx->current_scope, "int", -1);
+                       if (!integer_decl) {
+                               _PERROR("%s", "cannot find \"int\" type for enumeration");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+               } else {
+                       ret = visit_type_declarator(ctx, container_type,
+                               &qdummy_id, NULL, &integer_decl, NULL);
+                       if (ret) {
+                               assert(!integer_decl);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+               }
+
+               assert(integer_decl);
+
+               if (!bt_ctf_field_type_is_integer(integer_decl)) {
+                       _PERROR("%s", "container type for enumeration is not an integer");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               *enum_decl = bt_ctf_field_type_enumeration_create(integer_decl);
+               if (!*enum_decl) {
+                       _PERROR("%s", "cannot create enumeration declaration");
+                       ret = -ENOMEM;
+                       goto error;
+               }
+
+               bt_list_for_each_entry(iter, enumerator_list, siblings) {
+                       ret = visit_enum_decl_entry(ctx, iter, *enum_decl,
+                               &last_value,
+                               bt_ctf_field_type_integer_get_signed(integer_decl));
+                       if (ret) {
+                               goto error;
+                       }
+               }
+
+               if (name) {
+                       ret = ctx_decl_scope_register_enum(ctx->current_scope,
+                               name, *enum_decl);
+                       if (ret) {
+                               goto error;
+                       }
+               }
+       }
+
+       BT_PUT(integer_decl);
+
+       return 0;
+
+error:
+       BT_PUT(integer_decl);
+       BT_PUT(*enum_decl);
+
+       return ret;
+}
+
+static
+int visit_type_specifier(struct ctx *ctx,
+       struct ctf_node *type_specifier_list,
+       struct bt_ctf_field_type **decl)
+{
+       int ret = 0;
+       GString *str = NULL;
+       _BT_CTF_FIELD_TYPE_INIT(decl_copy);
+
+       *decl = NULL;
+       str = g_string_new("");
+       ret = get_type_specifier_list_name(ctx, type_specifier_list, str);
+       if (ret) {
+               goto error;
+       }
+
+       *decl = ctx_decl_scope_lookup_alias(ctx->current_scope, str->str, -1);
+       if (!*decl) {
+               _PERROR("cannot find type alias \"%s\"", str->str);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       /* Make a copy of the type declaration */
+       decl_copy = bt_ctf_field_type_copy(*decl);
+       if (!decl_copy) {
+               _PERROR("%s", "cannot create copy of type declaration");
+               ret = -EINVAL;
+               goto error;
+       }
+
+       BT_MOVE(*decl, decl_copy);
+       (void) g_string_free(str, TRUE);
+       str = NULL;
+
+       return 0;
+
+error:
+       if (str) {
+               (void) g_string_free(str, TRUE);
+       }
+
+       BT_PUT(*decl);
+
+       return ret;
+}
+
+static
+int visit_integer_decl(struct ctx *ctx,
+       struct bt_list_head *expressions,
+       struct bt_ctf_field_type **integer_decl)
+{
+       int set = 0;
+       int ret = 0;
+       int signedness = 0;
+       struct ctf_node *expression;
+       uint64_t alignment = 0, size = 0;
+       struct bt_ctf_clock *mapped_clock = NULL;
+       enum bt_ctf_string_encoding encoding = BT_CTF_STRING_ENCODING_NONE;
+       enum bt_ctf_integer_base base = BT_CTF_INTEGER_BASE_DECIMAL;
+       enum bt_ctf_byte_order byte_order =
+               bt_ctf_trace_get_byte_order(ctx->trace);
+
+       *integer_decl = NULL;
+
+       bt_list_for_each_entry(expression, expressions, siblings) {
+               struct ctf_node *left, *right;
+
+               left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left,
+                       struct ctf_node, siblings);
+               right = _BT_LIST_FIRST_ENTRY(
+                       &expression->u.ctf_expression.right, struct ctf_node,
+                       siblings);
+
+               if (left->u.unary_expression.type != UNARY_STRING) {
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (!strcmp(left->u.unary_expression.u.string, "signed")) {
+                       if (_IS_SET(&set, _INTEGER_SIGNED_SET)) {
+                               _PERROR_DUP_ATTR("signed",
+                                       "integer declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       signedness = get_boolean(ctx->efd, right);
+                       if (signedness < 0) {
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(&set, _INTEGER_SIGNED_SET);
+               } else if (!strcmp(left->u.unary_expression.u.string,
+                               "byte_order")) {
+                       if (_IS_SET(&set, _INTEGER_BYTE_ORDER_SET)) {
+                               _PERROR_DUP_ATTR("byte_order",
+                                       "integer declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       byte_order = get_real_byte_order(ctx, right);
+                       if (byte_order == BT_CTF_BYTE_ORDER_UNKNOWN) {
+                               _PERROR("%s", "invalid \"byte_order\" attribute in integer declaration");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(&set, _INTEGER_BYTE_ORDER_SET);
+               } else if (!strcmp(left->u.unary_expression.u.string, "size")) {
+                       if (_IS_SET(&set, _INTEGER_SIZE_SET)) {
+                               _PERROR_DUP_ATTR("size",
+                                       "integer declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       if (right->u.unary_expression.type !=
+                                       UNARY_UNSIGNED_CONSTANT) {
+                               _PERROR("%s", "invalid \"size\" attribute in integer declaration: expecting unsigned constant");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       size = right->u.unary_expression.u.unsigned_constant;
+                       if (size == 0) {
+                               _PERROR("%s", "invalid \"size\" attribute in integer declaration: expecting positive constant");
+                               ret = -EINVAL;
+                               goto error;
+                       } else if (size > 64) {
+                               _PERROR("%s", "invalid \"size\" attribute in integer declaration: integers over 64-bit are not supported as of this version");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(&set, _INTEGER_SIZE_SET);
+               } else if (!strcmp(left->u.unary_expression.u.string,
+                               "align")) {
+                       if (_IS_SET(&set, _INTEGER_ALIGN_SET)) {
+                               _PERROR_DUP_ATTR("align",
+                                       "integer declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       if (right->u.unary_expression.type !=
+                                       UNARY_UNSIGNED_CONSTANT) {
+                               _PERROR("%s", "invalid \"align\" attribute in integer declaration: expecting unsigned constant");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       alignment =
+                               right->u.unary_expression.u.unsigned_constant;
+                       if (!is_align_valid(alignment)) {
+                               _PERROR("%s", "invalid \"align\" attribute in integer declaration: expecting power of two");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(&set, _INTEGER_ALIGN_SET);
+               } else if (!strcmp(left->u.unary_expression.u.string, "base")) {
+                       if (_IS_SET(&set, _INTEGER_BASE_SET)) {
+                               _PERROR_DUP_ATTR("base", "integer declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       switch (right->u.unary_expression.type) {
+                       case UNARY_UNSIGNED_CONSTANT:
+                       {
+                               uint64_t constant = right->u.unary_expression.
+                                       u.unsigned_constant;
+
+                               switch (constant) {
+                               case 2:
+                                       base = BT_CTF_INTEGER_BASE_BINARY;
+                                       break;
+                               case 8:
+                                       base = BT_CTF_INTEGER_BASE_OCTAL;
+                                       break;
+                               case 10:
+                                       base = BT_CTF_INTEGER_BASE_DECIMAL;
+                                       break;
+                               case 16:
+                                       base = BT_CTF_INTEGER_BASE_HEXADECIMAL;
+                                       break;
+                               default:
+                                       _PERROR("invalid \"base\" attribute in integer declaration: %" PRIu64,
+                                               right->u.unary_expression.u.unsigned_constant);
+                                       ret = -EINVAL;
+                                       goto error;
+                               }
+                               break;
+                       }
+                       case UNARY_STRING:
+                       {
+                               char *s_right = concatenate_unary_strings(
+                                       &expression->u.ctf_expression.right);
+                               if (!s_right) {
+                                       _PERROR("%s", "unexpected unary expression for integer declaration's \"base\" attribute");
+                                       ret = -EINVAL;
+                                       goto error;
+                               }
+
+                               if (!strcmp(s_right, "decimal") ||
+                                               !strcmp(s_right, "dec") ||
+                                               !strcmp(s_right, "d") ||
+                                               !strcmp(s_right, "i") ||
+                                               !strcmp(s_right, "u")) {
+                                       base = BT_CTF_INTEGER_BASE_DECIMAL;
+                               } else if (!strcmp(s_right, "hexadecimal") ||
+                                               !strcmp(s_right, "hex") ||
+                                               !strcmp(s_right, "x") ||
+                                               !strcmp(s_right, "X") ||
+                                               !strcmp(s_right, "p")) {
+                                       base = BT_CTF_INTEGER_BASE_HEXADECIMAL;
+                               } else if (!strcmp(s_right, "octal") ||
+                                               !strcmp(s_right, "oct") ||
+                                               !strcmp(s_right, "o")) {
+                                       base = BT_CTF_INTEGER_BASE_OCTAL;
+                               } else if (!strcmp(s_right, "binary") ||
+                                               !strcmp(s_right, "b")) {
+                                       base = BT_CTF_INTEGER_BASE_BINARY;
+                               } else {
+                                       _PERROR("unexpected unary expression for integer declaration's \"base\" attribute: \"%s\"",
+                                               s_right);
+                                       g_free(s_right);
+                                       ret = -EINVAL;
+                                       goto error;
+                               }
+
+                               g_free(s_right);
+                               break;
+                       }
+                       default:
+                               _PERROR("%s", "invalid \"base\" attribute in integer declaration: expecting unsigned constant or unary string");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(&set, _INTEGER_BASE_SET);
+               } else if (!strcmp(left->u.unary_expression.u.string,
+                               "encoding")) {
+                       char *s_right;
+
+                       if (_IS_SET(&set, _INTEGER_ENCODING_SET)) {
+                               _PERROR_DUP_ATTR("encoding",
+                                       "integer declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       if (right->u.unary_expression.type != UNARY_STRING) {
+                               _PERROR("%s", "invalid \"encoding\" attribute in integer declaration: expecting unary string");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       s_right = concatenate_unary_strings(
+                               &expression->u.ctf_expression.right);
+                       if (!s_right) {
+                               _PERROR("%s", "unexpected unary expression for integer declaration's \"encoding\" attribute");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       if (!strcmp(s_right, "UTF8") ||
+                                       !strcmp(s_right, "utf8") ||
+                                       !strcmp(s_right, "utf-8") ||
+                                       !strcmp(s_right, "UTF-8")) {
+                               encoding = BT_CTF_STRING_ENCODING_UTF8;
+                       } else if (!strcmp(s_right, "ASCII") ||
+                                       !strcmp(s_right, "ascii")) {
+                               encoding = BT_CTF_STRING_ENCODING_ASCII;
+                       } else if (!strcmp(s_right, "none")) {
+                               encoding = BT_CTF_STRING_ENCODING_NONE;
+                       } else {
+                               _PERROR("invalid \"encoding\" attribute in integer declaration: unknown encoding \"%s\"",
+                                       s_right);
+                               g_free(s_right);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       g_free(s_right);
+                       _SET(&set, _INTEGER_ENCODING_SET);
+               } else if (!strcmp(left->u.unary_expression.u.string, "map")) {
+                       const char *clock_name;
+
+                       if (_IS_SET(&set, _INTEGER_MAP_SET)) {
+                               _PERROR_DUP_ATTR("map", "integer declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       if (right->u.unary_expression.type != UNARY_STRING) {
+                               _PERROR("%s", "invalid \"map\" attribute in integer declaration: expecting unary string");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       clock_name =
+                               get_map_clock_name_value(
+                                       &expression->u.ctf_expression.right);
+                       if (!clock_name) {
+                               char *s_right = concatenate_unary_strings(
+                                       &expression->u.ctf_expression.right);
+
+                               if (!s_right) {
+                                       _PERROR("%s", "unexpected unary expression for integer declaration's \"map\" attribute");
+                                       ret = -EINVAL;
+                                       goto error;
+                               }
+
+                               _PWARNING("invalid \"map\" attribute in integer declaration: unknown clock: \"%s\"",
+                                       s_right);
+                               _SET(&set, _INTEGER_MAP_SET);
+                               g_free(s_right);
+                               continue;
+                       }
+
+                       mapped_clock = bt_ctf_trace_get_clock_by_name(
+                               ctx->trace, clock_name);
+                       if (!mapped_clock) {
+                               _PERROR("invalid \"map\" attribute in integer declaration: cannot find clock \"%s\"",
+                                       clock_name);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(&set, _INTEGER_MAP_SET);
+               } else {
+                       _PWARNING("unknown attribute \"%s\" in integer declaration",
+                               left->u.unary_expression.u.string);
+               }
+       }
+
+       if (!_IS_SET(&set, _INTEGER_SIZE_SET)) {
+               _PERROR("%s",
+                       "missing \"size\" attribute in integer declaration");
+               ret = -EPERM;
+               goto error;
+       }
+
+       if (!_IS_SET(&set, _INTEGER_ALIGN_SET)) {
+               if (size % CHAR_BIT) {
+                       /* Bit-packed alignment */
+                       alignment = 1;
+               } else {
+                       /* Byte-packed alignment */
+                       alignment = CHAR_BIT;
+               }
+       }
+
+       *integer_decl = bt_ctf_field_type_integer_create((unsigned int) size);
+       if (!*integer_decl) {
+               _PERROR("%s", "cannot create integer declaration");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       ret = bt_ctf_field_type_integer_set_signed(*integer_decl, signedness);
+       ret |= bt_ctf_field_type_integer_set_base(*integer_decl, base);
+       ret |= bt_ctf_field_type_integer_set_encoding(*integer_decl, encoding);
+       ret |= bt_ctf_field_type_set_alignment(*integer_decl,
+               (unsigned int) alignment);
+       ret |= bt_ctf_field_type_set_byte_order(*integer_decl, byte_order);
+
+       if (mapped_clock) {
+               /* Move clock */
+               ret |= bt_ctf_field_type_integer_set_mapped_clock(
+                       *integer_decl, mapped_clock);
+               bt_put(mapped_clock);
+               mapped_clock = NULL;
+       }
+
+       if (ret) {
+               _PERROR("%s", "cannot configure integer declaration");
+               ret = -EINVAL;
+               goto error;
+       }
+
+       return 0;
+
+error:
+       if (mapped_clock) {
+               bt_put(mapped_clock);
+       }
+
+       BT_PUT(*integer_decl);
+
+       return ret;
+}
+
+static
+int visit_floating_point_number_decl(struct ctx *ctx,
+       struct bt_list_head *expressions,
+       struct bt_ctf_field_type **float_decl)
+{
+       int set = 0;
+       int ret = 0;
+       struct ctf_node *expression;
+       uint64_t alignment = 1, exp_dig = 0, mant_dig = 0;
+       enum bt_ctf_byte_order byte_order =
+               bt_ctf_trace_get_byte_order(ctx->trace);
+
+       *float_decl = NULL;
+
+       bt_list_for_each_entry(expression, expressions, siblings) {
+               struct ctf_node *left, *right;
+
+               left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left,
+                       struct ctf_node, siblings);
+               right = _BT_LIST_FIRST_ENTRY(
+                       &expression->u.ctf_expression.right, struct ctf_node,
+                       siblings);
+
+               if (left->u.unary_expression.type != UNARY_STRING) {
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (!strcmp(left->u.unary_expression.u.string, "byte_order")) {
+                       if (_IS_SET(&set, _FLOAT_BYTE_ORDER_SET)) {
+                               _PERROR_DUP_ATTR("byte_order",
+                                       "floating point number declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       byte_order = get_real_byte_order(ctx, right);
+                       if (byte_order == BT_CTF_BYTE_ORDER_UNKNOWN) {
+                               _PERROR("%s", "invalid \"byte_order\" attribute in floating point number declaration");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(&set, _FLOAT_BYTE_ORDER_SET);
+               } else if (!strcmp(left->u.unary_expression.u.string,
+                               "exp_dig")) {
+                       if (_IS_SET(&set, _FLOAT_EXP_DIG_SET)) {
+                               _PERROR_DUP_ATTR("exp_dig",
+                                       "floating point number declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       if (right->u.unary_expression.type !=
+                                       UNARY_UNSIGNED_CONSTANT) {
+                               _PERROR("%s", "invalid \"exp_dig\" attribute in floating point number declaration: expecting unsigned constant");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       exp_dig = right->u.unary_expression.u.unsigned_constant;
+                       _SET(&set, _FLOAT_EXP_DIG_SET);
+               } else if (!strcmp(left->u.unary_expression.u.string,
+                               "mant_dig")) {
+                       if (_IS_SET(&set, _FLOAT_MANT_DIG_SET)) {
+                               _PERROR_DUP_ATTR("mant_dig",
+                                       "floating point number declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       if (right->u.unary_expression.type !=
+                                       UNARY_UNSIGNED_CONSTANT) {
+                               _PERROR("%s", "invalid \"mant_dig\" attribute in floating point number declaration: expecting unsigned constant");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       mant_dig = right->u.unary_expression.u.
+                               unsigned_constant;
+                       _SET(&set, _FLOAT_MANT_DIG_SET);
+               } else if (!strcmp(left->u.unary_expression.u.string,
+                               "align")) {
+                       if (_IS_SET(&set, _FLOAT_ALIGN_SET)) {
+                               _PERROR_DUP_ATTR("align",
+                                       "floating point number declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       if (right->u.unary_expression.type !=
+                                       UNARY_UNSIGNED_CONSTANT) {
+                               _PERROR("%s", "invalid \"align\" attribute in floating point number declaration: expecting unsigned constant");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       alignment = right->u.unary_expression.u.
+                               unsigned_constant;
+
+                       if (!is_align_valid(alignment)) {
+                               _PERROR("%s", "invalid \"align\" attribute in floating point number declaration: expecting power of two");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(&set, _FLOAT_ALIGN_SET);
+               } else {
+                       _PWARNING("unknown attribute \"%s\" in floating point number declaration",
+                               left->u.unary_expression.u.string);
+               }
+       }
+
+       if (!_IS_SET(&set, _FLOAT_MANT_DIG_SET)) {
+               _PERROR("%s", "missing \"mant_dig\" attribute in floating point number declaration");
+               ret = -EPERM;
+               goto error;
+       }
+
+       if (!_IS_SET(&set, _FLOAT_EXP_DIG_SET)) {
+               _PERROR("%s", "missing \"exp_dig\" attribute in floating point number declaration");
+               ret = -EPERM;
+               goto error;
+       }
+
+       if (!_IS_SET(&set, _INTEGER_ALIGN_SET)) {
+               if ((mant_dig + exp_dig) % CHAR_BIT) {
+                       /* Bit-packed alignment */
+                       alignment = 1;
+               } else {
+                       /* Byte-packed alignment */
+                       alignment = CHAR_BIT;
+               }
+       }
+
+       *float_decl = bt_ctf_field_type_floating_point_create();
+       if (!*float_decl) {
+               _PERROR("%s",
+                       "cannot create floating point number declaration");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       ret = bt_ctf_field_type_floating_point_set_exponent_digits(
+               *float_decl, exp_dig);
+       ret |= bt_ctf_field_type_floating_point_set_mantissa_digits(
+               *float_decl, mant_dig);
+       ret |= bt_ctf_field_type_set_byte_order(*float_decl, byte_order);
+       ret |= bt_ctf_field_type_set_alignment(*float_decl, alignment);
+       if (ret) {
+               _PERROR("%s",
+                       "cannot configure floating point number declaration");
+               ret = -EINVAL;
+               goto error;
+       }
+
+       return 0;
+
+error:
+       BT_PUT(*float_decl);
+
+       return ret;
+}
+
+static
+int visit_string_decl(struct ctx *ctx,
+       struct bt_list_head *expressions,
+       struct bt_ctf_field_type **string_decl)
+{
+       int set = 0;
+       int ret = 0;
+       struct ctf_node *expression;
+       enum bt_ctf_string_encoding encoding = BT_CTF_STRING_ENCODING_UTF8;
+
+       *string_decl = NULL;
+
+       bt_list_for_each_entry(expression, expressions, siblings) {
+               struct ctf_node *left, *right;
+
+               left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left,
+                       struct ctf_node, siblings);
+               right = _BT_LIST_FIRST_ENTRY(
+                       &expression->u.ctf_expression.right, struct ctf_node,
+                       siblings);
+
+               if (left->u.unary_expression.type != UNARY_STRING) {
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (!strcmp(left->u.unary_expression.u.string, "encoding")) {
+                       char *s_right;
+
+                       if (_IS_SET(&set, _STRING_ENCODING_SET)) {
+                               _PERROR_DUP_ATTR("encoding",
+                                       "string declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       if (right->u.unary_expression.type != UNARY_STRING) {
+                               _PERROR("%s", "invalid \"encoding\" attribute in string declaration: expecting unary string");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       s_right = concatenate_unary_strings(
+                               &expression->u.ctf_expression.right);
+                       if (!s_right) {
+                               _PERROR("%s", "unexpected unary expression for string declaration's \"encoding\" attribute");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       if (!strcmp(s_right, "UTF8") ||
+                                       !strcmp(s_right, "utf8") ||
+                                       !strcmp(s_right, "utf-8") ||
+                                       !strcmp(s_right, "UTF-8")) {
+                               encoding = BT_CTF_STRING_ENCODING_UTF8;
+                       } else if (!strcmp(s_right, "ASCII") ||
+                                       !strcmp(s_right, "ascii")) {
+                               encoding = BT_CTF_STRING_ENCODING_ASCII;
+                       } else if (!strcmp(s_right, "none")) {
+                               encoding = BT_CTF_STRING_ENCODING_NONE;
+                       } else {
+                               _PERROR("invalid \"encoding\" attribute in string declaration: unknown encoding \"%s\"",
+                                       s_right);
+                               g_free(s_right);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       g_free(s_right);
+                       _SET(&set, _STRING_ENCODING_SET);
+               } else {
+                       _PWARNING("unknown attribute \"%s\" in string declaration",
+                               left->u.unary_expression.u.string);
+               }
+       }
+
+       *string_decl = bt_ctf_field_type_string_create();
+       if (!*string_decl) {
+               _PERROR("%s", "cannot create string declaration");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       ret = bt_ctf_field_type_string_set_encoding(*string_decl, encoding);
+       if (ret) {
+               _PERROR("%s", "cannot configure string declaration");
+               ret = -EINVAL;
+               goto error;
+       }
+
+       return 0;
+
+error:
+       BT_PUT(*string_decl);
+
+       return ret;
+}
+
+static
+int visit_type_specifier_list(struct ctx *ctx,
+       struct ctf_node *ts_list,
+       struct bt_ctf_field_type **decl)
+{
+       int ret = 0;
+       struct ctf_node *first, *node;
+
+       *decl = NULL;
+
+       if (ts_list->type != NODE_TYPE_SPECIFIER_LIST) {
+               ret = -EINVAL;
+               goto error;
+       }
+
+       first = _BT_LIST_FIRST_ENTRY(&ts_list->u.type_specifier_list.head,
+               struct ctf_node, siblings);
+       if (first->type != NODE_TYPE_SPECIFIER) {
+               ret = -EINVAL;
+               goto error;
+       }
+
+       node = first->u.type_specifier.node;
+
+       switch (first->u.type_specifier.type) {
+       case TYPESPEC_INTEGER:
+               ret = visit_integer_decl(ctx, &node->u.integer.expressions,
+                       decl);
+               if (ret) {
+                       assert(!*decl);
+                       goto error;
+               }
+               break;
+       case TYPESPEC_FLOATING_POINT:
+               ret = visit_floating_point_number_decl(ctx,
+                       &node->u.floating_point.expressions, decl);
+               if (ret) {
+                       assert(!*decl);
+                       goto error;
+               }
+               break;
+       case TYPESPEC_STRING:
+               ret = visit_string_decl(ctx,
+                       &node->u.string.expressions, decl);
+               if (ret) {
+                       assert(!*decl);
+                       goto error;
+               }
+               break;
+       case TYPESPEC_STRUCT:
+               ret = visit_struct_decl(ctx, node->u._struct.name,
+                       &node->u._struct.declaration_list,
+                       node->u._struct.has_body,
+                       &node->u._struct.min_align, decl);
+               if (ret) {
+                       assert(!*decl);
+                       goto error;
+               }
+               break;
+       case TYPESPEC_VARIANT:
+               ret = visit_variant_decl(ctx, node->u.variant.name,
+                       node->u.variant.choice,
+                       &node->u.variant.declaration_list,
+                       node->u.variant.has_body, decl);
+               if (ret) {
+                       assert(!*decl);
+                       goto error;
+               }
+               break;
+       case TYPESPEC_ENUM:
+               ret = visit_enum_decl(ctx, node->u._enum.enum_id,
+                       node->u._enum.container_type,
+                       &node->u._enum.enumerator_list,
+                       node->u._enum.has_body, decl);
+               if (ret) {
+                       assert(!*decl);
+                       goto error;
+               }
+               break;
+       case TYPESPEC_VOID:
+       case TYPESPEC_CHAR:
+       case TYPESPEC_SHORT:
+       case TYPESPEC_INT:
+       case TYPESPEC_LONG:
+       case TYPESPEC_FLOAT:
+       case TYPESPEC_DOUBLE:
+       case TYPESPEC_SIGNED:
+       case TYPESPEC_UNSIGNED:
+       case TYPESPEC_BOOL:
+       case TYPESPEC_COMPLEX:
+       case TYPESPEC_IMAGINARY:
+       case TYPESPEC_CONST:
+       case TYPESPEC_ID_TYPE:
+               ret = visit_type_specifier(ctx, ts_list, decl);
+               if (ret) {
+                       assert(!*decl);
+                       goto error;
+               }
+               break;
+       default:
+               _PERROR("unexpected node type: %d",
+                       (int) first->u.type_specifier.type);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       assert(*decl);
+
+       return 0;
+
+error:
+       BT_PUT(*decl);
+
+       return ret;
+}
+
+static
+int visit_event_decl_entry(struct ctx *ctx, struct ctf_node *node,
+       struct bt_ctf_event_class *event_class, int64_t *stream_id,
+       int *set)
+{
+       int ret = 0;
+       char *left = NULL;
+       _BT_CTF_FIELD_TYPE_INIT(decl);
+
+       switch (node->type) {
+       case NODE_TYPEDEF:
+               ret = visit_typedef(ctx, node->u._typedef.type_specifier_list,
+                       &node->u._typedef.type_declarators);
+               if (ret) {
+                       _PERROR("%s",
+                               "cannot add typedef in \"event\" declaration");
+                       goto error;
+               }
+               break;
+       case NODE_TYPEALIAS:
+               ret = visit_typealias(ctx, node->u.typealias.target,
+                       node->u.typealias.alias);
+               if (ret) {
+                       _PERROR("%s", "cannot add typealias in \"event\" declaration");
+                       goto error;
+               }
+               break;
+       case NODE_CTF_EXPRESSION:
+       {
+               left = concatenate_unary_strings(&node->u.ctf_expression.left);
+               if (!left) {
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (!strcmp(left, "name")) {
+                       /* This is already known at this stage */
+                       if (_IS_SET(set, _EVENT_NAME_SET)) {
+                               _PERROR_DUP_ATTR("name", "event declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       _SET(set, _EVENT_NAME_SET);
+               } else if (!strcmp(left, "id")) {
+                       int64_t id;
+
+                       if (_IS_SET(set, _EVENT_ID_SET)) {
+                               _PERROR_DUP_ATTR("id", "event declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = get_unary_unsigned(&node->u.ctf_expression.right,
+                               (uint64_t *) &id);
+                       if (ret || id < 0) {
+                               _PERROR("%s", "unexpected unary expression for event declaration's \"id\" attribute");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       ret = bt_ctf_event_class_set_id(event_class, id);
+                       if (ret) {
+                               _PERROR("%s",
+                                       "cannot set event declaration's ID");
+                               goto error;
+                       }
+
+                       _SET(set, _EVENT_ID_SET);
+               } else if (!strcmp(left, "stream_id")) {
+                       if (_IS_SET(set, _EVENT_STREAM_ID_SET)) {
+                               _PERROR_DUP_ATTR("stream_id",
+                                       "event declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = get_unary_unsigned(&node->u.ctf_expression.right,
+                               (uint64_t *) stream_id);
+                       if (ret || *stream_id < 0) {
+                               _PERROR("%s", "unexpected unary expression for event declaration's \"stream_id\" attribute");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(set, _EVENT_STREAM_ID_SET);
+               } else if (!strcmp(left, "context")) {
+                       if (_IS_SET(set, _EVENT_CONTEXT_SET)) {
+                               _PERROR("%s", "duplicate \"context\" entry in event declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = visit_type_specifier_list(ctx,
+                               _BT_LIST_FIRST_ENTRY(
+                                       &node->u.ctf_expression.right,
+                                       struct ctf_node, siblings),
+                               &decl);
+                       if (ret) {
+                               _PERROR("%s", "cannot create event context declaration");
+                               goto error;
+                       }
+
+                       assert(decl);
+                       ret = bt_ctf_event_class_set_context_type(
+                               event_class, decl);
+                       BT_PUT(decl);
+                       if (ret) {
+                               _PERROR("%s", "cannot set event's context declaration");
+                               goto error;
+                       }
+
+                       _SET(set, _EVENT_CONTEXT_SET);
+               } else if (!strcmp(left, "fields")) {
+                       if (_IS_SET(set, _EVENT_FIELDS_SET)) {
+                               _PERROR("%s", "duplicate \"fields\" entry in event declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = visit_type_specifier_list(ctx,
+                               _BT_LIST_FIRST_ENTRY(
+                                       &node->u.ctf_expression.right,
+                                       struct ctf_node, siblings),
+                               &decl);
+                       if (ret) {
+                               _PERROR("%s", "cannot create event payload declaration");
+                               goto error;
+                       }
+
+                       assert(decl);
+                       ret = bt_ctf_event_class_set_payload_type(
+                               event_class, decl);
+                       BT_PUT(decl);
+                       if (ret) {
+                               _PERROR("%s", "cannot set event's payload declaration");
+                               goto error;
+                       }
+
+                       _SET(set, _EVENT_FIELDS_SET);
+               } else if (!strcmp(left, "loglevel")) {
+                       uint64_t loglevel;
+
+                       if (_IS_SET(set, _EVENT_LOGLEVEL_SET)) {
+                               _PERROR_DUP_ATTR("loglevel",
+                                       "event declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = get_unary_unsigned(&node->u.ctf_expression.right,
+                               &loglevel);
+                       if (ret) {
+                               _PERROR("%s", "unexpected unary expression for event declaration's \"loglevel\" attribute");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       // TODO: FIXME: set log level here
+
+                       _SET(set, _EVENT_LOGLEVEL_SET);
+               } else if (!strcmp(left, "model.emf.uri")) {
+                       char *right;
+
+                       if (_IS_SET(set, _EVENT_MODEL_EMF_URI_SET)) {
+                               _PERROR_DUP_ATTR("model.emf.uri",
+                                       "event declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       right = concatenate_unary_strings(
+                               &node->u.ctf_expression.right);
+                       if (!right) {
+                               _PERROR("%s", "unexpected unary expression for event declaration's \"model.emf.uri\" attribute");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       // TODO: FIXME: set model EMF URI here
+
+                       g_free(right);
+                       _SET(set, _EVENT_MODEL_EMF_URI_SET);
+               } else {
+                       _PWARNING("unknown attribute \"%s\" in event declaration",
+                               left);
+               }
+
+               g_free(left);
+               left = NULL;
+               break;
+       }
+       default:
+               ret = -EPERM;
+               goto error;
+       }
+
+       return 0;
+
+error:
+       if (left) {
+               g_free(left);
+       }
+
+       BT_PUT(decl);
+
+       return ret;
+}
+
+static
+char *get_event_decl_name(struct ctx *ctx, struct ctf_node *node)
+{
+       char *left = NULL;
+       char *name = NULL;
+       struct ctf_node *iter;
+       struct bt_list_head *decl_list = &node->u.event.declaration_list;
+
+       bt_list_for_each_entry(iter, decl_list, siblings) {
+               if (iter->type != NODE_CTF_EXPRESSION) {
+                       continue;
+               }
+
+               left = concatenate_unary_strings(&iter->u.ctf_expression.left);
+               if (!left) {
+                       goto error;
+               }
+
+               if (!strcmp(left, "name")) {
+                       name = concatenate_unary_strings(
+                               &iter->u.ctf_expression.right);
+                       if (!name) {
+                               _PERROR("%s", "unexpected unary expression for event declaration's \"name\" attribute");
+                               goto error;
+                       }
+               }
+
+               g_free(left);
+               left = NULL;
+
+               if (name) {
+                       break;
+               }
+       }
+
+       return name;
+
+error:
+       g_free(left);
+
+       return NULL;
+}
+
+static
+int reset_event_decl_types(struct ctx *ctx,
+       struct bt_ctf_event_class *event_class)
+{
+       int ret = 0;
+       _BT_CTF_FIELD_TYPE_INIT(decl);
+
+       /* Event context */
+       decl = bt_ctf_field_type_structure_create();
+       if (!decl) {
+               _PERROR("%s", "cannot create initial, empty event context structure");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       ret = bt_ctf_event_class_set_context_type(event_class, decl);
+       BT_PUT(decl);
+       if (ret) {
+               _PERROR("%s", "cannot set initial, empty event context structure");
+               goto error;
+       }
+
+       /* Event payload */
+       decl = bt_ctf_field_type_structure_create();
+       if (!decl) {
+               _PERROR("%s", "cannot create initial, empty event payload structure");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       ret = bt_ctf_event_class_set_payload_type(event_class, decl);
+       BT_PUT(decl);
+       if (ret) {
+               _PERROR("%s", "cannot set initial, empty event payload structure");
+               goto error;
+       }
+
+       return 0;
+
+error:
+       BT_PUT(decl);
+
+       return ret;
+}
+
+static
+int reset_stream_decl_types(struct ctx *ctx,
+       struct bt_ctf_stream_class *stream_class)
+{
+       int ret = 0;
+       _BT_CTF_FIELD_TYPE_INIT(decl);
+
+       /* Packet context */
+       decl = bt_ctf_field_type_structure_create();
+       if (!decl) {
+               _PERROR("%s", "cannot create initial, empty packet context structure");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       ret = bt_ctf_stream_class_set_packet_context_type(stream_class, decl);
+       BT_PUT(decl);
+       if (ret) {
+               _PERROR("%s", "cannot set initial, empty packet context structure");
+               goto error;
+       }
+
+       /* Event header */
+       decl = bt_ctf_field_type_structure_create();
+       if (!decl) {
+               _PERROR("%s", "cannot create initial, empty event header structure");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       ret = bt_ctf_stream_class_set_event_header_type(stream_class, decl);
+       BT_PUT(decl);
+       if (ret) {
+               _PERROR("%s", "cannot set initial, empty event header structure");
+               goto error;
+       }
+
+       /* Event context */
+       decl = bt_ctf_field_type_structure_create();
+       if (!decl) {
+               _PERROR("%s", "cannot create initial, empty stream event context structure");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       ret = bt_ctf_stream_class_set_event_context_type(stream_class, decl);
+       BT_PUT(decl);
+       if (ret) {
+               _PERROR("%s", "cannot set initial, empty stream event context structure");
+               goto error;
+       }
+
+       return 0;
+
+error:
+       BT_PUT(decl);
+
+       return ret;
+}
+
+static
+struct bt_ctf_stream_class *create_reset_stream_class(struct ctx *ctx)
+{
+       int ret;
+       struct bt_ctf_stream_class *stream_class;
+
+       stream_class = bt_ctf_stream_class_create(NULL);
+       if (!stream_class) {
+               _PERROR("%s", "cannot create stream class");
+               goto error;
+       }
+
+       /*
+        * Set packet context, event header, and event context to empty
+        * structures to override the default ones.
+        */
+       ret = reset_stream_decl_types(ctx, stream_class);
+       if (ret) {
+               goto error;
+       }
+
+       return stream_class;
+
+error:
+       BT_PUT(stream_class);
+
+       return NULL;
+}
+
+static
+int visit_event_decl(struct ctx *ctx, struct ctf_node *node)
+{
+       int ret = 0;
+       int set = 0;
+       int64_t event_id;
+       struct ctf_node *iter;
+       int64_t stream_id = -1;
+       char *event_name = NULL;
+       struct bt_ctf_event_class *event_class = NULL;
+       struct bt_ctf_event_class *eevent_class;
+       struct bt_ctf_stream_class *stream_class;
+       struct bt_list_head *decl_list = &node->u.event.declaration_list;
+
+       if (node->visited) {
+               goto end;
+       }
+
+       node->visited = TRUE;
+       event_name = get_event_decl_name(ctx, node);
+       if (!event_name) {
+               _PERROR("%s",
+                       "missing \"name\" attribute in event declaration");
+               ret = -EPERM;
+               goto error;
+       }
+
+       event_class = bt_ctf_event_class_create(event_name);
+
+       /*
+        * Set context and fields to empty structures to override the
+        * default ones.
+        */
+       ret = reset_event_decl_types(ctx, event_class);
+       if (ret) {
+               goto error;
+       }
+
+
+       ret = ctx_push_scope(ctx);
+       if (ret) {
+               _PERROR("%s", "cannot push scope");
+               goto error;
+       }
+
+       bt_list_for_each_entry(iter, decl_list, siblings) {
+               ret = visit_event_decl_entry(ctx, iter, event_class,
+                       &stream_id, &set);
+               if (ret) {
+                       goto error;
+               }
+       }
+
+       if (!_IS_SET(&set, _EVENT_STREAM_ID_SET)) {
+               GList *keys = NULL;
+               struct bt_ctf_stream_class *new_stream_class;
+
+               /* Allow missing stream_id if there is only a single stream */
+               switch (g_hash_table_size(ctx->stream_classes)) {
+               case 0:
+                       /* Create stream if there's none */
+                       new_stream_class = create_reset_stream_class(ctx);
+                       if (!new_stream_class) {
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       ret = bt_ctf_stream_class_set_id(new_stream_class, 0);
+                       if (ret) {
+                               _PERROR("%s", "cannot set stream's ID");
+                               BT_PUT(new_stream_class);
+                               goto error;
+                       }
+
+                       stream_id = 0;
+
+                       /* Move reference to visitor's context */
+                       g_hash_table_insert(ctx->stream_classes,
+                               (gpointer) stream_id, new_stream_class);
+                       new_stream_class = NULL;
+
+                       break;
+               case 1:
+                       /* Single stream: get its ID */
+                       keys = g_hash_table_get_keys(ctx->stream_classes);
+                       stream_id = (int64_t) keys->data;
+                       g_list_free(keys);
+                       keys = NULL;
+                       break;
+               default:
+                       _PERROR("%s", "missing \"stream_id\" attribute in event declaration");
+                       ret = -EPERM;
+                       goto error;
+               }
+       }
+
+
+
+       assert(stream_id >= 0);
+
+       /* We have the stream ID now; borrow the stream class if found */
+       stream_class = g_hash_table_lookup(ctx->stream_classes,
+               (gpointer) stream_id);
+       if (!stream_class) {
+               _PERROR("cannot find stream class with ID %" PRId64,
+                       stream_id);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       if (!_IS_SET(&set, _EVENT_ID_SET)) {
+               /* Allow only one event without ID per stream */
+               if (bt_ctf_stream_class_get_event_class_count(stream_class) !=
+                               0) {
+                       _PERROR("%s",
+                               "missing \"id\" field in event declaration");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               /* Automatic ID */
+               ret = bt_ctf_event_class_set_id(event_class, 0);
+               if (ret) {
+                       _PERROR("%s", "cannot set event's ID");
+                       goto error;
+               }
+       }
+
+       event_id = bt_ctf_event_class_get_id(event_class);
+       if (event_id < 0) {
+               _PERROR("%s", "cannot get event's ID");
+               ret = -EINVAL;
+               goto error;
+       }
+
+       eevent_class = bt_ctf_stream_class_get_event_class_by_id(stream_class,
+               event_id);
+       if (eevent_class) {
+               BT_PUT(eevent_class);
+               _PERROR("%s", "duplicate event with ID %" PRId64 " in same stream");
+               ret = -EEXIST;
+               goto error;
+       }
+
+       eevent_class = bt_ctf_stream_class_get_event_class_by_name(stream_class,
+               event_name);
+       if (eevent_class) {
+               BT_PUT(eevent_class);
+               eevent_class = NULL;
+               _PERROR("%s",
+                       "duplicate event with name \"%s\" in same stream");
+               ret = -EEXIST;
+               goto error;
+       }
+
+       g_free(event_name);
+       ret = bt_ctf_stream_class_add_event_class(stream_class, event_class);
+       BT_PUT(event_class);
+       event_class = NULL;
+
+       if (ret) {
+               _PERROR("%s", "cannot add event class to stream class");
+               goto error;
+       }
+
+end:
+       return 0;
+
+error:
+       g_free(event_name);
+       BT_PUT(event_class);
+
+       /* stream_class is borrowed; it still belongs to the hash table */
+
+       return ret;
+}
+
+static
+int visit_stream_decl_entry(struct ctx *ctx, struct ctf_node *node,
+       struct bt_ctf_stream_class *stream_class, int *set)
+{
+       int ret = 0;
+       char *left = NULL;
+       _BT_CTF_FIELD_TYPE_INIT(decl);
+
+       switch (node->type) {
+       case NODE_TYPEDEF:
+               ret = visit_typedef(ctx, node->u._typedef.type_specifier_list,
+                       &node->u._typedef.type_declarators);
+               if (ret) {
+                       _PERROR("%s",
+                               "cannot add typedef in \"stream\" declaration");
+                       goto error;
+               }
+               break;
+       case NODE_TYPEALIAS:
+               ret = visit_typealias(ctx, node->u.typealias.target,
+                       node->u.typealias.alias);
+               if (ret) {
+                       _PERROR("%s", "cannot add typealias in \"stream\" declaration");
+                       goto error;
+               }
+               break;
+       case NODE_CTF_EXPRESSION:
+       {
+               left = concatenate_unary_strings(&node->u.ctf_expression.left);
+               if (!left) {
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (!strcmp(left, "id")) {
+                       int64_t id;
+                       gpointer ptr;
+
+                       if (_IS_SET(set, _STREAM_ID_SET)) {
+                               _PERROR_DUP_ATTR("id", "stream declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = get_unary_unsigned(&node->u.ctf_expression.right,
+                               (uint64_t *) &id);
+                       if (ret || id < 0) {
+                               _PERROR("%s", "unexpected unary expression for stream declaration's \"id\" attribute");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       ptr = g_hash_table_lookup(ctx->stream_classes,
+                               (gpointer) id);
+                       if (ptr) {
+                               _PERROR("duplicate stream with ID %" PRId64,
+                                       id);
+                               ret = -EEXIST;
+                               goto error;
+                       }
+
+                       ret = bt_ctf_stream_class_set_id(stream_class, id);
+                       if (ret) {
+                               _PERROR("%s",
+                                       "cannot set stream declaration's ID");
+                               goto error;
+                       }
+
+                       _SET(set, _STREAM_ID_SET);
+               } else if (!strcmp(left, "event.header")) {
+                       if (_IS_SET(set, _STREAM_EVENT_HEADER_SET)) {
+                               _PERROR("%s", "duplicate \"event.header\" entry in stream declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = visit_type_specifier_list(ctx,
+                               _BT_LIST_FIRST_ENTRY(
+                                       &node->u.ctf_expression.right,
+                                       struct ctf_node, siblings),
+                               &decl);
+                       if (ret) {
+                               _PERROR("%s", "cannot create event header declaration");
+                               goto error;
+                       }
+
+                       assert(decl);
+
+                       ret = bt_ctf_stream_class_set_event_header_type(
+                               stream_class, decl);
+                       BT_PUT(decl);
+                       if (ret) {
+                               _PERROR("%s", "cannot set stream's event header declaration");
+                               goto error;
+                       }
+
+                       _SET(set, _STREAM_EVENT_HEADER_SET);
+               } else if (!strcmp(left, "event.context")) {
+                       if (_IS_SET(set, _STREAM_EVENT_CONTEXT_SET)) {
+                               _PERROR("%s", "duplicate \"event.context\" entry in stream declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = visit_type_specifier_list(ctx,
+                               _BT_LIST_FIRST_ENTRY(
+                                       &node->u.ctf_expression.right,
+                                       struct ctf_node, siblings),
+                               &decl);
+                       if (ret) {
+                               _PERROR("%s", "cannot create stream event context declaration");
+                               goto error;
+                       }
+
+                       assert(decl);
+
+                       ret = bt_ctf_stream_class_set_event_context_type(
+                               stream_class, decl);
+                       BT_PUT(decl);
+                       if (ret) {
+                               _PERROR("%s", "cannot set stream's event context declaration");
+                               goto error;
+                       }
+
+                       _SET(set, _STREAM_EVENT_CONTEXT_SET);
+               } else if (!strcmp(left, "packet.context")) {
+                       if (_IS_SET(set, _STREAM_PACKET_CONTEXT_SET)) {
+                               _PERROR("%s", "duplicate \"packet.context\" entry in stream declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = visit_type_specifier_list(ctx,
+                               _BT_LIST_FIRST_ENTRY(
+                                       &node->u.ctf_expression.right,
+                                       struct ctf_node, siblings),
+                               &decl);
+                       if (ret) {
+                               _PERROR("%s", "cannot create packet context declaration");
+                               goto error;
+                       }
+
+                       assert(decl);
+
+                       ret = bt_ctf_stream_class_set_packet_context_type(
+                               stream_class, decl);
+                       BT_PUT(decl);
+                       if (ret) {
+                               _PERROR("%s", "cannot set stream's packet context declaration");
+                               goto error;
+                       }
+
+                       _SET(set, _STREAM_PACKET_CONTEXT_SET);
+               } else {
+                       _PWARNING("unknown attribute \"%s\" in stream declaration",
+                               left);
+               }
+
+               g_free(left);
+               left = NULL;
+               break;
+       }
+
+       default:
+               ret = -EPERM;
+               goto error;
+       }
+
+       return 0;
+
+error:
+       g_free(left);
+       BT_PUT(decl);
+
+       return ret;
+}
+
+static
+int visit_stream_decl(struct ctx *ctx, struct ctf_node *node)
+{
+       int64_t id;
+       int set = 0;
+       int ret = 0;
+       struct ctf_node *iter;
+       struct bt_ctf_stream_class *stream_class = NULL;
+       struct bt_list_head *decl_list = &node->u.stream.declaration_list;
+
+       if (node->visited) {
+               goto end;
+       }
+
+       node->visited = TRUE;
+       stream_class = create_reset_stream_class(ctx);
+       if (!stream_class) {
+               ret = -EINVAL;
+               goto error;
+       }
+
+       ret = ctx_push_scope(ctx);
+       if (ret) {
+               _PERROR("%s", "cannot push scope");
+               goto error;
+       }
+
+       bt_list_for_each_entry(iter, decl_list, siblings) {
+               ret = visit_stream_decl_entry(ctx, iter, stream_class, &set);
+               if (ret) {
+                       ctx_pop_scope(ctx);
+                       goto error;
+               }
+       }
+
+       ctx_pop_scope(ctx);
+
+       if (_IS_SET(&set, _STREAM_ID_SET)) {
+               /* Check that packet header has stream_id field */
+               _BT_CTF_FIELD_TYPE_INIT(stream_id_decl);
+               _BT_CTF_FIELD_TYPE_INIT(packet_header_decl);
+
+               packet_header_decl =
+                       bt_ctf_trace_get_packet_header_type(ctx->trace);
+               if (!packet_header_decl) {
+                       _PERROR("%s",
+                               "cannot get trace packet header declaration");
+                       goto error;
+               }
+
+               stream_id_decl =
+                       bt_ctf_field_type_structure_get_field_type_by_name(
+                               packet_header_decl, "stream_id");
+               BT_PUT(packet_header_decl);
+               if (!stream_id_decl) {
+                       _PERROR("%s", "missing \"stream_id\" field in packet header declaration, but \"id\" attribute is declared for stream");
+                       goto error;
+               }
+
+               if (!bt_ctf_field_type_is_integer(stream_id_decl)) {
+                       BT_PUT(stream_id_decl);
+                       _PERROR("%s", "\"stream_id\" field in packet header declaration is not an integer");
+                       goto error;
+               }
+
+               BT_PUT(stream_id_decl);
+       } else {
+               /* Allow only _one_ ID-less stream */
+               if (g_hash_table_size(ctx->stream_classes) != 0) {
+                       _PERROR("%s",
+                               "missing \"id\" field in stream declaration");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               /* Automatic ID: 0 */
+               ret = bt_ctf_stream_class_set_id(stream_class, 0);
+       }
+
+       id = bt_ctf_stream_class_get_id(stream_class);
+       if (id < 0) {
+               _PERROR("wrong stream ID: %" PRId64, id);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       /* Move reference to visitor's context */
+       g_hash_table_insert(ctx->stream_classes, (gpointer) (int64_t) id,
+               stream_class);
+       stream_class = NULL;
+
+end:
+       return 0;
+
+error:
+       BT_PUT(stream_class);
+
+       return ret;
+}
+
+static
+int visit_trace_decl_entry(struct ctx *ctx, struct ctf_node *node, int *set)
+{
+       int ret = 0;
+       char *left = NULL;
+       _BT_CTF_FIELD_TYPE_INIT(packet_header_decl);
+
+       switch (node->type) {
+       case NODE_TYPEDEF:
+               ret = visit_typedef(ctx, node->u._typedef.type_specifier_list,
+                       &node->u._typedef.type_declarators);
+               if (ret) {
+                       _PERROR("%s",
+                               "cannot add typedef in \"trace\" declaration");
+                       goto error;
+               }
+               break;
+       case NODE_TYPEALIAS:
+               ret = visit_typealias(ctx, node->u.typealias.target,
+                       node->u.typealias.alias);
+               if (ret) {
+                       _PERROR("%s",
+                               "cannot add typealias in \"trace\" declaration");
+                       goto error;
+               }
+               break;
+       case NODE_CTF_EXPRESSION:
+       {
+               left = concatenate_unary_strings(&node->u.ctf_expression.left);
+               if (!left) {
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (!strcmp(left, "major")) {
+                       if (_IS_SET(set, _TRACE_MAJOR_SET)) {
+                               _PERROR_DUP_ATTR("major", "trace declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = get_unary_unsigned(&node->u.ctf_expression.right,
+                               &ctx->trace_major);
+                       if (ret) {
+                               _PERROR("%s", "unexpected unary expression for trace's \"major\" attribute");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(set, _TRACE_MAJOR_SET);
+               } else if (!strcmp(left, "minor")) {
+                       if (_IS_SET(set, _TRACE_MINOR_SET)) {
+                               _PERROR_DUP_ATTR("minor", "trace declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = get_unary_unsigned(&node->u.ctf_expression.right,
+                               &ctx->trace_minor);
+                       if (ret) {
+                               _PERROR("%s", "unexpected unary expression for trace's \"minor\" attribute");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(set, _TRACE_MINOR_SET);
+               } else if (!strcmp(left, "uuid")) {
+                       if (_IS_SET(set, _TRACE_UUID_SET)) {
+                               _PERROR_DUP_ATTR("uuid", "trace declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = get_unary_uuid(&node->u.ctf_expression.right,
+                               ctx->trace_uuid);
+                       if (ret) {
+                               _PERROR("%s",
+                                       "invalid trace declaration's UUID");
+                               goto error;
+                       }
+
+                       _SET(set, _TRACE_UUID_SET);
+               } else if (!strcmp(left, "byte_order")) {
+                       /* Native byte order is already known at this stage */
+                       if (_IS_SET(set, _TRACE_BYTE_ORDER_SET)) {
+                               _PERROR_DUP_ATTR("byte_order",
+                                       "trace declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       _SET(set, _TRACE_BYTE_ORDER_SET);
+               } else if (!strcmp(left, "packet.header")) {
+                       if (_IS_SET(set, _TRACE_PACKET_HEADER_SET)) {
+                               _PERROR("%s", "duplicate \"packet.header\" entry in trace declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = visit_type_specifier_list(ctx,
+                               _BT_LIST_FIRST_ENTRY(
+                                       &node->u.ctf_expression.right,
+                                       struct ctf_node, siblings),
+                               &packet_header_decl);
+                       if (ret) {
+                               _PERROR("%s", "cannot create packet header declaration");
+                               goto error;
+                       }
+
+                       assert(packet_header_decl);
+                       ret = bt_ctf_trace_set_packet_header_type(ctx->trace,
+                               packet_header_decl);
+                       BT_PUT(packet_header_decl);
+                       if (ret) {
+                               _PERROR("%s", "cannot set trace declaration's packet header declaration");
+                               goto error;
+                       }
+
+                       _SET(set, _TRACE_PACKET_HEADER_SET);
+               } else {
+                       _PWARNING("%s", "unknown attribute \"%s\" in trace declaration");
+               }
+
+               g_free(left);
+               left = NULL;
+               break;
+       }
+       default:
+               _PERROR("%s", "unknown expression in trace declaration");
+               ret = -EINVAL;
+               goto error;
+       }
+
+       return 0;
+
+error:
+       g_free(left);
+       BT_PUT(packet_header_decl);
+
+       return ret;
+}
+
+static
+int visit_trace_decl(struct ctx *ctx, struct ctf_node *node)
+{
+       int ret = 0;
+       int set = 0;
+       struct ctf_node *iter;
+       struct bt_list_head *decl_list = &node->u.trace.declaration_list;
+
+       if (node->visited) {
+               goto end;
+       }
+
+       node->visited = TRUE;
+
+       if (ctx->is_trace_visited) {
+               _PERROR("%s", "duplicate \"trace\" block");
+               ret = -EEXIST;
+               goto error;
+       }
+
+       ret = ctx_push_scope(ctx);
+       if (ret) {
+               _PERROR("%s", "cannot push scope");
+               goto error;
+       }
+
+       bt_list_for_each_entry(iter, decl_list, siblings) {
+               ret = visit_trace_decl_entry(ctx, iter, &set);
+               if (ret) {
+                       ctx_pop_scope(ctx);
+                       goto error;
+               }
+       }
+
+       ctx_pop_scope(ctx);
+
+       if (!_IS_SET(&set, _TRACE_MAJOR_SET)) {
+               _PERROR("%s",
+                       "missing \"major\" attribute in trace declaration");
+               ret = -EPERM;
+               goto error;
+       }
+
+       if (!_IS_SET(&set, _TRACE_MINOR_SET)) {
+               _PERROR("%s",
+                       "missing \"minor\" attribute in trace declaration");
+               ret = -EPERM;
+               goto error;
+       }
+
+       if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
+               _PERROR("%s", "missing \"byte_order\" attribute in trace declaration");
+               ret = -EPERM;
+               goto error;
+       }
+
+       ctx->is_trace_visited = TRUE;
+
+end:
+       return 0;
+
+error:
+       return ret;
+}
+
+static
+int visit_env(struct ctx *ctx, struct ctf_node *node)
+{
+       int ret = 0;
+       char *left = NULL;
+       struct ctf_node *entry_node;
+       struct bt_list_head *decl_list = &node->u.env.declaration_list;
+
+       if (node->visited) {
+               goto end;
+       }
+
+       node->visited = TRUE;
+
+       bt_list_for_each_entry(entry_node, decl_list, siblings) {
+               struct bt_list_head *right_head =
+                       &entry_node->u.ctf_expression.right;
+
+               if (entry_node->type != NODE_CTF_EXPRESSION) {
+                       _PERROR("%s", "wrong expression in environment entry");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               left = concatenate_unary_strings(
+                       &entry_node->u.ctf_expression.left);
+               if (!left) {
+                       _PERROR("%s", "cannot get environment entry name");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (is_unary_string(right_head)) {
+                       char *right = concatenate_unary_strings(right_head);
+
+                       if (!right) {
+                               _PERROR("unexpected unary expression for environment entry's value (\"%s\")",
+                                       left);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       printf_verbose("env.%s = \"%s\"\n", left, right);
+                       ret = bt_ctf_trace_set_environment_field_string(
+                               ctx->trace, left, right);
+                       g_free(right);
+
+                       if (ret) {
+                               _PERROR("environment: cannot add entry \"%s\" to trace",
+                                       left);
+                               goto error;
+                       }
+               } else if (is_unary_unsigned(right_head) ||
+                               is_unary_signed(right_head)) {
+                       int64_t v;
+
+                       if (is_unary_unsigned(right_head)) {
+                               ret = get_unary_unsigned(right_head,
+                                       (uint64_t *) &v);
+                       } else {
+                               ret = get_unary_signed(right_head, &v);
+                       }
+                       if (ret) {
+                               _PERROR("unexpected unary expression for environment entry's value (\"%s\")",
+                                       left);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       printf_verbose("env.%s = %" PRId64 "\n", left, v);
+                       ret = bt_ctf_trace_set_environment_field_integer(
+                               ctx->trace, left, v);
+                       if (ret) {
+                               _PERROR("environment: cannot add entry \"%s\" to trace",
+                                       left);
+                               goto error;
+                       }
+               } else {
+                       printf_verbose("%s: environment entry \"%s\" has unknown type\n",
+                               __func__, left);
+               }
+
+               g_free(left);
+               left = NULL;
+       }
+
+end:
+       return 0;
+
+error:
+       g_free(left);
+
+       return ret;
+}
+
+static
+int set_trace_byte_order(struct ctx *ctx, struct ctf_node *trace_node)
+{
+       int ret = 0;
+       int set = 0;
+       char *left = NULL;
+       struct ctf_node *node;
+       struct bt_list_head *decl_list = &trace_node->u.trace.declaration_list;
+
+       bt_list_for_each_entry(node, decl_list, siblings) {
+               if (node->type == NODE_CTF_EXPRESSION) {
+                       struct ctf_node *right_node;
+
+                       left = concatenate_unary_strings(
+                               &node->u.ctf_expression.left);
+                       if (!left) {
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       if (!strcmp(left, "byte_order")) {
+                               enum bt_ctf_byte_order bo;
+
+                               if (_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
+                                       _PERROR_DUP_ATTR("byte_order",
+                                               "trace declaration");
+                                       ret = -EPERM;
+                                       goto error;
+                               }
+
+                               _SET(&set, _TRACE_BYTE_ORDER_SET);
+                               right_node = _BT_LIST_FIRST_ENTRY(
+                                       &node->u.ctf_expression.right,
+                                       struct ctf_node, siblings);
+                               bo = byte_order_from_unary_expr(ctx->efd,
+                                       right_node);
+                               if (bo == BT_CTF_BYTE_ORDER_UNKNOWN) {
+                                       _PERROR("%s", "unknown \"byte_order\" attribute in trace declaration");
+                                       ret = -EINVAL;
+                                       goto error;
+                               } else if (bo == BT_CTF_BYTE_ORDER_NATIVE) {
+                                       _PERROR("%s", "\"byte_order\" attribute cannot be set to \"native\" in trace declaration");
+                                       ret = -EPERM;
+                                       goto error;
+                               }
+
+                               ret = bt_ctf_trace_set_byte_order(
+                                       ctx->trace, bo);
+                               if (ret) {
+                                       _PERROR("cannot set trace's byte order (%d)",
+                                               ret);
+                                       goto error;
+                               }
+                       }
+
+                       g_free(left);
+                       left = NULL;
+               }
+       }
+
+       if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
+               _PERROR("%s", "missing \"byte_order\" attribute in trace declaration");
+               ret = -EINVAL;
+               goto error;
+       }
+
+       return 0;
+
+error:
+       g_free(left);
+
+       return ret;
+}
+
+static
+int visit_clock_decl_entry(struct ctx *ctx, struct ctf_node *entry_node,
+       struct bt_ctf_clock *clock, int *set)
+{
+       int ret = 0;
+       char *left = NULL;
+
+       if (entry_node->type != NODE_CTF_EXPRESSION) {
+               ret = -EPERM;
+               goto error;
+       }
+
+       left = concatenate_unary_strings(&entry_node->u.ctf_expression.left);
+       if (!left) {
+               ret = -EINVAL;
+               goto error;
+       }
+
+       if (!strcmp(left, "name")) {
+               char *right;
+
+               if (_IS_SET(set, _CLOCK_NAME_SET)) {
+                       _PERROR_DUP_ATTR("name", "clock declaration");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               right = concatenate_unary_strings(
+                       &entry_node->u.ctf_expression.right);
+               if (!right) {
+                       _PERROR("%s", "unexpected unary expression for clock declaration's \"name\" attribute");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               ret = bt_ctf_clock_set_name(clock, right);
+               if (ret) {
+                       _PERROR("%s", "cannot set clock's name");
+                       g_free(right);
+                       goto error;
+               }
+
+               g_free(right);
+               _SET(set, _CLOCK_NAME_SET);
+       } else if (!strcmp(left, "uuid")) {
+               unsigned char uuid[BABELTRACE_UUID_LEN];
+
+               if (_IS_SET(set, _CLOCK_UUID_SET)) {
+                       _PERROR_DUP_ATTR("uuid", "clock declaration");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               ret = get_unary_uuid(&entry_node->u.ctf_expression.right, uuid);
+               if (ret) {
+                       _PERROR("%s", "invalid clock UUID");
+                       goto error;
+               }
+
+               ret = bt_ctf_clock_set_uuid(clock, uuid);
+               if (ret) {
+                       _PERROR("%s", "cannot set clock's UUID");
+                       goto error;
+               }
+
+               _SET(set, _CLOCK_UUID_SET);
+       } else if (!strcmp(left, "description")) {
+               char *right;
+
+               if (_IS_SET(set, _CLOCK_DESCRIPTION_SET)) {
+                       _PERROR_DUP_ATTR("description", "clock declaration");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               right = concatenate_unary_strings(
+                       &entry_node->u.ctf_expression.right);
+               if (!right) {
+                       _PERROR("%s", "unexpected unary expression for clock's \"description\" attribute");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               ret = bt_ctf_clock_set_description(clock, right);
+               if (ret) {
+                       _PERROR("%s", "cannot set clock's description");
+                       g_free(right);
+                       goto error;
+               }
+
+               g_free(right);
+               _SET(set, _CLOCK_DESCRIPTION_SET);
+       } else if (!strcmp(left, "freq")) {
+               uint64_t freq;
+
+               if (_IS_SET(set, _CLOCK_FREQ_SET)) {
+                       _PERROR_DUP_ATTR("freq", "clock declaration");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               ret = get_unary_unsigned(
+                       &entry_node->u.ctf_expression.right, &freq);
+               if (ret) {
+                       _PERROR("%s", "unexpected unary expression for clock declaration's \"freq\" attribute");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               ret = bt_ctf_clock_set_frequency(clock, freq);
+               if (ret) {
+                       _PERROR("%s", "cannot set clock's frequency");
+                       goto error;
+               }
+
+               _SET(set, _CLOCK_FREQ_SET);
+       } else if (!strcmp(left, "precision")) {
+               uint64_t precision;
+
+               if (_IS_SET(set, _CLOCK_PRECISION_SET)) {
+                       _PERROR_DUP_ATTR("precision", "clock declaration");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               ret = get_unary_unsigned(
+                       &entry_node->u.ctf_expression.right, &precision);
+               if (ret) {
+                       _PERROR("%s", "unexpected unary expression for clock declaration's \"precision\" attribute");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               ret = bt_ctf_clock_set_precision(clock, precision);
+               if (ret) {
+                       _PERROR("%s", "cannot set clock's precision");
+                       goto error;
+               }
+
+               _SET(set, _CLOCK_PRECISION_SET);
+       } else if (!strcmp(left, "offset_s")) {
+               uint64_t offset_s;
+
+               if (_IS_SET(set, _CLOCK_OFFSET_S_SET)) {
+                       _PERROR_DUP_ATTR("offset_s", "clock declaration");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               ret = get_unary_unsigned(
+                       &entry_node->u.ctf_expression.right, &offset_s);
+               if (ret) {
+                       _PERROR("%s", "unexpected unary expression for clock declaration's \"offset_s\" attribute");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               ret = bt_ctf_clock_set_offset_s(clock, offset_s);
+               if (ret) {
+                       _PERROR("%s", "cannot set clock's offset in seconds");
+                       goto error;
+               }
+
+               _SET(set, _CLOCK_OFFSET_S_SET);
+       } else if (!strcmp(left, "offset")) {
+               uint64_t offset;
+
+               if (_IS_SET(set, _CLOCK_OFFSET_SET)) {
+                       _PERROR_DUP_ATTR("offset", "clock declaration");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               ret = get_unary_unsigned(
+                       &entry_node->u.ctf_expression.right, &offset);
+               if (ret) {
+                       _PERROR("%s", "unexpected unary expression for clock declaration's \"offset\" attribute");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               ret = bt_ctf_clock_set_offset(clock, offset);
+               if (ret) {
+                       _PERROR("%s", "cannot set clock's offset in cycles");
+                       goto error;
+               }
+
+               _SET(set, _CLOCK_OFFSET_SET);
+       } else if (!strcmp(left, "absolute")) {
+               struct ctf_node *right;
+
+               if (_IS_SET(set, _CLOCK_ABSOLUTE_SET)) {
+                       _PERROR_DUP_ATTR("absolute", "clock declaration");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               right = _BT_LIST_FIRST_ENTRY(
+                       &entry_node->u.ctf_expression.right,
+                       struct ctf_node, siblings);
+               ret = get_boolean(ctx->efd, right);
+               if (ret < 0) {
+                       _PERROR("%s", "unexpected unary expression for clock declaration's \"absolute\" attribute");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               ret = bt_ctf_clock_set_is_absolute(clock, ret);
+               if (ret) {
+                       _PERROR("%s", "cannot set clock's absolute option");
+                       goto error;
+               }
+
+               _SET(set, _CLOCK_ABSOLUTE_SET);
+       } else {
+               _PWARNING("unknown attribute \"%s\" in clock declaration",
+                       left);
+       }
+
+       g_free(left);
+       left = NULL;
+
+       return 0;
+
+error:
+       g_free(left);
+
+       return ret;
+}
+
+static
+int visit_clock_decl(struct ctx *ctx, struct ctf_node *clock_node)
+{
+       int ret = 0;
+       int set = 0;
+       struct bt_ctf_clock *clock;
+       struct ctf_node *entry_node;
+       struct bt_list_head *decl_list = &clock_node->u.clock.declaration_list;
+
+       if (clock_node->visited) {
+               return 0;
+       }
+
+       clock_node->visited = TRUE;
+       clock = bt_ctf_clock_create_empty();
+       if (!clock) {
+               _PERROR("%s", "cannot create clock");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       bt_list_for_each_entry(entry_node, decl_list, siblings) {
+               ret = visit_clock_decl_entry(ctx, entry_node, clock, &set);
+               if (ret) {
+                       goto error;
+               }
+       }
+
+       if (!_IS_SET(&set, _CLOCK_NAME_SET)) {
+               _PERROR("%s",
+                       "missing \"name\" attribute in clock declaration");
+               ret = -EPERM;
+               goto error;
+       }
+
+       if (bt_ctf_trace_get_clock_count(ctx->trace) != 0) {
+               _PERROR("%s", "only CTF traces with a single clock declaration are supported as of this version");
+               ret = -EINVAL;
+               goto error;
+       }
+
+       ret = bt_ctf_trace_add_clock(ctx->trace, clock);
+       if (ret) {
+               _PERROR("%s", "cannot add clock to trace");
+               goto error;
+       }
+
+error:
+       BT_PUT(clock);
+
+       return ret;
+}
+
+static
+int visit_root_decl(struct ctx *ctx, struct ctf_node *root_decl_node)
+{
+       int ret = 0;
+
+       if (root_decl_node->visited) {
+               goto end;
+       }
+
+       root_decl_node->visited = TRUE;
+
+       switch (root_decl_node->type) {
+       case NODE_TYPEDEF:
+               ret = visit_typedef(ctx,
+                       root_decl_node->u._typedef.type_specifier_list,
+                       &root_decl_node->u._typedef.type_declarators);
+               if (ret) {
+                       _PERROR("%s", "cannot add typedef in root scope");
+                       goto end;
+               }
+               break;
+       case NODE_TYPEALIAS:
+               ret = visit_typealias(ctx, root_decl_node->u.typealias.target,
+                       root_decl_node->u.typealias.alias);
+               if (ret) {
+                       _PERROR("%s", "cannot add typealias in root scope");
+                       goto end;
+               }
+               break;
+       case NODE_TYPE_SPECIFIER_LIST:
+       {
+               _BT_CTF_FIELD_TYPE_INIT(decl);
+
+               /*
+                * Just add the type specifier to the root
+                * declaration scope. Put local reference.
+                */
+               ret = visit_type_specifier_list(ctx, root_decl_node, &decl);
+               if (ret) {
+                       assert(!decl);
+                       goto end;
+               }
+
+               BT_PUT(decl);
+               break;
+       }
+       default:
+               ret = -EPERM;
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static
+int add_stream_classes_to_trace(struct ctx *ctx)
+{
+       int ret;
+       GHashTableIter iter;
+       gpointer key, stream_class;
+
+       g_hash_table_iter_init(&iter, ctx->stream_classes);
+
+       while (g_hash_table_iter_next(&iter, &key, &stream_class)) {
+               ret = bt_ctf_trace_add_stream_class(ctx->trace,
+                       stream_class);
+               if (ret) {
+                       int64_t id = bt_ctf_stream_class_get_id(stream_class);
+                       _PERROR("cannot add stream class %" PRId64 " to trace",
+                               id);
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+int ctf_visitor_generate_ir(FILE *efd, struct ctf_node *node,
+       struct bt_ctf_trace **trace)
+{
+       int ret = 0;
+       struct ctx *ctx = NULL;
+       _BT_CTF_FIELD_TYPE_INIT(packet_header_decl);
+
+       printf_verbose("CTF visitor: AST -> CTF IR...\n");
+
+       *trace = bt_ctf_trace_create();
+       if (!*trace) {
+               _FPERROR(efd, "%s", "cannot create trace");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       /* Set packet header to an empty struct tu override the default one */
+       packet_header_decl = bt_ctf_field_type_structure_create();
+
+       if (!packet_header_decl) {
+               _FPERROR(efd,
+                       "%s",
+                       "cannot create initial, empty packet header structure");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       ret = bt_ctf_trace_set_packet_header_type(*trace, packet_header_decl);
+       BT_PUT(packet_header_decl);
+       if (ret) {
+               _FPERROR(efd,
+                       "%s",
+                       "cannot set initial, empty packet header structure");
+               goto error;
+       }
+
+       ctx = ctx_create(*trace, efd);
+       if (!ctx) {
+               _FPERROR(efd, "%s", "cannot create visitor context");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       switch (node->type) {
+       case NODE_ROOT:
+       {
+               struct ctf_node *iter;
+               int got_trace_decl = FALSE;
+               int found_callsite = FALSE;
+
+               /*
+                * Find trace declaration's byte order first (for early
+                * type aliases).
+                */
+               bt_list_for_each_entry(iter, &node->u.root.trace, siblings) {
+                       if (got_trace_decl) {
+                               _PERROR("%s", "duplicate trace declaration");
+                               goto error;
+                       }
+
+                       ret = set_trace_byte_order(ctx, iter);
+                       if (ret) {
+                               _PERROR("cannot set trace's byte order (%d)",
+                                       ret);
+                               goto error;
+                       }
+
+                       got_trace_decl = TRUE;
+               }
+
+               if (!got_trace_decl) {
+                       _PERROR("no trace declaration found (%d)", ret);
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               /*
+                * Visit clocks first since any early integer can be mapped
+                * to one.
+                */
+               bt_list_for_each_entry(iter, &node->u.root.clock, siblings) {
+                       ret = visit_clock_decl(ctx, iter);
+                       if (ret) {
+                               _PERROR("error while visiting clock declaration (%d)",
+                                       ret);
+                               goto error;
+                       }
+               }
+
+               /*
+                * Visit root declarations next, as they can be used by any
+                * following entity.
+                */
+               bt_list_for_each_entry(iter, &node->u.root.declaration_list,
+                               siblings) {
+                       ret = visit_root_decl(ctx, iter);
+                       if (ret) {
+                               _PERROR("error while visiting root declaration (%d)",
+                                       ret);
+                               goto error;
+                       }
+               }
+
+               /* Callsite are not supported */
+               bt_list_for_each_entry(iter, &node->u.root.callsite, siblings) {
+                       found_callsite = TRUE;
+                       break;
+               }
+
+               if (found_callsite) {
+                       _PWARNING("%s", "\"callsite\" blocks are not supported as of this version");
+               }
+
+               /* Environment */
+               bt_list_for_each_entry(iter, &node->u.root.env, siblings) {
+                       ret = visit_env(ctx, iter);
+                       if (ret) {
+                               _PERROR("error while visiting environment block (%d)",
+                                       ret);
+                               goto error;
+                       }
+               }
+
+               /* Trace */
+               bt_list_for_each_entry(iter, &node->u.root.trace, siblings) {
+                       ret = visit_trace_decl(ctx, iter);
+                       if (ret) {
+                               _PERROR("%s", "error while visiting trace declaration");
+                               goto error;
+                       }
+               }
+
+               /* Streams */
+               bt_list_for_each_entry(iter, &node->u.root.stream, siblings) {
+                       ret = visit_stream_decl(ctx, iter);
+                       if (ret) {
+                               _PERROR("%s", "error while visiting stream declaration");
+                               goto error;
+                       }
+               }
+
+               /* Events */
+               bt_list_for_each_entry(iter, &node->u.root.event, siblings) {
+                       ret = visit_event_decl(ctx, iter);
+                       if (ret) {
+                               _PERROR("%s", "error while visiting event declaration");
+                               goto error;
+                       }
+               }
+               break;
+       }
+       case NODE_UNKNOWN:
+       default:
+               _PERROR("unknown node type: %d", (int) node->type);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       /* Add stream classes to trace now */
+       ret = add_stream_classes_to_trace(ctx);
+       if (ret) {
+               _PERROR("%s", "cannot add stream classes to trace");
+       }
+
+       ctx_destroy(ctx);
+       printf_verbose("done!\n");
+
+       return ret;
+
+error:
+       BT_PUT(packet_header_decl);
+       ctx_destroy(ctx);
+       BT_PUT(*trace);
+
+       return ret;
+}
diff --git a/ctf-reader-proto/metadata-parsing/ctf-visitor-parent-links.c b/ctf-reader-proto/metadata-parsing/ctf-visitor-parent-links.c
new file mode 100644 (file)
index 0000000..037496a
--- /dev/null
@@ -0,0 +1,461 @@
+/*
+ * ctf-visitor-parent-links.c
+ *
+ * Common Trace Format Metadata Parent Link Creator.
+ *
+ * Copyright 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <glib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <babeltrace/babeltrace-internal.h>
+#include <babeltrace/list.h>
+#include "ctf-scanner.h"
+#include "ctf-parser.h"
+#include "ctf-ast.h"
+
+#define fprintf_dbg(fd, fmt, args...)  fprintf(fd, "%s: " fmt, __func__, ## args)
+
+static
+int ctf_visitor_unary_expression(FILE *fd, int depth, struct ctf_node *node)
+{
+       int ret = 0;
+
+       switch (node->u.unary_expression.link) {
+       case UNARY_LINK_UNKNOWN:
+       case UNARY_DOTLINK:
+       case UNARY_ARROWLINK:
+       case UNARY_DOTDOTDOT:
+               break;
+       default:
+               fprintf(fd, "[error] %s: unknown expression link type %d\n", __func__,
+                       (int) node->u.unary_expression.link);
+               return -EINVAL;
+       }
+
+       switch (node->u.unary_expression.type) {
+       case UNARY_STRING:
+       case UNARY_SIGNED_CONSTANT:
+       case UNARY_UNSIGNED_CONSTANT:
+               break;
+       case UNARY_SBRAC:
+               node->u.unary_expression.u.sbrac_exp->parent = node;
+               ret = ctf_visitor_unary_expression(fd, depth + 1,
+                       node->u.unary_expression.u.sbrac_exp);
+               if (ret)
+                       return ret;
+               break;
+
+       case UNARY_UNKNOWN:
+       default:
+               fprintf(fd, "[error] %s: unknown expression type %d\n", __func__,
+                       (int) node->u.unary_expression.type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static
+int ctf_visitor_type_specifier(FILE *fd, int depth, struct ctf_node *node)
+{
+       int ret;
+
+       switch (node->u.type_specifier.type) {
+       case TYPESPEC_VOID:
+       case TYPESPEC_CHAR:
+       case TYPESPEC_SHORT:
+       case TYPESPEC_INT:
+       case TYPESPEC_LONG:
+       case TYPESPEC_FLOAT:
+       case TYPESPEC_DOUBLE:
+       case TYPESPEC_SIGNED:
+       case TYPESPEC_UNSIGNED:
+       case TYPESPEC_BOOL:
+       case TYPESPEC_COMPLEX:
+       case TYPESPEC_IMAGINARY:
+       case TYPESPEC_CONST:
+       case TYPESPEC_ID_TYPE:
+               break;
+       case TYPESPEC_FLOATING_POINT:
+       case TYPESPEC_INTEGER:
+       case TYPESPEC_STRING:
+       case TYPESPEC_STRUCT:
+       case TYPESPEC_VARIANT:
+       case TYPESPEC_ENUM:
+               node->u.type_specifier.node->parent = node;
+               ret = ctf_visitor_parent_links(fd, depth + 1, node->u.type_specifier.node);
+               if (ret)
+                       return ret;
+               break;
+
+       case TYPESPEC_UNKNOWN:
+       default:
+               fprintf(fd, "[error] %s: unknown type specifier %d\n", __func__,
+                       (int) node->u.type_specifier.type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static
+int ctf_visitor_type_declarator(FILE *fd, int depth, struct ctf_node *node)
+{
+       int ret = 0;
+       struct ctf_node *iter;
+
+       depth++;
+
+       bt_list_for_each_entry(iter, &node->u.type_declarator.pointers,
+                               siblings) {
+               iter->parent = node;
+               ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+               if (ret)
+                       return ret;
+       }
+
+       switch (node->u.type_declarator.type) {
+       case TYPEDEC_ID:
+               break;
+       case TYPEDEC_NESTED:
+               if (node->u.type_declarator.u.nested.type_declarator) {
+                       node->u.type_declarator.u.nested.type_declarator->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1,
+                               node->u.type_declarator.u.nested.type_declarator);
+                       if (ret)
+                               return ret;
+               }
+               if (!node->u.type_declarator.u.nested.abstract_array) {
+                       bt_list_for_each_entry(iter, &node->u.type_declarator.u.nested.length,
+                                               siblings) {
+                               iter->parent = node;
+                               ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                               if (ret)
+                                       return ret;
+                       }
+               }
+               if (node->u.type_declarator.bitfield_len) {
+                       node->u.type_declarator.bitfield_len = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1,
+                               node->u.type_declarator.bitfield_len);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case TYPEDEC_UNKNOWN:
+       default:
+               fprintf(fd, "[error] %s: unknown type declarator %d\n", __func__,
+                       (int) node->u.type_declarator.type);
+               return -EINVAL;
+       }
+       depth--;
+       return 0;
+}
+
+int ctf_visitor_parent_links(FILE *fd, int depth, struct ctf_node *node)
+{
+       int ret = 0;
+       struct ctf_node *iter;
+
+       if (node->visited)
+               return 0;
+
+       switch (node->type) {
+       case NODE_ROOT:
+               bt_list_for_each_entry(iter, &node->u.root.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.root.trace, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.root.stream, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.root.event, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.root.clock, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.root.callsite, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       case NODE_EVENT:
+               bt_list_for_each_entry(iter, &node->u.event.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_STREAM:
+               bt_list_for_each_entry(iter, &node->u.stream.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_ENV:
+               bt_list_for_each_entry(iter, &node->u.env.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_TRACE:
+               bt_list_for_each_entry(iter, &node->u.trace.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_CLOCK:
+               bt_list_for_each_entry(iter, &node->u.clock.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_CALLSITE:
+               bt_list_for_each_entry(iter, &node->u.callsite.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       case NODE_CTF_EXPRESSION:
+               depth++;
+               bt_list_for_each_entry(iter, &node->u.ctf_expression.left, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.ctf_expression.right, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               depth--;
+               break;
+       case NODE_UNARY_EXPRESSION:
+               return ctf_visitor_unary_expression(fd, depth, node);
+
+       case NODE_TYPEDEF:
+               depth++;
+               node->u._typedef.type_specifier_list->parent = node;
+               ret = ctf_visitor_parent_links(fd, depth + 1, node->u._typedef.type_specifier_list);
+               if (ret)
+                       return ret;
+               bt_list_for_each_entry(iter, &node->u._typedef.type_declarators, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               depth--;
+               break;
+       case NODE_TYPEALIAS_TARGET:
+               depth++;
+               node->u.typealias_target.type_specifier_list->parent = node;
+               ret = ctf_visitor_parent_links(fd, depth + 1, node->u.typealias_target.type_specifier_list);
+               if (ret)
+                       return ret;
+               bt_list_for_each_entry(iter, &node->u.typealias_target.type_declarators, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               depth--;
+               break;
+       case NODE_TYPEALIAS_ALIAS:
+               depth++;
+               node->u.typealias_alias.type_specifier_list->parent = node;
+               ret = ctf_visitor_parent_links(fd, depth + 1, node->u.typealias_alias.type_specifier_list);
+               if (ret)
+                       return ret;
+               bt_list_for_each_entry(iter, &node->u.typealias_alias.type_declarators, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               depth--;
+               break;
+       case NODE_TYPEALIAS:
+               node->u.typealias.target->parent = node;
+               ret = ctf_visitor_parent_links(fd, depth + 1, node->u.typealias.target);
+               if (ret)
+                       return ret;
+               node->u.typealias.alias->parent = node;
+               ret = ctf_visitor_parent_links(fd, depth + 1, node->u.typealias.alias);
+               if (ret)
+                       return ret;
+               break;
+
+       case NODE_TYPE_SPECIFIER_LIST:
+               bt_list_for_each_entry(iter, &node->u.type_specifier_list.head, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       case NODE_TYPE_SPECIFIER:
+               ret = ctf_visitor_type_specifier(fd, depth, node);
+               if (ret)
+                       return ret;
+               break;
+       case NODE_POINTER:
+               break;
+       case NODE_TYPE_DECLARATOR:
+               ret = ctf_visitor_type_declarator(fd, depth, node);
+               if (ret)
+                       return ret;
+               break;
+
+       case NODE_FLOATING_POINT:
+               bt_list_for_each_entry(iter, &node->u.floating_point.expressions, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_INTEGER:
+               bt_list_for_each_entry(iter, &node->u.integer.expressions, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_STRING:
+               bt_list_for_each_entry(iter, &node->u.string.expressions, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_ENUMERATOR:
+               bt_list_for_each_entry(iter, &node->u.enumerator.values, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_ENUM:
+               depth++;
+               if (node->u._enum.container_type) {
+                       ret = ctf_visitor_parent_links(fd, depth + 1, node->u._enum.container_type);
+                       if (ret)
+                               return ret;
+               }
+
+               bt_list_for_each_entry(iter, &node->u._enum.enumerator_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               depth--;
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               node->u.struct_or_variant_declaration.type_specifier_list->parent = node;
+               ret = ctf_visitor_parent_links(fd, depth + 1,
+                       node->u.struct_or_variant_declaration.type_specifier_list);
+               if (ret)
+                       return ret;
+               bt_list_for_each_entry(iter, &node->u.struct_or_variant_declaration.type_declarators, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_VARIANT:
+               bt_list_for_each_entry(iter, &node->u.variant.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_STRUCT:
+               bt_list_for_each_entry(iter, &node->u._struct.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u._struct.min_align,
+                                       siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       case NODE_UNKNOWN:
+       default:
+               fprintf(fd, "[error] %s: unknown node type %d\n", __func__,
+                       (int) node->type);
+               return -EINVAL;
+       }
+       return ret;
+}
diff --git a/ctf-reader-proto/metadata-parsing/ctf-visitor-semantic-validator.c b/ctf-reader-proto/metadata-parsing/ctf-visitor-semantic-validator.c
new file mode 100644 (file)
index 0000000..96776a9
--- /dev/null
@@ -0,0 +1,978 @@
+/*
+ * ctf-visitor-semantic-validator.c
+ *
+ * Common Trace Format Metadata Semantic Validator.
+ *
+ * Copyright 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <glib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <babeltrace/babeltrace-internal.h>
+#include <babeltrace/list.h>
+#include "ctf-scanner.h"
+#include "ctf-parser.h"
+#include "ctf-ast.h"
+
+#define _bt_list_first_entry(ptr, type, member)        \
+       bt_list_entry((ptr)->next, type, member)
+
+#define fprintf_dbg(fd, fmt, args...)  fprintf(fd, "%s: " fmt, __func__, ## args)
+
+static
+int _ctf_visitor_semantic_check(FILE *fd, int depth, struct ctf_node *node);
+
+static
+int ctf_visitor_unary_expression(FILE *fd, int depth, struct ctf_node *node)
+{
+       struct ctf_node *iter;
+       int is_ctf_exp = 0, is_ctf_exp_left = 0;
+
+       switch (node->parent->type) {
+       case NODE_CTF_EXPRESSION:
+               is_ctf_exp = 1;
+               bt_list_for_each_entry(iter, &node->parent->u.ctf_expression.left,
+                                       siblings) {
+                       if (iter == node) {
+                               is_ctf_exp_left = 1;
+                               /*
+                                * We are a left child of a ctf expression.
+                                * We are only allowed to be a string.
+                                */
+                               if (node->u.unary_expression.type != UNARY_STRING) {
+                                       fprintf(fd, "[error]: semantic error (left child of a ctf expression is only allowed to be a string)\n");
+
+                                       goto errperm;
+                               }
+                               break;
+                       }
+               }
+               /* Right child of a ctf expression can be any type of unary exp. */
+               break;                  /* OK */
+       case NODE_TYPE_DECLARATOR:
+               /*
+                * We are the length of a type declarator.
+                */
+               switch (node->u.unary_expression.type) {
+               case UNARY_UNSIGNED_CONSTANT:
+               case UNARY_STRING:
+                       break;
+               default:
+                       fprintf(fd, "[error]: semantic error (children of type declarator and enum can only be unsigned numeric constants or references to fields (a.b.c))\n");
+                       goto errperm;
+               }
+               break;                  /* OK */
+
+       case NODE_STRUCT:
+               /*
+                * We are the size of a struct align attribute.
+                */
+               switch (node->u.unary_expression.type) {
+               case UNARY_UNSIGNED_CONSTANT:
+                       break;
+               default:
+                       fprintf(fd, "[error]: semantic error (structure alignment attribute can only be unsigned numeric constants)\n");
+                       goto errperm;
+               }
+               break;
+
+       case NODE_ENUMERATOR:
+               /* The enumerator's parent has validated its validity already. */
+               break;                  /* OK */
+
+       case NODE_UNARY_EXPRESSION:
+               /*
+                * We disallow nested unary expressions and "sbrac" unary
+                * expressions.
+                */
+               fprintf(fd, "[error]: semantic error (nested unary expressions not allowed ( () and [] ))\n");
+               goto errperm;
+
+       case NODE_ROOT:
+       case NODE_EVENT:
+       case NODE_STREAM:
+       case NODE_ENV:
+       case NODE_TRACE:
+       case NODE_CLOCK:
+       case NODE_CALLSITE:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_TYPEALIAS:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_POINTER:
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_VARIANT:
+       default:
+               goto errinval;
+       }
+
+       switch (node->u.unary_expression.link) {
+       case UNARY_LINK_UNKNOWN:
+               /* We don't allow empty link except on the first node of the list */
+               if (is_ctf_exp && _bt_list_first_entry(is_ctf_exp_left ?
+                                         &node->parent->u.ctf_expression.left :
+                                         &node->parent->u.ctf_expression.right,
+                                         struct ctf_node,
+                                         siblings) != node) {
+                       fprintf(fd, "[error]: semantic error (empty link not allowed except on first node of unary expression (need to separate nodes with \".\" or \"->\")\n");
+                       goto errperm;
+               }
+               break;                  /* OK */
+       case UNARY_DOTLINK:
+       case UNARY_ARROWLINK:
+               /* We only allow -> and . links between children of ctf_expression. */
+               if (node->parent->type != NODE_CTF_EXPRESSION) {
+                       fprintf(fd, "[error]: semantic error (links \".\" and \"->\" are only allowed as children of ctf expression)\n");
+                       goto errperm;
+               }
+               /*
+                * Only strings can be separated linked by . or ->.
+                * This includes "", '' and non-quoted identifiers.
+                */
+               if (node->u.unary_expression.type != UNARY_STRING) {
+                       fprintf(fd, "[error]: semantic error (links \".\" and \"->\" are only allowed to separate strings and identifiers)\n");
+                       goto errperm;
+               }
+               /* We don't allow link on the first node of the list */
+               if (is_ctf_exp && _bt_list_first_entry(is_ctf_exp_left ?
+                                         &node->parent->u.ctf_expression.left :
+                                         &node->parent->u.ctf_expression.right,
+                                         struct ctf_node,
+                                         siblings) == node) {
+                       fprintf(fd, "[error]: semantic error (links \".\" and \"->\" are not allowed before first node of the unary expression list)\n");
+                       goto errperm;
+               }
+               break;
+       case UNARY_DOTDOTDOT:
+               /* We only allow ... link between children of enumerator. */
+               if (node->parent->type != NODE_ENUMERATOR) {
+                       fprintf(fd, "[error]: semantic error (link \"...\" is only allowed within enumerator)\n");
+                       goto errperm;
+               }
+               /* We don't allow link on the first node of the list */
+               if (_bt_list_first_entry(&node->parent->u.enumerator.values,
+                                         struct ctf_node,
+                                         siblings) == node) {
+                       fprintf(fd, "[error]: semantic error (link \"...\" is not allowed on the first node of the unary expression list)\n");
+                       goto errperm;
+               }
+               break;
+       default:
+               fprintf(fd, "[error] %s: unknown expression link type %d\n", __func__,
+                       (int) node->u.unary_expression.link);
+               return -EINVAL;
+       }
+       return 0;
+
+errinval:
+       fprintf(fd, "[error] %s: incoherent parent type %s for node type %s\n", __func__,
+               node_type(node->parent), node_type(node));
+       return -EINVAL;         /* Incoherent structure */
+
+errperm:
+       fprintf(fd, "[error] %s: semantic error (parent type %s for node type %s)\n", __func__,
+               node_type(node->parent), node_type(node));
+       return -EPERM;          /* Structure not allowed */
+}
+
+static
+int ctf_visitor_type_specifier_list(FILE *fd, int depth, struct ctf_node *node)
+{
+       switch (node->parent->type) {
+       case NODE_CTF_EXPRESSION:
+       case NODE_TYPE_DECLARATOR:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_ROOT:
+               break;                  /* OK */
+
+       case NODE_EVENT:
+       case NODE_STREAM:
+       case NODE_ENV:
+       case NODE_TRACE:
+       case NODE_CLOCK:
+       case NODE_CALLSITE:
+       case NODE_UNARY_EXPRESSION:
+       case NODE_TYPEALIAS:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_TYPE_SPECIFIER_LIST:
+       case NODE_POINTER:
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_ENUMERATOR:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+       default:
+               goto errinval;
+       }
+       return 0;
+errinval:
+       fprintf(fd, "[error] %s: incoherent parent type %s for node type %s\n", __func__,
+               node_type(node->parent), node_type(node));
+       return -EINVAL;         /* Incoherent structure */
+}
+
+static
+int ctf_visitor_type_specifier(FILE *fd, int depth, struct ctf_node *node)
+{
+       switch (node->parent->type) {
+       case NODE_TYPE_SPECIFIER_LIST:
+               break;                  /* OK */
+
+       case NODE_CTF_EXPRESSION:
+       case NODE_TYPE_DECLARATOR:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_ROOT:
+       case NODE_EVENT:
+       case NODE_STREAM:
+       case NODE_ENV:
+       case NODE_TRACE:
+       case NODE_CLOCK:
+       case NODE_CALLSITE:
+       case NODE_UNARY_EXPRESSION:
+       case NODE_TYPEALIAS:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_POINTER:
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_ENUMERATOR:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+       default:
+               goto errinval;
+       }
+       return 0;
+errinval:
+       fprintf(fd, "[error] %s: incoherent parent type %s for node type %s\n", __func__,
+               node_type(node->parent), node_type(node));
+       return -EINVAL;         /* Incoherent structure */
+}
+
+static
+int ctf_visitor_type_declarator(FILE *fd, int depth, struct ctf_node *node)
+{
+       int ret = 0;
+       struct ctf_node *iter;
+
+       depth++;
+
+       switch (node->parent->type) {
+       case NODE_TYPE_DECLARATOR:
+               /*
+                * A nested type declarator is not allowed to contain pointers.
+                */
+               if (!bt_list_empty(&node->u.type_declarator.pointers))
+                       goto errperm;
+               break;                  /* OK */
+       case NODE_TYPEALIAS_TARGET:
+               break;                  /* OK */
+       case NODE_TYPEALIAS_ALIAS:
+               /*
+                * Only accept alias name containing:
+                * - identifier
+                * - identifier *   (any number of pointers)
+                * NOT accepting alias names containing [] (would otherwise
+                * cause semantic clash for later declarations of
+                * arrays/sequences of elements, where elements could be
+                * arrays/sequences themselves (if allowed in typealias).
+                * NOT accepting alias with identifier. The declarator should
+                * be either empty or contain pointer(s).
+                */
+               if (node->u.type_declarator.type == TYPEDEC_NESTED)
+                       goto errperm;
+               bt_list_for_each_entry(iter, &node->parent->u.typealias_alias.type_specifier_list->u.type_specifier_list.head,
+                                       siblings) {
+                       switch (iter->u.type_specifier.type) {
+                       case TYPESPEC_FLOATING_POINT:
+                       case TYPESPEC_INTEGER:
+                       case TYPESPEC_STRING:
+                       case TYPESPEC_STRUCT:
+                       case TYPESPEC_VARIANT:
+                       case TYPESPEC_ENUM:
+                               if (bt_list_empty(&node->u.type_declarator.pointers))
+                                       goto errperm;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+               if (node->u.type_declarator.type == TYPEDEC_ID &&
+                   node->u.type_declarator.u.id != NULL)
+                       goto errperm;
+               break;                  /* OK */
+       case NODE_TYPEDEF:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               break;                  /* OK */
+
+       case NODE_ROOT:
+       case NODE_EVENT:
+       case NODE_STREAM:
+       case NODE_ENV:
+       case NODE_TRACE:
+       case NODE_CLOCK:
+       case NODE_CALLSITE:
+       case NODE_CTF_EXPRESSION:
+       case NODE_UNARY_EXPRESSION:
+       case NODE_TYPEALIAS:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_POINTER:
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_ENUMERATOR:
+       case NODE_ENUM:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+       default:
+               goto errinval;
+       }
+
+       bt_list_for_each_entry(iter, &node->u.type_declarator.pointers,
+                               siblings) {
+               ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+               if (ret)
+                       return ret;
+       }
+
+       switch (node->u.type_declarator.type) {
+       case TYPEDEC_ID:
+               break;
+       case TYPEDEC_NESTED:
+       {
+               if (node->u.type_declarator.u.nested.type_declarator) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1,
+                               node->u.type_declarator.u.nested.type_declarator);
+                       if (ret)
+                               return ret;
+               }
+               if (!node->u.type_declarator.u.nested.abstract_array) {
+                       bt_list_for_each_entry(iter, &node->u.type_declarator.u.nested.length,
+                                               siblings) {
+                               if (iter->type != NODE_UNARY_EXPRESSION) {
+                                       fprintf(fd, "[error] %s: expecting unary expression as length\n", __func__);
+                                       return -EINVAL;
+                               }
+                               ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                               if (ret)
+                                       return ret;
+                       }
+               } else {
+                       if (node->parent->type == NODE_TYPEALIAS_TARGET) {
+                               fprintf(fd, "[error] %s: abstract array declarator not permitted as target of typealias\n", __func__);
+                               return -EINVAL;
+                       }
+               }
+               if (node->u.type_declarator.bitfield_len) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1,
+                               node->u.type_declarator.bitfield_len);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       }
+       case TYPEDEC_UNKNOWN:
+       default:
+               fprintf(fd, "[error] %s: unknown type declarator %d\n", __func__,
+                       (int) node->u.type_declarator.type);
+               return -EINVAL;
+       }
+       depth--;
+       return 0;
+
+errinval:
+       fprintf(fd, "[error] %s: incoherent parent type %s for node type %s\n", __func__,
+               node_type(node->parent), node_type(node));
+       return -EINVAL;         /* Incoherent structure */
+
+errperm:
+       fprintf(fd, "[error] %s: semantic error (parent type %s for node type %s)\n", __func__,
+               node_type(node->parent), node_type(node));
+       return -EPERM;          /* Structure not allowed */
+}
+
+static
+int _ctf_visitor_semantic_check(FILE *fd, int depth, struct ctf_node *node)
+{
+       int ret = 0;
+       struct ctf_node *iter;
+
+       if (node->visited)
+               return 0;
+
+       switch (node->type) {
+       case NODE_ROOT:
+               bt_list_for_each_entry(iter, &node->u.root.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.root.trace, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.root.stream, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.root.event, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       case NODE_EVENT:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+
+               bt_list_for_each_entry(iter, &node->u.event.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_STREAM:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+
+               bt_list_for_each_entry(iter, &node->u.stream.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_ENV:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+
+               bt_list_for_each_entry(iter, &node->u.env.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_TRACE:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+
+               bt_list_for_each_entry(iter, &node->u.trace.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_CLOCK:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+
+               bt_list_for_each_entry(iter, &node->u.clock.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_CALLSITE:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+
+               bt_list_for_each_entry(iter, &node->u.callsite.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       case NODE_CTF_EXPRESSION:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+               case NODE_EVENT:
+               case NODE_STREAM:
+               case NODE_ENV:
+               case NODE_TRACE:
+               case NODE_CLOCK:
+               case NODE_CALLSITE:
+               case NODE_FLOATING_POINT:
+               case NODE_INTEGER:
+               case NODE_STRING:
+                       break;                  /* OK */
+
+               case NODE_CTF_EXPRESSION:
+               case NODE_UNARY_EXPRESSION:
+               case NODE_TYPEDEF:
+               case NODE_TYPEALIAS_TARGET:
+               case NODE_TYPEALIAS_ALIAS:
+               case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               case NODE_TYPEALIAS:
+               case NODE_TYPE_SPECIFIER:
+               case NODE_TYPE_SPECIFIER_LIST:
+               case NODE_POINTER:
+               case NODE_TYPE_DECLARATOR:
+               case NODE_ENUMERATOR:
+               case NODE_ENUM:
+               case NODE_VARIANT:
+               case NODE_STRUCT:
+               default:
+                       goto errinval;
+               }
+
+               depth++;
+               bt_list_for_each_entry(iter, &node->u.ctf_expression.left, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.ctf_expression.right, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               depth--;
+               break;
+       case NODE_UNARY_EXPRESSION:
+               return ctf_visitor_unary_expression(fd, depth, node);
+
+       case NODE_TYPEDEF:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+               case NODE_EVENT:
+               case NODE_STREAM:
+               case NODE_TRACE:
+               case NODE_VARIANT:
+               case NODE_STRUCT:
+                       break;                  /* OK */
+
+               case NODE_CTF_EXPRESSION:
+               case NODE_UNARY_EXPRESSION:
+               case NODE_TYPEDEF:
+               case NODE_TYPEALIAS_TARGET:
+               case NODE_TYPEALIAS_ALIAS:
+               case NODE_TYPEALIAS:
+               case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               case NODE_TYPE_SPECIFIER:
+               case NODE_TYPE_SPECIFIER_LIST:
+               case NODE_POINTER:
+               case NODE_TYPE_DECLARATOR:
+               case NODE_FLOATING_POINT:
+               case NODE_INTEGER:
+               case NODE_STRING:
+               case NODE_ENUMERATOR:
+               case NODE_ENUM:
+               case NODE_CLOCK:
+               case NODE_CALLSITE:
+               case NODE_ENV:
+               default:
+                       goto errinval;
+               }
+
+               depth++;
+               ret = _ctf_visitor_semantic_check(fd, depth + 1,
+                       node->u._typedef.type_specifier_list);
+               if (ret)
+                       return ret;
+               bt_list_for_each_entry(iter, &node->u._typedef.type_declarators, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               depth--;
+               break;
+       case NODE_TYPEALIAS_TARGET:
+       {
+               int nr_declarators;
+
+               switch (node->parent->type) {
+               case NODE_TYPEALIAS:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+
+               depth++;
+               ret = _ctf_visitor_semantic_check(fd, depth + 1,
+                       node->u.typealias_target.type_specifier_list);
+               if (ret)
+                       return ret;
+               nr_declarators = 0;
+               bt_list_for_each_entry(iter, &node->u.typealias_target.type_declarators, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+                       nr_declarators++;
+               }
+               if (nr_declarators > 1) {
+                       fprintf(fd, "[error] %s: Too many declarators in typealias alias (%d, max is 1)\n", __func__, nr_declarators);
+               
+                       return -EINVAL;
+               }
+               depth--;
+               break;
+       }
+       case NODE_TYPEALIAS_ALIAS:
+       {
+               int nr_declarators;
+
+               switch (node->parent->type) {
+               case NODE_TYPEALIAS:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+
+               depth++;
+               ret = _ctf_visitor_semantic_check(fd, depth + 1,
+                       node->u.typealias_alias.type_specifier_list);
+               if (ret)
+                       return ret;
+               nr_declarators = 0;
+               bt_list_for_each_entry(iter, &node->u.typealias_alias.type_declarators, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+                       nr_declarators++;
+               }
+               if (nr_declarators > 1) {
+                       fprintf(fd, "[error] %s: Too many declarators in typealias alias (%d, max is 1)\n", __func__, nr_declarators);
+               
+                       return -EINVAL;
+               }
+               depth--;
+               break;
+       }
+       case NODE_TYPEALIAS:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+               case NODE_EVENT:
+               case NODE_STREAM:
+               case NODE_TRACE:
+               case NODE_VARIANT:
+               case NODE_STRUCT:
+                       break;                  /* OK */
+
+               case NODE_CTF_EXPRESSION:
+               case NODE_UNARY_EXPRESSION:
+               case NODE_TYPEDEF:
+               case NODE_TYPEALIAS_TARGET:
+               case NODE_TYPEALIAS_ALIAS:
+               case NODE_TYPEALIAS:
+               case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               case NODE_TYPE_SPECIFIER:
+               case NODE_TYPE_SPECIFIER_LIST:
+               case NODE_POINTER:
+               case NODE_TYPE_DECLARATOR:
+               case NODE_FLOATING_POINT:
+               case NODE_INTEGER:
+               case NODE_STRING:
+               case NODE_ENUMERATOR:
+               case NODE_ENUM:
+               case NODE_CLOCK:
+               case NODE_CALLSITE:
+               case NODE_ENV:
+               default:
+                       goto errinval;
+               }
+
+               ret = _ctf_visitor_semantic_check(fd, depth + 1, node->u.typealias.target);
+               if (ret)
+                       return ret;
+               ret = _ctf_visitor_semantic_check(fd, depth + 1, node->u.typealias.alias);
+               if (ret)
+                       return ret;
+               break;
+
+       case NODE_TYPE_SPECIFIER_LIST:
+               ret = ctf_visitor_type_specifier_list(fd, depth, node);
+               if (ret)
+                       return ret;
+               break;
+       case NODE_TYPE_SPECIFIER:
+               ret = ctf_visitor_type_specifier(fd, depth, node);
+               if (ret)
+                       return ret;
+               break;
+       case NODE_POINTER:
+               switch (node->parent->type) {
+               case NODE_TYPE_DECLARATOR:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+               break;
+       case NODE_TYPE_DECLARATOR:
+               ret = ctf_visitor_type_declarator(fd, depth, node);
+               if (ret)
+                       return ret;
+               break;
+
+       case NODE_FLOATING_POINT:
+               switch (node->parent->type) {
+               case NODE_TYPE_SPECIFIER:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+
+               case NODE_UNARY_EXPRESSION:
+                       goto errperm;
+               }
+               bt_list_for_each_entry(iter, &node->u.floating_point.expressions, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_INTEGER:
+               switch (node->parent->type) {
+               case NODE_TYPE_SPECIFIER:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+
+               }
+
+               bt_list_for_each_entry(iter, &node->u.integer.expressions, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_STRING:
+               switch (node->parent->type) {
+               case NODE_TYPE_SPECIFIER:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+
+               case NODE_UNARY_EXPRESSION:
+                       goto errperm;
+               }
+
+               bt_list_for_each_entry(iter, &node->u.string.expressions, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_ENUMERATOR:
+               switch (node->parent->type) {
+               case NODE_ENUM:
+                       break;
+               default:
+                       goto errinval;
+               }
+               /*
+                * Enumerators are only allows to contain:
+                *    numeric unary expression
+                * or num. unary exp. ... num. unary exp
+                */
+               {
+                       int count = 0;
+
+                       bt_list_for_each_entry(iter, &node->u.enumerator.values,
+                                               siblings) {
+                               switch (count++) {
+                               case 0: if (iter->type != NODE_UNARY_EXPRESSION
+                                           || (iter->u.unary_expression.type != UNARY_SIGNED_CONSTANT
+                                               && iter->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT)
+                                           || iter->u.unary_expression.link != UNARY_LINK_UNKNOWN) {
+                                               fprintf(fd, "[error]: semantic error (first unary expression of enumerator is unexpected)\n");
+                                               goto errperm;
+                                       }
+                                       break;
+                               case 1: if (iter->type != NODE_UNARY_EXPRESSION
+                                           || (iter->u.unary_expression.type != UNARY_SIGNED_CONSTANT
+                                               && iter->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT)
+                                           || iter->u.unary_expression.link != UNARY_DOTDOTDOT) {
+                                               fprintf(fd, "[error]: semantic error (second unary expression of enumerator is unexpected)\n");
+                                               goto errperm;
+                                       }
+                                       break;
+                               default:
+                                       goto errperm;
+                               }
+                       }
+               }
+
+               bt_list_for_each_entry(iter, &node->u.enumerator.values, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_ENUM:
+               switch (node->parent->type) {
+               case NODE_TYPE_SPECIFIER:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+
+               case NODE_UNARY_EXPRESSION:
+                       goto errperm;
+               }
+
+               depth++;
+               ret = _ctf_visitor_semantic_check(fd, depth + 1, node->u._enum.container_type);
+               if (ret)
+                       return ret;
+
+               bt_list_for_each_entry(iter, &node->u._enum.enumerator_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               depth--;
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               switch (node->parent->type) {
+               case NODE_STRUCT:
+               case NODE_VARIANT:
+                       break;
+               default:
+                       goto errinval;
+               }
+               ret = _ctf_visitor_semantic_check(fd, depth + 1,
+                       node->u.struct_or_variant_declaration.type_specifier_list);
+               if (ret)
+                       return ret;
+               bt_list_for_each_entry(iter, &node->u.struct_or_variant_declaration.type_declarators, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_VARIANT:
+               switch (node->parent->type) {
+               case NODE_TYPE_SPECIFIER:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+
+               case NODE_UNARY_EXPRESSION:
+                       goto errperm;
+               }
+               bt_list_for_each_entry(iter, &node->u.variant.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       case NODE_STRUCT:
+               switch (node->parent->type) {
+               case NODE_TYPE_SPECIFIER:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+
+               case NODE_UNARY_EXPRESSION:
+                       goto errperm;
+               }
+               bt_list_for_each_entry(iter, &node->u._struct.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(fd, depth + 1, iter);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       case NODE_UNKNOWN:
+       default:
+               fprintf(fd, "[error] %s: unknown node type %d\n", __func__,
+                       (int) node->type);
+               return -EINVAL;
+       }
+       return ret;
+
+errinval:
+       fprintf(fd, "[error] %s: incoherent parent type %s for node type %s\n", __func__,
+               node_type(node->parent), node_type(node));
+       return -EINVAL;         /* Incoherent structure */
+
+errperm:
+       fprintf(fd, "[error] %s: semantic error (parent type %s for node type %s)\n", __func__,
+               node_type(node->parent), node_type(node));
+       return -EPERM;          /* Structure not allowed */
+}
+
+int ctf_visitor_semantic_check(FILE *fd, int depth, struct ctf_node *node)
+{
+       int ret = 0;
+
+       /*
+        * First make sure we create the parent links for all children. Let's
+        * take the safe route and recreate them at each validation, just in
+        * case the structure has changed.
+        */
+       printf_verbose("CTF visitor: parent links creation... ");
+       ret = ctf_visitor_parent_links(fd, depth, node);
+       if (ret)
+               return ret;
+       printf_verbose("done.\n");
+       printf_verbose("CTF visitor: semantic check... ");
+       ret = _ctf_visitor_semantic_check(fd, depth, node);
+       if (ret)
+               return ret;
+       printf_verbose("done.\n");
+       return ret;
+}
diff --git a/ctf-reader-proto/metadata-parsing/objstack.c b/ctf-reader-proto/metadata-parsing/objstack.c
new file mode 100644 (file)
index 0000000..8087722
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * objstack.c
+ *
+ * Common Trace Format Object Stack.
+ *
+ * Copyright 2013 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <babeltrace/list.h>
+#include <babeltrace/babeltrace-internal.h>
+#include <babeltrace/align.h>
+
+#define OBJSTACK_ALIGN                 8       /* Object stack alignment */
+#define OBJSTACK_INIT_LEN              128
+#define OBJSTACK_POISON                        0xcc
+
+struct objstack {
+       struct bt_list_head head;       /* list of struct objstack_node */
+};
+
+struct objstack_node {
+       struct bt_list_head node;
+       size_t len;
+       size_t used_len;
+       char __attribute__ ((aligned (OBJSTACK_ALIGN))) data[];
+};
+
+BT_HIDDEN
+struct objstack *objstack_create(void)
+{
+       struct objstack *objstack;
+       struct objstack_node *node;
+
+       objstack = calloc(1, sizeof(*objstack));
+       if (!objstack)
+               return NULL;
+       node = calloc(sizeof(struct objstack_node) + OBJSTACK_INIT_LEN,
+                       sizeof(char));
+       if (!node) {
+               free(objstack);
+               return NULL;
+       }
+       BT_INIT_LIST_HEAD(&objstack->head);
+       bt_list_add_tail(&node->node, &objstack->head);
+       node->len = OBJSTACK_INIT_LEN;
+       return objstack;
+}
+
+static
+void objstack_node_free(struct objstack_node *node)
+{
+       size_t offset, len;
+       char *p;
+
+       if (!node)
+               return;
+       p = (char *) node;
+       len = sizeof(*node) + node->len;
+       for (offset = 0; offset < len; offset++)
+               p[offset] = OBJSTACK_POISON;
+       free(node);
+}
+
+BT_HIDDEN
+void objstack_destroy(struct objstack *objstack)
+{
+       struct objstack_node *node, *p;
+
+       if (!objstack)
+               return;
+       bt_list_for_each_entry_safe(node, p, &objstack->head, node) {
+               bt_list_del(&node->node);
+               objstack_node_free(node);
+       }
+       free(objstack);
+}
+
+static
+struct objstack_node *objstack_append_node(struct objstack *objstack)
+{
+       struct objstack_node *last_node, *new_node;
+
+       /* Get last node */
+       last_node = bt_list_entry(objstack->head.prev,
+                       struct objstack_node, node);
+
+       /* Allocate new node with double of size of last node */
+       new_node = calloc(sizeof(struct objstack_node) + (last_node->len << 1),
+                       sizeof(char));
+       if (!new_node) {
+               return NULL;
+       }
+       bt_list_add_tail(&new_node->node, &objstack->head);
+       new_node->len = last_node->len << 1;
+       return new_node;
+}
+
+BT_HIDDEN
+void *objstack_alloc(struct objstack *objstack, size_t len)
+{
+       struct objstack_node *last_node;
+       void *p;
+
+       len = ALIGN(len, OBJSTACK_ALIGN);
+
+       /* Get last node */
+       last_node = bt_list_entry(objstack->head.prev,
+                       struct objstack_node, node);
+       while (last_node->len - last_node->used_len < len) {
+               last_node = objstack_append_node(objstack);
+               if (!last_node) {
+                       return NULL;
+               }
+       }
+       p = &last_node->data[last_node->used_len];
+       last_node->used_len += len;
+       return p;
+}
diff --git a/ctf-reader-proto/metadata-parsing/objstack.h b/ctf-reader-proto/metadata-parsing/objstack.h
new file mode 100644 (file)
index 0000000..c026eb5
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef _OBJSTACK_H
+#define _OBJSTACK_H
+
+/*
+ * objstack.h
+ *
+ * Common Trace Format Object Stack.
+ *
+ * Copyright 2013 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+struct objstack;
+
+BT_HIDDEN
+struct objstack *objstack_create(void);
+BT_HIDDEN
+void objstack_destroy(struct objstack *objstack);
+
+/*
+ * Allocate len bytes of zeroed memory.
+ * Return NULL on error.
+ */
+BT_HIDDEN
+void *objstack_alloc(struct objstack *objstack, size_t len);
+
+#endif /* _OBJSTACK_H */
diff --git a/ctf-reader-proto/print.h b/ctf-reader-proto/print.h
new file mode 100644 (file)
index 0000000..4c47f62
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef CTF_FS_PRINT_H
+#define CTF_FS_PRINT_H
+
+/*
+ * Define PRINT_PREFIX and PRINT_ERR_STREAM, then include this file.
+ *
+ * Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+
+#include "ctf-fs.h"
+
+#define PERR(fmt, ...)                                                 \
+       do {                                                            \
+               if (PRINT_ERR_STREAM) {                                 \
+                       fprintf(PRINT_ERR_STREAM,                       \
+                               "Error: " PRINT_PREFIX ": " fmt,        \
+                               ##__VA_ARGS__);                         \
+               }                                                       \
+       } while (0)
+
+#define PWARN(fmt, ...)                                                        \
+       do {                                                            \
+               if (PRINT_ERR_STREAM) {                                 \
+                       fprintf(PRINT_ERR_STREAM,                       \
+                               "Warning: " PRINT_PREFIX ": " fmt,      \
+                               ##__VA_ARGS__);                         \
+               }                                                       \
+       } while (0)
+
+#define PDBG(fmt, ...)                                                 \
+       do {                                                            \
+               if (ctf_fs_debug) {                                     \
+                       fprintf(stderr,                                 \
+                               "Debug: " PRINT_PREFIX ": " fmt,        \
+                               ##__VA_ARGS__);                         \
+               }                                                       \
+       } while (0)
+
+#endif /* CTF_FS_PRINT_H */
diff --git a/ctf-reader-proto/protorectoral.c b/ctf-reader-proto/protorectoral.c
new file mode 100644 (file)
index 0000000..2844ee8
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016 - Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ctf-fs.h"
+
+int main(int main, char *argv[])
+{
+       ctf_fs_test(argv[1]);
+
+       return 0;
+}
This page took 0.212247 seconds and 4 git commands to generate.