diff --git a/src/main.c b/src/main.c
index ea110da2..f480b315 100644
--- a/src/main.c
+++ b/src/main.c
@@ -135,72 +135,6 @@ static void usage(void)
 }
 
 
-static void update_progress_tty (int percent, double etime, char *hdr)
-{
-  static char hashes[51];
-  static char *header;
-  static int last = 0;
-  int i;
-
-  setvbuf(stderr, (char*)NULL, _IONBF, 0);
-
-  hashes[50] = 0;
-
-  memset (hashes, ' ', 50);
-  for (i=0; i<percent; i+=2) {
-    hashes[i/2] = '#';
-  }
-
-  if (hdr) {
-    avrdude_message(MSG_INFO, "\n");
-    last = 0;
-    header = hdr;
-  }
-
-  if (last == 0) {
-    avrdude_message(MSG_INFO, "\r%s | %s | %d%% %0.2fs",
-            header, hashes, percent, etime);
-  }
-
-  if (percent == 100) {
-    if (!last) avrdude_message(MSG_INFO, "\n\n");
-    last = 1;
-  }
-
-  setvbuf(stderr, (char*)NULL, _IOLBF, 0);
-}
-
-static void update_progress_no_tty (int percent, double etime, char *hdr)
-{
-  static int done = 0;
-  static int last = 0;
-  int cnt = (percent>>1)*2;
-
-  setvbuf(stderr, (char*)NULL, _IONBF, 0);
-
-  if (hdr) {
-    avrdude_message(MSG_INFO, "\n%s | ", hdr);
-    last = 0;
-    done = 0;
-  }
-  else {
-    while ((cnt > last) && (done == 0)) {
-      avrdude_message(MSG_INFO, "#");
-      cnt -=  2;
-    }
-  }
-
-  if ((percent == 100) && (done == 0)) {
-    avrdude_message(MSG_INFO, " | 100%% %0.2fs\n\n", etime);
-    last = 0;
-    done = 1;
-  }
-  else
-    last = (percent>>1)*2;    /* Make last a multiple of 2. */
-
-  setvbuf(stderr, (char*)NULL, _IOLBF, 0);
-}
-
 static void list_programmers_callback(const char *name, const char *desc,
                                       const char *cfgname, int cfglineno,
                                       void *cookie)
@@ -760,18 +694,8 @@ int main(int argc, char * argv [])
   }
 #endif
 
-  if (quell_progress == 0) {
-    if (isatty (STDERR_FILENO))
-      update_progress = update_progress_tty;
-    else {
-      update_progress = update_progress_no_tty;
-      /* disable all buffering of stderr for compatibility with
-         software that captures and redirects output to a GUI
-         i.e. Programmers Notepad */
-      setvbuf( stderr, NULL, _IONBF, 0 );
-      setvbuf( stdout, NULL, _IONBF, 0 );
-    }
-  }
+  if (quell_progress == 0)
+    terminal_setup_update_progress();
 
   /*
    * Print out an identifying string so folks can tell what version
diff --git a/src/term.c b/src/term.c
index dc771273..f02f95c5 100644
--- a/src/term.c
+++ b/src/term.c
@@ -25,7 +25,10 @@
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <stdarg.h>
 #include <limits.h>
+#include <unistd.h>
+#include <errno.h>
 
 #if defined(HAVE_LIBREADLINE)
 #  include <readline/readline.h>
@@ -90,14 +93,17 @@ static int cmd_pgm   (PROGRAMMER * pgm, struct avrpart * p,
 static int cmd_verbose (PROGRAMMER * pgm, struct avrpart * p,
 		      int argc, char *argv[]);
 
+static int cmd_quell (PROGRAMMER * pgm, struct avrpart * p,
+		      int argc, char *argv[]);
+
 struct command cmd[] = {
-  { "dump",  cmd_dump,  "dump memory  : %s <memtype> <addr> <N-Bytes>" },
+  { "dump",  cmd_dump,  "%s <memory> [<addr> <len> | <addr> ... | <addr> | ...]" },
   { "read",  cmd_dump,  "alias for dump" },
-  { "write", cmd_write, "write memory : %s <memtype> <addr> <b1> <b2> ... <bN>" },
+  { "write", cmd_write, "%s <memory> <addr> [<data>[,] {<data>[,]} | <len> <data>[,] {<data>[,]} ...]" },
   { "erase", cmd_erase, "perform a chip erase" },
   { "sig",   cmd_sig,   "display device signature bytes" },
   { "part",  cmd_part,  "display the current part information" },
-  { "send",  cmd_send,  "send a raw command : %s <b1> <b2> <b3> <b4>" },
+  { "send",  cmd_send,  "send a raw command: %s <b1> <b2> <b3> <b4>" },
   { "parms", cmd_parms, "display adjustable parameters (STK500 and Curiosity Nano only)" },
   { "vtarg", cmd_vtarg, "set <V[target]> (STK500 and Curiosity Nano only)" },
   { "varef", cmd_varef, "set <V[aref]> (STK500 only)" },
@@ -106,12 +112,13 @@ struct command cmd[] = {
   { "spi",   cmd_spi,   "enter direct SPI mode" },
   { "pgm",   cmd_pgm,   "return to programming mode" },
   { "verbose", cmd_verbose, "change verbosity" },
+  { "quell", cmd_quell, "set quell level for progress bars" },
   { "help",  cmd_help,  "help" },
   { "?",     cmd_help,  "help" },
   { "quit",  cmd_quit,  "quit" }
 };
 
-#define NCMDS (sizeof(cmd)/sizeof(struct command))
+#define NCMDS ((int)(sizeof(cmd)/sizeof(struct command)))
 
 
 
@@ -119,19 +126,22 @@ static int spi_mode = 0;
 
 static int nexttok(char * buf, char ** tok, char ** next)
 {
-  char * q, * n;
+  unsigned char *q, *n;
 
-  q = buf;
-  while (isspace((int)*q))
+  q = (unsigned char *) buf;
+  while (isspace(*q))
     q++;
 
   /* isolate first token */
   n = q;
   uint8_t quotes = 0;
-  while (*n && (!isspace((int)*n) || quotes)) {
-    if (*n == '\"')
+  while (*n && (!isspace(*n) || quotes)) {
+    // poor man's quote and escape processing
+    if (*n == '"' || *n == '\'')
       quotes++;
-    else if (isspace((int)*n) && *(n-1) == '\"')
+    else if(*n == '\\' && n[1])
+      n++;
+    else if (isspace(*n) && (n > q+1) && (n[-1] == '"' || n[-1] == '\''))
       break;
     n++;
   }
@@ -142,11 +152,11 @@ static int nexttok(char * buf, char ** tok, char ** next)
   }
 
   /* find start of next token */
-  while (isspace((int)*n))
+  while (isspace(*n))
     n++;
 
-  *tok  = q;
-  *next = n;
+  *tok  = (char *) q;
+  *next = (char *) n;
 
   return 0;
 }
