dvd-sector-copy.c: Report final statistics correctly.
[dvdrip] / dvd-sector-copy.c
index bed3cf6..cd45710 100644 (file)
@@ -3,7 +3,7 @@
 static void usage(FILE *fp)
 {
   fprintf(fp,
-         "usage: %s [-c] [-B PARAM=VALUE,...] [-R MAP]\n"
+         "usage: %s [-ci] [-B PARAM=VALUE,...] [-R MAP]\n"
          "\t[-b OUTMAP] [-r [START]-[END]] DEVICE OUTFILE\n",
          prog);
 }
@@ -15,49 +15,6 @@ static double tvdiff(const struct timeval *tv_lo,
          (tv_hi->tv_usec - tv_lo->tv_usec)/1.0e6);
 }
 
-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 open_file_on_demand(const char *file, FILE **fp_inout,
-                               const char *what)
-{
-  FILE *fp;
-
-  if (!*fp_inout) {
-    fp = fopen(file, "w");
-    if (!fp)
-      bail_syserr(errno, "failed to open %s file `%s'", what, file);
-    fprintf(fp, "## %s\n\n", what);
-    *fp_inout = fp;
-  }
-}
-
-static void check_write(FILE *fp, const char *what)
-{
-  fflush(fp);
-  if (ferror(fp)) bail_syserr(errno, "error writing %s file", what);
-}
-
-static void carefully_fclose(FILE *fp, const char *what)
-{
-  if (fp && (ferror(fp) || fclose(fp)))
-    bail_syserr(errno, "error writing %s file", what);
-}
-
 #define DEFVEC(vtype, etype)                                           \
        typedef struct { etype *v; size_t n, sz; } vtype
 #define VEC_INIT { 0, 0, 0 }
@@ -313,6 +270,8 @@ static void render_badblock_progress(struct progress_item *item,
   progress_shownotice(render, bg, 7);
 }
 
