Commit | Line | Data |
---|---|---|
be15bd14 MW |
1 | /* -*-c-*- |
2 | * | |
3 | * Common functions for the DVDrip C utilities. | |
4 | * | |
5 | * (c) 2022 Mark Wooding | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of the DVD ripping toolset. | |
11 | * | |
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. | |
16 | * | |
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 | |
20 | * for more details. | |
21 | * | |
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/>. | |
24 | */ | |
25 | ||
dc53ebfa MW |
26 | #ifndef LIB_H |
27 | #define LIB_H | |
28 | ||
be15bd14 MW |
29 | /*----- Preliminaries -----------------------------------------------------*/ |
30 | ||
dc53ebfa MW |
31 | #define _GNU_SOURCE |
32 | #define _FILE_OFFSET_BITS 64 | |
33 | ||
be15bd14 MW |
34 | /*----- Header files ------------------------------------------------------*/ |
35 | ||
dc53ebfa MW |
36 | #include <assert.h> |
37 | #include <ctype.h> | |
38 | #include <errno.h> | |
39 | #include <float.h> | |
40 | #include <inttypes.h> | |
41 | #include <limits.h> | |
42 | #include <locale.h> | |
43 | #include <math.h> | |
44 | #include <stdarg.h> | |
45 | #include <stdint.h> | |
46 | #include <stdio.h> | |
47 | #include <stdlib.h> | |
48 | #include <string.h> | |
49 | #include <time.h> | |
50 | ||
51 | #include <unistd.h> | |
52 | #include <fcntl.h> | |
53 | #include <sys/ioctl.h> | |
54 | #include <sys/select.h> | |
55 | #include <sys/stat.h> | |
56 | #include <sys/time.h> | |
57 | ||
58 | #include <getopt.h> | |
59 | ||
60 | #include <linux/fs.h> | |
61 | ||
62 | #include <dvdread/dvd_reader.h> | |
63 | #include <dvdread/dvd_udf.h> | |
64 | #include <dvdread/ifo_read.h> | |
65 | #include <dvdread/ifo_types.h> | |
66 | ||
67 | #include "multiprogress.h" | |
68 | ||
be15bd14 MW |
69 | /*----- Various macros with wide applicability ----------------------------*/ |
70 | ||
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. | |
74 | */ | |
dc53ebfa MW |
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) | |
78 | ||
be15bd14 MW |
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. | |
82 | */ | |
dc53ebfa MW |
83 | #define STRCMP(a, op, b) (strcmp((a), (b)) op 0) |
84 | #define STRNCMP(a, op, b, n) (strncmp((a), (b), (n)) op 0) | |
2bbc0426 | 85 | #define MEMCMP(a, op, b, n) (memcmp((a), (b), (n)) op 0) |
dc53ebfa | 86 | |
be15bd14 | 87 | /* Suppress some code unless we're debugging. */ |
dc53ebfa MW |
88 | #ifdef DEBUG |
89 | # define D(x) x | |
90 | #else | |
91 | # define D(x) | |
92 | #endif | |
93 | ||
be15bd14 | 94 | /* Count the number of elements in an array. */ |
dc53ebfa MW |
95 | #define N(v) (sizeof(v)/sizeof((v)[0])) |
96 | ||
be15bd14 MW |
97 | /* Function attributes. If you're not using GCC to build then you'll need to |
98 | * say something different here. | |
99 | */ | |
33d28756 MW |
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)) | |
106 | #else | |
107 | # define PRINTF_LIKE(fmt, dots) | |
108 | # define NORETURN | |
109 | #endif | |
1b77f6d3 | 110 | |
be15bd14 MW |
111 | /*----- Definitions for low-level DVD access ------------------------------*/ |
112 | ||
113 | #define SECTORSZ 2048 /* the DVD sector size */ | |
dc53ebfa | 114 | #define SECTORS(n) (((n) + (SECTORSZ - 1))/SECTORSZ) |
be15bd14 MW |
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 */ | |
119 | ||
120 | /*----- Diagnostics -------------------------------------------------------*/ | |
dc53ebfa | 121 | |
be15bd14 | 122 | extern const char *prog; /* the program name; set with `set_prog' */ |
dc53ebfa MW |
123 | |
124 | extern void set_prog(const char *p); | |
be15bd14 MW |
125 | /* Set the program name to P, stripping any directory names. */ |
126 | ||
dc53ebfa | 127 | extern void vmoan(const char *fmt, va_list ap); |
7ea9ce2b | 128 | extern void vmoan_syserr(int err, const char *fmt, va_list ap); |
be15bd14 MW |
129 | /* Low-level warning reporting. See `moan' and `moan_syserr'. */ |
130 | ||
dc53ebfa | 131 | extern PRINTF_LIKE(1, 2) void moan(const char *fmt, ...); |
7ea9ce2b | 132 | extern PRINTF_LIKE(2, 3) void moan_syserr(int err, const char *fmt, ...); |
be15bd14 MW |
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' | |
136 | * value ERR. | |
137 | */ | |
138 | ||
dc53ebfa MW |
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, ...); | |
be15bd14 MW |
142 | /* Like the corresponding `moan' functions, except that they also |
143 | * exit with status code 2. | |
144 | */ | |
145 | ||
c62cd11a MW |
146 | /*----- Resizing buffers --------------------------------------------------*/ |
147 | ||
148 | struct buf { | |
149 | /* A buffer for a string which can grow automatically. */ | |
150 | ||
151 | char *p; /* pointer to the buffer */ | |
152 | size_t n, sz; /* string length, buffer size */ | |
153 | }; | |
154 | #define BUF_INIT { 0, 0, 0 } | |
155 | ||
156 | static inline void buf_rewind(struct buf *b) { b->n = 0; } | |
157 | /* Throw away the current contents of B so that new stuff gets added | |
158 | * to the beginning. | |
159 | */ | |
160 | ||
161 | static inline void buf_free(struct buf *b) | |
162 | { free(b->p); b->p = 0; b->n = b->sz = 0; } | |
163 | /* Release the memory allocated for B. The buffer can be reused | |
164 | * immediately and/or freed again safely. | |
165 | */ | |
166 | ||
167 | extern void buf__grow(struct buf *b); | |
168 | /* Make B's buffer larger, so that (at least) one extra byte can be | |
169 | * written to it. (Internal to `buf_putc'.) | |
170 | */ | |
171 | ||
172 | static inline void buf_putc(struct buf *b, int ch) | |
173 | { if (b->n >= b->sz) buf__grow(b); b->p[b->n++] = ch; } | |
174 | /* Append the character CH to the buffer B. */ | |
175 | ||
176 | static inline void buf_putz(struct buf *b) | |
177 | { if (b->n >= b->sz) buf__grow(b); b->p[b->n] = 0; } | |
178 | /* Append a zero byte to B without increasing the string length, so | |
179 | * that a future `buf_putc' will overwrite it. | |
180 | */ | |
181 | ||
cd997467 MW |
182 | /*----- Resizing vectors --------------------------------------------------*/ |
183 | ||
184 | #define DEFVEC(vtype, etype) \ | |
185 | typedef struct { etype *v; size_t n, sz; } vtype | |
186 | #define VEC_INIT { 0, 0, 0 } | |
187 | /* Define VTYPE as a (structured) type for vectors holding elements | |
188 | * of ETYPE. | |
189 | * | |
190 | * A vector V has `V.n' elements, addressed as `V.v[0]' up to | |
191 | * `V.v[V.n - 1]'. | |
192 | */ | |
193 | ||
194 | #define VEC_FREE(vv) do { \ | |
195 | free((vv)->v); (vv)->v 0; (vv)->n = (vv)->sz = 0; \ | |
196 | } while (0) | |
197 | /* Free the vector VV. It's safe to free a vector multiple times. */ | |
198 | ||
42c936a1 MW |
199 | extern void *vec__grow(void *p, size_t esz, size_t *sz_inout); |
200 | /* Extend the buffer P, which currently has space for *SZ_INOUT | |
201 | * elements, each ESZ bytes in size, so that there's space for at | |
202 | * least one one more; return the new buffer address, and update | |
203 | * *SZ_INOUT with the new size. | |
204 | */ | |
205 | ||
cd997467 | 206 | #define VEC_PUSH(p, vv) do { \ |
42c936a1 MW |
207 | if ((vv)->n >= (vv)->sz) \ |
208 | (vv)->v = vec__grow((vv)->v, sizeof(*(vv)->v), &(vv)->sz); \ | |
cd997467 MW |
209 | (p) = &(vv)->v[(vv)->n++]; \ |
210 | } while (0) | |
211 | /* Add an initialized element to the end of vector VV, storing its | |
212 | * address in P. | |
213 | */ | |
214 | ||
be15bd14 | 215 | /*----- Parsing utilities -------------------------------------------------*/ |
dc53ebfa | 216 | |
f82e4cd7 MW |
217 | #define PNF_JUNK 1u |
218 | extern double parse_float(const char **p_inout, unsigned f, | |
219 | double min, double max, const char *what); | |
220 | extern long parse_int(const char **p_inout, unsigned f, | |
221 | long min, long max, const char *what); | |
be15bd14 MW |
222 | /* Parse a number starting at *P_IN OUT, advancing that pointer past |
223 | * it, and return the resulting value. If no number can be read from | |
224 | * the string, or the resulting number is not between MIN and MAX | |
225 | * inclusive, or the `PNF_JUNK' bit is clear in F and the number is | |
226 | * followed by anything other than whitespace, then report a fatal | |
227 | * error, quoting WHAT as having been expected. | |
228 | */ | |
229 | ||
230 | /*----- System utilities --------------------------------------------------*/ | |
f82e4cd7 | 231 | |
cd997467 MW |
232 | extern double tvdiff(const struct timeval *tv_lo, |
233 | const struct timeval *tv_hi); | |
234 | /* Return the (signed) difference from TV_LO to TV_HI, as a floating- | |
235 | * point number of seconds. | |
236 | */ | |
237 | ||
dc53ebfa | 238 | extern void sit(double t); |
be15bd14 MW |
239 | /* Do nothing for T seconds. As implied by the type, T may be |
240 | * fractional. | |
241 | */ | |
dc53ebfa | 242 | |
d0a95de5 MW |
243 | extern int read_line(FILE *fp, struct buf *b); |
244 | /* Read a line from FP, appending it to the buffer B, leaving the | |
245 | * string in B null-terminated (as if by `buf_putz'). Return 0 on | |
246 | * success, or -1 if nothing was read (not even an empty line) before | |
247 | * we encountered end-of-file or a read error. | |
248 | */ | |
249 | ||
00a5be1d | 250 | extern void carefully_write(int fd, const void *buf, size_t sz); |
be15bd14 MW |
251 | /* Write SZ bytes to file descriptor FD, starting at BUF. Report a |
252 | * fatal error if this fails. Correctly handles short writes and | |
253 | * `EINTR'. | |
254 | */ | |
255 | ||
00a5be1d MW |
256 | extern void open_file_on_demand(const char *file, FILE **fp_inout, |
257 | const char *what); | |
be15bd14 MW |
258 | /* If *FP_INOUT is not null, then do nothing. Otherwise, open FILE |
259 | * for writing, storing the resulting stream handle in *FP_INOUT; if | |
260 | * this can't be done then report a fatal error, quoting WHAT as the | |
261 | * kind of file. | |
262 | */ | |
263 | ||
00a5be1d | 264 | extern void check_write(FILE *fp, const char *what); |
be15bd14 MW |
265 | /* Flush any remaining output to FP and check that there were no |
266 | * errors. If there were problems, report a fatal error quoting WHAT | |
267 | * as the kind of file. | |
268 | */ | |
269 | ||
00a5be1d | 270 | extern void carefully_fclose(FILE *fp, const char *what); |
be15bd14 MW |
271 | /* Flush output to FP and close it, reporting fatal errors as for |
272 | * `check_write'. If FP is null, then do nothing. | |
273 | */ | |
274 | ||
4bd4876f | 275 | extern off_t device_size(int fd, const char *file, int *blksz_out); |
be15bd14 MW |
276 | /* Determine the size of the device referred to by FD. Specifically, |
277 | * if FD is a regular file, then this is simply the size of the file; | |
278 | * if FD is a block device, then this is the size of the block | |
279 | * device. Return the resulting size, and, in the case of a block | |
280 | * device only, store the block size in *BLKSZ_OUT. (Hence, | |
281 | * *BLKSZ_OUT will be left unchanged if FD is open on a regular | |
282 | * file.) If FD refers to any other kind of object then report a | |
3ff388b0 | 283 | * fatal error quoting FILE as the name of the device. |
be15bd14 MW |
284 | */ |
285 | ||
286 | /*----- Progress utilities ------------------------------------------------*/ | |
00a5be1d | 287 | |
1b77f6d3 | 288 | struct banner_progress_item { |
be15bd14 | 289 | /* A progress item which simply shows a banner message. */ |
1b77f6d3 MW |
290 | struct progress_item _base; |
291 | const char *msg; | |
292 | }; | |
293 | ||
be15bd14 | 294 | extern struct progress_state progress; /* the shared progress reporter */ |
1b77f6d3 MW |
295 | |
296 | extern void show_banner(const char *msg); | |
297 | extern void hide_banner(void); | |
be15bd14 MW |
298 | /* Show or hide a banner reporting a message. If a banner is already |
299 | * showing, then `show_banner' just changes the message. | |
300 | */ | |
301 | ||
302 | /*----- DVD utilities -----------------------------------------------------*/ | |
1b77f6d3 | 303 | |
d23998cb MW |
304 | extern int open_dvd(const char *device, int mode, |
305 | int *fd_out, dvd_reader_t **dvd_out); | |
be15bd14 MW |
306 | /* Open the DEVICE. If FD_OUT is not null, then open a file |
307 | * descriptor onto the device, with the given open(2)-style MODE, | |
308 | * storing the descriptor in *FD_OUT; if DVD_OUT is not null, then | |
309 | * open a `libdvdread' handle onto the devie and store it in | |
310 | * *DVD_OUT. If both are null, then why are you calling this | |
d23998cb | 311 | * function? Returns 0 on success or -1 on failure. |
be15bd14 MW |
312 | * |
313 | * If DEVICE refers to an actual block device, and no medium is | |
314 | * currently inserted, then put up a banner prompting the user and | |
d23998cb MW |
315 | * wait for a medium to be inserted. Other problems are reported to |
316 | * stderr. | |
be15bd14 | 317 | */ |
1b77f6d3 | 318 | |
dc53ebfa MW |
319 | enum { RAW, IFO, VOB, BUP }; |
320 | typedef uint_least32_t ident; | |
be15bd14 MW |
321 | /* A convenient name for interesting files on a DVD. It consists of |
322 | * three components: | |
323 | * | |
324 | * * A `kind', which is `RAW', `IFO', `VOB', or `BUP'. `RAW' is a | |
325 | * special token which refers to the whole disc; the other kinds | |
326 | * refer to files in the DVD filesystem with the corresponding | |
327 | * extensions. | |
328 | * | |
329 | * * A `title', which is a number between 0 and 99 inclusive. | |
330 | * Title zero refers to the video management information and its | |
331 | * corresponding menu; nonzero numbers refer to video titlesets. | |
332 | * | |
333 | * * A `part', which is only relevant for `VOB' files; part 0 | |
334 | * refers to the menu data, while parts 1 to 9 inclusive refer to | |
335 | * portions of the video titleset itself. | |
336 | * | |
337 | * Components which aren't applicable must be zero, so that idents | |
338 | * can conveniently be compared as integers (so, specifically, the | |
339 | * title, if kind is `RAW', and the part, if kind is not `VOB' or | |
340 | * title is zero. | |
341 | */ | |
dc53ebfa MW |
342 | |
343 | static inline ident mkident(unsigned kind, unsigned title, unsigned part) | |
344 | { return (((ident)kind << 0) | ((ident)title << 8) | ((ident)part << 16)); } | |
345 | static inline unsigned id_kind(ident id) { return ((id >> 0)&0x0ff); } | |
346 | static inline unsigned id_title(ident id) { return ((id >> 8)&0x0ff); } | |
347 | static inline unsigned id_part(ident id) { return ((id >> 16)&0x0ff); } | |
be15bd14 MW |
348 | /* Functions for constructing and picking apart the fields of an |
349 | * ident. | |
350 | */ | |
dc53ebfa MW |
351 | |
352 | #define MAXFNSZ (1 + 8 + 1 + 12 + 1) | |
353 | extern void store_filename(char *buf, ident id); | |
be15bd14 MW |
354 | /* Store in BUF the filename corresponding to the ident ID. The |
355 | * filename will be at most `MAXFNSZ' bytes long, including the | |
356 | * terminating zero. | |
357 | */ | |
dc53ebfa | 358 | |
9b86c33f MW |
359 | #define DIF_MUSTVOLINF 1u |
360 | #define DIF_MUSTIFOHASH 2u | |
423a8198 | 361 | #define MAXIDSZ (32 + 1 + 32 + 1 + 32 + 1) |
9b86c33f | 362 | extern int dvd_id(char *p, dvd_reader_t *dvd, unsigned f, const char *file); |
be15bd14 MW |
363 | /* Determine a (hopefully) unique identifier for DVD. The identifier |
364 | * consists of two parts: | |
365 | * | |
366 | * * the volume name and serial number, from the volume | |
367 | * information, and | |
368 | * | |
369 | * * a cryptographic hash of the `.IFO' files on the disc. | |
370 | * | |
371 | * The identifier is written, as plain text, at P, and consists of at | |
372 | * most `MAXIDSZ' bytes, including the terminating zero. | |
373 | * | |
374 | * It's possible that determining either, or both, of these might | |
375 | * fail: the behaviour is controlled by the `DIF_MUSTVOLINF' and | |
376 | * `DIF_MUSTIFOHASH' flags in F: if the volume name/serial number, or | |
377 | * `.IFO' hash, respectively, can't be determined, and the | |
378 | * corresponding flag is clear, then a placeholder error message is | |
379 | * written to the output buffer in place of the correct data; if the | |
380 | * flag is set, then a warning message is printed to standard error | |
381 | * and -1 is returned. In practice, the `.IFO' hash is more likely | |
382 | * to be computed successfully, and probably more likely to actually | |
383 | * be unique. | |
384 | * | |
385 | * Returns zero if the identifier was successfully determined, within | |
386 | * the parameters set by the flags. | |
387 | */ | |
388 | ||
389 | /*----- That's all, folks -------------------------------------------------*/ | |
9b86c33f | 390 | |
dc53ebfa | 391 | #endif |