diff --git a/src/libavrdude.h b/src/libavrdude.h
index dad47922..95e08d18 100644
--- a/src/libavrdude.h
+++ b/src/libavrdude.h
@@ -888,6 +888,7 @@ enum updateflags {
   UF_NONE = 0,
   UF_NOWRITE = 1,
   UF_AUTO_ERASE = 2,
+  UF_VERIFY = 4,
 };
 
 
@@ -898,6 +899,17 @@ typedef struct update_t {
   int    format;
 } UPDATE;
 
+typedef struct {                // File reads for flash can exclude trailing 0xff, which are cut off
+  int nbytes,                   // Number of bytes set including 0xff but excluding cut off, trailing 0xff
+      nsections,                // Number of consecutive sections in source excluding cut off, trailing 0xff
+      npages,                   // Number of memory pages needed excluding pages solely with trailing 0xff
+      nfill,                    // Number of fill bytes to make up full pages that are needed
+      ntrailing,                // Number of trailing 0xff in source
+      firstaddr,                // First address set in [0, mem->size-1]
+      lastaddr;                 // Highest address set by input file
+} Filestats;
+
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -910,6 +922,14 @@ extern void free_update(UPDATE * upd);
 extern int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd,
 		 enum updateflags flags);
 
+extern int memstats(struct avrpart *p, char *memtype, int size, Filestats *fsp);
+
+// Convenience functions for printing
+const char *plural(int x);
+const char *inname(const char *fn);
+const char *outname(const char *fn);
+const char *interval(int a, int b);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/main.c b/src/main.c
index 772762f3..f4095d5f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -237,7 +237,7 @@ static void cleanup_main(void)
 static void replace_backslashes(char *s)
 {
   // Replace all backslashes with forward slashes
-  for (int i = 0; i < strlen(s); i++) {
+  for (size_t i = 0; i < strlen(s); i++) {
     if (s[i] == '\\') {
       s[i] = '/';
     }
@@ -267,7 +267,6 @@ int main(int argc, char * argv [])
   int     calibrate;   /* 1=calibrate RC oscillator, 0=don't */
   char  * port;        /* device port (/dev/xxx) */
   int     terminal;    /* 1=enter terminal mode, 0=don't */
-  int     verify;      /* perform a verify operation */
   char  * exitspecs;   /* exit specs string from command line */
   char  * programmer;  /* programmer id */
   char  * partdesc;    /* part id */
@@ -284,7 +283,7 @@ int main(int argc, char * argv [])
   int     init_ok;     /* Device initialization worked well */
   int     is_open;     /* Device open succeeded */
   char  * logfile;     /* Use logfile rather than stderr for diagnostics */
-  enum updateflags uflags = UF_AUTO_ERASE; /* Flags for do_op() */
+  enum updateflags uflags = UF_AUTO_ERASE | UF_VERIFY; /* Flags for do_op() */
 
 #if !defined(WIN32)
   char  * homedir;
@@ -349,7 +348,6 @@ int main(int argc, char * argv [])
   p             = NULL;
   ovsigck       = 0;
   terminal      = 0;
-  verify        = 1;        /* on by default */
   quell_progress = 0;
   exitspecs     = NULL;
   pgm           = NULL;
@@ -525,12 +523,6 @@ int main(int argc, char * argv [])
           exit(1);
         }
         ladd(updates, upd);
-
-        if (verify && upd->op == DEVICE_WRITE) {
-          upd = dup_update(upd);
-          upd->op = DEVICE_VERIFY;
-          ladd(updates, upd);
-        }
         break;
 
       case 'v':
@@ -538,7 +530,7 @@ int main(int argc, char * argv [])
         break;
 
       case 'V':
-        verify = 0;
+        uflags &= ~UF_VERIFY;
         break;
 
       case 'x':
diff --git a/src/update.c b/src/update.c
index 1840f81f..ffddfa16 100644
--- a/src/update.c
+++ b/src/update.c
@@ -45,7 +45,7 @@ UPDATE * parse_op(char * s)
 
   i = 0;
   p = s;
-  while ((i < (sizeof(buf)-1) && *p && (*p != ':')))
+  while (i < (int) sizeof(buf)-1 && *p && *p != ':')
     buf[i++] = *p++;
   buf[i] = 0;
 
@@ -214,18 +214,124 @@ void free_update(UPDATE * u)
 }
 
 
+// Memory statistics considering holes after a file read returned size bytes
+int memstats(struct avrpart *p, char *memtype, int size, Filestats *fsp) {
+  Filestats ret = { 0 };
+  AVRMEM *mem = avr_locate_mem(p, memtype);
+
+  if(!mem) {
+    avrdude_message(MSG_INFO, "%s: %s %s undefined\n",
+      progname, p->desc, memtype);
+    return LIBAVRDUDE_GENERAL_FAILURE;
+  }
+
+  if(!mem->buf || !mem->tags) {
+    avrdude_message(MSG_INFO, "%s: %s %s is not set\n",
+      progname, p->desc, memtype);
+    return LIBAVRDUDE_GENERAL_FAILURE;
+  }
+
+  int pgsize = mem->page_size;
+  if(pgsize < 1)
+    pgsize = 1;
+
+  if(size < 0 || size > mem->size) {
+    avrdude_message(MSG_INFO, "%s: memstats() size %d at odds with %s %s size %d\n",
+      progname, size, p->desc, memtype, mem->size);
+    return LIBAVRDUDE_GENERAL_FAILURE;
+  }
+
+  ret.lastaddr = -1;
+  int firstset = 0, insection = 0;
+  // Scan all memory
+  for(int addr = 0; addr < mem->size; ) {
+    int pageset = 0;
+    // Go page by page
+    for(int pgi = 0; pgi < pgsize; pgi++, addr++) {
+      if(mem->tags[addr] & TAG_ALLOCATED) {
+        if(!firstset) {
+          firstset = 1;
+          ret.firstaddr = addr;
+        }
+        ret.lastaddr = addr;
+        // size can be smaller than tags suggest owing to flash trailing-0xff
+        if(addr < size) {
+          ret.nbytes++;
+          if(!pageset) {
+            pageset = 1;
+            ret.nfill += pgi;
+            ret.npages++;
+          }
+          if(!insection) {
+            insection = 1;
+            ret.nsections++;
+          }
+        } else {                // Now beyond size returned by input file read
+          ret.ntrailing++;
+          if(pageset)
+            ret.nfill++;
+        }
+      } else {                  // In a hole or beyond input file
+        insection = 0;
+        if(pageset)
+          ret.nfill++;
+      }
+    }
+  }
+
+  if(fsp)
+    *fsp = ret;
+
+  return LIBAVRDUDE_SUCCESS;
+}
+
+
+// Convenience functions for printing
+const char *plural(int x) {
+  return x==1? "": "s";
+}
+
+const char *inname(const char *fn) {
+  return !fn? "???": strcmp(fn, "-")? fn: "<stdin>";
+}
+
+const char *outname(const char *fn) {
+  return !fn? "???": strcmp(fn, "-")? fn: "<stdout>";
+}
+
+// Return sth like "[0, 0x1ff]"
+const char *interval(int a, int b) {
+  // Cyclic buffer for 20+ temporary interval strings each max 41 bytes at 64-bit int
+  static char space[20*41 + 80], *sp;
+  if(!sp || sp-space > (int) sizeof space - 80)
+    sp = space;
+
+  char *ret = sp;
+
+  sprintf(sp, a<16? "[%d": "[0x%x", a);
+  sp += strlen(sp);
+  sprintf(sp, b<16? ", %d]": ", 0x%x]", b);
+
+  // Advance beyond return string in temporary ring buffer
+  sp += strlen(sp)+1;
+
+  return ret;
+}
+
+
 int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags flags)
 {
   struct avrpart * v;
   AVRMEM * mem;
-  int size, vsize;
+  int size;
   int rc;
+  Filestats fs;
 
   mem = avr_locate_mem(p, upd->memtype);
   if (mem == NULL) {
-    avrdude_message(MSG_INFO, "\"%s\" memory type not defined for part \"%s\"\n",
-            upd->memtype, p->desc);
-    return -1;
+    avrdude_message(MSG_INFO, "%s memory type not defined for part %s\n",
+      upd->memtype, p->desc);
+    return LIBAVRDUDE_GENERAL_FAILURE;
   }
 
   AVRMEM_ALIAS * alias_mem = avr_find_memalias(p, mem);
@@ -235,167 +341,189 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
     strcat(alias_mem_desc, alias_mem->desc);
   }
   
