/* Function attributes. If you're not using GCC to build then you'll need to
* say something different here.
*/
-#define PRINTF_LIKE(fmt, dots) __attribute__((format(printf, fmt, dots)))
-#define NORETURN __attribute__((noreturn))
+#if (defined(__GNUC__) && (__GNUC__ > 2 || \
+ (__GNUC__ == 2 && __GNUC_MINOR__ >= 5))) || \
+ (defined(__clang__) && (__clang_major__ > 3 || \
+ (__clang_major__ == 3 && __clang_minor__ >= 3)))
+# define PRINTF_LIKE(fmt, dots) __attribute__((format(printf, fmt, dots)))
+# define NORETURN __attribute__((noreturn))
+#else
+# define PRINTF_LIKE(fmt, dots)
+# define NORETURN
+#endif
/*----- Definitions for low-level DVD access ------------------------------*/
* 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) \
+ typedef struct { etype *v; size_t n, sz; } vtype
+#define VEC_INIT { 0, 0, 0 }
+ /* Define VTYPE as a (structured) type for vectors holding elements
+ * of ETYPE.
+ *
+ * A vector V has `V.n' elements, addressed as `V.v[0]' up to
+ * `V.v[V.n - 1]'.
+ */
+
+#define VEC_FREE(vv) do { \
+ free((vv)->v); (vv)->v 0; (vv)->n = (vv)->sz = 0; \
+} 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 { \
+ 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
+ * address in P.
+ */
+
/*----- Parsing utilities -------------------------------------------------*/
#define PNF_JUNK 1u
/*----- System utilities --------------------------------------------------*/
+extern double tvdiff(const struct timeval *tv_lo,
+ const struct timeval *tv_hi);
+ /* Return the (signed) difference from TV_LO to TV_HI, as a floating-
+ * point number of seconds.
+ */
+
extern void sit(double t);
/* Do nothing for T seconds. As implied by the type, T may be
* 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
* device only, store the block size in *BLKSZ_OUT. (Hence,
* *BLKSZ_OUT will be left unchanged if FD is open on a regular
* file.) If FD refers to any other kind of object then report a
- * fatal error.
+ * fatal error quoting FILE as the name of the device.
*/
/*----- Progress utilities ------------------------------------------------*/
/*----- 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 };