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);
}
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 }
typedef uint_least32_t secaddr;
#define PRIuSEC PRIuLEAST32
+#define SECLIMIT 0x00400000
#define MAXFILES (1 + 2*99 + 1)
struct file {
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)
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)
{
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;
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
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;
}
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;
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;
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);
}
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'",
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;
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();
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'",
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);