From 876d75a1a8771bd239dc4cf347643078d33088eb Mon Sep 17 00:00:00 2001
From: Joerg Wunsch <j@uriah.heep.sax.de>
Date: Fri, 8 Sep 2006 21:28:24 +0000
Subject: [PATCH] Implement numerical output formats for decimal, hexadecimal,
 octal, and binary numbers.

Closes bug #16129: more output formats for fuse bits (avrdude
enhancement request)

* fileio.c: Implement fileio_num() and the itoa_simple() helper function.
* fileio.h: Add new file formats to FILEFMT.
* main.c: Parse the new file formats.
* avrdude.1: Document all this.
* doc/avrdude.texi: (Ditto.)


git-svn-id: svn://svn.savannah.nongnu.org/avrdude/trunk/avrdude@654 81a1dc3b-b13d-400b-aceb-764788c761c2
---
 ChangeLog        |  13 +++++
 avrdude.1        |  18 +++++++
 doc/avrdude.texi |  17 +++++++
 fileio.c         | 122 +++++++++++++++++++++++++++++++++++++++++++++++
 fileio.h         |   6 ++-
 main.c           |   4 ++
 6 files changed, 179 insertions(+), 1 deletion(-)

diff --git a/ChangeLog b/ChangeLog
index da8d8cbf..d1115fd0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2006-09-08 Joerg Wunsch <j@uriah.heep.sax.de>
+
+	Implement numerical output formats for decimal, hexadecimal,
+	octal, and binary numbers.
+	Closes bug #16129: more output formats for fuse bits
+	(avrdude enhancement request)
+	* fileio.c: Implement fileio_num() and the itoa_simple()
+	helper function.
+	* fileio.h: Add new file formats to FILEFMT.
+	* main.c: Parse the new file formats.
+	* avrdude.1: Document all this.
+	* doc/avrdude.texi: (Ditto.)
+
 2006-09-08 Joerg Wunsch <j@uriah.heep.sax.de>
 
 	* fileio.c: CPP statements start in column #1.
diff --git a/avrdude.1 b/avrdude.1
index 26fb9493..c652be0a 100644
--- a/avrdude.1
+++ b/avrdude.1
@@ -505,6 +505,24 @@ having to create a single-byte file or enter terminal mode.
 auto detect; valid for input only, and only if the input is not
 provided at
 .Em stdin .
+.It Ar d
+decimal; this and the following formats are only valid on output.
+They generate one line of output for the respective memory section,
+forming a comma-separated list of the values.
+This can be particularly useful for subsequent processing, like for
+fuse bit settings.
+.It Ar h
+hexadecimal; each value will get the string
+.Em 0x
+prepended.
+.It Ar o
+octal; each value will get a
+.Em 0
+prepended unless it is less than 8 in which case it gets no prefix.
+.It Ar b
+binary; each value will get the string
+.Em 0b
+prepended.
 .El
 .Pp
 The default is to use auto detection for input files, and raw binary
diff --git a/doc/avrdude.texi b/doc/avrdude.texi
index 192c5ce3..78939a5c 100644
--- a/doc/avrdude.texi
+++ b/doc/avrdude.texi
@@ -749,6 +749,23 @@ treated as decimal.
 auto detect; valid for input only, and only if the input is not provided
 at stdin.
 
+@itemx d
+decimal; this and the following formats are only valid on output.
+They generate one line of output for the respective memory section,
+forming a comma-separated list of the values.
+This can be particularly useful for subsequent processing, like for
+fuse bit settings.
+
+@itemx h
+hexadecimal; each value will get the string @emph{0x} prepended.
+
+@itemx o
+octal; each value will get a @emph{0}
+prepended unless it is less than 8 in which case it gets no prefix.
+
+@itemx b
+binary; each value will get the string @emph{0b} prepended.
+
 @end table
 
 The default is to use auto detection for input files, and raw binary
