diff --git a/src/avr.c b/src/avr.c
index 7f91e9a1..eea0ad8a 100644
--- a/src/avr.c
+++ b/src/avr.c
@@ -1220,7 +1220,55 @@ int avr_put_cycle_count(PROGRAMMER * pgm, AVRPART * p, int cycles)
   }
 
   return 0;
+}
+
+
+// Typical order in which memories show in avrdude.conf, runtime adds unknown ones (if any)
+const char *avr_mem_order[100] = {
+  "eeprom",       "flash",        "application",  "apptable",
+  "boot",         "lfuse",        "hfuse",        "efuse",
+  "fuse",         "fuse0",        "wdtcfg",       "fuse1",
+  "bodcfg",       "fuse2",        "osccfg",       "fuse3",
+  "fuse4",        "tcd0cfg",      "fuse5",        "syscfg0",
+  "fuse6",        "syscfg1",      "fuse7",        "append",
+  "codesize",     "fuse8",        "fuse9",        "bootend",
+  "bootsize",     "fuses",        "lock",         "lockbits",
+  "tempsense",    "signature",    "prodsig",      "sernum",
+  "calibration",  "osccal16",     "osccal20",     "osc16err",
+  "osc20err",     "usersig",      "userrow",      "data",
+};
+
+void avr_add_mem_order(const char *str) {
+  for(size_t i=0; i < sizeof avr_mem_order/sizeof *avr_mem_order; i++) {
+    if(avr_mem_order[i] && !strcmp(avr_mem_order[i], str))
+      return;
+    if(!avr_mem_order[i]) {
+      avr_mem_order[i] = strdup(str);
+      return;
+    }
   }
+  avrdude_message(MSG_INFO,
+    "%s: avr_mem_order[] under-dimensioned in avr.c; increase and recompile\n",
+    progname);
+  exit(1);
+}
+
+int avr_mem_is_known(const char *str) {
+  if(str && *str)
+    for(size_t i=0; i < sizeof avr_mem_order/sizeof *avr_mem_order; i++)
+      if(avr_mem_order[i] && !strcmp(avr_mem_order[i], str))
+        return 1;
+  return 0;
+}
+
+int avr_mem_might_be_known(const char *str) {
+  if(str && *str)
+    for(size_t i=0; i < sizeof avr_mem_order/sizeof *avr_mem_order; i++)
+      if(avr_mem_order[i] && !strncmp(avr_mem_order[i], str, strlen(str)))
+        return 1;
+  return 0;
+}
+
 
 int avr_chip_erase(PROGRAMMER * pgm, AVRPART * p)
 {
diff --git a/src/config_gram.y b/src/config_gram.y
index 68718dd1..ce11b924 100644
--- a/src/config_gram.y
+++ b/src/config_gram.y
@@ -1320,6 +1320,7 @@ part_parm :
         mem->desc[AVR_MEMDESCLEN-1] = 0;
         ladd(current_part->mem, mem);
       }
+      avr_add_mem_order($2->value.string);
       current_mem = mem;
       free_token($2);
     }
diff --git a/src/developer_opts.c b/src/developer_opts.c
index f4a55478..b82c1805 100644
--- a/src/developer_opts.c
+++ b/src/developer_opts.c
@@ -280,35 +280,6 @@ static void dev_stack_out(bool tsv, AVRPART *p, const char *name, unsigned char
 }
 
 
-// order in which memories are processed, runtime adds unknown ones (but there shouldn't be any)
-static const char *mem_order[100] = {
-  "eeprom",       "flash",        "application",  "apptable",
-  "boot",         "lfuse",        "hfuse",        "efuse",
-  "fuse",         "fuse0",        "wdtcfg",       "fuse1",
-  "bodcfg",       "fuse2",        "osccfg",       "fuse3",
-  "fuse4",        "tcd0cfg",      "fuse5",        "syscfg0",
-  "fuse6",        "syscfg1",      "fuse7",        "append",
-  "codesize",     "fuse8",        "fuse9",        "bootend",
-  "bootsize",     "fuses",        "lock",         "lockbits",
-  "tempsense",    "signature",    "prodsig",      "sernum",
-  "calibration",  "osccal16",     "osccal20",     "osc16err",
-  "osc20err",     "usersig",      "userrow",      "data",
-};
-
-static void add_mem_order(const char *str) {
-  for(size_t i=0; i < sizeof mem_order/sizeof *mem_order; i++) {
-    if(mem_order[i] && !strcmp(mem_order[i], str))
-      return;
-    if(!mem_order[i]) {
-      mem_order[i] = strdup(str);
-      return;
-    }
-  }
-  dev_info("%s: mem_order[] under-dimensioned in developer_opts.c; increase and recompile\n", progname);
-  exit(1);
-}
-
-
 static int intcmp(int a, int b) {
   return a-b;
 }
