+ assert(begin);
+ assert(need_offsets);
+
+ if (!signature_is_valid(contents, false))
+ return -EINVAL;
+
+ if (c->signature && c->signature[c->index]) {
+ size_t l;
+
+ l = strlen(contents);
+
+ if (c->signature[c->index] != SD_BUS_TYPE_STRUCT_BEGIN ||
+ !startswith(c->signature + c->index + 1, contents) ||
+ c->signature[c->index + 1 + l] != SD_BUS_TYPE_STRUCT_END)
+ return -ENXIO;
+
+ nindex = c->index + 1 + l + 1;
+ } else {
+ char *e;
+
+ if (c->enclosing != 0)
+ return -ENXIO;
+
+ e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_STRUCT_BEGIN), contents, CHAR_TO_STR(SD_BUS_TYPE_STRUCT_END), NULL);
+ if (!e) {
+ m->poisoned = true;
+ return -ENOMEM;
+ }
+
+ nindex = e - c->signature;
+ }
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ int alignment;
+
+ alignment = bus_gvariant_get_alignment(contents);
+ if (alignment < 0)
+ return alignment;
+
+ if (!message_extend_body(m, alignment, 0, false))
+ return -ENOMEM;
+
+ r = bus_gvariant_is_fixed_size(contents);
+ if (r < 0)
+ return r;
+
+ *begin = m->header->body_size;
+ *need_offsets = r == 0;
+ } else {
+ /* Align contents to 8 byte boundary */
+ if (!message_extend_body(m, 8, 0, false))
+ return -ENOMEM;
+ }
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index = nindex;
+
+ return 0;
+}
+
+static int bus_message_open_dict_entry(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents,
+ size_t *begin,
+ bool *need_offsets) {
+
+ int r;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+ assert(begin);
+ assert(need_offsets);
+
+ if (!signature_is_pair(contents))
+ return -EINVAL;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ return -ENXIO;
+
+ if (c->signature && c->signature[c->index]) {
+ size_t l;
+
+ l = strlen(contents);
+
+ if (c->signature[c->index] != SD_BUS_TYPE_DICT_ENTRY_BEGIN ||
+ !startswith(c->signature + c->index + 1, contents) ||
+ c->signature[c->index + 1 + l] != SD_BUS_TYPE_DICT_ENTRY_END)
+ return -ENXIO;
+ } else
+ return -ENXIO;
+
+ if (BUS_MESSAGE_IS_GVARIANT(m)) {
+ int alignment;
+
+ alignment = bus_gvariant_get_alignment(contents);
+ if (alignment < 0)
+ return alignment;
+
+ if (!message_extend_body(m, alignment, 0, false))
+ return -ENOMEM;
+
+ r = bus_gvariant_is_fixed_size(contents);
+ if (r < 0)
+ return r;
+
+ *begin = m->header->body_size;
+ *need_offsets = r == 0;
+ } else {
+ /* Align contents to 8 byte boundary */
+ if (!message_extend_body(m, 8, 0, false))
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+_public_ int sd_bus_message_open_container(
+ sd_bus_message *m,
+ char type,
+ const char *contents) {
+
+ struct bus_container *c, *w;
+ uint32_t *array_size = NULL;
+ char *signature;
+ size_t before, begin;
+ bool need_offsets = false;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(contents, -EINVAL);
+ assert_return(!m->poisoned, -ESTALE);
+
+ /* Make sure we have space for one more container */
+ w = realloc(m->containers, sizeof(struct bus_container) * (m->n_containers + 1));
+ if (!w) {
+ m->poisoned = true;
+ return -ENOMEM;
+ }
+
+ m->containers = w;
+
+ c = message_get_container(m);
+
+ signature = strdup(contents);
+ if (!signature) {
+ m->poisoned = true;
+ return -ENOMEM;
+ }
+
+ /* Save old index in the parent container, in case we have to
+ * abort this container */
+ c->saved_index = c->index;
+ before = m->header->body_size;
+
+ if (type == SD_BUS_TYPE_ARRAY)
+ r = bus_message_open_array(m, c, contents, &array_size, &begin, &need_offsets);
+ else if (type == SD_BUS_TYPE_VARIANT)
+ r = bus_message_open_variant(m, c, contents);
+ else if (type == SD_BUS_TYPE_STRUCT)
+ r = bus_message_open_struct(m, c, contents, &begin, &need_offsets);
+ else if (type == SD_BUS_TYPE_DICT_ENTRY)
+ r = bus_message_open_dict_entry(m, c, contents, &begin, &need_offsets);
+ else
+ r = -EINVAL;
+
+ if (r < 0) {
+ free(signature);
+ return r;
+ }
+
+ /* OK, let's fill it in */
+ w += m->n_containers++;
+ w->enclosing = type;
+ w->signature = signature;
+ w->index = 0;
+ w->array_size = array_size;
+ w->before = before;
+ w->begin = begin;
+ w->n_offsets = w->n_offsets_allocated = 0;
+ w->offsets = NULL;
+ w->need_offsets = need_offsets;
+
+ return 0;
+}
+
+static size_t determine_word_size(size_t sz, size_t extra) {
+ if (sz <= 0 && extra == 0)
+ return 0;
+ else if (sz + extra <= 0xFF)
+ return 1;
+ else if (sz + extra*2 <= 0xFFFF)
+ return 2;
+ else if (sz + extra*4 <= 0xFFFFFFFF)
+ return 4;
+ else
+ return 8;
+}
+
+static size_t read_word_le(void *p, size_t sz) {
+ union {
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ } x;
+
+ assert(p);
+
+ if (sz == 1)
+ return *(uint8_t*) p;
+
+ memcpy(&x, p, sz);
+
+ if (sz == 2)
+ return le16toh(x.u16);
+ else if (sz == 4)
+ return le32toh(x.u32);
+ else if (sz == 4)
+ return le64toh(x.u64);
+
+ assert_not_reached("unknown word width");
+}
+
+static void write_word_le(void *p, size_t sz, size_t value) {
+ union {
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ } x;
+
+ assert(p);
+ assert(sz == 8 || (value < (1ULL << (sz*8))));
+
+ if (sz == 1) {
+ *(uint8_t*) p = value;
+ return;
+ } else if (sz == 2)
+ x.u16 = htole16((uint16_t) value);
+ else if (sz == 4)
+ x.u32 = htole32((uint32_t) value);
+ else if (sz == 8)
+ x.u64 = htole64((uint64_t) value);
+ else
+ assert_not_reached("unknown word width");
+
+ memcpy(p, &x, sz);
+}
+
+static int bus_message_close_array(sd_bus_message *m, struct bus_container *c) {
+
+ assert(m);
+ assert(c);
+
+ if (!BUS_MESSAGE_IS_GVARIANT(m))
+ return 0;
+
+ if (c->need_offsets) {
+ size_t payload, sz, i;
+ uint8_t *a;
+
+ /* Variable-width arrays */
+
+ payload = c->n_offsets > 0 ? c->offsets[c->n_offsets-1] - c->begin : 0;
+ sz = determine_word_size(payload, c->n_offsets);
+
+ a = message_extend_body(m, 1, sz * c->n_offsets, true);
+ if (!a)
+ return -ENOMEM;
+
+ for (i = 0; i < c->n_offsets; i++)
+ write_word_le(a + sz*i, sz, c->offsets[i] - c->begin);
+ } else {
+ void *a;
+
+ /* Fixed-width or empty arrays */
+
+ a = message_extend_body(m, 1, 0, true); /* let's add offset to parent */
+ if (!a)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int bus_message_close_variant(sd_bus_message *m, struct bus_container *c) {
+ uint8_t *a;
+ size_t l;
+
+ assert(m);
+ assert(c);
+
+ if (!BUS_MESSAGE_IS_GVARIANT(m))
+ return 0;
+
+ l = strlen(c->signature);
+
+ a = message_extend_body(m, 1, 1 + l, true);
+ if (!a)
+ return -ENOMEM;
+
+ a[0] = 0;
+ memcpy(a+1, c->signature, l);
+
+ return 0;
+}
+
+static int bus_message_close_struct(sd_bus_message *m, struct bus_container *c, bool add_offset) {
+ size_t n_variable = 0;
+ unsigned i = 0;
+ const char *p;
+ uint8_t *a;
+ int r;
+
+ assert(m);
+ assert(c);
+
+ if (!BUS_MESSAGE_IS_GVARIANT(m))
+ return 0;
+
+ p = strempty(c->signature);
+ while (*p != 0) {
+ size_t n;
+
+ r = signature_element_length(p, &n);
+ if (r < 0)
+ return r;
+ else {
+ char t[n+1];
+
+ memcpy(t, p, n);
+ t[n] = 0;
+
+ r = bus_gvariant_is_fixed_size(t);
+ if (r < 0)
+ return r;
+ }
+
+ assert(i <= c->n_offsets);
+
+ /* We need to add an offset for each item that has a
+ * variable size and that is not the last one in the
+ * list */
+ if (r == 0 && p[n] != 0)
+ n_variable++;
+
+ i++;
+ p += n;
+ }
+
+ assert(i == c->n_offsets);
+
+ if (n_variable <= 0) {
+ a = message_extend_body(m, 1, 0, add_offset);
+ if (!a)
+ return -ENOMEM;
+ } else {
+ size_t sz;
+ unsigned j;
+
+ assert(c->offsets[c->n_offsets-1] == m->header->body_size);
+
+ sz = determine_word_size(m->header->body_size - c->begin, n_variable);
+
+ a = message_extend_body(m, 1, sz * n_variable, add_offset);
+ if (!a)
+ return -ENOMEM;
+
+ p = strempty(c->signature);
+ for (i = 0, j = 0; i < c->n_offsets; i++) {
+ unsigned k;
+ size_t n;
+
+ r = signature_element_length(p, &n);
+ if (r < 0)
+ return r;
+ else {
+ char t[n+1];
+
+ memcpy(t, p, n);
+ t[n] = 0;
+
+ p += n;
+
+ r = bus_gvariant_is_fixed_size(t);
+ if (r < 0)
+ return r;
+ if (r > 0 || p[0] == 0)
+ continue;
+ }
+
+ k = n_variable - 1 - j;
+
+ write_word_le(a + k * sz, sz, c->offsets[i] - c->begin);
+
+ j++;
+ }
+ }
+
+ return 0;
+}
+
+_public_ int sd_bus_message_close_container(sd_bus_message *m) {
+ struct bus_container *c;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(m->n_containers > 0, -EINVAL);
+ assert_return(!m->poisoned, -ESTALE);
+
+ c = message_get_container(m);
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ if (c->signature && c->signature[c->index] != 0)
+ return -EINVAL;
+
+ m->n_containers--;
+
+ if (c->enclosing == SD_BUS_TYPE_ARRAY)
+ r = bus_message_close_array(m, c);
+ else if (c->enclosing == SD_BUS_TYPE_VARIANT)
+ r = bus_message_close_variant(m, c);
+ else if (c->enclosing == SD_BUS_TYPE_STRUCT || c->enclosing == SD_BUS_TYPE_DICT_ENTRY)
+ r = bus_message_close_struct(m, c, true);
+ else
+ assert_not_reached("Unknown container type");
+
+ free(c->signature);
+ free(c->offsets);
+
+ return r;
+}
+
+typedef struct {
+ const char *types;
+ unsigned n_struct;
+ unsigned n_array;
+} TypeStack;
+
+static int type_stack_push(TypeStack *stack, unsigned max, unsigned *i, const char *types, unsigned n_struct, unsigned n_array) {
+ assert(stack);
+ assert(max > 0);
+
+ if (*i >= max)
+ return -EINVAL;
+
+ stack[*i].types = types;
+ stack[*i].n_struct = n_struct;
+ stack[*i].n_array = n_array;
+ (*i)++;
+
+ return 0;
+}
+
+static int type_stack_pop(TypeStack *stack, unsigned max, unsigned *i, const char **types, unsigned *n_struct, unsigned *n_array) {
+ assert(stack);
+ assert(max > 0);
+ assert(types);
+ assert(n_struct);
+ assert(n_array);
+
+ if (*i <= 0)
+ return 0;
+
+ (*i)--;
+ *types = stack[*i].types;
+ *n_struct = stack[*i].n_struct;
+ *n_array = stack[*i].n_array;
+
+ return 1;
+}
+
+int bus_message_append_ap(
+ sd_bus_message *m,
+ const char *types,
+ va_list ap) {
+
+ unsigned n_array, n_struct;
+ TypeStack stack[BUS_CONTAINER_DEPTH];
+ unsigned stack_ptr = 0;
+ int r;
+
+ assert(m);
+
+ if (!types)
+ return 0;
+
+ n_array = (unsigned) -1;
+ n_struct = strlen(types);
+
+ for (;;) {
+ const char *t;
+
+ if (n_array == 0 || (n_array == (unsigned) -1 && n_struct == 0)) {
+ r = type_stack_pop(stack, ELEMENTSOF(stack), &stack_ptr, &types, &n_struct, &n_array);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ continue;
+ }
+
+ t = types;
+ if (n_array != (unsigned) -1)
+ n_array --;
+ else {
+ types ++;
+ n_struct--;
+ }
+
+ switch (*t) {
+
+ case SD_BUS_TYPE_BYTE: {
+ uint8_t x;
+
+ x = (uint8_t) va_arg(ap, int);
+ r = sd_bus_message_append_basic(m, *t, &x);
+ break;
+ }
+
+ case SD_BUS_TYPE_BOOLEAN:
+ case SD_BUS_TYPE_INT32:
+ case SD_BUS_TYPE_UINT32:
+ case SD_BUS_TYPE_UNIX_FD: {
+ uint32_t x;
+
+ /* We assume a boolean is the same as int32_t */
+ assert_cc(sizeof(int32_t) == sizeof(int));
+
+ x = va_arg(ap, uint32_t);
+ r = sd_bus_message_append_basic(m, *t, &x);
+ break;
+ }
+
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16: {
+ uint16_t x;
+
+ x = (uint16_t) va_arg(ap, int);
+ r = sd_bus_message_append_basic(m, *t, &x);
+ break;
+ }
+
+ case SD_BUS_TYPE_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE: {
+ uint64_t x;
+
+ x = va_arg(ap, uint64_t);
+ r = sd_bus_message_append_basic(m, *t, &x);
+ break;
+ }
+
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_SIGNATURE: {
+ const char *x;
+
+ x = va_arg(ap, const char*);
+ r = sd_bus_message_append_basic(m, *t, x);
+ break;
+ }
+
+ case SD_BUS_TYPE_ARRAY: {
+ size_t k;
+
+ r = signature_element_length(t + 1, &k);
+ if (r < 0)
+ return r;
+
+ {
+ char s[k + 1];
+ memcpy(s, t + 1, k);
+ s[k] = 0;
+
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s);
+ if (r < 0)
+ return r;
+ }
+
+ if (n_array == (unsigned) -1) {
+ types += k;
+ n_struct -= k;
+ }
+
+ r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
+ if (r < 0)
+ return r;
+
+ types = t + 1;
+ n_struct = k;
+ n_array = va_arg(ap, unsigned);
+
+ break;
+ }
+
+ case SD_BUS_TYPE_VARIANT: {
+ const char *s;
+
+ s = va_arg(ap, const char*);
+ if (!s)
+ return -EINVAL;
+
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, s);
+ if (r < 0)
+ return r;
+
+ r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
+ if (r < 0)
+ return r;
+
+ types = s;
+ n_struct = strlen(s);
+ n_array = (unsigned) -1;
+
+ break;
+ }
+
+ case SD_BUS_TYPE_STRUCT_BEGIN:
+ case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
+ size_t k;
+
+ r = signature_element_length(t, &k);
+ if (r < 0)
+ return r;
+
+ {
+ char s[k - 1];
+
+ memcpy(s, t + 1, k - 2);
+ s[k - 2] = 0;
+
+ r = sd_bus_message_open_container(m, *t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
+ if (r < 0)
+ return r;
+ }
+
+ if (n_array == (unsigned) -1) {
+ types += k - 1;
+ n_struct -= k - 1;
+ }
+
+ r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array);
+ if (r < 0)
+ return r;
+
+ types = t + 1;
+ n_struct = k - 2;
+ n_array = (unsigned) -1;
+
+ break;
+ }
+
+ default:
+ r = -EINVAL;
+ }
+
+ if (r < 0)
+ return r;
+ }
+
+ return 1;
+}
+
+_public_ int sd_bus_message_append(sd_bus_message *m, const char *types, ...) {
+ va_list ap;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(types, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+ assert_return(!m->poisoned, -ESTALE);