chiark / gitweb /
@@@ dvdrip-upload: change settings while i'm stealing someone else's internet
[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 int read_line(FILE *fp, struct buf *b)
141 {
142   int ch;
143
144   ch = getc(fp);
145   if (ch == EOF)
146     return (-1);
147   else if (ch != '\n')
148     do { buf_putc(b, ch); ch = getc(fp); } while (ch != EOF && ch != '\n');
149   buf_putz(b);
150   return (0);
151 }
152
153 void carefully_write(int fd, const void *buf, size_t sz)
154 {
155   const unsigned char *p = buf;
156   ssize_t n;
157
158   if (fd < 0) return;
159   while (sz) {
160     n = write(fd, p, sz);
161     if (n < 0) {
162       if (errno == EINTR) continue;
163       bail_syserr(errno, "failed to write to output file");
164     }
165     if (!n) bail("unexpected short write to output file");
166     p += n; sz -= n;
167   }
168 }
169
170 void open_file_on_demand(const char *file, FILE **fp_inout, const char *what)
171 {
172   FILE *fp;
173
174   if (!*fp_inout) {
175     fp = fopen(file, "w");
176     if (!fp) bail_syserr(errno, "failed to open %s file `%s'", what, file);
177     fprintf(fp, "## %s\n\n", what);
178     *fp_inout = fp;
179   }
180 }
181
182 void check_write(FILE *fp, const char *what)
183 {
184   fflush(fp);
185   if (ferror(fp)) bail_syserr(errno, "error writing %s file", what);
186 }
187
188 void carefully_fclose(FILE *fp, const char *what)
189 {
190   if (fp && (ferror(fp) || fclose(fp)))
191     bail_syserr(errno, "error writing %s file", what);
192 }
193
194 off_t device_size(int fd, const char *file, int *blksz_out)
195 {
196   struct stat st;
197   uint64_t volsz;
198
199   if (fstat(fd, &st))
200     bail_syserr(errno, "failed to obtain status for `%s'", file);
201   if (S_ISREG(st.st_mode))
202     volsz = st.st_size;
203   else if (S_ISBLK(st.st_mode)) {
204     if (ioctl(fd, BLKGETSIZE64, &volsz))
205       bail_syserr(errno, "failed to get volume size for `%s'", file);
206     if (ioctl(fd, BLKSSZGET, blksz_out))
207       bail_syserr(errno, "failed to get block size for `%s'", file);
208   } else
209     bail("can't read size for `%s': expected file or block device", file);
210   return ((off_t)volsz);
211 }
212
213 /*----- Progress utilities ------------------------------------------------*/
214
215 struct progress_state progress = PROGRESS_STATE_INIT;
216 static struct banner_progress_item banner_progress;
217
218 static void render_banner_progress(struct progress_item *item,
219                             struct progress_render_state *render)
220 {
221   struct banner_progress_item *bi = (struct banner_progress_item *)item;
222
223   progress_putleft(render, " %s", bi->msg);
224   progress_shownotice(render, 4, 7);
225 }
226
227 void show_banner(const char *msg)
228 {
229   banner_progress._base.render = render_banner_progress;
230   progress_additem(&progress, &banner_progress._base);
231   banner_progress.msg = msg;
232   progress_update(&progress);
233 }
234
235 void hide_banner(void)
236 {
237   if (!progress_removeitem(&progress, &banner_progress._base))
238     progress_update(&progress);
239 }
240
241 /*----- DVD utilities -----------------------------------------------------*/
242
243 #ifdef notdef
244 static void logfn(void *p, dvd_logger_level_t lev,
245                   const char *fmt, va_list ap)
246 {
247   switch (lev) {
248     case DVD_LOGGER_LEVEL_ERROR:
249       fprintf("%s (libdvdread error): ", prog);
250       break;
251     case DVD_LOGGER_LEVEL_WARN:
252       fprintf("%s (libdvdread warning): ", prog);
253       break;
254     default:
255       return;
256   }
257   vfprintf(stderr, fmt, ap);
258   fputc('\n', stderr);
259 }
260 static const dvd_logger_cb logger = { logfn };
261 #endif
262
263 int open_dvd(const char *device, int mode,
264              int *fd_out, dvd_reader_t **dvd_out)
265 {
266   int fd = -1;
267   dvd_reader_t *dvd = 0;
268   int bannerp = 0, rc;
269
270   for (;;) {
271     fd = open(device, mode);
272     if (fd >= 0 || errno != ENOMEDIUM) break;
273     if (!bannerp) {
274       show_banner("Waiting for disc to be inserted...");
275       bannerp = 1;
276     }
277     sit(0.2);
278   }
279   if (bannerp) hide_banner();
280
281   if (fd < 0) {
282     moan_syserr(errno, "failed to open device `%s'", device);
283     rc = -1; goto end;
284   }
285
286   if (dvd_out) {
287 #ifdef notdef
288     dvd = DVDOpen2(0, &logger, device);
289 #else
290     dvd = DVDOpen(device);
291 #endif
292     if (!dvd) {
293       moan("failed to open DVD on `%s'", device);
294       rc = -1; goto end;
295     }
296   }
297
298   if (fd_out) { *fd_out = fd; fd = -1; }
299   if (dvd_out) { *dvd_out = dvd; dvd = 0; }
300   rc = 0;
301
302 end:
303   if (fd >= 0) close(fd);
304   DVDClose(dvd);
305   return (rc);
306 }
307
308 void store_filename(char *buf, ident id)
309 {
310   switch (id_kind(id)) {
311     case RAW:
312       sprintf(buf, "#<raw device>");
313       break;
314     case IFO:
315       if (!id_title(id)) sprintf(buf, "/VIDEO_TS/VIDEO_TS.IFO");
316       else sprintf(buf, "/VIDEO_TS/VTS_%02u_0.IFO", id_title(id));
317       break;
318     case BUP:
319       if (!id_title(id)) sprintf(buf, "/VIDEO_TS/VIDEO_TS.BUP");
320       else sprintf(buf, "/VIDEO_TS/VTS_%02u_0.BUP", id_title(id));
321       break;
322     case VOB:
323       if (!id_title(id)) sprintf(buf, "/VIDEO_TS/VIDEO_TS.VOB");
324       else
325         sprintf(buf, "/VIDEO_TS/VTS_%02u_%u.VOB", id_title(id), id_part(id));
326       break;
327     default:
328       abort();
329   }
330 }
331
332 static char *copy_string(char *p, const char *q)
333 {
334   while (*q) *p++ = *q++;
335   *p = 0; return (p);
336 }
337
338 static char *copy_hex(char *p, const unsigned char *q, size_t sz)
339 {
340   while (sz) {
341     sprintf(p, "%02x", *q);
342     p += 2; q++; sz--;
343   }
344   return (p);
345 }
346
347 int dvd_id(char *p, dvd_reader_t *dvd, unsigned f, const char *file)
348 {
349   char volid[33];
350   unsigned char volsetid[16], discid[16];
351   int rc;
352   size_t n;
353
354   rc = DVDUDFVolumeInfo(dvd,
355                         volid, sizeof(volid),
356                         volsetid, sizeof(volsetid));
357   if (!rc) {
358     p = copy_string(p, volid);
359     *p++ = '-';
360     for (n = sizeof(volsetid); n && !volsetid[n - 1]; n--);
361     p = copy_hex(p, volsetid, n);
362   } else if (f&DIF_MUSTVOLINF) {
363     if (file) moan("failed to read volume info for `%s'", file);
364     else moan("failed to read volume info");
365     return (-1);
366   } else
367     p = copy_string(p, "<error reading volume info>");
368
369   *p++ = ':';
370   rc = DVDDiscID(dvd, discid);
371   if (!rc)
372     p = copy_hex(p, discid, sizeof(discid));
373   else if (f&DIF_MUSTIFOHASH) {
374     if (file) moan("failed to determine disc id of `%s'", file);
375     else moan("failed to determine disc id");
376     return (-1);
377   } else
378     p = copy_string(p, "<error reading disc-id>");
379
380   return (0);
381 }
382
383 /*----- That's all, folks -------------------------------------------------*/