X-Git-Url: https://git.distorted.org.uk/~mdw/dvdrip/blobdiff_plain/5cc1b8be9f072032c1b6f97a9ba658cc8d2183bf..12db0342d238464a54ee65dab857885948e1caf6:/dvd-sector-copy.c?ds=sidebyside diff --git a/dvd-sector-copy.c b/dvd-sector-copy.c index 6fec2de..c6bad44 100644 --- a/dvd-sector-copy.c +++ b/dvd-sector-copy.c @@ -43,8 +43,8 @@ static int status = 0; static void usage(FILE *fp) { fprintf(fp, - "usage: %s [-c] [-D DEV] [-R MAP] " - "[-b OUTMAP] [-o OUTFILE] [-r [START]-[END]]\n", + "usage: %s [-c] [-R MAP] [-b OUTMAP] [-r [START]-[END]]\n" + "\tDEVICE OUTFILE\n", prog); } @@ -81,6 +81,23 @@ static void bail_syserr(int err, const char *fmt, ...) exit(2); } +static void carefully_write(int fd, const void *buf, size_t sz) +{ + const unsigned char *p = buf; + ssize_t n; + + if (fd < 0) return; + while (sz) { + n = write(fd, p, sz); + if (n < 0) { + if (errno == EINTR) continue; + bail_syserr(errno, "failed to write to output file"); + } + if (!n) bail("unexpected short write to output file"); + p += n; sz -= n; + } +} + #define DEFVEC(vtype, etype) \ typedef struct { etype *v; size_t n, sz; } vtype #define VEC_INIT { 0, 0, 0 } @@ -135,6 +152,7 @@ static void store_filename(char *buf, ident id) typedef uint_least32_t secaddr; #define PRIuSEC PRIuLEAST32 +#define SECLIMIT 0x00400000 #define MAXFILES (1 + 2*99 + 1) struct file { @@ -267,7 +285,10 @@ static void put_title(dvd_reader_t *dvd, unsigned title) static int progresslen = 0; static void clear_progress_internal(void) - { while (progresslen) { fputs("\b \b", stdout); progresslen--; } } +{ + while (progresslen) { fputs("\b \b", stdout); progresslen--; } + putchar('\r'); +} static void clear_progress(void) { clear_progress_internal(); fflush(stdout); } static void vappend_progress(const char *fmt, va_list ap) @@ -292,17 +313,12 @@ static void print_progress(const char *fmt, ...) va_end(ap); } -static dvd_reader_t *dvd; -static int dvdfd = -1; -static struct file *file; -static dvd_file_t *vob; - unsigned flags; # define F_ALLPROGRESS 1u static secaddr last_pos, limit, nsectors, ndone; static struct timeval last_time; static double wsum, wcount; -static const char *mapfile; static FILE *mapfp; +static struct file *file; static void report_progress(secaddr pos) { @@ -369,6 +385,11 @@ static void report_bad_blocks_progress(secaddr lo, secaddr hi, int err) fflush(stdout); } +static dvd_reader_t *dvd; +static int dvdfd = -1, outfd = -1; +static dvd_file_t *vob; +static const char *mapfile; static FILE *mapfp; + static ssize_t read_sectors(secaddr pos, void *buf, secaddr want) { ssize_t n; @@ -390,24 +411,7 @@ again: return (n); } -static void carefully_write(int fd, const void *buf, size_t sz) -{ - const unsigned char *p = buf; - ssize_t n; - - if (fd < 0) return; - while (sz) { - n = write(fd, p, sz); - if (n < 0) { - if (errno == EINTR) continue; - bail_syserr(errno, "failed to write to output file"); - } - if (!n) bail("unexpected short write to output file"); - p += n; sz -= n; - } -} - -static void emit(int outfd, secaddr start, secaddr end) +static void emit(secaddr start, secaddr end) { #define BUFSECTORS 512 @@ -523,8 +527,7 @@ static void emit(int outfd, secaddr start, secaddr end) if (ferror(mapfp)) bail_syserr(errno, "error writing bad-sector map file"); } - if (outfd >= 0 && - lseek(outfd, (off_t)(bad_hi - bad_lo)*SECTORSZ, SEEK_CUR) < 0) + if (lseek(outfd, (off_t)(bad_hi - bad_lo)*SECTORSZ, SEEK_CUR) < 0) bail_syserr(errno, "failed to seek past bad sectors"); status = 1; } @@ -595,6 +598,49 @@ static int read_line(FILE *fp, struct buf *b) return (0); } +#define PRF_HYPHEN 1u +static int parse_range(const char *p, unsigned f, + secaddr *start_out, secaddr *end_out) +{ + char *q; + int err, rc; + unsigned long start, end; + + err = errno; + + if (ISDIGIT(*p)) { + start = strtoul(p, &q, 0); + if (errno || start >= SECLIMIT) { rc = -1; goto end; } + *start_out = start; p = q; + } else if (!(f&PRF_HYPHEN)) + { rc = -1; goto end; } + else + start = 0; + + if (f&PRF_HYPHEN) { + if (*p != '-') { rc = -1; goto end; } + p++; + } else { + if (!ISSPACE(*p)) { rc = -1; goto end; } + do p++; while (ISSPACE(*p)); + } + + if (ISDIGIT(*p)) { + end = strtoul(p, &q, 0); + if (errno || end > SECLIMIT || end < start) { rc = -1; goto end; } + *end_out = end; p = q; + } else if (!(f&PRF_HYPHEN)) + { rc = -1; goto end; } + + if (!(f&PRF_HYPHEN)) while (ISSPACE(*p)) p++; + if (*p) { rc = -1; goto end; } + + rc = 0; +end: + errno = err; + return (rc); +} + int main(int argc, char *argv[]) { unsigned f = 0; @@ -602,10 +648,10 @@ int main(int argc, char *argv[]) uint64_t volsz; secaddr pos; off_t off; - unsigned long start, end; + secaddr start, end, last; const struct event *ev; - const char *device = "/dev/dvd", *outfile = 0; - int opt, err, outfd = -1, blksz; + const char *device, *outfile; + int opt, blksz; unsigned n; size_t i; FILE *fp; @@ -623,81 +669,58 @@ int main(int argc, char *argv[]) p = strrchr(argv[0], '/'); prog = p ? p + 1 : argv[0]; for (;;) { - opt = getopt(argc, argv, "hD:FR:b:co:r:"); if (opt < 0) break; + opt = getopt(argc, argv, "hFR:X:b:cr:"); if (opt < 0) break; switch (opt) { case 'h': usage(stderr); exit(0); - case 'D': device = optarg; break; case 'F': f |= f_fixup; break; case 'R': fp = fopen(optarg, "r"); if (!fp) bail_syserr(errno, "failed to open ranges file `%s'", optarg); - i = 0; + i = 0; last = -1; for (;;) { BUF_REWIND(&buf); if (read_line(fp, &buf)) break; p = buf.p; i++; while (ISSPACE(*p)) p++; if (!*p || *p == '#') continue; - if (!ISDIGIT(*p)) goto bad_range_file; - start = strtoul(p, &p, 0); - if (errno || !ISSPACE(*p)) goto bad_range_file; - do p++; while (ISSPACE(*p)); - if (!ISDIGIT(*p)) goto bad_range_file; - end = strtoul(p, &p, 0); - if (errno || (*p && !ISSPACE(*p))) goto bad_range_file; - while (ISSPACE(*p)) p++; - if (*p) goto bad_range_file; - if (start > end) goto bad_range_file; + if (parse_range(p, 0, &start, &end) || + (last <= SECLIMIT && start < last)) + bail("bad range `%s' at `%s' line %zu", buf.p, optarg, i); if (start < end) { - put_event(EV_WRITE, 0, start); - put_event(EV_STOP, 0, end); + if (start == last) + eventq.v[eventq.n - 1].pos = end; + else { + put_event(EV_WRITE, 0, start); + put_event(EV_STOP, 0, end); + } + last = end; } } if (ferror(fp)) bail_syserr(errno, "failed to read ranges file `%s'", optarg); break; - bad_range_file: - bail("bad range `%s' at `%s' line %zu", buf.p, optarg, i); case 'b': if (mapfile) bail("can't have multiple map files"); mapfile = optarg; break; case 'c': f |= f_continue; break; - case 'o': outfile = optarg; break; case 'r': - err = errno; errno = 0; - p = optarg; - if (*p == '-') - start = 0; - else { - if (!ISDIGIT(*p)) goto bad_range; - start = strtoul(p, &p, 0); - if (errno || *p != '-') goto bad_range; - } - p++; - if (!*p) + start = 0; end = -1; + if (parse_range(optarg, PRF_HYPHEN, &start, &end)) + bail("bad range `%s'", optarg); + if (start < end) { put_event(EV_WRITE, 0, start); - else { - if (!ISDIGIT(*p)) goto bad_range; - end = strtoul(p, &p, 0); - if (errno || *p) goto bad_range; - if (start > end) goto bad_range; - if (start < end) { - put_event(EV_WRITE, 0, start); - put_event(EV_STOP, 0, end); - } + if (end <= SECLIMIT) put_event(EV_STOP, 0, end); } - errno = err; - break; - bad_range: - bail("bad range `%s'", optarg); break; default: f |= f_bogus; break; } } - if (optind < argc) f |= f_bogus; + if (argc - optind != 2) f |= f_bogus; if (f&f_bogus) { usage(stderr); exit(2); } + device = argv[optind]; outfile = argv[optind + 1]; + dvdfd = open(device, O_RDONLY); if (dvdfd < 0) bail_syserr(errno, "failed to open device `%s'", device); @@ -727,7 +750,6 @@ int main(int argc, char *argv[]) } if (f&f_continue) { - if (!outfile) bail("can't continue without output file"); off = lseek(outfd, 0, SEEK_END); if (off < 0) bail_syserr(errno, "failed to seek to end of output file `%s'", @@ -774,7 +796,7 @@ int main(int argc, char *argv[]) switch (ev->ev) { case EV_WRITE: if (f&f_write) - bail("overlapping ranges: range from %lu still open at %"PRIuSEC"", + bail("overlapping ranges: range from %"PRIuSEC" still open at %"PRIuSEC"", start, ev->pos); n++; f |= f_write; start = ev->pos; break; @@ -812,7 +834,7 @@ int main(int argc, char *argv[]) for (pos = 0, i = 0; i < eventq.n; i++) { ev = &eventq.v[i]; if (ev->pos > pos) { - if (f&f_write) emit(outfd, pos, ev->pos); + if (f&f_write) emit(pos, ev->pos); pos = ev->pos; #ifdef DEBUG clear_progress(); @@ -830,8 +852,7 @@ int main(int argc, char *argv[]) break; case EV_WRITE: gettimeofday(&last_time, 0); last_pos = pos; - if (outfd >= 0 && - lseek(outfd, (off_t)ev->pos*SECTORSZ, SEEK_SET) < 0) + if (lseek(outfd, (off_t)ev->pos*SECTORSZ, SEEK_SET) < 0) bail_syserr(errno, "failed to seek to resume position " "(sector %"PRIuSEC") in output file `%s'", @@ -863,7 +884,7 @@ int main(int argc, char *argv[]) if (progresslen) putchar('\n'); - if (outfd >= 0 && ftruncate(outfd, (off_t)limit*SECTORSZ) < 0) + if (ftruncate(outfd, (off_t)limit*SECTORSZ) < 0) bail_syserr(errno, "failed to set output file `%s' length", outfile); if (dvd) DVDClose(dvd);