diff --git a/fileio.c b/fileio.c
index 0e3b18e1..2f14b6d3 100644
--- a/fileio.c
+++ b/fileio.c
@@ -77,6 +77,10 @@ int fileio_ihex(struct fioparms * fio,
 int fileio_srec(struct fioparms * fio,
                   char * filename, FILE * f, unsigned char * buf, int size);
 
+int fileio_num(struct fioparms * fio,
+		char * filename, FILE * f, unsigned char * buf, int size,
+		FILEFMT fmt);
+
 int fmt_autodetect(char * fname);
 
 
@@ -658,6 +662,45 @@ int srec2b(char * infile, FILE * inf,
   return maxaddr;
 }
 
+/*
+ * Simple itoa() implementation.  Caller needs to allocate enough
+ * space in buf.  Only positive integers are handled.
+ */
+static char *itoa_simple(int n, char *buf, int base)
+{
+  div_t q;
+  char c, *cp, *cp2;
+
+  cp = buf;
+  /*
+   * Divide by base until the number disappeared, but ensure at least
+   * one digit will be emitted.
+   */
+  do {
+    q = div(n, base);
+    n = q.quot;
+    if (q.rem >= 10)
+      c = q.rem - 10 + 'a';
+    else
+      c = q.rem + '0';
+    *cp++ = c;
+  } while (q.quot != 0);
+
+  /* Terminate the string. */
+  *cp-- = '\0';
+
+  /* Now revert the result string. */
+  cp2 = buf;
+  while (cp > cp2) {
+    c = *cp;
+    *cp-- = *cp2;
+    *cp2++ = c;
+  }
+
+  return buf;
+}
+
+
 
 int fileio_rbin(struct fioparms * fio,
                   char * filename, FILE * f, unsigned char * buf, int size)
@@ -793,6 +836,78 @@ int fileio_srec(struct fioparms * fio,
 }
 
 
+int fileio_num(struct fioparms * fio,
+	       char * filename, FILE * f, unsigned char * buf, int size,
+	       FILEFMT fmt)
+{
+  const char *prefix;
+  char cbuf[20];
+  int base, i, num;
+
+  switch (fmt) {
+    case FMT_HEX:
+      prefix = "0x";
+      base = 16;
+      break;
+
+    default:
+    case FMT_DEC:
+      prefix = "";
+      base = 10;
+      break;
+
+    case FMT_OCT:
+      prefix = "0";
+      base = 8;
+      break;
+
+    case FMT_BIN:
+      prefix = "0b";
+      base = 2;
+      break;
+
+  }
+
+  switch (fio->op) {
+    case FIO_WRITE:
+      break;
+    default:
+      fprintf(stderr, "%s: fileio: invalid operation=%d\n",
+              progname, fio->op);
+      return -1;
+  }
+
+  for (i = 0; i < size; i++) {
+    if (i > 0) {
+      if (putc(',', f) == EOF)
+	goto writeerr;
+    }
+    num = (unsigned int)buf[i];
+    /*
+     * For a base of 8 and a value < 8 to convert, don't write the
+     * prefix.  The conversion will be indistinguishable from a
+     * decimal one then.
+     */
+    if (prefix[0] != '\0' && !(base == 8 && num < 8)) {
+      if (fputs(prefix, f) == EOF)
+	goto writeerr;
+    }
+    itoa_simple(num, cbuf, base);
+    if (fputs(cbuf, f) == EOF)
+      goto writeerr;
+  }
+  if (putc('\n', f) == EOF)
+    goto writeerr;
+
+  return 0;
+
+ writeerr:
+  fprintf(stderr, "%s: error writing to %s: %s\n",
+	  progname, filename, strerror(errno));
+  return -1;
+}
+
+
 int fileio_setparms(int op, struct fioparms * fp)
 {
   fp->op = op;
@@ -1005,6 +1120,13 @@ int fileio(int op, char * filename, FILEFMT format,
       rc = fileio_imm(&fio, fname, f, buf, size);
       break;
 
+    case FMT_HEX:
+    case FMT_DEC:
+    case FMT_OCT:
+    case FMT_BIN:
+      rc = fileio_num(&fio, fname, f, buf, size, format);
+      break;
+
     default:
       fprintf(stderr, "%s: invalid %s file format: %d\n",
               progname, fio.iodesc, format);
diff --git a/fileio.h b/fileio.h
index 63082c25..08efc492 100644
--- a/fileio.h
+++ b/fileio.h
@@ -27,7 +27,11 @@ typedef enum {
   FMT_SREC,
   FMT_IHEX,
   FMT_RBIN,
-  FMT_IMM
+  FMT_IMM,
+  FMT_HEX,
+  FMT_DEC,
+  FMT_OCT,
+  FMT_BIN
 } FILEFMT;
 
 struct fioparms {
diff --git a/main.c b/main.c
index 15f16aaf..5e00e645 100644
--- a/main.c
+++ b/main.c
@@ -414,6 +414,10 @@ UPDATE * parse_op(char * s)
       case 'i': upd->format = FMT_IHEX; break;
       case 'r': upd->format = FMT_RBIN; break;
       case 'm': upd->format = FMT_IMM; break;
+      case 'b': upd->format = FMT_BIN; break;
+      case 'd': upd->format = FMT_DEC; break;
+      case 'h': upd->format = FMT_HEX; break;
+      case 'o': upd->format = FMT_OCT; break;
       default:
         fprintf(stderr, "%s: invalid file format '%s' in update specifier\n",
                 progname, p);