aboutsummaryrefslogblamecommitdiffstats
path: root/libaxl_receive.c
blob: e35cf1b3abae442ccf67c19d864297875ae523f4 (plain) (tree)







































































































































































































































                                                                                                     



                                                                                                               
                                                              


                                                                                                                                    
                                                      





























                                                                                                    



                                                                                                                       
                                                                      



                                                                                                                
                                                              

















































































































                                                                                                                          




































































                                                                                                  
                                         








                                                                             
                                         















                                                                             
                                         









                                                                             
                                         










































































                                                                                             

                                                                                                      


                                    

                                                                                                      








































                                                                                                                                   
/* See LICENSE file for copyright and license details. */
#include "common.h"

static const char *const event_formats[] = {
	/*
	 * . = 1 unused byte
	 * , = 2 unused bytes
	 * _ = 3 unused bytes
	 * - = 4 unused bytes
	 * # = 32 uint8_t
	 * ! = libaxl_bool_t
	 * 1 = uint8_t
	 * 2 = uint16_t
	 * 4 = uint32_t
	 * z = int8_t
	 * s = int16_t
	 * S = int32_t
	 * ? = special encoding
	 */
	[LIBAXL_ERROR]                   = "112421",
	[LIBAXL_REPLY]                   = "?",
	[LIBAXL_EVENT_KEY_PRESS]         = "1124444ssss2!",
	[LIBAXL_EVENT_KEY_RELEASE]       = "1124444ssss2!",
	[LIBAXL_EVENT_BUTTON_PRESS]      = "1124444ssss2!",
	[LIBAXL_EVENT_BUTTON_RELEASE]    = "1124444ssss2!",
	[LIBAXL_EVENT_MOTION_NOTIFY]     = "1124444ssss2!",
	[LIBAXL_EVENT_ENTER_NOTIFY]      = "1124444ssss211",
	[LIBAXL_EVENT_LEAVE_NOTIFY]      = "1124444ssss211",
	[LIBAXL_EVENT_FOCUS_IN]          = "112411",
	[LIBAXL_EVENT_FOCUS_OUT]         = "112411",
	[LIBAXL_EVENT_KEYMAP_NOTIFY]     = "#",
	[LIBAXL_EVENT_EXPOSE]            = "1.2422222",
	[LIBAXL_EVENT_GRAPHICS_EXPOSURE] = "1.242222221",
	[LIBAXL_EVENT_NO_EXPOSURE]       = "1.242",
	[LIBAXL_EVENT_VISIBILITY_NOTIFY] = "1.241",
	[LIBAXL_EVENT_CREATE_NOTIFY]     = "1.244sssss!",
	[LIBAXL_EVENT_DESTROY_NOTIFY]    = "1.244",
	[LIBAXL_EVENT_UNMAP_NOTIFY]      = "1.244!",
	[LIBAXL_EVENT_MAP_NOTIFY]        = "1.244!",
	[LIBAXL_EVENT_MAP_REQUEST]       = "1.244",
	[LIBAXL_EVENT_REPARENT_NOTIFY]   = "1.2444ss!",
	[LIBAXL_EVENT_CONFIGURE_NOTIFY]  = "1.2444ss222!",
	[LIBAXL_EVENT_CONFIGURE_REQUEST] = "112444ss2222",
	[LIBAXL_EVENT_GRAVITY_NOTIFY]    = "1.244ss",
	[LIBAXL_EVENT_RESIZE_REQUEST]    = "1.2422",
	[LIBAXL_EVENT_CIRCULATE_NOTIFY]  = "1.244-1",
	[LIBAXL_EVENT_CIRCULATE_REQUEST] = "1.244-1",
	[LIBAXL_EVENT_PROPERTY_NOTIFY]   = "1.2441",
	[LIBAXL_EVENT_SELECTION_CLEAR]   = "1.2444",
	[LIBAXL_EVENT_SELECTION_REQUEST] = "1.2444444",
	[LIBAXL_EVENT_SELECTION_NOTIFY]  = "1.244444",
	[LIBAXL_EVENT_COLORMAP_NOTIFY]   = "1.244!1",
	[LIBAXL_EVENT_CLIENT_MESSAGE]    = "11244",
	[LIBAXL_EVENT_MAPPING_NOTIFY]    = "1.2111"
};

