diff options
Diffstat (limited to 'pg_array.c')
-rw-r--r-- | pg_array.c | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/pg_array.c b/pg_array.c new file mode 100644 index 0000000..7b1cd23 --- /dev/null +++ b/pg_array.c @@ -0,0 +1,151 @@ +/*------------------------------------------------------------------------- + * pg_array.c + * + * Functions for libpq array manipulation + * This is intended to contain functions to manage libpq arrays. + * This isn't likely to be comprehensive + * + * Copyright (c) 2004-2007, PostgreSQL Global Development Group + * Author: Joshua Tolley + * + * $Id: pg_array.c,v 1.6 2007/09/13 14:20:43 h-saito Exp $ + * + *------------------------------------------------------------------------- + */ + +#include "pg_array.h" + +/* Text array format consists of leading and trailing curly braces. Values containing + * characters that might be confusing (backslashes, double-quotes, etc.) are double- + * quoted, and confusing characters are backslash-escaped */ + +/* pg_text_array_parse() +* Takes an input string as returned by PQgetvalue(), parses it as a text array, and + * builds an array of strings, which it returns, or NULL on error. pg_text_array also + * sets len to the number of strings in the array. THE USER IS RESPONSIBLE + * FOR FREEING CHAR ***OUTPUT, perhaps by calling pg_text_array_free. */ + +/* TODO: Add debugging/logging */ + +char ** pg_text_array_parse(char *input, int *len) { + char **results = NULL, **tmp; + char *incursor, *outcursor, *output, *start_element; + int escaped = 0, done_single_element = 0, done_all_elements = 0, i, + cursorlen, inputlen, elements = 0, outputlen, error = 0, in_quotes = 0; + + inputlen = strlen(input); + if (!inputlen || inputlen < 3 || SIZE_MAX / inputlen < sizeof(char*)) return NULL; + incursor = input + 1; + while (!done_all_elements) { + in_quotes = 0; + if (*incursor == '"') { + incursor++; + in_quotes = 1; + } +/* else if (*incursor == ',') break; */ + start_element = incursor; + outcursor = strpbrk(incursor, ",}"); + if (!outcursor) return NULL; + /* FACT: outcursor - incursor >= 1 */ + outputlen = outcursor - incursor + 1; + output = calloc(outputlen, sizeof(char)); + if (!output) return NULL; + outcursor = output; + done_single_element = 0; + while (!done_single_element) { + if (!escaped) { + switch (*incursor) { + case '}': + done_single_element = 1; + done_all_elements = 1; + break; + case ',': + if (!in_quotes) done_single_element = 1; + break; + case '"': + in_quotes = 0; + done_single_element = 1; + incursor++; + if (*incursor == '}') done_all_elements = 1; + break; + } + } + if (!done_single_element) { + if (!escaped && *incursor == '\\') escaped = 1; + else { + if (outcursor - output >= outputlen) { + cursorlen = outcursor - output; + outcursor = strpbrk(incursor + 1, ",}"); + if (!outcursor) { + error = 1; + break; + } + outputlen = outcursor - start_element; + outcursor = realloc(output, outputlen * sizeof(char)); + if (!outcursor) { + error = 1; + break; + } + output = outcursor; + outcursor = output + cursorlen; + } + if (escaped) { + escaped = 0; + *outcursor = *incursor; + outcursor++; + } + else { + *outcursor = *incursor; + outcursor++; + } + } + } + if (done_single_element) { + elements++; + memset(outcursor, 0, outputlen - (outcursor - output)); + if (results) { + tmp = realloc(results, elements * sizeof(char*)); + if (!tmp) { + error = 1; + break; + } + results = tmp; + } + else { + results = calloc(1, sizeof(char*)); + if (!results) { + error = 1; + break; + } + } + results[elements - 1] = output; + } + if (!done_all_elements) { + incursor++; + if (incursor - input >= inputlen) { + error = 1; + break; + } + } + } + if (error) break; + } + if (error) { + for (i = 0; i < elements; i++) + free(results[i]); + free(results); + free(output); + return NULL; + } + *len = elements; + return results; +} + +/* pg_text_array_free() + * Frees an array of strings such as is returned by pg_text_array_parse() */ +void pg_text_array_free(char **array, int len) { + int i; + for (i = 0; i < len; i++) + free(array[i]); + free(array); +} |