X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flibpspp%2Fstr.c;h=afe32de9f2049bfcc5a80ac7a95b36b348be4cb7;hb=8830c95bb9e8d72621787866141a27fc22e8c786;hp=6d25c339fad9550d618f63d6f33f9ba3f442eec7;hpb=3af5716f625f5a1e5175a957d734270dae99e8aa;p=pspp-builds.git diff --git a/src/libpspp/str.c b/src/libpspp/str.c index 6d25c339..afe32de9 100644 --- a/src/libpspp/str.c +++ b/src/libpspp/str.c @@ -1,35 +1,34 @@ -/* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. - Written by Ben Pfaff . +/* PSPP - a program for statistical analysis. + Copyright (C) 1997-9, 2000, 2006, 2009 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with this program. If not, see . */ #include #include "str.h" #include -#include +#include +#include #include -#include #include +#include +#include #include "minmax.h" -#include "size_max.h" +#include "xalloc.h" #include "xsize.h" /* Reverses the order of NBYTES bytes at address P, thus converting @@ -70,12 +69,12 @@ buf_compare_case (const char *a_, const char *b_, size_t size) const unsigned char *a = (unsigned char *) a_; const unsigned char *b = (unsigned char *) b_; - while (size-- > 0) + while (size-- > 0) { unsigned char ac = toupper (*a++); unsigned char bc = toupper (*b++); - if (ac != bc) + if (ac != bc) return ac > bc ? 1 : -1; } @@ -95,17 +94,17 @@ buf_compare_rpad (const char *a, size_t a_len, const char *b, size_t b_len) result = memcmp (a, b, min_len); if (result != 0) return result; - else + else { size_t idx; - - if (a_len < b_len) + + if (a_len < b_len) { for (idx = min_len; idx < b_len; idx++) if (' ' != b[idx]) return ' ' > b[idx] ? 1 : -1; } - else + else { for (idx = min_len; idx < a_len; idx++) if (a[idx] != ' ') @@ -126,9 +125,9 @@ str_compare_rpad (const char *a, const char *b) /* Copies string SRC to buffer DST, of size DST_SIZE bytes. DST is truncated to DST_SIZE bytes or padded on the right with - spaces as needed. */ + copies of PAD as needed. */ void -buf_copy_str_rpad (char *dst, size_t dst_size, const char *src) +buf_copy_str_rpad (char *dst, size_t dst_size, const char *src, char pad) { size_t src_len = strlen (src); if (src_len >= dst_size) @@ -136,15 +135,15 @@ buf_copy_str_rpad (char *dst, size_t dst_size, const char *src) else { memcpy (dst, src, src_len); - memset (&dst[src_len], ' ', dst_size - src_len); + memset (&dst[src_len], pad, dst_size - src_len); } } /* Copies string SRC to buffer DST, of size DST_SIZE bytes. DST is truncated to DST_SIZE bytes or padded on the left with - spaces as needed. */ + copies of PAD as needed. */ void -buf_copy_str_lpad (char *dst, size_t dst_size, const char *src) +buf_copy_str_lpad (char *dst, size_t dst_size, const char *src, char pad) { size_t src_len = strlen (src); if (src_len >= dst_size) @@ -152,24 +151,42 @@ buf_copy_str_lpad (char *dst, size_t dst_size, const char *src) else { size_t pad_cnt = dst_size - src_len; - memset (&dst[0], ' ', pad_cnt); + memset (&dst[0], pad, pad_cnt); memcpy (dst + pad_cnt, src, src_len); } } +/* Copies buffer SRC, of SRC_SIZE bytes, to DST, of DST_SIZE bytes. + DST is truncated to DST_SIZE bytes or padded on the left with + copies of PAD as needed. */ +void +buf_copy_lpad (char *dst, size_t dst_size, + const char *src, size_t src_size, + char pad) +{ + if (src_size >= dst_size) + memmove (dst, src, dst_size); + else + { + memset (dst, pad, dst_size - src_size); + memmove (&dst[dst_size - src_size], src, src_size); + } +} + /* Copies buffer SRC, of SRC_SIZE bytes, to DST, of DST_SIZE bytes. DST is truncated to DST_SIZE bytes or padded on the right with - spaces as needed. */ + copies of PAD as needed. */ void buf_copy_rpad (char *dst, size_t dst_size, - const char *src, size_t src_size) + const char *src, size_t src_size, + char pad) { if (src_size >= dst_size) memmove (dst, src, dst_size); else { memmove (dst, src, src_size); - memset (&dst[src_size], ' ', dst_size - src_size); + memset (&dst[src_size], pad, dst_size - src_size); } } @@ -180,27 +197,30 @@ buf_copy_rpad (char *dst, size_t dst_size, void str_copy_rpad (char *dst, size_t dst_size, const char *src) { - size_t src_len = strlen (src); - if (src_len < dst_size - 1) + if (dst_size > 0) { - memcpy (dst, src, src_len); - memset (&dst[src_len], ' ', dst_size - 1 - src_len); + size_t src_len = strlen (src); + if (src_len < dst_size - 1) + { + memcpy (dst, src, src_len); + memset (&dst[src_len], ' ', dst_size - 1 - src_len); + } + else + memcpy (dst, src, dst_size - 1); + dst[dst_size - 1] = 0; } - else - memcpy (dst, src, dst_size - 1); - dst[dst_size - 1] = 0; } /* Copies SRC to DST, which is in a buffer DST_SIZE bytes long. Truncates DST to DST_SIZE - 1 characters, if necessary. */ void -str_copy_trunc (char *dst, size_t dst_size, const char *src) +str_copy_trunc (char *dst, size_t dst_size, const char *src) { size_t src_len = strlen (src); assert (dst_size > 0); if (src_len + 1 < dst_size) memcpy (dst, src, src_len + 1); - else + else { memcpy (dst, src, dst_size - 1); dst[dst_size - 1] = '\0'; @@ -212,7 +232,7 @@ str_copy_trunc (char *dst, size_t dst_size, const char *src) Truncates DST to DST_SIZE - 1 characters, if necessary. */ void str_copy_buf_trunc (char *dst, size_t dst_size, - const char *src, size_t src_size) + const char *src, size_t src_size) { size_t dst_len; assert (dst_size > 0); @@ -224,7 +244,7 @@ str_copy_buf_trunc (char *dst, size_t dst_size, /* Converts each character in S to uppercase. */ void -str_uppercase (char *s) +str_uppercase (char *s) { for (; *s != '\0'; s++) *s = toupper ((unsigned char) *s); @@ -232,16 +252,51 @@ str_uppercase (char *s) /* Converts each character in S to lowercase. */ void -str_lowercase (char *s) +str_lowercase (char *s) { for (; *s != '\0'; s++) *s = tolower ((unsigned char) *s); } +/* Converts NUMBER into a string in 26-adic notation in BUFFER, + which has room for SIZE bytes. Returns true if successful, + false if NUMBER, plus a trailing null, is too large to fit in + the available space. + + 26-adic notation is "spreadsheet column numbering": 1 = A, 2 = + B, 3 = C, ... 26 = Z, 27 = AA, 28 = AB, 29 = AC, ... + + 26-adic notation is the special case of a k-adic numeration + system (aka bijective base-k numeration) with k=26. In k-adic + numeration, the digits are {1, 2, 3, ..., k} (there is no + digit 0), and integer 0 is represented by the empty string. + For more information, see + http://en.wikipedia.org/wiki/Bijective_numeration. */ +bool +str_format_26adic (unsigned long int number, char buffer[], size_t size) +{ + size_t length = 0; + + while (number-- > 0) + { + if (length >= size) + return false; + buffer[length++] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[number % 26]; + number /= 26; + } + + if (length >= size) + return false; + buffer[length] = '\0'; + + buf_reverse (buffer, length); + return true; +} + /* Formats FORMAT into DST, as with sprintf(), and returns the address of the terminating null written to DST. */ char * -spprintf (char *dst, const char *format, ...) +spprintf (char *dst, const char *format, ...) { va_list args; int count; @@ -252,12 +307,21 @@ spprintf (char *dst, const char *format, ...) return dst + count; } + +/* Sets the SIZE bytes starting at BLOCK to C, + and returns the byte following BLOCK. */ +void * +mempset (void *block, int c, size_t size) +{ + memset (block, c, size); + return (char *) block + size; +} /* Substrings. */ /* Returns an empty substring. */ struct substring -ss_empty (void) +ss_empty (void) { struct substring ss; ss.string = NULL; @@ -268,15 +332,15 @@ ss_empty (void) /* Returns a substring whose contents are the given C-style string CSTR. */ struct substring -ss_cstr (const char *cstr) +ss_cstr (const char *cstr) { return ss_buffer (cstr, strlen (cstr)); } /* Returns a substring whose contents are the CNT characters in - BUFFER. */ + BUFFER. */ struct substring -ss_buffer (const char *buffer, size_t cnt) +ss_buffer (const char *buffer, size_t cnt) { struct substring ss; ss.string = (char *) buffer; @@ -291,14 +355,14 @@ ss_substr (struct substring ss, size_t start, size_t cnt) { if (start < ss.length) return ss_buffer (ss.string + start, MIN (cnt, ss.length - start)); - else + else return ss_buffer (ss.string + ss.length, 0); } /* Returns a substring whose contents are the first CNT characters in SS. */ struct substring -ss_head (struct substring ss, size_t cnt) +ss_head (struct substring ss, size_t cnt) { return ss_buffer (ss.string, MIN (cnt, ss.length)); } @@ -306,7 +370,7 @@ ss_head (struct substring ss, size_t cnt) /* Returns a substring whose contents are the last CNT characters in SS. */ struct substring -ss_tail (struct substring ss, size_t cnt) +ss_tail (struct substring ss, size_t cnt) { if (cnt < ss.length) return ss_buffer (ss.string + (ss.length - cnt), cnt); @@ -317,7 +381,7 @@ ss_tail (struct substring ss, size_t cnt) /* Makes a malloc()'d copy of the contents of OLD and stores it in NEW. */ void -ss_alloc_substring (struct substring *new, struct substring old) +ss_alloc_substring (struct substring *new, struct substring old) { new->string = xmalloc (old.length); new->length = old.length; @@ -326,22 +390,41 @@ ss_alloc_substring (struct substring *new, struct substring old) /* Allocates room for a CNT-character string in NEW. */ void -ss_alloc_uninit (struct substring *new, size_t cnt) +ss_alloc_uninit (struct substring *new, size_t cnt) { new->string = xmalloc (cnt); new->length = cnt; } +/* Makes a pool_alloc_unaligned()'d copy of the contents of OLD + in POOL, and stores it in NEW. */ +void +ss_alloc_substring_pool (struct substring *new, struct substring old, + struct pool *pool) +{ + new->string = pool_alloc_unaligned (pool, old.length); + new->length = old.length; + memcpy (new->string, old.string, old.length); +} + +/* Allocates room for a CNT-character string in NEW in POOL. */ +void +ss_alloc_uninit_pool (struct substring *new, size_t cnt, struct pool *pool) +{ + new->string = pool_alloc_unaligned (pool, cnt); + new->length = cnt; +} + /* Frees the string that SS points to. */ void -ss_dealloc (struct substring *ss) +ss_dealloc (struct substring *ss) { free (ss->string); } /* Truncates SS to at most CNT characters in length. */ void -ss_truncate (struct substring *ss, size_t cnt) +ss_truncate (struct substring *ss, size_t cnt) { if (ss->length > cnt) ss->length = cnt; @@ -350,16 +433,16 @@ ss_truncate (struct substring *ss, size_t cnt) /* Removes trailing characters in TRIM_SET from SS. Returns number of characters removed. */ size_t -ss_rtrim (struct substring *ss, struct substring trim_set) +ss_rtrim (struct substring *ss, struct substring trim_set) { size_t cnt = 0; while (cnt < ss->length && ss_find_char (trim_set, - ss->string[ss->length - cnt - 1]) != SIZE_MAX) + ss->string[ss->length - cnt - 1]) != SIZE_MAX) cnt++; ss->length -= cnt; return cnt; -} +} /* Removes leading characters in TRIM_SET from SS. Returns number of characters removed. */ @@ -373,7 +456,7 @@ ss_ltrim (struct substring *ss, struct substring trim_set) /* Trims leading and trailing characters in TRIM_SET from SS. */ void -ss_trim (struct substring *ss, struct substring trim_set) +ss_trim (struct substring *ss, struct substring trim_set) { ss_ltrim (ss, trim_set); ss_rtrim (ss, trim_set); @@ -382,7 +465,7 @@ ss_trim (struct substring *ss, struct substring trim_set) /* If the last character in SS is C, removes it and returns true. Otherwise, returns false without changing the string. */ bool -ss_chomp (struct substring *ss, char c) +ss_chomp (struct substring *ss, char c) { if (ss_last (*ss) == c) { @@ -407,7 +490,7 @@ ss_chomp (struct substring *ss, char c) empty string contains a single token. */ bool ss_separate (struct substring ss, struct substring delimiters, - size_t *save_idx, struct substring *token) + size_t *save_idx, struct substring *token) { if (*save_idx <= ss_length (ss)) { @@ -417,17 +500,18 @@ ss_separate (struct substring ss, struct substring delimiters, *save_idx += length + 1; return true; } - else + else { *token = ss_empty (); - return false; + return false; } } /* Divides SS into tokens separated by any of the DELIMITERS, merging adjacent delimiters so that the empty string is never produced as a token. Each call replaces TOKEN by the next - token in SS, or by an empty string if no tokens remain. + token in SS, or by an empty string if no tokens remain, and + then skips past the first delimiter following the token. Returns true if a token was obtained, false otherwise. Before the first call, initialize *SAVE_IDX to 0. Do not @@ -438,13 +522,14 @@ ss_tokenize (struct substring ss, struct substring delimiters, { ss_advance (&ss, *save_idx); *save_idx += ss_ltrim (&ss, delimiters); - *save_idx += ss_get_chars (&ss, ss_cspan (ss, delimiters), token); + ss_get_chars (&ss, ss_cspan (ss, delimiters), token); + *save_idx += ss_length (*token) + 1; return ss_length (*token) > 0; } /* Removes the first CNT characters from SS. */ void -ss_advance (struct substring *ss, size_t cnt) +ss_advance (struct substring *ss, size_t cnt) { if (cnt > ss->length) cnt = ss->length; @@ -455,7 +540,7 @@ ss_advance (struct substring *ss, size_t cnt) /* If the first character in SS is C, removes it and returns true. Otherwise, returns false without changing the string. */ bool -ss_match_char (struct substring *ss, char c) +ss_match_char (struct substring *ss, char c) { if (ss_first (*ss) == c) { @@ -467,13 +552,45 @@ ss_match_char (struct substring *ss, char c) return false; } +/* If the first character in SS is in MATCH, removes it and + returns the character that was removed. + Otherwise, returns EOF without changing the string. */ +int +ss_match_char_in (struct substring *ss, struct substring match) +{ + int c = EOF; + if (ss->length > 0 + && memchr (match.string, ss->string[0], match.length) != NULL) + { + c = ss->string[0]; + ss->string++; + ss->length--; + } + return c; +} + +/* If SS begins with TARGET, removes it and returns true. + Otherwise, returns false without changing SS. */ +bool +ss_match_string (struct substring *ss, const struct substring target) +{ + size_t length = ss_length (target); + if (ss_equals (ss_head (*ss, length), target)) + { + ss_advance (ss, length); + return true; + } + else + return false; +} + /* Removes the first character from SS and returns it. If SS is empty, returns EOF without modifying SS. */ int -ss_get_char (struct substring *ss) +ss_get_char (struct substring *ss) { int c = ss_first (*ss); - if (c != EOF) + if (c != EOF) { ss->string++; ss->length--; @@ -496,38 +613,68 @@ ss_get_until (struct substring *ss, char delimiter, struct substring *out) is shorter than CNT characters). Trims the same characters from the beginning of SS. Returns CNT. */ size_t -ss_get_chars (struct substring *ss, size_t cnt, struct substring *out) +ss_get_chars (struct substring *ss, size_t cnt, struct substring *out) { *out = ss_head (*ss, cnt); ss_advance (ss, cnt); return cnt; } +/* Parses and removes an optionally signed decimal integer from + the beginning of SS. Returns 0 if an error occurred, + otherwise the number of characters removed from SS. Stores + the integer's value into *VALUE. */ +size_t +ss_get_long (struct substring *ss, long *value) +{ + char tmp[64]; + size_t length; + + length = ss_span (*ss, ss_cstr ("+-")); + length += ss_span (ss_substr (*ss, length, SIZE_MAX), ss_cstr (CC_DIGITS)); + if (length > 0 && length < sizeof tmp) + { + char *tail; + + memcpy (tmp, ss_data (*ss), length); + tmp[length] = '\0'; + + *value = strtol (tmp, &tail, 10); + if (tail - tmp == length) + { + ss_advance (ss, length); + return length; + } + } + *value = 0; + return 0; +} + /* Returns true if SS is empty (contains no characters), false otherwise. */ bool -ss_is_empty (struct substring ss) +ss_is_empty (struct substring ss) { return ss.length == 0; } /* Returns the number of characters in SS. */ size_t -ss_length (struct substring ss) +ss_length (struct substring ss) { return ss.length; } /* Returns a pointer to the characters in SS. */ char * -ss_data (struct substring ss) +ss_data (struct substring ss) { return ss.string; } /* Returns a pointer just past the last character in SS. */ char * -ss_end (struct substring ss) +ss_end (struct substring ss) { return ss.string + ss.length; } @@ -536,7 +683,7 @@ ss_end (struct substring ss) range of unsigned char. Returns EOF if IDX is out of the range of indexes for SS. */ int -ss_at (struct substring ss, size_t idx) +ss_at (struct substring ss, size_t idx) { return idx < ss.length ? (unsigned char) ss.string[idx] : EOF; } @@ -544,7 +691,7 @@ ss_at (struct substring ss, size_t idx) /* Returns the first character in SS as a value in the range of unsigned char. Returns EOF if SS is the empty string. */ int -ss_first (struct substring ss) +ss_first (struct substring ss) { return ss_at (ss, 0); } @@ -552,7 +699,7 @@ ss_first (struct substring ss) /* Returns the last character in SS as a value in the range of unsigned char. Returns EOF if SS is the empty string. */ int -ss_last (struct substring ss) +ss_last (struct substring ss) { return ss.length > 0 ? (unsigned char) ss.string[ss.length - 1] : EOF; } @@ -560,31 +707,31 @@ ss_last (struct substring ss) /* Returns the number of contiguous characters at the beginning of SS that are in SKIP_SET. */ size_t -ss_span (struct substring ss, struct substring skip_set) +ss_span (struct substring ss, struct substring skip_set) { size_t i; - for (i = 0; i < ss.length; i++) + for (i = 0; i < ss.length; i++) if (ss_find_char (skip_set, ss.string[i]) == SIZE_MAX) - break; + break; return i; } /* Returns the number of contiguous characters at the beginning of SS that are not in SKIP_SET. */ size_t -ss_cspan (struct substring ss, struct substring stop_set) +ss_cspan (struct substring ss, struct substring stop_set) { size_t i; - for (i = 0; i < ss.length; i++) + for (i = 0; i < ss.length; i++) if (ss_find_char (stop_set, ss.string[i]) != SIZE_MAX) - break; + break; return i; } /* Returns the offset in SS of the first instance of C, or SIZE_MAX if C does not occur in SS. */ size_t -ss_find_char (struct substring ss, char c) +ss_find_char (struct substring ss, char c) { const char *p = memchr (ss.string, c, ss.length); return p != NULL ? p - ss.string : SIZE_MAX; @@ -601,6 +748,34 @@ ss_compare (struct substring a, struct substring b) return retval; } +/* Compares A and B case-insensitively and returns a + strcmp()-type comparison result. */ +int +ss_compare_case (struct substring a, struct substring b) +{ + int retval = memcasecmp (a.string, b.string, MIN (a.length, b.length)); + if (retval == 0) + retval = a.length < b.length ? -1 : a.length > b.length; + return retval; +} + +/* Compares A and B and returns true if their contents are + identical, false otherwise. */ +int +ss_equals (struct substring a, struct substring b) +{ + return a.length == b.length && !memcmp (a.string, b.string, a.length); +} + +/* Compares A and B and returns true if their contents are + identical except possibly for case differences, false + otherwise. */ +int +ss_equals_case (struct substring a, struct substring b) +{ + return a.length == b.length && !memcasecmp (a.string, b.string, a.length); +} + /* Returns the position in SS that the character at P occupies. P must point within SS or one past its end. */ size_t @@ -614,7 +789,7 @@ ss_pointer_to_position (struct substring ss, const char *p) /* Allocates and returns a null-terminated string that contains SS. */ char * -ss_xstrdup (struct substring ss) +ss_xstrdup (struct substring ss) { char *s = xmalloc (ss.length + 1); memcpy (s, ss.string, ss.length); @@ -632,7 +807,7 @@ ds_init_empty (struct string *st) /* Initializes ST with initial contents S. */ void -ds_init_string (struct string *st, const struct string *s) +ds_init_string (struct string *st, const struct string *s) { ds_init_substring (st, ds_ss (s)); } @@ -649,7 +824,7 @@ ds_init_substring (struct string *st, struct substring ss) /* Initializes ST with initial contents S. */ void -ds_init_cstr (struct string *st, const char *s) +ds_init_cstr (struct string *st, const char *s) { ds_init_substring (st, ss_cstr (s)); } @@ -658,28 +833,52 @@ ds_init_cstr (struct string *st, const char *s) void ds_destroy (struct string *st) { - if (st != NULL) + if (st != NULL) { ss_dealloc (&st->ss); st->ss.string = NULL; st->ss.length = 0; - st->capacity = 0; + st->capacity = 0; } } /* Swaps the contents of strings A and B. */ void -ds_swap (struct string *a, struct string *b) +ds_swap (struct string *a, struct string *b) { struct string tmp = *a; *a = *b; *b = tmp; } +/* Helper function for ds_register_pool. */ +static void +free_string (void *st_) +{ + struct string *st = st_; + ds_destroy (st); +} + +/* Arranges for ST to be destroyed automatically as part of + POOL. */ +void +ds_register_pool (struct string *st, struct pool *pool) +{ + pool_register (pool, free_string, st); +} + +/* Cancels the arrangement for ST to be destroyed automatically + as part of POOL. */ +void +ds_unregister_pool (struct string *st, struct pool *pool) +{ + pool_unregister (pool, st); +} + /* Copies SRC into DST. DST and SRC may be the same string. */ void -ds_assign_string (struct string *dst, const struct string *src) +ds_assign_string (struct string *dst, const struct string *src) { ds_assign_substring (dst, ds_ss (src)); } @@ -711,7 +910,7 @@ ds_clear (struct string *st) /* Returns a substring that contains ST. */ struct substring -ds_ss (const struct string *st) +ds_ss (const struct string *st) { return st->ss; } @@ -724,7 +923,7 @@ ds_ss (const struct string *st) exceeds the length of ST, then the substring will only be ds_length(ST) - START characters long. */ struct substring -ds_substr (const struct string *st, size_t start, size_t cnt) +ds_substr (const struct string *st, size_t start, size_t cnt) { return ss_substr (ds_ss (st), start, cnt); } @@ -733,7 +932,7 @@ ds_substr (const struct string *st, size_t start, size_t cnt) ST. If CNT exceeds the length of ST, then the substring will contain all of ST. */ struct substring -ds_head (const struct string *st, size_t cnt) +ds_head (const struct string *st, size_t cnt) { return ss_head (ds_ss (st), cnt); } @@ -783,7 +982,7 @@ ds_truncate (struct string *st, size_t length) /* Removes trailing characters in TRIM_SET from ST. Returns number of characters removed. */ size_t -ds_rtrim (struct string *st, struct substring trim_set) +ds_rtrim (struct string *st, struct substring trim_set) { return ss_rtrim (&st->ss, trim_set); } @@ -791,7 +990,7 @@ ds_rtrim (struct string *st, struct substring trim_set) /* Removes leading characters in TRIM_SET from ST. Returns number of characters removed. */ size_t -ds_ltrim (struct string *st, struct substring trim_set) +ds_ltrim (struct string *st, struct substring trim_set) { size_t cnt = ds_span (st, trim_set); if (cnt > 0) @@ -802,7 +1001,7 @@ ds_ltrim (struct string *st, struct substring trim_set) /* Trims leading and trailing characters in TRIM_SET from ST. Returns number of charactesr removed. */ size_t -ds_trim (struct string *st, struct substring trim_set) +ds_trim (struct string *st, struct substring trim_set) { size_t cnt = ds_rtrim (st, trim_set); return cnt + ds_ltrim (st, trim_set); @@ -811,7 +1010,7 @@ ds_trim (struct string *st, struct substring trim_set) /* If the last character in ST is C, removes it and returns true. Otherwise, returns false without modifying ST. */ bool -ds_chomp (struct string *st, char c) +ds_chomp (struct string *st, char c) { return ss_chomp (&st->ss, c); } @@ -854,15 +1053,55 @@ ds_tokenize (const struct string *st, struct substring delimiters, LENGTH characters in size. If ST is initially LENGTH characters or longer, this is a no-op. */ void -ds_rpad (struct string *st, size_t length, char pad) +ds_rpad (struct string *st, size_t length, char pad) { if (length > st->ss.length) ds_put_char_multiple (st, pad, length - st->ss.length); } +/* Sets the length of ST to exactly NEW_LENGTH, + either by truncating characters from the end, + or by padding on the right with PAD. */ +void +ds_set_length (struct string *st, size_t new_length, char pad) +{ + if (st->ss.length < new_length) + ds_rpad (st, new_length, pad); + else + st->ss.length = new_length; +} + +/* Removes N characters from ST starting at offset START. */ +void +ds_remove (struct string *st, size_t start, size_t n) +{ + if (n > 0 && start < st->ss.length) + { + if (st->ss.length - start <= n) + { + /* All characters at or beyond START are deleted. */ + st->ss.length = start; + } + else + { + /* Some characters remain and must be shifted into + position. */ + memmove (st->ss.string + st->ss.length, + st->ss.string + st->ss.length + n, + st->ss.length - start - n); + st->ss.length -= n; + } + } + else + { + /* There are no characters to delete or no characters at or + beyond START, hence deletion is a no-op. */ + } +} + /* Returns true if ST is empty, false otherwise. */ bool -ds_is_empty (const struct string *st) +ds_is_empty (const struct string *st) { return ss_is_empty (st->ss); } @@ -894,7 +1133,7 @@ ds_end (const struct string *st) range of unsigned char. Returns EOF if IDX is out of the range of indexes for ST. */ int -ds_at (const struct string *st, size_t idx) +ds_at (const struct string *st, size_t idx) { return ss_at (ds_ss (st), idx); } @@ -902,7 +1141,7 @@ ds_at (const struct string *st, size_t idx) /* Returns the first character in ST as a value in the range of unsigned char. Returns EOF if ST is the empty string. */ int -ds_first (const struct string *st) +ds_first (const struct string *st) { return ss_first (ds_ss (st)); } @@ -910,7 +1149,7 @@ ds_first (const struct string *st) /* Returns the last character in ST as a value in the range of unsigned char. Returns EOF if ST is the empty string. */ int -ds_last (const struct string *st) +ds_last (const struct string *st) { return ss_last (ds_ss (st)); } @@ -959,7 +1198,7 @@ ds_pointer_to_position (const struct string *st, const char *p) /* Allocates and returns a null-terminated string that contains ST. */ char * -ds_xstrdup (const struct string *st) +ds_xstrdup (const struct string *st) { return ss_xstrdup (ds_ss (st)); } @@ -976,37 +1215,48 @@ char * ds_cstr (const struct string *st_) { struct string *st = (struct string *) st_; - if (st->ss.string == NULL) + if (st->ss.string == NULL) ds_extend (st, 1); st->ss.string[st->ss.length] = '\0'; return st->ss.string; } -/* Appends to ST a newline-terminated line read from STREAM. - Newline is the last character of ST on return, unless an I/O error - or end of file is encountered after reading some characters. - Returns true if a line is successfully read, false if no characters at - all were read before an I/O error or end of file was - encountered. */ +/* Reads characters from STREAM and appends them to ST, stopping + after MAX_LENGTH characters, after appending a newline, or + after an I/O error or end of file was encountered, whichever + comes first. Returns true if at least one character was added + to ST, false if no characters were read before an I/O error or + end of file (or if MAX_LENGTH was 0). + + This function accepts LF, CR LF, and CR sequences as new-line, + and translates each of them to a single '\n' new-line + character in ST. */ bool -ds_read_line (struct string *st, FILE *stream) +ds_read_line (struct string *st, FILE *stream, size_t max_length) { - int c; - - c = getc (stream); - if (c == EOF) - return false; + size_t length; - for (;;) + for (length = 0; length < max_length; length++) { + int c = getc (stream); + if (c == EOF) + break; + + if (c == '\r') + { + c = getc (stream); + if (c != '\n') + { + ungetc (c, stream); + c = '\n'; + } + } ds_put_char (st, c); if (c == '\n') - return true; - - c = getc (stream); - if (c == EOF) - return true; + return true; } + + return length > 0; } /* Removes a comment introduced by `#' from ST, @@ -1016,7 +1266,7 @@ remove_comment (struct string *st) { char *cp; int quote = 0; - + for (cp = ds_data (st); cp < ds_end (st); cp++) if (quote) { @@ -1041,7 +1291,7 @@ remove_comment (struct string *st) - Deletes comments introduced by `#' outside of single or double quotes. - - Deletes trailing white space. + - Deletes trailing white space. Returns true if a line was successfully read, false on failure. If LINE_NUMBER is non-null, then *LINE_NUMBER is @@ -1052,22 +1302,22 @@ ds_read_config_line (struct string *st, int *line_number, FILE *stream) ds_clear (st); do { - if (!ds_read_line (st, stream)) + if (!ds_read_line (st, stream, SIZE_MAX)) return false; (*line_number)++; ds_rtrim (st, ss_cstr (CC_SPACES)); } while (ds_chomp (st, '\\')); - + remove_comment (st); return true; } /* Attempts to read SIZE * CNT bytes from STREAM and append them to ST. - Returns number of bytes actually read. */ -size_t -ds_read_stream (struct string *st, size_t size, size_t cnt, FILE *stream) + Returns true if all the requested data was read, false otherwise. */ +bool +ds_read_stream (struct string *st, size_t size, size_t cnt, FILE *stream) { if (size != 0) { @@ -1075,12 +1325,18 @@ ds_read_stream (struct string *st, size_t size, size_t cnt, FILE *stream) if (size_in_bounds_p (xsum (ds_length (st), try_bytes))) { char *buffer = ds_put_uninit (st, try_bytes); - size_t got_bytes = fread (buffer, size, cnt, stream); + size_t got_bytes = fread (buffer, 1, try_bytes, stream); ds_truncate (st, ds_length (st) - (try_bytes - got_bytes)); - return got_bytes; + return got_bytes == try_bytes; + } + else + { + errno = ENOMEM; + return false; } } - return 0; + else + return true; } /* Concatenates S onto ST. */ @@ -1138,7 +1394,7 @@ ds_put_vformat (struct string *st, const char *format, va_list args_) vsprintf (ds_put_uninit (st, needed), format, args); va_end (args); } - else + else { /* Some old libc's returned -1 when the destination string was too short. */ @@ -1150,7 +1406,7 @@ ds_put_vformat (struct string *st, const char *format, va_list args_) va_copy (args, args_); needed = vsnprintf (ds_end (st), avail, format, args); va_end (args); - } + } st->ss.length += needed; } } @@ -1164,7 +1420,46 @@ ds_put_char (struct string *st, int ch) /* Appends CNT copies of character CH to ST. */ void -ds_put_char_multiple (struct string *st, int ch, size_t cnt) +ds_put_char_multiple (struct string *st, int ch, size_t cnt) { memset (ds_put_uninit (st, cnt), ch, cnt); } + + +/* If relocation has been enabled, replace ST, + with its relocated version */ +void +ds_relocate (struct string *st) +{ + const char *orig = ds_cstr (st); + const char *rel = relocate (orig); + + if ( orig != rel) + { + ds_clear (st); + ds_put_cstr (st, rel); + free ((char *) rel); + } +} + + + + +/* Operations on uint8_t "strings" */ + +/* Copies buffer SRC, of SRC_SIZE bytes, to DST, of DST_SIZE bytes. + DST is truncated to DST_SIZE bytes or padded on the right with + copies of PAD as needed. */ +void +u8_buf_copy_rpad (uint8_t *dst, size_t dst_size, + const uint8_t *src, size_t src_size, + char pad) +{ + if (src_size >= dst_size) + memmove (dst, src, dst_size); + else + { + memmove (dst, src, src_size); + memset (&dst[src_size], pad, dst_size - src_size); + } +}