+ size_t n_pad = dst_size - src_len;
+ memset (&dst[0], pad, n_pad);
+ memcpy (dst + n_pad, 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
+ copies of PAD as needed. */
+void
+buf_copy_rpad (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
+ {
+ memmove (dst, src, src_size);
+ memset (&dst[src_size], pad, dst_size - src_size);
+ }
+}
+
+/* Copies string SRC to string DST, which is in a buffer DST_SIZE
+ bytes long.
+ Truncates DST to DST_SIZE - 1 bytes or right-pads with
+ spaces to DST_SIZE - 1 bytes if necessary. */
+void
+str_copy_rpad (char *dst, size_t dst_size, const char *src)
+{
+ if (dst_size > 0)
+ {
+ 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;
+ }
+}
+
+/* Copies SRC to DST, which is in a buffer DST_SIZE bytes long.
+ Truncates DST to DST_SIZE - 1 bytes, if necessary. */
+void
+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
+ {
+ memcpy (dst, src, dst_size - 1);
+ dst[dst_size - 1] = '\0';
+ }
+}
+
+/* Copies buffer SRC, of SRC_LEN bytes,
+ to DST, which is in a buffer DST_SIZE bytes long.
+ Truncates DST to DST_SIZE - 1 bytes, if necessary. */
+void
+str_copy_buf_trunc (char *dst, size_t dst_size,
+ const char *src, size_t src_size)
+{
+ size_t dst_len;
+ assert (dst_size > 0);
+
+ dst_len = src_size < dst_size ? src_size : dst_size - 1;
+ memcpy (dst, src, dst_len);
+ dst[dst_len] = '\0';
+}
+
+/* Converts each byte in S to uppercase.
+
+ This is suitable only for ASCII strings. Use utf8_to_upper() for UTF-8
+ strings.*/
+void
+str_uppercase (char *s)
+{
+ for (; *s != '\0'; s++)
+ *s = c_toupper ((unsigned char) *s);
+}
+
+/* Converts each byte in S to lowercase.
+
+ This is suitable only for ASCII strings. Use utf8_to_lower() for UTF-8
+ strings.*/
+void
+str_lowercase (char *s)
+{
+ for (; *s != '\0'; s++)
+ *s = c_tolower ((unsigned char) *s);
+}
+
+/* Converts NUMBER into a string in 26-adic notation in BUFFER,
+ which has room for SIZE bytes. Uses uppercase if UPPERCASE is
+ true, otherwise lowercase, 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, bool uppercase,
+ char buffer[], size_t size)
+{
+ const char *alphabet
+ = uppercase ? "ABCDEFGHIJKLMNOPQRSTUVWXYZ" : "abcdefghijklmnopqrstuvwxyz";
+ size_t length = 0;
+
+ while (number-- > 0)
+ {
+ if (length >= size)
+ goto overflow;
+ buffer[length++] = alphabet[number % 26];
+ number /= 26;
+ }
+
+ if (length >= size)
+ goto overflow;
+ buffer[length] = '\0';
+
+ buf_reverse (buffer, length);
+ return true;
+
+overflow:
+ if (length > 0)
+ buffer[0] = '\0';
+ return false;
+}
+
+/* Copies IN to buffer OUT with size OUT_SIZE, appending a null terminator. If
+ IN is too long for OUT, or if IN contains a new-line, replaces the tail with
+ "...".
+
+ OUT_SIZE must be at least 16. */
+void
+str_ellipsize (struct substring in, char *out, size_t out_size)
+{
+ assert (out_size >= 16);
+
+ size_t out_maxlen = out_size - 1;
+ if (in.length > out_maxlen - 3)
+ out_maxlen -= 3;
+
+ size_t out_len = 0;
+ while (out_len < in.length
+ && in.string[out_len] != '\n'
+ && in.string[out_len] != '\0'
+ && (in.string[out_len] != '\r'
+ || out_len + 1 >= in.length
+ || in.string[out_len + 1] != '\n'))
+ {
+ int mblen = u8_mblen (CHAR_CAST (const uint8_t *, in.string + out_len),
+ in.length - out_len);
+ if (mblen < 0 || out_len + mblen > out_maxlen)
+ break;
+ out_len += mblen;
+ }
+
+ memcpy (out, in.string, out_len);
+ strcpy (&out[out_len], out_len < in.length ? "..." : "");
+}
+
+/* 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;
+}
+\f
+/* Substrings. */
+
+/* Returns a substring whose contents are the N bytes
+ starting at the (0-based) position START in SS. */
+struct substring
+ss_substr (struct substring ss, size_t start, size_t n)
+{
+ if (start < ss.length)
+ return ss_buffer (ss.string + start, MIN (n, ss.length - start));
+ else
+ return ss_buffer (ss.string + ss.length, 0);
+}
+
+/* Returns a substring whose contents are the first N
+ bytes in SS. */
+struct substring
+ss_head (struct substring ss, size_t n)
+{
+ return ss_buffer (ss.string, MIN (n, ss.length));
+}
+
+/* Returns a substring whose contents are the last N bytes
+ in SS. */
+struct substring
+ss_tail (struct substring ss, size_t n)
+{
+ if (n < ss.length)
+ return ss_buffer (ss.string + (ss.length - n), n);
+ else
+ return ss;
+}
+
+/* Makes a malloc()'d, null-terminated copy of the contents of OLD
+ and stores it in NEW. */
+void
+ss_alloc_substring (struct substring *new, struct substring old)
+{
+ new->string = xmemdup0 (old.string, old.length);
+ new->length = old.length;
+}
+
+/* Allocates room for a N-byte string in NEW. */
+void
+ss_alloc_uninit (struct substring *new, size_t n)
+{
+ new->string = xmalloc (n);
+ new->length = n;
+}
+
+void
+ss_realloc (struct substring *ss, size_t size)
+{
+ ss->string = xrealloc (ss->string, size);
+}
+
+/* Makes a pool_alloc_unaligned()'d, null-terminated 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 + 1);
+ new->length = old.length;
+ memcpy (new->string, old.string, old.length);
+ new->string[old.length] = '\0';
+}
+
+/* Allocates room for a N-byte string in NEW in POOL. */
+void
+ss_alloc_uninit_pool (struct substring *new, size_t n, struct pool *pool)
+{
+ new->string = pool_alloc_unaligned (pool, n);
+ new->length = n;
+}
+
+/* Frees the string that SS points to. */
+void
+ss_dealloc (struct substring *ss)
+{
+ free (ss->string);
+}
+
+/* Exchanges the contents of A and B. */
+void
+ss_swap (struct substring *a, struct substring *b)
+{
+ struct substring tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+/* Truncates SS to at most N bytes in length. */
+void
+ss_truncate (struct substring *ss, size_t n)
+{
+ if (ss->length > n)
+ ss->length = n;
+}
+
+/* Removes trailing bytes in TRIM_SET from SS.
+ Returns number of bytes removed. */
+size_t
+ss_rtrim (struct substring *ss, struct substring trim_set)
+{
+ size_t n = 0;
+ while (n < ss->length
+ && ss_find_byte (trim_set,
+ ss->string[ss->length - n - 1]) != SIZE_MAX)
+ n++;
+ ss->length -= n;
+ return n;
+}
+
+/* Removes leading bytes in TRIM_SET from SS.
+ Returns number of bytes removed. */
+size_t
+ss_ltrim (struct substring *ss, struct substring trim_set)
+{
+ size_t n = ss_span (*ss, trim_set);
+ ss_advance (ss, n);
+ return n;
+}
+
+/* Trims leading and trailing bytes in TRIM_SET from SS. */
+void
+ss_trim (struct substring *ss, struct substring trim_set)
+{
+ ss_ltrim (ss, trim_set);
+ ss_rtrim (ss, trim_set);
+}
+
+/* If the last byte in SS is C, removes it and returns true.
+ Otherwise, returns false without changing the string. */
+bool
+ss_chomp_byte (struct substring *ss, char c)
+{
+ if (ss_last (*ss) == c)
+ {
+ ss->length--;
+ return true;
+ }
+ else
+ return false;
+}
+
+/* If SS ends with SUFFIX, removes it and returns true.
+ Otherwise, returns false without changing the string. */
+bool
+ss_chomp (struct substring *ss, struct substring suffix)
+{
+ if (ss_ends_with (*ss, suffix))
+ {
+ ss->length -= suffix.length;
+ return true;
+ }
+ else
+ return false;
+}
+
+/* Divides SS into tokens separated by any of the DELIMITERS.
+ Each call replaces TOKEN by the next token in SS, or by an
+ empty string if no tokens remain. Returns true if a token was
+ obtained, false otherwise.
+
+ Before the first call, initialize *SAVE_IDX to 0. Do not
+ modify *SAVE_IDX between calls.
+
+ SS divides into exactly one more tokens than it contains
+ delimiters. That is, a delimiter at the start or end of SS or
+ a pair of adjacent delimiters yields an empty token, and the
+ empty string contains a single token. */
+bool
+ss_separate (struct substring ss, struct substring delimiters,
+ size_t *save_idx, struct substring *token)
+{
+ if (*save_idx <= ss_length (ss))
+ {
+ struct substring tmp = ss_substr (ss, *save_idx, SIZE_MAX);
+ size_t length = ss_cspan (tmp, delimiters);
+ *token = ss_head (tmp, length);
+ *save_idx += length + 1;
+ return true;
+ }
+ else
+ {
+ *token = ss_empty ();
+ return false;