2 #define _FILE_OFFSET_BITS 64
18 #include <sys/ioctl.h>
25 #include <dvdread/dvd_reader.h>
26 #include <dvdread/dvd_udf.h>
27 #include <dvdread/ifo_read.h>
28 #include <dvdread/ifo_types.h>
31 #define SECTORS(n) (((n) + (SECTORSZ - 1))/SECTORSZ)
33 #define CTYPE_HACK(fn, ch) fn((unsigned char)(ch))
34 #define ISDIGIT(ch) CTYPE_HACK(isdigit, ch)
35 #define ISSPACE(ch) CTYPE_HACK(isspace, ch)
37 #define N(v) (sizeof(v)/sizeof((v)[0]))
39 enum { RAW
, IFO
, VOB
, BUP
};
40 typedef uint_least32_t ident
;
42 static inline ident
mkident(unsigned kind
, unsigned title
, unsigned part
)
43 { return (((ident
)kind
<< 0) | ((ident
)title
<< 8) | ((ident
)part
<< 16)); }
44 static inline unsigned id_kind(ident id
) { return ((id
>> 0)&0x0ff); }
45 static inline unsigned id_title(ident id
) { return ((id
>> 8)&0x0ff); }
46 static inline unsigned id_part(ident id
) { return ((id
>> 16)&0x0ff); }
48 static const char *prog
= "<unset>";
49 static int status
= 0;
51 static void usage(FILE *fp
)
54 "usage: %s [-c] [-D DEV] [-R MAP] "
55 "[-b OUTMAP] [-o OUTFILE] [-r [START]-[END]]\n",
59 static void vmoan(const char *fmt
, va_list ap
)
60 { fprintf(stderr
, "%s: ", prog
); vfprintf(stderr
, fmt
, ap
); }
61 __attribute__((noreturn
, format(printf
, 1, 2)))
62 static void bail(const char *fmt
, ...)
66 va_start(ap
, fmt
); vmoan(fmt
, ap
); va_end(ap
);
70 __attribute__((noreturn
, format(printf
, 2, 3)))
71 static void bail_syserr(int err
, const char *fmt
, ...)
75 va_start(ap
, fmt
); vmoan(fmt
, ap
); va_end(ap
);
76 if (err
) fprintf(stderr
, ": %s", strerror(errno
));
81 #define MAXFNSZ (1 + 8 + 1 + 12 + 1)
83 static void store_filename(char *buf
, ident id
)
85 switch (id_kind(id
)) {
87 sprintf(buf
, "#<raw device>");
90 if (!id_title(id
)) sprintf(buf
, "/VIDEO_TS/VIDEO_TS.IFO");
91 else sprintf(buf
, "/VIDEO_TS/VTS_%02u_0.IFO", id_title(id
));
94 if (!id_title(id
)) sprintf(buf
, "/VIDEO_TS/VIDEO_TS.BUP");
95 else sprintf(buf
, "/VIDEO_TS/VTS_%02u_0.BUP", id_title(id
));
98 if (!id_title(id
)) sprintf(buf
, "/VIDEO_TS/VIDEO_TS.VOB");
100 sprintf(buf
, "/VIDEO_TS/VTS_%02u_%u.VOB", id_title(id
), id_part(id
));
107 #define DEFVEC(vtype, etype) \
108 typedef struct { etype *v; size_t n, sz; } vtype
109 #define VEC_INIT { 0, 0, 0 }
110 #define VEC_FREE(vv) do { \
111 free((vv)->v); (vv)->v 0; (vv)->n = (vv)->sz = 0; \
113 #define VEC_PUSH(p, vv) do { \
115 if ((vv)->n >= (vv)->sz) { \
116 (vv)->sz = (vv)->sz ? 2*(vv)->sz : 32; \
117 _want = (vv)->sz*sizeof(*(vv)->v); \
118 (vv)->v = realloc((vv)->v, _want); \
119 if (!(vv)->v) bail("out of memory allocating %zu bytes", _want); \
121 (p) = &(vv)->v[(vv)->n++]; \
124 #define MAXFILES (1 + 2*99 + 1)
129 DEFVEC(file_v
, struct file
);
130 static file_v filetab
= VEC_INIT
;
132 enum { EV_WRITE
, EV_BEGIN
, EV_END
, EV_STOP
};
134 unsigned char ev
, file
;
137 DEFVEC(event_v
, struct event
);
138 static event_v eventq
= VEC_INIT
;
140 typedef uint_least32_t bits
;
141 static bits live
[(MAXFILES
+ 31)/32];
143 static inline int livep(unsigned i
)
144 { return (live
[i
/32]&((bits
)1 << (i
%32))); }
145 static inline void set_live(unsigned i
)
146 { live
[i
/32] |= (bits
)1 << (i
%32); }
147 static inline void clear_live(unsigned i
)
148 { live
[i
/32] &= ~((bits
)1 << (i
%32)); }
149 static inline int least_live(void)
151 unsigned i
, n
= (filetab
.n
+ 32)/32;
154 for (i
= 0; i
< n
; i
++) { b
= live
[i
]; if (b
) goto found
; }
158 if (!(b
&0x0000ffff)) { b
>>= 16; i
+= 16; }
159 if (!(b
&0x000000ff)) { b
>>= 8; i
+= 8; }
160 if (!(b
&0x0000000f)) { b
>>= 4; i
+= 4; }
161 if (!(b
&0x00000003)) { b
>>= 2; i
+= 2; }
162 if (!(b
&0x00000001)) { b
>>= 1; i
+= 1; }
167 static void put_event(unsigned evtype
, unsigned file
, uint32_t pos
)
171 VEC_PUSH(ev
, &eventq
);
172 ev
->ev
= evtype
; ev
->file
= file
; ev
->pos
= pos
;
175 static void put_file(ident id
, uint32_t start
, uint32_t end
)
180 VEC_PUSH(f
, &filetab
); i
= f
- filetab
.v
;
181 f
->id
= id
; f
->start
= start
; f
->end
= end
;
182 put_event(EV_BEGIN
, i
, start
);
183 put_event(EV_END
, i
, end
);
186 static void put_menu(dvd_reader_t
*dvd
, unsigned title
)
188 ident id
= mkident(VOB
, title
, 0);
192 store_filename(fn
, id
);
193 start
= UDFFindFile(dvd
, fn
, &len
); if (!start
) return;
195 printf(";; %8"PRIu32
" .. %-8"PRIu32
": %s\n",
196 start
, start
+ SECTORS(len
), fn
);
198 put_file(id
, start
, start
+ SECTORS(len
));
201 static void put_title(dvd_reader_t
*dvd
, unsigned title
)
204 uint32_t start
[9], len
[9];
207 for (i
= 0; i
< 9; i
++) {
208 store_filename(fn
, mkident(VOB
, title
, i
+ 1));
209 start
[i
] = UDFFindFile(dvd
, fn
, &len
[i
]); if (!start
[i
]) break;
211 npart
= i
; if (!npart
) return;
214 for (i
= 0; i
< npart
; i
++) {
215 store_filename(fn
, mkident(VOB
, title
, i
+ 1));
216 printf(";; %8"PRIu32
" .. %-8"PRIu32
": %s\n",
217 start
[i
], start
[i
] + SECTORS(len
[i
]), fn
);
222 for (i
= 0; i
< npart
- 1; i
++) {
224 bail("title %u part %u length = %"PRIu32
" not a multiple of %d",
225 title
, i
, len
[i
], SECTORSZ
);
226 if (start
[i
] + len
[i
]/SECTORSZ
!= start
[i
+ 1])
227 bail("title %u part %u end = %"PRIu32
" /= part %u start = %"PRIu32
"",
228 title
, i
, start
[i
] + len
[i
]/SECTORSZ
, i
+ 1, start
[i
+ 1]);
231 put_file(mkident(VOB
, title
, 1),
232 start
[0], start
[npart
- 1] + SECTORS(len
[npart
- 1]));
235 static int compare_event(const void *a
, const void *b
)
237 const struct event
*eva
= a
, *evb
= b
;
239 if (eva
->pos
< evb
->pos
) return (-1);
240 else if (eva
->pos
> evb
->pos
) return (+1);
242 if (eva
->ev
< evb
->ev
) return (-1);
243 else if (eva
->ev
> evb
->ev
) return (+1);
245 if (eva
->file
< evb
->file
) return (-1);
246 else if (eva
->file
> evb
->file
) return (+1);
251 static int progresslen
= 0;
253 static void clear_progress_internal(void)
254 { while (progresslen
) { fputs("\b \b", stdout
); progresslen
--; } }
255 static void clear_progress(void)
256 { clear_progress_internal(); fflush(stdout
); }
257 static void vappend_progress(const char *fmt
, va_list ap
)
258 { progresslen
+= vprintf(fmt
, ap
); }
259 __attribute__((format(printf
, 1, 2)))
260 static void append_progress(const char *fmt
, ...)
265 vappend_progress(fmt
, ap
);
268 __attribute__((format(printf
, 1, 2)))
269 static void print_progress(const char *fmt
, ...)
274 clear_progress_internal();
275 vappend_progress(fmt
, ap
);
285 uint32_t last_pos
, limit
, nsectors
, ndone
;
286 struct timeval last_time
;
288 const char *mapfile
; FILE *mapfp
;
290 #define SOURCE_INIT { 0, -1, 0, 0, 0, 0, 0, 0, { 0, 0 }, 0.0, 0.0, 0, 0 }
292 static void report_progress(struct source
*src
, uint32_t pos
)
297 double t
, f
, g
, rate
;
301 #define BETA (1 - ALPHA)
303 gettimeofday(&now
, 0);
304 t
= (now
.tv_sec
- src
->last_time
.tv_sec
) +
305 (now
.tv_usec
- src
->last_time
.tv_usec
)/1000000.0;
308 g
= src
->wcount ?
pow(BETA
, t
) : 0.0; f
= (1 - g
)/(1 - BETA
);
309 src
->wsum
= f
*(pos
- src
->last_pos
)/t
+ g
*src
->wsum
;
310 src
->wcount
= f
+ g
*src
->wcount
;
311 src
->ndone
+= pos
- src
->last_pos
;
312 src
->last_time
= now
; src
->last_pos
= pos
;
315 if (!src
->wsum
|| !src
->wcount
)
316 { rate
= 0; strcpy(etastr
, "???"); }
318 rate
= src
->wsum
/src
->wcount
;
319 eta
= (int)((src
->nsectors
- src
->ndone
)/rate
);
320 sprintf(etastr
, "%d:%02d:%02d", eta
/3600, (eta
/60)%60, eta
%60);
323 rate
*= SECTORSZ
; unit
= "";
324 if (rate
> 128) { rate
/= 1024; unit
= "k"; }
325 if (rate
> 128) { rate
/= 1024; unit
= "M"; }
326 if (rate
> 128) { rate
/= 1024; unit
= "G"; }
328 print_progress("copied %.1f%% (%"PRIu32
" of %"PRIu32
"; %.1f %sB/s, ETA %s)",
329 src
->ndone
*100.0/src
->nsectors
, pos
, src
->limit
,
331 if (src
->file
&& id_kind(src
->file
->id
) == VOB
) {
332 append_progress(" -- %s %d %3.1f%%",
333 id_part(src
->file
->id
) ?
"title" : "menu",
334 id_title(src
->file
->id
),
335 (pos
- src
->file
->start
)*100.0/
336 (src
->file
->end
- src
->file
->start
));
343 static void report_bad_blocks_progress(struct source
*src
,
344 uint32_t lo
, uint32_t hi
, int err
)
346 report_progress(src
, hi
);
348 if (lo
== hi
) append_progress(": retrying bad sector");
350 append_progress(": %"PRIu32
" bad %s",
351 hi
- lo
, hi
== lo
+ 1 ?
"sector" : "sectors");
352 if (err
!= EIO
) append_progress(" (%s)", strerror(err
));
356 static ssize_t
read_sectors(struct source
*src
, uint32_t pos
,
357 void *buf
, uint32_t want
)
363 n
= DVDReadBlocks(src
->vob
, pos
- src
->file
->start
, want
, buf
);
364 else if (src
->file
) {
365 if (lseek(src
->dvdfd
, (off_t
)pos
*SECTORSZ
, SEEK_SET
) < 0)
366 bail_syserr(errno
, "failed to seek to sector %"PRIu32
"", pos
);
367 n
= read(src
->dvdfd
, buf
, want
*SECTORSZ
);
368 if (n
>= 0) n
/= SECTORSZ
;
370 memset(buf
, 0, want
*SECTORSZ
);
374 if (n
< 0 && errno
== EINTR
) goto again
;
378 static void carefully_write(int fd
, const void *buf
, size_t sz
)
380 const unsigned char *p
= buf
;
385 n
= write(fd
, p
, sz
);
387 if (errno
== EINTR
) continue;
388 bail_syserr(errno
, "failed to write to output file");
390 if (!n
) bail("unexpected short write to output file");
395 static void emit(struct source
*src
, int outfd
, uint32_t start
, uint32_t end
)
397 #define BUFSECTORS 512
400 unsigned char buf
[BUFSECTORS
*SECTORSZ
];
402 uint32_t bad_lo
, bad_hi
, good
, step
;
405 static int first_time
= 1;
412 least
= least_live();
415 printf(";; %8"PRIu32
" .. %"PRIu32
"\n", start
, end
);
417 for (i
= 0; i
< filetab
.n
; i
++) {
418 if (!livep(i
)) continue;
419 if (act
== -1) act
= i
;
420 f
= &filetab
.v
[i
]; store_filename(fn
, f
->id
);
421 printf(";;\t\t%8"PRIu32
" .. %-8"PRIu32
" %s\n",
422 start
- f
->start
, end
- f
->start
, fn
);
424 if (act
== -1) printf(";;\t\t#<no live source>\n");
425 assert(act
== least
);
429 { src
->file
= 0; src
->vob
= 0; }
431 src
->file
= &filetab
.v
[least
];
432 switch (id_kind(src
->file
->id
)) {
437 if (first_time
) { clear_progress(); first_time
= 0; }
438 src
->vob
= DVDOpenFile(src
->dvd
, id_title(src
->file
->id
),
439 id_part(src
->file
->id
)
440 ? DVD_READ_TITLE_VOBS
441 : DVD_READ_MENU_VOBS
);
443 bail("failed to open %s %u",
444 id_part(src
->file
->id
) ?
"title" : "menu",
445 id_title(src
->file
->id
));
454 want
= end
- pos
; if (want
> BUFSECTORS
) want
= BUFSECTORS
;
455 n
= read_sectors(src
, pos
, buf
, want
);
458 report_bad_blocks_progress(src
, pos
, pos
, errno
);
459 for (i
= 0; i
< 4; i
++) {
460 n
= read_sectors(src
, pos
, buf
, 1);
463 fprintf(stderr
, "%s: sector %"PRIu32
" read ok after retry\n",
465 bad_lo
= bad_hi
= pos
;
470 bad_lo
= pos
; step
= 1; bad_hi
= pos
+ 1;
472 report_bad_blocks_progress(src
, bad_lo
, bad_hi
, errno
);
475 fprintf(stderr
, "%s: giving up on this extent\n", prog
);
476 n
= 0; goto recovered
;
479 if (step
> end
- bad_lo
) step
= end
- bad_lo
;
480 pos
= bad_lo
+ step
- 1;
481 n
= read_sectors(src
, pos
, buf
, 1);
487 while (good
> bad_hi
) {
488 report_bad_blocks_progress(src
, bad_lo
, bad_hi
, errno
);
489 pos
= bad_hi
+ (good
- bad_hi
)/2;
490 n
= read_sectors(src
, pos
, buf
, 1);
491 if (n
> 0) good
= pos
;
492 else bad_hi
= pos
+ 1;
495 if (bad_hi
> bad_lo
) {
497 fprintf(stderr
, "%s: skipping %"PRIu32
" bad sectors "
498 "(%"PRIu32
" .. %"PRIu32
")\n",
499 prog
, bad_hi
- bad_lo
, bad_lo
, bad_hi
);
502 src
->mapfp
= fopen(src
->mapfile
, "w");
504 bail_syserr(errno
, "failed to open bad-sector map file `%s'",
506 fprintf(src
->mapfp
, "## bad sector map\n\n");
508 fprintf(src
->mapfp
, "%"PRIu32
" %"PRIu32
"\n", bad_lo
, bad_hi
);
510 if (ferror(src
->mapfp
))
511 bail_syserr(errno
, "error writing bad-sector map file");
514 lseek(outfd
, (off_t
)(bad_hi
- bad_lo
)*SECTORSZ
, SEEK_CUR
) < 0)
515 bail_syserr(errno
, "failed to seek past bad sectors");
521 if (n
> 0) { carefully_write(outfd
, buf
, n
*SECTORSZ
); pos
+= n
; }
522 report_progress(src
, pos
); fflush(stdout
);
525 if (src
->vob
) { DVDCloseFile(src
->vob
); src
->vob
= 0; }
531 static void logfn(void *p
, dvd_logger_level_t lev
,
532 const char *fmt
, va_list ap
)
535 case DVD_LOGGER_LEVEL_ERROR
:
536 fprintf("%s (libdvdread error): ", prog
);
538 case DVD_LOGGER_LEVEL_WARN
:
539 fprintf("%s (libdvdread warning): ", prog
);
544 vfprintf(stderr
, fmt
, ap
);
547 static const dvd_logger_cb logger
= { logfn
};
554 #define BUF_INIT { 0, 0, 0 }
555 #define BUF_REWIND(b) do { (b)->n = 0; } while (0)
556 #define BUF_FREE(b) do { \
558 free(_b->p); _b->p = 0; _b->n = _b->sz = 0; \
560 #define BUF_PUTC(b, ch) do { \
561 struct buf *_b = (b); \
562 if (_b->n >= _b->sz) { \
563 _b->sz = _b->sz ? 2*_b->sz : 32; \
564 _b->p = realloc(_b->p, _b->sz); \
565 if (!_b->p) bail("out of memory allocating %zu bytes", _b->sz); \
567 _b->p[_b->n] = (ch); \
570 static int read_line(FILE *fp
, struct buf
*b
)
577 else if (ch
!= '\n') do {
578 BUF_PUTC(b
, ch
); b
->n
++;
580 } while (ch
!= EOF
&& ch
!= '\n');
585 int main(int argc
, char *argv
[])
592 struct source src
= SOURCE_INIT
;
593 unsigned long start
, end
;
594 const struct event
*ev
;
595 const char *device
= "/dev/dvd", *outfile
= 0;
596 int opt
, err
, outfd
= -1, blksz
;
599 struct buf buf
= BUF_INIT
;
601 const struct file
*file
;
606 #define f_continue 2u
610 p
= strrchr(argv
[0], '/'); prog
= p ? p
+ 1 : argv
[0];
612 opt
= getopt(argc
, argv
, "hD:FR:b:co:r:"); if (opt
< 0) break;
614 case 'h': usage(stderr
); exit(0);
615 case 'D': device
= optarg
; break;
616 case 'F': f
|= f_fixup
; break;
618 fp
= fopen(optarg
, "r");
620 bail_syserr(errno
, "failed to open ranges file `%s'", optarg
);
623 BUF_REWIND(&buf
); if (read_line(fp
, &buf
)) break;
625 while (ISSPACE(*p
)) p
++;
626 if (!*p
|| *p
== '#') continue;
627 if (!ISDIGIT(*p
)) goto bad_range_file
;
628 start
= strtoul(p
, &p
, 0);
629 if (errno
|| !ISSPACE(*p
)) goto bad_range_file
;
630 do p
++; while (ISSPACE(*p
));
631 if (!ISDIGIT(*p
)) goto bad_range_file
;
632 end
= strtoul(p
, &p
, 0);
633 if (errno
|| (*p
&& !ISSPACE(*p
))) goto bad_range_file
;
634 while (ISSPACE(*p
)) p
++;
635 if (*p
) goto bad_range_file
;
636 if (start
> end
) goto bad_range_file
;
638 put_event(EV_WRITE
, 0, start
);
639 put_event(EV_STOP
, 0, end
);
643 bail_syserr(errno
, "failed to read ranges file `%s'", optarg
);
646 bail("bad range `%s' at `%s' line %zu", buf
.p
, optarg
, i
);
648 if (src
.mapfile
) bail("can't have multiple map files");
649 src
.mapfile
= optarg
;
651 case 'c': f
|= f_continue
; break;
652 case 'o': outfile
= optarg
; break;
654 err
= errno
; errno
= 0;
659 if (!ISDIGIT(*p
)) goto bad_range
;
660 start
= strtoul(p
, &p
, 0);
661 if (errno
|| *p
!= '-') goto bad_range
;
665 put_event(EV_WRITE
, 0, start
);
667 if (!ISDIGIT(*p
)) goto bad_range
;
668 end
= strtoul(p
, &p
, 0);
669 if (errno
|| *p
) goto bad_range
;
670 if (start
> end
) goto bad_range
;
672 put_event(EV_WRITE
, 0, start
);
673 put_event(EV_STOP
, 0, end
);
679 bail("bad range `%s'", optarg
);
681 default: f
|= f_bogus
; break;
684 if (optind
< argc
) f
|= f_bogus
;
685 if (f
&f_bogus
) { usage(stderr
); exit(2); }
687 src
.dvdfd
= open(device
, O_RDONLY
);
688 if (src
.dvdfd
< 0) bail_syserr(errno
, "failed to open device `%s'", device
);
689 if (ioctl(src
.dvdfd
, BLKSSZGET
, &blksz
))
690 bail_syserr(errno
, "failed to get block size for `%s'", device
);
691 if (ioctl(src
.dvdfd
, BLKGETSIZE64
, &volsz
))
692 bail_syserr(errno
, "failed to get volume size for `%s'", device
);
694 if (blksz
!= SECTORSZ
)
695 bail("device `%s' block size %d /= %d", device
, blksz
, SECTORSZ
);
697 bail("device `%s' volume size %"PRIu64
" not a multiple of %d",
698 device
, volsz
, SECTORSZ
);
701 outfd
= open(outfile
, O_WRONLY
| O_CREAT
, 0666);
703 bail_syserr(errno
, "failed to create output file `%s'", outfile
);
707 if (!outfile
) bail("can't continue without output file");
708 off
= lseek(outfd
, 0, SEEK_END
);
710 bail_syserr(errno
, "failed to seek to end of output file `%s'",
712 put_event(EV_WRITE
, 0, off
/SECTORSZ
);
713 } else if (!eventq
.n
&& !(f
&f_fixup
))
714 put_event(EV_WRITE
, 0, 0);
717 src
.dvd
= DVDOpen2(0, &logger
, device
);
719 src
.dvd
= DVDOpen(device
);
721 if (!src
.dvd
) bail("failed to open DVD on `%s'", device
);
723 /* It's fast enough just to check everything. */
724 put_menu(src
.dvd
, 0);
725 for (i
= 1; i
< 100; i
++) {
726 put_menu(src
.dvd
, i
);
727 put_title(src
.dvd
, i
);
729 put_file(mkident(RAW
, 0, 0), 0, volsz
/SECTORSZ
);
730 assert(filetab
.n
<= MAXFILES
);
732 for (i
= 0, src
.limit
= 0; i
< filetab
.n
; i
++)
733 if (filetab
.v
[i
].end
> src
.limit
) src
.limit
= filetab
.v
[i
].end
;
735 if (end
> src
.limit
) end
= src
.limit
;
738 printf("\n;; files:\n");
739 for (i
= 0; i
< filetab
.n
; i
++) {
740 file
= &filetab
.v
[i
];
741 store_filename(fn
, file
->id
);
742 printf(";;\t%8"PRIu32
" %s\n", file
->start
, fn
);
746 qsort(eventq
.v
, eventq
.n
, sizeof(struct event
), compare_event
);
748 f
&= ~f_write
; start
= 0;
749 for (i
= 0; i
< eventq
.n
; i
++) {
754 bail("overlapping ranges: range from %lu still open at %"PRIu32
"",
756 f
|= f_write
; start
= ev
->pos
;
764 f
&= ~f_write
; start
= 0;
765 for (i
= 0; i
< eventq
.n
; i
++) {
768 case EV_WRITE
: start
= ev
->pos
; f
|= f_write
; break;
769 case EV_STOP
: src
.nsectors
+= ev
->pos
- start
; f
&= ~f_write
; break;
771 if (ev
->pos
>= src
.limit
) break;
772 if (f
&f_fixup
) start
= ev
->pos
;
776 put_event(EV_WRITE
, 0, start
);
780 src
.nsectors
+= src
.limit
- start
;
781 put_event(EV_STOP
, 0, src
.limit
);
786 printf("\n;; event sweep:\n");
788 for (pos
= 0, i
= 0; i
< eventq
.n
; i
++) {
791 if (f
&f_write
) emit(&src
, outfd
, pos
, ev
->pos
);
802 store_filename(fn
, filetab
.v
[ev
->file
].id
);
804 printf(";; %8"PRIu32
": begin `%s'\n", pos
, fn
);
808 gettimeofday(&src
.last_time
, 0); src
.last_pos
= pos
;
810 lseek(outfd
, (off_t
)ev
->pos
*SECTORSZ
, SEEK_SET
) < 0)
812 "failed to seek to resume position "
813 "(sector %"PRIu32
") in output file `%s'",
817 printf(";; %8"PRIu32
": begin write\n", pos
);
825 printf(";; %8"PRIu32
": end write\n", pos
);
829 clear_live(ev
->file
);
831 store_filename(fn
, filetab
.v
[ev
->file
].id
);
833 printf(";; %8"PRIu32
": end `%s'\n", pos
, fn
);
840 if (progresslen
) putchar('\n');
842 if (outfd
>= 0 && ftruncate(outfd
, (off_t
)src
.limit
*SECTORSZ
) < 0)
843 bail_syserr(errno
, "failed to set output file `%s' length", outfile
);
845 if (src
.dvd
) DVDClose(src
.dvd
);
846 if (src
.dvdfd
>= 0) close(src
.dvdfd
);
847 if (outfd
>= 0) close(outfd
);
849 if (ferror(src
.mapfp
) || fclose(src
.mapfp
))
850 bail_syserr(errno
, "error writing bad-sector map file");