+static double alpha = 0.1;
+
 static void update_progress(secaddr pos)
 {
   struct timeval now;
@@ -321,8 +280,7 @@ static void update_progress(secaddr pos)
   gettimeofday(&now, 0);
   t = tvdiff(&last_time, &now);
 
-#define ALPHA 0.1
-#define BETA (1 - ALPHA)
+#define BETA (1 - alpha)
 
   if (t) {
     g = wcount ? pow(BETA, t) : 0.0; f = (1 - g)/(1 - BETA);
@@ -332,7 +290,6 @@ static void update_progress(secaddr pos)
     last_time = now; last_pos = pos;
   }
 
-#undef ALPHA
 #undef BETA
 
   throbix++; if (!throbber[throbix]) throbix = 0;
@@ -378,7 +335,7 @@ static ssize_t read_sectors(secaddr pos, void *buf, secaddr want)
   if (badblocks.n) {
     best = 0; lo = 0; hi = badblocks.n;
 #ifdef DEBUG
-    progress_clear();
+    progress_clear(&progress);
     printf(";; searching badblocks for %"PRIuSEC" .. %"PRIuSEC"\n",
           pos, pos + want);
 #endif
@@ -498,7 +455,7 @@ static PRINTF_LIKE(2, 3)
   va_list ap;
 
   va_start(ap, what);
-  progress_clear();
+  progress_clear(&progress);
   printf(";; recovery buffer (");
   vprintf(what, ap);
   printf("): "
@@ -531,7 +488,7 @@ static ssize_t recovery_read_buffer(struct recoverybuf *r,
   ssize_t n;
 
 #ifdef DEBUG
-  progress_clear();
+  progress_clear(&progress);
   show_recovery_buffer_map(r, "begin(%"PRIuSEC", %"PRIuSEC")", pos, want);
 #endif
 
@@ -553,7 +510,7 @@ static ssize_t recovery_read_buffer(struct recoverybuf *r,
   } else if (pos > r->pos + r->end) {
       r->pos = pos; r->start = r->end = 0;
 #ifdef DEBUG
-p      show_recovery_buffer_map(r, "cleared; beyond previous region");
+      show_recovery_buffer_map(r, "cleared; beyond previous region");
 #endif
   } else if (pos + want > r->pos + r->sz) {
     diff = (pos + want) - (r->pos + r->sz);
@@ -979,11 +936,44 @@ end:
   return (rc);
 }
 
+#ifdef DEBUG
+static void dump_eventq(const char *what)
+{
+  unsigned i;
+  const struct event *ev;
+  char fn[MAXFNSZ];
+
+  printf("\n;; event dump (%s):\n", what);
+  for (i = 0; i < eventq.n; i++) {
+    ev = &eventq.v[i];
+    switch (ev->ev) {
+      case EV_BEGIN:
+       store_filename(fn, filetab.v[ev->file].id);
+       printf(";; %8"PRIuSEC": begin %s\n", ev->pos, fn);
+       break;
+      case EV_END:
+       store_filename(fn, filetab.v[ev->file].id);
+       printf(";; %8"PRIuSEC": end %s\n", ev->pos, fn);
+       break;
+      case EV_WRITE:
+       printf(";; %8"PRIuSEC": write\n", ev->pos);
+       break;
+      case EV_STOP:
+       printf(";; %8"PRIuSEC": stop\n", ev->pos);
+       break;
+      default:
+       printf(";; %8"PRIuSEC": ?%u\n", ev->pos, ev->ev);
+       break;
+    }
+  }
+}
+#endif
+
 int main(int argc, char *argv[])
 {
   unsigned f = 0;
   const char *p;
-  uint64_t volsz;
+  off_t volsz;
   secaddr pos;
   off_t off;
   secaddr start, end, last;
@@ -991,15 +981,14 @@ int main(int argc, char *argv[])
   const char *device, *outfile;
   struct badblock *bad;
   int opt, blksz;
-  unsigned n;
   size_t i;
   FILE *fp;
   struct buf buf = BUF_INIT;
   struct timeval tv0, tv1;
   double t, rate, tot;
   const char *rateunit, *totunit;
-  char timebuf[TIMESTRMAX];
-  struct stat st;
+  char timebuf[TIMESTRMAX], id_in[MAXIDSZ], id_out[MAXIDSZ];
+  dvd_reader_t *dvd_out;
 #ifdef DEBUG
   const struct file *file;
   char fn[MAXFNSZ];
@@ -1009,11 +998,12 @@ int main(int argc, char *argv[])
 #define f_continue 2u
 #define f_fixup 4u
 #define f_stats 8u
+#define f_checkid 16u
 #define f_write 256u
 
   set_prog(argv[0]);
   for (;;) {
-    opt = getopt(argc, argv, "hB:E:FR:X:b:cr:s"); if (opt < 0) break;
+    opt = getopt(argc, argv, "hB:E:FR:X:b:cir:s"); if (opt < 0) break;
     switch (opt) {
       case 'h': usage(stderr); exit(0);
       case 'B':
@@ -1035,6 +1025,8 @@ int main(int argc, char *argv[])
            step_max = parse_int(&p, 1, SECLIMIT - 1, "step maximum");
          else if (SKIP_PREFIX("retry"))
            max_retries = parse_int(&p, 0, INT_MAX, "retries");
+         else if (SKIP_PREFIX("alpha"))
+           alpha = parse_float(&p, 0, 1, "average decay factor");
          else if (SKIP_PREFIX("_badwait"))
            bad_block_delay = parse_float(&p, 0, DBL_MAX, "bad-block delay");
          else if (SKIP_PREFIX("_blkwait"))
@@ -1099,6 +1091,7 @@ int main(int argc, char *argv[])
        mapfile = optarg;
        break;
       case 'c': f |= f_continue; break;
+      case 'i': f |= f_checkid; break;
       case 'r':
        start = 0; end = -1;
        if (parse_range(optarg, PRF_HYPHEN, &start, &end))
@@ -1131,25 +1124,24 @@ int main(int argc, char *argv[])
   }
 
   open_dvd(device, &dvdfd, &dvd);
-  if (fstat(dvdfd, &st))
-    bail_syserr(errno, "failed to stat device `%s'", device);
-  if (S_ISREG(st.st_mode)) {
-    blksz = SECTORSZ;
-    volsz = st.st_size;
-  } else if (S_ISBLK(st.st_mode)) {
-    if (ioctl(dvdfd, BLKSSZGET, &blksz))
-      bail_syserr(errno, "failed to get block size for `%s'", device);
-    if (ioctl(dvdfd, BLKGETSIZE64, &volsz))
-      bail_syserr(errno, "failed to get volume size for `%s'", device);
-  } else
-    bail("can't use `%s' as source: expected file or block device", device);
 
+  blksz = SECTORSZ; volsz = device_size(dvdfd, device, &blksz);
   if (blksz != SECTORSZ)
     bail("device `%s' block size %d /= %d", device, blksz, SECTORSZ);
   if (volsz%SECTORSZ)
     bail("device `%s' volume size %"PRIu64" not a multiple of %d",
         device, volsz, SECTORSZ);
 
+  if (f&f_checkid) {
+    open_dvd(outfile, 0, &dvd_out);
+    if (dvd_id(id_in, dvd, DIF_MUSTIFOHASH, device) ||
+       dvd_id(id_out, dvd_out, DIF_MUSTIFOHASH, device))
+      exit(2);
+    if (STRCMP(id_in, !=, id_out))
+      bail("DVD id mismatch: input `%s' is `%s'; output `%s' is `%s'",
+          device, id_in, outfile, id_out);
+  }
+
   outfd = open(outfile, O_WRONLY | O_CREAT, 0666);
   if (outfd < 0)
     bail_syserr(errno, "failed to create output file `%s'", outfile);
@@ -1188,7 +1180,7 @@ int main(int argc, char *argv[])
 
   qsort(eventq.v, eventq.n, sizeof(struct event), compare_event);
 
-  f &= ~f_write; start = 0; n = 0;
+  f &= ~f_write; start = 0;
   for (i = 0; i < eventq.n; i++) {
     ev = &eventq.v[i];
     switch (ev->ev) {
@@ -1196,7 +1188,7 @@ int main(int argc, char *argv[])
        if (f&f_write)
          bail("overlapping ranges: range from %"PRIuSEC" still open at %"PRIuSEC"",
               start, ev->pos);
-       n++; f |= f_write; start = ev->pos;
+       f |= f_write; start = ev->pos;
        break;
       case EV_STOP:
        f &= ~f_write;
@@ -1204,6 +1196,9 @@ int main(int argc, char *argv[])
     }
   }
 
+#ifdef DEBUG
+  dump_eventq("initial");
+#endif
   f &= ~f_write; start = 0;
   for (i = 0; i < eventq.n; i++) {
     ev = &eventq.v[i];
@@ -1215,14 +1210,20 @@ int main(int argc, char *argv[])
     if (f&f_fixup) start = ev->pos;
   }
   eventq.n = i;
+#ifdef DEBUG
+  dump_eventq("trimmed");
+#endif
   if (f&f_fixup) {
     put_event(EV_WRITE, 0, start);
-    n++; f |= f_write;
+    f |= f_write;
   }
   if (f&f_write) {
     nsectors += limit - start;
     put_event(EV_STOP, 0, limit);
   }
+#ifdef DEBUG
+  dump_eventq("final");
+#endif
 
   copy_progress.render = render_copy_progress;
   progress_additem(&progress, &copy_progress);
@@ -1297,7 +1298,7 @@ int main(int argc, char *argv[])
 
   if (f&f_stats) {
     gettimeofday(&tv1, 0); t = tvdiff(&tv0, &tv1);
-    if (nsectors == limit - start) { ndone -= start; nsectors -= start; }
+    if (nsectors == limit) { ndone -= start; nsectors -= start; }
     tot = scale_bytes((double)nsectors*SECTORSZ, &totunit);
     rate = scale_bytes((double)nsectors*SECTORSZ/t, &rateunit);
     moan("all done: %.1f %sB in %s -- %.1f %sB/s",