-  if (upd->op == DEVICE_READ) {
-    /*
-     * read out the specified device memory and write it to a file
-     */
+  switch (upd->op) {
+  case DEVICE_READ:
+    // Read out the specified device memory and write it to a file
     if (upd->format == FMT_IMM) {
       avrdude_message(MSG_INFO,
-                      "%s: Invalid file format 'immediate' for output\n",
-                      progname, upd->filename);
-      return -1;
+        "%s: Invalid file format 'immediate' for output\n", progname);
+      return LIBAVRDUDE_GENERAL_FAILURE;
     }
-    if (quell_progress < 2) {
-      avrdude_message(MSG_INFO, "%s: reading %s%s memory:\n",
-            progname, mem->desc, alias_mem_desc);
-	  }
-    report_progress(0,1,"Reading");
+    if (quell_progress < 2)
+      avrdude_message(MSG_INFO, "%s: reading %s%s memory ...\n",
+        progname, mem->desc, alias_mem_desc);
+
+    report_progress(0, 1, "Reading");
     rc = avr_read(pgm, p, upd->memtype, 0);
+    report_progress(1, 1, NULL);
     if (rc < 0) {
       avrdude_message(MSG_INFO, "%s: failed to read all of %s%s memory, rc=%d\n",
-              progname, mem->desc, alias_mem_desc, rc);
-      return -1;
+        progname, mem->desc, alias_mem_desc, rc);
+      return LIBAVRDUDE_GENERAL_FAILURE;
     }
