From 2434c3f7f665e06541aae27695a1c1248e6fb15c Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Thu, 10 Nov 2022 19:38:21 +0000
Subject: [PATCH] Make terminal write's automatic number width less surprising

---
 src/avrdude.1        | 18 +++++++++++-------
 src/doc/avrdude.texi | 19 ++++++++++++-------
 src/term.c           | 32 +++++++++++++++++++-------------
 3 files changed, 42 insertions(+), 27 deletions(-)

diff --git a/src/avrdude.1 b/src/avrdude.1
index a2225a1a..3b9e0ce5 100644
--- a/src/avrdude.1
+++ b/src/avrdude.1
@@ -866,15 +866,19 @@ always treated as unsigned. +0x or -0x hex numbers are treated as signed
 unless they have a U suffix. Unsigned integers cannot be larger than 2^64-1.
 If n is an unsigned integer then -n is also a valid unsigned integer as in C.
 Signed integers must fall into the [-2^63, 2^63-1] range or a correspondingly
-smaller range when a suffix specifies a smaller type. Out of range signed
-numbers trigger a warning.
+smaller range when a suffix specifies a smaller type.
 .Pp
 Ordinary 0x hex integers with n hex digits (counting leading zeros) use the
-smallest size of 1, 2, 4 and 8 bytes that can accommodate any n-digit hex
-integer. If an integer suffix specifies a size explicitly the corresponding
-number of least significant bytes are written. Otherwise, signed and unsigned
-integers alike occupy the smallest of 1, 2, 4, or 8 bytes needed to
-accommodate them in their respective representation.
+smallest size of one, two, four and eight bytes that can accommodate any
+n-digit hex integer. If an integer suffix specifies a size explicitly the
+corresponding number of least significant bytes are written, and a warning
+shown if the number does not fit into the desired representation. Otherwise,
+unsigned integers occupy the smallest of one, two, four or eight bytes
+needed. Signed numbers are allowed to fit into the smallest signed or
+smallest unsigned representation: For example, 255 is stored as one byte as
+255U would fit in one byte, though as a signed number it would not fit into a
+one-byte interval [-128, 127]. The number -1 is stored in one byte whilst -1U
+needs eight bytes as it is the same as 0xFFFFffffFFFFffffU.
 .Pp
 One trailing comma at the end of
 .Ar data
diff --git a/src/doc/avrdude.texi b/src/doc/avrdude.texi
index cb8bd103..4bd90b94 100644
--- a/src/doc/avrdude.texi
+++ b/src/doc/avrdude.texi
@@ -1429,15 +1429,20 @@ numbers are treated as signed unless they have a @code{U} suffix. Unsigned
 integers cannot be larger than 2^64-1. If @var{n} is an unsigned integer then @var{-n}
 is also a valid unsigned integer as in C. Signed integers must fall into
 the [-2^63, 2^63-1] range or a correspondingly smaller range when a suffix
-specifies a smaller type. Out of range signed numbers trigger a warning.
+specifies a smaller type.
 
 Ordinary @code{0x} hex integers with @var{n} hex digits (counting leading
-zeros) use the smallest size of 1, 2, 4 and 8 bytes that can accommodate
-any n-digit hex integer. If an integer suffix specifies a size explicitly
-the corresponding number of least significant bytes are written.
-Otherwise, signed and unsigned integers alike occupy the smallest of 1, 2,
-4, or 8 bytes needed to accommodate them in their respective
-representation.
+zeros) use the smallest size of one, two, four and eight bytes that can
+accommodate any n-digit hex integer. If an integer suffix specifies a size
+explicitly the corresponding number of least significant bytes are
+written, and a warning shown if the number does not fit into the desired
+representation. Otherwise, unsigned integers occupy the smallest of one,
+two, four or eight bytes needed. Signed numbers are allowed to fit into
+the smallest signed or smallest unsigned representation: For example,
+@code{255} is stored as one byte as @code{255U} would fit in one byte,
+though as a signed number it would not fit into a one-byte interval [-128,
+127]. The number @code{-1} is stored in one byte whilst @code{-1U} needs
+eight bytes as it is the same as @code{0xFFFFffffFFFFffffU}.
 
 One trailing comma at the end of data items is ignored to facilitate copy
 and paste of lists.
diff --git a/src/term.c b/src/term.c
index bd4e57b1..943b964f 100644
--- a/src/term.c
+++ b/src/term.c
@@ -185,7 +185,7 @@ static int chardump_line(char *buffer, unsigned char *p, int n, int pad) {
   unsigned char b[128];
 
   // Sanity check
-  n = n < 1? 1: n > sizeof b? sizeof b: n;
+  n = n < 1? 1: n > (int) sizeof b? (int) sizeof b: n;
 
   memcpy(b, p, n);
   for (int i = 0; i < n; i++)
@@ -390,15 +390,19 @@ static int cmd_write(PROGRAMMER *pgm, AVRPART *p, int argc, char *argv[]) {
       "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"
+      "smaller range when a suffix specifies a smaller type.\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"
+      "smallest size of one, two, four and eight bytes that can accommodate any\n"
+      "n-digit hex integer. If an integer suffix specifies a size explicitly the\n"
+      "corresponding number of least significant bytes are written, and a warning\n"
+      "shown if the number does not fit into the desired representation. Otherwise,\n"
+      "unsigned integers occupy the smallest of one, two, four or eight bytes\n"
+      "needed. Signed numbers are allowed to fit into the smallest signed or\n"
+      "smallest unsigned representation: For example, 255 is stored as one byte as\n"
+      "255U would fit in one byte, though as a signed number it would not fit into a\n"
+      "one-byte interval [-128, 127]. The number -1 is stored in one byte whilst -1U\n"
+      "needs eight bytes as it is the same as 0xFFFFffffFFFFffffU.\n"
     );
     return -1;
   }
@@ -530,12 +534,12 @@ static int cmd_write(PROGRAMMER *pgm, AVRPART *p, int argc, char *argv[]) {
                 data.size = nhexdigs > 8? 8: nhexdigs > 4? 4: nhexdigs > 2? 2: 1;
 
               } else if(is_signed) {
-                // Smallest size that fits signed representation
+                // Smallest size that fits signed or unsigned (asymmetric to meet user expectation)
                 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;
+                  data.ll < INT32_MIN || data.ll > (long long) UINT32_MAX? 8:
+                  data.ll < INT16_MIN || data.ll > (long long) UINT16_MAX? 4:
+                  data.ll < INT8_MIN  || data.ll > (long long) UINT8_MAX? 2: 1;
 
               } else {
                 // Smallest size that fits unsigned representation
@@ -564,9 +568,11 @@ static int cmd_write(PROGRAMMER *pgm, AVRPART *p, int argc, char *argv[]) {
               }
             } else if(nl==2 && nh==0 && ns==0) { // LL
               data.size = 8;
+              if(is_outside_int64_t || is_signed)
+                is_out_of_range = 1;
             }
 
-            if(is_outside_int64_t || is_out_of_range)
+            if(is_out_of_range)
               pmsg_error("(write) %s out of int%d_t range, "
                 "interpreted as %d-byte %lld; consider 'U' suffix\n", argi, data.size*8, data.size, (long long int) data.ll);
           }