+/* -*-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 <https://www.gnu.org/licenses/>.
+ */
+
#ifndef LIB_H
#define LIB_H
+/*----- Preliminaries -----------------------------------------------------*/
+
#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64
+/*----- Header files ------------------------------------------------------*/
+
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#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]))
+/* 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))
-#define SECTORSZ 2048
+/*----- 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 */
+
+/*----- 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.
+ */
+
+/*----- 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 void sit(double t);
+ /* Do nothing for T seconds. As implied by the type, T may be
+ * fractional.
+ */
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.
+ */
+
+/*----- 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;
+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 void 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?
+ *
+ * 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.
+ */
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 (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.
+ */
+
+/*----- That's all, folks -------------------------------------------------*/
#endif