X-Git-Url: https://git.distorted.org.uk/~mdw/dvdrip/blobdiff_plain/972381afacd6efbd0d20c77ff7e00981f95b7418..refs/heads/mdw/cleanup:/lib.h diff --git a/lib.h b/lib.h index 069cd37..7c4a0cb 100644 --- a/lib.h +++ b/lib.h @@ -1,9 +1,38 @@ +/* -*-c-*- + * + * Common functions for the DVDrip C utilities. + * + * (c) 2022 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the DVD ripping toolset. + * + * DVDrip is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * DVDrip is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with DVDrip. If not, see . + */ + #ifndef LIB_H #define LIB_H +/*----- Preliminaries -----------------------------------------------------*/ + #define _GNU_SOURCE #define _FILE_OFFSET_BITS 64 +/*----- Header files ------------------------------------------------------*/ + #include #include #include @@ -37,85 +66,326 @@ #include "multiprogress.h" +/*----- Various macros with wide applicability ----------------------------*/ + +/* `ctype.h' functions are troublesome: in particular, they don't handle + * negative characters properly, if they're a thing that your platform + * believes in. So we have these macros which fix things up properly. + */ #define CTYPE_HACK(fn, ch) fn((unsigned char)(ch)) #define ISDIGIT(ch) CTYPE_HACK(isdigit, ch) #define ISSPACE(ch) CTYPE_HACK(isspace, ch) +/* It's easy to screw up the `foocmp' functions by leaving off the comparison + * with zero. These macros make it impossible to forget, and put the + * relation in the right place syntactically. + */ #define STRCMP(a, op, b) (strcmp((a), (b)) op 0) #define STRNCMP(a, op, b, n) (strncmp((a), (b), (n)) op 0) #define MEMCMP(a, op, b, n) (memcmp((a), (b), (n)) op 0) +/* Suppress some code unless we're debugging. */ #ifdef DEBUG # define D(x) x #else # define D(x) #endif +/* Count the number of elements in an array. */ #define N(v) (sizeof(v)/sizeof((v)[0])) -#define SECTORSZ 2048 +/* Function attributes. If you're not using GCC to build then you'll need to + * say something different here. + */ +#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 ------------------------------*/ + +#define SECTORSZ 2048 /* the DVD sector size */ #define SECTORS(n) (((n) + (SECTORSZ - 1))/SECTORSZ) -typedef uint_least32_t secaddr; -#define PRIuSEC PRIuLEAST32 -#define SECLIMIT 0x00400000 + /* convert bytes to * sectors, rounding up */ +typedef uint_least32_t secaddr; /* a type for sector numbers */ +#define PRIuSEC PRIuLEAST32 /* how to print a sector number */ +#define SECLIMIT 0x00400000 /* upper bound on sector numbers */ -#define PRINTF_LIKE(fmt, dots) __attribute__((format(printf, fmt, dots))) -#define NORETURN __attribute__((noreturn)) +/*----- Diagnostics -------------------------------------------------------*/ -extern const char *prog; +extern const char *prog; /* the program name; set with `set_prog' */ extern void set_prog(const char *p); + /* Set the program name to P, stripping any directory names. */ + extern void vmoan(const char *fmt, va_list ap); extern void vmoan_syserr(int err, const char *fmt, va_list ap); + /* Low-level warning reporting. See `moan' and `moan_syserr'. */ + extern PRINTF_LIKE(1, 2) void moan(const char *fmt, ...); extern PRINTF_LIKE(2, 3) void moan_syserr(int err, const char *fmt, ...); + /* Print a warning message, given as a `printf'-like format string + * FMT and arguments, to standard error. If ERR is nonzero, then + * append a colon and the human-readable description of the `errno' + * value ERR. + */ + extern PRINTF_LIKE(1, 2) NORETURN void bail(const char *fmt, ...); extern PRINTF_LIKE(2, 3) NORETURN void bail_syserr(int err, const char *fmt, ...); + /* Like the corresponding `moan' functions, except that they also + * 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 extern double parse_float(const char **p_inout, unsigned f, double min, double max, const char *what); extern long parse_int(const char **p_inout, unsigned f, long min, long max, const char *what); + /* Parse a number starting at *P_IN OUT, advancing that pointer past + * it, and return the resulting value. If no number can be read from + * the string, or the resulting number is not between MIN and MAX + * inclusive, or the `PNF_JUNK' bit is clear in F and the number is + * followed by anything other than whitespace, then report a fatal + * error, quoting WHAT as having been expected. + */ + +/*----- 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 + * `EINTR'. + */ + extern void open_file_on_demand(const char *file, FILE **fp_inout, const char *what); + /* If *FP_INOUT is not null, then do nothing. Otherwise, open FILE + * for writing, storing the resulting stream handle in *FP_INOUT; if + * this can't be done then report a fatal error, quoting WHAT as the + * kind of file. + */ + extern void check_write(FILE *fp, const char *what); + /* Flush any remaining output to FP and check that there were no + * errors. If there were problems, report a fatal error quoting WHAT + * as the kind of file. + */ + extern void carefully_fclose(FILE *fp, const char *what); + /* Flush output to FP and close it, reporting fatal errors as for + * `check_write'. If FP is null, then do nothing. + */ + extern off_t device_size(int fd, const char *file, int *blksz_out); + /* Determine the size of the device referred to by FD. Specifically, + * if FD is a regular file, then this is simply the size of the file; + * if FD is a block device, then this is the size of the block + * device. Return the resulting size, and, in the case of a block + * 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 quoting FILE as the name of the device. + */ + +/*----- Progress utilities ------------------------------------------------*/ + +struct banner_progress_item { + /* A progress item which simply shows a banner message. */ + struct progress_item _base; + const char *msg; +}; + +extern struct progress_state progress; /* the shared progress reporter */ + +extern void show_banner(const char *msg); +extern void hide_banner(void); + /* Show or hide a banner reporting a message. If a banner is already + * showing, then `show_banner' just changes the message. + */ + +/*----- DVD utilities -----------------------------------------------------*/ + +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? 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 to + * stderr. + */ enum { RAW, IFO, VOB, BUP }; typedef uint_least32_t ident; + /* A convenient name for interesting files on a DVD. It consists of + * three components: + * + * * A `kind', which is `RAW', `IFO', `VOB', or `BUP'. `RAW' is a + * special token which refers to the whole disc; the other kinds + * refer to files in the DVD filesystem with the corresponding + * extensions. + * + * * A `title', which is a number between 0 and 99 inclusive. + * Title zero refers to the video management information and its + * corresponding menu; nonzero numbers refer to video titlesets. + * + * * A `part', which is only relevant for `VOB' files; part 0 + * refers to the menu data, while parts 1 to 9 inclusive refer to + * portions of the video titleset itself. + * + * Components which aren't applicable must be zero, so that idents + * can conveniently be compared as integers (so, specifically, the + * title, if kind is `RAW', and the part, if kind is not `VOB' or + * title is zero. + */ static inline ident mkident(unsigned kind, unsigned title, unsigned part) { return (((ident)kind << 0) | ((ident)title << 8) | ((ident)part << 16)); } static inline unsigned id_kind(ident id) { return ((id >> 0)&0x0ff); } static inline unsigned id_title(ident id) { return ((id >> 8)&0x0ff); } static inline unsigned id_part(ident id) { return ((id >> 16)&0x0ff); } + /* Functions for constructing and picking apart the fields of an + * ident. + */ #define MAXFNSZ (1 + 8 + 1 + 12 + 1) extern void store_filename(char *buf, ident id); + /* Store in BUF the filename corresponding to the ident ID. The + * filename will be at most `MAXFNSZ' bytes long, including the + * terminating zero. + */ #define DIF_MUSTVOLINF 1u #define DIF_MUSTIFOHASH 2u -#define MAXIDSZ 99 +#define MAXIDSZ (32 + 1 + 32 + 1 + 32 + 1) extern int dvd_id(char *p, dvd_reader_t *dvd, unsigned f, const char *file); + /* Determine a (hopefully) unique identifier for DVD. The identifier + * consists of two parts: + * + * * the volume name and serial number, from the volume + * information, and + * + * * a cryptographic hash of the `.IFO' files on the disc. + * + * The identifier is written, as plain text, at P, and consists of at + * most `MAXIDSZ' bytes, including the terminating zero. + * + * It's possible that determining either, or both, of these might + * fail: the behaviour is controlled by the `DIF_MUSTVOLINF' and + * `DIF_MUSTIFOHASH' flags in F: if the volume name/serial number, or + * `.IFO' hash, respectively, can't be determined, and the + * corresponding flag is clear, then a placeholder error message is + * written to the output buffer in place of the correct data; if the + * flag is set, then a warning message is printed to standard error + * and -1 is returned. In practice, the `.IFO' hash is more likely + * to be computed successfully, and probably more likely to actually + * be unique. + * + * Returns zero if the identifier was successfully determined, within + * the parameters set by the flags. + */ -struct banner_progress_item { - struct progress_item _base; - const char *msg; -}; - -extern struct progress_state progress; - -extern void show_banner(const char *msg); -extern void hide_banner(void); - -extern void open_dvd(const char *device, int mode, - int *fd_out, dvd_reader_t **dvd_out); +/*----- That's all, folks -------------------------------------------------*/ #endif