chiark / gitweb /
Merge branch 'master' of git.distorted.org.uk:~mdw/publish/public-git/dvdrip
[dvdrip] / lib.c
1 #include "lib.h"
2
3 const char *prog = "<unset>";
4
5 void set_prog(const char *p)
6   { const char *q = strrchr(p, '/'); prog = q ? q + 1 : p; }
7
8 void vmoan(const char *fmt, va_list ap)
9   { vmoan_syserr(0, fmt, ap); }
10
11 void vmoan_syserr(int err, const char *fmt, va_list ap)
12 {
13   fprintf(stderr, "%s: ", prog);
14   vfprintf(stderr, fmt, ap);
15   if (err) fprintf(stderr, ": %s", strerror(errno));
16   fputc('\n', stderr);
17 }
18
19 void moan(const char *fmt, ...)
20   { va_list ap; va_start(ap, fmt); vmoan(fmt, ap); va_end(ap); }
21
22 void moan_syserr(int err, const char *fmt, ...)
23   { va_list ap; va_start(ap, fmt); vmoan_syserr(err, fmt, ap); va_end(ap); }
24
25 void bail(const char *fmt, ...)
26   { va_list ap; va_start(ap, fmt); vmoan(fmt, ap); va_end(ap); exit(2); }
27
28 void bail_syserr(int err, const char *fmt, ...)
29 {
30   va_list ap;
31
32   va_start(ap, fmt); vmoan_syserr(err, fmt, ap); va_end(ap);
33   exit(2);
34 }
35
36 double parse_float(const char **p_inout, unsigned f,
37                    double min, double max, const char *what)
38 {
39   const char *p;
40   char *q;
41   double x;
42   int err;
43
44   err = errno; errno = 0;
45   p = *p_inout;
46   x = strtod(p, &q);
47   if (errno || x < min || x > max || (!(f&PNF_JUNK) && *q))
48     bail("bad %s `%s'", what, p);
49   *p_inout = q; errno = err;
50   return (x);
51 }
52
53 long parse_int(const char **p_inout, unsigned f,
54                long min, long max, const char *what)
55 {
56   const char *p;
57   char *q;
58   long x;
59   int err;
60
61   err = errno; errno = 0;
62   p = *p_inout;
63   x = strtoul(p, &q, 0);
64   if (errno || x < min || x > max || (!(f&PNF_JUNK) && *q))
65     bail("bad %s `%s'", what, p);
66   *p_inout = q; errno = err;
67   return (x);
68 }
69
70 void sit(double t)
71 {
72   struct timeval tv;
73   double whole = floor(t);
74
75   if (t) {
76     tv.tv_sec = whole; tv.tv_usec = floor((t - whole)*1.0e6) + 1;
77     if (select(0, 0, 0, 0, &tv) < 0) bail_syserr(errno, "failed to sleep");
78   }
79 }
80
81 void carefully_write(int fd, const void *buf, size_t sz)
82 {
83   const unsigned char *p = buf;
84   ssize_t n;
85
86   if (fd < 0) return;
87   while (sz) {
88     n = write(fd, p, sz);
89     if (n < 0) {
90       if (errno == EINTR) continue;
91       bail_syserr(errno, "failed to write to output file");
92     }
93     if (!n) bail("unexpected short write to output file");
94     p += n; sz -= n;
95   }
96 }
97
98 void open_file_on_demand(const char *file, FILE **fp_inout, const char *what)
99 {
100   FILE *fp;
101
102   if (!*fp_inout) {
103     fp = fopen(file, "w");
104     if (!fp) bail_syserr(errno, "failed to open %s file `%s'", what, file);
105     fprintf(fp, "## %s\n\n", what);
106     *fp_inout = fp;
107   }
108 }
109
110 void check_write(FILE *fp, const char *what)
111 {
112   fflush(fp);
113   if (ferror(fp)) bail_syserr(errno, "error writing %s file", what);
114 }
115
116 void carefully_fclose(FILE *fp, const char *what)
117 {
118   if (fp && (ferror(fp) || fclose(fp)))
119     bail_syserr(errno, "error writing %s file", what);
120 }
121
122 off_t device_size(int fd, const char *file, int *blksz_out)
123 {
124   struct stat st;
125   uint64_t volsz;
126
127   if (fstat(fd, &st))
128     bail_syserr(errno, "failed to obtain status for `%s'", file);
129   if (S_ISREG(st.st_mode))
130     volsz = st.st_size;
131   else if (S_ISBLK(st.st_mode)) {
132     if (ioctl(fd, BLKGETSIZE64, &volsz))
133       bail_syserr(errno, "failed to get volume size for `%s'", file);
134     if (ioctl(fd, BLKSSZGET, blksz_out))
135       bail_syserr(errno, "failed to get block size for `%s'", file);
136   } else
137     bail("can't read size for `%s': expected file or block device", file);
138   return ((off_t)volsz);
139 }
140
141 void store_filename(char *buf, ident id)
142 {
143   switch (id_kind(id)) {
144     case RAW:
145       sprintf(buf, "#<raw device>");
146       break;
147     case IFO:
148       if (!id_title(id)) sprintf(buf, "/VIDEO_TS/VIDEO_TS.IFO");
149       else sprintf(buf, "/VIDEO_TS/VTS_%02u_0.IFO", id_title(id));
150       break;
151     case BUP:
152       if (!id_title(id)) sprintf(buf, "/VIDEO_TS/VIDEO_TS.BUP");
153       else sprintf(buf, "/VIDEO_TS/VTS_%02u_0.BUP", id_title(id));
154       break;
155     case VOB:
156       if (!id_title(id)) sprintf(buf, "/VIDEO_TS/VIDEO_TS.VOB");
157       else
158         sprintf(buf, "/VIDEO_TS/VTS_%02u_%u.VOB", id_title(id), id_part(id));
159       break;
160     default:
161       abort();
162   }
163 }
164
165 static char *copy_string(char *p, const char *q)
166 {
167   while (*q) *p++ = *q++;
168   *p = 0; return (p);
169 }
170
171 static char *copy_hex(char *p, const unsigned char *q, size_t sz)
172 {
173   while (sz) {
174     sprintf(p, "%02x", *q);
175     p += 2; q++; sz--;
176   }
177   return (p);
178 }
179
180 int dvd_id(char *p, dvd_reader_t *dvd, unsigned f, const char *file)
181 {
182   char volid[33];
183   unsigned char volsetid[16], discid[16];
184   int rc;
185   size_t n;
186
187   rc = DVDUDFVolumeInfo(dvd,
188                         volid, sizeof(volid),
189                         volsetid, sizeof(volsetid));
190   if (!rc) {
191     p = copy_string(p, volid);
192     *p++ = '-';
193     for (n = sizeof(volsetid); n && !volsetid[n - 1]; n--);
194     p = copy_hex(p, volsetid, n);
195   } else if (f&DIF_MUSTVOLINF) {
196     if (file) moan("failed to read volume info for `%s'", file);
197     else moan("failed to read volume info");
198     return (-1);
199   } else
200     p = copy_string(p, "<error reading volume info>");
201
202   *p++ = ':';
203   rc = DVDDiscID(dvd, discid);
204   if (!rc)
205     p = copy_hex(p, discid, sizeof(discid));
206   else if (f&DIF_MUSTIFOHASH) {
207     if (file) moan("failed to determine disc id of `%s'", file);
208     else moan("failed to determine disc id");
209     return (-1);
210   } else
211     p = copy_string(p, "<error reading disc-id>");
212
213   return (0);
214 }
215
216 struct progress_state progress = PROGRESS_STATE_INIT;
217 static struct banner_progress_item banner_progress;
218
219 static void render_banner_progress(struct progress_item *item,
220                             struct progress_render_state *render)
221 {
222   struct banner_progress_item *bi = (struct banner_progress_item *)item;
223
224   progress_putleft(render, " %s", bi->msg);
225   progress_shownotice(render, 4, 7);
226 }
227
228 void show_banner(const char *msg)
229 {
230   banner_progress._base.render = render_banner_progress;
231   progress_additem(&progress, &banner_progress._base);
232   banner_progress.msg = msg;
233   progress_update(&progress);
234 }
235
236 void hide_banner(void)
237 {
238   if (!progress_removeitem(&progress, &banner_progress._base))
239     progress_update(&progress);
240 }
241
242 #ifdef notdef
243 static void logfn(void *p, dvd_logger_level_t lev,
244                   const char *fmt, va_list ap)
245 {
246   switch (lev) {
247     case DVD_LOGGER_LEVEL_ERROR:
248       fprintf("%s (libdvdread error): ", prog);
249       break;
250     case DVD_LOGGER_LEVEL_WARN:
251       fprintf("%s (libdvdread warning): ", prog);
252       break;
253     default:
254       return;
255   }
256   vfprintf(stderr, fmt, ap);
257   fputc('\n', stderr);
258 }
259 static const dvd_logger_cb logger = { logfn };
260 #endif
261
262 void open_dvd(const char *device, int mode,
263               int *fd_out, dvd_reader_t **dvd_out)
264 {
265   int fd;
266   dvd_reader_t *dvd;
267   int bannerp = 0;
268
269   for (;;) {
270     fd = open(device, mode);
271     if (fd >= 0 || errno != ENOMEDIUM) break;
272     if (!bannerp) {
273       show_banner("Waiting for disc to be inserted...");
274       bannerp = 1;
275     }
276     sit(0.2);
277   }
278   if (bannerp) hide_banner();
279   if (fd < 0) bail_syserr(errno, "failed to open device `%s'", device);
280   if (dvd_out) {
281 #ifdef notdef
282     dvd = DVDOpen2(0, &logger, device);
283 #else
284     dvd = DVDOpen(device);
285 #endif
286     if (!dvd) bail("failed to open DVD on `%s'", device);
287     *dvd_out = dvd;
288   }
289   if (fd_out) *fd_out = fd;
290   else close(fd);
291 }