static
void parser(zsock_t *pipe, void *args)
{
    parser_state_t *state = (parser_state_t*)args;
    state->pipe = pipe;
    set_thread_name(state->me);
    size_t id = state->id;

    // signal readyiness after sockets have been created
    zsock_signal(pipe, 0);

    zpoller_t *poller = zpoller_new(state->pipe, state->pull_socket, NULL);
    assert(poller);

    while (!zsys_interrupted) {
        // -1 == block until something is readable
        void *socket = zpoller_wait(poller, -1);
        zmsg_t *msg = NULL;
        if (socket == state->pipe) {
            msg = zmsg_recv(state->pipe);
            char *cmd = zmsg_popstr(msg);
            zmsg_destroy(&msg);
            if (streq(cmd, "$TERM")) {
                fprintf(stderr, "[D] parser [%zu]: received $TERM command\n", id);
                free(cmd);
                break;
            } else {
                fprintf(stderr, "[E] parser [%zu]: received unknown command: %s\n", id, cmd);
                free(cmd);
                assert(false);
            }
        } else if (socket == state->pull_socket) {
            process_logjam_message(state);
        } else {
            // socket == NULL, probably interrupted by signal handler
            break;
        }
    }

    printf("[I] parser [%zu]: shutting down\n", id);
    parser_state_destroy(&state);
    printf("[I] parser [%zu]: terminated\n", id);
}
static gboolean
file_parse (gchar                     *filename,
	    GtkSourceLanguage         *language,
	    GtkSourceContextData      *ctx_data,
	    GHashTable                *defined_regexes,
	    GHashTable                *styles,
	    GHashTable                *loaded_lang_ids,
	    GQueue                    *replacements,
	    GError                   **error)
{
	ParserState *parser_state;
	xmlTextReader *reader = NULL;
	int fd = -1;
	GError *tmp_error = NULL;
	GtkSourceLanguageManager *lm;
	const gchar *rng_lang_schema;

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	DEBUG (g_message ("loading file '%s'", filename));

	/*
	 * Use fd instead of filename so that it's utf8 safe on w32.
	 */
	fd = g_open (filename, O_RDONLY, 0);

	if (fd != -1)
		reader = xmlReaderForFd (fd, filename, NULL, 0);

	if (reader == NULL)
	{
		g_set_error (&tmp_error,
			     PARSER_ERROR,
			     PARSER_ERROR_CANNOT_OPEN,
			     "unable to open the file");
		goto error;
	}

	lm = _gtk_source_language_get_language_manager (language);
	rng_lang_schema = _gtk_source_language_manager_get_rng_file (lm);

	if (rng_lang_schema == NULL)
	{
		g_set_error (&tmp_error,
			     PARSER_ERROR,
			     PARSER_ERROR_CANNOT_VALIDATE,
			     "could not find the RelaxNG schema file");
		goto error;
	}

	if (xmlTextReaderRelaxNGValidate (reader, rng_lang_schema))
	{
		g_set_error (&tmp_error,
			     PARSER_ERROR,
			     PARSER_ERROR_CANNOT_VALIDATE,
			     "unable to load the RelaxNG schema '%s'",
			     rng_lang_schema);
		goto error;
	}

	parser_state = parser_state_new (language, ctx_data,
					 defined_regexes, styles,
					 replacements, reader,
					 filename, loaded_lang_ids);
	xmlTextReaderSetStructuredErrorHandler (reader,
						(xmlStructuredErrorFunc) text_reader_structured_error_func,
						parser_state);

	while ((parser_state->error == NULL) &&
	        (1 == xmlTextReaderRead (parser_state->reader)))
	{
		int type;

		/* FIXME: does xmlTextReaderRead already do it? */
		xmlTextReaderIsValid (parser_state->reader);

		if (parser_state->error != NULL)
			break;

		type = xmlTextReaderNodeType (parser_state->reader);

		switch (type)
		{
			case XML_READER_TYPE_ELEMENT:
				element_start (parser_state);
				break;
			case XML_READER_TYPE_END_ELEMENT:
				element_end (parser_state);
				break;
		}
	}

	if (parser_state->error != NULL)
	{
		g_propagate_error (&tmp_error, parser_state->error);
		parser_state->error = NULL;
	}

	parser_state_destroy (parser_state);

	if (tmp_error != NULL)
		goto error;

	close (fd);

	return TRUE;

error:
	if (fd != -1)
		close (fd);
	g_propagate_error (error, tmp_error);
	return FALSE;
}