From df2ab8384f703d308ba09211b4670b9896681b53 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 5 Sep 2012 17:13:05 -0700 Subject: [PATCH] Refactored instruction functions into instruction.c. --- libguac/src/Makefile.am | 5 +- libguac/src/instruction.c | 334 ++++++++++++++++++++++++++++++++++++++ libguac/src/protocol.c | 291 --------------------------------- 3 files changed, 337 insertions(+), 293 deletions(-) create mode 100644 libguac/src/instruction.c diff --git a/libguac/src/Makefile.am b/libguac/src/Makefile.am index 8779c50c..10d36873 100644 --- a/libguac/src/Makefile.am +++ b/libguac/src/Makefile.am @@ -57,11 +57,12 @@ noinst_HEADERS = \ libguac_la_SOURCES = \ client.c \ - socket.c \ - protocol.c \ client-handlers.c \ error.c \ + instruction.c \ palette.c \ + protocol.c \ + socket.c \ unicode.c lib_LTLIBRARIES = libguac.la diff --git a/libguac/src/instruction.c b/libguac/src/instruction.c new file mode 100644 index 00000000..6073a69d --- /dev/null +++ b/libguac/src/instruction.c @@ -0,0 +1,334 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include +#include + +#include "error.h" +#include "instruction.h" +#include "protocol.h" +#include "socket.h" +#include "unicode.h" + +int __guac_fill_instructionbuf(guac_socket* socket) { + + int retval; + + /* Attempt to fill buffer */ + retval = read( + socket->fd, + socket->__instructionbuf + socket->__instructionbuf_used_length, + socket->__instructionbuf_size - socket->__instructionbuf_used_length + ); + + /* Set guac_error if recv() unsuccessful */ + if (retval < 0) { + guac_error = GUAC_STATUS_SEE_ERRNO; + guac_error_message = "Error filling instruction buffer"; + return retval; + } + + socket->__instructionbuf_used_length += retval; + + /* Expand buffer if necessary */ + if (socket->__instructionbuf_used_length > + socket->__instructionbuf_size / 2) { + + socket->__instructionbuf_size *= 2; + socket->__instructionbuf = realloc(socket->__instructionbuf, + socket->__instructionbuf_size); + } + + return retval; + +} + + +/* Returns new instruction if one exists, or NULL if no more instructions. */ +guac_instruction* guac_instruction_read(guac_socket* socket, + int usec_timeout) { + + int retval; + + /* Loop until a instruction is read */ + for (;;) { + + /* Length of element, in Unicode characters */ + int element_length = 0; + + /* Length of element, in bytes */ + int element_byte_length = 0; + + /* Current position within the element, in Unicode characters */ + int current_unicode_length = 0; + + /* Position within buffer */ + int i = socket->__instructionbuf_parse_start; + + /* Parse instruction in buffer */ + while (i < socket->__instructionbuf_used_length) { + + /* Read character from buffer */ + char c = socket->__instructionbuf[i++]; + + /* If digit, calculate element length */ + if (c >= '0' && c <= '9') + element_length = element_length * 10 + c - '0'; + + /* Otherwise, if end of length */ + else if (c == '.') { + + /* Calculate element byte length by walking buffer */ + while (i + element_byte_length < + socket->__instructionbuf_used_length + && current_unicode_length < element_length) { + + /* Get next byte */ + c = socket->__instructionbuf[i + element_byte_length]; + + /* Update byte and character lengths */ + element_byte_length += guac_utf8_charsize((unsigned) c); + current_unicode_length++; + + } + + /* Verify element is fully read */ + if (current_unicode_length == element_length) { + + /* Get element value */ + char* elementv = &(socket->__instructionbuf[i]); + + /* Get terminator, set null terminator of elementv */ + char terminator = elementv[element_byte_length]; + elementv[element_byte_length] = '\0'; + + /* Move to char after terminator of element */ + i += element_byte_length+1; + + /* Reset element length */ + element_length = + element_byte_length = + current_unicode_length = 0; + + /* As element has been read successfully, update + * parse start */ + socket->__instructionbuf_parse_start = i; + + /* Save element */ + socket->__instructionbuf_elementv[socket->__instructionbuf_elementc++] = elementv; + + /* Finish parse if terminator is a semicolon */ + if (terminator == ';') { + + guac_instruction* parsed_instruction; + int j; + + /* Allocate instruction */ + parsed_instruction = malloc(sizeof(guac_instruction)); + if (parsed_instruction == NULL) { + guac_error = GUAC_STATUS_NO_MEMORY; + guac_error_message = "Could not allocate memory for parsed instruction"; + return NULL; + } + + /* Init parsed instruction */ + parsed_instruction->argc = socket->__instructionbuf_elementc - 1; + parsed_instruction->argv = malloc(sizeof(char*) * parsed_instruction->argc); + + /* Fail if memory could not be alloc'd for argv */ + if (parsed_instruction->argv == NULL) { + guac_error = GUAC_STATUS_NO_MEMORY; + guac_error_message = "Could not allocate memory for arguments of parsed instruction"; + free(parsed_instruction); + return NULL; + } + + /* Set opcode */ + parsed_instruction->opcode = strdup(socket->__instructionbuf_elementv[0]); + + /* Fail if memory could not be alloc'd for opcode */ + if (parsed_instruction->opcode == NULL) { + guac_error = GUAC_STATUS_NO_MEMORY; + guac_error_message = "Could not allocate memory for opcode of parsed instruction"; + free(parsed_instruction->argv); + free(parsed_instruction); + return NULL; + } + + + /* Copy element values to parsed instruction */ + for (j=0; jargc; j++) { + parsed_instruction->argv[j] = strdup(socket->__instructionbuf_elementv[j+1]); + + /* Free memory and fail if out of mem */ + if (parsed_instruction->argv[j] == NULL) { + guac_error = GUAC_STATUS_NO_MEMORY; + guac_error_message = "Could not allocate memory for single argument of parsed instruction"; + + /* Free all alloc'd argv values */ + while (--j >= 0) + free(parsed_instruction->argv[j]); + + free(parsed_instruction->opcode); + free(parsed_instruction->argv); + free(parsed_instruction); + return NULL; + } + + } + + /* Reset buffer */ + memmove(socket->__instructionbuf, socket->__instructionbuf + i, socket->__instructionbuf_used_length - i); + socket->__instructionbuf_used_length -= i; + socket->__instructionbuf_parse_start = 0; + socket->__instructionbuf_elementc = 0; + + /* Done */ + return parsed_instruction; + + } /* end if terminator */ + + /* Error if expected comma is not present */ + else if (terminator != ',') { + guac_error = GUAC_STATUS_BAD_ARGUMENT; + guac_error_message = "Element terminator of instruction was not ';' nor ','"; + return NULL; + } + + } /* end if element fully read */ + + /* Otherwise, read more data */ + else + break; + + } + + /* Error if length is non-numeric or does not end in a period */ + else { + guac_error = GUAC_STATUS_BAD_ARGUMENT; + guac_error_message = "Non-numeric character in element length"; + return NULL; + } + + } + + /* No instruction yet? Get more data ... */ + retval = guac_socket_select(socket, usec_timeout); + if (retval <= 0) + return NULL; + + /* If more data is available, fill into buffer */ + retval = __guac_fill_instructionbuf(socket); + + /* Error, guac_error already set */ + if (retval < 0) + return NULL; + + /* EOF */ + if (retval == 0) { + guac_error = GUAC_STATUS_NO_INPUT; + guac_error_message = "End of stream reached while reading instruction"; + return NULL; + } + + } + +} + + +guac_instruction* guac_instruction_expect(guac_socket* socket, int usec_timeout, + const char* opcode) { + + guac_instruction* instruction; + + /* Wait for data until timeout */ + if (guac_instruction_waiting(socket, usec_timeout) <= 0) + return NULL; + + /* Read available instruction */ + instruction = guac_instruction_read(socket, usec_timeout); + if (instruction == NULL) + return NULL; + + /* Validate instruction */ + if (strcmp(instruction->opcode, opcode) != 0) { + guac_error = GUAC_STATUS_BAD_STATE; + guac_error_message = "Instruction read did not have expected opcode"; + guac_instruction_free(instruction); + return NULL; + } + + /* Return instruction if valid */ + return instruction; + +} + + +void guac_instruction_free(guac_instruction* instruction) { + + int argc = instruction->argc; + + /* Free opcode */ + free(instruction->opcode); + + /* Free argv if set (may be NULL of argc is 0) */ + if (instruction->argv) { + + /* All argument values */ + while (argc > 0) + free(instruction->argv[--argc]); + + /* Free actual array */ + free(instruction->argv); + + } + + /* Free instruction */ + free(instruction); + +} + + +int guac_instruction_waiting(guac_socket* socket, int usec_timeout) { + + if (socket->__instructionbuf_used_length > 0) + return 1; + + return guac_socket_select(socket, usec_timeout); +} + diff --git a/libguac/src/protocol.c b/libguac/src/protocol.c index bfb7b1cd..d3e9d5ff 100644 --- a/libguac/src/protocol.c +++ b/libguac/src/protocol.c @@ -67,7 +67,6 @@ #endif #include "error.h" -#include "instruction.h" #include "layer.h" #include "palette.h" #include "protocol.h" @@ -368,296 +367,6 @@ int __guac_socket_write_length_png(guac_socket* socket, cairo_surface_t* surface } -/* Instruction I/O */ - -int __guac_fill_instructionbuf(guac_socket* socket) { - - int retval; - - /* Attempt to fill buffer */ - retval = read( - socket->fd, - socket->__instructionbuf + socket->__instructionbuf_used_length, - socket->__instructionbuf_size - socket->__instructionbuf_used_length - ); - - /* Set guac_error if recv() unsuccessful */ - if (retval < 0) { - guac_error = GUAC_STATUS_SEE_ERRNO; - guac_error_message = "Error filling instruction buffer"; - return retval; - } - - socket->__instructionbuf_used_length += retval; - - /* Expand buffer if necessary */ - if (socket->__instructionbuf_used_length > - socket->__instructionbuf_size / 2) { - - socket->__instructionbuf_size *= 2; - socket->__instructionbuf = realloc(socket->__instructionbuf, - socket->__instructionbuf_size); - } - - return retval; - -} - - -/* Returns new instruction if one exists, or NULL if no more instructions. */ -guac_instruction* guac_instruction_read(guac_socket* socket, - int usec_timeout) { - - int retval; - - /* Loop until a instruction is read */ - for (;;) { - - /* Length of element, in Unicode characters */ - int element_length = 0; - - /* Length of element, in bytes */ - int element_byte_length = 0; - - /* Current position within the element, in Unicode characters */ - int current_unicode_length = 0; - - /* Position within buffer */ - int i = socket->__instructionbuf_parse_start; - - /* Parse instruction in buffer */ - while (i < socket->__instructionbuf_used_length) { - - /* Read character from buffer */ - char c = socket->__instructionbuf[i++]; - - /* If digit, calculate element length */ - if (c >= '0' && c <= '9') - element_length = element_length * 10 + c - '0'; - - /* Otherwise, if end of length */ - else if (c == '.') { - - /* Calculate element byte length by walking buffer */ - while (i + element_byte_length < - socket->__instructionbuf_used_length - && current_unicode_length < element_length) { - - /* Get next byte */ - c = socket->__instructionbuf[i + element_byte_length]; - - /* Update byte and character lengths */ - element_byte_length += guac_utf8_charsize((unsigned) c); - current_unicode_length++; - - } - - /* Verify element is fully read */ - if (current_unicode_length == element_length) { - - /* Get element value */ - char* elementv = &(socket->__instructionbuf[i]); - - /* Get terminator, set null terminator of elementv */ - char terminator = elementv[element_byte_length]; - elementv[element_byte_length] = '\0'; - - /* Move to char after terminator of element */ - i += element_byte_length+1; - - /* Reset element length */ - element_length = - element_byte_length = - current_unicode_length = 0; - - /* As element has been read successfully, update - * parse start */ - socket->__instructionbuf_parse_start = i; - - /* Save element */ - socket->__instructionbuf_elementv[socket->__instructionbuf_elementc++] = elementv; - - /* Finish parse if terminator is a semicolon */ - if (terminator == ';') { - - guac_instruction* parsed_instruction; - int j; - - /* Allocate instruction */ - parsed_instruction = malloc(sizeof(guac_instruction)); - if (parsed_instruction == NULL) { - guac_error = GUAC_STATUS_NO_MEMORY; - guac_error_message = "Could not allocate memory for parsed instruction"; - return NULL; - } - - /* Init parsed instruction */ - parsed_instruction->argc = socket->__instructionbuf_elementc - 1; - parsed_instruction->argv = malloc(sizeof(char*) * parsed_instruction->argc); - - /* Fail if memory could not be alloc'd for argv */ - if (parsed_instruction->argv == NULL) { - guac_error = GUAC_STATUS_NO_MEMORY; - guac_error_message = "Could not allocate memory for arguments of parsed instruction"; - free(parsed_instruction); - return NULL; - } - - /* Set opcode */ - parsed_instruction->opcode = strdup(socket->__instructionbuf_elementv[0]); - - /* Fail if memory could not be alloc'd for opcode */ - if (parsed_instruction->opcode == NULL) { - guac_error = GUAC_STATUS_NO_MEMORY; - guac_error_message = "Could not allocate memory for opcode of parsed instruction"; - free(parsed_instruction->argv); - free(parsed_instruction); - return NULL; - } - - - /* Copy element values to parsed instruction */ - for (j=0; jargc; j++) { - parsed_instruction->argv[j] = strdup(socket->__instructionbuf_elementv[j+1]); - - /* Free memory and fail if out of mem */ - if (parsed_instruction->argv[j] == NULL) { - guac_error = GUAC_STATUS_NO_MEMORY; - guac_error_message = "Could not allocate memory for single argument of parsed instruction"; - - /* Free all alloc'd argv values */ - while (--j >= 0) - free(parsed_instruction->argv[j]); - - free(parsed_instruction->opcode); - free(parsed_instruction->argv); - free(parsed_instruction); - return NULL; - } - - } - - /* Reset buffer */ - memmove(socket->__instructionbuf, socket->__instructionbuf + i, socket->__instructionbuf_used_length - i); - socket->__instructionbuf_used_length -= i; - socket->__instructionbuf_parse_start = 0; - socket->__instructionbuf_elementc = 0; - - /* Done */ - return parsed_instruction; - - } /* end if terminator */ - - /* Error if expected comma is not present */ - else if (terminator != ',') { - guac_error = GUAC_STATUS_BAD_ARGUMENT; - guac_error_message = "Element terminator of instruction was not ';' nor ','"; - return NULL; - } - - } /* end if element fully read */ - - /* Otherwise, read more data */ - else - break; - - } - - /* Error if length is non-numeric or does not end in a period */ - else { - guac_error = GUAC_STATUS_BAD_ARGUMENT; - guac_error_message = "Non-numeric character in element length"; - return NULL; - } - - } - - /* No instruction yet? Get more data ... */ - retval = guac_socket_select(socket, usec_timeout); - if (retval <= 0) - return NULL; - - /* If more data is available, fill into buffer */ - retval = __guac_fill_instructionbuf(socket); - - /* Error, guac_error already set */ - if (retval < 0) - return NULL; - - /* EOF */ - if (retval == 0) { - guac_error = GUAC_STATUS_NO_INPUT; - guac_error_message = "End of stream reached while reading instruction"; - return NULL; - } - - } - -} - - -guac_instruction* guac_instruction_expect(guac_socket* socket, int usec_timeout, - const char* opcode) { - - guac_instruction* instruction; - - /* Wait for data until timeout */ - if (guac_instruction_waiting(socket, usec_timeout) <= 0) - return NULL; - - /* Read available instruction */ - instruction = guac_instruction_read(socket, usec_timeout); - if (instruction == NULL) - return NULL; - - /* Validate instruction */ - if (strcmp(instruction->opcode, opcode) != 0) { - guac_error = GUAC_STATUS_BAD_STATE; - guac_error_message = "Instruction read did not have expected opcode"; - guac_instruction_free(instruction); - return NULL; - } - - /* Return instruction if valid */ - return instruction; - -} - - -void guac_instruction_free(guac_instruction* instruction) { - - int argc = instruction->argc; - - /* Free opcode */ - free(instruction->opcode); - - /* Free argv if set (may be NULL of argc is 0) */ - if (instruction->argv) { - - /* All argument values */ - while (argc > 0) - free(instruction->argv[--argc]); - - /* Free actual array */ - free(instruction->argv); - - } - - /* Free instruction */ - free(instruction); - -} - - -int guac_instruction_waiting(guac_socket* socket, int usec_timeout) { - - if (socket->__instructionbuf_used_length > 0) - return 1; - - return guac_socket_select(socket, usec_timeout); -} - - guac_timestamp guac_timestamp_current() { #ifdef HAVE_CLOCK_GETTIME