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