Exemplo n.º 1
0
SkObject *sk_message_dispatch_simple(SkObject *self) {
    SkObject *result = NULL;
    bstring name = sk_string_get_bstring(sk_message_get_name(self));
    /* is a string */
    if(bchar(name, 0) == '"' && bchar(name, name->slen - 1) == '"') {
        return sk_string_from_bstring(self->vm, bmidstr(name, 1, name->slen - 2));
    } 
    /* is a number */
    else if(is_number(name)) {
        return sk_number_create(self->vm, atoi(bstr2cstr(name, '\0')));
    }
    /* is a command terminator. */
    else if(biseqcstr(name, ";") == 1) {
        return NULL;
    }
    /* a message. */
    else {
        int i;
        SkObjectList *callstack = sk_vm_callstack(SK_VM);
        for(i = kv_size(*callstack) - 1; i >= 0; i--) {
            SkObject *object = kv_A(*callstack, i);
            result = sk_object_dispatch_message(object, self);
            if(result) {
                return result;
            }
        }
        sk_printf("name: %s\n", name->data);
        sk_printf("thread: 0x%x\n", (unsigned int)pthread_self());
        sk_exc_raise(SK_VM, sk_exception_create_lazy(SK_VM, "MessageError",
                    bformat("Nobody is answering to the message '%s'.", name->data)));
        return NULL;
    }
}
Exemplo n.º 2
0
void draw_line(bstring s, int y)
{
	int i;
	int j;
	int screen_abs = 0;
	saveyx();
	move(y, 0);
	clrtoeol();
	
	/* FIXME : wrap line if we go beyond COLS */
	for (i = 0; i < blength(s); i++) {
		if (i == COLS - 1) {
			mvwaddch(buffer_win, y, screen_abs, '\\');
			goto bail_out;
			return;
		}
		
		if (bchar(s, i) == '\t') {
			for (j = 0; j < TAB_LEN; j++)
				mvwaddch(buffer_win, y, screen_abs++, ' ');
		}  else {
			mvwaddch(buffer_win, y, screen_abs, bchar(s, i));
			screen_abs++;
		}
	}
	
bail_out:
	restoreyx();
}
Exemplo n.º 3
0
Arquivo: object.c Projeto: txus/shitdb
Object*
String_to_object(bstring string)
{
  Object *obj = NULL;

  if (bchar(string, 0) == '"') {
    int len = blength(string) - 2;
    obj = Object_create_string(bmidstr(string, 1, len));
  } else if (bchar(string, 0) == '[') {
    int strlen = blength(string) - 2;
    bstring inner_str = bmidstr(string, 1, strlen);
    struct bstrList *elements = bsplit(inner_str, ',');

    int len = elements->qty;
    int i = 0;

    DArray *array = DArray_create(sizeof(Object*), len);
    bstring *ptr = elements->entry;
    for(i = 0; i < len; i++) {
      btrimws(*ptr);
      DArray_push(array, String_to_object(*ptr));
      ptr++;
    }
    obj = Object_create_array(array);
  } else {
    int value = atoi(bdata(string));
    if (value != 0) {
      obj = Object_create_integer(atoi(bdata(string)));
    } else {
      return NULL;
    }
  }
  return obj;
}
Exemplo n.º 4
0
Dir *Dir_create(const char *base, const char *index_file, const char *default_ctype)
{
    Dir *dir = calloc(sizeof(Dir), 1);
    check_mem(dir);

    if(!MAX_SEND_BUFFER || !MAX_DIR_PATH) {
        MAX_SEND_BUFFER = Setting_get_int("limits.dir_send_buffer", 16 * 1024);
        MAX_DIR_PATH = Setting_get_int("limits.dir_max_path", 256);
        log_info("MAX limits.dir_send_buffer=%d, limits.dir_max_path=%d",
                MAX_SEND_BUFFER, MAX_DIR_PATH);
    }

    dir->base = bfromcstr(base);
    check(blength(dir->base) < MAX_DIR_PATH, "Base directory is too long, must be less than %d", MAX_DIR_PATH);
    check(bchar(dir->base, 0) != '/', "Don't start the base with / in %s, that will fail when not in chroot.", base);
    check(bchar(dir->base, blength(dir->base) - 1) == '/', "End directory base with / in %s or it won't work right.", base);

    dir->index_file = bfromcstr(index_file);
    dir->default_ctype = bfromcstr(default_ctype);

    dir->fr_cache = Cache_create(FR_CACHE_SIZE, filerecord_cache_lookup,
                                 filerecord_cache_evict);
    check(dir->fr_cache, "Failed to create FileRecord cache");

    return dir;

error:
    if(dir)
        free(dir);

    return NULL;
}
Exemplo n.º 5
0
Dir *Dir_create(const char *base, const char *prefix, const char *index_file, const char *default_ctype)
{
    Dir *dir = calloc(sizeof(Dir), 1);
    check_mem(dir);

    dir->base = bfromcstr(base);
    check(blength(dir->base) < MAX_DIR_PATH, "Base directory is too long, must be less than %d", MAX_DIR_PATH);

    // dir can come from the routing table so it could have a pattern in it, strip that off
    bstring pattern = bfromcstr(prefix);
    int first_paren = bstrchr(pattern, '(');
    dir->prefix = first_paren >= 0 ? bHead(pattern, first_paren) : bstrcpy(pattern);
    bdestroy(pattern);

    check(blength(dir->prefix) < MAX_DIR_PATH, "Prefix is too long, must be less than %d", MAX_DIR_PATH);

    check(bchar(dir->prefix, 0) == '/' && bchar(dir->prefix, blength(dir->prefix)-1) == '/',
                "Dir route prefix (%s) must start with / and end with / or else you break the internet.", prefix);

    dir->index_file = bfromcstr(index_file);
    dir->default_ctype = bfromcstr(default_ctype);

    dir->fr_cache = Cache_create(FR_CACHE_SIZE, filerecord_cache_lookup,
                                 filerecord_cache_evict);
    check(dir->fr_cache, "Failed to create FileRecord cache");

    return dir;

error:
    if(dir)
        free(dir);

    return NULL;
}
Exemplo n.º 6
0
static int
parse_addr(const char *addr, struct sockaddr *out, socklen_t *out_len)
{
	struct addrinfo hints, *info;
	bstring str, host, service;
	int i, ret = -1;

	if ((NULL == addr) || (NULL == out) || (NULL == out_len))
		return (-1);

	str = bfromcstr(addr);
	if (NULL == str)
		return (-1);

	i = bstrrchr(str, ':');
	if ((BSTR_ERR == i) || (i <= 0)) {
		bdestroy(str);
		return (-1);
	}
	if (('[' == bchar(str, 0)) && (']' == bchar(str, i - 1)))
		host = bmidstr(str, 1, i - 2);  /* IPv6 */
	else
		host = bmidstr(str, 0, i);      /* IPv4 */
	if (NULL == host) {
		bdestroy(str);
		return (-1);
	}
	service = bmidstr(str, i + 1, blength(str) - i);
	if (NULL == service) {
		bdestroy(str);
		bdestroy(host);
		return (-1);
	}
	if ((0 != blength(host)) && (0 != blength(service))) {
		/* Use getaddrinfo to parse the strings */
		memset(&hints, 0, sizeof(hints));
		hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
		hints.ai_family = AF_UNSPEC;
		hints.ai_socktype = SOCK_STREAM;
		if (0 == getaddrinfo(bdata(host), bdata(service), &hints,
			    &info)) {
			if (*out_len >= info->ai_addrlen) {
				memcpy(out, info->ai_addr, info->ai_addrlen);
				*out_len = info->ai_addrlen;
				ret = 0;
			}
			freeaddrinfo(info);
		}
	}
	bdestroy(str);
	bdestroy(host);
	bdestroy(service);

	return (ret);
}
Exemplo n.º 7
0
char *test_bchar() {
    bstring bstr = NULL;
    char c = bchar(bstr, 0);
    mu_assert(c == '\0', "bchar(bstring b, 0) should return '\0' when b is NULL");

    bstr = bfromcstr("");
    c = bchar(bstr, 0);
    mu_assert(c == '\0', "bchar(bstring b, 0) should return '\0' when b is EMPTY");

    bassigncstr(bstr, "hello");
    c = bchar(bstr, 1);
    mu_assert(c = 'e', "bchar should return return 'e'");

    bdestroy(bstr);
    return NULL;
}
Exemplo n.º 8
0
FileRecord *Dir_resolve_file(Dir *dir, bstring path)
{
    FileRecord *file = NULL;
    bstring target = NULL;

    check(Dir_lazy_normalize_base(dir) == 0, "Failed to normalize base path when requesting %s",
            bdata(path));

    check(bstrncmp(path, dir->prefix, blength(dir->prefix)) == 0, 
            "Request for path %s does not start with %s prefix.", 
            bdata(path), bdata(dir->prefix));

    file = FileRecord_cache_check(dir, path);

    if(file) {
        // TODO: double check this gives the right users count
        file->users++;
        return file;
    }

    // We subtract one from the blengths below, because dir->prefix includes
    // a trailing '/'.  If we skip over this in path->data, we drop the '/'
    // from the URI, breaking the target path
    if(bchar(path, blength(path) - 1) == '/') {
        target = bformat("%s%s%s",
                    bdata(dir->normalized_base),
                    path->data + blength(dir->prefix) - 1,
                    bdata(dir->index_file));
    } else {
        target = bformat("%s%s",
                bdata(dir->normalized_base),
                path->data + blength(dir->prefix) - 1);
    }

    check(target, "Couldn't construct target path for %s", bdata(path));

    check_debug(normalize_path(target) == 0, "Failed to normalize target path.");
   
    check_debug(bstrncmp(target, dir->normalized_base, blength(dir->normalized_base)) == 0, 
            "Request for path %s does not start with %s base after normalizing.", 
            bdata(target), bdata(dir->base));

    // the FileRecord now owns the target
    file = Dir_find_file(target, dir->default_ctype);
    check_debug(file, "Error opening file: %s", bdata(target));

    // Increment the user count because we're adding it to the cache
    file->users++;
    file->request_path = bstrcpy(path);
    Cache_add(dir->fr_cache, file);


    return file;

error:
    bdestroy(target);
    FileRecord_release(file);
    return NULL;
}
Exemplo n.º 9
0
void Parse_print_error(const char *message, bstring content, int at, int line_number)
{
    int prev_nl = bstrrchrp(content, '\n', at);
    int next_nl = bstrchrp(content, '\n', at);

    if(prev_nl < 0) {
        log_err("%s AT '%c' on line %d:\n%.*s\n%*s", message,
                bchar(content, at), line_number-1, 
                next_nl, bdata(content),
                at, "^");
    } else {
        log_err("%s AT '%c' on line %d:%.*s\n%*s", message,
                bchar(content, at), line_number, 
                next_nl - prev_nl, bdataofs(content, prev_nl),
                at - prev_nl, "^");
    }
}
Exemplo n.º 10
0
/*
  setBoard: takes two arguments: the number of rows and columns in the board, 
  and an array of arrays of strings representing what is shown on the face 
  of dice on a Boggle board. A Boggle board is a rectangular grid of dice; 
  the height (number of rows) is given by the first argument, the width of 
  this grid (i.e. number of columns) is given by the second argument. The 
  elements of the vector specify the contents of the board in row major order. 
  That is, consider the board as a two-dimensional array, with columns 
  indexed 0 to width, and rows indexed 0 to height. The die at row R and 
  column C has on its face the string in the position diceArray[R][C]. Each 
  string may be upper or lower case, and may contain one or more letters. 
  (In official Boggle, these strings would contain one character only, 
  except for the double letter die face "Qu"; but your BogglePlayer should 
  work correctly for strings of any length as we have modified the game so 
  that a die can show multi-character strings, e.g. "ion") For purposes of 
  finding words on the board, they should all be considered lowercase. 
  Therefore, this function should convert the strings on each die face to 
  lower case. The function will then use the transformed information to 
  construct a data structure representing the Boggle board in a way that
  lends itself well to your search algorithms.
*/
void BogglePlayer::setBoard(unsigned int rows, unsigned int cols, 
                            string** diceArray) {
  this->board.resize(rows, bcharvector(cols));
  for (size_t r=0;r<rows;r++) {
    this->board[r].resize(cols);
    for (size_t c=0;c<cols;c++)
      this->board[r][c] = bchar(diceArray[r][c], r, c, false);
  }
  setBoardCalled = true; 
}
Exemplo n.º 11
0
void Node_catbstr(bstring str, Node *d, char sep, int follow_sibs) 
{
  int rc = 0;

  assert_not(str, NULL);

  if(d == NULL) return;

  if(d->sibling != NULL && follow_sibs) {
    Node_catbstr(str, d->sibling, sep, follow_sibs);
  }

  if(d->type == TYPE_GROUP) {
    rc = bformata(str, "[%c", sep);
    assert(rc == BSTR_OK);
  }

  if(d->child != NULL) {
    // we always follow siblings on the children
    Node_catbstr(str, d->child, sep, 1);
  }

  switch(d->type) {
    case TYPE_BLOB:
      bformata(str, "'%zu:" , blength(d->value.string));
      bconcat(str, d->value.string);
      bformata(str, "\'%c", sep);
      break;
    case TYPE_STRING:
      bformata(str, "\"%s\"%c" , bdata(d->value.string), sep);
      break;
    case TYPE_NUMBER:
      bformata(str, "%llu%c", d->value.number, sep);
      break;
    case TYPE_FLOAT:
      bformata(str, "%f%c", d->value.floating, sep);
      break;
    case TYPE_GROUP: 
      if(!d->name || bchar(d->name, 0) == '@') {
        rc = bformata(str, "]%c", sep);
        assert(rc == BSTR_OK);
      }
      break;
    case TYPE_INVALID: // fallthrough
    default:
      assert(!"invalid type for node");
      break;
  }

  if(d->name != NULL) {
    rc = bformata(str, "%s%c", bdata(d->name), sep); 
    assert(rc == BSTR_OK);
  }
}
Exemplo n.º 12
0
void input_loop(void)
{
	while(1) {

		int c = getch();
		
		if (command_mode)
			process_command(c);
		else 
			process_input(c);

		refresh();

		miniprintf("%c, line length: %d, y: %d, x%d",
			   (bchar(current_buf->contents, current_buf->point) == '\n') ? 'N' : bchar(current_buf->contents, current_buf->point),
			   screen_line_length(current_buf->contents, current_buf->point),
			   current_buf->y, current_buf->x);

	}

}
Exemplo n.º 13
0
static _Bool is_number(bstring string) {
    int i;
    char ch = bchar(string, 0);
    /* if the first char is neither a digit nor a '+' nor a '-', it's invalid. */
    if(!(isdigit(ch) || ch == '+' || ch == '-')) {
        return 0;
    }
    /* if the first char is not a digit (-> '+' or '-'), but the string is only
     * one char long, it's invalid.*/
    if(!isdigit(ch) && string->slen == 1) {
        return 0;
    }
    /* check all following, have to be digits. */
    for(i = 1; i < string->slen; i++) {
        ch = bchar(string, i);
        if(!isdigit(ch)) {
            return 0;
        }
    }
    return 1;
}
Exemplo n.º 14
0
unsigned int makehash(bstring key, unsigned int tablesize)
{
    unsigned int num = 0;
    int c;

    for (c = 0; c < blength(key); c++)
    {
        /* these comments are just some DJB string hashes */
        /* num = ((num << 5) + num) + str->data[c]; */
        /* num = num * 33 ^ str->data[c]; */
        num += bchar(key, c);
    }
    return num % tablesize;
}
Exemplo n.º 15
0
char *test_blength_bdata_bchar(void)
{
	int i = 0;
	
	bstring b = bfromcstr(test);
	mu_assert(b != NULL, "Failed to create bstring.");
	mu_assert(blength(b) == strlen(test), "Wrong string length on blength().");
	mu_assert(strcmp(bdata(b), test) == 0, "Wrong on bdada().");
	for (i = 0; i < blength(b); i++) {
		mu_assert(bchar(b, i) == test[i], "Wrong on bchar().");	
	}
	mu_assert(bdestroy(b) == BSTR_OK, "Failed to bdestroy() afetr blength().");
	
	b = bfromcstr(test_1);
	mu_assert(b != NULL, "Failed to create bstring.");
	mu_assert(blength(b) == strlen(test_1), "Wrong string length on blength().");
	mu_assert(strcmp(bdata(b), test_1) == 0, "Wrong on bdata().");
	for (i = 0; i < blength(b); i++) {
		mu_assert(bchar(b, i) == test_1[i], "Wrong on bchar().");	
	}
	mu_assert(bdestroy(b) == BSTR_OK, "Failed to bdestroy() afetr blength().");
	
	return NULL;
}
Exemplo n.º 16
0
Node *Node_parse_seq(bstring buf, size_t *from)
{
  size_t nread = *from;
  stackish_parser parser;
  stackish_parser_init(&parser);
  char last = bchar(buf, blength(buf) - 1);

  assert_not(buf, NULL);
  assert_not(from, NULL);

  // make sure that the string ends in at least one space of some kind for the parser
  check(last == ' ' || last == '\n' || last == '\t', "buffer doesn't end in either ' \\n\\t'");

  nread = stackish_parser_execute(&parser, (const char *)bdata(buf), blength(buf), nread);
  check(!stackish_parser_has_error(&parser), "parsing error on stackish string");

  while(stackish_more(&parser) && !stackish_parser_is_finished(&parser) && nread < (size_t)blength(buf)) {
    assert(nread+stackish_more(&parser)+1 < (size_t)blength(buf) && "buffer overflow");
    // there is a blob that we have to pull out in order to continue
    nread = stackish_parser_execute(&parser, (const char *)bdata(buf), blength(buf), 
        nread+stackish_more(&parser)+1);
    check(!stackish_parser_has_error(&parser), "parsing error after BLOB");
  }

  *from = nread + 1;

  // skip over a last trailing newline
  while(bchar(buf, *from) == '\n') (*from)++;

  return parser.root;

  on_fail(dbg("failed parsing after %zu bytes", nread);
      stackish_node_clear(&parser); 
      *from = nread + 1;
      return NULL);
}
Exemplo n.º 17
0
void Node_dump(Node *d, char sep, int follow_sibs) 
{
  int i = 0;
  bstring str = Node_bstr(d, follow_sibs);

  assert_not(d, NULL);

  // go through and convert non-printables to printable, but not the last \n
  for(i = 0; i < blength(str)-1; i++) {
    if(!isprint(str->data[i])) {
      str->data[i] = str->data[i] % (126-32) + 32;
    }
  }

  fwrite(str->data, blength(str), 1, stderr);

  if(bchar(str,blength(str)-1) != '\n') {
    fprintf(stderr, "\n");
  }

  bdestroy(str);
}
Exemplo n.º 18
0
/* Description: Recieve a packet and send it.
 * Author: Albin Severinson
 * Date: 03/03/15
 */
