diff options
Diffstat (limited to 'lib/qstring.c')
-rw-r--r-- | lib/qstring.c | 434 |
1 files changed, 359 insertions, 75 deletions
diff --git a/lib/qstring.c b/lib/qstring.c index f847e0b0..a3dc95cd 100644 --- a/lib/qstring.c +++ b/lib/qstring.c @@ -35,29 +35,18 @@ * Returns: address of qstring * * NB: assumes initialising a new structure. If not, then caller should - * use qs_reset() or qs_set_empty(). + * use qs_reset() or qs_clear(). */ extern qstring qs_init_new(qstring qs, size_t len) { if (qs == NULL) - qs = XCALLOC(MTYPE_QSTRING, sizeof(qstring_t)) ; + qs = qs_new() ; else - memset(qs, 0, sizeof(qstring_t)) ; - - /* Zeroising has set: - * - * body = NULL -- no body - * size = 0 -- no body - * - * len = 0 - * cp = 0 - * - * Nothing more to do unless initial size != 0 - */ + memset(qs, 0, sizeof(qstring_t)) ; /* see qs_new() */ if (len != 0) - qs_alloc(qs, len) ; + return qs_make_to_length(qs, len) ; return qs ; } ; @@ -65,48 +54,102 @@ qs_init_new(qstring qs, size_t len) /*------------------------------------------------------------------------------ * Allocate or reallocate so that string is big enough for the given length. * - * Allocates to 16 byte boundaries. + * Allocate qstring if required. Returns with a body with size > 0. * - * Returns: the number of bytes *allocated*, which includes the byte for - * possible trailing '\0'. + * Allocates to 16 byte boundaries with space for '\0' beyond given length. * - * NB: allocates EXTRA space for trailing '\0' beyond given length. + * Returns: address of qstring + * + * NB: allocates new body if the size == 0. + * + * If the qstring is a "dummy", its contents are now copied to the new + * real qstring body -- up to a maximum of the new length. */ -extern size_t -qs_alloc(qstring qs, size_t len) +extern qstring +qs_make_to_length(qstring qs, size_t len) { - len = (len + 0x10) & ~(size_t)(0x10 - 1) ; + size_t size = (len + 0x10) & ~(size_t)(0x10 - 1) ; - if (qs->body == NULL) - { - assert(qs->size == 0) ; - qs->size = len ; - qs->body = XMALLOC(MTYPE_QSTRING_BODY, qs->size) ; - } - else + if (qs == NULL) + qs = qs_new() ; + + if (size > qs->size) { - assert(qs->size > 0) ; - qs->size *= 2 ; - if (qs->size < len) - qs->size = len ; - qs->body = XREALLOC(MTYPE_QSTRING_BODY, qs->body, qs->size) ; - } ; + if (qs->size == 0) + { + void* old ; + old = qs->body ; + + qs->size = size ; + qs->body = XMALLOC(MTYPE_QSTRING_BODY, qs->size) ; + + if ((qs->len != 0) && (old != NULL)) + memcpy(qs->body, old, (qs->len <= len) ? qs->len : len) ; + } + else + { + qs->size *= 2 ; + if (qs->size < size) + qs->size = size ; + qs->body = XREALLOC(MTYPE_QSTRING_BODY, qs->body, qs->size) ; + } ; + }; - return qs->size ; + return qs ; +} ; + +/*------------------------------------------------------------------------------ + * Add 'n' to the current string length, allocating or extending the body as + * required. + * + * Allocate qstring if required. Returns with a body with size > 0. + * + * Allocates to 16 byte boundaries with space for '\0' beyond new length. + * + * Returns: address of qstring + * + * also: sets char** p_ep to point at the *end* of the new len. + * + * NB: allocates new body if the size == 0. + * + * If the qstring is a "dummy", its contents are now copied to the new + * real qstring body -- up to a maximum of the new length. + */ +extern qstring +qs_add_len(qstring qs, size_t n, char** p_ep) +{ + size_t len ; + len = (qs != NULL) ? qs->len + n : n ; + + qs = qs_make_to_length(qs, len) ; + + qs->len = len ; + + *p_ep = (char*)qs->body + len ; + + return qs ; } ; /*------------------------------------------------------------------------------ * Free body of qstring -- zeroise size, len and cp + * + * Does nothing if qstring is NULL + * + * NB: frees the body if the size != 0. So, a "dummy" qstring will not retain + * the old body. */ extern void qs_free_body(qstring qs) { - if (qs->body != NULL) - XFREE(MTYPE_QSTRING_BODY, qs->body) ; /* sets qs->body = NULL */ + if (qs != NULL) + { + if (qs->size != 0) + XFREE(MTYPE_QSTRING_BODY, qs->body) ; /* sets qs->body = NULL */ - qs->size = 0 ; - qs->len = 0 ; - qs->cp = 0 ; + qs->size = 0 ; + qs->len = 0 ; + qs->cp = 0 ; + } ; } ; /*------------------------------------------------------------------------------ @@ -115,73 +158,102 @@ qs_free_body(qstring qs) * If not freeing the structure, zeroise size, len and cp -- qs_free_body() * * Returns: NULL if freed the structure - * address of structure, otherwise + * address of structure (if any), otherwise + * + * NB: frees the body if the size != 0. So, a "dummy" qstring will not retain + * the old body. */ extern qstring qs_reset(qstring qs, int free_structure) { - if (qs->body != NULL) - XFREE(MTYPE_QSTRING_BODY, qs->body) ; /* sets qs->body = NULL */ - - if (free_structure) - XFREE(MTYPE_QSTRING, qs) ; /* sets qs = NULL */ - else + if (qs != NULL) { - qs->size = 0 ; - qs->len = 0 ; - qs->cp = 0 ; - } ; + if (qs->size != 0) + XFREE(MTYPE_QSTRING_BODY, qs->body) ; /* sets qs->body = NULL */ + if (free_structure) + XFREE(MTYPE_QSTRING, qs) ; /* sets qs = NULL */ + else + { + qs->size = 0 ; + qs->len = 0 ; + qs->cp = 0 ; + } ; + } ; return qs ; } ; /*============================================================================== - * printf(0 and vprintf() type functions + * printf() and vprintf() type functions */ /*------------------------------------------------------------------------------ * Formatted print to qstring -- cf printf() + * + * Allocate qstring if required. + * + * Returns: address of qstring if OK + * NULL if failed (unlikely though that is) */ -extern int +extern qstring qs_printf(qstring qs, const char* format, ...) { va_list args; - int result ; va_start (args, format); - result = qs_vprintf(qs, format, args); + qs = qs_vprintf(qs, format, args); va_end (args); - return result; + return qs; } ; /*------------------------------------------------------------------------------ * Formatted print to qstring -- cf vprintf() * - * Note that vsnprintf() returns the length of what it would like to have - * produced, if it had the space. That length does not include the trailing - * '\0'. + * Allocate qstring if required. * - * Also note that given a zero length the string address may be NULL, and the - * result is still the length required. + * Returns: address of qstring if OK + * NULL if failed (unlikely though that is) */ -extern int +extern qstring qs_vprintf(qstring qs, const char *format, va_list args) { va_list ac ; - int len ; + int len ; + qstring qqs ; + + qqs = qs ; + if (qs == NULL) + qs = qs_new() ; while (1) { + /* Note that vsnprintf() returns the length of what it would like to have + * produced, if it had the space. That length does not include the + * trailing '\0'. + * + * Also note that given a zero length the string address may be NULL, and + * the result is still the length required. + */ va_copy(ac, args); qs->len = len = vsnprintf (qs->body, qs->size, format, ac) ; va_end(ac); + if (len < 0) + break ; + if (len < (int)qs->size) - return len ; /* quit if done (or error) */ + return qs ; - qs_alloc(qs, len) ; + qs_make_to_length(qs, len) ; } ; + + if (qqs == NULL) + qs_reset_free(qs) ; /* discard what was allocated */ + else + qs->len = 0 ; + + return NULL ; } ; /*============================================================================== @@ -191,37 +263,249 @@ qs_vprintf(qstring qs, const char *format, va_list args) /*------------------------------------------------------------------------------ * Set qstring to be copy of the given string. * + * Allocates a qstring, if required. + * * Sets qs->len to the length of the string (excluding trailing '\0') * - * NB: if stc == NULL, sets qstring to be zero length string. + * NB: if src == NULL, sets qstring to be zero length string. + * + * Returns: address of the qstring copied to. + * + * NB: if copying to a dummy qstring, the old body is simply discarded. */ -extern size_t +extern qstring qs_set(qstring qs, const char* src) { - qs_set_len(qs, (src != NULL) ? strlen(src) : 0) ; + qs = qs_set_len(qs, (src != NULL) ? strlen(src) : 0) ; if (qs->len != 0) memcpy(qs->body, src, qs->len + 1) ; else *((char*)qs->body) = '\0' ; - return qs->len ; + return qs ; } ; /*------------------------------------------------------------------------------ * Set qstring to be leading 'n' bytes of given string. * - * NB: src string MUST be at least that long. + * Allocates qstring if required. + * + * Inserts '\0' terminator after the 'n' bytes copied. + * + * Returns: address of the qstring copied to. * - * NB: src may not be NULL unless len == 0. + * NB: src string MUST be at least 'n' bytes long. + * + * NB: src may not be NULL unless n == 0. + * + * NB: if copying to a dummy qstring, the old body is simply discarded. */ -extern size_t +extern qstring qs_set_n(qstring qs, const char* src, size_t n) { - qs_need(qs, n) ; /* sets qs->len */ + qs = qs_set_len(qs, n) ; /* ensures have body > n */ if (n != 0) memcpy(qs->body, src, n) ; *((char*)qs->body + n) = '\0' ; - return n ; + return qs ; +} ; + +/*------------------------------------------------------------------------------ + * Append given string to a qstring. + * + * Allocates a qstring, if required. + * + * Sets qs->len to the length of the result (excluding trailing '\0') + * + * NB: if src == NULL, appends nothing -- but result will be '\0' terminated. + * + * Returns: address of the qstring copied to. + * + * NB: if copying to a dummy qstring, the old body is simply discarded. + */ +extern qstring qs_append(qstring qs, const char* src) +{ + size_t n ; + char* ep ; + + n = (src != NULL) ? strlen(src) : 0 ; + + qs = qs_add_len(qs, n, &ep) ; + ep = (char*)qs->body + qs->len ; + + if (n != 0) + memcpy(ep - n, src, n + 1) ; + else + *ep = '\0' ; + + return qs ; +} ; + +/*------------------------------------------------------------------------------ + * Set qstring to be leading 'n' bytes of given string. + * + * Allocates qstring if required. + * + * Returns: address of the qstring copied to. + * + * NB: src string MUST be at least 'n' bytes long. + * + * NB: src may not be NULL unless n == 0. + * + * NB: if n == 0, appends nothing -- but result will be '\0' terminated. + * + * NB: if copying to a dummy qstring, the old body is simply discarded. + * + * NB: if copying to a dummy qstring, the old body is simply discarded. + */ +extern qstring +qs_append_n(qstring qs, const char* src, size_t n) +{ + char* ep ; + + qs = qs_add_len(qs, n, &ep) ; + + if (n != 0) + memcpy(ep - n, src, n) ; + + *ep = '\0' ; + + return qs ; +} ; + +/*------------------------------------------------------------------------------ + * Copy one qstring to another + * + * If both are NULL, returns NULL. + * + * Otherwise if dst is NULL, creates a new qstring. + * + * Sets dst: body = copy of src->len bytes of src->body -- '\0' terminated. + * cp = src->cp + * len = src->len + * + * Where a NULL src has zero cp and len. + * + * If not NULL, the destination is guaranteed to have a body, and that will be + * '\0' terminated. + * + * Returns: the destination qstring + * + * NB: if copying to a dummy qstring, the old body is simply discarded. + */ +extern qstring +qs_copy(qstring dst, qstring src) +{ + size_t n ; + + if (src == NULL) + { + if (dst == NULL) + return dst ; + + n = 0 ; + dst->cp = 0 ; + } + else + { + if (dst == NULL) + dst = qs_new() ; + + n = src->len ; + dst->cp = src->cp ; + } ; + + qs_set_len(dst, n) ; + + if (n > 0) + memcpy(dst->body, src->body, n) ; + + *((char*)dst->body + n) = '\0' ; + + return dst ; +} ; + +/*------------------------------------------------------------------------------ + * Compare significant parts of two qstrings. + * + * By significant, mean excluding leading/trailing isspace() and treating + * multiple isspace() as single isspace(). + * + * Compares the 'len' portions of the strings. + * + * If either is NULL, it is deemed to be an empty string. + * + * Returns: -1 => a < b + * 0 => a == b + * +1 => a > b + */ +extern int +qs_cmp_sig(qstring a, qstring b) +{ + const unsigned char* p_a ; + const unsigned char* e_a ; + const unsigned char* p_b ; + const unsigned char* e_b ; + + /* Set up pointers and dispense with leading and trailing isspace() + * + * Dummy up if NULL + */ + if (a != NULL) + { + p_a = a->body ; + e_a = p_a + a->len ; + + while ((p_a < e_a) && isspace(*p_a)) + ++p_a ; + while ((p_a < e_a) && isspace(*(e_a - 1))) + --e_a ; + } + else + { + p_a = NULL ; + e_a = NULL ; + } + + if (b != NULL) + { + p_b = b->body ; + e_b = p_b + b->len ; + + while ((p_b < e_b) && isspace(*p_b)) + ++p_b ; + while ((p_b < e_b) && isspace(*(e_b - 1))) + --e_b ; + } + else + { + p_b = NULL ; + e_b = NULL ; + } ; + + /* Now set about finding the first difference */ + while ((p_a != e_a) && (p_b != e_b)) + { + if (isspace(*p_a) && isspace(*p_b)) + { + do { ++p_a ; } while isspace(*p_a) ; + do { ++p_b ; } while isspace(*p_b) ; + } ; + + if (*p_a != *p_b) + return (*p_a < *p_b) ? -1 : +1 ; + + ++p_a ; + ++p_b ; + } ; + + /* No difference before ran out of one or both */ + if (p_a != e_a) + return +1 ; + else if (p_b != e_b) + return -1 ; + else + return 0 ; } ; |