-    report_progress(1,1,NULL);
     size = rc;
 
     if (quell_progress < 2) {
       if (rc == 0)
-        avrdude_message(MSG_INFO, "%s: Flash is empty, resulting file has no contents.\n",
-                        progname);
-      avrdude_message(MSG_INFO, "%s: writing output file \"%s\"\n",
-                      progname,
-                      strcmp(upd->filename, "-")==0 ? "<stdout>" : upd->filename);
+        avrdude_message(MSG_INFO, "%s: flash is empty, resulting file has no contents\n",
+          progname);
+      avrdude_message(MSG_INFO, "%s: writing output file %s\n",
+        progname, outname(upd->filename));
     }
     rc = fileio(FIO_WRITE, upd->filename, upd->format, p, upd->memtype, size);
     if (rc < 0) {
-      avrdude_message(MSG_INFO, "%s: write to file '%s' failed\n",
-              progname, upd->filename);
-      return -1;
-    }
-  }
-  else if (upd->op == DEVICE_WRITE) {
-    /*
-     * write the selected device memory using data from a file; first
-     * read the data from the specified file
-     */
-    if (quell_progress < 2) {
-      avrdude_message(MSG_INFO, "%s: reading input file \"%s\"\n",
-                      progname,
-                      strcmp(upd->filename, "-")==0 ? "<stdin>" : upd->filename);
+      avrdude_message(MSG_INFO, "%s: write to file %s failed\n",
+        progname, outname(upd->filename));
+      return LIBAVRDUDE_GENERAL_FAILURE;
     }
+    break;
+
+  case DEVICE_WRITE:
+    // Write the selected device memory using data from a file
+
     rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1);
+    if (quell_progress < 2)
+      avrdude_message(MSG_INFO, "%s: reading input file %s for %s%s\n",
+        progname, inname(upd->filename), mem->desc, alias_mem_desc);
     if (rc < 0) {
-      avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
-              progname, upd->filename);
-      return -1;
+      avrdude_message(MSG_INFO, "%s: read from file %s failed\n",
+        progname, inname(upd->filename));
+      return LIBAVRDUDE_GENERAL_FAILURE;
     }
     size = rc;
 