static const char *const reply_formats[] = {
	/*
	 * = = 8 unused bytes
	 * $ = previous marked length, in units
	 * % = previous marked format, in bits
	 * @ = non-encoded size_t length marked, align output to (size_t)
	 * u = STRING8, align output to (void *) and enter
	 * U = STRING16, align output to (void *) and enter
	 * d = data, uses %, align output to (void *) and enter
	 * * = following are repeated, align output to (void *) and enter
	 * & = * but requires allocation
	 * p = padding
	 */
	[0]                                         = NULL,
	[LIBAXL_REQUEST_CREATE_WINDOW]              = NULL,
	[LIBAXL_REQUEST_CHANGE_WINDOW_ATTRIBUTES]   = NULL,
	[LIBAXL_REQUEST_GET_WINDOW_ATTRIBUTES]      = "1124421144!!1!4442",
	[LIBAXL_REQUEST_DESTROY_WINDOW]             = NULL,
	[LIBAXL_REQUEST_DESTROY_SUBWINDOWS]         = NULL,
	[LIBAXL_REQUEST_CHANGE_SAVE_SET]            = NULL,
	[LIBAXL_REQUEST_REPARENT_WINDOW]            = NULL,
	[LIBAXL_REQUEST_MAP_WINDOW]                 = NULL,
	[LIBAXL_REQUEST_MAP_SUBWINDOWS]             = NULL,
	[LIBAXL_REQUEST_UNMAP_WINDOW]               = NULL,
	[LIBAXL_REQUEST_UNMAP_SUBWINDOWS]           = NULL,
	[LIBAXL_REQUEST_CONFIGURE_WINDOW]           = NULL,
	[LIBAXL_REQUEST_CIRCULATE_WINDOW]           = NULL,
	[LIBAXL_REQUEST_GET_GEOMETRY]               = "1244ss222",
	[LIBAXL_REQUEST_QUERY_TREE]                 = "1.24442$=-,*4",
	[LIBAXL_REQUEST_INTERN_ATOM]                = "1.244",
	[LIBAXL_REQUEST_GET_ATOM_NAME]              = "1.242$==-,u",
	[LIBAXL_REQUEST_CHANGE_PROPERTY]            = NULL,
	[LIBAXL_REQUEST_DELETE_PROPERTY]            = NULL,
	[LIBAXL_REQUEST_GET_PROPERTY]               = "11%24444$=-d",
	[LIBAXL_REQUEST_LIST_PROPERTIES]            = "1.242$==-,*4",
	[LIBAXL_REQUEST_SET_SELECTION_OWNER]        = NULL,
	[LIBAXL_REQUEST_GET_SELECTION_OWNER]        = "1.244",
	[LIBAXL_REQUEST_CONVERT_SELECTION]          = NULL,
	[LIBAXL_REQUEST_SEND_EVENT]                 = NULL,
	[LIBAXL_REQUEST_GRAB_POINTER]               = "1124",
	[LIBAXL_REQUEST_UNGRAB_POINTER]             = NULL,
	[LIBAXL_REQUEST_GRAB_BUTTON]                = NULL,
	[LIBAXL_REQUEST_UNGRAB_BUTTON]              = NULL,
	[LIBAXL_REQUEST_CHANGE_ACTIVE_POINTER_GRAB] = NULL,
	[LIBAXL_REQUEST_GRAB_KEYBOARD]              = "1124",
	[LIBAXL_REQUEST_UNGRAB_KEYBOARD]            = NULL,
	[LIBAXL_REQUEST_GRAB_KEY]                   = NULL,
	[LIBAXL_REQUEST_UNGRAB_KEY]                 = NULL,
	[LIBAXL_REQUEST_ALLOW_EVENTS]               = NULL,
	[LIBAXL_REQUEST_GRAB_SERVER]                = NULL,
	[LIBAXL_REQUEST_UNGRAB_SERVER]              = NULL,
	[LIBAXL_REQUEST_QUERY_POINTER]              = ".!2444ssss2",
	[LIBAXL_REQUEST_GET_MOTION_EVENTS]          = "1!244$==-*4ss",
	[LIBAXL_REQUEST_TRANSLATE_COORDINATES]      = "1!244ss",
	[LIBAXL_REQUEST_WARP_POINTER]               = NULL,
	[LIBAXL_REQUEST_SET_INPUT_FOCUS]            = "11244",
	[LIBAXL_REQUEST_GET_INPUT_FOCUS]            = NULL,
	[LIBAXL_REQUEST_QUERY_KEYMAP]               = "1.24#",
	[LIBAXL_REQUEST_OPEN_FONT]                  = NULL,
	[LIBAXL_REQUEST_CLOSE_FONT]                 = NULL,
	[LIBAXL_REQUEST_QUERY_FONT]                 = "1.24sssss2-sssss2-2222$111!ss4$*44*sssss2",
	[LIBAXL_REQUEST_QUERY_TEXT_EXTENTS]         = "1124ssssSSS",
	[LIBAXL_REQUEST_LIST_FONTS]                 = "1.242$==-,&1$u",
	[LIBAXL_REQUEST_LIST_FONTS_WITH_INFO]       = "11$24?sssss2-sssss2-2222$?111!ss4*44u",
	[LIBAXL_REQUEST_SET_FONT_PATH]              = NULL,
	[LIBAXL_REQUEST_GET_FONT_PATH]              = "1.242$==-,&1$u",
	[LIBAXL_REQUEST_CREATE_PIXMAP]              = NULL,
	[LIBAXL_REQUEST_FREE_PIXMAP]                = NULL,
	[LIBAXL_REQUEST_CREATE_GC]                  = NULL,
	[LIBAXL_REQUEST_CHANGE_GC]                  = NULL,
	[LIBAXL_REQUEST_COPY_GC]                    = NULL,
	[LIBAXL_REQUEST_SET_DASHES]                 = NULL,
	[LIBAXL_REQUEST_SET_CLIP_RECTANGLES]        = NULL,
	[LIBAXL_REQUEST_FREE_GC]                    = NULL,
	[LIBAXL_REQUEST_CLEAR_AREA]                 = NULL,
	[LIBAXL_REQUEST_COPY_AREA]                  = NULL,
	[LIBAXL_REQUEST_COPY_PLANE]                 = NULL,
	[LIBAXL_REQUEST_POLY_POINT]                 = NULL,
	[LIBAXL_REQUEST_POLY_LINE]                  = NULL,
	[LIBAXL_REQUEST_POLY_SEGMENT]               = NULL,
	[LIBAXL_REQUEST_POLY_RECTANGLE]             = NULL,
	[LIBAXL_REQUEST_POLY_ARC]                   = NULL,
	[LIBAXL_REQUEST_FILL_POLY]                  = NULL,
	[LIBAXL_REQUEST_POLY_FILL_RECTANGLE]        = NULL,
	[LIBAXL_REQUEST_POLY_FILL_ARC]              = NULL,
	[LIBAXL_REQUEST_PUT_IMAGE]                  = NULL,
	[LIBAXL_REQUEST_GET_IMAGE]                  = "11244==-@u", /* "u" is actually "*1" */
	[LIBAXL_REQUEST_POLY_TEXT8]                 = NULL,
	[LIBAXL_REQUEST_POLY_TEXT16]                = NULL,
	[LIBAXL_REQUEST_IMAGE_TEXT8]                = NULL,
	[LIBAXL_REQUEST_IMAGE_TEXT16]               = NULL,
	[LIBAXL_REQUEST_CREATE_COLORMAP]            = NULL,
	[LIBAXL_REQUEST_FREE_COLORMAP]              = NULL,
	[LIBAXL_REQUEST_COPY_COLORMAP_AND_FREE]     = NULL,
	[LIBAXL_REQUEST_INSTALL_COLORMAP]           = NULL,
	[LIBAXL_REQUEST_UNINSTALL_COLORMAP]         = NULL,
	[LIBAXL_REQUEST_LIST_INSTALLED_COLORMAPS]   = "1.242$==-,*4",
	[LIBAXL_REQUEST_ALLOC_COLOR]                = "1.24222,4",
	[LIBAXL_REQUEST_ALLOC_NAMED_COLOR]          = "1.244222222",
	[LIBAXL_REQUEST_ALLOC_COLOR_CELLS]          = "1.242$2$==-*4*4",
	[LIBAXL_REQUEST_ALLOC_COLOR_PLANES]         = "1.242$,444=*4",
	[LIBAXL_REQUEST_FREE_COLORS]                = NULL,
	[LIBAXL_REQUEST_STORE_COLORS]               = NULL,
	[LIBAXL_REQUEST_STORE_NAMED_COLOR]          = NULL,
	[LIBAXL_REQUEST_QUERY_COLORS]               = "1.242$==-,*222,",
	[LIBAXL_REQUEST_LOOKUP_COLOR]               = "1.24222222",
	[LIBAXL_REQUEST_CREATE_CURSOR]              = NULL,
	[LIBAXL_REQUEST_CREATE_GLYPH_CURSOR]        = NULL,
	[LIBAXL_REQUEST_FREE_CURSOR]                = NULL,
	[LIBAXL_REQUEST_RECOLOR_CURSOR]             = NULL,
	[LIBAXL_REQUEST_QUERY_BEST_SIZE]            = "1.2422",
	[LIBAXL_REQUEST_QUERY_EXTENSION]            = "1.24!1111",
	[LIBAXL_REQUEST_LIST_EXTENSIONS]            = "11$24===&1$u",
	[LIBAXL_REQUEST_CHANGE_KEYBOARD_MAPPING]    = NULL,
	[LIBAXL_REQUEST_GET_KEYBOARD_MAPPING]       = "1124$===*4",
	[LIBAXL_REQUEST_CHANGE_KEYBOARD_CONTROL]    = NULL,
	[LIBAXL_REQUEST_GET_KEYBOARD_CONTROL]       = "112441122,#",
	[LIBAXL_REQUEST_BELL]                       = NULL,
	[LIBAXL_REQUEST_CHANGE_POINTER_CONTROL]     = NULL,
	[LIBAXL_REQUEST_GET_POINTER_CONTROL]        = "1.24222",
	[LIBAXL_REQUEST_SET_SCREEN_SAVER]           = NULL,
	[LIBAXL_REQUEST_GET_SCREEN_SAVER]           = "1.242211",
	[LIBAXL_REQUEST_CHANGE_HOSTS]               = NULL,
	[LIBAXL_REQUEST_LIST_HOSTS]                 = "1124$2==-,&1.2$up", /* "u" is actually "*1" */
	[LIBAXL_REQUEST_SET_ACCESS_CONTROL]         = NULL,
	[LIBAXL_REQUEST_SET_CLOSE_DOWN_MODE]        = NULL,
	[LIBAXL_REQUEST_KILL_CLIENT]                = NULL,
	[LIBAXL_REQUEST_ROTATE_PROPERTIES]          = NULL,
	[LIBAXL_REQUEST_FORCE_SCREEN_SAVER]         = NULL,
	[LIBAXL_REQUEST_SET_POINTER_MAPPING]        = "1124",
	[LIBAXL_REQUEST_GET_POINTER_MAPPING]        = "11$24===*1",
	[LIBAXL_REQUEST_SET_MODIFIER_MAPPING]       = "1124",
	[LIBAXL_REQUEST_GET_MODIFIER_MAPPING]       = "11$24===*11",
	[LIBAXL_REQUEST_NO_OPERATION]               = NULL
};

