aboutsummaryrefslogblamecommitdiffstats
path: root/libaxl_receive.c
blob: a4c05dc8034699e09adec7c1352c6fbd742f1217 (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.244ss222!",
	[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 (equivalent to "*1/")
	 * U = STRING16, align output to (void *) and enter (equivalent to "*2/")
	 * d = data, uses %, align output to (void *) and enter
	 * * = following are repeated, align output to (void *) and enter
	 * & = * but requires allocation (there is only support for use once) (choose padding after alignment)
	 * / = end of * or &, for & unless finished realigned output and start over
	 * p = padding, align input to byte-quad
	 */
	[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]            = NULL,
	[LIBAXL_REQUEST_GET_INPUT_FOCUS]            = "11244",
	[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*44/u",
	[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",
	[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/",
	[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
};

static size_t
calculate_struct_size(const char *fmt, size_t *alignmentp)
{
	size_t size = 0, i;
	*alignmentp = 1;
	for (;; fmt++) {
		switch (*fmt) {
		case '?':
		case '$':
		case '%':
		case 'p':
			break;

		case '.':
		case '!':
		case '1':
		case 'z':
			size += 1;
			break;

		case ',':
		case '2':
		case 's':
			*alignmentp = MAX(*alignmentp, 2);
			size += 2;
			break;

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

		case '-':
		case '4':
		case 'S':
			*alignmentp = MAX(*alignmentp, 4);
			size += 4;
			break;

		case '=':
			*alignmentp = MAX(*alignmentp, 4); /* 2 uint32_t, not 1 uint64_t */
			size += 8;
			break;

		case '#':
			size += 32;
			break;

		case '@':
			*alignmentp = MAX(*alignmentp, sizeof(size_t));
			ALIGN(&size, size_t);
			size += sizeof(size_t);
			break;

		case '*':
			for (i = 0;; fmt++) {
				if (*fmt == '*') {
					i += 1;
				} else if (*fmt == '/') {
					if (!--i)
						break;
				} else if (!*fmt) {
					abort();
				}
			}
			/* fall through */

		case 'u':
		case 'U':
		case 'd':
			*alignmentp = MAX(*alignmentp, sizeof(void *));
			ALIGN(&size, void *);
			size += sizeof(void *);
			break;

		case '/':
			return size;

		default:
			abort();
		}
	}
}

/* XXX make it possible to prefetch pending messages */
int
libaxl_receive(LIBAXL_CONTEXT *restrict ctx, union libaxl_input *restrict msgp, int flags)
{
	struct {
		const char *fmt;
		char *msg;
		size_t ic, oc, o, count;
	} stack[3];
	size_t si = 0;
	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 = 0;
	ssize_t r;
	uint64_t n, u64;
	uint8_t code;
	size_t t, i, j, o, ic = 0, oc = 0, size, alignment = 1;
	int qc = 0;
#ifdef MSG_TRUNC
	int flag_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;
	code &= ~LIBAXL_SYNTHETIC_EVENT_BIT;

	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 == '@' || *fmt == '$' || *fmt == '%' || *fmt == '/'; 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 '*':
			alignment = 1;
			ALIGN(&o, void *);
			*(void **)&msg[o] = data = &inbuf[i];
			o += sizeof(void *);
			stack[si].fmt = fmt;
			stack[si].msg = msg;
			stack[si].ic = ic;
			stack[si].oc = oc + 1;
			stack[si].o = o;
			stack[si].count = count;
			si += 1;
			count = counts[oc++];
			o = 0;
			msg = data;
			if (!count)
				goto jump_to_end_of_repeat;
			break;

		case '&':
			size = calculate_struct_size(&fmt[1], &alignment);
			ALIGN_VAR(&size, alignment);
			alignment = size;
			data = NULL;
			if (counts[oc]) {
				size *= counts[oc];
				if (size > ctx->aux_buf_size) {
					data = liberror_realloc(ctx->aux_buf, size);
					if (!data)
						return LIBAXL_ERROR_SYSTEM;
					ctx->aux_buf = data;
					ctx->aux_buf_size = size;
				} else {
					data = ctx->aux_buf;
				}
			}
			ALIGN(&o, void *);
			*(void **)&msg[o] = data;
			o += sizeof(void *);
			stack[si].fmt = fmt;
			stack[si].msg = msg;
			stack[si].ic = ic;
			stack[si].oc = oc + 1;
			stack[si].o = o;
			stack[si].count = count;
			si += 1;
			count = counts[oc++];
			o = 0;
			msg = data;
			if (!count)
				goto jump_to_end_of_repeat;
			break;

		case '/':
			if (--count) {
				fmt = stack[si - 1].fmt;
				ALIGN_VAR(&o, alignment);
			} else {
				if (0) {
				jump_to_end_of_repeat:
					for (j = 0;; fmt++) {
						if (*fmt == '*' || *fmt == '&') {
							j += 1;
						} else if (*fmt == '/') {
							if (!j--)
								break;
						} else if (!*fmt) {
							abort();
						}
					}
				}
				si -= 1;
				msg = stack[si].msg;
				ic = stack[si].ic;
				oc = stack[si].oc;
				o = stack[si].o;
				count = stack[si].count;
			}
			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();
		}
	}

	for (; *fmt && oc < ic && !counts[oc++]; fmt++) {
		if (*fmt == '*' || *fmt == '&') {
			ALIGN(&o, void *);
			*(void **)&msg[o] = NULL;
			o += sizeof(void *);
			for (j = 0;; fmt++) {
				if (*fmt == '*' || *fmt == '&') {
					j += 1;
				} else if (*fmt == '/') {
					if (!--j)
						break;
				} else if (!*fmt) {
					abort();
				}
			}
		} else if (*fmt == 'u' || *fmt == 'U' || *fmt == 'd') {
			ALIGN(&o, void *);
			*(void **)&msg[o] = NULL;
			o += sizeof(void *);
		} else {
			break;
		}
	}

	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;
}