-    /*
-     * write the buffer contents to the selected memory type
-     */
-    if (quell_progress < 2) {
-      avrdude_message(MSG_INFO, "%s: writing %s%s (%d bytes):\n",
-            progname, mem->desc, alias_mem_desc, size);
-	  }
+    if(memstats(p, upd->memtype, size, &fs) < 0)
+      return LIBAVRDUDE_GENERAL_FAILURE;
+
+    if(quell_progress < 2) {
+      int level = fs.nsections > 1 || fs.firstaddr > 0 || fs.ntrailing? MSG_INFO: MSG_NOTICE;
+
+      avrdude_message(level, "%*s with %d byte%s in %d section%s within %s\n",
+        (int) strlen(progname)+1, "",
+        fs.nbytes, plural(fs.nbytes),
+        fs.nsections, plural(fs.nsections),
+        interval(fs.firstaddr, fs.lastaddr));
+      if(mem->page_size > 1) {
+        avrdude_message(level, "%*s using %d page%s and %d pad byte%s",
+          (int) strlen(progname)+1, "",
+          fs.npages, plural(fs.npages),
+          fs.nfill, plural(fs.nfill));
+        if(fs.ntrailing)
+          avrdude_message(level, ", cutting off %d trailing 0xff byte%s",
+            fs.ntrailing, plural(fs.ntrailing));
+        avrdude_message(level, "\n");
+      }
+    }
+
+    // Write the buffer contents to the selected memory type
+    if (quell_progress < 2)
+      avrdude_message(MSG_INFO, "%s: writing %d byte%s %s%s ...\n",
+        progname, fs.nbytes, plural(fs.nbytes), mem->desc, alias_mem_desc);
 
     if (!(flags & UF_NOWRITE)) {
-      report_progress(0,1,"Writing");
+      report_progress(0, 1, "Writing");
       rc = avr_write(pgm, p, upd->memtype, size, (flags & UF_AUTO_ERASE) != 0);
-      report_progress(1,1,NULL);
-    }
-    else {
-      /*
-       * test mode, don't actually write to the chip, output the buffer
-       * to stdout in intel hex instead
-       */
+      report_progress(1, 1, NULL);
+    } else {
+      // Test mode: write to stdout in intel hex rather than to the chip
       rc = fileio(FIO_WRITE, "-", FMT_IHEX, p, upd->memtype, size);
     }
 
     if (rc < 0) {
       avrdude_message(MSG_INFO, "%s: failed to write %s%s memory, rc=%d\n",
-              progname, mem->desc, alias_mem_desc, rc);
-      return -1;
+        progname, mem->desc, alias_mem_desc, rc);
+      return LIBAVRDUDE_GENERAL_FAILURE;
     }
 
-    vsize = rc;
+    if (quell_progress < 2)
+      avrdude_message(MSG_INFO, "%s: %d byte%s of %s%s written\n",
+        progname, fs.nbytes, plural(fs.nbytes), mem->desc, alias_mem_desc);
 
-    if (quell_progress < 2) {
-      avrdude_message(MSG_INFO, "%s: %d bytes of %s%s written\n", progname,
-            vsize, mem->desc, alias_mem_desc);
-    }
+    // Fall through for (default) auto verify, ie, unless -V was specified
+    if (!(flags & UF_VERIFY))
+      break;
 