int send_packet(bstring packet)
{
  int rc = 0;
  int i = 0;
  int j = 0;
  char byte = 0;

  debug("[SENDING]: %s", bdata(packet));

  //Store packet size
  int data_size = blength(packet);

  for(j = 0;j < MAX_PACKET_RESEND;j++){
    debug("[SENDING PACKET] size: %d try: %d", data_size, j);

    //Transmit one byte at a time
    for(i = 0;i < data_size;i++){
      byte = bchar(packet, i);
      send_byte(byte);
      //debug("Sent [%d = %d]", i, byte);
    }

    //Wait for ACK frame
    if(data_size != 1){
      rc = get_ack();
      
      //Return if we got the ACK
      if(rc == 0){
        bdestroy(packet);
        return 0;
      }
    }
  }

  bdestroy(packet);
  return -1;
}
Exemplo n.º 19
0
static int
parse_server_xport_options(allium_ptcfg *cfg, const char *options)
{
	struct bstrList *l;
	struct tagbstring str;
	int i;

	assert(NULL != cfg);

	if ((NULL == options) || (0 == strlen(options)))
		return (0);

	btfromcstr(str, options);
	l = bsplit(&str, ';');
	if (NULL == l) {
		fprintf(stdout, "ENV-ERROR OOM parsing Transport Options\n");
		return (-1);
	}
	for (i = 0; i < l->qty; /* See next_i */) {
		bstring arg_str;
		int next_i = i + 1;

		if (0 == blength(l->entry[i])) {
out_malformed:
			fprintf(stdout, "ENV-ERROR Malformed Transport Option\n");
			bstrListDestroy_safe(l);
			return (-1);
		}

		/*
		 * Allocate arg_str such that realloc will never be called by
		 * bconcat
		 */
		arg_str = bfromcstralloc(btlength(str), bdata(l->entry[i]));
		if (NULL == arg_str) {
out_oom:
			bstrListDestroy_safe(l);
			fprintf(stdout, "ENV-ERROR OOM parsing Transport Options\n");
			return (-1);
		}
		while ('\\' == bchar(arg_str, blength(arg_str) - 1) && next_i < l->qty) {
			*bdataofs(arg_str, blength(arg_str) - 1) = ';';
			if (NULL == l->entry[next_i]) {
				next_i++;
				break;
			}
			if (BSTR_ERR == bconcat(arg_str, l->entry[next_i])) {
				bdestroy_safe(arg_str);
				goto out_oom;
			}
			next_i++;
		}
		if (parse_server_xport_option(cfg, arg_str)) {
			/*
			 * XXX: This also will claim that the option is
			 * malformed if a malloc fails in the subroutine.
			 * However if that happens, you have bigger problems.
			 */
			bdestroy_safe(arg_str);
			goto out_malformed;
		}
		i = next_i;
		bdestroy_safe(arg_str);
	}
	bstrListDestroy_safe(l);

	return (0);
}
Exemplo n.º 20
0
FileRecord *Dir_resolve_file(Dir *dir, bstring pattern, bstring path)
{
    FileRecord *file = NULL;
    bstring target = NULL;
    bstring prefix = NULL;

    check(Dir_lazy_normalize_base(dir) == 0, "Failed to normalize base path when requesting %s",
            bdata(path));

    file = FileRecord_cache_check(dir, path);

    if(file) {
        // TODO: double check this gives the right users count
        file->users++;
        return file;
    }
    
    int paren = bstrchr(pattern, '(');
    prefix = (paren > 0) ? bHead(pattern, paren) : bstrcpy(pattern);

    check(bchar(prefix, 0) == '/', "Route '%s' pointing to directory must have pattern with leading '/'", bdata(pattern));
    check(blength(prefix) < MAX_DIR_PATH, "Prefix is too long, must be less than %d", MAX_DIR_PATH);

    debug("Building target from base: %s pattern: %s prefix: %s path: %s index_file: %s", 
            bdata(dir->normalized_base),
            bdata(pattern),
            bdata(prefix),
            bdata(path),
            bdata(dir->index_file));

    if(bchar(path, blength(path) - 1) == '/') {
        // a directory so figureo out the index file
        target = bformat("%s%s%s",
                         bdata(dir->normalized_base),
                         path->data + blength(prefix) - 1,
                         bdata(dir->index_file));
    } else if(biseq(prefix, path)) {
        target = bformat("%s%s", bdata(dir->normalized_base), bdata(path));

    } else {
        target = bformat("%s%s", bdata(dir->normalized_base), path->data + blength(prefix) - 1);
    }

    check(target, "Couldn't construct target path for %s", bdata(path));

    check_debug(normalize_path(target) == 0,
            "Failed to normalize target path: %s", bdata(target));
   
    check_debug(bstrncmp(target, dir->normalized_base, blength(dir->normalized_base)) == 0, 
            "Request for path %s does not start with %s base after normalizing.", 
            bdata(target), bdata(dir->base));

    // the FileRecord now owns the target
    file = Dir_find_file(target, dir->default_ctype);
    check_debug(file, "Error opening file: %s", bdata(target));

    // Increment the user count because we're adding it to the cache
    file->users++;
    file->request_path = bstrcpy(path);
    Cache_add(dir->fr_cache, file);


    return file;

error:
    bdestroy(target);
    FileRecord_release(file);
    return NULL;
}
Exemplo n.º 21
0
// Generates the LuaJIT header given a Lua script and a property file. The
// header file is generated based on the property usage of the 'event'
// variable in the script.
//
// source          - The source code of the Lua script.
// property_file   - The property file used to lookup properties.
// event_decl      - A pointer to where the struct def should be returned.
// init_descriptor_func - A pointer to where the descriptor init function should be returned.
//
// Returns 0 if successful, otherwise returns -1.
int sky_lua_generate_event_info(bstring source,
                                sky_property_file *property_file,
                                bstring *event_decl,
                                bstring *init_descriptor_func)
{
    int rc;
    bstring identifier = NULL;
    assert(source != NULL);
    assert(property_file != NULL);
    assert(event_decl != NULL);
    assert(init_descriptor_func != NULL);

    // Initialize returned value.
    *event_decl = bfromcstr(
        "  int64_t timestamp;\n"
        "  uint16_t action_id;\n"
    );
    check_mem(*event_decl);
    *init_descriptor_func = bfromcstr(
        "  ffi.C.sky_data_descriptor_set_timestamp_offset(descriptor, ffi.offsetof(\"sky_lua_event_t\", \"timestamp\"));\n"
        "  ffi.C.sky_data_descriptor_set_action_id_offset(descriptor, ffi.offsetof(\"sky_lua_event_t\", \"action_id\"));\n"
    );
    check_mem(*init_descriptor_func);

    // Setup a lookup of properties.
    bool lookup[SKY_PROPERTY_ID_COUNT+1];
    memset(lookup, 0, sizeof(lookup));

    // Loop over every mention of an "event." property.
    int pos = 0;
    struct tagbstring EVENT_DOT_STR = bsStatic("event.");
    while((pos = binstr(source, pos, &EVENT_DOT_STR)) != BSTR_ERR) {
        // Make sure that this is not part of another identifier.
        bool skip = false;
        if(pos > 0 && (isalnum(bchar(source, pos-1)) || bchar(source, pos-1) == '_')) {
            skip = true;
        }
        
        // Move past the "event." string.
        pos += blength(&EVENT_DOT_STR);

        if(!skip) {
            // Read in identifier.
            int i;
            for(i=pos+1; i<blength(source); i++) {
                char ch = bchar(source, i);
                if(!(isalnum(ch) || ch == '_')) {
                    break;
                }
            }
            identifier = bmidstr(source, pos, i-pos); check_mem(identifier);
            if(blength(identifier)) {
                sky_property *property = NULL;
                rc = sky_property_file_find_by_name(property_file, identifier, &property);
                check(rc == 0, "Unable to find property by name: %s", bdata(identifier));
                check(property != NULL, "Property not found: %s", bdata(identifier));
            
                if(!lookup[property->id-SKY_PROPERTY_ID_MIN]) {
                    // Append property definition to event decl and function.
                    switch(property->data_type) {
                        case SKY_DATA_TYPE_STRING: {
                            bformata(*event_decl, "  char %s[];\n", bdata(property->name));
                            break;
                        }
                        case SKY_DATA_TYPE_INT: {
                            bformata(*event_decl, "  int64_t %s;\n", bdata(property->name));
                            break;
                        }
                        case SKY_DATA_TYPE_DOUBLE: {
                            bformata(*event_decl, "  double %s;\n", bdata(property->name));
                            break;
                        }
                        case SKY_DATA_TYPE_BOOLEAN: {
                            bformata(*event_decl, "  bool %s;\n", bdata(property->name));
                            break;
                        }
                        default:{
                            sentinel("Invalid sky lua type: %d", property->data_type);
                        }
                    }
                    check_mem(*event_decl);

                    bformata(*init_descriptor_func, "  ffi.C.sky_data_descriptor_set_property(descriptor, %d, ffi.offsetof(\"sky_lua_event_t\", \"%s\"), %d);\n", property->id, bdata(property->name), property->data_type);
                    check_mem(*init_descriptor_func);

                    // Flag the property as already processed.
                    lookup[property->id - SKY_PROPERTY_ID_MIN] = true;
                }
            }

            bdestroy(identifier);
            identifier = NULL;
        }
    }

    // Wrap properties in a struct.
    bassignformat(*event_decl, "typedef struct {\n%s} sky_lua_event_t;", bdata(*event_decl));
    check_mem(*event_decl);

    // Wrap info function.
    bassignformat(*init_descriptor_func,
        "function sky_init_descriptor(_descriptor)\n"
        "  descriptor = ffi.cast(\"sky_data_descriptor_t*\", _descriptor)\n"
        "%s"
        "end\n",
        bdata(*init_descriptor_func)
    );
    check_mem(*init_descriptor_func);

    return 0;

error:
    bdestroy(identifier);
    bdestroy(*event_decl);
    *event_decl = NULL;
    bdestroy(*init_descriptor_func);
    *init_descriptor_func = NULL;
    return -1;
}