+/* Substrings. */
+
+/* Returns an empty substring. */
+struct substring
+ss_empty (void)
+{
+ struct substring ss;
+ ss.string = NULL;
+ ss.length = 0;
+ return ss;
+}
+
+/* Returns a substring whose contents are the given C-style
+ string CSTR. */
+struct substring
+ss_cstr (const char *cstr)
+{
+ return ss_buffer (cstr, strlen (cstr));
+}
+
+/* Returns a substring whose contents are the CNT characters in
+ BUFFER. */
+struct substring
+ss_buffer (const char *buffer, size_t cnt)
+{
+ struct substring ss;
+ ss.string = (char *) buffer;
+ ss.length = cnt;
+ return ss;
+}
+
+/* Returns a substring whose contents are the CNT characters
+ starting at the (0-based) position START in SS. */
+struct substring
+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
+ 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)
+{
+ return ss_buffer (ss.string, MIN (cnt, ss.length));
+}
+
+/* Returns a substring whose contents are the last CNT characters
+ in SS. */
+struct substring
+ss_tail (struct substring ss, size_t cnt)
+{
+ if (cnt < ss.length)
+ return ss_buffer (ss.string + (ss.length - cnt), cnt);
+ else
+ return ss;
+}
+
+/* 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)
+{
+ new->string = xmalloc (old.length);
+ new->length = old.length;
+ memcpy (new->string, old.string, old.length);
+}
+
+/* Allocates room for a CNT-character string in NEW. */
+void
+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)
+{
+ free (ss->string);
+}
+
+/* Truncates SS to at most CNT characters in length. */
+void
+ss_truncate (struct substring *ss, size_t cnt)
+{
+ if (ss->length > cnt)
+ ss->length = 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)
+{
+ size_t cnt = 0;
+ while (cnt < ss->length
+ && ss_find_char (trim_set,
+ 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. */
+size_t
+ss_ltrim (struct substring *ss, struct substring trim_set)
+{
+ size_t cnt = ss_span (*ss, trim_set);
+ ss_advance (ss, cnt);
+ return cnt;
+}
+
+/* Trims leading and trailing characters 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 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)
+{
+ if (ss_last (*ss) == c)
+ {
+ ss->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;
+ }
+}
+
+/* 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, 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
+ modify *SAVE_IDX between calls. */
+bool
+ss_tokenize (struct substring ss, struct substring delimiters,
+ size_t *save_idx, struct substring *token)
+{
+ ss_advance (&ss, *save_idx);
+ *save_idx += ss_ltrim (&ss, delimiters);
+ 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. */