diff options
Diffstat (limited to 'lib/qfstring.c')
-rw-r--r-- | lib/qfstring.c | 448 |
1 files changed, 310 insertions, 138 deletions
diff --git a/lib/qfstring.c b/lib/qfstring.c index 35708640..eda2aa9a 100644 --- a/lib/qfstring.c +++ b/lib/qfstring.c @@ -18,58 +18,102 @@ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ +#include "misc.h" + +#include <stdio.h> #include "qfstring.h" -#include "zassert.h" -#include <stdbool.h> /*============================================================================== */ /*------------------------------------------------------------------------------ - * Initialise qf_str -- to given size (which includes the '\0') + * Initialise qf_str -- to given size, zero offset and zero overflow. * - * Sets pointers and terminates an empty string with one byte reserved for the - * terminating '\0'. + * Note that does not terminate the string -- that must be done separately. * * This operation is async-signal-safe. */ extern void -qfs_init(qf_str qfs, char* str, size_t size) +qfs_init(qf_str qfs, char* str, uint size) { - assert(size > 0) ; + qfs->str = str ; + qfs->ptr = str ; + qfs->end = str + size ; + qfs->offset = 0 ; + qfs->overflow = 0 ; +} ; - qfs->str = str ; - qfs->end = str + size - 1 ; +/*------------------------------------------------------------------------------ + * Initialise qf_str -- to given size, with given offset and zero overflow. + * + * Note that does not terminate the string -- that must be done separately. + * + * This operation is async-signal-safe. + */ +extern void +qfs_init_offset(qf_str qfs, char* str, uint size, uint offset) +{ + qfs->str = str ; + qfs->ptr = str ; + qfs->end = str + size ; + qfs->offset = offset ; + qfs->overflow = 0 ; +} ; - *str = '\0' ; - qfs->ptr = str ; +/*------------------------------------------------------------------------------ + * Reset given qf_str -- with the given offset and zero overflow. + * + * Sets ptr back to the start of the string and set the given offset. + * + * This operation is async-signal-safe. + */ +extern void +qfs_reset_offset(qf_str qfs, uint offset) +{ + qfs->ptr = qfs->str ; + qfs->offset = offset ; + qfs->overflow = 0 ; } ; /*------------------------------------------------------------------------------ - * Initialise qf_str which already contains string -- to given size (which - * includes the '\0') + * Initialise qf_str which already contains string -- to given size with zero + * overflow. * * This may be used to prepare for appending to a buffer which already contains * something. * - * Sets pointers, setting the write pointer to the existing terminating '\0'. + * Sets pointers, setting the write pointer to the existing '\0'. * * This operation is async-signal-safe. + * + * NB: it is a mistake if the size given is less than the length of the + * string (excluding the trailing '\0'). */ extern void -qfs_init_as_is(qf_str qfs, char* str, size_t size) +qfs_init_as_is(qf_str qfs, char* str, uint size) { assert(size > 0) ; - qfs->str = str ; - qfs->end = str + size - 1 ; + qfs->str = str ; + qfs->end = str + size ; + qfs->offset = 0 ; + qfs->overflow = 0 ; + + while (*str != '\0') + ++str ; - qfs->ptr = strchr(str, '\0') ; + qfs->ptr = str ; /* point at '\0' */ + + assert(qfs->ptr <= qfs->end) ; } ; /*------------------------------------------------------------------------------ - * Terminate string with the given string. + * Terminate string with the given string if given length (which may include + * a '\0'). + * + * This is for when the qstring has overflowed, and wish to indicate that at + * the end -- so takes no notice of offset. * * If necessary, characters are discarded from the end of the string in order * to fit in the terminating stuff. @@ -81,34 +125,25 @@ qfs_init_as_is(qf_str qfs, char* str, size_t size) * This operation is async-signal-safe. */ extern void -qfs_term(qf_str qfs, const char* src) +qfs_term_string(qf_str qfs, const char* src, uint n) { - int len ; - int excess ; + uint h ; - if ((src == NULL) || (*src == '\0')) - { - *qfs->ptr = '\0' ; /* should be true anyway */ - return ; - } ; + h = qfs->end - qfs->ptr ; /* space available */ - len = strlen(src) ; - excess = qfs_len(qfs) - len ; - if (excess > 0) + if (h < n) { - if (excess <= (qfs->ptr - qfs->str)) - qfs->ptr -= excess ; - else + h = qfs->end - qfs->str ; /* total space */ + if (h < n) { - int want = len ; - len = qfs->end - qfs->str ; /* take what can... */ - src += (want - len) ; /* ... from the end */ - qfs->ptr = qfs->str ; + src += n - h ; /* past what will not fit */ + n = h ; } ; + qfs->ptr = qfs->end - n ; } ; - memcpy(qfs->ptr, src, len + 1) ; /* include the '\0' */ - qfs->ptr += len ; + while (n--) + *qfs->ptr++ = *src++ ; } ; /*============================================================================== @@ -120,26 +155,29 @@ qfs_term(qf_str qfs, const char* src) * * May append nothing at all ! * - * This operation is async-signal-safe. + * This operation is async-signal-safe. Takes into account the offset, and + * adds up any overflow */ extern void qfs_append(qf_str qfs, const char* src) { - int n ; - - if ((src == NULL) || (*src == '\0')) + if (src == NULL) return ; - n = strlen(src) ; - - if (n > qfs_left(qfs)) - n = qfs_left(qfs) ; - - if (n == 0) - return ; + while (qfs->offset > 0) + { + if (*src++ == '\0') + return ; + --qfs->offset ; + } ; - memcpy(qfs->ptr, src, n + 1) ; - qfs->ptr += n ; + while (*src != '\0') + { + if (qfs->ptr < qfs->end) + *qfs->ptr++ = *src++ ; + else + ++qfs->overflow ; + } ; } ; /*------------------------------------------------------------------------------ @@ -148,43 +186,76 @@ qfs_append(qf_str qfs, const char* src) * * May append nothing at all ! * - * This operation is async-signal-safe. + * src may be NULL iff n == 0 + * + * This operation is async-signal-safe. Takes into account the offset, and + * adds up any overflow */ extern void -qfs_append_n(qf_str qfs, const char* src, size_t n) +qfs_append_n(qf_str qfs, const char* src, uint n) { - if ((int)n > qfs_left(qfs)) - n = qfs_left(qfs) ; + uint h ; - if (n <= 0) - return ; + if (qfs->offset > 0) + { + if (qfs->offset >= n) + { + qfs->offset -= n ; + return ; + } ; + + src += qfs->offset ; + n -= qfs->offset ; + + qfs->offset = 0 ; + } ; - memcpy(qfs->ptr, src, n) ; - qfs->ptr += n ; + h = (qfs->end - qfs->ptr) ; + if (n > h) + { + qfs->overflow += n - h ; + n = h ; + } ; - *qfs->ptr = '\0' ; + while (n--) + *qfs->ptr++ = *src++ ; } ; /*------------------------------------------------------------------------------ - * Append upto 'n' copies of the given character to the qf_str + * Append upto 'n' copies of the given character to the qf_str. * * May append nothing at all ! * - * This operation is async-signal-safe. + * This operation is async-signal-safe. Takes into account the offset, and + * adds up any overflow */ extern void -qfs_append_ch_x_n(qf_str qfs, char ch, size_t n) +qfs_append_ch_x_n(qf_str qfs, char ch, uint n) { - if ((int)n > qfs_left(qfs)) - n = qfs_left(qfs) ; + uint h ; - if (n <= 0) - return ; + if (qfs->offset > 0) + { + if (qfs->offset >= n) + { + qfs->offset -= n ; + return ; + } ; + + n -= qfs->offset ; + + qfs->offset = 0 ; + } ; + + h = (qfs->end - qfs->ptr) ; + if (n > h) + { + qfs->overflow += n - h ; + n = h ; + } ; while (n--) *qfs->ptr++ = ch ; - - *qfs->ptr = '\0' ; } ; /*------------------------------------------------------------------------------ @@ -197,19 +268,13 @@ qfs_append_ch_x_n(qf_str qfs, char ch, size_t n) * * May append nothing at all ! * - * This operation is async-signal-safe. + * This operation is async-signal-safe. Takes into account the offset, and + * adds up any overflow */ extern void qfs_append_justified(qf_str qfs, const char* src, int width) { - size_t n ; - - if ((src == NULL) || (*src == '\0')) - n = 0 ; - else - n = strlen(src) ; - - qfs_append_justified_n(qfs, src, n, width) ; + qfs_append_justified_n(qfs, src, qfs_strlen(src), width) ; } ; /*------------------------------------------------------------------------------ @@ -222,10 +287,11 @@ qfs_append_justified(qf_str qfs, const char* src, int width) * * May append nothing at all ! * - * This operation is async-signal-safe. + * This operation is async-signal-safe. Takes into account the offset, and + * adds up any overflow */ extern void -qfs_append_justified_n(qf_str qfs, const char* src, size_t n, int width) +qfs_append_justified_n(qf_str qfs, const char* src, uint n, int width) { if ((int)n >= abs(width)) width = 0 ; @@ -239,6 +305,23 @@ qfs_append_justified_n(qf_str qfs, const char* src, size_t n, int width) qfs_append_ch_x_n(qfs, ' ', - width - n) ; } ; +/*------------------------------------------------------------------------------ + * Append single character. + * + * This operation is async-signal-safe. Takes into account the offset, and + * adds up any overflow + */ +inline static void +qfs_append_ch(qf_str qfs, char ch) +{ + if (qfs->offset > 0) + --qfs->offset ; + else if (qfs->ptr < qfs->end) + *qfs->ptr++ = ch ; + else + ++qfs->overflow ; +} ; + /*============================================================================== * Number conversion */ @@ -252,7 +335,8 @@ qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags, * * Result is appended to the given qf_str. * - * This operation is async-signal-safe. + * This operation is async-signal-safe. Takes into account the offset, and + * adds up any overflow */ extern void qfs_signed(qf_str qfs, intmax_t s_val, enum pf_flags flags, @@ -280,7 +364,8 @@ qfs_signed(qf_str qfs, intmax_t s_val, enum pf_flags flags, * * Result is appended to the given qf_str. * - * This operation is async-signal-safe. + * This operation is async-signal-safe. Takes into account the offset, and + * adds up any overflow */ extern void qfs_unsigned(qf_str qfs, uintmax_t u_val, enum pf_flags flags, @@ -294,7 +379,8 @@ qfs_unsigned(qf_str qfs, uintmax_t u_val, enum pf_flags flags, * * Result is appended to the given qf_str. * - * This operation is async-signal-safe. + * This operation is async-signal-safe. Takes into account the offset, and + * adds up any overflow */ extern void qfs_pointer(qf_str qfs, void* p_val, enum pf_flags flags, @@ -365,7 +451,8 @@ qfs_pointer(qf_str qfs, void* p_val, enum pf_flags flags, * characters are to be generated -- ie no: pf_plus, pf_space, pf_zeros, * or pf_alt (with pf_hex) -- then nothing is generated. * - * This operation is async-signal-safe. + * This operation is async-signal-safe. Takes into account the offset, and + * adds up any overflow */ static void qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags, @@ -447,31 +534,49 @@ qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags, /* Set up any required sign and radix prefix */ if ((flags & pf_unsigned) || (sign == 0)) - sign_str = "" ; + { + sign_str = "" ; + sign_len = 0 ; + } else if (sign < 0) - sign_str = "-" ; + { + sign_str = "-" ; + sign_len = 1 ; + } else if (flags & pf_plus) - sign_str = "+" ; + { + sign_str = "+" ; + sign_len = 1 ; + } else if (flags & pf_space) - sign_str = " " ; + { + sign_str = " " ; + sign_len = 1 ; + } else - sign_str = "" ; - - sign_len = strlen(sign_str) ; + { + sign_str = "" ; + sign_len = 0 ; + } ; radix_str = "" ; + radix_len = 0 ; + if (flags & pf_alt) { if (flags & pf_hex) - radix_str = (flags & pf_uc) ? "0X" : "0x" ; + { + confirm(pf_uc != 0) ; + radix_str = (flags & pf_uc) ? "0X" : "0x" ; + radix_len = 2 ; + } else if ((flags & pf_oct) && (val != 0)) - radix_str = "0" ; - - confirm(pf_uc != 0) ; + { + radix_str = "0" ; + radix_len = 1 ; + } ; } ; - radix_len = strlen(radix_str) ; - /* Turn off zero fill if left justify (width < 0) */ if (width < 0) flags &= ~pf_zeros ; @@ -496,8 +601,7 @@ qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags, digits = (flags & pf_uc) ? uc : lc ; confirm(pf_uc != 0) ; - e = p = num + sizeof(num) - 1 ; - *p = '\0' ; + e = p = num + sizeof(num) ; v = val ; do { @@ -545,6 +649,7 @@ qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags, while (t--) *cp++ = *cq++ ; *cp++ = comma ; + t = interval ; } ; assert(len == (e - p)) ; @@ -688,42 +793,51 @@ static enum pf_phase qfs_arg_float(qf_str qfs, va_list* p_va, const char* start, size_t flen, enum arg_num_type ant) ; /*------------------------------------------------------------------------------ - * Formatted print to qf_str -- cf printf() + * Formatted print to qf_str -- cf printf() -- appends to the qf_str. * * This operation is async-signal-safe -- EXCEPT for floating point values. + * Takes into account the offset, and adds up any overflow. + * + * Returns: the resulting length of the qf_str. */ -extern void +extern uint qfs_printf(qf_str qfs, const char* format, ...) { va_list va ; + uint did ; va_start (va, format); - qfs_vprintf(qfs, format, va); + did = qfs_vprintf(qfs, format, va); va_end (va); + + return did ; } ; /*------------------------------------------------------------------------------ - * Formatted print to qf_str -- cf vprintf() + * Formatted print to qf_str -- cf vprintf() -- appends to the qf_str. * * This operation is async-signal-safe -- EXCEPT for floating point values. + * Takes into account the offset, and adds up any overflow. * * Operates on a copy of the va_list -- so the original is *unchanged*. + * + * Returns: the resulting length of the qf_str. */ -extern void +extern uint qfs_vprintf(qf_str qfs, const char *format, va_list va) { va_list vac ; if (format == NULL) - return ; + return qfs_len(qfs) ; va_copy(vac, va) ; - while ((qfs->ptr < qfs->end) && (*format != '\0')) + while (*format != '\0') { /* Have space for one byte and current format byte is not '\0' */ if (*format != '%') - *qfs->ptr++ = *format++ ; + qfs_append_ch(qfs, *format++) ; else { const char* start = format++ ; /* start points at the '%' ... @@ -744,8 +858,12 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va) { case '%': /* %% only */ if (phase == pfp_null) - *qfs->ptr++ = '%' ; - phase = (phase == pfp_null) ? pfp_done : pfp_failed ; + { + qfs_append_ch(qfs, '%') ; + phase = pfp_done ; + } + else + phase = pfp_failed ; break ; case '\'': @@ -940,14 +1058,14 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va) if (phase == pfp_failed) { format = start ; /* back to the start */ - *qfs->ptr++ = *format++ ; /* copy the '%' */ + qfs_append_ch(qfs, *format++) ; } ; } ; } ; - *qfs->ptr = '\0' ; - va_end(vac) ; + + return qfs_len(qfs) ; } ; /*------------------------------------------------------------------------------ @@ -968,7 +1086,8 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va) * pf_unsigned * pf_ptr * - * This operation is async-signal-safe. + * This operation is async-signal-safe. Takes into account the offset, and + * adds up any overflow */ static enum pf_phase qfs_arg_string(qf_str qfs, const char* src, enum pf_flags flags, @@ -985,7 +1104,10 @@ qfs_arg_string(qf_str qfs, const char* src, enum pf_flags flags, flags &= ~pf_precision ; } ; - len = (src != NULL) ? strlen(src) : 0 ; + len = 0 ; + if (src != NULL) + while (*(src + len) != '\0') ++len ; + if (((precision > 0) || (flags & pf_precision)) && (len > precision)) len = precision ; @@ -1012,7 +1134,8 @@ qfs_arg_string(qf_str qfs, const char* src, enum pf_flags flags, * pf_unsigned * pf_ptr * - * This operation is async-signal-safe. + * This operation is async-signal-safe. Takes into account the offset, and + * adds up any overflow */ static enum pf_phase qfs_arg_char(qf_str qfs, char ch, enum pf_flags flags, int width, int precision) @@ -1049,7 +1172,8 @@ qfs_arg_char(qf_str qfs, char ch, enum pf_flags flags, int width, int precision) * Rejects: ant == ant_long_double -- which is how the parser spots an * erroneous %Ld for example. * - * This operation is async-signal-safe. + * This operation is async-signal-safe. Takes into account the offset, and + * adds up any overflow */ static enum pf_phase qfs_arg_integer(qf_str qfs, va_list* p_va, enum pf_flags flags, @@ -1163,42 +1287,90 @@ qfs_arg_integer(qf_str qfs, va_list* p_va, enum pf_flags flags, * doomed to failure, because any floating point operation may affect flags * and other state in the processor :-( * - * This operation is *NOT* async-signal-safe. + * This operation is *NOT* async-signal-safe. Takes into account the offset, + * and adds up any overflow */ + +union float_value +{ + double d ; + long double ld ; +} ; + +static int +qfs_arg_float_snprintf(void* buf, int have, const char* format, + union float_value* p_val, enum arg_num_type ant) +{ + if (ant == ant_default) + return snprintf(buf, have, format, p_val->d) ; + else + return snprintf(buf, have, format, p_val->ld) ; +} ; + static enum pf_phase qfs_arg_float(qf_str qfs, va_list* p_va, const char* start, size_t flen, enum arg_num_type ant) { + union float_value val ; char format[flen + 1] ; - int want ; - int have ; + int want ; + + if (ant == ant_default) + val.d = va_arg(*p_va, double) ; + else + val.ld = va_arg(*p_va, long double) ; memcpy(format, start, flen) ; format[flen + 1] = '\0' ; - have = qfs_left(qfs) + 1 ; - - if (ant == ant_default) + if (qfs->offset == 0) { - double val ; - val = va_arg(*p_va, double) ; - want = snprintf(qfs_ptr(qfs), have, format, val) ; + /* No offset, so can use the qfs directly. + */ + int have ; + + have = qfs_left(qfs) ; + want = qfs_arg_float_snprintf(qfs->ptr, have + 1, format, &val, ant) ; + + if (want > 0) + { + if (want <= have) + qfs->ptr += want ; + else + { + qfs->ptr = qfs->end ; + qfs->overflow += (want - have) ; + } ; + } ; } else { - long double val ; - assert(ant == ant_long_double) ; - val = va_arg(*p_va, long double) ; - want = snprintf(qfs_ptr(qfs), have, format, val) ; - } ; + /* Because the offset is not zero, need to use an intermediate + * buffer and then copy part after the offset. + * + * First, discover full extent of the formatted value, then if that + * exceeds the offset, construct buffer and copy what we can to the + * qps; otherwise, reduce the offset. + */ + want = qfs_arg_float_snprintf(NULL, 0, format, &val, ant) ; - if (want < 0) - return pfp_failed ; + if (want > 0) + { + int take ; - if (want < have) - qfs->ptr += want ; - else - qfs->ptr = qfs->end ; + take = qfs->offset + qfs_left(qfs) ; + if (take > want) + take = want ; - return pfp_done ; + { + char tmp[take + 1] ; + want = qfs_arg_float_snprintf(tmp, take + 1, format, &val, ant) ; + + if (want > 0) + qfs_append_n(qfs, tmp, want) ; + } ; + } ; + } ; + + return (want >= 0) ? pfp_done : pfp_failed ; } ; |