3 * Common functions for the DVDrip C utilities.
5 * (c) 2022 Mark Wooding
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the DVD ripping toolset.
12 * DVDrip is free software: you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 3 of the License, or (at your
15 * option) any later version.
17 * DVDrip is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * You should have received a copy of the GNU General Public License
23 * along with DVDrip. If not, see <https://www.gnu.org/licenses/>.
29 /*----- Preliminaries -----------------------------------------------------*/
32 #define _FILE_OFFSET_BITS 64
34 /*----- Header files ------------------------------------------------------*/
53 #include <sys/ioctl.h>
54 #include <sys/select.h>
62 #include <dvdread/dvd_reader.h>
63 #include <dvdread/dvd_udf.h>
64 #include <dvdread/ifo_read.h>
65 #include <dvdread/ifo_types.h>
67 #include "multiprogress.h"
69 /*----- Various macros with wide applicability ----------------------------*/
71 /* `ctype.h' functions are troublesome: in particular, they don't handle
72 * negative characters properly, if they're a thing that your platform
73 * believes in. So we have these macros which fix things up properly.
75 #define CTYPE_HACK(fn, ch) fn((unsigned char)(ch))
76 #define ISDIGIT(ch) CTYPE_HACK(isdigit, ch)
77 #define ISSPACE(ch) CTYPE_HACK(isspace, ch)
79 /* It's easy to screw up the `foocmp' functions by leaving off the comparison
80 * with zero. These macros make it impossible to forget, and put the
81 * relation in the right place syntactically.
83 #define STRCMP(a, op, b) (strcmp((a), (b)) op 0)
84 #define STRNCMP(a, op, b, n) (strncmp((a), (b), (n)) op 0)
85 #define MEMCMP(a, op, b, n) (memcmp((a), (b), (n)) op 0)
87 /* Suppress some code unless we're debugging. */
94 /* Count the number of elements in an array. */
95 #define N(v) (sizeof(v)/sizeof((v)[0]))
97 /* Function attributes. If you're not using GCC to build then you'll need to
98 * say something different here.
100 #if (defined(__GNUC__) && (__GNUC__ > 2 || \
101 (__GNUC__ == 2 && __GNUC_MINOR__ >= 5))) || \
102 (defined(__clang__) && (__clang_major__ > 3 || \
103 (__clang_major__ == 3 && __clang_minor__ >= 3)))
104 # define PRINTF_LIKE(fmt, dots) __attribute__((format(printf, fmt, dots)))
105 # define NORETURN __attribute__((noreturn))
107 # define PRINTF_LIKE(fmt, dots)
111 /*----- Definitions for low-level DVD access ------------------------------*/
113 #define SECTORSZ 2048 /* the DVD sector size */
114 #define SECTORS(n) (((n) + (SECTORSZ - 1))/SECTORSZ)
115 /* convert bytes to * sectors, rounding up */
116 typedef uint_least32_t secaddr
; /* a type for sector numbers */
117 #define PRIuSEC PRIuLEAST32 /* how to print a sector number */
118 #define SECLIMIT 0x00400000 /* upper bound on sector numbers */
120 /*----- Diagnostics -------------------------------------------------------*/
122 extern const char *prog
; /* the program name; set with `set_prog' */
124 extern void set_prog(const char *p
);
125 /* Set the program name to P, stripping any directory names. */
127 extern void vmoan(const char *fmt
, va_list ap
);
128 extern void vmoan_syserr(int err
, const char *fmt
, va_list ap
);
129 /* Low-level warning reporting. See `moan' and `moan_syserr'. */
131 extern PRINTF_LIKE(1, 2) void moan(const char *fmt
, ...);
132 extern PRINTF_LIKE(2, 3) void moan_syserr(int err
, const char *fmt
, ...);
133 /* Print a warning message, given as a `printf'-like format string
134 * FMT and arguments, to standard error. If ERR is nonzero, then
135 * append a colon and the human-readable description of the `errno'
139 extern PRINTF_LIKE(1, 2) NORETURN
void bail(const char *fmt
, ...);
140 extern PRINTF_LIKE(2, 3) NORETURN
141 void bail_syserr(int err
, const char *fmt
, ...);
142 /* Like the corresponding `moan' functions, except that they also
143 * exit with status code 2.
146 /*----- Resizing vectors --------------------------------------------------*/
148 #define DEFVEC(vtype, etype) \
149 typedef struct { etype *v; size_t n, sz; } vtype
150 #define VEC_INIT { 0, 0, 0 }
151 /* Define VTYPE as a (structured) type for vectors holding elements
154 * A vector V has `V.n' elements, addressed as `V.v[0]' up to
158 #define VEC_FREE(vv) do { \
159 free((vv)->v); (vv)->v 0; (vv)->n = (vv)->sz = 0; \
161 /* Free the vector VV. It's safe to free a vector multiple times. */
163 #define VEC_PUSH(p, vv) do { \
165 if ((vv)->n >= (vv)->sz) { \
166 (vv)->sz = (vv)->sz ? 2*(vv)->sz : 32; \
167 _want = (vv)->sz*sizeof(*(vv)->v); \
168 (vv)->v = realloc((vv)->v, _want); \
169 if (!(vv)->v) bail("out of memory allocating %zu bytes", _want); \
171 (p) = &(vv)->v[(vv)->n++]; \
173 /* Add an initialized element to the end of vector VV, storing its
177 /*----- Parsing utilities -------------------------------------------------*/
180 extern double parse_float(const char **p_inout
, unsigned f
,
181 double min
, double max
, const char *what
);
182 extern long parse_int(const char **p_inout
, unsigned f
,
183 long min
, long max
, const char *what
);
184 /* Parse a number starting at *P_IN OUT, advancing that pointer past
185 * it, and return the resulting value. If no number can be read from
186 * the string, or the resulting number is not between MIN and MAX
187 * inclusive, or the `PNF_JUNK' bit is clear in F and the number is
188 * followed by anything other than whitespace, then report a fatal
189 * error, quoting WHAT as having been expected.
192 /*----- System utilities --------------------------------------------------*/
194 extern double tvdiff(const struct timeval
*tv_lo
,
195 const struct timeval
*tv_hi
);
196 /* Return the (signed) difference from TV_LO to TV_HI, as a floating-
197 * point number of seconds.
200 extern void sit(double t
);
201 /* Do nothing for T seconds. As implied by the type, T may be
205 extern void carefully_write(int fd
, const void *buf
, size_t sz
);
206 /* Write SZ bytes to file descriptor FD, starting at BUF. Report a
207 * fatal error if this fails. Correctly handles short writes and
211 extern void open_file_on_demand(const char *file
, FILE **fp_inout
,
213 /* If *FP_INOUT is not null, then do nothing. Otherwise, open FILE
214 * for writing, storing the resulting stream handle in *FP_INOUT; if
215 * this can't be done then report a fatal error, quoting WHAT as the
219 extern void check_write(FILE *fp
, const char *what
);
220 /* Flush any remaining output to FP and check that there were no
221 * errors. If there were problems, report a fatal error quoting WHAT
222 * as the kind of file.
225 extern void carefully_fclose(FILE *fp
, const char *what
);
226 /* Flush output to FP and close it, reporting fatal errors as for
227 * `check_write'. If FP is null, then do nothing.
230 extern off_t
device_size(int fd
, const char *file
, int *blksz_out
);
231 /* Determine the size of the device referred to by FD. Specifically,
232 * if FD is a regular file, then this is simply the size of the file;
233 * if FD is a block device, then this is the size of the block
234 * device. Return the resulting size, and, in the case of a block
235 * device only, store the block size in *BLKSZ_OUT. (Hence,
236 * *BLKSZ_OUT will be left unchanged if FD is open on a regular
237 * file.) If FD refers to any other kind of object then report a
241 /*----- Progress utilities ------------------------------------------------*/
243 struct banner_progress_item
{
244 /* A progress item which simply shows a banner message. */
245 struct progress_item _base
;
249 extern struct progress_state progress
; /* the shared progress reporter */
251 extern void show_banner(const char *msg
);
252 extern void hide_banner(void);
253 /* Show or hide a banner reporting a message. If a banner is already
254 * showing, then `show_banner' just changes the message.
257 /*----- DVD utilities -----------------------------------------------------*/
259 extern void open_dvd(const char *device
, int mode
,
260 int *fd_out
, dvd_reader_t
**dvd_out
);
261 /* Open the DEVICE. If FD_OUT is not null, then open a file
262 * descriptor onto the device, with the given open(2)-style MODE,
263 * storing the descriptor in *FD_OUT; if DVD_OUT is not null, then
264 * open a `libdvdread' handle onto the devie and store it in
265 * *DVD_OUT. If both are null, then why are you calling this
268 * If DEVICE refers to an actual block device, and no medium is
269 * currently inserted, then put up a banner prompting the user and
270 * wait for a medium to be inserted. Other problems are reported as
274 enum { RAW
, IFO
, VOB
, BUP
};
275 typedef uint_least32_t ident
;
276 /* A convenient name for interesting files on a DVD. It consists of
279 * * A `kind', which is `RAW', `IFO', `VOB', or `BUP'. `RAW' is a
280 * special token which refers to the whole disc; the other kinds
281 * refer to files in the DVD filesystem with the corresponding
284 * * A `title', which is a number between 0 and 99 inclusive.
285 * Title zero refers to the video management information and its
286 * corresponding menu; nonzero numbers refer to video titlesets.
288 * * A `part', which is only relevant for `VOB' files; part 0
289 * refers to the menu data, while parts 1 to 9 inclusive refer to
290 * portions of the video titleset itself.
292 * Components which aren't applicable must be zero, so that idents
293 * can conveniently be compared as integers (so, specifically, the
294 * title, if kind is `RAW', and the part, if kind is not `VOB' or
298 static inline ident
mkident(unsigned kind
, unsigned title
, unsigned part
)
299 { return (((ident
)kind
<< 0) | ((ident
)title
<< 8) | ((ident
)part
<< 16)); }
300 static inline unsigned id_kind(ident id
) { return ((id
>> 0)&0x0ff); }
301 static inline unsigned id_title(ident id
) { return ((id
>> 8)&0x0ff); }
302 static inline unsigned id_part(ident id
) { return ((id
>> 16)&0x0ff); }
303 /* Functions for constructing and picking apart the fields of an
307 #define MAXFNSZ (1 + 8 + 1 + 12 + 1)
308 extern void store_filename(char *buf
, ident id
);
309 /* Store in BUF the filename corresponding to the ident ID. The
310 * filename will be at most `MAXFNSZ' bytes long, including the
314 #define DIF_MUSTVOLINF 1u
315 #define DIF_MUSTIFOHASH 2u
316 #define MAXIDSZ (32 + 1 + 32 + 1 + 32 + 1)
317 extern int dvd_id(char *p
, dvd_reader_t
*dvd
, unsigned f
, const char *file
);
318 /* Determine a (hopefully) unique identifier for DVD. The identifier
319 * consists of two parts:
321 * * the volume name and serial number, from the volume
324 * * a cryptographic hash of the `.IFO' files on the disc.
326 * The identifier is written, as plain text, at P, and consists of at
327 * most `MAXIDSZ' bytes, including the terminating zero.
329 * It's possible that determining either, or both, of these might
330 * fail: the behaviour is controlled by the `DIF_MUSTVOLINF' and
331 * `DIF_MUSTIFOHASH' flags in F: if the volume name/serial number, or
332 * `.IFO' hash, respectively, can't be determined, and the
333 * corresponding flag is clear, then a placeholder error message is
334 * written to the output buffer in place of the correct data; if the
335 * flag is set, then a warning message is printed to standard error
336 * and -1 is returned. In practice, the `.IFO' hash is more likely
337 * to be computed successfully, and probably more likely to actually
340 * Returns zero if the identifier was successfully determined, within
341 * the parameters set by the flags.
344 /*----- That's all, folks -------------------------------------------------*/