lib.[ch], dvd-sector-copy.c: Publish the `buf' machinery as inline functions.
[dvdrip] / lib.c
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
26 /*----- Header files ------------------------------------------------------*/
27
28 #include "lib.h"
29
30 /*----- Diagnostics -------------------------------------------------------*/
31
32 const char *prog = "<unset>";
33
34 void set_prog(const char *p)
35 { const char *q = strrchr(p, '/'); prog = q ? q + 1 : p; }
36
37 void vmoan(const char *fmt, va_list ap)
38 { vmoan_syserr(0, fmt, ap); }
39
40 void vmoan_syserr(int err, const char *fmt, va_list ap)
41 {
42 fprintf(stderr, "%s: ", prog);
43 vfprintf(stderr, fmt, ap);
44 if (err) fprintf(stderr, ": %s", strerror(errno));
45 fputc('\n', stderr);
46 }
47
48 void moan(const char *fmt, ...)
49 { va_list ap; va_start(ap, fmt); vmoan(fmt, ap); va_end(ap); }
50
51 void moan_syserr(int err, const char *fmt, ...)
52 { va_list ap; va_start(ap, fmt); vmoan_syserr(err, fmt, ap); va_end(ap); }
53
54 void bail(const char *fmt, ...)
55 { va_list ap; va_start(ap, fmt); vmoan(fmt, ap); va_end(ap); exit(2); }
56
57 void bail_syserr(int err, const char *fmt, ...)
58 {
59 va_list ap;
60
61 va_start(ap, fmt); vmoan_syserr(err, fmt, ap); va_end(ap);
62 exit(2);
63 }
64
65 /*----- Parsing utilities -------------------------------------------------*/
66
67 double parse_float(const char **p_inout, unsigned f,
68 double min, double max, const char *what)
69 {
70 const char *p;
71 char *q;
72 double x;
73 int err;
74
75 err = errno; errno = 0;
76 p = *p_inout;
77 x = strtod(p, &q);
78 if (errno || x < min || x > max || (!(f&PNF_JUNK) && *q))
79 bail("bad %s `%s'", what, p);
80 *p_inout = q; errno = err;
81 return (x);
82 }
83
84 long parse_int(const char **p_inout, unsigned f,
85 long min, long max, const char *what)
86 {
87 const char *p;
88 char *q;
89 long x;
90 int err;
91
92 err = errno; errno = 0;
93 p = *p_inout;
94 x = strtoul(p, &q, 0);
95 if (errno || x < min || x > max || (!(f&PNF_JUNK) && *q))
96 bail("bad %s `%s'", what, p);
97 *p_inout = q; errno = err;
98 return (x);
99 }
100
101 /*----- Resizing buffers and arrays ---------------------------------------*/
102
103 void buf__grow(struct buf *b)
104 {
105 b->sz = b->sz ? 2*b->sz : 32;
106 b->p = realloc(b->p, b->sz);
107 if (!b->p) bail("out of memory allocating %zu bytes", b->sz);
108 }
109
110 void *vec__grow(void *p, size_t esz, size_t *sz_inout)
111 {
112 size_t sz = *sz_inout, want;
113
114 sz = sz ? 2*sz : 32;
115 want = sz*esz;
116 p = realloc(p, want);
117 if (!p) bail("out of memory allocating %zu bytes", want);
118 *sz_inout = sz; return (p);
119 }
120
121 /*----- System utilities --------------------------------------------------*/
122
123 void sit(double t)
124 {
125 struct timeval tv;
126 double whole = floor(t);
127
128 if (t) {
129 tv.tv_sec = whole; tv.tv_usec = floor((t - whole)*1.0e6) + 1;
130 if (select(0, 0, 0, 0, &tv) < 0) bail_syserr(errno, "failed to sleep");
131 }
132 }
133
134 double tvdiff(const struct timeval *tv_lo, const struct timeval *tv_hi)
135 {
136 return ((tv_hi->tv_sec - tv_lo->tv_sec) +
137 (tv_hi->tv_usec - tv_lo->tv_usec)/1.0e6);
138 }
139
140 void carefully_write(int fd, const void *buf, size_t sz)
141 {
142 const unsigned char *p = buf;
143 ssize_t n;
144
145 if (fd < 0) return;
146 while (sz) {
147 n = write(fd, p, sz);
148 if (n < 0) {
149 if (errno == EINTR) continue;
150 bail_syserr(errno, "failed to write to output file");
151 }
152 if (!n) bail("unexpected short write to output file");
153 p += n; sz -= n;
154 }
155 }
156
157 void open_file_on_demand(const char *file, FILE **fp_inout, const char *what)
158 {
159 FILE *fp;
160
161 if (!*fp_inout) {
162 fp = fopen(file, "w");
163 if (!fp) bail_syserr(errno, "failed to open %s file `%s'", what, file);
164 fprintf(fp, "## %s\n\n", what);
165 *fp_inout = fp;
166 }
167 }
168
169 void check_write(FILE *fp, const char *what)
170 {
171 fflush(fp);
172 if (ferror(fp)) bail_syserr(errno, "error writing %s file", what);
173 }
174
175 void carefully_fclose(FILE *fp, const char *what)
176 {
177 if (fp && (ferror(fp) || fclose(fp)))
178 bail_syserr(errno, "error writing %s file", what);
179 }
180
181 off_t device_size(int fd, const char *file, int *blksz_out)
182 {
183 struct stat st;
184 uint64_t volsz;
185
186 if (fstat(fd, &st))
187 bail_syserr(errno, "failed to obtain status for `%s'", file);
188 if (S_ISREG(st.st_mode))
189 volsz = st.st_size;
190 else if (S_ISBLK(st.st_mode)) {
191 if (ioctl(fd, BLKGETSIZE64, &volsz))
192 bail_syserr(errno, "failed to get volume size for `%s'", file);
193 if (ioctl(fd, BLKSSZGET, blksz_out))
194 bail_syserr(errno, "failed to get block size for `%s'", file);
195 } else
196 bail("can't read size for `%s': expected file or block device", file);
197 return ((off_t)volsz);
198 }
199
200 /*----- Progress utilities ------------------------------------------------*/
201
202 struct progress_state progress = PROGRESS_STATE_INIT;
203 static struct banner_progress_item banner_progress;
204
205 static void render_banner_progress(struct progress_item *item,
206 struct progress_render_state *render)
207 {
208 struct banner_progress_item *bi = (struct banner_progress_item *)item;
209
210 progress_putleft(render, " %s", bi->msg);
211 progress_shownotice(render, 4, 7);
212 }
213
214 void show_banner(const char *msg)
215 {
216 banner_progress._base.render = render_banner_progress;
217 progress_additem(&progress, &banner_progress._base);
218 banner_progress.msg = msg;
219 progress_update(&progress);
220 }
221
222 void hide_banner(void)
223 {
224 if (!progress_removeitem(&progress, &banner_progress._base))
225 progress_update(&progress);
226 }
227
228 /*----- DVD utilities -----------------------------------------------------*/
229
230 #ifdef notdef
231 static void logfn(void *p, dvd_logger_level_t lev,
232 const char *fmt, va_list ap)
233 {
234 switch (lev) {
235 case DVD_LOGGER_LEVEL_ERROR:
236 fprintf("%s (libdvdread error): ", prog);
237 break;
238 case DVD_LOGGER_LEVEL_WARN:
239 fprintf("%s (libdvdread warning): ", prog);
240 break;
241 default:
242 return;
243 }
244 vfprintf(stderr, fmt, ap);
245 fputc('\n', stderr);
246 }
247 static const dvd_logger_cb logger = { logfn };
248 #endif
249
250 void open_dvd(const char *device, int mode,
251 int *fd_out, dvd_reader_t **dvd_out)
252 {
253 int fd;
254 dvd_reader_t *dvd;
255 int bannerp = 0;
256
257 for (;;) {
258 fd = open(device, mode);
259 if (fd >= 0 || errno != ENOMEDIUM) break;
260 if (!bannerp) {
261 show_banner("Waiting for disc to be inserted...");
262 bannerp = 1;
263 }
264 sit(0.2);
265 }
266 if (bannerp) hide_banner();
267 if (fd < 0) bail_syserr(errno, "failed to open device `%s'", device);
268 if (dvd_out) {
269 #ifdef notdef
270 dvd = DVDOpen2(0, &logger, device);
271 #else
272 dvd = DVDOpen(device);
273 #endif
274 if (!dvd) bail("failed to open DVD on `%s'", device);
275 *dvd_out = dvd;
276 }
277 if (fd_out) *fd_out = fd;
278 else close(fd);
279 }
280
281 void store_filename(char *buf, ident id)
282 {
283 switch (id_kind(id)) {
284 case RAW:
285 sprintf(buf, "#<raw device>");
286 break;
287 case IFO:
288 if (!id_title(id)) sprintf(buf, "/VIDEO_TS/VIDEO_TS.IFO");
289 else sprintf(buf, "/VIDEO_TS/VTS_%02u_0.IFO", id_title(id));
290 break;
291 case BUP:
292 if (!id_title(id)) sprintf(buf, "/VIDEO_TS/VIDEO_TS.BUP");
293 else sprintf(buf, "/VIDEO_TS/VTS_%02u_0.BUP", id_title(id));
294 break;
295 case VOB:
296 if (!id_title(id)) sprintf(buf, "/VIDEO_TS/VIDEO_TS.VOB");
297 else
298 sprintf(buf, "/VIDEO_TS/VTS_%02u_%u.VOB", id_title(id), id_part(id));
299 break;
300 default:
301 abort();
302 }
303 }
304
305 static char *copy_string(char *p, const char *q)
306 {
307 while (*q) *p++ = *q++;
308 *p = 0; return (p);
309 }
310
311 static char *copy_hex(char *p, const unsigned char *q, size_t sz)
312 {
313 while (sz) {
314 sprintf(p, "%02x", *q);
315 p += 2; q++; sz--;
316 }
317 return (p);
318 }
319
320 int dvd_id(char *p, dvd_reader_t *dvd, unsigned f, const char *file)
321 {
322 char volid[33];
323 unsigned char volsetid[16], discid[16];
324 int rc;
325 size_t n;
326
327 rc = DVDUDFVolumeInfo(dvd,
328 volid, sizeof(volid),
329 volsetid, sizeof(volsetid));
330 if (!rc) {
331 p = copy_string(p, volid);
332 *p++ = '-';
333 for (n = sizeof(volsetid); n && !volsetid[n - 1]; n--);
334 p = copy_hex(p, volsetid, n);
335 } else if (f&DIF_MUSTVOLINF) {
336 if (file) moan("failed to read volume info for `%s'", file);
337 else moan("failed to read volume info");
338 return (-1);
339 } else
340 p = copy_string(p, "<error reading volume info>");
341
342 *p++ = ':';
343 rc = DVDDiscID(dvd, discid);
344 if (!rc)
345 p = copy_hex(p, discid, sizeof(discid));
346 else if (f&DIF_MUSTIFOHASH) {
347 if (file) moan("failed to determine disc id of `%s'", file);
348 else moan("failed to determine disc id");
349 return (-1);
350 } else
351 p = copy_string(p, "<error reading disc-id>");
352
353 return (0);
354 }
355
356 /*----- That's all, folks -------------------------------------------------*/