@@ -410,8 +381,8 @@ static int avrpart_deep_copy(AVRPARTdeep *d, AVRPART *p) {
 
   // fill in all memories we got in defined order
   di = 0;
-  for(size_t mi=0; mi < sizeof mem_order/sizeof *mem_order && mem_order[mi]; mi++) {
-    m = p->mem? avr_locate_mem(p, mem_order[mi]): NULL;
+  for(size_t mi=0; mi < sizeof avr_mem_order/sizeof *avr_mem_order && avr_mem_order[mi]; mi++) {
+    m = p->mem? avr_locate_mem(p, avr_mem_order[mi]): NULL;
     if(m) {
       if(di >= sizeof d->mems/sizeof *d->mems) {
         avrdude_message(MSG_INFO, "%s: ran out of mems[] space, increase size in AVRMEMdeep of developer_opts.c and recompile\n", progname);
@@ -566,11 +537,11 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
     if(!base || opcodecmp(p->op[i], base->op[i], i))
       dev_part_strct_entry(tsv, ".ptop", p->desc, "part", opcodename(i), opcode2str(p->op[i], i, !tsv));
 
-  for(size_t mi=0; mi < sizeof mem_order/sizeof *mem_order && mem_order[mi]; mi++) {
+  for(size_t mi=0; mi < sizeof avr_mem_order/sizeof *avr_mem_order && avr_mem_order[mi]; mi++) {
     AVRMEM *m, *bm;
 
-    m = p->mem? avr_locate_mem(p, mem_order[mi]): NULL;
-    bm = base && base->mem? avr_locate_mem(base, mem_order[mi]): NULL;
+    m = p->mem? avr_locate_mem(p, avr_mem_order[mi]): NULL;
+    bm = base && base->mem? avr_locate_mem(base, avr_mem_order[mi]): NULL;
 
     if(!m && bm && !tsv)
       dev_info("\n    memory \"%s\" = NULL;\n", bm->desc);
@@ -694,12 +665,12 @@ void dev_output_part_defs(char *partdesc) {
     AVRPART *p = ldata(ln1);
     if(p->mem)
       for(LNODEID lnm=lfirst(p->mem); lnm; lnm=lnext(lnm))
-         add_mem_order(((AVRMEM *) ldata(lnm))->desc);
+        avr_add_mem_order(((AVRMEM *) ldata(lnm))->desc);
 
     // same for aliased memories (though probably not needed)
     if(p->mem_alias)
       for(LNODEID lnm=lfirst(p->mem_alias); lnm; lnm=lnext(lnm))
-         add_mem_order(((AVRMEM_ALIAS *) ldata(lnm))->desc);
+        avr_add_mem_order(((AVRMEM_ALIAS *) ldata(lnm))->desc);
   }
 
   nprinted = dev_nprinted;
diff --git a/src/libavrdude.h b/src/libavrdude.h
index dad47922..a30c1402 100644
--- a/src/libavrdude.h
+++ b/src/libavrdude.h
@@ -783,6 +783,7 @@ void sort_programmers(LISTID programmers);
 typedef void (*FP_UpdateProgress)(int percent, double etime, char *hdr);
 
 extern struct avrpart parts[];
+extern const char *avr_mem_order[100];
 
 extern FP_UpdateProgress update_progress;
 
@@ -818,6 +819,11 @@ int avr_get_cycle_count(PROGRAMMER * pgm, AVRPART * p, int * cycles);
 
 int avr_put_cycle_count(PROGRAMMER * pgm, AVRPART * p, int cycles);
 
+void avr_add_mem_order(const char *str);
+
+int avr_mem_is_known(const char *str);
+int avr_mem_might_be_known(const char *str);
+
 #define disable_trailing_ff_removal() avr_mem_hiaddr(NULL)
 int avr_mem_hiaddr(AVRMEM * mem);
 
@@ -888,6 +894,7 @@ enum updateflags {
   UF_NONE = 0,
   UF_NOWRITE = 1,
   UF_AUTO_ERASE = 2,
+  UF_VERIFY = 4,
 };
 
 
@@ -898,6 +905,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 +928,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..9f72847f 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':
@@ -759,6 +751,7 @@ int main(int argc, char * argv [])
     bitclock = default_bitclock;
   }
 
+
   avrdude_message(MSG_NOTICE, "\n");
 
   // developer option -p <wildcard>/[*codws] prints various aspects of part descriptions and exits
@@ -926,6 +919,12 @@ int main(int argc, char * argv [])
         exit(1);
       }
     }
+
+    if (!avr_mem_might_be_known(upd->memtype)) {
+      avrdude_message(MSG_INFO, "%s: unknown memory type %s\n", progname, upd->memtype);
+      exit(1);
+    }
+    // TODO: check whether filename other than "-" is readable/writable
   }
 
   /*
@@ -1251,7 +1250,7 @@ int main(int argc, char * argv [])
   for (ln=lfirst(updates); ln; ln=lnext(ln)) {
     upd = ldata(ln);
     rc = do_op(pgm, p, upd, uflags);
-    if (rc) {
+    if (rc && rc != LIBAVRDUDE_SOFTFAIL) {
       exitrc = 1;
       break;
     }
diff --git a/src/update.c b/src/update.c
index 1840f81f..025bfb6b 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: skipping -U %s:... as memory not defined for part %s\n",
+      progname, upd->memtype, p->desc);
+    return LIBAVRDUDE_SOFTFAIL;
   }
 
   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;
 }
-