parse_json_message(
    char *s)
{
    int i;
    int len = strlen(s);
    GPtrArray *message_array = g_ptr_array_sized_new(100);
    char *token;
    message_t *message = NULL;
    gboolean expect_key = TRUE;
    char *key = NULL;
    int nb_arg = 0;

    for (i = 0; i < len && s[i] != '\0'; i++) {
	char c =  s[i];

	switch (c) {
	    case '[':
		if (message && key) {
		    message->arg_array[nb_arg].key = key;
		    message->arg_array[nb_arg].value.type = MESSAGE_ARRAY;
		    message->arg_array[nb_arg].value.array = g_ptr_array_sized_new(10);
		    nb_arg++;
		    if (nb_arg >= message->argument_allocated) {
			message->argument_allocated *=2;
			message->arg_array = g_realloc(message->arg_array, (message->argument_allocated+1) * sizeof(message_arg_array_t));
		    }
		    message->arg_array[nb_arg].key = NULL;
		    message->arg_array[nb_arg].value.type = MESSAGE_NULL;
		    message->arg_array[nb_arg].value.string = NULL;
		    parse_json_array(s, &i, message->arg_array[nb_arg-1].value.array);
		    key = NULL;
		    expect_key = TRUE;
		}
		break;
	    case ']':
		break;
	    case '{':
		if (message && key) {
		    message->arg_array[nb_arg].key = key;
		    message->arg_array[nb_arg].value.type = MESSAGE_HASH;
		    message->arg_array[nb_arg].value.hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_message_value_full);
		    nb_arg++;
		    if (nb_arg >= message->argument_allocated) {
			message->argument_allocated *=2;
			message->arg_array = g_realloc(message->arg_array, (message->argument_allocated+1) * sizeof(message_arg_array_t));
		    }
		    message->arg_array[nb_arg].key = NULL;
		    message->arg_array[nb_arg].value.type = MESSAGE_NULL;
		    message->arg_array[nb_arg].value.string = NULL;
		    parse_json_hash(s, &i, message->arg_array[nb_arg-1].value.hash);
		    key = NULL;
		    expect_key = TRUE;
		} else {
		    message = g_new0(message_t, 1);
		    nb_arg = 0;
		    message->argument_allocated = 10;
		    message->arg_array = g_new0(message_arg_array_t, message->argument_allocated+1);
		}
		break;
	    case '}':
		assert(message);
		if (message->merrno != 0 && message->errnocode == NULL) {
		    message->errnocode = errcode[message->merrno];
		}
		if (message->merrno != 0 && message->errnostr == NULL) {
		    message->errnostr = g_strdup(strerror(message->merrno));
		}
		g_ptr_array_add(message_array, message);
		message = NULL;
		break;
	    case '"':
		token = json_parse_string(s, &i, len);
		assert(token);
		assert(message);
		if (expect_key) {
		    expect_key = FALSE;
		    assert(key == NULL);
		    key = token;
		} else {
		    assert(key != NULL);
		    expect_key = TRUE;
		    if (strncmp_const(key, "source_filename") == 0) {
			g_free(key);
			key = NULL;
			message->file = token;
		    } else if (strncmp_const(key, "source_line") == 0) {
			g_free(key);
			key = NULL;
			message->line = atoi(token);
			g_free(token);
		    } else if (strncmp_const(key, "severity") == 0) {
			g_free(key);
			key = NULL;
			if (strncmp_const(token, "success") == 0) {
			    message->severity = MSG_SUCCESS;
			} else if (strncmp_const(token, "info") == 0) {
			    message->severity = MSG_INFO;
			} else if (strncmp_const(token, "message") == 0) {
			    message->severity = MSG_MESSAGE;
			} else if (strncmp_const(token, "warning") == 0) {
			    message->severity = MSG_WARNING;
			} else if (strncmp_const(token, "error") == 0) {
			    message->severity = MSG_ERROR;
			} else { /* critical or any other value */
			    message->severity = MSG_CRITICAL;
			}
			g_free(token);
		    } else if (strncmp_const(key, "code") == 0) {
			g_free(key);
			key = NULL;
			message->code = atoi(token);
			g_free(token);
		    } else if (strncmp_const(key, "message") == 0) {
			g_free(key);
			key = NULL;
			message->msg = token;
		    } else if (strncmp_const(key, "hint") == 0) {
			g_free(key);
			key = NULL;
			message->hint = token;
		    } else if (strncmp_const(key, "process") == 0) {
			g_free(key);
			key = NULL;
			message->process = token;
		    } else if (strncmp_const(key, "running_on") == 0) {
			g_free(key);
			key = NULL;
			message->running_on = token;
		    } else if (strncmp_const(key, "component") == 0) {
			g_free(key);
			key = NULL;
			message->component = token;
		    } else if (strncmp_const(key, "module") == 0) {
			g_free(key);
			key = NULL;
			message->module = token;
		    } else if (strncmp_const(key, "errno") == 0) {
			g_free(key);
			key = NULL;
			message->merrno = atoi(token);
			g_free(token);
		    } else if (strncmp_const(key, "errnocode") == 0) {
			g_free(key);
			key = NULL;
			message->errnocode = token;
		    } else if (strncmp_const(key, "errnostr") == 0) {
			g_free(key);
			key = NULL;
			message->errnostr = token;
		    } else {
			message->arg_array[nb_arg].key = key;
			key = NULL;
			message->arg_array[nb_arg].value.type = MESSAGE_STRING;
			message->arg_array[nb_arg].value.string = token;
			nb_arg++;
			if (nb_arg >= message->argument_allocated) {
			    message->argument_allocated *=2;
			    message->arg_array = g_realloc(message->arg_array, (message->argument_allocated+1) * sizeof(message_arg_array_t));
			}
			message->arg_array[nb_arg].key = NULL;
			message->arg_array[nb_arg].value.type = MESSAGE_NULL;
			message->arg_array[nb_arg].value.string = NULL;
		    }
		}
		token = NULL;

		break;
	    case '\t':
	    case '\r':
	    case '\n':
	    case ':':
	    case ',':
	    case ' ':
		break;

	    default:
		token = parse_json_primitive(s, &i, len);
		assert(token==NULL);
		if (expect_key) {
		    expect_key = FALSE;
		    key = g_strdup(token);
		} else if (token == NULL) {
		    message->arg_array[nb_arg].key = key;
		    message->arg_array[nb_arg].value.type = MESSAGE_NULL;
		    message->arg_array[nb_arg].value.string = NULL;
		    nb_arg++;
		    if (nb_arg >= message->argument_allocated) {
			message->argument_allocated *=2;
			message->arg_array = g_realloc(message->arg_array, (message->argument_allocated+1) * sizeof(message_arg_array_t));
		    }

		    expect_key = TRUE;
		}
		token = NULL;
		break;
	}
    }
    s=s;

    return message_array;
}