-  }
-  else if (upd->op == DEVICE_VERIFY) {
-    /*
-     * verify that the in memory file (p->mem[AVR_M_FLASH|AVR_M_EEPROM])
-     * is the same as what is on the chip
-     */
+  case DEVICE_VERIFY:
+    // Verify that the in memory file is the same as what is on the chip
     pgm->vfy_led(pgm, ON);
 
+    int userverify = upd->op == DEVICE_VERIFY; // Explicit -U :v by user
+
     if (quell_progress < 2) {
-      avrdude_message(MSG_INFO, "%s: verifying %s%s memory against %s:\n",
-            progname, mem->desc, alias_mem_desc, upd->filename);
+      avrdude_message(MSG_INFO, "%s: verifying %s%s memory against %s\n",
+        progname, mem->desc, alias_mem_desc, inname(upd->filename));
 
-      avrdude_message(MSG_NOTICE2, "%s: load data %s%s data from input file %s:\n",
-            progname, mem->desc, alias_mem_desc, upd->filename);
+      if (userverify)
+        avrdude_message(MSG_NOTICE, "%s: load %s%s data from input file %s\n",
+          progname, mem->desc, alias_mem_desc, inname(upd->filename));
     }
 
-    rc = fileio(FIO_READ_FOR_VERIFY, upd->filename, upd->format, p, upd->memtype, -1);
-    if (rc < 0) {
-      avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
-              progname, upd->filename);
-      return -1;
+    // No need to read file when fallen through from DEVICE_WRITE
+    if (userverify) {
+      rc = fileio(FIO_READ_FOR_VERIFY, upd->filename, upd->format, p, upd->memtype, -1);
+
+      if (rc < 0) {
+        avrdude_message(MSG_INFO, "%s: read from file %s failed\n",
+          progname, inname(upd->filename));
+        return LIBAVRDUDE_GENERAL_FAILURE;
+      }
+      size = rc;
+
+      if(memstats(p, upd->memtype, size, &fs) < 0)
+        return LIBAVRDUDE_GENERAL_FAILURE;
+    } else {
+      // Correct size of last read to include potentially cut off, trailing 0xff (flash)
+      size = fs.lastaddr+1;
     }
+
     v = avr_dup_part(p);
-    size = rc;
+
     if (quell_progress < 2) {
-      avrdude_message(MSG_NOTICE2, "%s: input file %s contains %d bytes\n",
-            progname, upd->filename, size);
-      avrdude_message(MSG_NOTICE2, "%s: reading on-chip %s%s data:\n",
-            progname, mem->desc, alias_mem_desc);
+      if (userverify)
+        avrdude_message(MSG_NOTICE, "%s: input file %s contains %d byte%s\n",
+          progname, inname(upd->filename), fs.nbytes, plural(fs.nbytes));
+      avrdude_message(MSG_NOTICE2, "%s: reading on-chip %s%s data ...\n",
+        progname, mem->desc, alias_mem_desc);
     }
 
     report_progress (0,1,"Reading");
     rc = avr_read(pgm, p, upd->memtype, v);
+    report_progress (1,1,NULL);
     if (rc < 0) {
       avrdude_message(MSG_INFO, "%s: failed to read all of %s%s memory, rc=%d\n",
-              progname, mem->desc, alias_mem_desc, rc);
+        progname, mem->desc, alias_mem_desc, rc);
       pgm->err_led(pgm, ON);
       avr_free_part(v);
-      return -1;
+      return LIBAVRDUDE_GENERAL_FAILURE;
     }
-    report_progress (1,1,NULL);
 
-
-
-    if (quell_progress < 2) {
+    if (quell_progress < 2)
       avrdude_message(MSG_NOTICE2, "%s: verifying ...\n", progname);
-    }
+
     rc = avr_verify(p, v, upd->memtype, size);
     if (rc < 0) {
       avrdude_message(MSG_INFO, "%s: verification error; content mismatch\n",
-              progname);
+        progname);
       pgm->err_led(pgm, ON);
       avr_free_part(v);
-      return -1;
+      return LIBAVRDUDE_GENERAL_FAILURE;
     }
 
     if (quell_progress < 2) {
-      avrdude_message(MSG_INFO, "%s: %d bytes of %s%s verified\n",
-              progname, rc, mem->desc, alias_mem_desc);
+      int verified = fs.nbytes+fs.ntrailing;
+      avrdude_message(MSG_INFO, "%s: %d byte%s of %s%s verified\n",
+        progname, verified, plural(verified), mem->desc, alias_mem_desc);
     }
 
     pgm->vfy_led(pgm, OFF);
     avr_free_part(v);
-  }
-  else {
+    break;
+
+  default:
     avrdude_message(MSG_INFO, "%s: invalid update operation (%d) requested\n",
-            progname, upd->op);
-    return -1;
+      progname, upd->op);
+    return LIBAVRDUDE_GENERAL_FAILURE;
   }
 
-  return 0;
+  return LIBAVRDUDE_SUCCESS;
 }
-