+ ds_clear (st);
+ do
+ {
+ if (!ds_read_line (st, stream, SIZE_MAX))
+ return false;
+ (*line_number)++;
+ ds_rtrim (st, ss_cstr (CC_SPACES));
+ }
+ while (ds_chomp_byte (st, '\\'));
+
+ remove_comment (st);
+ return true;
+}
+
+/* Attempts to read SIZE * CNT bytes from STREAM and append them
+ to ST.
+ 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)
+ {
+ size_t try_bytes = xtimes (cnt, size);
+ 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, 1, try_bytes, stream);
+ ds_truncate (st, ds_length (st) - (try_bytes - got_bytes));
+ return got_bytes == try_bytes;
+ }
+ else
+ {
+ errno = ENOMEM;
+ return false;
+ }
+ }
+ else
+ return true;
+}
+
+/* Concatenates S onto ST. */
+void
+ds_put_cstr (struct string *st, const char *s)
+{
+ if (s != NULL)
+ ds_put_substring (st, ss_cstr (s));
+}
+
+/* Concatenates SS to ST. */
+void
+ds_put_substring (struct string *st, struct substring ss)
+{
+ memcpy (ds_put_uninit (st, ss_length (ss)), ss_data (ss), ss_length (ss));
+}
+
+/* Returns ds_end(ST) and THEN increases the length by INCR. */
+char *
+ds_put_uninit (struct string *st, size_t incr)
+{
+ char *end;
+ ds_extend (st, ds_length (st) + incr);
+ end = ds_end (st);
+ st->ss.length += incr;
+ return end;
+}
+
+/* Moves the bytes in ST following offset OFS + OLD_LEN in ST to offset OFS +
+ NEW_LEN and returns the byte at offset OFS. The first min(OLD_LEN, NEW_LEN)
+ bytes at the returned position are unchanged; if NEW_LEN > OLD_LEN then the
+ following NEW_LEN - OLD_LEN bytes are initially indeterminate.
+
+ The intention is that the caller should write NEW_LEN bytes at the returned
+ position, to effectively replace the OLD_LEN bytes previously at that
+ position. */
+char *
+ds_splice_uninit (struct string *st,
+ size_t ofs, size_t old_len, size_t new_len)
+{
+ if (new_len != old_len)
+ {
+ if (new_len > old_len)
+ ds_extend (st, ds_length (st) + (new_len - old_len));
+ memmove (ds_data (st) + (ofs + new_len),
+ ds_data (st) + (ofs + old_len),
+ ds_length (st) - (ofs + old_len));
+ st->ss.length += new_len - old_len;
+ }
+ return ds_data (st) + ofs;
+}
+
+/* Formats FORMAT as a printf string and appends the result to ST. */
+void
+ds_put_format (struct string *st, const char *format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ ds_put_vformat (st, format, args);
+ va_end (args);
+}
+
+/* Formats FORMAT as a printf string and appends the result to ST. */
+void
+ds_put_vformat (struct string *st, const char *format, va_list args_)
+{
+ int avail, needed;
+ va_list args;
+
+ va_copy (args, args_);
+ avail = st->ss.string != NULL ? st->capacity - st->ss.length + 1 : 0;
+ needed = vsnprintf (st->ss.string + st->ss.length, avail, format, args);
+ va_end (args);
+
+ if (needed >= avail)
+ {
+ va_copy (args, args_);
+ vsprintf (ds_put_uninit (st, needed), format, args);
+ va_end (args);
+ }
+ else
+ {
+ /* Some old libc's returned -1 when the destination string
+ was too short. */
+ while (needed == -1)
+ {
+ ds_extend (st, (st->capacity + 1) * 2);
+ avail = st->capacity - st->ss.length + 1;
+
+ va_copy (args, args_);
+ needed = vsnprintf (ds_end (st), avail, format, args);
+ va_end (args);
+ }
+ st->ss.length += needed;
+ }
+}
+
+/* Appends byte CH to ST. */
+void
+ds_put_byte (struct string *st, int ch)
+{
+ ds_put_uninit (st, 1)[0] = ch;
+}
+
+/* Appends CNT copies of byte CH to ST. */
+void
+ds_put_byte_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);
+ /* The documentation for relocate says that casting away const
+ and then freeing is appropriate ... */
+ free (CONST_CAST (char *, rel));
+ }
+}
+
+
+\f
+
+/* 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);
+ }