* exit with status code 2.
*/
+/*----- Resizing buffers --------------------------------------------------*/
+
+struct buf {
+ /* A buffer for a string which can grow automatically. */
+
+ char *p; /* pointer to the buffer */
+ size_t n, sz; /* string length, buffer size */
+};
+#define BUF_INIT { 0, 0, 0 }
+
+static inline void buf_rewind(struct buf *b) { b->n = 0; }
+ /* Throw away the current contents of B so that new stuff gets added
+ * to the beginning.
+ */
+
+static inline void buf_free(struct buf *b)
+ { free(b->p); b->p = 0; b->n = b->sz = 0; }
+ /* Release the memory allocated for B. The buffer can be reused
+ * immediately and/or freed again safely.
+ */
+
+extern void buf__grow(struct buf *b);
+ /* Make B's buffer larger, so that (at least) one extra byte can be
+ * written to it. (Internal to `buf_putc'.)
+ */
+
+static inline void buf_putc(struct buf *b, int ch)
+ { if (b->n >= b->sz) buf__grow(b); b->p[b->n++] = ch; }
+ /* Append the character CH to the buffer B. */
+
+static inline void buf_putz(struct buf *b)
+ { if (b->n >= b->sz) buf__grow(b); b->p[b->n] = 0; }
+ /* Append a zero byte to B without increasing the string length, so
+ * that a future `buf_putc' will overwrite it.
+ */
+
/*----- Resizing vectors --------------------------------------------------*/
#define DEFVEC(vtype, etype) \
} while (0)
/* Free the vector VV. It's safe to free a vector multiple times. */
+extern void *vec__grow(void *p, size_t esz, size_t *sz_inout);
+ /* Extend the buffer P, which currently has space for *SZ_INOUT
+ * elements, each ESZ bytes in size, so that there's space for at
+ * least one one more; return the new buffer address, and update
+ * *SZ_INOUT with the new size.
+ */
+
#define VEC_PUSH(p, vv) do { \
- size_t _want; \
- if ((vv)->n >= (vv)->sz) { \
- (vv)->sz = (vv)->sz ? 2*(vv)->sz : 32; \
- _want = (vv)->sz*sizeof(*(vv)->v); \
- (vv)->v = realloc((vv)->v, _want); \
- if (!(vv)->v) bail("out of memory allocating %zu bytes", _want); \
- } \
+ if ((vv)->n >= (vv)->sz) \
+ (vv)->v = vec__grow((vv)->v, sizeof(*(vv)->v), &(vv)->sz); \
(p) = &(vv)->v[(vv)->n++]; \
} while (0)
/* Add an initialized element to the end of vector VV, storing its
* fractional.
*/
+extern int read_line(FILE *fp, struct buf *b);
+ /* Read a line from FP, appending it to the buffer B, leaving the
+ * string in B null-terminated (as if by `buf_putz'). Return 0 on
+ * success, or -1 if nothing was read (not even an empty line) before
+ * we encountered end-of-file or a read error.
+ */
+
extern void carefully_write(int fd, const void *buf, size_t sz);
/* Write SZ bytes to file descriptor FD, starting at BUF. Report a
* fatal error if this fails. Correctly handles short writes and
/*----- DVD utilities -----------------------------------------------------*/
-extern void open_dvd(const char *device, int mode,
- int *fd_out, dvd_reader_t **dvd_out);
+extern int open_dvd(const char *device, int mode,
+ int *fd_out, dvd_reader_t **dvd_out);
/* Open the DEVICE. If FD_OUT is not null, then open a file
* descriptor onto the device, with the given open(2)-style MODE,
* storing the descriptor in *FD_OUT; if DVD_OUT is not null, then
* open a `libdvdread' handle onto the devie and store it in
* *DVD_OUT. If both are null, then why are you calling this
- * function?
+ * function? Returns 0 on success or -1 on failure.
*
* If DEVICE refers to an actual block device, and no medium is
* currently inserted, then put up a banner prompting the user and
- * wait for a medium to be inserted. Other problems are reported as
- * fatal errors.
+ * wait for a medium to be inserted. Other problems are reported to
+ * stderr.
*/
enum { RAW, IFO, VOB, BUP };