@@ -156,8 +166,8 @@ static int hexdump_line(char * buffer, unsigned char * p, int n, int pad)
 {
   char * hexdata = "0123456789abcdef";
   char * b = buffer;
-  int32_t i = 0;
-  int32_t j = 0;
+  int i = 0;
+  int j = 0;
 
   for (i=0; i<n; i++) {
     if (i && ((i % 8) == 0))
@@ -185,16 +195,15 @@ static int hexdump_line(char * buffer, unsigned char * p, int n, int pad)
 static int chardump_line(char * buffer, unsigned char * p, int n, int pad)
 {
   int i;
-  char b [ 128 ];
+  unsigned char b[128];
 
-  for (int32_t i = 0; i < n; i++) {
-    memcpy(b, p, n);
-    buffer[i] = '.';
-    if (isalpha((int)(b[i])) || isdigit((int)(b[i])) || ispunct((int)(b[i])))
-      buffer[i] = b[i];
-    else if (isspace((int)(b[i])))
-      buffer[i] = ' ';
-  }
+  // sanity check
+  n = n < 1? 1: n > sizeof b? sizeof b: n;
+
+  memcpy(b, p, n);
+  for (int i = 0; i < n; i++)
+    buffer[i] = isascii(b[i]) && isspace(b[i])? ' ':
+      isascii(b[i]) && isgraph(b[i])? b[i]: '.';
 
   for (i = n; i < pad; i++)
     buffer[i] = ' ';
@@ -210,10 +219,10 @@ static int hexdump_buf(FILE * f, int startaddr, unsigned char * buf, int len)
   char dst1[80];
   char dst2[80];
 
-  int32_t addr = startaddr;
+  int addr = startaddr;
   unsigned char * p = (unsigned char *)buf;
   while (len) {
-    int32_t n = 16;
+    int n = 16;
     if (n > len)
       n = len;
     hexdump_line(dst1, p, n, 48);
@@ -231,44 +240,47 @@ static int hexdump_buf(FILE * f, int startaddr, unsigned char * buf, int len)
 static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p,
 		    int argc, char * argv[])
 {
-  if (argc < 2) {
-  avrdude_message(MSG_INFO, "Usage: %s <memtype> [<start addr> <len>]\n"
-                            "       %s <memtype> [<start addr> <...>]\n"
-                            "       %s <memtype> <...>\n"
-                            "       %s <memtype>\n",
-                            argv[0], argv[0], argv[0], argv[0]);
-  return -1;
+  if (argc < 2 || argc > 4) {
+    terminal_message(MSG_INFO,
+      "Usage: %s <memory> <addr> <len>\n"
+      "       %s <memory> <addr> ...\n"
+      "       %s <memory> <addr>\n"
+      "       %s <memory> ...\n"
+      "       %s <memory>\n",
+      argv[0], argv[0], argv[0], argv[0], argv[0]);
+    return -1;
   }
 
   enum { read_size = 256 };
-  static char prevmem[128] = {0x00};
+  static char prevmem[AVR_MEMDESCLEN] = {0x00};
   char * memtype = argv[1];
   AVRMEM * mem = avr_locate_mem(p, memtype);
   if (mem == NULL) {
-    avrdude_message(MSG_INFO, "\"%s\" memory type not defined for part \"%s\"\n",
-            memtype, p->desc);
+    terminal_message(MSG_INFO, "%s (dump): %s memory type not defined for part %s\n",
+      progname, memtype, p->desc);
     return -1;
   }
-  uint32_t maxsize = mem->size;
+  int maxsize = mem->size;
 
   // Get start address if present
   char * end_ptr;
-  static uint32_t addr = 0;
-  if (argc == 4) {
+  static int addr = 0;
+
+  if (argc >= 3 && strcmp(argv[2], "...") != 0) {
     addr = strtoul(argv[2], &end_ptr, 0);
     if (*end_ptr || (end_ptr == argv[2])) {
-      avrdude_message(MSG_INFO, "%s (%s): can't parse address \"%s\"\n",
-              progname, argv[0], argv[2]);
+      terminal_message(MSG_INFO, "%s (dump): can't parse address %s\n",
+        progname, argv[2]);
       return -1;
     } else if (addr >= maxsize) {
-      avrdude_message(MSG_INFO, "%s (%s): address 0x%05lx is out of range for %s memory\n",
-                      progname, argv[0], addr, mem->desc);
+      terminal_message(MSG_INFO, "%s (dump): address 0x%05lx is out of range for %s memory\n",
+        progname, (long) addr, mem->desc);
       return -1;
     }
   }
 
   // Get no. bytes to read if present
-  static int32_t len = read_size;
+  static int len = read_size;
   if (argc >= 3) {
     memset(prevmem, 0x00, sizeof(prevmem));
     if (strcmp(argv[argc - 1], "...") == 0) {
@@ -278,8 +290,8 @@ static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p,
     } else if (argc == 4) {
       len = strtol(argv[3], &end_ptr, 0);
       if (*end_ptr || (end_ptr == argv[3])) {
-        avrdude_message(MSG_INFO, "%s (%s): can't parse length \"%s\"\n",
-              progname, argv[0], argv[3]);
+        terminal_message(MSG_INFO, "%s (dump): can't parse length %s\n",
+          progname, argv[3]);
         return -1;
       }
     } else {
@@ -304,19 +316,19 @@ static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p,
 
   uint8_t * buf = malloc(len);
   if (buf == NULL) {
-    avrdude_message(MSG_INFO, "%s (dump): out of memory\n", progname);
+    terminal_message(MSG_INFO, "%s (dump): out of memory\n", progname);
     return -1;
   }
 
   report_progress(0, 1, "Reading");
-  for (uint32_t i = 0; i < len; i++) {
-    int32_t rc = pgm->read_byte(pgm, p, mem, addr + i, &buf[i]);
+  for (int i = 0; i < len; i++) {
+    int rc = pgm->read_byte(pgm, p, mem, addr + i, &buf[i]);
     if (rc != 0) {
-      avrdude_message(MSG_INFO, "error reading %s address 0x%05lx of part %s\n",
-              mem->desc, addr + i, p->desc);
+      terminal_message(MSG_INFO, "%s (dump): error reading %s address 0x%05lx of part %s\n",
+        progname, mem->desc, (long) addr + i, p->desc);
       if (rc == -1)
-        avrdude_message(MSG_INFO, "read operation not supported on memory type \"%s\"\n",
-                mem->desc);
+        terminal_message(MSG_INFO, "%*sread operation not supported on memory type %s\n",
+          (int) strlen(progname)+9, "", mem->desc);
       return -1;
     }
     report_progress(i, len, NULL);
@@ -334,49 +346,290 @@ static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p,
 }
 
 
+// Convert the next n hex digits of s to a hex number
+static unsigned int tohex(const unsigned char *s, unsigned int n) {
+  int ret, c;
+
+  ret = 0;
+  while(n--) {
+    ret *= 16;
+    c = *s++;
+    ret += c >= '0' && c <= '9'? c - '0': c >= 'a' && c <= 'f'? c - 'a' + 10: c - 'A' + 10;
+  }
+
+  return ret;
+}
+
+/*
+ * Create a utf-8 character sequence from a single unicode character.
+ * Permissive for some invalid unicode sequences but not for those with
+ * high bit set). Returns numbers of characters written (0-6).
+ */
+static int wc_to_utf8str(unsigned int wc, unsigned char *str) {
+  if(!(wc & ~0x7fu)) {
+    *str = (char) wc;
+    return 1;
+  }
+  if(!(wc & ~0x7ffu)) {
+    *str++ = (char) ((wc >> 6) | 0xc0);
+    *str++ = (char) ((wc & 0x3f) | 0x80);
+    return 2;
+  }
+  if(!(wc & ~0xffffu)) {
+    *str++ = (char) ((wc >> 12) | 0xe0);
+    *str++ = (char) (((wc >> 6) & 0x3f) | 0x80);
+    *str++ = (char) ((wc & 0x3f) | 0x80);
+    return 3;
+  }
+  if(!(wc & ~0x1fffffu)) {
+    *str++ = (char) ((wc >> 18) | 0xf0);
+    *str++ = (char) (((wc >> 12) & 0x3f) | 0x80);
+    *str++ = (char) (((wc >> 6) & 0x3f) | 0x80);
+    *str++ = (char) ((wc & 0x3f) | 0x80);
+    return 4;
+  }
+  if(!(wc & ~0x3ffffffu)) {
+    *str++ = (char) ((wc >> 24) | 0xf8);
+    *str++ = (char) (((wc >> 18) & 0x3f) | 0x80);
+    *str++ = (char) (((wc >> 12) & 0x3f) | 0x80);
+    *str++ = (char) (((wc >> 6) & 0x3f) | 0x80);
+    *str++ = (char) ((wc & 0x3f) | 0x80);
+    return 5;
+  }
+  if(!(wc & ~0x7fffffffu)) {
+    *str++ = (char) ((wc >> 30) | 0xfc);
+    *str++ = (char) (((wc >> 24) & 0x3f) | 0x80);
+    *str++ = (char) (((wc >> 18) & 0x3f) | 0x80);
+    *str++ = (char) (((wc >> 12) & 0x3f) | 0x80);
+    *str++ = (char) (((wc >> 6) & 0x3f) | 0x80);
+    *str++ = (char) ((wc & 0x3f) | 0x80);
+    return 6;
+  }
+  return 0;
+}
+
+// Unescape C-style strings, destination d must hold enough space (and can be source s)
+static unsigned char *unescape(unsigned char *d, const unsigned char *s) {
+  unsigned char *ret = d;
+  int n, k;
+
+  while(*s) {
+    switch (*s) {
+    case '\\':
+      switch (*++s) {
+      case 'n':
+        *d = '\n';
+        break;
+      case 't':
+        *d = '\t';
+        break;
+      case 'a':
+        *d = '\a';
+        break;
+      case 'b':
+        *d = '\b';
+        break;
+      case 'e':                 // Non-standard ESC
+        *d = 27;
+        break;
+      case 'f':
+        *d = '\f';
+        break;
+      case 'r':
+        *d = '\r';
+        break;
+      case 'v':
+        *d = '\v';
+        break;
+      case '?':
+        *d = '?';
+        break;
+      case '`':
+        *d = '`';
+        break;
+      case '"':
+        *d = '"';
+        break;
+      case '\'':
+        *d = '\'';
+        break;
+      case '\\':
+        *d = '\\';
+        break;
+      case '0':
+      case '1':
+      case '2':
+      case '3':
+      case '4':
+      case '5':
+      case '6':
+      case '7':                 // 1-3 octal digits
+        n = *s - '0';
+        for(k = 0; k < 2 && s[1] >= '0' && s[1] <= '7'; k++)  // Max 2 more octal characters
+          n *= 8, n += s[1] - '0', s++;
+        *d = n;
+        break;
+      case 'x':                 // Unlimited hex digits
+        for(k = 0; isxdigit(s[k + 1]); k++)
+          continue;
+        if(k > 0) {
+          *d = tohex(s + 1, k);
+          s += k;
+        } else {                // No hex digits after \x? copy \x
+          *d++ = '\\';
+          *d = 'x';
+        }
+        break;
+      case 'u':                 // Exactly 4 hex digits and valid unicode
+        if(isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]) &&
+          (n = wc_to_utf8str(tohex(s+1, 4), d))) {
+          d += n - 1;
+          s += 4;
+        } else {                // Invalid \u sequence? copy \u
+          *d++ = '\\';
+          *d = 'u';
+        }
+        break;
+      case 'U':                 // Exactly 6 hex digits and valid unicode
+        if(isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]) && isxdigit(s[5]) && isxdigit(s[6]) &&
+          (n = wc_to_utf8str(tohex(s+1, 6), d))) {
+          d += n - 1;
+          s += 6;
+        } else {                // Invalid \U sequence? copy \U
+          *d++ = '\\';
+          *d = 'U';
+        }
+        break;
+      default:                  // Keep the escape sequence (C would warn and remove \)
+        *d++ = '\\';
+        *d = *s;
+      }
+      break;
+
+    default:                    // Not an escape sequence: just copy the character
+      *d = *s;
+    }
+    d++;
+    s++;
+  }
+  *d = *s;                      // Terminate
+
+  return ret;
+}
+
+
+static size_t maxstrlen(int argc, char **argv) {
+  size_t max = 0;
+
+  for(int i=0; i<argc; i++)
+    if(strlen(argv[i]) > max)
+      max = strlen(argv[i]);
+
+  return max;
+}
+
+
+// Change data item p of size bytes from big endian to little endian and vice versa
+static void change_endian(void *p, int size) {
+  uint8_t tmp, *w = p;
+
+  for(int i=0; i<size/2; i++)
+    tmp = w[i], w[i] = w[size-i-1], w[size-i-1] = tmp;
+}
+
+
+// Looks like a double mantissa in hex or dec notation
+static int is_mantissa_only(char *p) {
+  char *digs;
+
+  if(*p == '+' || *p == '-')
+    p++;
+
+  if(*p == '0' && (p[1] == 'x' || p[1] == 'X')) {
+    p += 2;
+    digs = "0123456789abcdefABCDEF";
+  } else
+    digs = "0123456789";
+
+  if(!*p)
+    return 0;
+
+  while(*p)
+    if(!strchr(digs, *p++))
+      return 0;
+
+  return 1;
+}
+
+
 static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
 		     int argc, char * argv[])
 {
   if (argc < 4) {
-    avrdude_message(MSG_INFO,
-      "Usage: write <memtype> <start addr> <data1> <data2> <dataN>\n"
-      "       write <memtype> <start addr> <no. bytes> <data1> <dataN> <...>\n\n"
-      "       Add a suffix to manually specify the size for each field:\n"
-      "       HH/hh: 8-bit, H/h/S/s: 16-bit, L/l: 32-bit, LL/ll: 64-bit, F/f: 32-bit float\n");
+    terminal_message(MSG_INFO,
+      "Usage: write <memory> <addr> <data>[,] {<data>[,]} \n"
+      "       write <memory> <addr> <len> <data>[,] {<data>[,]} ...\n"
+      "\n"
+      "Ellipsis ... writes <len> bytes padded by repeating the last <data> item.\n"
+      "\n"
+      "<data> can be hexadecimal, octal or decimal integers, floating point numbers\n"
+      "or C-style strings and characters. For integers, an optional case-insensitive\n"
+      "suffix specifies the data size: HH 8 bit, H/S 16 bit, L 32 bit, LL 64 bit.\n"
+      "Suffix D indicates a 64-bit double, F a 32-bit float, whilst a floating point\n"
+      "number without suffix  defaults to 32-bit float. Hexadecimal floating point\n"
+      "notation is supported. An ambiguous trailing suffix, eg, 0x1.8D, is read as\n"
+      "no-suffix float where D is part of the mantissa; use a zero exponent 0x1.8p0D\n"
+      "to clarify.\n"
+      "\n"
+      "An optional U suffix makes integers unsigned. Ordinary 0x hex integers are\n"
+      "always treated as unsigned. +0x or -0x hex numbers are treated as signed\n"
+      "unless they have a U suffix. Unsigned integers cannot be larger than 2^64-1.\n"
+      "If n is an unsigned integer then -n is also a valid unsigned integer as in C.\n"
+      "Signed integers must fall into the [-2^63, 2^63-1] range or a correspondingly\n"
+      "smaller range when a suffix specifies a smaller type. Out of range signed\n"
+      "numbers trigger a warning.\n"
+      "\n"
+      "Ordinary 0x hex integers with n hex digits (counting leading zeros) use the\n"
+      "smallest size of 1, 2, 4 and 8 bytes that can accommodate any n-digit hex\n"
+      "integer. If an integer suffix specifies a size explicitly the corresponding\n"
+      "number of least significant bytes are written. Otherwise, signed and unsigned\n"
+      "integers alike occupy the smallest of 1, 2, 4, or 8 bytes needed to\n"
+      "accommodate them in their respective representation.\n"
+    );
     return -1;
   }
 
-  int32_t i;
+  int i;
   uint8_t write_mode;       // Operation mode, "standard" or "fill"
   uint8_t start_offset;     // Which argc argument
-  int32_t len;              // Number of bytes to write to memory
+  int len;                  // Number of bytes to write to memory
   char * memtype = argv[1]; // Memory name string
   AVRMEM * mem = avr_locate_mem(p, memtype);
   if (mem == NULL) {
-    avrdude_message(MSG_INFO, "\"%s\" memory type not defined for part \"%s\"\n",
-            memtype, p->desc);
+    terminal_message(MSG_INFO, "%s (write): %s memory type not defined for part %s\n",
+      progname, memtype, p->desc);
     return -1;
   }
-  uint32_t maxsize = mem->size;
+  int maxsize = mem->size;
 
   char * end_ptr;
-  int32_t addr = strtoul(argv[2], &end_ptr, 0);
+  int addr = strtoul(argv[2], &end_ptr, 0);
   if (*end_ptr || (end_ptr == argv[2])) {
-    avrdude_message(MSG_INFO, "%s (write): can't parse address \"%s\"\n",
-            progname, argv[2]);
+    terminal_message(MSG_INFO, "%s (write): can't parse address %s\n",
+      progname, argv[2]);
     return -1;
   }
 
   if (addr > maxsize) {
-    avrdude_message(MSG_INFO, "%s (write): address 0x%05lx is out of range for %s memory\n",
-                    progname, addr, memtype);
+    terminal_message(MSG_INFO, "%s (write): address 0x%05lx is out of range for %s memory\n",
+      progname, (long) addr, memtype);
     return -1;
   }
 
   // Allocate a buffer guaranteed to be large enough
-  uint8_t * buf = calloc(mem->size + 0x10 + strlen(argv[argc - 2]), sizeof(uint8_t));
+  uint8_t * buf = calloc(mem->size + 8 + maxstrlen(argc-3, argv+3)+1, sizeof(uint8_t));
   if (buf == NULL) {
-    avrdude_message(MSG_INFO, "%s (write): out of memory\n", progname);
+    terminal_message(MSG_INFO, "%s (write): out of memory\n", progname);
     return -1;
   }
 
@@ -386,8 +639,8 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
     start_offset = 4;
     len = strtoul(argv[3], &end_ptr, 0);
     if (*end_ptr || (end_ptr == argv[3])) {
-      avrdude_message(MSG_INFO, "%s (write ...): can't parse address \"%s\"\n",
-            progname, argv[3]);
+      terminal_message(MSG_INFO, "%s (write ...): can't parse length %s\n",
+        progname, argv[3]);
       free(buf);
       return -1;
     }
@@ -400,141 +653,186 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
   // Structure related to data that is being written to memory
   struct Data {
     // Data info
-    int32_t bytes_grown;
+    int bytes_grown;
     uint8_t size;
-    bool is_float;
-    bool is_signed;
-    char * str_ptr;
+    char *str_ptr;
     // Data union
     union {
       float f;
+      double d;
       int64_t ll;
+      uint64_t ull;
       uint8_t a[8];
     };
   } data = {
     .bytes_grown = 0,
     .size        = 0,
-    .is_float    = false,
-    .is_signed   = false,
     .str_ptr     = NULL,
-    .ll = 0
+    .ull         = 1
   };
 
-  for (i = start_offset; i < len + start_offset; i++) {
-    data.is_float = false;
-    data.size = 0;
+  if(sizeof(long long) != sizeof(int64_t) || (data.a[0]^data.a[7]) != 1)
+    terminal_message(MSG_INFO, "%s (write): assumption on data types not met? "
+      "Check source and recompile\n", progname);
+  bool is_big_endian = data.a[7];
 
+  for (i = start_offset; i < len + start_offset; i++) {
     // Handle the next argument
     if (i < argc - start_offset + 3) {
+      char *argi = argv[i];
+      size_t arglen = strlen(argi);
+
+      data.size = 0;
+
       // Free string pointer if already allocated
       if(data.str_ptr) {
         free(data.str_ptr);
         data.str_ptr = NULL;
       }
 
-      // Get suffix if present
-      char suffix  = argv[i][strlen(argv[i]) - 1];
-      char lsuffix = argv[i][strlen(argv[i]) - 2];
-      if ((suffix == 'L' && lsuffix == 'L') || (suffix == 'l' && lsuffix == 'l')) {
-        argv[i][strlen(argv[i]) - 2] = '\0';
-        data.size = 8;
-      } else if (suffix == 'L' || suffix == 'l') {
-        argv[i][strlen(argv[i]) - 1] = '\0';
-        data.size = 4;
-      } else if ((suffix == 'F' || suffix == 'f') &&
-          strncmp(argv[i], "0x", 2) != 0 && strncmp(argv[i], "-0x", 3) != 0) {
-        argv[i][strlen(argv[i]) - 1] = '\0';
-        data.size = 4;
-      } else if ((suffix == 'H' && lsuffix == 'H') || (suffix == 'h' && lsuffix == 'h')) {
-        argv[i][strlen(argv[i]) - 2] = '\0';
-        data.size = 1;
-      } else if (suffix == 'H' || suffix == 'h' || suffix == 'S' || suffix == 's') {
-        argv[i][strlen(argv[i]) - 1] = '\0';
-        data.size = 2;
-      } else if (suffix == '\'') {
-        data.size = 1;
-      }
+      // Remove trailing comma to allow cut and paste of lists
+      if(arglen > 0 && argi[arglen-1] == ',')
+        argi[--arglen] = 0;
 
-      // Try integers
-      data.ll = strtoll(argv[i], &end_ptr, 0);
-      if (*end_ptr || (end_ptr == argv[i])) {
-        // Try float
-        data.f = strtof(argv[i], &end_ptr);
-        data.is_float = true;
-        if (*end_ptr || (end_ptr == argv[i])) {
-          data.is_float = false;
-          // Try single character
-          if (argv[i][0] == '\'' && argv[i][2] == '\'') {
-            data.ll = argv[i][1];
-          } else {
-            // Try string that starts and ends with quotes
-            if (argv[i][0] == '\"' && argv[i][strlen(argv[i]) - 1] == '\"') {
-              data.str_ptr = calloc(strlen(argv[i]), sizeof(char));
-              if (data.str_ptr == NULL) {
-                avrdude_message(MSG_INFO, "%s (write str): out of memory\n", progname);
-                return -1;
-              }
-              // Strip start and end quotes
-              strncpy(data.str_ptr, argv[i] + 1, strlen(argv[i]) - 2);
-            } else {
-            avrdude_message(MSG_INFO, "\n%s (write): can't parse data '%s'\n",
-                            progname, argv[i]);
-            free(buf);
-            if(data.str_ptr != NULL)
-              free(data.str_ptr);
-            return -1;
+      // Try integers and assign data size
+      errno = 0;
+      data.ull = strtoull(argi, &end_ptr, 0);
+      if (!(end_ptr == argi || errno)) {
+        unsigned int nu=0, nl=0, nh=0, ns=0, nx=0;
+        char *p;
+
+        // Parse suffixes: ULL, LL, UL, L ... UHH, HH
+        for(p=end_ptr; *p; p++)
+          switch(toupper(*p)) {
+          case 'U': nu++; break;
+          case 'L': nl++; break;
+          case 'H': nh++; break;
+          case 'S': ns++; break;
+          default: nx++;
+          }
+
+        if(nx==0 && nu<2 && nl<3 && nh<3 && ns<2) { // Could be valid integer suffix
+          if(nu==0 || toupper(*end_ptr) == 'U' || toupper(p[-1]) == 'U') { // If U, then must be at start or end
+            bool is_hex = strncasecmp(argi, "0x", 2) == 0; // Ordinary hex: 0x... without explicit +/- sign
+            bool is_signed = !(nu || is_hex);              // Neither explicitly unsigned nor ordinary hex
+            bool is_outside_int64_t = 0;
+            bool is_out_of_range = 0;
+            int nhexdigs = p-argi-2;
+
+            if(is_signed) {   // Is input in range for int64_t?
+              errno = 0; (void) strtoll(argi, NULL, 0);
+              is_outside_int64_t = errno == ERANGE;
             }
+
+            if(nl==0 && ns==0 && nh==0) { // No explicit data size
+              // Ordinary hex numbers have implicit size given by number of hex digits, including leading zeros
+              if(is_hex) {
+                data.size = nhexdigs > 8? 8: nhexdigs > 4? 4: nhexdigs > 2? 2: 1;
+
+              } else if(is_signed) {
+                // Smallest size that fits signed representation
+                data.size =
+                  is_outside_int64_t? 8:
+                  data.ll < INT32_MIN || data.ll > INT32_MAX? 8:
+                  data.ll < INT16_MIN || data.ll > INT16_MAX? 4:
+                  data.ll < INT8_MIN  || data.ll > INT8_MAX? 2: 1;
+
+              } else {
+                // Smallest size that fits unsigned representation
+                data.size =
+                  data.ull > UINT32_MAX? 8:
+                  data.ull > UINT16_MAX? 4:
+                  data.ull > UINT8_MAX? 2: 1;
+              }
+            } else if(nl==0 && nh==2 && ns==0) { // HH
+              data.size = 1;
+              if(is_outside_int64_t || (is_signed && (data.ll < INT8_MIN  || data.ll > INT8_MAX))) {
+                is_out_of_range = 1;
+                data.ll = (int8_t) data.ll;
+              }
+            } else if(nl==0 && ((nh==1 && ns==0) || (nh==0 && ns==1))) { // H or S
+              data.size = 2;
+              if(is_outside_int64_t || (is_signed && (data.ll < INT16_MIN  || data.ll > INT16_MAX))) {
+                is_out_of_range = 1;
+                data.ll = (int16_t) data.ll;
+              }
+            } else if(nl==1 && nh==0 && ns==0) { // L
+              data.size = 4;
+              if(is_outside_int64_t || (is_signed && (data.ll < INT32_MIN  || data.ll > INT32_MAX))) {
+                is_out_of_range = 1;
+                data.ll = (int32_t) data.ll;
+              }
+            } else if(nl==2 && nh==0 && ns==0) { // LL
+              data.size = 8;
+            }
+
+            if(is_outside_int64_t || is_out_of_range)
+              terminal_message(MSG_INFO, "%s (write): %s out of int%d_t range, "
+                "interpreted as %d-byte %lld; consider 'U' suffix\n",
+                progname, argi, data.size*8, data.size, data.ll);
           }
         }
       }
 
-      // Print warning if data size might be ambiguous
-      bool is_hex       = (strncmp(argv[i], "0x",  2) == 0);
-      bool is_neg_hex   = (strncmp(argv[i], "-0x", 3) == 0);
-      bool leading_zero = (strncmp(argv[i], "0x0", 3) == 0);
-      int8_t hex_digits = (strlen(argv[i]) - 2);
-      if(!data.size                                                                      // No pre-defined size
-        && (is_neg_hex                                                                   // Hex with - sign in front
-        || (is_hex && leading_zero && (hex_digits & (hex_digits - 1)))                   // Hex with 3, 5, 6 or 7 digits
-        || (!is_hex && !data.is_float && llabs(data.ll) > 0xFF && strlen(argv[i]) > 2))) // Base10 int greater than 255
-      {
-        avrdude_message(MSG_INFO, "Warning: no size suffix specified for \"%s\". "
-                                  "Writing %d byte(s)\n",
-                                  argv[i],
-                                  llabs(data.ll) > UINT32_MAX ? 8 :
-                                  llabs(data.ll) > UINT16_MAX || data.is_float ? 4 : \
-                                  llabs(data.ll) > UINT8_MAX ? 2 : 1);
-      }
-      // Flag if signed integer and adjust size
-      if (data.ll < 0 && !data.is_float) {
-        data.is_signed = true;
-        if (data.ll < INT32_MIN)
+      if(!data.size) {          // Try double now that input was rejected as integer
+        data.d = strtod(argi, &end_ptr);
+        if (end_ptr != argi && toupper(*end_ptr) == 'D' && end_ptr[1] == 0)
           data.size = 8;
-        else if (data.ll < INT16_MIN)
+      }
+
+      if(!data.size) {          // Try float
+        data.f = strtof(argi, &end_ptr);
+        if (end_ptr != argi && toupper(*end_ptr) == 'F' && end_ptr[1] == 0)
           data.size = 4;
-        else if (data.ll < INT8_MIN)
-          data.size = 2;
-        else
-          data.size = 1;
+        if (end_ptr != argi && *end_ptr == 0) // no suffix defaults to float but ...
+        // ... do not accept valid mantissa-only floats that are integer rejects (eg, 078 or ULL overflows)
+          if (!is_mantissa_only(argi))
+            data.size = 4;
       }
+
+      if(!data.size && arglen > 1) { // Try C-style string or single character
+        if ((*argi == '\'' && argi[arglen-1] == '\'') || (*argi == '\"' && argi[arglen-1] == '\"')) {
+          char *s = calloc(arglen-1, 1);
+          if (s == NULL) {
+            terminal_message(MSG_INFO, "%s (write str): out of memory\n", progname);
+            free(buf);
+            return -1;
+          }
+          // Strip start and end quotes, and unescape C string
+          strncpy(s, argi+1, arglen-2);
+          unescape((unsigned char *) s, (unsigned char *) s);
+          if (*argi == '\'') { // Single C-style character
+            if(*s && s[1])
+              terminal_message(MSG_INFO, "%s (write): only using first character of %s\n",
+                progname, argi);
+            data.ll = *s;
+            data.size = 1;
+            free(s);
+          } else {             // C-style string
+            data.str_ptr = s;
+          }
+        }
+      }
+
+      if(!data.size && !data.str_ptr) {
+        terminal_message(MSG_INFO, "%s (write): can't parse data %s\n",
+          progname, argi);
+        free(buf);
+        return -1;
+      }
+
+      // Assume endianness is the same for double and int, and ensure little endian representation
+      if(is_big_endian && data.size > 1)
+        change_endian(data.a, data.size);
     }
+
     if(data.str_ptr) {
-      for(int16_t j = 0; j < strlen(data.str_ptr); j++)
+      for(size_t j = 0; j < strlen(data.str_ptr); j++)
         buf[i - start_offset + data.bytes_grown++] = (uint8_t)data.str_ptr[j];
-    } else {
-      buf[i - start_offset + data.bytes_grown]     = data.a[0];
-      if (llabs(data.ll) > 0x000000FF || data.size >= 2 || data.is_float)
-        buf[i - start_offset + ++data.bytes_grown] = data.a[1];
-      if (llabs(data.ll) > 0x0000FFFF || data.size >= 4 || data.is_float) {
-        buf[i - start_offset + ++data.bytes_grown] = data.a[2];
-        buf[i - start_offset + ++data.bytes_grown] = data.a[3];
-      }
-      if (llabs(data.ll) > 0xFFFFFFFF || data.size == 8) {
-        buf[i - start_offset + ++data.bytes_grown] = data.a[4];
-        buf[i - start_offset + ++data.bytes_grown] = data.a[5];
-        buf[i - start_offset + ++data.bytes_grown] = data.a[6];
-        buf[i - start_offset + ++data.bytes_grown] = data.a[7];
-      }
+    } else if(data.size > 0) {
+      for(int k=0; k<data.size; k++)
+        buf[i - start_offset + data.bytes_grown + k] = data.a[k];
+      data.bytes_grown += data.size-1;
     }
 
     // Make sure buf does not overflow
@@ -547,9 +845,8 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
     data.bytes_grown = 0;
 
   if ((addr + len + data.bytes_grown) > maxsize) {
-    avrdude_message(MSG_INFO, "%s (write): selected address and # bytes exceed "
-                    "range for %s memory\n",
-                    progname, memtype);
+    terminal_message(MSG_INFO, "%s (write): selected address and # bytes exceed "
+      "range for %s memory\n", progname, memtype);
     free(buf);
     return -1;
   }
@@ -557,31 +854,31 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
   if(data.str_ptr)
     free(data.str_ptr);
 
-  avrdude_message(MSG_NOTICE, "\nInfo: Writing %d bytes starting from address 0x%02x",
-                  len + data.bytes_grown, addr);
+  terminal_message(MSG_NOTICE, "Info: writing %d bytes starting from address 0x%02lx",
+    len + data.bytes_grown, (long) addr);
   if (write_mode == WRITE_MODE_FILL)
-    avrdude_message(MSG_NOTICE, ". Remaining space filled with %s", argv[argc - 2]);
-  avrdude_message(MSG_NOTICE, "\n");
+    terminal_message(MSG_NOTICE, "; remaining space filled with %s", argv[argc - 2]);
+  terminal_message(MSG_NOTICE, "\n");
 
   pgm->err_led(pgm, OFF);
   bool werror = false;
   report_progress(0, 1, "Writing");
   for (i = 0; i < (len + data.bytes_grown); i++) {
-    int32_t rc = avr_write_byte(pgm, p, mem, addr+i, buf[i]);
+    int rc = avr_write_byte(pgm, p, mem, addr+i, buf[i]);
     if (rc) {
-      avrdude_message(MSG_INFO, "%s (write): error writing 0x%02x at 0x%05lx, rc=%d\n",
-              progname, buf[i], addr+i, rc);
+      terminal_message(MSG_INFO, "%s (write): error writing 0x%02x at 0x%05lx, rc=%d\n",
+        progname, buf[i], (long) addr+i, (int) rc);
       if (rc == -1)
-        avrdude_message(MSG_INFO, "write operation not supported on memory type \"%s\"\n",
-                        mem->desc);
+        terminal_message(MSG_INFO, "%*swrite operation not supported on memory type %s\n",
+          (int) strlen(progname)+10, "", mem->desc);
       werror = true;
     }
 
     uint8_t b;
     rc = pgm->read_byte(pgm, p, mem, addr+i, &b);
     if (b != buf[i]) {
-      avrdude_message(MSG_INFO, "%s (write): error writing 0x%02x at 0x%05lx cell=0x%02x\n",
-                      progname, buf[i], addr+i, b);
+      terminal_message(MSG_INFO, "%s (write): error writing 0x%02x at 0x%05lx cell=0x%02x\n",
+        progname, buf[i], (long) addr+i, b);
       werror = true;
     }
 
@@ -608,20 +905,20 @@ static int cmd_send(PROGRAMMER * pgm, struct avrpart * p,
   int len;
 
   if (pgm->cmd == NULL) {
-    avrdude_message(MSG_INFO, "The %s programmer does not support direct ISP commands.\n",
-                    pgm->type);
+    terminal_message(MSG_INFO, "%s (send): the %s programmer does not support direct ISP commands\n",
+      progname, pgm->type);
     return -1;
   }
 
   if (spi_mode && (pgm->spi == NULL)) {
-    avrdude_message(MSG_INFO, "The %s programmer does not support direct SPI transfers.\n",
-                    pgm->type);
+    terminal_message(MSG_INFO, "%s (send): the %s programmer does not support direct SPI transfers\n",
+      progname, pgm->type);
     return -1;
   }
 
 
   if ((argc > 5) || ((argc < 5) && (!spi_mode))) {
-    avrdude_message(MSG_INFO, spi_mode?
+    terminal_message(MSG_INFO, spi_mode?
       "Usage: send <byte1> [<byte2> [<byte3> [<byte4>]]]\n":
       "Usage: send <byte1> <byte2> <byte3> <byte4>\n");
     return -1;
@@ -634,8 +931,8 @@ static int cmd_send(PROGRAMMER * pgm, struct avrpart * p,
   for (i=1; i<argc; i++) {
     cmd[i-1] = strtoul(argv[i], &e, 0);
     if (*e || (e == argv[i])) {
-      avrdude_message(MSG_INFO, "%s (send): can't parse byte \"%s\"\n",
-              progname, argv[i]);
+      terminal_message(MSG_INFO, "%s (send): can't parse byte %s\n",
+        progname, argv[i]);
       return -1;
     }
   }
@@ -650,10 +947,10 @@ static int cmd_send(PROGRAMMER * pgm, struct avrpart * p,
   /*
    * display results
    */
-  avrdude_message(MSG_INFO, "results:");
+  terminal_message(MSG_INFO, "results:");
   for (i=0; i<len; i++)
-    avrdude_message(MSG_INFO, " %02x", res[i]);
-  avrdude_message(MSG_INFO, "\n");
+    terminal_message(MSG_INFO, " %02x", res[i]);
+  terminal_message(MSG_INFO, "\n");
 
   fprintf(stdout, "\n");
 
@@ -664,7 +961,7 @@ static int cmd_send(PROGRAMMER * pgm, struct avrpart * p,
 static int cmd_erase(PROGRAMMER * pgm, struct avrpart * p,
 		     int argc, char * argv[])
 {
-  avrdude_message(MSG_INFO, "%s: erasing chip\n", progname);
+  terminal_message(MSG_INFO, "%s: erasing chip\n", progname);
   pgm->chip_erase(pgm, p);
   return 0;
 }
@@ -690,14 +987,14 @@ static int cmd_sig(PROGRAMMER * pgm, struct avrpart * p,
 
   rc = avr_signature(pgm, p);
   if (rc != 0) {
-    avrdude_message(MSG_INFO, "error reading signature data, rc=%d\n",
-            rc);
+    terminal_message(MSG_INFO, "%s (sig): error reading signature data, rc=%d\n",
+      progname, rc);
   }
 
   m = avr_locate_mem(p, "signature");
   if (m == NULL) {
-    avrdude_message(MSG_INFO, "signature data not defined for device \"%s\"\n",
-                    p->desc);
+    terminal_message(MSG_INFO, "%s (sig): signature data not defined for device %s\n",
+      progname, p->desc);
   }
   else {
     fprintf(stdout, "Device signature = 0x");
@@ -725,9 +1022,8 @@ static int cmd_parms(PROGRAMMER * pgm, struct avrpart * p,
 		     int argc, char * argv[])
 {
   if (pgm->print_parms == NULL) {
-    avrdude_message(MSG_INFO, "%s (parms): the %s programmer does not support "
-                    "adjustable parameters\n",
-                    progname, pgm->type);
+    terminal_message(MSG_INFO, "%s (parms): the %s programmer does not support "
+      "adjustable parameters\n", progname, pgm->type);
     return -1;
   }
   pgm->print_parms(pgm);
@@ -744,23 +1040,23 @@ static int cmd_vtarg(PROGRAMMER * pgm, struct avrpart * p,
   char *endp;
 
   if (argc != 2) {
-    avrdude_message(MSG_INFO, "Usage: vtarg <value>\n");
+    terminal_message(MSG_INFO, "Usage: vtarg <value>\n");
     return -1;
   }
   v = strtod(argv[1], &endp);
   if (endp == argv[1]) {
-    avrdude_message(MSG_INFO, "%s (vtarg): can't parse voltage \"%s\"\n",
-            progname, argv[1]);
+    terminal_message(MSG_INFO, "%s (vtarg): can't parse voltage %s\n",
+      progname, argv[1]);
     return -1;
   }
   if (pgm->set_vtarget == NULL) {
-    avrdude_message(MSG_INFO, "%s (vtarg): the %s programmer cannot set V[target]\n",
-	    progname, pgm->type);
+    terminal_message(MSG_INFO, "%s (vtarg): the %s programmer cannot set V[target]\n",
+      progname, pgm->type);
     return -2;
   }
   if ((rc = pgm->set_vtarget(pgm, v)) != 0) {
-    avrdude_message(MSG_INFO, "%s (vtarg): failed to set V[target] (rc = %d)\n",
-	    progname, rc);
+    terminal_message(MSG_INFO, "%s (vtarg): failed to set V[target] (rc = %d)\n",
+      progname, rc);
     return -3;
   }
   return 0;
@@ -775,7 +1071,7 @@ static int cmd_fosc(PROGRAMMER * pgm, struct avrpart * p,
   char *endp;
 
   if (argc != 2) {
-    avrdude_message(MSG_INFO, "Usage: fosc <value>[M|k] | off\n");
+    terminal_message(MSG_INFO, "Usage: fosc <value>[M|k] | off\n");
     return -1;
   }
   v = strtod(argv[1], &endp);
@@ -783,8 +1079,8 @@ static int cmd_fosc(PROGRAMMER * pgm, struct avrpart * p,
     if (strcmp(argv[1], "off") == 0)
       v = 0.0;
     else {
-      avrdude_message(MSG_INFO, "%s (fosc): can't parse frequency \"%s\"\n",
-	      progname, argv[1]);
+      terminal_message(MSG_INFO, "%s (fosc): can't parse frequency %s\n",
+        progname, argv[1]);
       return -1;
     }
   }
@@ -793,13 +1089,13 @@ static int cmd_fosc(PROGRAMMER * pgm, struct avrpart * p,
   else if (*endp == 'k' || *endp == 'K')
     v *= 1e3;
   if (pgm->set_fosc == NULL) {
-    avrdude_message(MSG_INFO, "%s (fosc): the %s programmer cannot set oscillator frequency\n",
-                    progname, pgm->type);
+    terminal_message(MSG_INFO, "%s (fosc): the %s programmer cannot set oscillator frequency\n",
+      progname, pgm->type);
     return -2;
   }
   if ((rc = pgm->set_fosc(pgm, v)) != 0) {
-    avrdude_message(MSG_INFO, "%s (fosc): failed to set oscillator frequency (rc = %d)\n",
-	    progname, rc);
+    terminal_message(MSG_INFO, "%s (fosc): failed to set oscillator frequency (rc = %d)\n",
+      progname, rc);
     return -3;
   }
   return 0;
@@ -814,24 +1110,24 @@ static int cmd_sck(PROGRAMMER * pgm, struct avrpart * p,
   char *endp;
 
   if (argc != 2) {
-    avrdude_message(MSG_INFO, "Usage: sck <value>\n");
+    terminal_message(MSG_INFO, "Usage: sck <value>\n");
     return -1;
   }
   v = strtod(argv[1], &endp);
   if (endp == argv[1]) {
-    avrdude_message(MSG_INFO, "%s (sck): can't parse period \"%s\"\n",
-	    progname, argv[1]);
+    terminal_message(MSG_INFO, "%s (sck): can't parse period %s\n",
+      progname, argv[1]);
     return -1;
   }
   v *= 1e-6;			/* Convert from microseconds to seconds. */
   if (pgm->set_sck_period == NULL) {
-    avrdude_message(MSG_INFO, "%s (sck): the %s programmer cannot set SCK period\n",
-                    progname, pgm->type);
+    terminal_message(MSG_INFO, "%s (sck): the %s programmer cannot set SCK period\n",
+      progname, pgm->type);
     return -2;
   }
   if ((rc = pgm->set_sck_period(pgm, v)) != 0) {
-    avrdude_message(MSG_INFO, "%s (sck): failed to set SCK period (rc = %d)\n",
-	    progname, rc);
+    terminal_message(MSG_INFO, "%s (sck): failed to set SCK period (rc = %d)\n",
+      progname, rc);
     return -3;
   }
   return 0;
@@ -847,39 +1143,39 @@ static int cmd_varef(PROGRAMMER * pgm, struct avrpart * p,
   char *endp;
 
   if (argc != 2 && argc != 3) {
-    avrdude_message(MSG_INFO, "Usage: varef [channel] <value>\n");
+    terminal_message(MSG_INFO, "Usage: varef [channel] <value>\n");
     return -1;
   }
   if (argc == 2) {
     chan = 0;
     v = strtod(argv[1], &endp);
     if (endp == argv[1]) {
-      avrdude_message(MSG_INFO, "%s (varef): can't parse voltage \"%s\"\n",
-              progname, argv[1]);
+      terminal_message(MSG_INFO, "%s (varef): can't parse voltage %s\n",
+        progname, argv[1]);
       return -1;
     }
   } else {
     chan = strtoul(argv[1], &endp, 10);
     if (endp == argv[1]) {
-      avrdude_message(MSG_INFO, "%s (varef): can't parse channel \"%s\"\n",
-              progname, argv[1]);
+      terminal_message(MSG_INFO, "%s (varef): can't parse channel %s\n",
+        progname, argv[1]);
       return -1;
     }
     v = strtod(argv[2], &endp);
     if (endp == argv[2]) {
-      avrdude_message(MSG_INFO, "%s (varef): can't parse voltage \"%s\"\n",
-              progname, argv[2]);
+      terminal_message(MSG_INFO, "%s (varef): can't parse voltage %s\n",
+        progname, argv[2]);
       return -1;
     }
   }
   if (pgm->set_varef == NULL) {
-    avrdude_message(MSG_INFO, "%s (varef): the %s programmer cannot set V[aref]\n",
-	    progname, pgm->type);
+    terminal_message(MSG_INFO, "%s (varef): the %s programmer cannot set V[aref]\n",
+      progname, pgm->type);
     return -2;
   }
   if ((rc = pgm->set_varef(pgm, chan, v)) != 0) {
-    avrdude_message(MSG_INFO, "%s (varef): failed to set V[aref] (rc = %d)\n",
-	    progname, rc);
+    terminal_message(MSG_INFO, "%s (varef): failed to set V[aref] (rc = %d)\n",
+      progname, rc);
     return -3;
   }
   return 0;
@@ -891,13 +1187,13 @@ static int cmd_help(PROGRAMMER * pgm, struct avrpart * p,
 {
   int i;
 
-  fprintf(stdout, "Valid commands:\n\n");
+  fprintf(stdout, "Valid commands:\n");
   for (i=0; i<NCMDS; i++) {
-    fprintf(stdout, "  %-6s : ", cmd[i].name);
+    fprintf(stdout, "  %-7s : ", cmd[i].name);
     fprintf(stdout, cmd[i].desc, cmd[i].name);
     fprintf(stdout, "\n");
   }
-  fprintf(stdout, 
+  fprintf(stdout,
           "\nUse the 'part' command to display valid memory types for use with the\n"
           "'dump' and 'write' commands.\n\n");
 
@@ -912,7 +1208,8 @@ static int cmd_spi(PROGRAMMER * pgm, struct avrpart * p,
     spi_mode = 1;
     return 0;
   }
-  avrdude_message(MSG_INFO, "`spi' command unavailable for this programmer type\n");
+  terminal_message(MSG_INFO, "%s: spi command unavailable for this programmer type\n",
+    progname);
   return -1;
 }
 
@@ -925,7 +1222,8 @@ static int cmd_pgm(PROGRAMMER * pgm, struct avrpart * p,
     pgm->initialize(pgm, p);
     return 0;
   }
-  avrdude_message(MSG_INFO, "`pgm' command unavailable for this programmer type\n");
+  terminal_message(MSG_INFO, "%s: pgm command unavailable for this programmer type\n",
+    progname);
   return -1;
 }
 
@@ -936,26 +1234,62 @@ static int cmd_verbose(PROGRAMMER * pgm, struct avrpart * p,
   char *endp;
 
   if (argc != 1 && argc != 2) {
-    avrdude_message(MSG_INFO, "Usage: verbose [<value>]\n");
+    terminal_message(MSG_INFO, "Usage: verbose [<value>]\n");
     return -1;
   }
   if (argc == 1) {
-    avrdude_message(MSG_INFO, "Verbosity level: %d\n", verbose);
+    terminal_message(MSG_INFO, "Verbosity level: %d\n", verbose);
     return 0;
   }
   nverb = strtol(argv[1], &endp, 0);
-  if (endp == argv[2]) {
-    avrdude_message(MSG_INFO, "%s: can't parse verbosity level \"%s\"\n",
-	    progname, argv[2]);
+  if (endp == argv[1] || *endp) {
+    terminal_message(MSG_INFO, "%s (verbose): can't parse verbosity level %s\n",
+      progname, argv[1]);
     return -1;
   }
   if (nverb < 0) {
-    avrdude_message(MSG_INFO, "%s: verbosity level must be positive: %d\n",
-	    progname, nverb);
+    terminal_message(MSG_INFO, "%s: verbosity level must not be negative: %d\n",
+      progname, nverb);
     return -1;
   }
   verbose = nverb;
-  avrdude_message(MSG_INFO, "New verbosity level: %d\n", verbose);
+  terminal_message(MSG_INFO, "New verbosity level: %d\n", verbose);
+
+  return 0;
+}
+
+static int cmd_quell(PROGRAMMER * pgm, struct avrpart * p,
+		       int argc, char * argv[])
+{
+  int nquell;
+  char *endp;
+
+  if (argc != 1 && argc != 2) {
+    terminal_message(MSG_INFO, "Usage: quell [<value>]\n");
+    return -1;
+  }
+  if (argc == 1) {
+    terminal_message(MSG_INFO, "Quell level: %d\n", quell_progress);
+    return 0;
+  }
+  nquell = strtol(argv[1], &endp, 0);
+  if (endp == argv[1] || *endp) {
+    terminal_message(MSG_INFO, "%s (quell): can't parse quell level %s\n",
+      progname, argv[1]);
+    return -1;
+  }
+  if (nquell < 0) {
+    terminal_message(MSG_INFO, "%s: quell level must not be negative: %d\n",
+      progname, nquell);
+    return -1;
+  }
+  quell_progress = nquell;
+  terminal_message(MSG_INFO, "New quell level: %d\n", quell_progress);
+
+  if(quell_progress > 0)
+    update_progress = NULL;
+  else
+    terminal_setup_update_progress();
 
   return 0;
 }
@@ -974,9 +1308,9 @@ static int tokenize(char * s, char *** argv)
 
   slen = strlen(s);
 
-  /* 
+  /*
    * initialize allow for 20 arguments, use realloc to grow this if
-   * necessary 
+   * necessary
    */
   nargs   = 20;
   bufsize = slen + 20;
@@ -1032,7 +1366,7 @@ static int tokenize(char * s, char *** argv)
     }
   }
 
-  /* 
+  /*
    * We have parsed all the args, n == argc, bufv contains an array of
    * pointers to each arg, and buf points to one memory block that
    * contains all the args, back to back, seperated by a nul
@@ -1073,8 +1407,8 @@ static int do_cmd(PROGRAMMER * pgm, struct avrpart * p,
     }
     else if (strncasecmp(argv[0], cmd[i].name, len)==0) {
       if (hold != -1) {
-        avrdude_message(MSG_INFO, "%s: command \"%s\" is ambiguous\n",
-                progname, argv[0]);
+        terminal_message(MSG_INFO, "%s (cmd): command %s is ambiguous\n",
+          progname, argv[0]);
         return -1;
       }
       hold = i;
@@ -1084,8 +1418,8 @@ static int do_cmd(PROGRAMMER * pgm, struct avrpart * p,
   if (hold != -1)
     return cmd[hold].func(pgm, p, argc, argv);
 
-  avrdude_message(MSG_INFO, "%s: invalid command \"%s\"\n",
-          progname, argv[0]);
+  terminal_message(MSG_INFO, "%s (cmd): invalid command %s\n",
+    progname, argv[0]);
 
   return -1;
 }
@@ -1117,7 +1451,6 @@ char * terminal_get_input(const char *prompt)
 int terminal_mode(PROGRAMMER * pgm, struct avrpart * p)
 {
   char  * cmdbuf;
-  int     i;
   char  * q;
   int     rc;
   int     argc;
@@ -1125,11 +1458,11 @@ int terminal_mode(PROGRAMMER * pgm, struct avrpart * p)
 
   rc = 0;
   while ((cmdbuf = terminal_get_input("avrdude> ")) != NULL) {
-    /* 
+    /*
      * find the start of the command, skipping any white space
      */
     q = cmdbuf;
-    while (*q && isspace((int)*q))
+    while (*q && isspace((unsigned char) *q))
       q++;
 
     /* skip blank lines and comments */
@@ -1143,10 +1476,12 @@ int terminal_mode(PROGRAMMER * pgm, struct avrpart * p)
       return argc;
     }
 
+#if !defined(HAVE_LIBREADLINE) || defined(WIN32) || defined(__APPLE__)
     fprintf(stdout, ">>> ");
-    for (i=0; i<argc; i++)
+    for (int i=0; i<argc; i++)
       fprintf(stdout, "%s ", argv[i]);
     fprintf(stdout, "\n");
+#endif
 
     /* run the command */
     rc = do_cmd(pgm, p, argc, argv);
@@ -1162,3 +1497,97 @@ int terminal_mode(PROGRAMMER * pgm, struct avrpart * p)
 }
 
 
+int terminal_message(const int msglvl, const char *format, ...) {
+  int rc = 0;
+  va_list ap;
+
+  fflush(stdout); fflush(stderr);
+  if (verbose >= msglvl) {
+    va_start(ap, format);
+    rc = vfprintf(stderr, format, ap);
+    va_end(ap);
+  }
+  fflush(stderr);
+
+  return rc;
+}
+
+
+static void update_progress_tty (int percent, double etime, char *hdr)
+{
+  static char hashes[51];
+  static char *header;
+  static int last = 0;
+  int i;
+
+  setvbuf(stderr, (char*)NULL, _IONBF, 0);
+
+  hashes[50] = 0;
+
+  memset (hashes, ' ', 50);
+  for (i=0; i<percent; i+=2) {
+    hashes[i/2] = '#';
+  }
+
+  if (hdr) {
+    avrdude_message(MSG_INFO, "\n");
+    last = 0;
+    header = hdr;
+  }
+
+  if (last == 0) {
+    avrdude_message(MSG_INFO, "\r%s | %s | %d%% %0.2fs",
+            header, hashes, percent, etime);
+  }
+
+  if (percent == 100) {
+    if (!last) avrdude_message(MSG_INFO, "\n\n");
+    last = 1;
+  }
+
+  setvbuf(stderr, (char*)NULL, _IOLBF, 0);
+}
+
+static void update_progress_no_tty (int percent, double etime, char *hdr)
+{
+  static int done = 0;
+  static int last = 0;
+  int cnt = (percent>>1)*2;
+
+  setvbuf(stderr, (char*)NULL, _IONBF, 0);
+
+  if (hdr) {
+    avrdude_message(MSG_INFO, "\n%s | ", hdr);
+    last = 0;
+    done = 0;
+  }
+  else {
+    while ((cnt > last) && (done == 0)) {
+      avrdude_message(MSG_INFO, "#");
+      cnt -=  2;
+    }
+  }
+
+  if ((percent == 100) && (done == 0)) {
+    avrdude_message(MSG_INFO, " | 100%% %0.2fs\n\n", etime);
+    last = 0;
+    done = 1;
+  }
+  else
+    last = (percent>>1)*2;    /* Make last a multiple of 2. */
+
+  setvbuf(stderr, (char*)NULL, _IOLBF, 0);
+}
+
+void terminal_setup_update_progress() {
+  if (isatty (STDERR_FILENO))
+    update_progress = update_progress_tty;
+  else {
+    update_progress = update_progress_no_tty;
+    /* disable all buffering of stderr for compatibility with
+       software that captures and redirects output to a GUI
+       i.e. Programmers Notepad */
+    setvbuf( stderr, NULL, _IONBF, 0 );
+    setvbuf( stdout, NULL, _IONBF, 0 );
+  }
+}
diff --git a/src/term.h b/src/term.h
index f114d4b0..a89927ea 100644
--- a/src/term.h
+++ b/src/term.h
@@ -34,6 +34,8 @@ typedef enum {
 
 int terminal_mode(PROGRAMMER * pgm, struct avrpart * p);
 char * terminal_get_input(const char *prompt);
+void terminal_setup_update_progress();
+int terminal_message(const int msglvl, const char *format, ...);
 
 #ifdef __cplusplus
 }