Merge branch 'master' of git.distorted.org.uk:~mdw/publish/public-git/dvdrip
[dvdrip] / lib.c
CommitLineData
dc53ebfa
MW
1#include "lib.h"
2
3const char *prog = "<unset>";
4
5void set_prog(const char *p)
6 { const char *q = strrchr(p, '/'); prog = q ? q + 1 : p; }
7
8void vmoan(const char *fmt, va_list ap)
7ea9ce2b 9 { vmoan_syserr(0, fmt, ap); }
dc53ebfa 10
7ea9ce2b 11void vmoan_syserr(int err, const char *fmt, va_list ap)
dc53ebfa 12{
7ea9ce2b
MW
13 fprintf(stderr, "%s: ", prog);
14 vfprintf(stderr, fmt, ap);
15 if (err) fprintf(stderr, ": %s", strerror(errno));
dc53ebfa
MW
16 fputc('\n', stderr);
17}
18
7ea9ce2b
MW
19void moan(const char *fmt, ...)
20 { va_list ap; va_start(ap, fmt); vmoan(fmt, ap); va_end(ap); }
dc53ebfa 21
7ea9ce2b
MW
22void moan_syserr(int err, const char *fmt, ...)
23 { va_list ap; va_start(ap, fmt); vmoan_syserr(err, fmt, ap); va_end(ap); }
24
25void bail(const char *fmt, ...)
26 { va_list ap; va_start(ap, fmt); vmoan(fmt, ap); va_end(ap); exit(2); }
dc53ebfa 27
dc53ebfa
MW
28void bail_syserr(int err, const char *fmt, ...)
29{
30 va_list ap;
31
7ea9ce2b 32 va_start(ap, fmt); vmoan_syserr(err, fmt, ap); va_end(ap);
dc53ebfa
MW
33 exit(2);
34}
35
f82e4cd7
MW
36double 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
53long 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
dc53ebfa
MW
70void 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
00a5be1d
MW
81void 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
98void 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
110void 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
116void 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
4bd4876f
MW
122off_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
dc53ebfa
MW
141void 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
9b86c33f
MW
165static char *copy_string(char *p, const char *q)
166{
167 while (*q) *p++ = *q++;
168 *p = 0; return (p);
169}
170
171static 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
180int 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
dc53ebfa
MW
216struct progress_state progress = PROGRESS_STATE_INIT;
217static struct banner_progress_item banner_progress;
218
219static 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
228void 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
236void hide_banner(void)
237{
238 if (!progress_removeitem(&progress, &banner_progress._base))
239 progress_update(&progress);
240}
241
242#ifdef notdef
243static 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}
259static const dvd_logger_cb logger = { logfn };
260#endif
261
972381af
MW
262void open_dvd(const char *device, int mode,
263 int *fd_out, dvd_reader_t **dvd_out)
dc53ebfa
MW
264{
265 int fd;
266 dvd_reader_t *dvd;
267 int bannerp = 0;
268
269 for (;;) {
972381af 270 fd = open(device, mode);
dc53ebfa
MW
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}