/* TODO make it possible to prefetch pending messages */
int
libaxl_receive(LIBAXL_CONTEXT *restrict ctx, union libaxl_input *restrict msgp, int flags)
{
	LIBAXL_CONNECTION *conn = ctx->conn;
	const char *fmt;
	char *restrict msg = (char *)msgp;
	char *restrict inbuf, *data;
	size_t format = 1, counts[] = {0, 0, 0}, count;
	ssize_t r;
	uint64_t n, u64;
	uint8_t code;
	size_t t, i, j, o, ic = 0, oc = 0;
	int qc = 0;
#ifdef MSG_TRUNC
	int flag_trunc;
#endif

#ifdef MSG_TRUNC
	flags ^= flag_trunc = flags & MSG_TRUNC;
#endif

	WLOCK_CONNECTION_RECV(conn);

	inbuf = conn->in_buf;
	n = 32;

	if (conn->in_buf_size < n) {
		inbuf = liberror_realloc(inbuf, n);
		if (!inbuf) {
			WUNLOCK_CONNECTION_RECV(conn);
			return LIBAXL_ERROR_SYSTEM;
		}
		conn->in_buf = inbuf;
		conn->in_buf_size = n;
	}

	while (conn->in_progress < n) {
		r = recv(conn->fd, &inbuf[conn->in_progress], n - conn->in_progress, flags);
		if (r <= 0) {
			liberror_save_backtrace(NULL);
			if (!r) {
				liberror_set_error("The connection to the display server has been closed",
				                   "libaxl_receive", "libaxl", LIBAXL_ERROR_CONNECTION_CLOSED);
				WUNLOCK_CONNECTION_RECV(conn);
				return LIBAXL_ERROR_CONNECTION_CLOSED;
			}
			liberror_recv_failed(conn->fd, &inbuf[conn->in_progress], n - conn->in_progress, flags, "<display server>");
			WUNLOCK_CONNECTION_RECV(conn);
			return LIBAXL_ERROR_SYSTEM;
		}
		conn->in_progress += (size_t)r;
	}

	code = *(uint8_t *)inbuf;

	if (code == LIBAXL_REPLY) {
		n = (uint64_t)ntohl(*(uint32_t *)&inbuf[4]);
		n = 4 * n + 32;
		if (n > SIZE_MAX) {
			WUNLOCK_CONNECTION_RECV(conn);
			liberror_save_backtrace(NULL);
			liberror_set_error("Received message is too large to allocate",
			                   "libaxl_receive", "libaxl", LIBAXL_ERROR_SYSTEM);
			errno = ENOMEM;
			return LIBAXL_ERROR_SYSTEM;
		}
		if (conn->in_buf_size < n) {
			inbuf = liberror_realloc(inbuf, n);
			if (!inbuf) {
				WUNLOCK_CONNECTION_RECV(conn);
				return LIBAXL_ERROR_SYSTEM;
			}
			conn->in_buf = inbuf;
			conn->in_buf_size = n;
		}
		while (conn->in_progress < n) {
			r = recv(conn->fd, &inbuf[conn->in_progress], n - conn->in_progress, flags);
			if (r <= 0) {
				liberror_save_backtrace(NULL);
				if (!r) {
					liberror_set_error("The connection to the display server has been closed",
					                   "libaxl_receive", "libaxl", LIBAXL_ERROR_CONNECTION_CLOSED);
					WUNLOCK_CONNECTION_RECV(conn);
					return LIBAXL_ERROR_CONNECTION_CLOSED;
				}
				liberror_recv_failed(conn->fd, &inbuf[conn->in_progress], n - conn->in_progress,
				                     flags, "<display server>");
				WUNLOCK_CONNECTION_RECV(conn);
				return LIBAXL_ERROR_SYSTEM;
			}
			conn->in_progress += (size_t)r;
		}
	}

	conn->in_progress = 0;

#ifdef MSG_TRUNC
	if (flag_trunc) {
		WUNLOCK_CONNECTION_RECV(conn);
		return 0;
	}
#endif

	t = conn->in_buf_size;
	conn->in_buf_size = ctx->in_buf_size;
	ctx->in_buf_size = t;

	conn->in_buf = ctx->in_buf;
	ctx->in_buf = inbuf;

	WUNLOCK_CONNECTION_RECV(conn);

	if (code > sizeof(event_formats) / sizeof(*event_formats) ||
	    !(fmt = event_formats[code])) {
		liberror_save_backtrace(NULL);
		liberror_set_error("Received message's code is invalid", "libaxl_receive", "libaxl", LIBAXL_ERROR_SYSTEM);
		errno = EBADMSG;
		return LIBAXL_ERROR_SYSTEM;
	}

	for (i = 0; i < n; fmt++) {
		switch (*fmt) {
		case '\0':
			goto end_of_fmt;

		case '.':
			i += 1;
			break;

		case ',':
			i += 2;
			break;

		case '_':
			i += 3;
			break;

		case '-':
			i += 4;
			break;

		case '#':
			*(uint64_t *)&msg[i +  0] = *(uint64_t *)&inbuf[i +  0];
			*(uint64_t *)&msg[i +  8] = *(uint64_t *)&inbuf[i +  8];
			*(uint64_t *)&msg[i + 16] = *(uint64_t *)&inbuf[i + 16];
			*(uint64_t *)&msg[i + 24] = *(uint64_t *)&inbuf[i + 24];
			i += 32;
			break;

		case '!':
		case '1':
			*(uint8_t *)&msg[i] = *(uint8_t *)&inbuf[i];
			i += 1;
			break;

		case '2':
			if (i + 2 <= n) {
				liberror_save_backtrace(NULL);
				goto short_msg;
			}
			*(uint16_t *)&msg[i] = ntohs(*(uint16_t *)&inbuf[i]);
			i += 2;
			break;

		case '4':
			if (i + 4 <= n) {
				liberror_save_backtrace(NULL);
				goto short_msg;
			}
			*(uint32_t *)&msg[i] = ntohl(*(uint32_t *)&inbuf[i]);
			i += 4;
			break;

		case 'z':
			*(uint8_t *)&msg[i] = *(uint8_t *)&inbuf[i];
			UNTWOS_COMPLEMENT8(&msg[i]);
			i += 1;
			break;

		case 's':
			if (i + 2 <= n) {
				liberror_save_backtrace(NULL);
				goto short_msg;
			}
			*(uint16_t *)&msg[i] = ntohs(*(uint16_t *)&inbuf[i]);
			UNTWOS_COMPLEMENT16(&msg[i]);
			i += 2;
			break;

		case 'S':
			if (i + 4 <= n) {
				liberror_save_backtrace(NULL);
				goto short_msg;
			}
			*(uint32_t *)&msg[i] = ntohl(*(uint32_t *)&inbuf[i]);
			UNTWOS_COMPLEMENT32(&msg[i]);
			i += 4;
			break;

		case '?':
			/* We know it is LIBAXL_REPLY message */
			goto received_reply;

		default:
			abort();
		}
	}

	if (*fmt) {
		liberror_save_backtrace(NULL);
		goto short_msg;
	}

end_of_fmt:
	return 0;

received_reply:
	code = conn->request_map[ntohs(*(uint16_t *)&inbuf[2])]; /* read-lock here is pointless */
	fmt = reply_formats[code];
	if (!fmt) {
		liberror_set_error("Received reply message with unrecognised opcode",
		                   "libaxl_receive", "libaxl", LIBAXL_ERROR_INVALID_REPLY_OPCODE);
		errno = EBADMSG;
		return LIBAXL_ERROR_INVALID_REPLY_OPCODE;
	}

	for (i = o = 0; i < n; fmt++) {
		switch (*fmt) {
		case '\0':
			goto end_of_fmt;

		case '.':
			i += 1;
			o += 1;
			break;

		case ',':
			i += 2;
			o += 2;
			break;

		case '_':
			i += 3;
			o += 3;
			break;

		case '-':
			i += 4;
			o += 4;
			break;

		case '=':
			i += 8;
			o += 8;
			break;

		case '#':
			*(uint64_t *)&msg[o +  0] = *(uint64_t *)&inbuf[i +  0];
			*(uint64_t *)&msg[o +  8] = *(uint64_t *)&inbuf[i +  8];
			*(uint64_t *)&msg[o + 16] = *(uint64_t *)&inbuf[i + 16];
			*(uint64_t *)&msg[o + 24] = *(uint64_t *)&inbuf[i + 24];
			i += 32;
			o += 32;
			break;

		case '!':
		case '1':
			*(uint8_t *)&msg[o++] = *(uint8_t *)&inbuf[i++];
			break;

		case '2':
			if (i + 2 >= n) {
				liberror_save_backtrace(NULL);
				goto short_msg;
			}
			*(uint16_t *)&msg[o] = ntohs(*(uint16_t *)&inbuf[i]);
			i += 2;
			o += 2;
			break;

		case '4':
			if (i + 4 >= n) {
				liberror_save_backtrace(NULL);
				goto short_msg;
			}
			*(uint32_t *)&msg[o] = ntohl(*(uint32_t *)&inbuf[i]);
			i += 4;
			o += 4;
			break;

		case 'z':
			*(uint8_t *)&msg[o] = *(uint8_t *)&inbuf[i];
			UNTWOS_COMPLEMENT8(&msg[o]);
			i += 1;
			o += 1;
			break;

		case 's':
			if (i + 2 >= n) {
				liberror_save_backtrace(NULL);
				goto short_msg;
			}
			*(uint16_t *)&msg[o] = ntohs(*(uint16_t *)&inbuf[i]);
			UNTWOS_COMPLEMENT16(&msg[o]);
			i += 2;
			o += 2;
			break;

		case 'S':
			if (i + 4 >= n) {
				liberror_save_backtrace(NULL);
				goto short_msg;
			}
			*(uint32_t *)&msg[o] = ntohl(*(uint32_t *)&inbuf[i]);
			UNTWOS_COMPLEMENT32(&msg[o]);
			i += 4;
			o += 4;
			break;

		case '%':
			if (fmt[-1] == '1')
				format = (size_t)*(uint8_t *)&msg[i - 1];
			else if (fmt[-1] == '2')
				format = (size_t)*(uint16_t *)&msg[i - 2];
			else
				format = (size_t)*(uint32_t *)&msg[i - 4];
			break;

		case '@':
			ALIGN(&o, size_t);
			u64 = 4 * (uint64_t)*(uint32_t *)&msg[4];
			if (u64 > SIZE_MAX) {
				liberror_save_backtrace(NULL);
				goto corrupt_reply;
			}
			*(size_t *)&msg[o] = counts[ic++] = (size_t)u64;
			o += sizeof(size_t);
			break;

		case '$':
			if (fmt[-1] == '2')
				counts[ic++] = (size_t)*(uint16_t *)&msg[i - 2];
			else if (fmt[-1] == '1')
				counts[ic++] = (size_t)*(uint8_t *)&msg[i - 1];
			else
				counts[ic++] = (size_t)*(uint32_t *)&msg[i - 4];
			break;

		case 'p':
			ALIGN(&i, uint32_t);
			break;

		case 'u':
			format = 8;
			goto case_d;

		case 'U':
			format = 16;
			goto case_d;

		case 'd':
		case_d:
			ALIGN(&o, void *);
			*(void **)&msg[o] = data = &inbuf[i];
			count = counts[oc++];
			count *= format / 8;
			if (format == 16) {
				for (j = 0; j < count; j += 2)
					*(uint16_t *)&data[j] = ntohs(*(uint16_t *)&data[j]);
			} else if (format == 32) {
				for (j = 0; j < count; j += 2)
					*(uint32_t *)&data[j] = ntohl(*(uint32_t *)&data[j]);
			} else if (format != 8 && format != 0) {
				liberror_save_backtrace(NULL);
				goto corrupt_reply;
			}
			o += sizeof(void *);
			i += count;
			break;

		case '*':
			ALIGN(&o, void *);
			*(void **)&msg[o] = data = &inbuf[i];
			count = counts[oc++];
			/* TODO */
			fprintf(stderr, "libaxl_receive: function not fully implemented: '*' case\n");
			abort();
			break;

		case '&': /* TODO */
			fprintf(stderr, "libaxl_receive: function not fully implemented: '&' case\n");
			abort();
			/*
			  LIBAXL_REQUEST_LIST_FONTS
			  LIBAXL_REQUEST_GET_FONT_PATH
			  LIBAXL_REQUEST_LIST_EXTENSIONS
			  LIBAXL_REQUEST_LIST_HOSTS
			 */
			break;

		case '?':
			/* We know that code == LIBAXL_REQUEST_LIST_FONTS_WITH_INFO */
			if (!qc++) {
				if (!counts[0])
					goto done;
			} else {
				count = counts[0];
				counts[0] = counts[1];
				counts[1] = count;
			}
			break;

		default:
			abort();
		}
	}

	if (*fmt) {
		liberror_save_backtrace(NULL);
	short_msg:
		liberror_set_error("Received message's is shorter than expected", "libaxl_receive", "libaxl", LIBAXL_ERROR_SYSTEM);
		errno = EBADMSG;
		return LIBAXL_ERROR_SYSTEM;
	}

done:
	return 0;

corrupt_reply:
	liberror_set_error("Corrupt reply message received", "libaxl_receive", "libaxl", LIBAXL_ERROR_SYSTEM);
	errno = EBADMSG;
	return LIBAXL_ERROR_SYSTEM;
}