From 81136688f677473a27ca8aeae875e4282de907e2 Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Sun, 7 Aug 2022 08:53:24 +0100
Subject: [PATCH 01/19] Establish a third option to print out part definitions

Introduced -p <part>/A, which prints what -p <part>/S used to print, ie, all
components of the part structures including the default ones. Now -p <part>/S
prints the expanded part structure without use of parent and without printing
default values. This functionality is new and predominantly needed for
checking specific avrdude.conf entries, eg, avrdude -p*/St | grep pollindex

The option -p <part>/s continues to print a short entry of `avrdude.conf`
using its parent if so defined:

$ avrdude -p m328p/s

part parent "m328"
    desc                = "ATmega328P";
    id                  = "m328p";
    signature           = 0x1e 0x95 0x0f;
;
---
 src/developer_opts.c | 27 ++++++++++++++++-----------
 1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/src/developer_opts.c b/src/developer_opts.c
index b82c1805..f80f5537 100644
--- a/src/developer_opts.c
+++ b/src/developer_opts.c
@@ -601,7 +601,7 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
 
 // -p */[cdosw*]
 void dev_output_part_defs(char *partdesc) {
-  bool cmdok, waits, opspi, descs, strct, cmpst, raw, all, tsv;
+  bool cmdok, waits, opspi, descs, astrc, strct, cmpst, raw, all, tsv;
   char *flags;
   int nprinted;
   AVRPART *nullpart = avr_new_part();
@@ -612,7 +612,7 @@ void dev_output_part_defs(char *partdesc) {
   if(!flags && !strcmp(partdesc, "*")) // treat -p * as if it was -p */*
     flags = "*";
 
-  if(!*flags || !strchr("cdosSrw*t", *flags)) {
+  if(!*flags || !strchr("cdoASsrw*t", *flags)) {
     dev_info("%s: flags for developer option -p <wildcard>/<flags> not recognised\n", progname);
     dev_info(
       "Wildcard examples (these need protecting in the shell through quoting):\n"
@@ -621,14 +621,15 @@ void dev_output_part_defs(char *partdesc) {
       "  *32[0-9] matches ATmega329, ATmega325 and ATmega328\n"
       "      *32? matches ATmega329, ATmega32A, ATmega325 and ATmega328\n"
       "Flags (one or more of the characters below):\n"
-      "       c  check and report errors in address bits of SPI commands\n"
       "       d  description of core part features\n"
-      "       o  opcodes for SPI programming parts and memories\n"
-      "       S  show entries of avrdude.conf parts with all values\n"
-      "       s  show entries of avrdude.conf parts with necessary values\n"
+      "       A  show entries of avrdude.conf parts with all values\n"
+      "       S  show entries of avrdude.conf parts with necessary values\n"
+      "       s  show short entries of avrdude.conf parts using parent\n"
       "       r  show entries of avrdude.conf parts as raw dump\n"
+      "       c  check and report errors in address bits of SPI commands\n"
+      "       o  opcodes for SPI programming parts and memories\n"
       "       w  wd_... constants for ISP parts\n"
-      "       *  all of the above except s\n"
+      "       *  all of the above except s and S\n"
       "       t  use tab separated values as much as possible\n"
       "Examples:\n"
       "  $ avrdude -p ATmega328P/s\n"
@@ -638,7 +639,7 @@ void dev_output_part_defs(char *partdesc) {
       "  -p * is the same as -p */*\n"
       "  This help message is printed using any unrecognised flag, eg, -p/h\n"
       "  Leaving no space after -p can be an OK substitute for quoting in shells\n"
-      "  /s and /S outputs are designed to be used as input in avrdude.conf\n"
+      "  /s, /S and /A outputs are designed to be used as input in avrdude.conf\n"
       "  Sorted /r output should stay invariant when rearranging avrdude.conf\n"
       "  The /c, /o and /w flags are less generic and may be removed sometime\n"
       "  These options are just to help development, so not further documented\n"
@@ -654,8 +655,9 @@ void dev_output_part_defs(char *partdesc) {
   descs = all || !!strchr(flags, 'd');
   opspi = all || !!strchr(flags, 'o');
   waits = all || !!strchr(flags, 'w');
-  strct = all || !!strchr(flags, 'S');
+  astrc = all || !!strchr(flags, 'A');
   raw   = all || !!strchr(flags, 'r');
+  strct = !!strchr(flags, 'S');
   cmpst = !!strchr(flags, 's');
   tsv   = !!strchr(flags, 't');
 
@@ -687,8 +689,11 @@ void dev_output_part_defs(char *partdesc) {
     if(!part_match(partdesc, p->desc) && !part_match(partdesc, p->id))
       continue;
 
-    if(strct || cmpst)
-      dev_part_strct(p, tsv, !cmpst? NULL: p->parent_id? locate_part(part_list, p->parent_id): nullpart);
+    if(astrc || strct || cmpst)
+      dev_part_strct(p, tsv,
+        astrc? NULL:
+        strct? nullpart:
+        p->parent_id? locate_part(part_list, p->parent_id): nullpart);
 
     if(raw)
       dev_part_raw(p);

From 08049a40ea661494f8d5465055b216522d5e3d22 Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Sun, 7 Aug 2022 14:05:54 +0100
Subject: [PATCH 02/19] Implement dev option -c */[ASsrt] skeleton

Also changed usbdev, usbsn, usbvendor and usbproduct components from
PROGRAMMER structure to be cached string pointers rather than fixed-size
arrays. These will be initialised by pgm_new() with a pointer to nul;
---
 src/avrftdi.c        |   2 +-
 src/avrpart.c        |   5 +-
 src/config.h         |   2 -
 src/config_gram.y    |  12 ++---
 src/developer_opts.c | 124 +++++++++++++++++++++++++++++++++++++++++--
 src/developer_opts.h |   1 +
 src/ft245r.c         |   4 +-
 src/libavrdude.h     |  14 ++---
 src/main.c           |   8 ++-
 src/pgm.c            |  12 +++--
 src/ser_avrdoper.c   |   4 +-
 src/usbasp.c         |   8 +--
 12 files changed, 159 insertions(+), 37 deletions(-)

diff --git a/src/avrftdi.c b/src/avrftdi.c
index f0c07a6a..315f23e8 100644
--- a/src/avrftdi.c
+++ b/src/avrftdi.c
@@ -651,7 +651,7 @@ static int avrftdi_pin_setup(PROGRAMMER * pgm)
 static int avrftdi_open(PROGRAMMER * pgm, char *port)
 {
 	int vid, pid, interface, index, err;
-	char * serial, *desc;
+	const char *serial, *desc;
 	
 	avrftdi_t* pdata = to_pdata(pgm);
 
diff --git a/src/avrpart.c b/src/avrpart.c
index 4fc6304f..970dfde8 100644
--- a/src/avrpart.c
+++ b/src/avrpart.c
@@ -672,6 +672,7 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, AVRPART * p,
 AVRPART * avr_new_part(void)
 {
   AVRPART * p;
+  char *nulp = cache_string("");
 
   p = (AVRPART *)malloc(sizeof(AVRPART));
   if (p == NULL) {
@@ -686,8 +687,8 @@ AVRPART * avr_new_part(void)
   p->reset_disposition = RESET_DEDICATED;
   p->retry_pulse = PIN_AVR_SCK;
   p->flags = AVRPART_SERIALOK | AVRPART_PARALLELOK | AVRPART_ENABLEPAGEPROGRAMMING;
-  p->parent_id = NULL;
-  p->config_file = NULL;
+  p->parent_id = nulp;
+  p->config_file = nulp;
   p->lineno = 0;
   memset(p->signature, 0xFF, 3);
   p->ctl_stack_type = CTL_STACK_NONE;
diff --git a/src/config.h b/src/config.h
index a20af733..a7d2563d 100644
--- a/src/config.h
+++ b/src/config.h
@@ -101,8 +101,6 @@ void pyytext(void);
 
 char * dup_string(const char * str);
 
-char * cache_string(const char * file);
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/config_gram.y b/src/config_gram.y
index ce11b924..552832ec 100644
--- a/src/config_gram.y
+++ b/src/config_gram.y
@@ -532,8 +532,7 @@ prog_parm_conntype_id:
 prog_parm_usb:
   K_USBDEV TKN_EQUAL TKN_STRING {
     {
-      strncpy(current_prog->usbdev, $3->value.string, PGM_USBSTRINGLEN);
-      current_prog->usbdev[PGM_USBSTRINGLEN-1] = 0;
+      current_prog->usbdev = cache_string($3->value.string);
       free_token($3);
     }
   } |
@@ -546,22 +545,19 @@ prog_parm_usb:
   K_USBPID TKN_EQUAL usb_pid_list |
   K_USBSN TKN_EQUAL TKN_STRING {
     {
-      strncpy(current_prog->usbsn, $3->value.string, PGM_USBSTRINGLEN);
-      current_prog->usbsn[PGM_USBSTRINGLEN-1] = 0;
+      current_prog->usbsn = cache_string($3->value.string);
       free_token($3);
     }
   } |
   K_USBVENDOR TKN_EQUAL TKN_STRING {
     {
-      strncpy(current_prog->usbvendor, $3->value.string, PGM_USBSTRINGLEN);
-      current_prog->usbvendor[PGM_USBSTRINGLEN-1] = 0;
+      current_prog->usbvendor = cache_string($3->value.string);
       free_token($3);
     }
   } |
   K_USBPRODUCT TKN_EQUAL TKN_STRING {
     {
-      strncpy(current_prog->usbproduct, $3->value.string, PGM_USBSTRINGLEN);
-      current_prog->usbproduct[PGM_USBSTRINGLEN-1] = 0;
+      current_prog->usbproduct = cache_string($3->value.string);
       free_token($3);
     }
   }
diff --git a/src/developer_opts.c b/src/developer_opts.c
index f80f5537..8aea0bb2 100644
--- a/src/developer_opts.c
+++ b/src/developer_opts.c
@@ -436,7 +436,7 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
     dev_info("#------------------------------------------------------------\n");
     dev_info("# %s\n", p->desc);
     dev_info("#------------------------------------------------------------\n");
-    if(p->parent_id)
+    if(p->parent_id && *p->parent_id)
       dev_info("\npart parent \"%s\"\n", p->parent_id);
     else
       dev_info("\npart\n");
@@ -597,9 +597,7 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
 }
 
 
-
-
-// -p */[cdosw*]
+// -p */[dASsrcow*t]
 void dev_output_part_defs(char *partdesc) {
   bool cmdok, waits, opspi, descs, astrc, strct, cmpst, raw, all, tsv;
   char *flags;
@@ -693,7 +691,7 @@ void dev_output_part_defs(char *partdesc) {
       dev_part_strct(p, tsv,
         astrc? NULL:
         strct? nullpart:
-        p->parent_id? locate_part(part_list, p->parent_id): nullpart);
+        p->parent_id && *p->parent_id? locate_part(part_list, p->parent_id): nullpart);
 
     if(raw)
       dev_part_raw(p);
@@ -891,3 +889,119 @@ void dev_output_part_defs(char *partdesc) {
     }
   }
 }
+
+
+static void dev_pgm_raw(PROGRAMMER *pgm) {
+  PROGRAMMER dp;
+
+  memcpy(&dp, pgm, sizeof dp);
+  dev_raw_dump((unsigned char *) &dp, sizeof dp, pgm->desc, "pgm", 0);
+}
+
+
+static void dev_pgm_strct(PROGRAMMER *pgm, bool tsv, PROGRAMMER *base) {
+  if(!tsv) {
+    int firstid = 1;
+
+    dev_info("#------------------------------------------------------------\n");
+    dev_info("# ");
+    for(LNODEID ln=lfirst(pgm->id); ln; ln=lnext(ln)) {
+      if(!firstid)
+        dev_info("/");
+      firstid = 0;
+      dev_info("%s", ldata(ln));
+    }
+    dev_info("\n");
+    dev_info("#------------------------------------------------------------\n");
+    if(pgm->parent_id && *pgm->parent_id)
+      dev_info("\nprogrammer parent \"%s\"\n", pgm->parent_id);
+    else
+      dev_info("\nprogrammer\n");
+  }
+
+  if(!tsv)
+    dev_info(";\n");
+}
+
+
+// -c */[ASsrt]
+void dev_output_pgm_defs(char *pgmid) {
+  bool astrc, strct, cmpst, raw, tsv;
+  char *flags;
+  int nprinted;
+  PROGRAMMER *nullpgm = pgm_new();
+
+  if((flags = strchr(pgmid, '/')))
+    *flags++ = 0;
+
+  if(!flags && !strcmp(pgmid, "*")) // treat -c * as if it was -c */A
+    flags = "A";
+
+  if(!*flags || !strchr("ASsrt", *flags)) {
+    dev_info("%s: flags for developer option -c <wildcard>/<flags> not recognised\n", progname);
+    dev_info(
+      "Wildcard examples (these need protecting in the shell through quoting):\n"
+      "         * all known programmers\n"
+      "   avrftdi just this programmer\n"
+      "  jtag*pdi matches jtag2pdi, jtag3pdi, jtag3updi and jtag2updi\n"
+      "  jtag?pdi matches jtag2pdi and jtag3pdi\n"
+      "Flags (one or more of the characters below):\n"
+      "       A  show entries of avrdude.conf programmers with all values\n"
+      "       S  show entries of avrdude.conf programmers with necessary values\n"
+      "       s  show short entries of avrdude.conf programmers using parent\n"
+      "       r  show entries of avrdude.conf programmers as raw dump\n"
+      "       t  use tab separated values as much as possible\n"
+      "Examples:\n"
+      "  $ avrdude -c usbasp/s\n"
+      "  $ avrdude -c */st | grep baudrate\n"
+      "  $ avrdude -c */r | sort\n"
+      "Notes:\n"
+      "  -c * is the same as -c */A\n"
+      "  This help message is printed using any unrecognised flag, eg, -c/h\n"
+      "  Leaving no space after -c can be an OK substitute for quoting in shells\n"
+      "  /s, /S and /A outputs are designed to be used as input in avrdude.conf\n"
+      "  Sorted /r output should stay invariant when rearranging avrdude.conf\n"
+      "  These options are just to help development, so not further documented\n"
+    );
+    return;
+  }
+
+  // redirect stderr to stdout
+  fflush(stderr); fflush(stdout); dup2(1, 2);
+
+  astrc = !!strchr(flags, 'A');
+  strct = !!strchr(flags, 'S');
+  cmpst = !!strchr(flags, 's');
+  raw   = !!strchr(flags, 'r');
+  tsv   = !!strchr(flags, 't');
+
+  nprinted = dev_nprinted;
+
+  LNODEID ln1, ln2;
+  for(ln1=lfirst(programmers); ln1; ln1=lnext(ln1)) {
+    PROGRAMMER *pgm = ldata(ln1);
+    int matched = 0;
+    for(ln2=lfirst(pgm->id); ln2; ln2=lnext(ln2)) {
+      if(part_match(pgmid, ldata(ln2))) {
+        matched = 1;
+        break;
+      }
+    }
+    if(!matched)
+      continue;
+
+    if(dev_nprinted > nprinted) {
+      dev_info("\n");
+      nprinted = dev_nprinted;
+    }
+
+    if(astrc || strct || cmpst)
+      dev_pgm_strct(pgm, tsv,
+        astrc? NULL:
+        strct? nullpgm:
+        pgm->parent_id && *pgm->parent_id? locate_programmer(programmers, pgm->parent_id): nullpgm);
+
+    if(raw)
+      dev_pgm_raw(pgm);
+  }
+}
diff --git a/src/developer_opts.h b/src/developer_opts.h
index 6c4b3b71..2079c109 100644
--- a/src/developer_opts.h
+++ b/src/developer_opts.h
@@ -20,5 +20,6 @@
 #define developer_opts_h
 
 void dev_output_part_defs(char *partdesc);
+void dev_output_pgm_defs(char *programmer);
 
 #endif
diff --git a/src/ft245r.c b/src/ft245r.c
index 874167ac..04790143 100644
--- a/src/ft245r.c
+++ b/src/ft245r.c
@@ -851,7 +851,7 @@ static int ft245r_open(PROGRAMMER * pgm, char * port) {
       avrdude_message(MSG_NOTICE,
           "%s: ft245r_open(): no device identifier in portname, using default\n",
           progname);
-      pgm->usbsn[0] = 0;
+      pgm->usbsn = cache_string("");
       devnum = 0;
     } else {
       if (strlen(device) == 8 ){ // serial number
@@ -863,7 +863,7 @@ static int ft245r_open(PROGRAMMER * pgm, char * port) {
               device);
         }
         // copy serial number to pgm struct
-        strcpy(pgm->usbsn, device);
+        pgm->usbsn = cache_string(device);
         // and use first device with matching serial (should be unique)
         devnum = 0;
       }
diff --git a/src/libavrdude.h b/src/libavrdude.h
index 23905953..7dcab3c2 100644
--- a/src/libavrdude.h
+++ b/src/libavrdude.h
@@ -217,7 +217,7 @@ typedef struct opcode {
 typedef struct avrpart {
   char          desc[AVR_DESCLEN];  /* long part name */
   char          id[AVR_IDLEN];      /* short part name */
-  char        * parent_id;          /* parent id if set, for -p.../s */
+  const char  * parent_id;          /* parent id if set, for -p.../s */
   char          family_id[AVR_FAMILYIDLEN+1]; /* family id in the SIB (avr8x) */
   int           hvupdi_variant;     /* HV pulse on UPDI pin, no pin or RESET pin */
   int           stk500_devcode;     /* stk500 device code */
@@ -280,7 +280,7 @@ typedef struct avrpart {
 
   LISTID        mem;                /* avr memory definitions */
   LISTID        mem_alias;          /* memory alias definitions */
-  char          *config_file;       /* config file where defined */
+  const char  * config_file;        /* config file where defined */
   int           lineno;             /* config file line number */
 } AVRPART;
 
@@ -640,7 +640,6 @@ extern struct serial_device usbhid_serdev;
 #define PGM_DESCLEN 80
 #define PGM_PORTLEN PATH_MAX
 #define PGM_TYPELEN 32
-#define PGM_USBSTRINGLEN 256
 
 typedef enum {
   EXIT_VCC_UNSPEC,
@@ -672,7 +671,7 @@ typedef struct programmer_t {
   char desc[PGM_DESCLEN];
   char type[PGM_TYPELEN];
   char port[PGM_PORTLEN];
-  char *parent_id;
+  const char *parent_id;
   void (*initpgm)(struct programmer_t * pgm);
   unsigned int pinno[N_PINS];
   struct pindef_t pin[N_PINS];
@@ -685,8 +684,7 @@ typedef struct programmer_t {
   int baudrate;
   int usbvid;
   LISTID usbpid;
-  char usbdev[PGM_USBSTRINGLEN], usbsn[PGM_USBSTRINGLEN];
-  char usbvendor[PGM_USBSTRINGLEN], usbproduct[PGM_USBSTRINGLEN];
+  const char *usbdev, *usbsn, *usbvendor, *usbproduct;
   double bitclock;    /* JTAG ICE clock period in microseconds */
   int ispdelay;    /* ISP clock delay */
   union filedescriptor fd;
@@ -740,7 +738,7 @@ typedef struct programmer_t {
   int  (*parseextparams) (struct programmer_t * pgm, LISTID xparams);
   void (*setup)          (struct programmer_t * pgm);
   void (*teardown)       (struct programmer_t * pgm);
-  char *config_file;          /* config file where defined */
+  const char *config_file;    /* config file where defined */
   int  lineno;                /* config file line number */
   void *cookie;		      /* for private use by the programmer */
   char flag;		      /* for private use of the programmer */
@@ -989,6 +987,8 @@ void cleanup_config(void);
 
 int read_config(const char * file);
 
+char *cache_string(const char *file);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/main.c b/src/main.c
index 9f72847f..83bde3e9 100644
--- a/src/main.c
+++ b/src/main.c
@@ -754,7 +754,7 @@ int main(int argc, char * argv [])
 
   avrdude_message(MSG_NOTICE, "\n");
 
-  // developer option -p <wildcard>/[*codws] prints various aspects of part descriptions and exits
+  // Developer option -p <wildcard>/[dASsrcow*t] prints part description(s) and exits
   if(partdesc && (strcmp(partdesc, "*") == 0 || strchr(partdesc, '/'))) {
     dev_output_part_defs(partdesc);
     exit(1);
@@ -770,6 +770,12 @@ int main(int argc, char * argv [])
     }
   }
 
+  // Developer option -c <wildcard>/[ASsrt] prints programmer description(s) and exits
+  if(programmer && (strcmp(programmer, "*") == 0 || strchr(programmer, '/'))) {
+    dev_output_pgm_defs(programmer);
+    exit(1);
+  }
+
   if (programmer) {
     if (strcmp(programmer, "?") == 0) {
       avrdude_message(MSG_INFO, "\n");
diff --git a/src/pgm.c b/src/pgm.c
index dd38552f..a4e0ed54 100644
--- a/src/pgm.c
+++ b/src/pgm.c
@@ -65,6 +65,7 @@ PROGRAMMER * pgm_new(void)
 {
   int i;
   PROGRAMMER * pgm;
+  char *nulp = cache_string("");
 
   pgm = (PROGRAMMER *)malloc(sizeof(*pgm));
   if (pgm == NULL) {
@@ -79,13 +80,18 @@ PROGRAMMER * pgm_new(void)
   pgm->usbpid = lcreat(NULL, 0);
   pgm->desc[0] = 0;
   pgm->type[0] = 0;
-  pgm->parent_id = NULL;
-  pgm->config_file = NULL;
+  pgm->parent_id = nulp;
+  pgm->config_file = nulp;
   pgm->lineno = 0;
   pgm->baudrate = 0;
   pgm->initpgm = NULL;
   pgm->hvupdi_support = lcreat(NULL, 0);
 
+  pgm->usbdev = nulp;
+  pgm->usbsn = nulp;
+  pgm->usbvendor = nulp;
+  pgm->usbproduct = nulp;
+
   for (i=0; i<N_PINS; i++) {
     pgm->pinno[i] = 0;
     pin_clear_all(&(pgm->pin[i]));
@@ -146,7 +152,7 @@ void pgm_free(PROGRAMMER * const p)
   ldestroy_cb(p->usbpid, free);
   p->id = NULL;
   p->usbpid = NULL;
-  /* do not free p->parent_id nor p->config_file */
+  /* do not free p->parent_id, p->config_file, p->usbdev, p->usbsn, p->usbvendor or p-> usbproduct */
   /* p->cookie is freed by pgm_teardown */
   free(p);
 }
diff --git a/src/ser_avrdoper.c b/src/ser_avrdoper.c
index a8414403..e459f921 100644
--- a/src/ser_avrdoper.c
+++ b/src/ser_avrdoper.c
@@ -65,8 +65,8 @@ static int              avrdoperRxPosition = 0; /* amount of bytes already consu
 /* ------------------------------------------------------------------------ */
 /* ------------------------------------------------------------------------ */
 
-static int usbOpenDevice(union filedescriptor *fdp, int vendor, char *vendorName,
-			 int product, char *productName, int doReportIDs)
+static int usbOpenDevice(union filedescriptor *fdp, int vendor, const char *vendorName,
+			 int product, const char *productName, int doReportIDs)
 {
     hid_device *dev;
 
diff --git a/src/usbasp.c b/src/usbasp.c
index 250c8a22..2b4c4831 100644
--- a/src/usbasp.c
+++ b/src/usbasp.c
@@ -160,9 +160,9 @@ static int usbasp_transmit(PROGRAMMER * pgm, unsigned char receive,
 			   unsigned char functionid, const unsigned char *send,
 			   unsigned char *buffer, int buffersize);
 #ifdef USE_LIBUSB_1_0
-static int usbOpenDevice(libusb_device_handle **device, int vendor, char *vendorName, int product, char *productName);
+static int usbOpenDevice(libusb_device_handle **device, int vendor, const char *vendorName, int product, const char *productName);
 #else
-static int usbOpenDevice(usb_dev_handle **device, int vendor, char *vendorName, int product, char *productName);
+static int usbOpenDevice(usb_dev_handle **device, int vendor, const char *vendorName, int product, const char *productName);
 #endif
 // interface - prog.
 static int usbasp_open(PROGRAMMER * pgm, char * port);
@@ -337,7 +337,7 @@ static int usbasp_transmit(PROGRAMMER * pgm,
  */
 #ifdef USE_LIBUSB_1_0
 static int usbOpenDevice(libusb_device_handle **device, int vendor,
-			 char *vendorName, int product, char *productName)
+			 const char *vendorName, int product, const char *productName)
 {
     libusb_device_handle *handle = NULL;
     int                  errorCode = USB_ERROR_NOTFOUND;
@@ -412,7 +412,7 @@ static int usbOpenDevice(libusb_device_handle **device, int vendor,
 }
 #else
 static int usbOpenDevice(usb_dev_handle **device, int vendor,
-			 char *vendorName, int product, char *productName)
+			 const char *vendorName, int product, const char *productName)
 {
 struct usb_bus       *bus;
 struct usb_device    *dev;

From 075dee1dd33da28be5472fad6d478421e970d1ca Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Sun, 7 Aug 2022 17:52:17 +0100
Subject: [PATCH 03/19] Implement -c */r (raw dump of programmer structure)

---
 src/developer_opts.c | 134 ++++++++++++++++++++++++++++---------------
 src/libavrdude.h     |  22 ++++---
 2 files changed, 101 insertions(+), 55 deletions(-)

diff --git a/src/developer_opts.c b/src/developer_opts.c
index 8aea0bb2..8d8c183c 100644
--- a/src/developer_opts.c
+++ b/src/developer_opts.c
@@ -34,6 +34,7 @@
 #include <stdlib.h>
 #include <whereami.h>
 #include <stdarg.h>
+#include <stddef.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
@@ -51,7 +52,7 @@
 #include "developer_opts.h"
 #include "developer_opts_private.h"
 
-// return 0 if op code would encode (essentially) the same SPI command
+// Return 0 if op code would encode (essentially) the same SPI command
 static int opcodecmp(OPCODE *op1, OPCODE *op2, int opnum) {
   char *opstr1, *opstr2, *p;
   int cmp;
@@ -68,7 +69,7 @@ static int opcodecmp(OPCODE *op1, OPCODE *op2, int opnum) {
     exit(1);
   }
 
-  // don't care x and 0 are functionally equivalent
+  // Don't care x and 0 are functionally equivalent
   for(p=opstr1; *p; p++)
     if(*p == 'x')
       *p = '0';
@@ -108,7 +109,7 @@ static void printallopcodes(AVRPART *p, const char *d, OPCODE **opa) {
 
 
 
-// mnemonic characterisation of flags
+// Mnemonic characterisation of flags
 static char *parttype(AVRPART *p) {
   static char type[1024];
 
@@ -144,7 +145,7 @@ static char *parttype(AVRPART *p) {
 }
 
 
-// check whether address bits are where they should be in ISP commands
+// Check whether address bits are where they should be in ISP commands
 static void checkaddr(int memsize, int pagesize, int opnum, OPCODE *op, AVRPART *p, AVRMEM *m) {
   int i, lo, hi;
   const char *opstr = opcodename(opnum);
@@ -152,7 +153,7 @@ static void checkaddr(int memsize, int pagesize, int opnum, OPCODE *op, AVRPART
   lo = intlog2(pagesize);
   hi = intlog2(memsize-1);
 
-  // address bits should be between positions lo and hi (and fall in line), outside should be 0 or don't care
+  // Address bits should be between positions lo and hi (and fall in line), outside should be 0 or don't care
   for(i=0; i<16; i++) {         // ISP programming only deals with 16-bit addresses (words for flash, bytes for eeprom)
     if(i < lo || i > hi) {
       if(op->bit[i+8].type != AVR_CMDBIT_IGNORE && !(op->bit[i+8].type == AVR_CMDBIT_VALUE && op->bit[i+8].value == 0)) {
@@ -168,7 +169,7 @@ static void checkaddr(int memsize, int pagesize, int opnum, OPCODE *op, AVRPART
         dev_info(".cmderr\t%s\t%s-%s\tbit %d inconsistent: a%d specified as a%d\n", p->desc, m->desc, opstr, i+8, i, op->bit[i+8].bitno);
     }
   }
-  for(i=0; i<32; i++)           // command bits 8..23 should not contain address bits
+  for(i=0; i<32; i++)           // Command bits 8..23 should not contain address bits
     if((i<8 || i>23) && op->bit[i].type == AVR_CMDBIT_ADDRESS)
       dev_info(".cmderr\t%s\t%s-%s\tbit %d contains a%d which it shouldn't\n", p->desc, m->desc, opstr, i, op->bit[i].bitno);
 }
@@ -180,7 +181,7 @@ static char *dev_sprintf(const char *fmt, ...) {
   char *p = NULL;
   va_list ap;
 
-  // compute size
+  // Compute size
   va_start(ap, fmt);
   size = vsnprintf(p, size, fmt, ap);
   va_end(ap);
@@ -188,7 +189,7 @@ static char *dev_sprintf(const char *fmt, ...) {
   if(size < 0)
     return NULL;
 
-  size++;                       // for temrinating '\0'
+  size++;                       // For temrinating '\0'
   if(!(p = malloc(size)))
    return NULL;
 
@@ -228,7 +229,7 @@ static int dev_part_strct_entry(bool tsv, char *col0, char *col1, char *col2, co
   const char *n = name? name: "name_error";
   const char *c = cont? cont: "cont_error";
 
-  if(tsv) {                     // tab separated values
+  if(tsv) {                     // Tab separated values
     if(col0) {
       dev_info("%s\t", col0);
       if(col1) {
@@ -239,7 +240,7 @@ static int dev_part_strct_entry(bool tsv, char *col0, char *col1, char *col2, co
       }
     }
     dev_info("%s\t%s\n", n, c);
-  } else {                      // grammar conform
+  } else {                      // Grammar conform
     int indent = col2 && strcmp(col2, "part");
 
     printf("%*s%-*s = %s;\n", indent? 8: 4, "", indent? 15: 19, n, c);
@@ -285,7 +286,7 @@ static int intcmp(int a, int b) {
 }
 
 
-// deep copies for comparison and raw output
+// Deep copies for comparison and raw output
 
 typedef struct {
   AVRMEM base;
@@ -297,19 +298,19 @@ static int avrmem_deep_copy(AVRMEMdeep *d, AVRMEM *m) {
 
   d->base = *m;
 
-  // zap all bytes beyond terminating nul of desc array
+  // Zap all bytes beyond terminating nul of desc array
   len = strlen(m->desc)+1;
   if(len < sizeof m->desc)
     memset(d->base.desc + len, 0, sizeof m->desc - len);
 
-  // zap address values
+  // Zap address values
   d->base.buf = NULL;
   d->base.tags = NULL;
   for(int i=0; i<AVR_OP_MAX; i++)
     d->base.op[i] = NULL;
 
 
-  // copy over the SPI operations themselves
+  // Copy over the SPI operations themselves
   memset(d->base.op, 0, sizeof d->base.op);
   memset(d->ops, 0, sizeof d->ops);
   for(size_t i=0; i<sizeof d->ops/sizeof *d->ops; i++)
@@ -353,7 +354,7 @@ static int avrpart_deep_copy(AVRPARTdeep *d, AVRPART *p) {
   d->base.config_file = NULL;
   d->base.lineno = 0;
 
-  // zap all bytes beyond terminating nul of desc, id and family_id array
+  // Zap all bytes beyond terminating nul of desc, id and family_id array
   len = strlen(p->desc);
   if(len < sizeof p->desc)
     memset(d->base.desc + len, 0, sizeof p->desc - len);
@@ -366,20 +367,20 @@ static int avrpart_deep_copy(AVRPARTdeep *d, AVRPART *p) {
   if(len < sizeof p->id)
     memset(d->base.id + len, 0, sizeof p->id - len);
 
-  // zap address values
+  // Zap address values
   d->base.mem = NULL;
   d->base.mem_alias = NULL;
   for(int i=0; i<AVR_OP_MAX; i++)
     d->base.op[i] = NULL;
 
-  // copy over the SPI operations
+  // Copy over the SPI operations
   memset(d->base.op, 0, sizeof d->base.op);
   memset(d->ops, 0, sizeof d->ops);
   for(int i=0; i<AVR_OP_MAX; i++)
     if(p->op[i])
       d->ops[i] = *p->op[i];
 
-  // fill in all memories we got in defined order
+  // Fill in all memories we got in defined order
   di = 0;
   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;
@@ -396,23 +397,28 @@ static int avrpart_deep_copy(AVRPARTdeep *d, AVRPART *p) {
   return di;
 }
 
+
 static char txtchar(unsigned char in) {
   in &= 0x7f;
   return in == ' '? '_': in > ' ' && in < 0x7f? in: '.';
 }
 
+static void dev_raw_dump(const char *p, int nbytes, const char *name, const char *sub, int idx) {
+  int n = (nbytes + 31)/32;
 
-static void dev_raw_dump(unsigned char *p, int nbytes, const char *name, const char *sub, int idx) {
-  unsigned char *end = p+nbytes;
-  int n = ((end - p) + 15)/16;
-
-  for(int i=0; i<n; i++, p += 16) {
-    dev_info("%s\t%s\t%02x%04x: ", name, sub, idx, i*16);
-    for(int j=0; j<16; j++)
-      dev_info("%02x", p+i*16+j<end? p[i*16+j]: 0);
+  for(int i=0; i<n; i++, p += 32, nbytes -= 32) {
+    dev_info("%s\t%s\t%02x%03x0: ", name, sub, idx, 2*i);
+    for(int j=0; j<32; j++) {
+      if(j && j%8 == 0)
+        dev_info(" ");
+      if(j < nbytes)
+        dev_info("%02x", (unsigned char) p[j]);
+      else
+        dev_info("  ");
+    }
     dev_info(" ");
-    for(int j=0; j<16; j++)
-      dev_info("%c", txtchar(p+i*16+j<end? p[i*16+j]: 0));
+    for(int j=0; j<32 && j < nbytes; j++)
+      dev_info("%c", txtchar((unsigned char) p[j]));
     dev_info("\n");
   }
 }
@@ -422,11 +428,11 @@ static void dev_part_raw(AVRPART *part) {
   AVRPARTdeep dp;
   int di = avrpart_deep_copy(&dp, part);
 
-  dev_raw_dump((unsigned char *) &dp.base, sizeof dp.base, part->desc, "part", 0);
-  dev_raw_dump((unsigned char *) &dp.ops, sizeof dp.ops, part->desc, "ops", 1);
+  dev_raw_dump((char *) &dp.base, sizeof dp.base, part->desc, "part", 0);
+  dev_raw_dump((char *) &dp.ops, sizeof dp.ops, part->desc, "ops", 1);
 
   for(int i=0; i<di; i++)
-    dev_raw_dump((unsigned char *) (dp.mems+i), sizeof dp.mems[i], part->desc, dp.mems[i].base.desc, i+2);
+    dev_raw_dump((char *) (dp.mems+i), sizeof dp.mems[i], part->desc, dp.mems[i].base.desc, i+2);
 }
 
 
@@ -527,7 +533,7 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
   _if_partout(intcmp, "0x%02x", idr);
   _if_partout(intcmp, "0x%02x", rampz);
   _if_partout(intcmp, "0x%02x", spmcr);
-  _if_partout(intcmp, "0x%02x", eecr);  // why is eecr an unsigned short?
+  _if_partout(intcmp, "0x%02x", eecr);  // Why is eecr an unsigned short?
   _if_partout(intcmp, "0x%04x", mcu_base);
   _if_partout(intcmp, "0x%04x", nvm_base);
   _if_partout(intcmp, "0x%04x", ocd_base);
@@ -553,7 +559,7 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
       bm = avr_new_memtype();
 
     if(!tsv) {
-      if(!memorycmp(bm, m))     // same memory bit for bit, no need to instantiate
+      if(!memorycmp(bm, m))     // Same memory bit for bit, no need to instantiate
         continue;
 
       dev_info("\n    memory \"%s\"\n", m->desc);
@@ -562,7 +568,7 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
     _if_memout_yn(paged);
     _if_memout(intcmp, m->size > 8192? "0x%x": "%d", size);
     _if_memout(intcmp, "%d", page_size);
-    _if_memout(intcmp, "%d", num_pages); // why can AVRDUDE not compute this?
+    _if_memout(intcmp, "%d", num_pages);
     _if_memout(intcmp, "0x%x", offset);
     _if_memout(intcmp, "%d", min_write_delay);
     _if_memout(intcmp, "%d", max_write_delay);
@@ -607,7 +613,7 @@ void dev_output_part_defs(char *partdesc) {
   if((flags = strchr(partdesc, '/')))
     *flags++ = 0;
 
-  if(!flags && !strcmp(partdesc, "*")) // treat -p * as if it was -p */*
+  if(!flags && !strcmp(partdesc, "*")) // Treat -p * as if it was -p */*
     flags = "*";
 
   if(!*flags || !strchr("cdoASsrw*t", *flags)) {
@@ -645,7 +651,7 @@ void dev_output_part_defs(char *partdesc) {
     return;
   }
 
-  // redirect stderr to stdout
+  // Redirect stderr to stdout
   fflush(stderr); fflush(stdout); dup2(1, 2);
 
   all = *flags == '*';
@@ -660,14 +666,14 @@ void dev_output_part_defs(char *partdesc) {
   tsv   = !!strchr(flags, 't');
 
 
-  // go through all memories and add them to the memory order list
+  // Go through all memories and add them to the memory order list
   for(LNODEID ln1 = lfirst(part_list); ln1; ln1 = lnext(ln1)) {
     AVRPART *p = ldata(ln1);
     if(p->mem)
       for(LNODEID lnm=lfirst(p->mem); lnm; lnm=lnext(lnm))
         avr_add_mem_order(((AVRMEM *) ldata(lnm))->desc);
 
-    // same for aliased memories (though probably not needed)
+    // Same for aliased memories (though probably not needed)
     if(p->mem_alias)
       for(LNODEID lnm=lfirst(p->mem_alias); lnm; lnm=lnext(lnm))
         avr_add_mem_order(((AVRMEM_ALIAS *) ldata(lnm))->desc);
@@ -696,7 +702,7 @@ void dev_output_part_defs(char *partdesc) {
     if(raw)
       dev_part_raw(p);
 
-    // identify core flash and eeprom parameters
+    // Identify core flash and eeprom parameters
 
     flashsize = flashoffset = flashpagesize = eepromsize = eepromoffset = eeprompagesize = 0;
     if(p->mem) {
@@ -715,7 +721,7 @@ void dev_output_part_defs(char *partdesc) {
       }
     }
 
-    // "real" entries don't seem to have a space in their desc (a bit hackey)
+    // "Real" entries don't seem to have a space in their desc (a bit hackey)
     if(flashsize && !strchr(p->desc, ' ')) {
       int ok, nfuses;
       AVRMEM *m;
@@ -819,7 +825,7 @@ void dev_output_part_defs(char *partdesc) {
       } else
         ok &= ~DEV_SPI_CALIBRATION;
 
-      // actually, some AT90S... parts cannot read, only write lock bits :-0
+      // Actually, some AT90S... parts cannot read, only write lock bits :-0
       if( ! ((m = avr_locate_mem(p, "lock")) && m->op[AVR_OP_WRITE]))
         ok &= ~DEV_SPI_LOCK;
 
@@ -866,14 +872,14 @@ void dev_output_part_defs(char *partdesc) {
       }
     }
 
-    // print wait delays for AVR family parts
+    // Print wait delays for AVR family parts
     if(waits) {
       if(!(p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI | AVRPART_HAS_TPI | AVRPART_AVR32)))
         dev_info(".wd_chip_erase %.3f ms %s\n", p->chip_erase_delay/1000.0, p->desc);
       if(p->mem) {
         for(LNODEID lnm=lfirst(p->mem); lnm; lnm=lnext(lnm)) {
           AVRMEM *m = ldata(lnm);
-          // write delays not needed for read-only calibration and signature memories
+          // Write delays not needed for read-only calibration and signature memories
           if(strcmp(m->desc, "calibration") && strcmp(m->desc, "signature")) {
             if(!(p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI | AVRPART_HAS_TPI | AVRPART_AVR32))) {
               if(m->min_write_delay == m->max_write_delay)
@@ -893,9 +899,45 @@ void dev_output_part_defs(char *partdesc) {
 
 static void dev_pgm_raw(PROGRAMMER *pgm) {
   PROGRAMMER dp;
+  int len, idx;
+  char *id = ldata(lfirst(pgm->id));
+  LNODEID ln;
 
   memcpy(&dp, pgm, sizeof dp);
-  dev_raw_dump((unsigned char *) &dp, sizeof dp, pgm->desc, "pgm", 0);
+
+  // Dump id, usbpid and hvupdi_support lists
+  for(idx=0, ln=lfirst(dp.id); ln; ln=lnext(ln))
+    dev_raw_dump(ldata(ln), strlen(ldata(ln))+1, id, "id", idx++);
+  for(idx=0, ln=lfirst(dp.usbpid); ln; ln=lnext(ln))
+    dev_raw_dump(ldata(ln), sizeof(int), id, "usbpid", idx++);
+  for(idx=0, ln=lfirst(dp.hvupdi_support); ln; ln=lnext(ln))
+    dev_raw_dump(ldata(ln), sizeof(int), id, "hvupdi_", idx++);
+
+  // Dump cache_string values
+  if(dp.usbdev && *dp.usbdev)
+    dev_raw_dump(dp.usbdev, strlen(dp.usbdev)+1, id, "usbdev", 0);
+  if(dp.usbsn && *dp.usbsn)
+    dev_raw_dump(dp.usbsn, strlen(dp.usbsn)+1, id, "usbsn", 0);
+  if(dp.usbvendor && *dp.usbvendor)
+    dev_raw_dump(dp.usbvendor, strlen(dp.usbvendor)+1, id, "usbvend", 0);
+  if(dp.usbproduct && *dp.usbproduct)
+    dev_raw_dump(dp.usbproduct, strlen(dp.usbproduct)+1, id, "usbprod", 0);
+
+  // Zap all bytes beyond terminating nul of desc, type and port array
+  if((len = strlen(dp.desc)+1) < sizeof dp.desc)
+    memset(dp.desc + len, 0, sizeof dp.desc - len);
+  if((len = strlen(dp.type)+1) < sizeof dp.type)
+    memset(dp.type + len, 0, sizeof dp.type - len);
+  if((len = strlen(dp.port)+1) < sizeof dp.port)
+    memset(dp.port + len, 0, sizeof dp.port - len);
+
+  // Zap address values
+  dp.id = NULL;
+  dp.parent_id = NULL;
+  dp.initpgm = NULL;
+
+  // Only dump contents of PROGRAMMER struct up to and excluding the fd component
+  dev_raw_dump((char *) &dp, offsetof(PROGRAMMER, fd), id, "pgm", 0);
 }
 
 
@@ -934,7 +976,7 @@ void dev_output_pgm_defs(char *pgmid) {
   if((flags = strchr(pgmid, '/')))
     *flags++ = 0;
 
-  if(!flags && !strcmp(pgmid, "*")) // treat -c * as if it was -c */A
+  if(!flags && !strcmp(pgmid, "*")) // Treat -c * as if it was -c */A
     flags = "A";
 
   if(!*flags || !strchr("ASsrt", *flags)) {
@@ -966,7 +1008,7 @@ void dev_output_pgm_defs(char *pgmid) {
     return;
   }
 
-  // redirect stderr to stdout
+  // Redirect stderr to stdout
   fflush(stderr); fflush(stdout); dup2(1, 2);
 
   astrc = !!strchr(flags, 'A');
diff --git a/src/libavrdude.h b/src/libavrdude.h
index 7dcab3c2..2dc4bcc5 100644
--- a/src/libavrdude.h
+++ b/src/libavrdude.h
@@ -666,13 +666,13 @@ typedef enum {
   CONNTYPE_SPI
 } conntype_t;
 
+/* Any changes here, please also reflect in dev_pgm_strct() of developer_opts.c */
 typedef struct programmer_t {
   LISTID id;
   char desc[PGM_DESCLEN];
   char type[PGM_TYPELEN];
   char port[PGM_PORTLEN];
   const char *parent_id;
-  void (*initpgm)(struct programmer_t * pgm);
   unsigned int pinno[N_PINS];
   struct pindef_t pin[N_PINS];
   exit_vcc_t exit_vcc;
@@ -684,11 +684,16 @@ typedef struct programmer_t {
   int baudrate;
   int usbvid;
   LISTID usbpid;
+  LISTID hvupdi_support;         // List of UPDI HV variants the tool supports, see HV_UPDI_VARIANT_x
   const char *usbdev, *usbsn, *usbvendor, *usbproduct;
-  double bitclock;    /* JTAG ICE clock period in microseconds */
-  int ispdelay;    /* ISP clock delay */
+  double bitclock;              // JTAG ICE clock period in microseconds
+  int ispdelay;                 // ISP clock delay
+  int  page_size;               // Page size if the programmer supports paged write/load
+
+  // Values below are not set by config_gram.y; first one must be fd for dev_pgm_raw()
   union filedescriptor fd;
-  int  page_size;  /* page size if the programmer supports paged write/load */
+  void (*initpgm)(struct programmer_t * pgm);
+
   int  (*rdy_led)        (struct programmer_t * pgm, int value);
   int  (*err_led)        (struct programmer_t * pgm, int value);
   int  (*pgm_led)        (struct programmer_t * pgm, int value);
@@ -738,11 +743,10 @@ typedef struct programmer_t {
   int  (*parseextparams) (struct programmer_t * pgm, LISTID xparams);
   void (*setup)          (struct programmer_t * pgm);
   void (*teardown)       (struct programmer_t * pgm);
-  const char *config_file;    /* config file where defined */
-  int  lineno;                /* config file line number */
-  void *cookie;		      /* for private use by the programmer */
-  char flag;		      /* for private use of the programmer */
-  LISTID hvupdi_support;  /* List of UPDI HV variants the tool supports. See HV_UPDI_VARIANT_ */
+  const char *config_file;      // Config file where defined
+  int  lineno;                  // Config file line number
+  void *cookie;                 // For private use by the programmer
+  char flag;                    // For private use of the programmer
 } PROGRAMMER;
 
 #ifdef __cplusplus

From 49fcd8a96e29db8ef7ed3a91197d4d18f8079f5b Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Mon, 8 Aug 2022 16:52:09 +0100
Subject: [PATCH 04/19] Implement -c */[sSA] (syntax-correct dump of programmer
 structure)

---
 src/avrpart.c                |   2 +-
 src/config.c                 |   7 ++-
 src/config.h                 |   2 +
 src/config_gram.y            |   3 +-
 src/developer_opts.c         |  93 +++++++++++++++++++++++++++---
 src/developer_opts_private.h |  16 ++++++
 src/lexer.l                  |  15 +++--
 src/libavrdude.h             |  63 +++++++++++++-------
 src/main.c                   |  20 ++++---
 src/pgm.c                    |   2 +-
 src/pgm_type.c               | 108 +++++++++++++++++++----------------
 src/pindefs.c                |  45 ++++++++++++++-
 12 files changed, 281 insertions(+), 95 deletions(-)

diff --git a/src/avrpart.c b/src/avrpart.c
index 970dfde8..c4a2bd8b 100644
--- a/src/avrpart.c
+++ b/src/avrpart.c
@@ -672,7 +672,7 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, AVRPART * p,
 AVRPART * avr_new_part(void)
 {
   AVRPART * p;
-  char *nulp = cache_string("");
+  const char *nulp = cache_string("");
 
   p = (AVRPART *)malloc(sizeof(AVRPART));
   if (p == NULL) {
diff --git a/src/config.c b/src/config.c
index 4308407e..34745daf 100644
--- a/src/config.c
+++ b/src/config.c
@@ -366,7 +366,7 @@ int read_config(const char * file)
 
 
 // Linear-search cache for a few often-referenced strings
-char *cache_string(const char *file) {
+const char *cache_string(const char *file) {
   static char **fnames;
   static int n=0;
 
@@ -394,3 +394,8 @@ char *cache_string(const char *file) {
 
   return fnames[n++];
 }
+
+// Captures comments during parsing
+int capture_comment_char(int c) {
+  return c;
+}
diff --git a/src/config.h b/src/config.h
index a7d2563d..1ebd2f70 100644
--- a/src/config.h
+++ b/src/config.h
@@ -101,6 +101,8 @@ void pyytext(void);
 
 char * dup_string(const char * str);
 
+int capture_comment_char(int c);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/config_gram.y b/src/config_gram.y
index 552832ec..c3554dcc 100644
--- a/src/config_gram.y
+++ b/src/config_gram.y
@@ -1397,7 +1397,8 @@ yesno :
 mem_specs :
   mem_spec TKN_SEMI |
   mem_alias TKN_SEMI |
-  mem_specs mem_spec TKN_SEMI
+  mem_specs mem_spec TKN_SEMI |
+  /* empty */
 ;
 
 
diff --git a/src/developer_opts.c b/src/developer_opts.c
index 8d8c183c..bd59e940 100644
--- a/src/developer_opts.c
+++ b/src/developer_opts.c
@@ -613,8 +613,8 @@ void dev_output_part_defs(char *partdesc) {
   if((flags = strchr(partdesc, '/')))
     *flags++ = 0;
 
-  if(!flags && !strcmp(partdesc, "*")) // Treat -p * as if it was -p */*
-    flags = "*";
+  if(!flags && !strcmp(partdesc, "*")) // Treat -p * as if it was -p */s
+    flags = "s";
 
   if(!*flags || !strchr("cdoASsrw*t", *flags)) {
     dev_info("%s: flags for developer option -p <wildcard>/<flags> not recognised\n", progname);
@@ -640,7 +640,7 @@ void dev_output_part_defs(char *partdesc) {
       "  $ avrdude -p m328*/st | grep chip_erase_delay\n"
       "  avrdude -p*/r | sort\n"
       "Notes:\n"
-      "  -p * is the same as -p */*\n"
+      "  -p * is the same as -p */s\n"
       "  This help message is printed using any unrecognised flag, eg, -p/h\n"
       "  Leaving no space after -p can be an OK substitute for quoting in shells\n"
       "  /s, /S and /A outputs are designed to be used as input in avrdude.conf\n"
@@ -935,19 +935,37 @@ static void dev_pgm_raw(PROGRAMMER *pgm) {
   dp.id = NULL;
   dp.parent_id = NULL;
   dp.initpgm = NULL;
+  dp.usbpid = NULL;
+  dp.usbdev = NULL;
+  dp.usbsn = NULL;
+  dp.usbvendor = NULL;
+  dp.usbproduct = NULL;
+  dp.hvupdi_support = NULL;
 
   // Only dump contents of PROGRAMMER struct up to and excluding the fd component
   dev_raw_dump((char *) &dp, offsetof(PROGRAMMER, fd), id, "pgm", 0);
 }
 
 
-static void dev_pgm_strct(PROGRAMMER *pgm, bool tsv, PROGRAMMER *base) {
-  if(!tsv) {
-    int firstid = 1;
+static const char *connstr(conntype_t conntype) {
+  switch(conntype) {
+  case CONNTYPE_PARALLEL: return "parallel";
+  case CONNTYPE_SERIAL: return "serial";
+  case CONNTYPE_USB: return "usb";
+  case CONNTYPE_SPI: return "spi";
+  default: return "<unknown>";
+  }
+}
 
+static void dev_pgm_strct(PROGRAMMER *pgm, bool tsv, PROGRAMMER *base) {
+  char *id = ldata(lfirst(pgm->id));
+  LNODEID ln;
+  int firstid;
+
+  if(!tsv) {
     dev_info("#------------------------------------------------------------\n");
     dev_info("# ");
-    for(LNODEID ln=lfirst(pgm->id); ln; ln=lnext(ln)) {
+    for(firstid=1, ln=lfirst(pgm->id); ln; ln=lnext(ln)) {
       if(!firstid)
         dev_info("/");
       firstid = 0;
@@ -961,6 +979,67 @@ static void dev_pgm_strct(PROGRAMMER *pgm, bool tsv, PROGRAMMER *base) {
       dev_info("\nprogrammer\n");
   }
 
+  if(tsv)
+    dev_info(".prog\t%s\tid\t", id);
+  else
+    dev_info("    %-19s = ", "id");
+  for(firstid=1, ln=lfirst(pgm->id); ln; ln=lnext(ln)) {
+    if(!firstid)
+      dev_info(", ");
+    firstid = 0;
+    dev_info("\"%s\"", ldata(ln));
+  }
+  dev_info(tsv? "\n": ";\n");
+
+  _if_pgmout(strcmp, "\"%s\"", desc);
+  _pgmout_fmt("type", "\"%s\"", locate_programmer_type_id(pgm->initpgm));
+  _pgmout_fmt("connection_type", "%s", connstr(pgm->conntype));
+  _if_pgmout(intcmp, "%d", baudrate);
+
+  _if_pgmout(intcmp, "0x%04x", usbvid);
+
+  if(pgm->usbpid && lfirst(pgm->usbpid)) {
+    if(tsv)
+      dev_info(".prog\t%s\tusbpid\t", id);
+    else
+      dev_info("    %-19s = ", "usbpid");
+    for(firstid=1, ln=lfirst(pgm->usbpid); ln; ln=lnext(ln)) {
+      if(!firstid)
+        dev_info(", ");
+      firstid = 0;
+      dev_info("0x%04x", *(unsigned int *) ldata(ln));
+    }
+    dev_info(tsv? "\n": ";\n");
+  }
+
+  _if_pgmout(strcmp, "\"%s\"", usbdev);
+  _if_pgmout(strcmp, "\"%s\"", usbsn);
+  _if_pgmout(strcmp, "\"%s\"", usbvendor);
+  _if_pgmout(strcmp, "\"%s\"", usbproduct);
+
+  for(int i=0; i<N_PINS; i++) {
+    char *str = pins_to_strdup(pgm->pin+i);
+    if(str && *str)
+      _pgmout_fmt(avr_pin_lcname(i), "%s", str);
+    if(str)
+      free(str);
+  }
+
+  if(pgm->hvupdi_support && lfirst(pgm->hvupdi_support)) {
+    if(tsv)
+      dev_info(".prog\t%s\thvupdu_support\t", id);
+    else
+      dev_info("    %-19s = ", "hvupdi_support");
+    for(firstid=1, ln=lfirst(pgm->hvupdi_support); ln; ln=lnext(ln)) {
+      if(!firstid)
+        dev_info(", ");
+      firstid = 0;
+      dev_info("%d", *(unsigned int *) ldata(ln));
+    }
+    dev_info(tsv? "\n": ";\n");
+  }
+
+
   if(!tsv)
     dev_info(";\n");
 }
diff --git a/src/developer_opts_private.h b/src/developer_opts_private.h
index 5a208199..e6be1ee9 100644
--- a/src/developer_opts_private.h
+++ b/src/developer_opts_private.h
@@ -51,6 +51,22 @@ static int dev_message(int msglvl, const char *fmt, ...);
 #define dev_notice(...)  dev_message(DEV_NOTICE,  __VA_ARGS__)
 #define dev_notice2(...) dev_message(DEV_NOTICE2, __VA_ARGS__)
 
+#define _pgmout(fmt, component) \
+  dev_part_strct_entry(tsv, ".prog", id, NULL, #component, dev_sprintf(fmt, pgm->component))
+
+#define _pgmout_fmt(name, fmt, what) \
+  dev_part_strct_entry(tsv, ".prog", id, NULL, name, dev_sprintf(fmt, what))
+
+#define _if_pgmout(cmp, fmt, component) do { \
+  if(!base || cmp(base->component, pgm->component)) \
+    dev_part_strct_entry(tsv, ".prog", id, NULL, #component, dev_sprintf(fmt, pgm->component)); \
+} while(0)
+
+#define _if_pgmout_str(cmp, result, component) do { \
+  if(!base || cmp(base->component, pgm->component)) \
+    dev_part_strct_entry(tsv, ".prog", id, NULL, #component, result); \
+} while(0)
+
 #define _partout(fmt, component) \
   dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, dev_sprintf(fmt, p->component))
 
diff --git a/src/lexer.l b/src/lexer.l
index 91d02597..c55d7853 100644
--- a/src/lexer.l
+++ b/src/lexer.l
@@ -67,13 +67,20 @@ SIGN     [+-]
 
 
 
-#   { /* The following eats '#' style comments to end of line */
+#   { /* The following captures all '#' style comments to end of line */
        BEGIN(comment); }
-<comment>[^\n] { /* eat comments */ }
-<comment>\n { cfg_lineno++; BEGIN(INITIAL); }
+<comment>[^\n]*\n+ { /* eat comments */
+  capture_comment_char('#');
+  for(int i=0; yytext[i]; i++) {
+    capture_comment_char(yytext[i]);
+    if(yytext[i] == '\n')
+      cfg_lineno++;
+  }
+  BEGIN(INITIAL);
+}
 
 
-"/*" {  /* The following eats multiline C style comments */
+"/*" {  /* The following eats multiline C style comments, they are not captured */
         int c;
         int comment_start;
         
diff --git a/src/libavrdude.h b/src/libavrdude.h
index 2dc4bcc5..c17f611b 100644
--- a/src/libavrdude.h
+++ b/src/libavrdude.h
@@ -516,7 +516,15 @@ int pins_check(const struct programmer_t * const pgm, const struct pin_checklist
 const char * avr_pin_name(int pinname);
 
 /**
- * This function returns a string representation of defined pins eg. ~1,2,~4,~5,7
+ * Returns the name of the pin as lowercase string.
+ *
+ * @param pinname the pinname which we want as string.
+ * @returns a lowercase string with the pinname, or <unknown> if pinname is invalid.
+ */
+const char * avr_pin_lcname(int pinname);
+
+/**
+ * This function returns a string of defined pins, eg, ~1,2,~4,~5,7 or " (not used)"
  * Another execution of this function will overwrite the previous result in the static buffer.
  *
  * @param[in] pindef the pin definition for which we want the string representation
@@ -525,9 +533,17 @@ const char * avr_pin_name(int pinname);
 const char * pins_to_str(const struct pindef_t * const pindef);
 
 /**
- * This function returns a string representation of pins in the mask eg. 1,3,5-7,9,12
+ * This function returns a string of defined pins, eg, ~1, 2, ~4, ~5, 7 or ""
+ *
+ * @param[in] pindef the pin definition for which we want the string representation
+ * @returns a pointer to a string, which was created by strdup (NULL if out of memory)
+ */
+char *pins_to_strdup(const struct pindef_t * const pindef);
+
+/**
+ * This function returns a string representation of pins in the mask, eg, 1,3,5-7,9,12
  * Another execution of this function will overwrite the previous result in the static buffer.
- * Consecutive pin number are representated as start-end.
+ * Consecutive pin number are represented as start-end.
  *
  * @param[in] pinmask the pin mask for which we want the string representation
  * @returns pointer to a static string.
@@ -670,29 +686,32 @@ typedef enum {
 typedef struct programmer_t {
   LISTID id;
   char desc[PGM_DESCLEN];
-  char type[PGM_TYPELEN];
-  char port[PGM_PORTLEN];
-  const char *parent_id;
-  unsigned int pinno[N_PINS];
+  void (*initpgm)(struct programmer_t *pgm);
+  const char *parent_id;        // Used by developer options -c*/[sr...]
   struct pindef_t pin[N_PINS];
-  exit_vcc_t exit_vcc;
-  exit_reset_t exit_reset;
-  exit_datahigh_t exit_datahigh;
   conntype_t conntype;
-  int ppidata;
-  int ppictrl;
   int baudrate;
   int usbvid;
   LISTID usbpid;
-  LISTID hvupdi_support;         // List of UPDI HV variants the tool supports, see HV_UPDI_VARIANT_x
-  const char *usbdev, *usbsn, *usbvendor, *usbproduct;
-  double bitclock;              // JTAG ICE clock period in microseconds
-  int ispdelay;                 // ISP clock delay
-  int  page_size;               // Page size if the programmer supports paged write/load
+  const char *usbdev;
+  const char *usbsn;
+  const char *usbvendor;
+  const char *usbproduct;
+  LISTID hvupdi_support;        // List of UPDI HV variants the tool supports, see HV_UPDI_VARIANT_x
 
-  // Values below are not set by config_gram.y; first one must be fd for dev_pgm_raw()
+  // Values below are not set by config_gram.y; make sure fd is first for dev_pgm_raw()
   union filedescriptor fd;
-  void (*initpgm)(struct programmer_t * pgm);
+  char type[PGM_TYPELEN];
+  char port[PGM_PORTLEN];
+  unsigned int pinno[N_PINS];   // TODO to be removed if old pin data no longer needed
+  exit_vcc_t exit_vcc;          // Should these be set in avrdude.conf?
+  exit_reset_t exit_reset;
+  exit_datahigh_t exit_datahigh;
+  int ppidata;
+  int ppictrl;
+  int ispdelay;                 // ISP clock delay
+  int page_size;                // Page size if the programmer supports paged write/load
+  double bitclock;              // JTAG ICE clock period in microseconds
 
   int  (*rdy_led)        (struct programmer_t * pgm, int value);
   int  (*err_led)        (struct programmer_t * pgm, int value);
@@ -957,7 +976,9 @@ typedef struct programmer_type_t {
 extern "C" {
 #endif
 
-const PROGRAMMER_TYPE * locate_programmer_type(/*LISTID programmer_types, */const char * id);
+const PROGRAMMER_TYPE *locate_programmer_type(const char *id);
+
+const char *locate_programmer_type_id(const void (*initpgm)(struct programmer_t *pgm));
 
 typedef void (*walk_programmer_types_cb)(const char *id, const char *desc,
                                     void *cookie);
@@ -991,7 +1012,7 @@ void cleanup_config(void);
 
 int read_config(const char * file);
 
-char *cache_string(const char *file);
+const char *cache_string(const char *file);
 
 #ifdef __cplusplus
 }
diff --git a/src/main.c b/src/main.c
index 83bde3e9..ab10b4fa 100644
--- a/src/main.c
+++ b/src/main.c
@@ -752,13 +752,21 @@ int main(int argc, char * argv [])
   }
 
 
-  avrdude_message(MSG_NOTICE, "\n");
-
+  int dev_opts = 0;
+  // Developer option -c <wildcard>/[ASsrt] prints programmer description(s) and exits
+  if(programmer && (strcmp(programmer, "*") == 0 || strchr(programmer, '/'))) {
+    dev_output_pgm_defs(programmer);
+    dev_opts = 1;
+  }
   // Developer option -p <wildcard>/[dASsrcow*t] prints part description(s) and exits
   if(partdesc && (strcmp(partdesc, "*") == 0 || strchr(partdesc, '/'))) {
     dev_output_part_defs(partdesc);
-    exit(1);
+    dev_opts = 1;
   }
+  if(dev_opts)
+    exit(0);
+
+  avrdude_message(MSG_NOTICE, "\n");
 
   if (partdesc) {
     if (strcmp(partdesc, "?") == 0) {
@@ -770,12 +778,6 @@ int main(int argc, char * argv [])
     }
   }
 
-  // Developer option -c <wildcard>/[ASsrt] prints programmer description(s) and exits
-  if(programmer && (strcmp(programmer, "*") == 0 || strchr(programmer, '/'))) {
-    dev_output_pgm_defs(programmer);
-    exit(1);
-  }
-
   if (programmer) {
     if (strcmp(programmer, "?") == 0) {
       avrdude_message(MSG_INFO, "\n");
diff --git a/src/pgm.c b/src/pgm.c
index a4e0ed54..238f61d8 100644
--- a/src/pgm.c
+++ b/src/pgm.c
@@ -65,7 +65,7 @@ PROGRAMMER * pgm_new(void)
 {
   int i;
   PROGRAMMER * pgm;
-  char *nulp = cache_string("");
+  const char *nulp = cache_string("");
 
   pgm = (PROGRAMMER *)malloc(sizeof(*pgm));
   if (pgm == NULL) {
diff --git a/src/pgm_type.c b/src/pgm_type.c
index 82320fab..46090424 100644
--- a/src/pgm_type.c
+++ b/src/pgm_type.c
@@ -57,55 +57,55 @@
 #include "xbee.h"
 
 
-const PROGRAMMER_TYPE programmers_types[] = {
-        {"arduino", arduino_initpgm, arduino_desc},
-        {"avr910", avr910_initpgm, avr910_desc},
-        {"avrftdi", avrftdi_initpgm, avrftdi_desc},
-        {"buspirate", buspirate_initpgm, buspirate_desc},
-        {"buspirate_bb", buspirate_bb_initpgm, buspirate_bb_desc},
-        {"butterfly", butterfly_initpgm, butterfly_desc},
-        {"butterfly_mk", butterfly_mk_initpgm, butterfly_mk_desc},
-        {"dragon_dw", jtagmkII_dragon_dw_initpgm, jtagmkII_dragon_dw_desc},
-        {"dragon_hvsp", stk500v2_dragon_hvsp_initpgm, stk500v2_dragon_hvsp_desc},
-        {"dragon_isp", stk500v2_dragon_isp_initpgm, stk500v2_dragon_isp_desc},
-        {"dragon_jtag", jtagmkII_dragon_initpgm, jtagmkII_dragon_desc},
-        {"dragon_pdi", jtagmkII_dragon_pdi_initpgm, jtagmkII_dragon_pdi_desc},
-        {"dragon_pp", stk500v2_dragon_pp_initpgm, stk500v2_dragon_pp_desc},
-        {"flip1", flip1_initpgm, flip1_desc},
-        {"flip2", flip2_initpgm, flip2_desc},
-        {"ftdi_syncbb", ft245r_initpgm, ft245r_desc},
-        {"jtagmki", jtagmkI_initpgm, jtagmkI_desc},
-        {"jtagmkii", jtagmkII_initpgm, jtagmkII_desc},
-        {"jtagmkii_avr32", jtagmkII_avr32_initpgm, jtagmkII_avr32_desc},
-        {"jtagmkii_dw", jtagmkII_dw_initpgm, jtagmkII_dw_desc},
-        {"jtagmkii_isp", stk500v2_jtagmkII_initpgm, stk500v2_jtagmkII_desc},
-        {"jtagmkii_pdi", jtagmkII_pdi_initpgm, jtagmkII_pdi_desc},
-        {"jtagmkii_updi", jtagmkII_updi_initpgm, jtagmkII_updi_desc},
-        {"jtagice3", jtag3_initpgm, jtag3_desc},
-        {"jtagice3_pdi", jtag3_pdi_initpgm, jtag3_pdi_desc},
-        {"jtagice3_updi", jtag3_updi_initpgm, jtag3_updi_desc},
-        {"jtagice3_dw", jtag3_dw_initpgm, jtag3_dw_desc},
-        {"jtagice3_isp", stk500v2_jtag3_initpgm, stk500v2_jtag3_desc},
-        {"linuxgpio", linuxgpio_initpgm, linuxgpio_desc},
-        {"linuxspi", linuxspi_initpgm, linuxspi_desc},
-        {"micronucleus", micronucleus_initpgm, micronucleus_desc},
-        {"par", par_initpgm, par_desc},
-        {"pickit2", pickit2_initpgm, pickit2_desc},
-        {"serbb", serbb_initpgm, serbb_desc},
-        {"serialupdi", serialupdi_initpgm, serialupdi_desc},
-        {"stk500", stk500_initpgm, stk500_desc},
-        {"stk500generic", stk500generic_initpgm, stk500generic_desc},
-        {"stk500v2", stk500v2_initpgm, stk500v2_desc},
-        {"stk500hvsp", stk500hvsp_initpgm, stk500hvsp_desc},
-        {"stk500pp", stk500pp_initpgm, stk500pp_desc},
-        {"stk600", stk600_initpgm, stk600_desc},
-        {"stk600hvsp", stk600hvsp_initpgm, stk600hvsp_desc},
-        {"stk600pp", stk600pp_initpgm, stk600pp_desc},
-        {"teensy", teensy_initpgm, teensy_desc},
-        {"usbasp", usbasp_initpgm, usbasp_desc},
-        {"usbtiny", usbtiny_initpgm, usbtiny_desc},
-        {"wiring", wiring_initpgm, wiring_desc},
-        {"xbee", xbee_initpgm, xbee_desc},
+const PROGRAMMER_TYPE programmers_types[] = { // Name(s) the programmers call themselves
+  {"arduino", arduino_initpgm, arduino_desc}, // "Arduino"
+  {"avr910", avr910_initpgm, avr910_desc}, // "avr910"
+  {"avrftdi", avrftdi_initpgm, avrftdi_desc}, // "avrftdi"
+  {"buspirate", buspirate_initpgm, buspirate_desc}, // "BusPirate"
+  {"buspirate_bb", buspirate_bb_initpgm, buspirate_bb_desc}, // "BusPirate_BB"
+  {"butterfly", butterfly_initpgm, butterfly_desc}, // "butterfly"
+  {"butterfly_mk", butterfly_mk_initpgm, butterfly_mk_desc}, // "butterfly_mk"
+  {"dragon_dw", jtagmkII_dragon_dw_initpgm, jtagmkII_dragon_dw_desc}, // "DRAGON_DW"
+  {"dragon_hvsp", stk500v2_dragon_hvsp_initpgm, stk500v2_dragon_hvsp_desc}, // "DRAGON_HVSP"
+  {"dragon_isp", stk500v2_dragon_isp_initpgm, stk500v2_dragon_isp_desc}, // "DRAGON_ISP"
+  {"dragon_jtag", jtagmkII_dragon_initpgm, jtagmkII_dragon_desc}, // "DRAGON_JTAG"
+  {"dragon_pdi", jtagmkII_dragon_pdi_initpgm, jtagmkII_dragon_pdi_desc}, // "DRAGON_PDI"
+  {"dragon_pp", stk500v2_dragon_pp_initpgm, stk500v2_dragon_pp_desc}, // "DRAGON_PP"
+  {"flip1", flip1_initpgm, flip1_desc}, // "flip1"
+  {"flip2", flip2_initpgm, flip2_desc}, // "flip2"
+  {"ftdi_syncbb", ft245r_initpgm, ft245r_desc}, // "ftdi_syncbb"
+  {"jtagmki", jtagmkI_initpgm, jtagmkI_desc}, // "JTAGMKI"
+  {"jtagmkii", jtagmkII_initpgm, jtagmkII_desc}, // "JTAGMKII"
+  {"jtagmkii_avr32", jtagmkII_avr32_initpgm, jtagmkII_avr32_desc}, // "JTAGMKII_AVR32"
+  {"jtagmkii_dw", jtagmkII_dw_initpgm, jtagmkII_dw_desc}, // "JTAGMKII_DW"
+  {"jtagmkii_isp", stk500v2_jtagmkII_initpgm, stk500v2_jtagmkII_desc}, // "JTAGMKII_ISP"
+  {"jtagmkii_pdi", jtagmkII_pdi_initpgm, jtagmkII_pdi_desc}, // "JTAGMKII_PDI"
+  {"jtagmkii_updi", jtagmkII_updi_initpgm, jtagmkII_updi_desc}, // "JTAGMKII_UPDI"
+  {"jtagice3", jtag3_initpgm, jtag3_desc}, // "JTAGICE3"
+  {"jtagice3_pdi", jtag3_pdi_initpgm, jtag3_pdi_desc}, // "JTAGICE3_PDI"
+  {"jtagice3_updi", jtag3_updi_initpgm, jtag3_updi_desc}, // "JTAGICE3_UPDI"
+  {"jtagice3_dw", jtag3_dw_initpgm, jtag3_dw_desc}, // "JTAGICE3_DW"
+  {"jtagice3_isp", stk500v2_jtag3_initpgm, stk500v2_jtag3_desc}, // "JTAG3_ISP"
+  {"linuxgpio", linuxgpio_initpgm, linuxgpio_desc}, // "linuxgpio"
+  {"linuxspi", linuxspi_initpgm, linuxspi_desc}, // LINUXSPI
+  {"micronucleus", micronucleus_initpgm, micronucleus_desc}, // "micronucleus" or "Micronucleus V2.0"
+  {"par", par_initpgm, par_desc}, // "PPI"
+  {"pickit2", pickit2_initpgm, pickit2_desc}, // "pickit2"
+  {"serbb", serbb_initpgm, serbb_desc}, // "SERBB"
+  {"serialupdi", serialupdi_initpgm, serialupdi_desc}, // "serialupdi"
+  {"stk500", stk500_initpgm, stk500_desc}, // "STK500"
+  {"stk500generic", stk500generic_initpgm, stk500generic_desc}, // "STK500GENERIC"
+  {"stk500v2", stk500v2_initpgm, stk500v2_desc}, // "STK500V2"
+  {"stk500hvsp", stk500hvsp_initpgm, stk500hvsp_desc}, // "STK500HVSP"
+  {"stk500pp", stk500pp_initpgm, stk500pp_desc}, // "STK500PP"
+  {"stk600", stk600_initpgm, stk600_desc}, // "STK600"
+  {"stk600hvsp", stk600hvsp_initpgm, stk600hvsp_desc}, // "STK600HVSP"
+  {"stk600pp", stk600pp_initpgm, stk600pp_desc}, // "STK600PP"
+  {"teensy", teensy_initpgm, teensy_desc}, // "teensy"
+  {"usbasp", usbasp_initpgm, usbasp_desc}, // "usbasp"
+  {"usbtiny", usbtiny_initpgm, usbtiny_desc}, // "USBtiny" or "usbtiny"
+  {"wiring", wiring_initpgm, wiring_desc}, // "Wiring"
+  {"xbee", xbee_initpgm, xbee_desc}, // "XBee"
 };
 
 const PROGRAMMER_TYPE * locate_programmer_type(const char * id)
@@ -128,6 +128,16 @@ const PROGRAMMER_TYPE * locate_programmer_type(const char * id)
   return NULL;
 }
 
+// Return type id given the init function or "" if not found
+const char *locate_programmer_type_id(void (*initpgm)(struct programmer_t *pgm)) {
+  for (int i=0; i < sizeof programmers_types/sizeof*programmers_types; i++)
+    if(programmers_types[i].initpgm == initpgm)
+      return programmers_types[i].id;
+
+  return "";
+}
+
+
 /*
  * Iterate over the list of programmers given as "programmers", and
  * call the callback function cb for each entry found.  cb is being
diff --git a/src/pindefs.c b/src/pindefs.c
index f51e5c20..1d860ebf 100644
--- a/src/pindefs.c
+++ b/src/pindefs.c
@@ -312,7 +312,7 @@ int pins_check(const struct programmer_t * const pgm, const struct pin_checklist
 }
 
 /**
- * This function returns a string representation of defined pins eg. ~1,2,~4,~5,7
+ * This function returns a string of defined pins, eg, ~1,2,~4,~5,7 or " (not used)"
  * Another execution of this function will overwrite the previous result in the static buffer.
  *
  * @param[in] pindef the pin definition for which we want the string representation
@@ -346,6 +346,28 @@ const char * pins_to_str(const struct pindef_t * const pindef) {
   return buf;
 }
 
+/**
+ * This function returns a string of defined pins, eg, ~1, 2, ~4, ~5, 7 or ""
+ *
+ * @param[in] pindef the pin definition for which we want the string representation
+ * @returns a pointer to a string, which was created by strdup (NULL if out of memory)
+ */
+char *pins_to_strdup(const struct pindef_t * const pindef) {
+  char buf[6*(PIN_MAX+1)], *p = buf;
+
+  *buf = 0;
+  for(int pin = PIN_MIN; pin <= PIN_MAX; pin++) {
+    int index = pin / PIN_FIELD_ELEMENT_SIZE, bit = pin % PIN_FIELD_ELEMENT_SIZE;
+    if(pindef->mask[index] & (1 << bit)) {
+      if(*buf)
+         *p++ = ',', *p++=' ';
+      p += sprintf(p, "~%d" + !(pindef->inverse[index] & (1 << bit)), pin);
+    }
+  }
+
+  return strdup(buf);
+}
+
 /**
  * Returns the name of the pin as string.
  *
@@ -369,3 +391,24 @@ const char * avr_pin_name(int pinname) {
 }
 
 
+/**
+ * Returns the name of the pin as string.
+ *
+ * @param pinname the pinname which we want as string.
+ * @returns a lowercase string with the pinname, or <unknown> if pinname is invalid.
+ */
+const char * avr_pin_lcname(int pinname) {
+  switch(pinname) {
+  case PPI_AVR_VCC   : return "vcc";
+  case PPI_AVR_BUFF  : return "buff";
+  case PIN_AVR_RESET : return "reset";
+  case PIN_AVR_SCK   : return "sck";
+  case PIN_AVR_MOSI  : return "mosi";
+  case PIN_AVR_MISO  : return "miso";
+  case PIN_LED_ERR   : return "errled";
+  case PIN_LED_RDY   : return "rdyled";
+  case PIN_LED_PGM   : return "pgmled";
+  case PIN_LED_VFY   : return "vfyled";
+  default : return "<unknown>";
+  }
+}

From f25bc5580634d6fee3d2d99409725c62a4af76f8 Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Mon, 8 Aug 2022 17:03:06 +0100
Subject: [PATCH 05/19] Treat -c* the same as -c*/s

---
 src/developer_opts.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/developer_opts.c b/src/developer_opts.c
index bd59e940..7e94aa15 100644
--- a/src/developer_opts.c
+++ b/src/developer_opts.c
@@ -1055,8 +1055,8 @@ void dev_output_pgm_defs(char *pgmid) {
   if((flags = strchr(pgmid, '/')))
     *flags++ = 0;
 
-  if(!flags && !strcmp(pgmid, "*")) // Treat -c * as if it was -c */A
-    flags = "A";
+  if(!flags && !strcmp(pgmid, "*")) // Treat -c * as if it was -c */s
+    flags = "s";
 
   if(!*flags || !strchr("ASsrt", *flags)) {
     dev_info("%s: flags for developer option -c <wildcard>/<flags> not recognised\n", progname);
@@ -1077,7 +1077,7 @@ void dev_output_pgm_defs(char *pgmid) {
       "  $ avrdude -c */st | grep baudrate\n"
       "  $ avrdude -c */r | sort\n"
       "Notes:\n"
-      "  -c * is the same as -c */A\n"
+      "  -c * is the same as -c */s\n"
       "  This help message is printed using any unrecognised flag, eg, -c/h\n"
       "  Leaving no space after -c can be an OK substitute for quoting in shells\n"
       "  /s, /S and /A outputs are designed to be used as input in avrdude.conf\n"

From 1da97f6825ed79db849d2aebf63a209e69e7ecc0 Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Mon, 8 Aug 2022 17:21:21 +0100
Subject: [PATCH 06/19] Adjust declaration of locate_programmer_type_id() to
 definition

---
 src/libavrdude.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libavrdude.h b/src/libavrdude.h
index c17f611b..9ab1a8d9 100644
--- a/src/libavrdude.h
+++ b/src/libavrdude.h
@@ -978,7 +978,7 @@ extern "C" {
 
 const PROGRAMMER_TYPE *locate_programmer_type(const char *id);
 
-const char *locate_programmer_type_id(const void (*initpgm)(struct programmer_t *pgm));
+const char *locate_programmer_type_id(void (*initpgm)(struct programmer_t *pgm));
 
 typedef void (*walk_programmer_types_cb)(const char *id, const char *desc,
                                     void *cookie);

From c21be27a7d7c0a6096e452db58cc10b8a71462aa Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Mon, 8 Aug 2022 17:27:38 +0100
Subject: [PATCH 07/19] Replace const char array indexing with equivalent code
 in pindefs.c

---
 src/pindefs.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pindefs.c b/src/pindefs.c
index 1d860ebf..625d1f18 100644
--- a/src/pindefs.c
+++ b/src/pindefs.c
@@ -361,7 +361,7 @@ char *pins_to_strdup(const struct pindef_t * const pindef) {
     if(pindef->mask[index] & (1 << bit)) {
       if(*buf)
          *p++ = ',', *p++=' ';
-      p += sprintf(p, "~%d" + !(pindef->inverse[index] & (1 << bit)), pin);
+      p += sprintf(p, pindef->inverse[index] & (1 << bit)? "~%d": "%d", pin);
     }
   }
 

From 7c8d336e27485cf4d8055eb4221befa185932711 Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Tue, 9 Aug 2022 09:23:26 +0100
Subject: [PATCH 08/19] Change dev_info() to stdout and no longer redirect
 stderr to stdout

---
 src/developer_opts.c | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/src/developer_opts.c b/src/developer_opts.c
index 7e94aa15..de245608 100644
--- a/src/developer_opts.c
+++ b/src/developer_opts.c
@@ -214,7 +214,7 @@ int dev_message(int msglvl, const char *fmt, ...) {
 
   if(verbose >= msglvl) {
     va_start(ap, fmt);
-    rc = vfprintf(stderr, fmt, ap);
+    rc = vfprintf(stdout, fmt, ap);
     va_end(ap);
     if(rc > 0)
       dev_nprinted += rc;
@@ -651,9 +651,6 @@ void dev_output_part_defs(char *partdesc) {
     return;
   }
 
-  // Redirect stderr to stdout
-  fflush(stderr); fflush(stdout); dup2(1, 2);
-
   all = *flags == '*';
   cmdok = all || !!strchr(flags, 'c');
   descs = all || !!strchr(flags, 'd');
@@ -1087,9 +1084,6 @@ void dev_output_pgm_defs(char *pgmid) {
     return;
   }
 
-  // Redirect stderr to stdout
-  fflush(stderr); fflush(stdout); dup2(1, 2);
-
   astrc = !!strchr(flags, 'A');
   strct = !!strchr(flags, 'S');
   cmpst = !!strchr(flags, 's');

From 8a717987ec1cdb9612fec4d42cdd1ec4165b1489 Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Tue, 9 Aug 2022 13:19:40 +0100
Subject: [PATCH 09/19] Change unsigned short eecr; to unsigned char eecr; in
 libavrdude's AVRPART

---
 src/developer_opts.c | 2 +-
 src/libavrdude.h     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/developer_opts.c b/src/developer_opts.c
index de245608..b4f69753 100644
--- a/src/developer_opts.c
+++ b/src/developer_opts.c
@@ -533,7 +533,7 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
   _if_partout(intcmp, "0x%02x", idr);
   _if_partout(intcmp, "0x%02x", rampz);
   _if_partout(intcmp, "0x%02x", spmcr);
-  _if_partout(intcmp, "0x%02x", eecr);  // Why is eecr an unsigned short?
+  _if_partout(intcmp, "0x%02x", eecr);
   _if_partout(intcmp, "0x%04x", mcu_base);
   _if_partout(intcmp, "0x%04x", nvm_base);
   _if_partout(intcmp, "0x%04x", ocd_base);
diff --git a/src/libavrdude.h b/src/libavrdude.h
index 9ab1a8d9..fb4a53dd 100644
--- a/src/libavrdude.h
+++ b/src/libavrdude.h
@@ -270,7 +270,7 @@ typedef struct avrpart {
   unsigned char idr;                /* JTAG ICE mkII XML file parameter */
   unsigned char rampz;              /* JTAG ICE mkII XML file parameter */
   unsigned char spmcr;              /* JTAG ICE mkII XML file parameter */
-  unsigned short eecr;              /* JTAC ICE mkII XML file parameter */
+  unsigned char eecr;               /* JTAC ICE mkII XML file parameter */
   unsigned int mcu_base;            /* Base address of MCU control block in ATxmega devices */
   unsigned int nvm_base;            /* Base address of NVM controller in ATxmega devices */
   unsigned int ocd_base;            /* Base address of OCD module in AVR8X/UPDI devices */

From 22c4dbf23e89e16491b76dceadfc443eca1c56bc Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Tue, 9 Aug 2022 21:20:44 +0100
Subject: [PATCH 10/19] Harden string processing during parsing in lexer.l,
 config_gram.y and otherwise

 - Replace strdup(s) with cfg_strdup(funname, s) that exits on out of mem
 - Replace malloc(n) with cfg_malloc(funname, n) that exits on out of mem
 - Change multiline string scanning in lexer.l to avoid core dump
 - Remove global variables string_buf and string_bug_ptr
 - Ensure reading strings unescapes strings C-Style
 - Ensure writing strings escapes strings C-Style again

Commit looks longer than needed as unescape() and auxiliary functions needed
to be moved from term.c (not in libavrdude) to config.c (in libavrdude).
---
 src/avr.c                    |   2 +-
 src/avrpart.c                |   6 +-
 src/config.c                 | 338 ++++++++++++++++++++++++++---------
 src/config.h                 |  29 ++-
 src/config_gram.y            |  18 +-
 src/developer_opts.c         |  51 +++---
 src/developer_opts_private.h |  15 +-
 src/lexer.l                  |  28 ++-
 src/libavrdude.h             |  12 +-
 src/main.c                   |   5 +-
 src/pgm.c                    |  24 +--
 src/pindefs.c                |   4 +-
 src/term.c                   | 175 +-----------------
 src/update.c                 |  50 ++----
 14 files changed, 355 insertions(+), 402 deletions(-)

diff --git a/src/avr.c b/src/avr.c
index eea0ad8a..62760ce7 100644
--- a/src/avr.c
+++ b/src/avr.c
@@ -1243,7 +1243,7 @@ void avr_add_mem_order(const char *str) {
     if(avr_mem_order[i] && !strcmp(avr_mem_order[i], str))
       return;
     if(!avr_mem_order[i]) {
-      avr_mem_order[i] = strdup(str);
+      avr_mem_order[i] = cfg_strdup("avr_mem_order()", str);
       return;
     }
   }
diff --git a/src/avrpart.c b/src/avrpart.c
index c4a2bd8b..40baa449 100644
--- a/src/avrpart.c
+++ b/src/avrpart.c
@@ -956,7 +956,7 @@ char *cmdbitstr(CMDBIT cb) {
   else
     space[1] = 0;
 
-  return strdup(space);
+  return cfg_strdup("cmdbitstr()", space);
 }
 
 
@@ -998,7 +998,7 @@ char *opcode2str(OPCODE *op, int opnum, int detailed) {
   int compact = 1;
 
   if(!op)
-    return strdup("NULL");
+    return cfg_strdup("opcode2str()", "NULL");
 
   // Can the opcode be printed in a compact way? Only if address bits are systematic.
   for(int i=31; i >= 0; i--)
@@ -1033,7 +1033,7 @@ char *opcode2str(OPCODE *op, int opnum, int detailed) {
     *sp++ = '"';
   *sp = 0;
 
-  return strdup(space);
+  return cfg_strdup("opcode2str()", space);
 }
 
 
diff --git a/src/config.c b/src/config.c
index 34745daf..8d99848d 100644
--- a/src/config.c
+++ b/src/config.c
@@ -25,6 +25,7 @@
 #include <stdlib.h>
 #include <stdarg.h>
 #include <string.h>
+#include <ctype.h>
 
 #include "avrdude.h"
 #include "libavrdude.h"
@@ -38,9 +39,6 @@ char default_serial[PATH_MAX];
 char default_spi[PATH_MAX];
 double default_bitclock;
 
-char string_buf[MAX_STR_CONST];
-char *string_buf_ptr;
-
 LISTID       string_list;
 LISTID       number_list;
 PROGRAMMER * current_prog;
@@ -82,6 +80,25 @@ int init_config(void)
   return 0;
 }
 
+void *cfg_malloc(const char *funcname, size_t n) {
+  void *ret = malloc(n);
+  if(!ret) {
+    avrdude_message(MSG_INFO, "%s: out of memory in %s\n", progname, funcname);
+    exit(1);
+  }
+  memset(ret, 0, n);
+  return ret;
+}
+
+
+char *cfg_strdup(const char *funcname, const char *s) {
+  char *ret = strdup(s);
+  if(!ret) {
+    avrdude_message(MSG_INFO, "%s: out of memory in %s\n", progname, funcname);
+    exit(1);
+  }
+  return ret;
+}
 
 
 int yywrap()
@@ -124,20 +141,9 @@ int yywarning(char * errmsg, ...)
 }
 
 
-TOKEN * new_token(int primary)
-{
-  TOKEN * tkn;
-
-  tkn = (TOKEN *)malloc(sizeof(TOKEN));
-  if (tkn == NULL) {
-    yyerror("new_token(): out of memory");
-    return NULL;
-  }
-
-  memset(tkn, 0, sizeof(TOKEN));
-
+TOKEN * new_token(int primary) {
+  TOKEN * tkn = (TOKEN *) cfg_malloc("new_token()", sizeof(TOKEN));
   tkn->primary = primary;
-
   return tkn;
 }
 
@@ -173,14 +179,8 @@ void free_tokens(int n, ...)
 
 
 
-TOKEN * number(char * text)
-{
-  struct token_t * tkn;
-
-  tkn = new_token(TKN_NUMBER);
-  if (tkn == NULL) {
-      return NULL; /* yyerror already called */
-  }
+TOKEN *number(const char *text) {
+  struct token_t *tkn = new_token(TKN_NUMBER);
   tkn->value.type   = V_NUM;
   tkn->value.number = atoi(text);
 
@@ -191,11 +191,8 @@ TOKEN * number(char * text)
   return tkn;
 }
 
-TOKEN * number_real(char * text)
-{
-  struct token_t * tkn;
-
-  tkn = new_token(TKN_NUMBER);
+TOKEN *number_real(const char *text) {
+  struct token_t * tkn = new_token(TKN_NUMBER);
   tkn->value.type   = V_NUM_REAL;
   tkn->value.number_real = atof(text);
 
@@ -206,15 +203,10 @@ TOKEN * number_real(char * text)
   return tkn;
 }
 
-TOKEN * hexnumber(char * text)
-{
-  struct token_t * tkn;
+TOKEN *hexnumber(const char *text) {
+  struct token_t *tkn = new_token(TKN_NUMBER);
   char * e;
 
-  tkn = new_token(TKN_NUMBER);
-  if (tkn == NULL) {
-      return NULL; /* yyerror already called */
-  }
   tkn->value.type   = V_NUM;
   tkn->value.number = strtoul(text, &e, 16);
   if ((e == text) || (*e != 0)) {
@@ -231,26 +223,10 @@ TOKEN * hexnumber(char * text)
 }
 
 
-TOKEN * string(char * text)
-{
-  struct token_t * tkn;
-  int len;
-
-  tkn = new_token(TKN_STRING);
-  if (tkn == NULL) {
-      return NULL; /* yyerror already called */
-  }
-
-  len = strlen(text);
-
+TOKEN *string(const char *text) {
+  struct token_t *tkn = new_token(TKN_STRING);
   tkn->value.type   = V_STR;
-  tkn->value.string = (char *) malloc(len+1);
-  if (tkn->value.string == NULL) {
-    yyerror("string(): out of memory");
-    free_token(tkn);
-    return NULL;
-  }
-  strcpy(tkn->value.string, text);
+  tkn->value.string = cfg_strdup("string()", text);
 
 #if DEBUG
   avrdude_message(MSG_INFO, "STRING(%s)\n", tkn->value.string);
@@ -260,13 +236,8 @@ TOKEN * string(char * text)
 }
 
 
-TOKEN * keyword(int primary)
-{
-  struct token_t * tkn;
-
-  tkn = new_token(primary);
-
-  return tkn;
+TOKEN * keyword(int primary) {
+  return new_token(primary);
 }
 
 
@@ -306,19 +277,6 @@ void pyytext(void)
 }
 
 
-char * dup_string(const char * str)
-{
-  char * s;
-
-  s = strdup(str);
-  if (s == NULL) {
-    yyerror("dup_string(): out of memory");
-    return NULL;
-  }
-
-  return s;
-}
-
 #ifdef HAVE_YYLEX_DESTROY
 /* reset lexer and free any allocated memory */
 extern int yylex_destroy(void);
@@ -386,11 +344,7 @@ const char *cache_string(const char *file) {
     }
   }
 
-  fnames[n] = strdup(file);
-  if(!fnames[n]) {
-    yyerror("cache_string(): out of memory");
-    return NULL;
-  }
+  fnames[n] = cfg_strdup("cache_string()", file);
 
   return fnames[n++];
 }
@@ -399,3 +353,227 @@ const char *cache_string(const char *file) {
 int capture_comment_char(int c) {
   return c;
 }
+
+
+// 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)
+unsigned char *cfg_unescapeu(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;
+}
+
+// Unescape C-style strings, destination d must hold enough space (and can be source s)
+char *cfg_unescape(char *d, const char *s) {
+  return (char *) cfg_unescapeu((unsigned char *) d, (const unsigned char *) s);
+}
+
+// Return an escaped string that looks like a C-style input string incl quotes, memory is malloc()ed
+char *cfg_escape(const char *s) {
+  char *ret = (char *) cfg_malloc("cfg_escape()", 4*strlen(s)+2+3), *d = ret;
+
+  *d++ = '"';
+  for(; *s; s++) {
+    switch(*s) {
+    case '\n':
+      *d++ = '\\'; *d++ = 'n';
+      break;
+    case '\t':
+      *d++ = '\\'; *d++ = 't';
+      break;
+    case '\a':
+      *d++ = '\\'; *d++ = 'a';
+      break;
+    case '\b':
+      *d++ = '\\'; *d++ = 'b';
+      break;
+    case '\f':
+      *d++ = '\\'; *d++ = 'f';
+      break;
+#if '\r' != '\n'
+    case '\r':
+      *d++ = '\\'; *d++ = 'r';
+      break;
+#endif
+    case '\v':
+      *d++ = '\\'; *d++ = 'v';
+      break;
+    case '\"':
+      *d++ = '\\'; *d++ = '\"';
+      break;
+    default:
+      if(*s == 0x7f || (*s >= 0 && *s < 32)) {
+        sprintf(d, "\\%03o", *s);
+        d += strlen(d);
+      } else
+        *d++ = *s;
+    }
+  }
+  *d++ = '"';
+  *d = 0;
+
+  return ret;
+}
diff --git a/src/config.h b/src/config.h
index 1ebd2f70..260d404a 100644
--- a/src/config.h
+++ b/src/config.h
@@ -35,11 +35,11 @@
 enum { V_NONE, V_NUM, V_NUM_REAL, V_STR };
 typedef struct value_t {
   int      type;
-  /*union { TODO: use an anonymous union here ? */
+  union {
     int      number;
     double   number_real;
     char   * string;
-  /*};*/
+  };
 } VALUE;
 
 
@@ -66,41 +66,36 @@ extern bool         is_alias; // current entry is alias
 #endif
 extern YYSTYPE yylval;
 
-extern char string_buf[MAX_STR_CONST];
-extern char *string_buf_ptr;
-
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 int yyparse(void);
 
-int yyerror(char * errmsg, ...);
+int yyerror(char *errmsg, ...);
 
-int yywarning(char * errmsg, ...);
+int yywarning(char *errmsg, ...);
 
-TOKEN * new_token(int primary);
+TOKEN *new_token(int primary);
 
-void free_token(TOKEN * tkn);
+void free_token(TOKEN *tkn);
 
 void free_tokens(int n, ...);
 
-TOKEN * number(char * text);
+TOKEN *number(const char *text);
 
-TOKEN * number_real(char * text);
+TOKEN *number_real(const char *text);
 
-TOKEN * hexnumber(char * text);
+TOKEN *hexnumber(const char *text);
 
-TOKEN * string(char * text);
+TOKEN *string(const char *text);
 
-TOKEN * keyword(int primary);
+TOKEN *keyword(int primary);
 
-void print_token(TOKEN * tkn);
+void print_token(TOKEN *tkn);
 
 void pyytext(void);
 
-char * dup_string(const char * str);
-
 int capture_comment_char(int c);
 
 #ifdef __cplusplus
diff --git a/src/config_gram.y b/src/config_gram.y
index c3554dcc..0b9cf6cd 100644
--- a/src/config_gram.y
+++ b/src/config_gram.y
@@ -453,25 +453,11 @@ prog_parms :
 prog_parm :
   K_ID TKN_EQUAL string_list {
     {
-      TOKEN * t;
-      char *s;
-      int do_yyabort = 0;
       while (lsize(string_list)) {
-        t = lrmv_n(string_list, 1);
-        if (!do_yyabort) {
-          s = dup_string(t->value.string);
-          if (s == NULL) {
-            do_yyabort = 1;
-          } else {
-            ladd(current_prog->id, s);
-          }
-        }
-        /* if do_yyabort == 1 just make the list empty */
+        TOKEN *t = lrmv_n(string_list, 1);
+        ladd(current_prog->id, cfg_strdup("config_gram.y", t->value.string));
         free_token(t);
       }
-      if (do_yyabort) {
-        YYABORT;
-      }
     }
   } |
   prog_parm_type
diff --git a/src/developer_opts.c b/src/developer_opts.c
index b4f69753..6cbfa4f2 100644
--- a/src/developer_opts.c
+++ b/src/developer_opts.c
@@ -187,20 +187,17 @@ static char *dev_sprintf(const char *fmt, ...) {
   va_end(ap);
 
   if(size < 0)
-    return NULL;
+    return cfg_strdup("dev_sprintf()", "");
 
-  size++;                       // For temrinating '\0'
-  if(!(p = malloc(size)))
-   return NULL;
+  size++;                       // For terminating '\0'
+  p = cfg_malloc("dev_sprintf()", size);
 
   va_start(ap, fmt);
   size = vsnprintf(p, size, fmt, ap);
   va_end(ap);
 
-  if(size < 0) {
-    free(p);
-    return NULL;
-  }
+  if(size < 0)
+    *p = 0;
 
   return p;
 }
@@ -438,9 +435,10 @@ static void dev_part_raw(AVRPART *part) {
 
 static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
 
+  char *descstr = cfg_escape(p->desc);
   if(!tsv) {
     dev_info("#------------------------------------------------------------\n");
-    dev_info("# %s\n", p->desc);
+    dev_info("# %.*s\n", strlen(descstr+1)-1, descstr+1);
     dev_info("#------------------------------------------------------------\n");
     if(p->parent_id && *p->parent_id)
       dev_info("\npart parent \"%s\"\n", p->parent_id);
@@ -448,9 +446,9 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
       dev_info("\npart\n");
   }
 
-  _if_partout(strcmp, "\"%s\"", desc);
-  _if_partout(strcmp, "\"%s\"", id);
-  _if_partout(strcmp, "\"%s\"", family_id);
+  _if_partout_str(strcmp, descstr, desc);
+  _if_partout_str(strcmp, cfg_escape(p->id), id);
+  _if_partout_str(strcmp, cfg_escape(p->family_id), family_id);
   _if_partout(intcmp, "%d", hvupdi_variant);
   _if_partout(intcmp, "0x%02x", stk500_devcode);
   _if_partout(intcmp, "0x%02x", avr910_devcode);
@@ -461,9 +459,13 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
   _if_partout(intcmp, "0x%04x", usbpid);
 
   if(!base || base->reset_disposition != p->reset_disposition)
-    _partout_str(strdup(p->reset_disposition == RESET_DEDICATED? "dedicated": p->reset_disposition == RESET_IO? "io": "unknown"), reset);
+    _partout_str(cfg_strdup("dev_part_strct()",
+       p->reset_disposition == RESET_DEDICATED? "dedicated": p->reset_disposition == RESET_IO? "io": "unknown"),
+       reset);
 
-  _if_partout_str(intcmp, strdup(p->retry_pulse == PIN_AVR_RESET? "reset": p->retry_pulse == PIN_AVR_SCK? "sck": "unknown"), retry_pulse);
+  _if_partout_str(intcmp, cfg_strdup("dev_part_strct()",
+     p->retry_pulse == PIN_AVR_RESET? "reset": p->retry_pulse == PIN_AVR_SCK? "sck": "unknown"),
+     retry_pulse);
 
   if(!base || base->flags != p->flags) {
     if(tsv) {
@@ -482,7 +484,9 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
 
       if(!base || (base->flags & (AVRPART_PARALLELOK | AVRPART_PSEUDOPARALLEL)) != (p->flags & (AVRPART_PARALLELOK | AVRPART_PSEUDOPARALLEL))) {
         int par = p->flags & (AVRPART_PARALLELOK | AVRPART_PSEUDOPARALLEL);
-        _partout_str(strdup(par == 0? "no": par == AVRPART_PSEUDOPARALLEL? "unknown": AVRPART_PARALLELOK? "yes": "pseudo"), parallel);
+        _partout_str(cfg_strdup("dev_part_strct()",
+          par == 0? "no": par == AVRPART_PSEUDOPARALLEL? "unknown": AVRPART_PARALLELOK? "yes": "pseudo"),
+          parallel);
       }
     }
   }
@@ -984,13 +988,16 @@ static void dev_pgm_strct(PROGRAMMER *pgm, bool tsv, PROGRAMMER *base) {
     if(!firstid)
       dev_info(", ");
     firstid = 0;
-    dev_info("\"%s\"", ldata(ln));
+    char *str = cfg_escape(ldata(ln));
+    dev_info("%s", str);
+    free(str);
   }
   dev_info(tsv? "\n": ";\n");
 
-  _if_pgmout(strcmp, "\"%s\"", desc);
+  _if_pgmout_str(strcmp, cfg_escape(pgm->desc), desc);
   _pgmout_fmt("type", "\"%s\"", locate_programmer_type_id(pgm->initpgm));
-  _pgmout_fmt("connection_type", "%s", connstr(pgm->conntype));
+  if(!base || base->conntype != pgm->conntype)
+    _pgmout_fmt("connection_type", "%s", connstr(pgm->conntype));
   _if_pgmout(intcmp, "%d", baudrate);
 
   _if_pgmout(intcmp, "0x%04x", usbvid);
@@ -1009,10 +1016,10 @@ static void dev_pgm_strct(PROGRAMMER *pgm, bool tsv, PROGRAMMER *base) {
     dev_info(tsv? "\n": ";\n");
   }
 
-  _if_pgmout(strcmp, "\"%s\"", usbdev);
-  _if_pgmout(strcmp, "\"%s\"", usbsn);
-  _if_pgmout(strcmp, "\"%s\"", usbvendor);
-  _if_pgmout(strcmp, "\"%s\"", usbproduct);
+  _if_pgmout_str(strcmp, cfg_escape(pgm->usbdev), usbdev);
+  _if_pgmout_str(strcmp, cfg_escape(pgm->usbsn), usbsn);
+  _if_pgmout_str(strcmp, cfg_escape(pgm->usbvendor), usbvendor);
+  _if_pgmout_str(strcmp, cfg_escape(pgm->usbproduct), usbproduct);
 
   for(int i=0; i<N_PINS; i++) {
     char *str = pins_to_strdup(pgm->pin+i);
diff --git a/src/developer_opts_private.h b/src/developer_opts_private.h
index e6be1ee9..a5c1562e 100644
--- a/src/developer_opts_private.h
+++ b/src/developer_opts_private.h
@@ -62,6 +62,7 @@ static int dev_message(int msglvl, const char *fmt, ...);
     dev_part_strct_entry(tsv, ".prog", id, NULL, #component, dev_sprintf(fmt, pgm->component)); \
 } while(0)
 
+// Result must be a malloc()ed string
 #define _if_pgmout_str(cmp, result, component) do { \
   if(!base || cmp(base->component, pgm->component)) \
     dev_part_strct_entry(tsv, ".prog", id, NULL, #component, result); \
@@ -80,14 +81,17 @@ static int dev_message(int msglvl, const char *fmt, ...);
     dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, dev_sprintf(fmt, p->component)); \
 } while(0)
 
+// Result must be a malloc()ed string
 #define _partout_str(result, component) \
   dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, result)
 
+// Result must be a malloc()ed string
 #define _if_partout_str(cmp, result, component) do { \
   if(!base || cmp(base->component, p->component)) \
     dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, result); \
 } while(0)
 
+// Result must be a malloc()ed string
 #define _if_n_partout_str(cmp, n, result, component) do { \
   if(!base || cmp(base->component, p->component, n)) \
     dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, result); \
@@ -102,30 +106,33 @@ static int dev_message(int msglvl, const char *fmt, ...);
     dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, dev_sprintf(fmt, m->component)); \
 } while(0)
 
+// Result must be a malloc()ed string
 #define _memout_str(result, component) \
   dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, result)
 
+// Result must be a malloc()ed string
 #define _if_n_memout_str(cmp, n, result, component) do { \
   if(!bm || cmp(bm->component, m->component, n)) \
     dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, result); \
 } while(0)
 
 #define _memout_yn(component) \
-  dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, strdup(m->component? "yes": "no"))
+  dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, cfg_strdup("_memout_yn()", m->component? "yes": "no"))
 
 #define _if_memout_yn(component) do { \
   if(!bm || bm->component != m->component) \
-    dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, strdup(m->component? "yes": "no")); \
+    dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, cfg_strdup("_if_memout_yn()", m->component? "yes": "no")); \
 } while(0)
 
 #define _flagout(mask, name) \
-  _partout_str(strdup(p->flags & (mask)? "yes": "no"), name)
+  _partout_str(cfg_strdup("_flagout()", p->flags & (mask)? "yes": "no"), name)
 
 #define _if_flagout(mask, name) do { \
   if(!base || (base->flags & (mask)) != (p->flags & (mask))) \
-    _partout_str(strdup(p->flags & (mask)? "yes": "no"), name); \
+    _partout_str(cfg_strdup("_if_flagout()", p->flags & (mask)? "yes": "no"), name); \
 } while(0)
 
+// Result must be a malloc()ed string
 #define _cmderr(result, component) \
   dev_part_strct_entry(tsv, ".cmderr", p->desc, m->desc, #component, result)
 
diff --git a/src/lexer.l b/src/lexer.l
index c55d7853..23fd2277 100644
--- a/src/lexer.l
+++ b/src/lexer.l
@@ -45,7 +45,6 @@ DIGIT    [0-9]
 HEXDIGIT [0-9a-fA-F]
 SIGN     [+-]
 
-%x strng
 %x incl
 %x comment
 %option nounput
@@ -61,12 +60,19 @@ SIGN     [+-]
 {SIGN}?{DIGIT}+"."{DIGIT}* { yylval = number_real(yytext); return TKN_NUMBER_REAL; }
 {SIGN}?"."{DIGIT}+         { yylval = number_real(yytext); return TKN_NUMBER_REAL; }
 
-"\""      { string_buf_ptr = string_buf; BEGIN(strng); }
+["]([^"\\\n]|\\.|\\\n)*["] {
+  char *str= cfg_strdup("lexer.l", yytext);
+  cfg_unescape(str, str+1);
+  size_t len = strlen(str);
+  if(len)
+    str[len-1] = 0;
+  yylval = string(str);
+  free(str);
+  return TKN_STRING;
+}
 
 0x{HEXDIGIT}+ { yylval = hexnumber(yytext); return TKN_NUMBER; }
 
-
-
 #   { /* The following captures all '#' style comments to end of line */
        BEGIN(comment); }
 <comment>[^\n]*\n+ { /* eat comments */
@@ -107,20 +113,6 @@ SIGN     [+-]
      }
 
 
-<strng>\" { *string_buf_ptr = 0; string_buf_ptr = string_buf;
-             yylval = string(string_buf_ptr); BEGIN(INITIAL); return TKN_STRING; }
-<strng>\\n  *string_buf_ptr++ = '\n';
-<strng>\\t  *string_buf_ptr++ = '\t';
-<strng>\\r  *string_buf_ptr++ = '\r';
-<strng>\\b  *string_buf_ptr++ = '\b';
-<strng>\\f  *string_buf_ptr++ = '\f';
-<strng>\\(.|\n)  *(string_buf_ptr++) = yytext[1];
-<strng>[^\\\n\"]+ { char *yptr = yytext; while (*yptr) 
-                                         *(string_buf_ptr++) = *(yptr++); }
-
-<strng>\n { yyerror("unterminated character constant");
-            return YYERRCODE; }
-
 alias            { yylval=NULL; return K_ALIAS; }
 allowfullpagebitstream { yylval=NULL; return K_ALLOWFULLPAGEBITSTREAM; }
 avr910_devcode   { yylval=NULL; return K_AVR910_DEVCODE; }
diff --git a/src/libavrdude.h b/src/libavrdude.h
index fb4a53dd..840d8fc1 100644
--- a/src/libavrdude.h
+++ b/src/libavrdude.h
@@ -536,7 +536,7 @@ const char * pins_to_str(const struct pindef_t * const pindef);
  * This function returns a string of defined pins, eg, ~1, 2, ~4, ~5, 7 or ""
  *
  * @param[in] pindef the pin definition for which we want the string representation
- * @returns a pointer to a string, which was created by strdup (NULL if out of memory)
+ * @returns a pointer to a string, which was created by strdup
  */
 char *pins_to_strdup(const struct pindef_t * const pindef);
 
@@ -1006,6 +1006,10 @@ extern double       default_bitclock;
 extern "C" {
 #endif
 
+void *cfg_malloc(const char *funcname, size_t n);
+
+char *cfg_strdup(const char *funcname, const char *s);
+
 int init_config(void);
 
 void cleanup_config(void);
@@ -1014,6 +1018,12 @@ int read_config(const char * file);
 
 const char *cache_string(const char *file);
 
+unsigned char *cfg_unescapeu(unsigned char *d, const unsigned char *s);
+
+char *cfg_unescape(char *d, const char *s);
+
+char *cfg_escape(const char *s);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/main.c b/src/main.c
index ab10b4fa..b696d6d7 100644
--- a/src/main.c
+++ b/src/main.c
@@ -922,10 +922,7 @@ int main(int argc, char * argv [])
                       progname,
                       (upd->op == DEVICE_READ)? 'r': (upd->op == DEVICE_WRITE)? 'w': 'v',
                       upd->filename, mtype);
-      if ((upd->memtype = strdup(mtype)) == NULL) {
-        avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
-        exit(1);
-      }
+      upd->memtype = cfg_strdup("main()", mtype);
     }
 
     if (!avr_mem_might_be_known(upd->memtype)) {
diff --git a/src/pgm.c b/src/pgm.c
index 238f61d8..07cbed57 100644
--- a/src/pgm.c
+++ b/src/pgm.c
@@ -67,14 +67,7 @@ PROGRAMMER * pgm_new(void)
   PROGRAMMER * pgm;
   const char *nulp = cache_string("");
 
-  pgm = (PROGRAMMER *)malloc(sizeof(*pgm));
-  if (pgm == NULL) {
-    avrdude_message(MSG_INFO, "%s: out of memory allocating programmer structure\n",
-            progname);
-    return NULL;
-  }
-
-  memset(pgm, 0, sizeof(*pgm));
+  pgm = (PROGRAMMER *) cfg_malloc("pgm_new()", sizeof(*pgm));
 
   pgm->id = lcreat(NULL, 0);
   pgm->usbpid = lcreat(NULL, 0);
@@ -162,24 +155,13 @@ PROGRAMMER * pgm_dup(const PROGRAMMER * const src)
   PROGRAMMER * pgm;
   LNODEID ln;
 
-  pgm = (PROGRAMMER *)malloc(sizeof(*pgm));
-  if (pgm == NULL) {
-    avrdude_message(MSG_INFO, "%s: out of memory allocating programmer structure\n",
-            progname);
-    return NULL;
-  }
-
+  pgm = (PROGRAMMER *) cfg_malloc("pgm_dup()", sizeof(*pgm));
   memcpy(pgm, src, sizeof(*pgm));
 
   pgm->id = lcreat(NULL, 0);
   pgm->usbpid = lcreat(NULL, 0);
   for (ln = lfirst(src->usbpid); ln; ln = lnext(ln)) {
-    int *ip = malloc(sizeof(int));
-    if (ip == NULL) {
-      avrdude_message(MSG_INFO, "%s: out of memory allocating programmer structure\n",
-              progname);
-      exit(1);
-    }
+    int *ip = cfg_malloc("pgm_dup()", sizeof(int));
     *ip = *(int *) ldata(ln);
     ladd(pgm->usbpid, ip);
   }
diff --git a/src/pindefs.c b/src/pindefs.c
index 625d1f18..2e78c8df 100644
--- a/src/pindefs.c
+++ b/src/pindefs.c
@@ -350,7 +350,7 @@ const char * pins_to_str(const struct pindef_t * const pindef) {
  * This function returns a string of defined pins, eg, ~1, 2, ~4, ~5, 7 or ""
  *
  * @param[in] pindef the pin definition for which we want the string representation
- * @returns a pointer to a string, which was created by strdup (NULL if out of memory)
+ * @returns a pointer to a string, which was created by strdup
  */
 char *pins_to_strdup(const struct pindef_t * const pindef) {
   char buf[6*(PIN_MAX+1)], *p = buf;
@@ -365,7 +365,7 @@ char *pins_to_strdup(const struct pindef_t * const pindef) {
     }
   }
 
-  return strdup(buf);
+  return cfg_strdup("pins_to_strdup()", buf);
 }
 
 /**
diff --git a/src/term.c b/src/term.c
index f02f95c5..672a09cd 100644
--- a/src/term.c
+++ b/src/term.c
@@ -20,7 +20,6 @@
 
 #include "ac_cfg.h"
 
-#include <ctype.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdint.h>
@@ -346,178 +345,6 @@ 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;
 
@@ -800,7 +627,7 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
           }
           // Strip start and end quotes, and unescape C string
           strncpy(s, argi+1, arglen-2);
-          unescape((unsigned char *) s, (unsigned char *) s);
+          cfg_unescape(s, s);
           if (*argi == '\'') { // Single C-style character
             if(*s && s[1])
               terminal_message(MSG_INFO, "%s (write): only using first character of %s\n",
diff --git a/src/update.c b/src/update.c
index 2aa2579b..972c3847 100644
--- a/src/update.c
+++ b/src/update.c
@@ -37,11 +37,7 @@ UPDATE * parse_op(char * s)
   int i;
   size_t fnlen;
 
-  upd = (UPDATE *)malloc(sizeof(UPDATE));
-  if (upd == NULL) {
-    avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
-    exit(1);
-  }
+  upd = (UPDATE *) cfg_malloc("parse_op()", sizeof(UPDATE));
 
   i = 0;
   p = s;
@@ -52,22 +48,12 @@ UPDATE * parse_op(char * s)
   if (*p != ':') {
     upd->memtype = NULL;        /* default memtype, "flash", or "application" */
     upd->op = DEVICE_WRITE;
-    upd->filename = (char *)malloc(strlen(buf) + 1);
-    if (upd->filename == NULL) {
-        avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
-        exit(1);
-    }
-    strcpy(upd->filename, buf);
+    upd->filename = cfg_strdup("parse_op()", buf);
     upd->format = FMT_AUTO;
     return upd;
   }
 
-  upd->memtype = (char *)malloc(strlen(buf)+1);
-  if (upd->memtype == NULL) {
-    avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
-    exit(1);
-  }
-  strcpy(upd->memtype, buf);
+  upd->memtype = cfg_strdup("parse_op()", buf);
 
   p++;
   if (*p == 'r') {
@@ -118,10 +104,10 @@ UPDATE * parse_op(char * s)
     // and to binary for read operations:
     upd->format = upd->op == DEVICE_READ? FMT_RBIN: FMT_AUTO;
     fnlen = strlen(cp);
-    upd->filename = (char *)malloc(fnlen + 1);
+    upd->filename = (char *) cfg_malloc("parse_op()", fnlen + 1);
   } else {
     fnlen = p - cp;
-    upd->filename = (char *)malloc(fnlen +1);
+    upd->filename = (char *) cfg_malloc("parse_op()", fnlen +1);
     c = *++p;
     if (c && p[1])
       /* More than one char - force failure below. */
@@ -147,12 +133,6 @@ UPDATE * parse_op(char * s)
     }
   }
 
-  if (upd->filename == NULL) {
-    avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
-    free(upd->memtype);
-    free(upd);
-    return NULL;
-  }
   memcpy(upd->filename, cp, fnlen);
   upd->filename[fnlen] = 0;
 
@@ -163,19 +143,15 @@ UPDATE * dup_update(UPDATE * upd)
 {
   UPDATE * u;
 
-  u = (UPDATE *)malloc(sizeof(UPDATE));
-  if (u == NULL) {
-    avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
-    exit(1);
-  }
+  u = (UPDATE *) cfg_malloc("dup_update()", sizeof(UPDATE));
 
   memcpy(u, upd, sizeof(UPDATE));
 
   if (upd->memtype != NULL)
-    u->memtype = strdup(upd->memtype);
+    u->memtype = cfg_strdup("dup_update()", upd->memtype);
   else
     u->memtype = NULL;
-  u->filename = strdup(upd->filename);
+  u->filename = cfg_strdup("dup_update()", upd->filename);
 
   return u;
 }
@@ -184,14 +160,10 @@ UPDATE * new_update(int op, char * memtype, int filefmt, char * filename)
 {
   UPDATE * u;
 
-  u = (UPDATE *)malloc(sizeof(UPDATE));
-  if (u == NULL) {
-    avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
-    exit(1);
-  }
+  u = (UPDATE *) cfg_malloc("new_update()", sizeof(UPDATE));
 
-  u->memtype = strdup(memtype);
-  u->filename = strdup(filename);
+  u->memtype = cfg_strdup("new_update()", memtype);
+  u->filename = cfg_strdup("new_update()", filename);
   u->op = op;
   u->format = filefmt;
 

From 7375477f7097b4cb835f4e17889fc0674bb1ed38 Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Tue, 9 Aug 2022 21:45:04 +0100
Subject: [PATCH 11/19] Replace string arrays with const char * and allocated
 space (part 1)

This commit deals with default_programmer, default_serial, default_parallel
and default_spi. The long term objective is to remove all fixed-size buffers
from the structures that lexer.l and config_gram.y deal with.
---
 src/config.c      |  8 ++++----
 src/config.h      |  2 --
 src/config_gram.y | 12 ++++--------
 src/libavrdude.h  |  8 ++++----
 src/main.c        | 17 +++++++++--------
 5 files changed, 21 insertions(+), 26 deletions(-)

diff --git a/src/config.c b/src/config.c
index 8d99848d..f0b65822 100644
--- a/src/config.c
+++ b/src/config.c
@@ -33,10 +33,10 @@
 
 #include "config_gram.h"
 
-char default_programmer[MAX_STR_CONST];
-char default_parallel[PATH_MAX];
-char default_serial[PATH_MAX];
-char default_spi[PATH_MAX];
+const char *default_programmer;
+const char *default_parallel;
+const char *default_serial;
+const char *default_spi;
 double default_bitclock;
 
 LISTID       string_list;
diff --git a/src/config.h b/src/config.h
index 260d404a..7b6c6d0d 100644
--- a/src/config.h
+++ b/src/config.h
@@ -30,8 +30,6 @@
 #endif
 
 
-#define MAX_STR_CONST 1024
-
 enum { V_NONE, V_NUM, V_NUM_REAL, V_STR };
 typedef struct value_t {
   int      type;
diff --git a/src/config_gram.y b/src/config_gram.y
index 0b9cf6cd..554de900 100644
--- a/src/config_gram.y
+++ b/src/config_gram.y
@@ -246,26 +246,22 @@ def :
   part_def TKN_SEMI |
 
   K_DEFAULT_PROGRAMMER TKN_EQUAL TKN_STRING TKN_SEMI {
-    strncpy(default_programmer, $3->value.string, MAX_STR_CONST);
-    default_programmer[MAX_STR_CONST-1] = 0;
+    default_programmer = cache_string($3->value.string);
     free_token($3);
   } |
 
   K_DEFAULT_PARALLEL TKN_EQUAL TKN_STRING TKN_SEMI {
-    strncpy(default_parallel, $3->value.string, PATH_MAX);
-    default_parallel[PATH_MAX-1] = 0;
+    default_parallel = cache_string($3->value.string);
     free_token($3);
   } |
 
   K_DEFAULT_SERIAL TKN_EQUAL TKN_STRING TKN_SEMI {
-    strncpy(default_serial, $3->value.string, PATH_MAX);
-    default_serial[PATH_MAX-1] = 0;
+    default_serial = cache_string($3->value.string);
     free_token($3);
   } |
 
   K_DEFAULT_SPI TKN_EQUAL TKN_STRING TKN_SEMI {
-    strncpy(default_spi, $3->value.string, PATH_MAX);
-    default_spi[PATH_MAX-1] = 0;
+    default_spi = cache_string($3->value.string);
     free_token($3);
   } |
 
diff --git a/src/libavrdude.h b/src/libavrdude.h
index 840d8fc1..3c2f2a57 100644
--- a/src/libavrdude.h
+++ b/src/libavrdude.h
@@ -992,10 +992,10 @@ void walk_programmer_types(/*LISTID programmer_types,*/ walk_programmer_types_cb
 
 extern LISTID       part_list;
 extern LISTID       programmers;
-extern char         default_programmer[];
-extern char         default_parallel[];
-extern char         default_serial[];
-extern char         default_spi[];
+extern const char *default_programmer;
+extern const char *default_parallel;
+extern const char *default_serial;
+extern const char *default_spi;
 extern double       default_bitclock;
 
 /* This name is fixed, it's only here for symmetry with
diff --git a/src/main.c b/src/main.c
index b696d6d7..08266f00 100644
--- a/src/main.c
+++ b/src/main.c
@@ -314,10 +314,11 @@ int main(int argc, char * argv [])
   else
     progname = argv[0];
 
-  default_parallel[0] = 0;
-  default_serial[0]   = 0;
-  default_spi[0]      = 0;
-  default_bitclock    = 0.0;
+  default_programmer = "";
+  default_parallel   = "";
+  default_serial     = "";
+  default_spi        = "";
+  default_bitclock   = 0.0;
 
   init_config();
 
@@ -351,7 +352,7 @@ int main(int argc, char * argv [])
   quell_progress = 0;
   exitspecs     = NULL;
   pgm           = NULL;
-  programmer    = default_programmer;
+  programmer    = cfg_strdup("main()", default_programmer);
   verbose       = 0;
   baudrate      = 0;
   bitclock      = 0.0;
@@ -755,7 +756,7 @@ int main(int argc, char * argv [])
   int dev_opts = 0;
   // Developer option -c <wildcard>/[ASsrt] prints programmer description(s) and exits
   if(programmer && (strcmp(programmer, "*") == 0 || strchr(programmer, '/'))) {
-    dev_output_pgm_defs(programmer);
+    dev_output_pgm_defs(cfg_strdup("main()", programmer));
     dev_opts = 1;
   }
   // Developer option -p <wildcard>/[dASsrcow*t] prints part description(s) and exits
@@ -849,11 +850,11 @@ int main(int argc, char * argv [])
     switch (pgm->conntype)
     {
       case CONNTYPE_PARALLEL:
-        port = default_parallel;
+        port = cfg_strdup("main()", default_parallel);
         break;
 
       case CONNTYPE_SERIAL:
-        port = default_serial;
+        port = cfg_strdup("main()", default_serial);
         break;
 
       case CONNTYPE_USB:

From f4c5a8350d9c13aef4c2d085ca9fc9be3b570b46 Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Wed, 10 Aug 2022 16:14:56 +0100
Subject: [PATCH 12/19] Replace string arrays with const char * and allocated
 space (part 2)

This commit replaces fixed-string buffers in PROGRAMMER, AVRPART and AVRMEM
that are dealt with by the parser and grammar. Now, string assignments are
always to const char *, ie, these are read-only strings with arbitrary
length.

config_gram.y now only needs to consider one type of string assignment.

This commit also

  - Replaces the simple linear-search cache_string() function with faster
    hashed cache_string(). Either way, the returned value is likely to be
    shared, so should never be free()'d.

  - Duplicates hvupdi_support list in pgm_dup() and frees it in pgm_free()

  - Adds const qualifier to some function args in avrpart.c and pgm.c

  - Hardens some functions against being called with NULL pointers

  - Ensures _new() and _dup() functions for parts, programmers and memory
    return a suitable memory. Out of memory triggers exit in one of three
    functions, cfg_malloc(), cfg_realloc() and cfg_strdup(); there is
    rarely anything useful that AVRDUDE or, for that matter, any
    application compiled against libavrdude can do once you run out of
    memory as AVRDUDE/libavrdude rely heavily on allocation of memory.
---
 src/avrpart.c        | 408 ++++++++++++++++---------------------------
 src/config.c         |  71 +++++---
 src/config.h         |   2 +-
 src/config_gram.y    |  54 +-----
 src/developer_opts.c |  59 ++++---
 src/jtagmkI.c        |   2 +-
 src/jtagmkII.c       |   2 +-
 src/lexer.l          |  10 +-
 src/libavrdude.h     |  56 +++---
 src/main.c           |  10 +-
 src/pgm.c            | 137 +++++++--------
 src/pickit2.c        |  20 ++-
 src/update.c         |  10 +-
 13 files changed, 359 insertions(+), 482 deletions(-)

diff --git a/src/avrpart.c b/src/avrpart.c
index 40baa449..526d0503 100644
--- a/src/avrpart.c
+++ b/src/avrpart.c
@@ -1,4 +1,3 @@
-
 /*
  * avrdude - A Downloader/Uploader for AVR device programmers
  * Copyright (C) 2000-2004  Brian S. Dean <bsd@bsdhome.com>
@@ -31,44 +30,23 @@
  *** Elementary functions dealing with OPCODE structures
  ***/
 
-OPCODE * avr_new_opcode(void)
-{
-  OPCODE * m;
-
-  m = (OPCODE *)malloc(sizeof(*m));
-  if (m == NULL) {
-    avrdude_message(MSG_INFO, "avr_new_opcode(): out of memory\n");
-    exit(1);
-  }
-
-  memset(m, 0, sizeof(*m));
-
-  return m;
+OPCODE *avr_new_opcode(void) {
+  return (OPCODE *) cfg_malloc("avr_new_opcode()", sizeof(OPCODE));
 }
 
-static OPCODE * avr_dup_opcode(OPCODE * op)
-{
-  OPCODE * m;
-  
-  /* this makes life easier */
-  if (op == NULL) {
+static OPCODE *avr_dup_opcode(const OPCODE *op) {
+  if(op == NULL)                // Caller wants NULL if op == NULL
     return NULL;
-  }
-
-  m = (OPCODE *)malloc(sizeof(*m));
-  if (m == NULL) {
-    avrdude_message(MSG_INFO, "avr_dup_opcode(): out of memory\n");
-    exit(1);
-  }
 
+  OPCODE *m = (OPCODE *) cfg_malloc("avr_dup_opcode()", sizeof(*m));
   memcpy(m, op, sizeof(*m));
 
   return m;
 }
 
-void avr_free_opcode(OPCODE * op)
-{
-  free(op);
+void avr_free_opcode(OPCODE *op) {
+  if(op)
+    free(op);
 }
 
 
@@ -268,11 +246,10 @@ int avr_set_input(OPCODE * op, unsigned char * cmd, unsigned char data)
 /*
  * avr_get_output()
  *
- * Retreive output data bits from the command results based on the
+ * Retrieve output data bits from the command results based on the
  * opcode data.
  */
-int avr_get_output(OPCODE * op, unsigned char * res, unsigned char * data)
-{
+int avr_get_output(const OPCODE *op, const unsigned char *res, unsigned char *data) {
   int i, j, bit;
   unsigned char value;
   unsigned char mask;
@@ -301,8 +278,7 @@ int avr_get_output(OPCODE * op, unsigned char * res, unsigned char * data)
  * Calculate the byte number of the output data based on the
  * opcode data.
  */
-int avr_get_output_index(OPCODE * op)
-{
+int avr_get_output_index(const OPCODE *op) {
   int i, j;
 
   for (i=0; i<32; i++) {
@@ -353,34 +329,17 @@ static char * bittype(int type)
  *** Elementary functions dealing with AVRMEM structures
  ***/
 
-AVRMEM * avr_new_memtype(void)
-{
-  AVRMEM * m;
-
-  m = (AVRMEM *)malloc(sizeof(*m));
-  if (m == NULL) {
-    avrdude_message(MSG_INFO, "avr_new_memtype(): out of memory\n");
-    exit(1);
-  }
-
-  memset(m, 0, sizeof(*m));
+AVRMEM *avr_new_memtype(void) {
+  AVRMEM *m = (AVRMEM *) cfg_malloc("avr_new_memtype()", sizeof(*m));
+  m->desc = cache_string("");
   m->page_size = 1; // ensure not 0
 
   return m;
 }
 
-AVRMEM_ALIAS * avr_new_memalias(void)
-{
-  AVRMEM_ALIAS * m;
-
-  m = (AVRMEM_ALIAS *)malloc(sizeof(*m));
-  if (m == NULL) {
-    avrdude_message(MSG_INFO, "avr_new_memalias(): out of memory\n");
-    exit(1);
-  }
-
-  memset(m, 0, sizeof(*m));
-
+AVRMEM_ALIAS *avr_new_memalias(void) {
+  AVRMEM_ALIAS *m = (AVRMEM_ALIAS *) cfg_malloc("avr_new_memalias()", sizeof*m);
+  m->desc = cache_string("");
   return m;
 }
 
@@ -389,106 +348,79 @@ AVRMEM_ALIAS * avr_new_memalias(void)
  * Allocate and initialize memory buffers for each of the device's
  * defined memory regions.
  */
-int avr_initmem(AVRPART * p)
-{
-  LNODEID ln;
-  AVRMEM * m;
+int avr_initmem(const AVRPART *p) {
+  if(p == NULL || p->mem == NULL)
+    return -1;
 
-  for (ln=lfirst(p->mem); ln; ln=lnext(ln)) {
-    m = ldata(ln);
-    m->buf = (unsigned char *) malloc(m->size);
-    if (m->buf == NULL) {
-      avrdude_message(MSG_INFO, "%s: can't alloc buffer for %s size of %d bytes\n",
-              progname, m->desc, m->size);
-      return -1;
-    }
-    m->tags = (unsigned char *) malloc(m->size);
-    if (m->tags == NULL) {
-      avrdude_message(MSG_INFO, "%s: can't alloc buffer for %s size of %d bytes\n",
-              progname, m->desc, m->size);
-      return -1;
-    }
+  for (LNODEID ln=lfirst(p->mem); ln; ln=lnext(ln)) {
+    AVRMEM *m = ldata(ln);
+    m->buf  = (unsigned char *) cfg_malloc("avr_initmem()", m->size);
+    m->tags = (unsigned char *) cfg_malloc("avr_initmem()", m->size);
   }
 
   return 0;
 }
 
 
-AVRMEM * avr_dup_mem(AVRMEM * m)
-{
-  AVRMEM * n;
-  int i;
+AVRMEM *avr_dup_mem(const AVRMEM *m) {
+  AVRMEM *n = avr_new_memtype();
 
-  n = avr_new_memtype();
+  if(m) {
+    *n = *m;
 
-  *n = *m;
-
-  if (m->buf != NULL) {
-    n->buf = (unsigned char *)malloc(n->size);
-    if (n->buf == NULL) {
-      avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
-                      n->size);
-      exit(1);
+    if(m->buf) {
+      n->buf = (unsigned char *) cfg_malloc("avr_dup_mem()", n->size);
+      memcpy(n->buf, m->buf, n->size);
     }
-    memcpy(n->buf, m->buf, n->size);
-  }
 
-  if (m->tags != NULL) {
-    n->tags = (unsigned char *)malloc(n->size);
-    if (n->tags == NULL) {
-      avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
-                      n->size);
-      exit(1);
+    if(m->tags) {
+      n->tags = (unsigned char *) cfg_malloc("avr_dup_mem()", n->size);
+      memcpy(n->tags, m->tags, n->size);
     }
-    memcpy(n->tags, m->tags, n->size);
-  }
 
-  for (i = 0; i < AVR_OP_MAX; i++) {
-    n->op[i] = avr_dup_opcode(n->op[i]);
+    for(int i = 0; i < AVR_OP_MAX; i++)
+      n->op[i] = avr_dup_opcode(n->op[i]);
   }
 
   return n;
 }
 
-AVRMEM_ALIAS * avr_dup_memalias(AVRMEM_ALIAS * m)
-{
-  AVRMEM_ALIAS * n;
+AVRMEM_ALIAS *avr_dup_memalias(const AVRMEM_ALIAS *m) {
+  AVRMEM_ALIAS *n = avr_new_memalias();
 
-  n = avr_new_memalias();
-
-  *n = *m;
+  if(m)
+    *n = *m;
 
   return n;
 }
 
-void avr_free_mem(AVRMEM * m)
-{
-    if (m->buf != NULL) {
-      free(m->buf);
-      m->buf = NULL;
-    }
-    if (m->tags != NULL) {
-      free(m->tags);
-      m->tags = NULL;
-    }
-    for(size_t i=0; i<sizeof(m->op)/sizeof(m->op[0]); i++)
-    {
-      if (m->op[i] != NULL)
-      {
-        avr_free_opcode(m->op[i]);
-        m->op[i] = NULL;
-      }
-    }
-    free(m);
-}
+void avr_free_mem(AVRMEM * m) {
+  if(m == NULL)
+    return;
 
-void avr_free_memalias(AVRMEM_ALIAS * m)
-{
+  if(m->buf) {
+    free(m->buf);
+    m->buf = NULL;
+  }
+  if(m->tags) {
+    free(m->tags);
+    m->tags = NULL;
+  }
+  for(size_t i=0; i<sizeof(m->op)/sizeof(m->op[0]); i++) {
+    if(m->op[i]) {
+      avr_free_opcode(m->op[i]);
+      m->op[i] = NULL;
+    }
+  }
   free(m);
 }
 
-AVRMEM_ALIAS * avr_locate_memalias(AVRPART * p, const char * desc)
-{
+void avr_free_memalias(AVRMEM_ALIAS *m) {
+  if(m)
+    free(m);
+}
+
+AVRMEM_ALIAS *avr_locate_memalias(const AVRPART *p, const char *desc) {
   AVRMEM_ALIAS * m, * match;
   LNODEID ln;
   int matches;
@@ -514,8 +446,7 @@ AVRMEM_ALIAS * avr_locate_memalias(AVRPART * p, const char * desc)
   return NULL;
 }
 
-AVRMEM * avr_locate_mem_noalias(AVRPART * p, const char * desc)
-{
+AVRMEM *avr_locate_mem_noalias(const AVRPART *p, const char *desc) {
   AVRMEM * m, * match;
   LNODEID ln;
   int matches;
@@ -542,8 +473,7 @@ AVRMEM * avr_locate_mem_noalias(AVRPART * p, const char * desc)
 }
 
 
-AVRMEM * avr_locate_mem(AVRPART * p, const char * desc)
-{
+AVRMEM *avr_locate_mem(const AVRPART *p, const char *desc) {
   AVRMEM * m, * match;
   AVRMEM_ALIAS * alias;
   LNODEID ln;
@@ -577,24 +507,20 @@ AVRMEM * avr_locate_mem(AVRPART * p, const char * desc)
   return NULL;
 }
 
-AVRMEM_ALIAS * avr_find_memalias(AVRPART * p, AVRMEM * m_orig)
-{
-  AVRMEM_ALIAS * m;
-  LNODEID ln;
-
-  for (ln=lfirst(p->mem_alias); ln; ln=lnext(ln)) {
-    m = ldata(ln);
-    if (m->aliased_mem == m_orig)
-      return m;
-  }
+AVRMEM_ALIAS *avr_find_memalias(const AVRPART *p, const AVRMEM *m_orig) {
+  if(p && p->mem_alias && m_orig)
+    for(LNODEID ln=lfirst(p->mem_alias); ln; ln=lnext(ln)) {
+      AVRMEM_ALIAS *m = ldata(ln);
+      if(m->aliased_mem == m_orig)
+        return m;
+    }
 
   return NULL;
 }
 
 
-void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, AVRPART * p,
-                     int type, int verbose)
-{
+void avr_mem_display(const char *prefix, FILE *f, const AVRMEM *m,
+                     const AVRPART *p, int verbose) {
   static unsigned int prev_mem_offset;
   static int prev_mem_size;
   int i, j;
@@ -623,7 +549,7 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, AVRPART * p,
       AVRMEM_ALIAS *ap = avr_find_memalias(p, m);
       /* Show alias if the current and the next memory section has the same offset
       and size, we're not out of band and a family_id is present */
-      char * mem_desc_alias = ap? ap->desc: "";
+      const char *mem_desc_alias = ap? ap->desc: "";
       fprintf(f,
               "%s%-11s %-8s %4d %5d %5d %4d %-6s %6d %4d %6d %5d %5d 0x%02x 0x%02x\n",
               prefix,
@@ -669,74 +595,63 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, AVRPART * p,
  * Elementary functions dealing with AVRPART structures
  */
 
-AVRPART * avr_new_part(void)
-{
-  AVRPART * p;
+AVRPART *avr_new_part(void) {
+  AVRPART *p = (AVRPART *) cfg_malloc("avr_new_part()", sizeof(AVRPART));
   const char *nulp = cache_string("");
 
-  p = (AVRPART *)malloc(sizeof(AVRPART));
-  if (p == NULL) {
-    avrdude_message(MSG_INFO, "new_part(): out of memory\n");
-    exit(1);
-  }
-
   memset(p, 0, sizeof(*p));
 
-  p->id[0]   = 0;
-  p->desc[0] = 0;
+  // Initialise const char * and LISTID entities
+  p->desc = nulp;
+  p->id = nulp;
+  p->parent_id = nulp;
+  p->family_id = nulp;
+  p->config_file = nulp;
+  p->mem = lcreat(NULL, 0);
+  p->mem_alias = lcreat(NULL, 0);
+
+  // Default values
+  p->hvupdi_variant = -1;
+  memset(p->signature, 0xFF, 3);
   p->reset_disposition = RESET_DEDICATED;
   p->retry_pulse = PIN_AVR_SCK;
   p->flags = AVRPART_SERIALOK | AVRPART_PARALLELOK | AVRPART_ENABLEPAGEPROGRAMMING;
-  p->parent_id = nulp;
-  p->config_file = nulp;
-  p->lineno = 0;
-  memset(p->signature, 0xFF, 3);
   p->ctl_stack_type = CTL_STACK_NONE;
   p->ocdrev = -1;
-  p->hvupdi_variant = -1;
-
-  p->mem = lcreat(NULL, 0);
-  p->mem_alias = lcreat(NULL, 0);
+  p->lineno = 0;
 
   return p;
 }
 
 
-AVRPART * avr_dup_part(AVRPART * d)
-{
-  AVRPART * p;
-  LISTID save, save2;
-  LNODEID ln, ln2;
-  int i;
+AVRPART *avr_dup_part(const AVRPART *d) {
+  AVRPART *p = avr_new_part();
 
-  p = avr_new_part();
-  save = p->mem;
-  save2 = p->mem_alias;
+  if(d) {
+    *p = *d;
 
-  *p = *d;
+    // Duplicate the memory and alias chains
+    p->mem = lcreat(NULL, 0);
+    p->mem_alias = lcreat(NULL, 0);
 
-  p->mem = save;
-  p->mem_alias = save2;
-  for (ln=lfirst(d->mem); ln; ln=lnext(ln)) {
-    AVRMEM *m = ldata(ln);
-    AVRMEM *m2 = avr_dup_mem(m);
-    ladd(p->mem, m2);
-    // see if there is any alias for it
-    for (ln2=lfirst(d->mem_alias); ln2; ln2=lnext(ln2)) {
-      AVRMEM_ALIAS *a = ldata(ln2);
-      if (a->aliased_mem == m) {
-        // yes, duplicate it
-        AVRMEM_ALIAS *a2 = avr_dup_memalias(a);
-        // ... adjust the pointer ...
-        a2->aliased_mem = m2;
-        // ... and add to new list
-        ladd(p->mem_alias, a2);
+    for(LNODEID ln=lfirst(d->mem); ln; ln=lnext(ln)) {
+      AVRMEM *m = ldata(ln);
+      AVRMEM *m2 = avr_dup_mem(m);
+      ladd(p->mem, m2);
+      // See if there is any alias for it
+      for(LNODEID ln2=lfirst(d->mem_alias); ln2; ln2=lnext(ln2)) {
+        AVRMEM_ALIAS *a = ldata(ln2);
+        if (a->aliased_mem == m) {
+          // Yes, duplicate it, adjust the pointer and add to new list
+          AVRMEM_ALIAS *a2 = avr_dup_memalias(a);
+          a2->aliased_mem = m2;
+          ladd(p->mem_alias, a2);
+        }
       }
     }
-  }
 
-  for (i = 0; i < AVR_OP_MAX; i++) {
-    p->op[i] = avr_dup_opcode(p->op[i]);
+    for(int i = 0; i < AVR_OP_MAX; i++)
+      p->op[i] = avr_dup_opcode(p->op[i]);
   }
 
   return p;
@@ -758,61 +673,45 @@ void avr_free_part(AVRPART * d)
   free(d);
 }
 
-AVRPART * locate_part(LISTID parts, const char * partdesc)
-{
-  LNODEID ln1;
+AVRPART *locate_part(const LISTID parts, const char *partdesc) {
   AVRPART * p = NULL;
-  int found;
+  int found = 0;
 
   if(!parts || !partdesc)
     return NULL;
 
-  found = 0;
-
-  for (ln1=lfirst(parts); ln1 && !found; ln1=lnext(ln1)) {
+  for (LNODEID ln1=lfirst(parts); ln1 && !found; ln1=lnext(ln1)) {
     p = ldata(ln1);
     if ((strcasecmp(partdesc, p->id) == 0) ||
         (strcasecmp(partdesc, p->desc) == 0))
       found = 1;
   }
 
-  if (found)
-    return p;
-
-  return NULL;
+  return found? p: NULL;
 }
 
-AVRPART * locate_part_by_avr910_devcode(LISTID parts, int devcode)
-{
-  LNODEID ln1;
-  AVRPART * p = NULL;
-
-  for (ln1=lfirst(parts); ln1; ln1=lnext(ln1)) {
-    p = ldata(ln1);
-    if (p->avr910_devcode == devcode)
-      return p;
-  }
-
-  return NULL;
-}
-
-AVRPART * locate_part_by_signature(LISTID parts, unsigned char * sig,
-                                   int sigsize)
-{
-  LNODEID ln1;
-  AVRPART * p = NULL;
-  int i;
-
-  if (sigsize == 3) {
-    for (ln1=lfirst(parts); ln1; ln1=lnext(ln1)) {
-      p = ldata(ln1);
-      for (i=0; i<3; i++)
-        if (p->signature[i] != sig[i])
-          break;
-      if (i == 3)
+AVRPART *locate_part_by_avr910_devcode(const LISTID parts, int devcode) {
+  if(parts)
+    for (LNODEID ln1=lfirst(parts); ln1; ln1=lnext(ln1)) {
+      AVRPART * p = ldata(ln1);
+      if (p->avr910_devcode == devcode)
+        return p;
+    }
+
+  return NULL;
+}
+
+AVRPART *locate_part_by_signature(const LISTID parts, unsigned char *sig, int sigsize) {
+  if(parts && sigsize == 3)
+    for(LNODEID ln1=lfirst(parts); ln1; ln1=lnext(ln1)) {
+      AVRPART *p = ldata(ln1);
+      int i;
+      for(i=0; i<3; i++)
+        if(p->signature[i] != sig[i])
+          break;
+      if(i == 3)
         return p;
     }
-  }
 
   return NULL;
 }
@@ -841,12 +740,11 @@ void walk_avrparts(LISTID avrparts, walk_avrparts_cb cb, void *cookie)
 /*
  * Compare function to sort the list of programmers
  */
-static int sort_avrparts_compare(AVRPART * p1,AVRPART * p2)
-{
-  if(p1 == NULL || p2 == NULL) {
+static int sort_avrparts_compare(const AVRPART *p1, const AVRPART *p2) {
+  if(p1 == NULL || p1->desc == NULL || p2 == NULL || p2->desc == NULL)
     return 0;
-  }
-  return strncasecmp(p1->desc,p2->desc,AVR_DESCLEN);
+
+  return strcasecmp(p1->desc, p2->desc);
 }
 
 /*
@@ -868,9 +766,7 @@ static char * reset_disp_str(int r)
 }
 
 
-void avr_display(FILE * f, AVRPART * p, const char * prefix, int verbose)
-{
-  int i;
+void avr_display(FILE *f, const AVRPART *p, const char *prefix, int verbose) {
   char * buf;
   const char * px;
   LNODEID ln;
@@ -905,23 +801,17 @@ void avr_display(FILE * f, AVRPART * p, const char * prefix, int verbose)
   fprintf(  f, "%sMemory Detail                 :\n\n", prefix);
 
   px = prefix;
-  i = strlen(prefix) + 5;
-  buf = (char *)malloc(i);
-  if (buf == NULL) {
-    /* ugh, this is not important enough to bail, just ignore it */
-  }
-  else {
-    strcpy(buf, prefix);
-    strcat(buf, "  ");
-    px = buf;
-  }
+  buf = (char *)cfg_malloc("avr_display()", strlen(prefix) + 5);
+  strcpy(buf, prefix);
+  strcat(buf, "  ");
+  px = buf;
+
+  if (verbose <= 2)
+    avr_mem_display(px, f, NULL, p, verbose);
 
-  if (verbose <= 2) {
-    avr_mem_display(px, f, NULL, p, 0, verbose);
-  }
   for (ln=lfirst(p->mem); ln; ln=lnext(ln)) {
     m = ldata(ln);
-    avr_mem_display(px, f, m, p, i, verbose);
+    avr_mem_display(px, f, m, p, verbose);
   }
 
   if (buf)
diff --git a/src/config.c b/src/config.c
index f0b65822..ac712b17 100644
--- a/src/config.c
+++ b/src/config.c
@@ -83,13 +83,24 @@ int init_config(void)
 void *cfg_malloc(const char *funcname, size_t n) {
   void *ret = malloc(n);
   if(!ret) {
-    avrdude_message(MSG_INFO, "%s: out of memory in %s\n", progname, funcname);
+    avrdude_message(MSG_INFO, "%s: out of memory in %s (needed %lu bytes)\n", progname, funcname, (unsigned long) n);
     exit(1);
   }
   memset(ret, 0, n);
   return ret;
 }
 
+void *cfg_realloc(const char *funcname, void *p, size_t n) {
+  void *ret;
+
+  if(!(ret = p? realloc(p, n): calloc(1, n))) {
+    avrdude_message(MSG_INFO, "%s: out of memory in %s (needed %lu bytes)\n", progname, funcname, (unsigned long) n);
+    exit(1);
+  }
+
+  return ret;
+}
+
 
 char *cfg_strdup(const char *funcname, const char *s) {
   char *ret = strdup(s);
@@ -323,35 +334,45 @@ int read_config(const char * file)
 }
 
 
-// Linear-search cache for a few often-referenced strings
-const char *cache_string(const char *file) {
-  static char **fnames;
-  static int n=0;
+// Adapted version of a neat empirical hash function from comp.lang.c by Daniel Bernstein
+unsigned strhash(const char *str) {
+  unsigned c, hash = 5381, n = 0;
 
-  if(!file)
-    return NULL;
+  while((c = (unsigned char) *str++) && n++ < 20)
+    hash = 33*hash ^ c;
 
-  // Exists in cache?
-  for(int i=0; i<n; i++)
-    if(strcmp(fnames[i], file) == 0)
-      return fnames[i];
+  return hash;
+}
 
-  // Expand cache?
-  if(n%128 == 0) {
-    if(!(fnames = realloc(fnames, (n+128)*sizeof*fnames))) {
-      yyerror("cache_string(): out of memory");
-      return NULL;
-    }
-  }
 
-  fnames[n] = cfg_strdup("cache_string()", file);
+static char **hstrings[1<<12];
 
-  return fnames[n++];
+// Return a copy of the argument as hashed string
+const char *cache_string(const char *p) {
+  int h, k;
+  char **hs;
+
+  if(!p)
+    p = "(NULL)";
+
+  h = strhash(p) % (sizeof hstrings/sizeof*hstrings);
+  if(!(hs=hstrings[h]))
+    hs = hstrings[h] = (char **) cfg_realloc("cache_string()", NULL, (16+1)*sizeof**hstrings);
+
+  for(k=0; hs[k]; k++)
+    if(*p == *hs[k] && !strcmp(p, hs[k]))
+      return hs[k];
+
+  if(k && k%16 == 0)
+    hstrings[h] = (char **) cfg_realloc("cache_string()", hstrings[h], (k+16+1)*sizeof**hstrings);
+
+  hstrings[h][k+1]=NULL;
+
+  return hstrings[h][k] = cfg_strdup("cache_string()", p);
 }
 
 // Captures comments during parsing
-int capture_comment_char(int c) {
-  return c;
+void capture_comment_str(const char *p) {
 }
 
 
@@ -426,6 +447,12 @@ unsigned char *cfg_unescapeu(unsigned char *d, const unsigned char *s) {
     switch (*s) {
     case '\\':
       switch (*++s) {
+      case '\n':                // String continuation over new line
+#if '\n' != '\r'
+      case '\r':
+#endif
+        --d;
+        break;
       case 'n':
         *d = '\n';
         break;
diff --git a/src/config.h b/src/config.h
index 7b6c6d0d..afd39b17 100644
--- a/src/config.h
+++ b/src/config.h
@@ -94,7 +94,7 @@ void print_token(TOKEN *tkn);
 
 void pyytext(void);
 
-int capture_comment_char(int c);
+void capture_comment_str(const char *str);
 
 #ifdef __cplusplus
 }
diff --git a/src/config_gram.y b/src/config_gram.y
index 554de900..2a027c7f 100644
--- a/src/config_gram.y
+++ b/src/config_gram.y
@@ -308,10 +308,6 @@ prog_def :
 prog_decl :
   K_PROGRAMMER
     { current_prog = pgm_new();
-      if (current_prog == NULL) {
-        yyerror("could not create pgm instance");
-        YYABORT;
-      }
       current_prog->config_file = cache_string(cfg_infile);
       current_prog->lineno = cfg_lineno;
     }
@@ -325,11 +321,6 @@ prog_decl :
         YYABORT;
       }
       current_prog = pgm_dup(pgm);
-      if (current_prog == NULL) {
-        yyerror("could not duplicate pgm instance");
-        free_token($3);
-        YYABORT;
-      }
       current_prog->parent_id = cache_string($3->value.string);
       current_prog->config_file = cache_string(cfg_infile);
       current_prog->lineno = cfg_lineno;
@@ -400,10 +391,6 @@ part_decl :
   K_PART
     {
       current_part = avr_new_part();
-      if (current_part == NULL) {
-        yyerror("could not create part instance");
-        YYABORT;
-      }
       current_part->config_file = cache_string(cfg_infile);
       current_part->lineno = cfg_lineno;
     } |
@@ -417,11 +404,6 @@ part_decl :
       }
 
       current_part = avr_dup_part(parent_part);
-      if (current_part == NULL) {
-        yyerror("could not duplicate part instance");
-        free_token($3);
-        YYABORT;
-      }
       current_part->parent_id = cache_string($3->value.string);
       current_part->config_file = cache_string(cfg_infile);
       current_part->lineno = cfg_lineno;
@@ -465,8 +447,7 @@ prog_parm :
   prog_parm_conntype
   |
   K_DESC TKN_EQUAL TKN_STRING {
-    strncpy(current_prog->desc, $3->value.string, PGM_DESCLEN);
-    current_prog->desc[PGM_DESCLEN-1] = 0;
+    current_prog->desc = cache_string($3->value.string);
     free_token($3);
   } |
   K_BAUDRATE TKN_EQUAL TKN_NUMBER {
@@ -686,22 +667,19 @@ retry_lines :
 part_parm :
   K_ID TKN_EQUAL TKN_STRING 
     {
-      strncpy(current_part->id, $3->value.string, AVR_IDLEN);
-      current_part->id[AVR_IDLEN-1] = 0;
+      current_part->id = cache_string($3->value.string);
       free_token($3);
     } |
 
   K_DESC TKN_EQUAL TKN_STRING 
     {
-      strncpy(current_part->desc, $3->value.string, AVR_DESCLEN - 1);
-      current_part->desc[AVR_DESCLEN-1] = 0;
+      current_part->desc = cache_string($3->value.string);
       free_token($3);
     } |
 
   K_FAMILY_ID TKN_EQUAL TKN_STRING
     {
-      strncpy(current_part->family_id, $3->value.string, AVR_FAMILYIDLEN);
-      current_part->family_id[AVR_FAMILYIDLEN] = 0;
+      current_part->family_id = cache_string($3->value.string);
       free_token($3);
     } |
 
@@ -1289,13 +1267,8 @@ part_parm :
     { /* select memory for extension or create if not there */
       AVRMEM *mem = avr_locate_mem_noalias(current_part, $2->value.string);
       if(!mem) {
-        if(!(mem = avr_new_memtype())) {
-          yyerror("could not create mem instance");
-          free_token($2);
-          YYABORT;
-        }
-        strncpy(mem->desc, $2->value.string, AVR_MEMDESCLEN - 1);
-        mem->desc[AVR_MEMDESCLEN-1] = 0;
+        mem = avr_new_memtype();
+        mem->desc = cache_string($2->value.string);
         ladd(current_part->mem, mem);
       }
       avr_add_mem_order($2->value.string);
@@ -1339,11 +1312,6 @@ part_parm :
       opnum = which_opcode($1);
       if (opnum < 0) YYABORT;
       op = avr_new_opcode();
-      if (op == NULL) {
-        yyerror("could not create opcode instance");
-        free_token($1);
-        YYABORT;
-      }
       if(0 != parse_cmdbits(op, opnum))
         YYABORT;
       if (current_part->op[opnum] != NULL) {
@@ -1500,11 +1468,6 @@ mem_spec :
       opnum = which_opcode($1);
       if (opnum < 0) YYABORT;
       op = avr_new_opcode();
-      if (op == NULL) {
-        yyerror("could not create opcode instance");
-        free_token($1);
-        YYABORT;
-      }
       if(0 != parse_cmdbits(op, opnum))
         YYABORT;
       if (current_mem->op[opnum] != NULL) {
@@ -1553,10 +1516,7 @@ mem_alias :
 
       is_alias = true;
       alias = avr_new_memalias();
-
-      // alias->desc and current_mem->desc have the same length
-      // definition, thus no need to check for length here
-      strcpy(alias->desc, current_mem->desc);
+      alias->desc = current_mem->desc;
       alias->aliased_mem = existing_mem;
       ladd(current_part->mem_alias, alias);
 
diff --git a/src/developer_opts.c b/src/developer_opts.c
index 6cbfa4f2..f176563e 100644
--- a/src/developer_opts.c
+++ b/src/developer_opts.c
@@ -222,7 +222,7 @@ int dev_message(int msglvl, const char *fmt, ...) {
 
 
 
-static int dev_part_strct_entry(bool tsv, char *col0, char *col1, char *col2, const char *name, char *cont) {
+static int dev_part_strct_entry(bool tsv, const char *col0, const char *col1, const char *col2, const char *name, char *cont) {
   const char *n = name? name: "name_error";
   const char *c = cont? cont: "cont_error";
 
@@ -286,23 +286,22 @@ static int intcmp(int a, int b) {
 // Deep copies for comparison and raw output
 
 typedef struct {
+  char descbuf[32];
   AVRMEM base;
   OPCODE ops[AVR_OP_MAX];
 } AVRMEMdeep;
 
 static int avrmem_deep_copy(AVRMEMdeep *d, AVRMEM *m) {
-  size_t len;
-
   d->base = *m;
 
-  // Zap all bytes beyond terminating nul of desc array
-  len = strlen(m->desc)+1;
-  if(len < sizeof m->desc)
-    memset(d->base.desc + len, 0, sizeof m->desc - len);
+  // Note memory desc (name, really) is limited to 31 char here
+  memset(d->descbuf, 0, sizeof d->descbuf);
+  strncpy(d->descbuf, m->desc, sizeof d->descbuf-1);
 
   // Zap address values
   d->base.buf = NULL;
   d->base.tags = NULL;
+  d->base.desc = NULL;
   for(int i=0; i<AVR_OP_MAX; i++)
     d->base.op[i] = NULL;
 
@@ -334,6 +333,9 @@ static int memorycmp(AVRMEM *m1, AVRMEM *m2) {
 
 
 typedef struct {
+  char descbuf[64];
+  char idbuf[32];
+  char family_idbuf[16];
   AVRPART base;
   OPCODE ops[AVR_OP_MAX];
   AVRMEMdeep mems[40];
@@ -341,7 +343,7 @@ typedef struct {
 
 static int avrpart_deep_copy(AVRPARTdeep *d, AVRPART *p) {
   AVRMEM *m;
-  size_t len, di;
+  size_t di;
 
   memset(d, 0, sizeof *d);
 
@@ -351,20 +353,21 @@ static int avrpart_deep_copy(AVRPARTdeep *d, AVRPART *p) {
   d->base.config_file = NULL;
   d->base.lineno = 0;
 
-  // Zap all bytes beyond terminating nul of desc, id and family_id array
-  len = strlen(p->desc);
-  if(len < sizeof p->desc)
-    memset(d->base.desc + len, 0, sizeof p->desc - len);
-
-  len = strlen(p->family_id);
-  if(len < sizeof p->family_id)
-    memset(d->base.family_id + len, 0, sizeof p->family_id - len);
-
-  len = strlen(p->id);
-  if(len < sizeof p->id)
-    memset(d->base.id + len, 0, sizeof p->id - len);
+  // Copy over desc, id, and family_id
+  memset(d->descbuf, 0, sizeof d->descbuf);
+  if(d->descbuf)
+    strncpy(d->descbuf, p->desc, sizeof d->descbuf-1);
+  memset(d->idbuf, 0, sizeof d->idbuf);
+  if(d->idbuf)
+    strncpy(d->idbuf, p->id, sizeof d->idbuf-1);
+  memset(d->family_idbuf, 0, sizeof d->family_idbuf);
+  if(d->family_idbuf)
+    strncpy(d->family_idbuf, p->family_id, sizeof d->family_idbuf-1);
 
   // Zap address values
+  d->base.desc = NULL;
+  d->base.id = NULL;
+  d->base.family_id = NULL;
   d->base.mem = NULL;
   d->base.mem_alias = NULL;
   for(int i=0; i<AVR_OP_MAX; i++)
@@ -397,14 +400,14 @@ static int avrpart_deep_copy(AVRPARTdeep *d, AVRPART *p) {
 
 static char txtchar(unsigned char in) {
   in &= 0x7f;
-  return in == ' '? '_': in > ' ' && in < 0x7f? in: '.';
+  return in == 0? '.': in > ' ' && in < 0x7f? in: '_';
 }
 
 static void dev_raw_dump(const char *p, int nbytes, const char *name, const char *sub, int idx) {
   int n = (nbytes + 31)/32;
 
   for(int i=0; i<n; i++, p += 32, nbytes -= 32) {
-    dev_info("%s\t%s\t%02x%03x0: ", name, sub, idx, 2*i);
+    dev_info("%s\t%s\t%02x.%03x0: ", name, sub, idx, 2*i);
     for(int j=0; j<32; j++) {
       if(j && j%8 == 0)
         dev_info(" ");
@@ -429,7 +432,7 @@ static void dev_part_raw(AVRPART *part) {
   dev_raw_dump((char *) &dp.ops, sizeof dp.ops, part->desc, "ops", 1);
 
   for(int i=0; i<di; i++)
-    dev_raw_dump((char *) (dp.mems+i), sizeof dp.mems[i], part->desc, dp.mems[i].base.desc, i+2);
+    dev_raw_dump((char *) (dp.mems+i), sizeof dp.mems[i], part->desc, dp.mems[i].descbuf, i+2);
 }
 
 
@@ -680,7 +683,10 @@ void dev_output_part_defs(char *partdesc) {
         avr_add_mem_order(((AVRMEM_ALIAS *) ldata(lnm))->desc);
   }
 
-  nprinted = dev_nprinted;
+  if((nprinted = dev_nprinted)) {
+    dev_info("\n");
+    nprinted = dev_nprinted;
+  }
   for(LNODEID ln1 = lfirst(part_list); ln1; ln1 = lnext(ln1)) {
     AVRPART *p = ldata(ln1);
     int flashsize, flashoffset, flashpagesize, eepromsize , eepromoffset, eeprompagesize;
@@ -914,6 +920,8 @@ static void dev_pgm_raw(PROGRAMMER *pgm) {
   for(idx=0, ln=lfirst(dp.hvupdi_support); ln; ln=lnext(ln))
     dev_raw_dump(ldata(ln), sizeof(int), id, "hvupdi_", idx++);
 
+  if(dp.desc)
+    dev_raw_dump(dp.desc, strlen(dp.desc)+1, id, "desc", 0);
   // Dump cache_string values
   if(dp.usbdev && *dp.usbdev)
     dev_raw_dump(dp.usbdev, strlen(dp.usbdev)+1, id, "usbdev", 0);
@@ -925,14 +933,13 @@ static void dev_pgm_raw(PROGRAMMER *pgm) {
     dev_raw_dump(dp.usbproduct, strlen(dp.usbproduct)+1, id, "usbprod", 0);
 
   // Zap all bytes beyond terminating nul of desc, type and port array
-  if((len = strlen(dp.desc)+1) < sizeof dp.desc)
-    memset(dp.desc + len, 0, sizeof dp.desc - len);
   if((len = strlen(dp.type)+1) < sizeof dp.type)
     memset(dp.type + len, 0, sizeof dp.type - len);
   if((len = strlen(dp.port)+1) < sizeof dp.port)
     memset(dp.port + len, 0, sizeof dp.port - len);
 
   // Zap address values
+  dp.desc = NULL;
   dp.id = NULL;
   dp.parent_id = NULL;
   dp.initpgm = NULL;
diff --git a/src/jtagmkI.c b/src/jtagmkI.c
index e5d3d5d8..2e84eb5c 100644
--- a/src/jtagmkI.c
+++ b/src/jtagmkI.c
@@ -609,7 +609,7 @@ static int jtagmkI_initialize(PROGRAMMER * pgm, AVRPART * p)
   if (jtagmkI_reset(pgm) < 0)
     return -1;
 
-  strcpy(hfuse.desc, "hfuse");
+  hfuse.desc = cache_string("hfuse");
   if (jtagmkI_read_byte(pgm, p, &hfuse, 1, &b) < 0)
     return -1;
   if ((b & OCDEN) != 0)
diff --git a/src/jtagmkII.c b/src/jtagmkII.c
index 7181d186..f52f3bc3 100644
--- a/src/jtagmkII.c
+++ b/src/jtagmkII.c
@@ -1439,7 +1439,7 @@ static int jtagmkII_initialize(PROGRAMMER * pgm, AVRPART * p)
   }
 
   if ((pgm->flag & PGM_FL_IS_JTAG) && !(p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI))) {
-    strcpy(hfuse.desc, "hfuse");
+    hfuse.desc = cache_string("hfuse");
     if (jtagmkII_read_byte(pgm, p, &hfuse, 1, &b) < 0)
       return -1;
     if ((b & OCDEN) != 0)
diff --git a/src/lexer.l b/src/lexer.l
index 23fd2277..47c3cb25 100644
--- a/src/lexer.l
+++ b/src/lexer.l
@@ -45,8 +45,6 @@ DIGIT    [0-9]
 HEXDIGIT [0-9a-fA-F]
 SIGN     [+-]
 
-%x incl
-%x comment
 %option nounput
 
 /* Bump resources for classic lex. */
@@ -73,16 +71,12 @@ SIGN     [+-]
 
 0x{HEXDIGIT}+ { yylval = hexnumber(yytext); return TKN_NUMBER; }
 
-#   { /* The following captures all '#' style comments to end of line */
-       BEGIN(comment); }
-<comment>[^\n]*\n+ { /* eat comments */
-  capture_comment_char('#');
+#[^\n]*\n+ { /* record and skip # comments */
+  capture_comment_str(yytext);
   for(int i=0; yytext[i]; i++) {
-    capture_comment_char(yytext[i]);
     if(yytext[i] == '\n')
       cfg_lineno++;
   }
-  BEGIN(INITIAL);
 }
 
 
diff --git a/src/libavrdude.h b/src/libavrdude.h
index 3c2f2a57..cbbc8a79 100644
--- a/src/libavrdude.h
+++ b/src/libavrdude.h
@@ -203,8 +203,6 @@ typedef struct opcode {
 #define HV_UPDI_VARIANT_1      1 /* Dedicated UPDI pin, no HV (megaAVR0/AVR-Dx) */
 #define HV_UPDI_VARIANT_2      2 /* Shared UPDI pin, HV on _RESET (AVR-Ex) */
 
-#define AVR_DESCLEN 64
-#define AVR_IDLEN   32
 #define AVR_FAMILYIDLEN 7
 #define AVR_SIBLEN 16
 #define CTL_STACK_SIZE 32
@@ -215,10 +213,10 @@ typedef struct opcode {
 
 /* Any changes here, please also reflect in dev_part_strct() of developer_opts.c */
 typedef struct avrpart {
-  char          desc[AVR_DESCLEN];  /* long part name */
-  char          id[AVR_IDLEN];      /* short part name */
+  const char  * desc;               /* long part name */
+  const char  * id;                 /* short part name */
   const char  * parent_id;          /* parent id if set, for -p.../s */
-  char          family_id[AVR_FAMILYIDLEN+1]; /* family id in the SIB (avr8x) */
+  const char  * family_id;          /* family id in the SIB (avr8x) */
   int           hvupdi_variant;     /* HV pulse on UPDI pin, no pin or RESET pin */
   int           stk500_devcode;     /* stk500 device code */
   int           avr910_devcode;     /* avr910 device code */
@@ -286,7 +284,7 @@ typedef struct avrpart {
 
 #define AVR_MEMDESCLEN 64
 typedef struct avrmem {
-  char desc[AVR_MEMDESCLEN];  /* memory description ("flash", "eeprom", etc) */
+  const char *desc;           /* memory description ("flash", "eeprom", etc) */
   int paged;                  /* page addressed (e.g. ATmega flash) */
   int size;                   /* total memory size in bytes */
   int page_size;              /* size of memory page (if page addressed) */
@@ -312,7 +310,7 @@ typedef struct avrmem {
 } AVRMEM;
 
 typedef struct avrmem_alias {
-  char desc[AVR_MEMDESCLEN];  /* alias name ("syscfg0" etc.) */
+  const char *desc;           /* alias name ("syscfg0" etc.) */
   AVRMEM *aliased_mem;
 } AVRMEM_ALIAS;
 
@@ -330,8 +328,8 @@ int avr_set_bits(OPCODE * op, unsigned char * cmd);
 int avr_set_addr(OPCODE * op, unsigned char * cmd, unsigned long addr);
 int avr_set_addr_mem(AVRMEM *mem, int opnum, unsigned char *cmd, unsigned long addr);
 int avr_set_input(OPCODE * op, unsigned char * cmd, unsigned char data);
-int avr_get_output(OPCODE * op, unsigned char * res, unsigned char * data);
-int avr_get_output_index(OPCODE * op);
+int avr_get_output(const OPCODE *op, const unsigned char *res, unsigned char *data);
+int avr_get_output_index(const OPCODE *op);
 char cmdbitchar(CMDBIT cb);
 char *cmdbitstr(CMDBIT cb);
 const char *opcodename(int opnum);
@@ -340,26 +338,26 @@ char *opcode2str(OPCODE *op, int opnum, int detailed);
 /* Functions for AVRMEM structures */
 AVRMEM * avr_new_memtype(void);
 AVRMEM_ALIAS * avr_new_memalias(void);
-int avr_initmem(AVRPART * p);
-AVRMEM * avr_dup_mem(AVRMEM * m);
+int avr_initmem(const AVRPART *p);
+AVRMEM * avr_dup_mem(const AVRMEM *m);
 void     avr_free_mem(AVRMEM * m);
 void     avr_free_memalias(AVRMEM_ALIAS * m);
-AVRMEM * avr_locate_mem(AVRPART * p, const char * desc);
-AVRMEM * avr_locate_mem_noalias(AVRPART * p, const char * desc);
-AVRMEM_ALIAS * avr_locate_memalias(AVRPART * p, const char * desc);
-AVRMEM_ALIAS * avr_find_memalias(AVRPART * p, AVRMEM * m_orig);
-void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, AVRPART * p,
-                     int type, int verbose);
+AVRMEM * avr_locate_mem(const AVRPART *p, const char *desc);
+AVRMEM * avr_locate_mem_noalias(const AVRPART *p, const char *desc);
+AVRMEM_ALIAS * avr_locate_memalias(const AVRPART *p, const char *desc);
+AVRMEM_ALIAS * avr_find_memalias(const AVRPART *p, const AVRMEM *m_orig);
+void avr_mem_display(const char *prefix, FILE *f, const AVRMEM *m,
+                     const AVRPART *p, int verbose);
 
 /* Functions for AVRPART structures */
 AVRPART * avr_new_part(void);
-AVRPART * avr_dup_part(AVRPART * d);
+AVRPART * avr_dup_part(const AVRPART *d);
 void      avr_free_part(AVRPART * d);
-AVRPART * locate_part(LISTID parts, const char * partdesc);
-AVRPART * locate_part_by_avr910_devcode(LISTID parts, int devcode);
-AVRPART * locate_part_by_signature(LISTID parts, unsigned char * sig,
+AVRPART * locate_part(const LISTID parts, const char *partdesc);
+AVRPART * locate_part_by_avr910_devcode(const LISTID parts, int devcode);
+AVRPART * locate_part_by_signature(const LISTID parts, unsigned char *sig,
                                    int sigsize);
-void avr_display(FILE * f, AVRPART * p, const char * prefix, int verbose);
+void avr_display(FILE *f, const AVRPART *p, const char *prefix, int verbose);
 
 typedef void (*walk_avrparts_cb)(const char *name, const char *desc,
                                  const char *cfgname, int cfglineno,
@@ -653,7 +651,6 @@ extern struct serial_device usbhid_serdev;
 #define ON  1
 #define OFF 0
 
-#define PGM_DESCLEN 80
 #define PGM_PORTLEN PATH_MAX
 #define PGM_TYPELEN 32
 
@@ -685,7 +682,7 @@ typedef enum {
 /* Any changes here, please also reflect in dev_pgm_strct() of developer_opts.c */
 typedef struct programmer_t {
   LISTID id;
-  char desc[PGM_DESCLEN];
+  const char *desc;
   void (*initpgm)(struct programmer_t *pgm);
   const char *parent_id;        // Used by developer options -c*/[sr...]
   struct pindef_t pin[N_PINS];
@@ -762,6 +759,7 @@ typedef struct programmer_t {
   int  (*parseextparams) (struct programmer_t * pgm, LISTID xparams);
   void (*setup)          (struct programmer_t * pgm);
   void (*teardown)       (struct programmer_t * pgm);
+
   const char *config_file;      // Config file where defined
   int  lineno;                  // Config file line number
   void *cookie;                 // For private use by the programmer
@@ -773,8 +771,8 @@ extern "C" {
 #endif
 
 PROGRAMMER * pgm_new(void);
-PROGRAMMER * pgm_dup(const PROGRAMMER * const src);
-void         pgm_free(PROGRAMMER * const p);
+PROGRAMMER * pgm_dup(const PROGRAMMER *src);
+void         pgm_free(PROGRAMMER *p);
 
 void programmer_display(PROGRAMMER * pgm, const char * p);
 
@@ -783,10 +781,10 @@ void programmer_display(PROGRAMMER * pgm, const char * p);
 #define SHOW_PPI_PINS ((1<<PPI_AVR_VCC)|(1<<PPI_AVR_BUFF))
 #define SHOW_AVR_PINS ((1<<PIN_AVR_RESET)|(1<<PIN_AVR_SCK)|(1<<PIN_AVR_MOSI)|(1<<PIN_AVR_MISO))
 #define SHOW_LED_PINS ((1<<PIN_LED_ERR)|(1<<PIN_LED_RDY)|(1<<PIN_LED_PGM)|(1<<PIN_LED_VFY))
-void pgm_display_generic_mask(PROGRAMMER * pgm, const char * p, unsigned int show);
-void pgm_display_generic(PROGRAMMER * pgm, const char * p);
+void pgm_display_generic_mask(const PROGRAMMER *pgm, const char *p, unsigned int show);
+void pgm_display_generic(const PROGRAMMER *pgm, const char *p);
 
-PROGRAMMER * locate_programmer(LISTID programmers, const char * configid);
+PROGRAMMER *locate_programmer(const LISTID programmers, const char *configid);
 
 typedef void (*walk_programmers_cb)(const char *name, const char *desc,
                                     const char *cfgname, int cfglineno,
diff --git a/src/main.c b/src/main.c
index 08266f00..2487faa7 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1073,9 +1073,9 @@ int main(int argc, char * argv [])
              pgm->read_sib(pgm, p, sib);
              avrdude_message(MSG_NOTICE, "%s: System Information Block: \"%s\"\n",
                              progname, sib);
-            if (quell_progress < 2) {
+            if (quell_progress < 2)
               avrdude_message(MSG_INFO, "%s: Received FamilyID: \"%.*s\"\n", progname, AVR_FAMILYIDLEN, sib);
-            }
+
             if (strncmp(p->family_id, sib, AVR_FAMILYIDLEN)) {
               avrdude_message(MSG_INFO, "%s: Expected FamilyID: \"%s\"\n", progname, p->family_id);
               if (!ovsigck) {
@@ -1093,9 +1093,8 @@ int main(int argc, char * argv [])
               avrdude_message(MSG_INFO, "%s: conflicting -e and -n options specified, NOT erasing chip\n",
                               progname);
             } else {
-              if (quell_progress < 2) {
+              if (quell_progress < 2)
                 avrdude_message(MSG_INFO, "%s: erasing chip\n", progname);
-              }
               exitrc = avr_unlock(pgm, p);
               if(exitrc) goto main_exit;
               goto sig_again;
@@ -1229,9 +1228,8 @@ int main(int argc, char * argv [])
       avrdude_message(MSG_INFO, "%s: conflicting -e and -n options specified, NOT erasing chip\n",
                       progname);
     } else {
-      if (quell_progress < 2) {
+      if (quell_progress < 2)
       	avrdude_message(MSG_INFO, "%s: erasing chip\n", progname);
-      }
       exitrc = avr_chip_erase(pgm, p);
       if(exitrc) goto main_exit;
     }
diff --git a/src/pgm.c b/src/pgm.c
index 07cbed57..518b208e 100644
--- a/src/pgm.c
+++ b/src/pgm.c
@@ -61,31 +61,29 @@ static void pgm_default_powerup_powerdown (struct programmer_t * pgm)
 }
 
 
-PROGRAMMER * pgm_new(void)
-{
-  int i;
-  PROGRAMMER * pgm;
+PROGRAMMER *pgm_new(void) {
+  PROGRAMMER *pgm = (PROGRAMMER *) cfg_malloc("pgm_new()", sizeof(*pgm));
   const char *nulp = cache_string("");
 
-  pgm = (PROGRAMMER *) cfg_malloc("pgm_new()", sizeof(*pgm));
-
+  // Initialise const char * and LISTID entities
   pgm->id = lcreat(NULL, 0);
   pgm->usbpid = lcreat(NULL, 0);
-  pgm->desc[0] = 0;
-  pgm->type[0] = 0;
-  pgm->parent_id = nulp;
-  pgm->config_file = nulp;
-  pgm->lineno = 0;
-  pgm->baudrate = 0;
-  pgm->initpgm = NULL;
   pgm->hvupdi_support = lcreat(NULL, 0);
-
+  pgm->desc = nulp;
+  pgm->parent_id = nulp;
   pgm->usbdev = nulp;
   pgm->usbsn = nulp;
   pgm->usbvendor = nulp;
   pgm->usbproduct = nulp;
+  pgm->config_file = nulp;
 
-  for (i=0; i<N_PINS; i++) {
+  // Default values
+  pgm->initpgm = NULL;
+  pgm->lineno = 0;
+  pgm->baudrate = 0;
+
+  // Clear pin array
+  for(int i=0; i<N_PINS; i++) {
     pgm->pinno[i] = 0;
     pin_clear_all(&(pgm->pin[i]));
   }
@@ -121,49 +119,70 @@ PROGRAMMER * pgm_new(void)
    * optional functions - these are checked to make sure they are
    * assigned before they are called
    */
+  pgm->unlock         = NULL;
   pgm->cmd            = NULL;
   pgm->cmd_tpi        = NULL;
   pgm->spi            = NULL;
   pgm->paged_write    = NULL;
   pgm->paged_load     = NULL;
+  pgm->page_erase     = NULL;
   pgm->write_setup    = NULL;
   pgm->read_sig_bytes = NULL;
+  pgm->read_sib       = NULL;
+  pgm->print_parms    = NULL;
   pgm->set_vtarget    = NULL;
   pgm->set_varef      = NULL;
   pgm->set_fosc       = NULL;
+  pgm->set_sck_period = NULL;
+  pgm->setpin         = NULL;
+  pgm->getpin         = NULL;
+  pgm->highpulsepin   = NULL;
+  pgm->parseexitspecs = NULL;
   pgm->perform_osccal = NULL;
   pgm->parseextparams = NULL;
   pgm->setup          = NULL;
   pgm->teardown       = NULL;
 
+  // For allocating "global" memory by the programmer
+  pgm->cookie          = NULL;
+
   return pgm;
 }
 
-void pgm_free(PROGRAMMER * const p)
-{
-  ldestroy_cb(p->id, free);
-  ldestroy_cb(p->usbpid, free);
-  p->id = NULL;
-  p->usbpid = NULL;
-  /* do not free p->parent_id, p->config_file, p->usbdev, p->usbsn, p->usbvendor or p-> usbproduct */
-  /* p->cookie is freed by pgm_teardown */
-  free(p);
+void pgm_free(PROGRAMMER *p) {
+  if(p) {
+    ldestroy_cb(p->id, free);
+    ldestroy_cb(p->usbpid, free);
+    ldestroy_cb(p->hvupdi_support, free);
+    p->id = NULL;
+    p->usbpid = NULL;
+    p->hvupdi_support = NULL;
+    // Never free const char *, eg, p->desc, which are set by cache_string()
+    // p->cookie is freed by pgm_teardown
+    free(p);
+  }
 }
 
-PROGRAMMER * pgm_dup(const PROGRAMMER * const src)
-{
-  PROGRAMMER * pgm;
-  LNODEID ln;
+PROGRAMMER *pgm_dup(const PROGRAMMER *src) {
+  PROGRAMMER *pgm = pgm_new();
 
-  pgm = (PROGRAMMER *) cfg_malloc("pgm_dup()", sizeof(*pgm));
-  memcpy(pgm, src, sizeof(*pgm));
+  if(src) {
+    memcpy(pgm, src, sizeof(*pgm));
+    pgm->id = lcreat(NULL, 0);
+    pgm->usbpid = lcreat(NULL, 0);
+    pgm->hvupdi_support = lcreat(NULL, 0);
 
-  pgm->id = lcreat(NULL, 0);
-  pgm->usbpid = lcreat(NULL, 0);
-  for (ln = lfirst(src->usbpid); ln; ln = lnext(ln)) {
-    int *ip = cfg_malloc("pgm_dup()", sizeof(int));
-    *ip = *(int *) ldata(ln);
-    ladd(pgm->usbpid, ip);
+    // Leave id list empty but copy usbpid and hvupdi_support over
+    for(LNODEID ln = lfirst(src->hvupdi_support); ln; ln = lnext(ln)) {
+      int *ip = cfg_malloc("pgm_dup()", sizeof(int));
+      *ip = *(int *) ldata(ln);
+      ladd(pgm->hvupdi_support, ip);
+    }
+    for(LNODEID ln = lfirst(src->usbpid); ln; ln = lnext(ln)) {
+      int *ip = cfg_malloc("pgm_dup()", sizeof(int));
+      *ip = *(int *) ldata(ln);
+      ladd(pgm->usbpid, ip);
+    }
   }
 
   return pgm;
@@ -207,8 +226,7 @@ static void pgm_default_6 (struct programmer_t * pgm, const char * p)
 }
 
 
-void programmer_display(PROGRAMMER * pgm, const char * p)
-{
+void programmer_display(PROGRAMMER *pgm, const char * p) {
   avrdude_message(MSG_INFO, "%sProgrammer Type : %s\n", p, pgm->type);
   avrdude_message(MSG_INFO, "%sDescription     : %s\n", p, pgm->desc);
 
@@ -216,8 +234,7 @@ void programmer_display(PROGRAMMER * pgm, const char * p)
 }
 
 
-void pgm_display_generic_mask(PROGRAMMER * pgm, const char * p, unsigned int show)
-{
+void pgm_display_generic_mask(const PROGRAMMER *pgm, const char *p, unsigned int show) {
   if(show & (1<<PPI_AVR_VCC)) 
     avrdude_message(MSG_INFO, "%s  VCC     = %s\n", p, pins_to_str(&pgm->pin[PPI_AVR_VCC]));
   if(show & (1<<PPI_AVR_BUFF))
@@ -240,33 +257,22 @@ void pgm_display_generic_mask(PROGRAMMER * pgm, const char * p, unsigned int sho
     avrdude_message(MSG_INFO, "%s  VFY LED = %s\n", p, pins_to_str(&pgm->pin[PIN_LED_VFY]));
 }
 
-void pgm_display_generic(PROGRAMMER * pgm, const char * p)
-{
+void pgm_display_generic(const PROGRAMMER *pgm, const char *p) {
   pgm_display_generic_mask(pgm, p, SHOW_ALL_PINS);
 }
 
-PROGRAMMER * locate_programmer(LISTID programmers, const char * configid)
-{
-  LNODEID ln1, ln2;
-  PROGRAMMER * p = NULL;
-  const char * id;
-  int found;
+PROGRAMMER *locate_programmer(const LISTID programmers, const char *configid) {
+  PROGRAMMER *p = NULL;
+  int found = 0;
 
-  found = 0;
-
-  for (ln1=lfirst(programmers); ln1 && !found; ln1=lnext(ln1)) {
+  for(LNODEID ln1=lfirst(programmers); ln1 && !found; ln1=lnext(ln1)) {
     p = ldata(ln1);
-    for (ln2=lfirst(p->id); ln2 && !found; ln2=lnext(ln2)) {
-      id = ldata(ln2);
-      if (strcasecmp(configid, id) == 0)
+    for(LNODEID ln2=lfirst(p->id); ln2 && !found; ln2=lnext(ln2))
+      if(strcasecmp(configid, (const char *) ldata(ln2)) == 0)
         found = 1;
-    }
   }
 
-  if (found)
-    return p;
-
-  return NULL;
+  return found? p: NULL;
 }
 
 /*
@@ -296,16 +302,11 @@ void walk_programmers(LISTID programmers, walk_programmers_cb cb, void *cookie)
 /*
  * Compare function to sort the list of programmers
  */
-static int sort_programmer_compare(PROGRAMMER * p1,PROGRAMMER * p2)
-{
-  char* id1;
-  char* id2;
-  if(p1 == NULL || p2 == NULL) {
+static int sort_programmer_compare(const PROGRAMMER *p1, const PROGRAMMER *p2) {
+  if(p1 == NULL || p1->id == NULL || p2 == NULL || p2->id == NULL)
     return 0;
-  }
-  id1 = ldata(lfirst(p1->id));
-  id2 = ldata(lfirst(p2->id));
-  return strncasecmp(id1,id2,AVR_IDLEN);
+
+  return strcasecmp(ldata(lfirst(p1->id)), ldata(lfirst(p2->id)));
 }
 
 /*
diff --git a/src/pickit2.c b/src/pickit2.c
index ac3781f5..a1d32f88 100644
--- a/src/pickit2.c
+++ b/src/pickit2.c
@@ -193,16 +193,18 @@ static int pickit2_open(PROGRAMMER * pgm, char * port)
     }
     else
     {
-        // get the device description while we're at it
-        short buff[PGM_DESCLEN-1], i;
-        HidD_GetProductString(PDATA(pgm)->usb_handle, buff, PGM_DESCLEN-1);
+        // Get the device description while we're at it and overlay it on pgm->desc
+        short wbuf[80-1];
+        char *cbuf = cfg_malloc("pickit2_open()", sizeof wbuf/sizeof*wbuf + (pgm->desc? strlen(pgm->desc): 0) + 2);
+        HidD_GetProductString(PDATA(pgm)->usb_handle, wbuf, sizeof wbuf/sizeof*wbuf);
 
-        // convert from wide chars, but do not overwrite trailing '\0'
-        memset(&(pgm->desc), 0, PGM_DESCLEN);
-        for (i = 0; i < (PGM_DESCLEN-1) && buff[i]; i++)
-        {
-            pgm->desc[i] = (char)buff[i]; // TODO what about little/big endian???
-        }
+        if(pgm->desc && *pgm->desc)
+          strcpy(cbuf, pgm->desc);
+
+        // Convert from wide chars and overlay over initial part of desc
+        for (int i = 0; i < sizeof wbuf/sizeof*wbuf && wbuf[i]; i++)
+          cbuf[i] = (char) wbuf[i]; // TODO what about little/big endian???
+        pgm->desc = cache_string(cbuf);
     }
 #else
     if (usb_open_device(&(PDATA(pgm)->usb_handle), PICKIT2_VID, PICKIT2_PID) < 0)
diff --git a/src/update.c b/src/update.c
index 972c3847..f3dbd335 100644
--- a/src/update.c
+++ b/src/update.c
@@ -306,11 +306,11 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
     return LIBAVRDUDE_SOFTFAIL;
   }
 
-  AVRMEM_ALIAS * alias_mem = avr_find_memalias(p, mem);
-  char alias_mem_desc[AVR_DESCLEN + 1] = "";
-  if(alias_mem) {
-    strcat(alias_mem_desc, "/");
-    strcat(alias_mem_desc, alias_mem->desc);
+  AVRMEM_ALIAS *alias_mem = avr_find_memalias(p, mem);
+  char *alias_mem_desc = cfg_malloc("do_op()", 2 + (alias_mem && alias_mem->desc? strlen(alias_mem->desc): 0));
+  if(alias_mem && alias_mem->desc && *alias_mem->desc) {
+    *alias_mem_desc = '/';
+    strcpy(alias_mem_desc+1, alias_mem->desc);
   }
   
   switch (upd->op) {

From c9cf308037a16d97f2be43d655af0d4c1da0ec5c Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Wed, 10 Aug 2022 16:24:26 +0100
Subject: [PATCH 13/19] Include ctype.h in term.c to resolve missing functions

---
 src/term.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/term.c b/src/term.c
index 672a09cd..839e5608 100644
--- a/src/term.c
+++ b/src/term.c
@@ -20,6 +20,7 @@
 
 #include "ac_cfg.h"
 
+#include <ctype.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdint.h>

From ccb576ebc1af85a3dee6dd005aebb4e68e54b7cf Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Wed, 10 Aug 2022 22:25:19 +0100
Subject: [PATCH 14/19] Ensure memories are printed at most once for -p */S

---
 src/developer_opts.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/developer_opts.c b/src/developer_opts.c
index f176563e..32d2c71e 100644
--- a/src/developer_opts.c
+++ b/src/developer_opts.c
@@ -383,7 +383,7 @@ 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 avr_mem_order/sizeof *avr_mem_order && avr_mem_order[mi]; mi++) {
-    m = p->mem? avr_locate_mem(p, avr_mem_order[mi]): NULL;
+    m = p->mem? avr_locate_mem_noalias(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);
@@ -553,8 +553,8 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
   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, avr_mem_order[mi]): NULL;
-    bm = base && base->mem? avr_locate_mem(base, avr_mem_order[mi]): NULL;
+    m = p->mem? avr_locate_mem_noalias(p, avr_mem_order[mi]): NULL;
+    bm = base && base->mem? avr_locate_mem_noalias(base, avr_mem_order[mi]): NULL;
 
     if(!m && bm && !tsv)
       dev_info("\n    memory \"%s\" = NULL;\n", bm->desc);

From c2c9053b1388bfbb03c947e05be63bfe036c26bc Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Fri, 12 Aug 2022 00:28:54 +0100
Subject: [PATCH 15/19] Show comments with -p*/s or -c*/s and reduce -p */r raw
 output

---
 src/config.c                 | 100 +++++++++++++-
 src/config.h                 |  21 ++-
 src/config_gram.y            |  41 +++---
 src/developer_opts.c         | 239 ++++++++++++++++++++++++++-------
 src/developer_opts.h         |   1 +
 src/developer_opts_private.h |  49 +++----
 src/lexer.l                  | 252 ++++++++++++++++++-----------------
 src/libavrdude.h             |   7 +-
 src/main.c                   |  26 ++--
 9 files changed, 505 insertions(+), 231 deletions(-)

diff --git a/src/config.c b/src/config.c
index ac712b17..d36e0814 100644
--- a/src/config.c
+++ b/src/config.c
@@ -371,10 +371,98 @@ const char *cache_string(const char *p) {
   return hstrings[h][k] = cfg_strdup("cache_string()", p);
 }
 
-// Captures comments during parsing
-void capture_comment_str(const char *p) {
+
+static LISTID cfg_comms;        // A chain of comment lines
+static LISTID cfg_prologue;     // Comment lines at start of avrdude.conf
+static char *lkw;               // Last seen keyword
+static int lkw_lineno;          // Line number of that
+
+static LISTID cfg_strctcomms;   // Passed on to config_gram.y
+static LISTID cfg_pushedcomms;  // Temporarily pushed main comments
+static int cfg_pushed;          // ... for memory sections
+
+COMMENT *locate_comment(const LISTID comments, const char *where, int rhs) {
+  if(comments)
+    for(LNODEID ln=lfirst(comments); ln; ln=lnext(ln)) {
+      COMMENT *n = ldata(ln);
+      if(n && rhs == n->rhs && n->kw && strcmp(where, n->kw) == 0)
+        return n;
+    }
+
+  return NULL;
 }
 
+static void addcomment(int rhs) {
+  if(lkw) {
+    COMMENT *node = cfg_malloc("addcomment()", sizeof(*node));
+    node->rhs = rhs;
+    node->kw = cfg_strdup("addcomment()", lkw);
+    node->comms = cfg_comms;
+    cfg_comms = NULL;
+    if(!cfg_strctcomms)
+      cfg_strctcomms = lcreat(NULL, 0);
+    ladd(cfg_strctcomms, node);
+  }
+}
+
+// Capture prologue during parsing (triggered by lexer.l)
+void cfg_capture_prologue(void) {
+  cfg_prologue = cfg_comms;
+  cfg_comms = NULL;
+}
+
+LISTID cfg_get_prologue(void) {
+  return cfg_prologue;
+}
+
+// Captures comments during parsing
+void capture_comment_str(const char *com, int lineno) {
+  if(!cfg_comms)
+    cfg_comms = lcreat(NULL, 0);
+  ladd(cfg_comms, cfg_strdup("capture_comment_str()", com));
+
+  // Last keyword lineno is the same as this comment's
+  if(lkw && lkw_lineno == lineno)
+    addcomment(1);              // Register comms to show right of lkw = ...;
+}
+
+// Capture assignments (keywords left of =) and associate comments to them
+void capture_lvalue_kw(const char *kw, int lineno) {
+  if(!strcmp(kw, "memory")) {   // Push part comments and start memory comments
+    if(!cfg_pushed) {           // config_gram.y pops the part comments
+      cfg_pushed = 1;
+      cfg_pushedcomms = cfg_strctcomms;
+      cfg_strctcomms = NULL;
+    }
+  }
+
+  if(!strcmp(kw, "programmer") || !strcmp(kw, "part") || !strcmp(kw, "memory"))
+    kw = "*";                   // Show comment before programmer/part/memory
+
+  if(lkw)
+    free(lkw);
+  lkw = cfg_strdup("capture_lvalue_kw()", kw);
+  lkw_lineno = lineno;
+  if(cfg_comms)                 // Accrued list of # one-line comments
+    addcomment(0);              // Register comment to appear before lkw assignment
+}
+
+// config_gram.y calls this once for each programmer/part/memory structure
+LISTID cfg_move_comments(void) {
+  capture_lvalue_kw(";", -1);
+
+  LISTID ret = cfg_strctcomms;
+  cfg_strctcomms = NULL;
+  return ret;
+}
+
+// config_gram.y calls this after ingressing the memory structure
+void cfg_pop_comms(void) {
+  if(cfg_pushed) {
+    cfg_pushed = 0;
+    cfg_strctcomms = cfg_pushedcomms;
+  }
+}
 
 // Convert the next n hex digits of s to a hex number
 static unsigned int tohex(const unsigned char *s, unsigned int n) {
@@ -558,12 +646,12 @@ char *cfg_unescape(char *d, const char *s) {
   return (char *) cfg_unescapeu((unsigned char *) d, (const unsigned char *) s);
 }
 
-// Return an escaped string that looks like a C-style input string incl quotes, memory is malloc()ed
+// Return an escaped string that looks like a C-style input string incl quotes, memory is malloc'd
 char *cfg_escape(const char *s) {
-  char *ret = (char *) cfg_malloc("cfg_escape()", 4*strlen(s)+2+3), *d = ret;
+  char buf[50*1024], *d = buf;
 
   *d++ = '"';
-  for(; *s; s++) {
+  for(; *s && d-buf < sizeof buf-7; s++) {
     switch(*s) {
     case '\n':
       *d++ = '\\'; *d++ = 'n';
@@ -602,5 +690,5 @@ char *cfg_escape(const char *s) {
   *d++ = '"';
   *d = 0;
 
-  return ret;
+  return cfg_strdup("cfg_escape()", buf);
 }
diff --git a/src/config.h b/src/config.h
index afd39b17..b8bd2031 100644
--- a/src/config.h
+++ b/src/config.h
@@ -30,6 +30,13 @@
 #endif
 
 
+typedef struct {
+  char *kw;                     // Keyword near the comments
+  LISTID comms;                 // Chained list of comments
+  int rhs;                      // Comments to print rhs of keyword line
+} COMMENT;
+
+
 enum { V_NONE, V_NUM, V_NUM_REAL, V_STR };
 typedef struct value_t {
   int      type;
@@ -94,7 +101,19 @@ void print_token(TOKEN *tkn);
 
 void pyytext(void);
 
-void capture_comment_str(const char *str);
+COMMENT *locate_comment(const LISTID comments, const char *where, int rhs);
+
+void cfg_capture_prologue(void);
+
+LISTID cfg_get_prologue(void);
+
+void capture_comment_str(const char *com, int lineno);
+
+void capture_lvalue_kw(const char *kw, int lineno);
+
+LISTID cfg_move_comments(void);
+
+void cfg_pop_comms(void);
 
 #ifdef __cplusplus
 }
diff --git a/src/config_gram.y b/src/config_gram.y
index 2a027c7f..2c3504f2 100644
--- a/src/config_gram.y
+++ b/src/config_gram.y
@@ -297,6 +297,7 @@ prog_def :
         lrmv_d(programmers, existing_prog);
         pgm_free(existing_prog);
       }
+      current_prog->comments = cfg_move_comments();
       LISTADD(programmers, current_prog);
 //      pgm_fill_old_pins(current_prog); // TODO to be removed if old pin data no longer needed
 //      pgm_display_generic(current_prog, id);
@@ -322,6 +323,7 @@ prog_decl :
       }
       current_prog = pgm_dup(pgm);
       current_prog->parent_id = cache_string($3->value.string);
+      current_prog->comments = NULL;
       current_prog->config_file = cache_string(cfg_infile);
       current_prog->lineno = cfg_lineno;
       free_token($3);
@@ -341,32 +343,33 @@ part_def :
         YYABORT;
       }
 
-      /*
-       * perform some sanity checking, and compute the number of bits
-       * to shift a page for constructing the page address for
-       * page-addressed memories.
-       */
+      // Sanity checks for memory sizes and compute/override num_pages entry
       for (ln=lfirst(current_part->mem); ln; ln=lnext(ln)) {
         m = ldata(ln);
         if (m->paged) {
-          if (m->page_size == 0) {
-            yyerror("must specify page_size for paged memory");
+          if (m->size <= 0) {
+            yyerror("must specify a positive size for paged memory %s", m->desc);
             YYABORT;
           }
-          if (m->num_pages == 0) {
-            yyerror("must specify num_pages for paged memory");
+          if (m->page_size <= 0) {
+            yyerror("must specify a positive page size for paged memory %s", m->desc);
             YYABORT;
           }
-          if (m->size != m->page_size * m->num_pages) {
-            yyerror("page size (%u) * num_pages (%u) = "
-                    "%u does not match memory size (%u)",
-                    m->page_size,
-                    m->num_pages,
-                    m->page_size * m->num_pages,
-                    m->size);
+          // Code base relies on page_size being a power of 2 in some places
+          if (m->page_size & (m->page_size - 1)) {
+            yyerror("page size must be a power of 2 for paged memory %s", m->desc);
             YYABORT;
           }
+          // Code base relies on size being a multiple of page_size
+          if (m->size % m->page_size) {
+            yyerror("size must be a multiple of page size for paged memory %s", m->desc);
+            YYABORT;
+          }
+          // Warn if num_pages was specified but is inconsistent with size and page size
+          if (m->num_pages && m->num_pages != m->size / m->page_size)
+            yywarning("overriding num_page to be %d for memory %s", m->size/m->page_size, m->desc);
 
+          m->num_pages = m->size / m->page_size;
         }
       }
 
@@ -382,6 +385,8 @@ part_def :
         lrmv_d(part_list, existing_part);
         avr_free_part(existing_part);
       }
+
+      current_part->comments = cfg_move_comments();
       LISTADD(part_list, current_part); 
       current_part = NULL; 
     }
@@ -405,6 +410,7 @@ part_decl :
 
       current_part = avr_dup_part(parent_part);
       current_part->parent_id = cache_string($3->value.string);
+      current_part->comments = NULL;
       current_part->config_file = cache_string(cfg_infile);
       current_part->lineno = cfg_lineno;
 
@@ -1291,7 +1297,9 @@ part_parm :
               yywarning("%s's %s %s misses a necessary address bit a%d",
                  current_part->desc, current_mem->desc, opcodename(i), bn-1);
             }
+        current_mem->comments = cfg_move_comments();
       }
+      cfg_pop_comms();
       current_mem = NULL; 
     } |
   K_MEMORY TKN_STRING TKN_EQUAL K_NULL
@@ -1302,6 +1310,7 @@ part_parm :
         avr_free_mem(existing_mem);
       }
       free_token($2);
+      cfg_pop_comms();
       current_mem = NULL;
     } |
   opcode TKN_EQUAL string_list {
diff --git a/src/developer_opts.c b/src/developer_opts.c
index 32d2c71e..e0c274b9 100644
--- a/src/developer_opts.c
+++ b/src/developer_opts.c
@@ -48,6 +48,7 @@
 
 #include "avrdude.h"
 #include "libavrdude.h"
+#include "config.h"
 
 #include "developer_opts.h"
 #include "developer_opts_private.h"
@@ -221,8 +222,49 @@ int dev_message(int msglvl, const char *fmt, ...) {
 }
 
 
+// Any of the strings in the list contains subs as substring?
+int dev_has_subsstr_comms(const LISTID comms, const char *subs) {
+  if(comms)
+    for(LNODEID ln=lfirst(comms); ln; ln=lnext(ln))
+       if(strstr((char *) ldata(ln), subs))
+         return 1;
+  return 0;
+}
+
+// Print a chained list of strings
+void dev_print_comment(const LISTID comms) {
+  if(comms)
+    for(LNODEID ln=lfirst(comms); ln; ln=lnext(ln))
+       dev_info("%s", (char *) ldata(ln));
+}
+
+// Conditional output of part, memory or programmer's comments field
+static void dev_cout(const LISTID comms, const char *name, int rhs, int elself) {
+  COMMENT *cp;
+
+  if((cp = locate_comment(comms, name, rhs)))
+    dev_print_comment(cp->comms);
+  else if(elself)
+    dev_info("\n");
+}
+
+// Print part->comments, mem->comments or pgm->comments (for debugging)
+void dev_print_kw_comments(const LISTID comms) {
+  if(comms)
+    for(LNODEID ln=lfirst(comms); ln; ln=lnext(ln)) {
+      COMMENT *n = ldata(ln);
+      if(n && n->comms) {
+        dev_info(">>> %s %c\n", n->kw, n->rhs? '>': '<');
+        dev_print_comment(n->comms);
+      }
+    }
+}
+
+// Ideally all assignment outputs run via this function
+static int dev_part_strct_entry(bool tsv,               // Print as spreadsheet?
+  const char *col0, const char *col1, const char *col2, // Descriptors of item
+  const char *name, char *cont, const LISTID comms) {   // Name, contents and comments
 
-static int dev_part_strct_entry(bool tsv, const char *col0, const char *col1, const char *col2, const char *name, char *cont) {
   const char *n = name? name: "name_error";
   const char *c = cont? cont: "cont_error";
 
@@ -239,8 +281,9 @@ static int dev_part_strct_entry(bool tsv, const char *col0, const char *col1, co
     dev_info("%s\t%s\n", n, c);
   } else {                      // Grammar conform
     int indent = col2 && strcmp(col2, "part");
-
-    printf("%*s%-*s = %s;\n", indent? 8: 4, "", indent? 15: 19, n, c);
+    dev_cout(comms, n, 0, 0); // Print comments before the line
+    dev_info("%*s%-*s = %s;", indent? 8: 4, "", indent? 15: 19, n, c);
+    dev_cout(comms, n, 1, 1);  // Print comments on rhs
   }
 
   if(cont)
@@ -267,14 +310,18 @@ static void dev_stack_out(bool tsv, AVRPART *p, const char *name, unsigned char
 
   if(tsv)
     dev_info(".pt\t%s\t%s\t", p->desc, name);
-  else
+  else {
+    dev_cout(p->comments, name, 0, 0);
     dev_info("    %-19s =%s", name, ns <=8? " ": "");
+  }
 
   if(ns <= 0)
-    dev_info(tsv? "NULL\n": "NULL;\n");
+    dev_info(tsv? "NULL\n": "NULL;");
   else
     for(int i=0; i<ns; i++)
-      dev_info("%s0x%02x%s", !tsv && ns > 8 && i%8 == 0? "\n        ": "", stack[i], i+1<ns? ", ": tsv? "\n": ";\n");
+      dev_info("%s0x%02x%s", !tsv && ns > 8 && i%8 == 0? "\n        ": "", stack[i], i+1<ns? ", ": tsv? "\n": ";");
+
+  dev_cout(p->comments, name, 1, 1);
 }
 
 
@@ -291,7 +338,7 @@ typedef struct {
   OPCODE ops[AVR_OP_MAX];
 } AVRMEMdeep;
 
-static int avrmem_deep_copy(AVRMEMdeep *d, AVRMEM *m) {
+static int avrmem_deep_copy(AVRMEMdeep *d, const AVRMEM *m) {
   d->base = *m;
 
   // Note memory desc (name, really) is limited to 31 char here
@@ -299,17 +346,16 @@ static int avrmem_deep_copy(AVRMEMdeep *d, AVRMEM *m) {
   strncpy(d->descbuf, m->desc, sizeof d->descbuf-1);
 
   // Zap address values
+  d->base.comments = NULL;
   d->base.buf = NULL;
   d->base.tags = NULL;
   d->base.desc = NULL;
   for(int i=0; i<AVR_OP_MAX; i++)
     d->base.op[i] = NULL;
 
-
   // Copy over the SPI operations themselves
-  memset(d->base.op, 0, sizeof d->base.op);
   memset(d->ops, 0, sizeof d->ops);
-  for(size_t i=0; i<sizeof d->ops/sizeof *d->ops; i++)
+  for(size_t i=0; i<AVR_OP_MAX; i++)
     if(m->op[i])
       d->ops[i] = *m->op[i];
 
@@ -324,7 +370,7 @@ static int memorycmp(AVRMEM *m1, AVRMEM *m2) {
 
   if(!m1 || !m2)
     return m1? -1: 1;
-  
+
   avrmem_deep_copy(&dm1, m1);
   avrmem_deep_copy(&dm2, m2);
 
@@ -341,7 +387,7 @@ typedef struct {
   AVRMEMdeep mems[40];
 } AVRPARTdeep;
 
-static int avrpart_deep_copy(AVRPARTdeep *d, AVRPART *p) {
+static int avrpart_deep_copy(AVRPARTdeep *d, const AVRPART *p) {
   AVRMEM *m;
   size_t di;
 
@@ -349,6 +395,7 @@ static int avrpart_deep_copy(AVRPARTdeep *d, AVRPART *p) {
 
   d->base = *p;
 
+  d->base.comments = NULL;
   d->base.parent_id = NULL;
   d->base.config_file = NULL;
   d->base.lineno = 0;
@@ -373,8 +420,7 @@ static int avrpart_deep_copy(AVRPARTdeep *d, AVRPART *p) {
   for(int i=0; i<AVR_OP_MAX; i++)
     d->base.op[i] = NULL;
 
-  // Copy over the SPI operations
-  memset(d->base.op, 0, sizeof d->base.op);
+  // Copy over all used SPI operations
   memset(d->ops, 0, sizeof d->ops);
   for(int i=0; i<AVR_OP_MAX; i++)
     if(p->op[i])
@@ -403,7 +449,8 @@ static char txtchar(unsigned char in) {
   return in == 0? '.': in > ' ' && in < 0x7f? in: '_';
 }
 
-static void dev_raw_dump(const char *p, int nbytes, const char *name, const char *sub, int idx) {
+static void dev_raw_dump(const void *v, int nbytes, const char *name, const char *sub, int idx) {
+  const unsigned char *p = v;
   int n = (nbytes + 31)/32;
 
   for(int i=0; i<n; i++, p += 32, nbytes -= 32) {
@@ -412,41 +459,69 @@ static void dev_raw_dump(const char *p, int nbytes, const char *name, const char
       if(j && j%8 == 0)
         dev_info(" ");
       if(j < nbytes)
-        dev_info("%02x", (unsigned char) p[j]);
+        dev_info("%02x", p[j]);
       else
         dev_info("  ");
     }
     dev_info(" ");
     for(int j=0; j<32 && j < nbytes; j++)
-      dev_info("%c", txtchar((unsigned char) p[j]));
+      dev_info("%c", txtchar(p[j]));
     dev_info("\n");
   }
 }
 
+static int _is_all_zero(const void *p, size_t n) {
+  const char *q = (const char *) p;
+  return n <= 0 || (*q == 0 && memcmp(q, q+1, n-1) == 0);
+}
+
+static char *opsnm(const char *pre, int opnum) {
+  static char ret[128];
+  sprintf(ret, "%.31s.%.95s", pre, opcodename(opnum));
+  return ret;
+}
 
 static void dev_part_raw(AVRPART *part) {
   AVRPARTdeep dp;
   int di = avrpart_deep_copy(&dp, part);
 
-  dev_raw_dump((char *) &dp.base, sizeof dp.base, part->desc, "part", 0);
-  dev_raw_dump((char *) &dp.ops, sizeof dp.ops, part->desc, "ops", 1);
+  dev_raw_dump(&dp.base, sizeof dp.base, part->desc, "part", 0);
+  for(int i=0; i<AVR_OP_MAX; i++)
+    if(!_is_all_zero(dp.ops+i, sizeof*dp.ops))
+      dev_raw_dump(dp.ops+i, sizeof*dp.ops, part->desc, opsnm("part", i), 1);
 
-  for(int i=0; i<di; i++)
-    dev_raw_dump((char *) (dp.mems+i), sizeof dp.mems[i], part->desc, dp.mems[i].descbuf, i+2);
+  for(int i=0; i<di; i++) {
+    char *nm = dp.mems[i].descbuf;
+
+    dev_raw_dump(nm, sizeof dp.mems[i].descbuf, part->desc, nm, i+2);
+    dev_raw_dump(&dp.mems[i].base, sizeof dp.mems[i].base, part->desc, nm, i+2);
+    for(int j=0; j<AVR_OP_MAX; j++)
+      if(!_is_all_zero(dp.mems[i].ops+j, sizeof(OPCODE)))
+        dev_raw_dump(dp.mems[i].ops+j, sizeof(OPCODE), part->desc, opsnm(nm, j), i+2);
+  }
 }
 
 
 static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
-
   char *descstr = cfg_escape(p->desc);
+  COMMENT *cp;
+
   if(!tsv) {
-    dev_info("#------------------------------------------------------------\n");
-    dev_info("# %.*s\n", strlen(descstr+1)-1, descstr+1);
-    dev_info("#------------------------------------------------------------\n");
+    const char *del = "#------------------------------------------------------------";
+    cp = locate_comment(p->comments, "*", 0);
+
+    if(!cp || !dev_has_subsstr_comms(cp->comms, del)) {
+      dev_info("%s\n", del);
+      dev_info("# %.*s\n", strlen(descstr)-2, descstr+1); // Remove double quotes
+      dev_info("%s\n\n", del);
+    }
+    if(cp)
+      dev_print_comment(cp->comms);
+
     if(p->parent_id && *p->parent_id)
-      dev_info("\npart parent \"%s\"\n", p->parent_id);
+      dev_info("part parent \"%s\"\n", p->parent_id);
     else
-      dev_info("\npart\n");
+      dev_info("part\n");
   }
 
   _if_partout_str(strcmp, descstr, desc);
@@ -548,7 +623,7 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
 
   for(int i=0; i < AVR_OP_MAX; i++)
     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));
+      dev_part_strct_entry(tsv, ".ptop", p->desc, "part", opcodename(i), opcode2str(p->op[i], i, !tsv), p->comments);
 
   for(size_t mi=0; mi < sizeof avr_mem_order/sizeof *avr_mem_order && avr_mem_order[mi]; mi++) {
     AVRMEM *m, *bm;
@@ -569,7 +644,8 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
       if(!memorycmp(bm, m))     // Same memory bit for bit, no need to instantiate
         continue;
 
-      dev_info("\n    memory \"%s\"\n", m->desc);
+      dev_cout(m->comments, "*", 0, 1);
+      dev_info("    memory \"%s\"\n", m->desc);
     }
 
     _if_memout_yn(paged);
@@ -589,10 +665,12 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
 
     for(int i=0; i < AVR_OP_MAX; i++)
       if(!bm || opcodecmp(bm->op[i], m->op[i], i))
-        dev_part_strct_entry(tsv, ".ptmmop", p->desc, m->desc, opcodename(i), opcode2str(m->op[i], i, !tsv));
+        dev_part_strct_entry(tsv, ".ptmmop", p->desc, m->desc, opcodename(i), opcode2str(m->op[i], i, !tsv), m->comments);
 
-    if(!tsv)
+    if(!tsv) {
+      dev_cout(m->comments, ";", 0, 0);
       dev_info("    ;\n");
+    }
 
     for(LNODEID lnm=lfirst(p->mem_alias); lnm; lnm=lnext(lnm)) {
       AVRMEM_ALIAS *ma = ldata(lnm);
@@ -605,8 +683,37 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
     }
   }
 
-  if(!tsv)
+  if(!tsv) {
+    dev_cout(p->comments, ";", 0, 0);
     dev_info(";\n");
+  }
+}
+
+
+void dev_output_pgm_part(int dev_opt_c, char *programmer, int dev_opt_p, char *partdesc) {
+  if(dev_opt_c == 2 && dev_opt_p == 2) {
+    char *p;
+
+    dev_print_comment(cfg_get_prologue());
+
+    dev_info("default_programmer = %s;\n", p = cfg_escape(default_programmer)); free(p);
+    dev_info("default_parallel   = %s;\n", p = cfg_escape(default_parallel)); free(p);
+    dev_info("default_serial     = %s;\n", p = cfg_escape(default_serial)); free(p);
+    dev_info("default_spi        = %s;\n", p = cfg_escape(default_spi)); free(p);
+
+    dev_info("\n#\n# PROGRAMMER DEFINITIONS\n#\n\n");
+  }
+
+  if(dev_opt_c)
+    dev_output_pgm_defs(cfg_strdup("main()", programmer));
+
+  if(dev_opt_p == 2 && dev_opt_c)
+    dev_info("\n");
+  if(dev_opt_p == 2)
+    dev_info("#\n# PART DEFINITIONS\n#\n");
+
+  if(dev_opt_p)
+    dev_output_part_defs(cfg_strdup("main()", partdesc));
 }
 
 
@@ -941,6 +1048,7 @@ static void dev_pgm_raw(PROGRAMMER *pgm) {
   // Zap address values
   dp.desc = NULL;
   dp.id = NULL;
+  dp.comments = NULL;
   dp.parent_id = NULL;
   dp.initpgm = NULL;
   dp.usbpid = NULL;
@@ -968,29 +1076,38 @@ static const char *connstr(conntype_t conntype) {
 static void dev_pgm_strct(PROGRAMMER *pgm, bool tsv, PROGRAMMER *base) {
   char *id = ldata(lfirst(pgm->id));
   LNODEID ln;
+  COMMENT *cp;
   int firstid;
 
   if(!tsv) {
-    dev_info("#------------------------------------------------------------\n");
-    dev_info("# ");
-    for(firstid=1, ln=lfirst(pgm->id); ln; ln=lnext(ln)) {
-      if(!firstid)
-        dev_info("/");
-      firstid = 0;
-      dev_info("%s", ldata(ln));
+    const char *del = "#------------------------------------------------------------";
+    cp = locate_comment(pgm->comments, "*", 0);
+
+    if(!cp || !dev_has_subsstr_comms(cp->comms, del)) {
+      dev_info("%s\n# ", del);
+      for(firstid=1, ln=lfirst(pgm->id); ln; ln=lnext(ln)) {
+        if(!firstid)
+          dev_info("/");
+        firstid = 0;
+        dev_info("%s", ldata(ln));
+      }
+      dev_info("\n%s\n\n", del);
     }
-    dev_info("\n");
-    dev_info("#------------------------------------------------------------\n");
+    if(cp)
+      dev_print_comment(cp->comms);
+
     if(pgm->parent_id && *pgm->parent_id)
-      dev_info("\nprogrammer parent \"%s\"\n", pgm->parent_id);
+      dev_info("programmer parent \"%s\"\n", pgm->parent_id);
     else
-      dev_info("\nprogrammer\n");
+      dev_info("programmer\n");
   }
 
   if(tsv)
     dev_info(".prog\t%s\tid\t", id);
-  else
+  else {
+    dev_cout(pgm->comments, "id", 0, 0);
     dev_info("    %-19s = ", "id");
+  }
   for(firstid=1, ln=lfirst(pgm->id); ln; ln=lnext(ln)) {
     if(!firstid)
       dev_info(", ");
@@ -999,7 +1116,12 @@ static void dev_pgm_strct(PROGRAMMER *pgm, bool tsv, PROGRAMMER *base) {
     dev_info("%s", str);
     free(str);
   }
-  dev_info(tsv? "\n": ";\n");
+  if(tsv)
+    dev_info("\n");
+  else {
+    dev_info(";");
+    dev_cout(pgm->comments, "id", 1, 1);
+  }
 
   _if_pgmout_str(strcmp, cfg_escape(pgm->desc), desc);
   _pgmout_fmt("type", "\"%s\"", locate_programmer_type_id(pgm->initpgm));
@@ -1012,15 +1134,22 @@ static void dev_pgm_strct(PROGRAMMER *pgm, bool tsv, PROGRAMMER *base) {
   if(pgm->usbpid && lfirst(pgm->usbpid)) {
     if(tsv)
       dev_info(".prog\t%s\tusbpid\t", id);
-    else
+    else {
+      dev_cout(pgm->comments, "usbpid", 0, 0);
       dev_info("    %-19s = ", "usbpid");
+    }
     for(firstid=1, ln=lfirst(pgm->usbpid); ln; ln=lnext(ln)) {
       if(!firstid)
         dev_info(", ");
       firstid = 0;
       dev_info("0x%04x", *(unsigned int *) ldata(ln));
     }
-    dev_info(tsv? "\n": ";\n");
+    if(tsv)
+      dev_info("\n");
+    else {
+      dev_info(";");
+      dev_cout(pgm->comments, "usbpid", 1, 1);
+    }
   }
 
   _if_pgmout_str(strcmp, cfg_escape(pgm->usbdev), usbdev);
@@ -1039,20 +1168,28 @@ static void dev_pgm_strct(PROGRAMMER *pgm, bool tsv, PROGRAMMER *base) {
   if(pgm->hvupdi_support && lfirst(pgm->hvupdi_support)) {
     if(tsv)
       dev_info(".prog\t%s\thvupdu_support\t", id);
-    else
+    else {
+      dev_cout(pgm->comments, "hvupdi_support", 0, 0);
       dev_info("    %-19s = ", "hvupdi_support");
+    }
     for(firstid=1, ln=lfirst(pgm->hvupdi_support); ln; ln=lnext(ln)) {
       if(!firstid)
         dev_info(", ");
       firstid = 0;
       dev_info("%d", *(unsigned int *) ldata(ln));
     }
-    dev_info(tsv? "\n": ";\n");
+    if(tsv)
+      dev_info("\n");
+    else {
+      dev_info(";");
+      dev_cout(pgm->comments, "hvupdi_support", 1, 1);
+    }
   }
 
-
-  if(!tsv)
+  if(!tsv) {
+    dev_cout(pgm->comments, ";", 0, 0);
     dev_info(";\n");
+  }
 }
 
 
diff --git a/src/developer_opts.h b/src/developer_opts.h
index 2079c109..bc04a9d8 100644
--- a/src/developer_opts.h
+++ b/src/developer_opts.h
@@ -19,6 +19,7 @@
 #ifndef developer_opts_h
 #define developer_opts_h
 
+void dev_output_pgm_part(int dev_opt_c, char *programmer, int dev_opt_p, char *partdesc);
 void dev_output_part_defs(char *partdesc);
 void dev_output_pgm_defs(char *programmer);
 
diff --git a/src/developer_opts_private.h b/src/developer_opts_private.h
index a5c1562e..54012838 100644
--- a/src/developer_opts_private.h
+++ b/src/developer_opts_private.h
@@ -52,76 +52,77 @@ static int dev_message(int msglvl, const char *fmt, ...);
 #define dev_notice2(...) dev_message(DEV_NOTICE2, __VA_ARGS__)
 
 #define _pgmout(fmt, component) \
-  dev_part_strct_entry(tsv, ".prog", id, NULL, #component, dev_sprintf(fmt, pgm->component))
+  dev_part_strct_entry(tsv, ".prog", id, NULL, #component, dev_sprintf(fmt, pgm->component), pgm->comments)
 
 #define _pgmout_fmt(name, fmt, what) \
-  dev_part_strct_entry(tsv, ".prog", id, NULL, name, dev_sprintf(fmt, what))
+  dev_part_strct_entry(tsv, ".prog", id, NULL, name, dev_sprintf(fmt, what), pgm->comments)
 
 #define _if_pgmout(cmp, fmt, component) do { \
   if(!base || cmp(base->component, pgm->component)) \
-    dev_part_strct_entry(tsv, ".prog", id, NULL, #component, dev_sprintf(fmt, pgm->component)); \
+    dev_part_strct_entry(tsv, ".prog", id, NULL, #component, dev_sprintf(fmt, pgm->component), pgm->comments); \
 } while(0)
 
-// Result must be a malloc()ed string
+// Result must be a malloc'd string
 #define _if_pgmout_str(cmp, result, component) do { \
   if(!base || cmp(base->component, pgm->component)) \
-    dev_part_strct_entry(tsv, ".prog", id, NULL, #component, result); \
+    dev_part_strct_entry(tsv, ".prog", id, NULL, #component, result, pgm->comments); \
 } while(0)
 
+
 #define _partout(fmt, component) \
-  dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, dev_sprintf(fmt, p->component))
+  dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, dev_sprintf(fmt, p->component), p->comments)
 
 #define _if_partout(cmp, fmt, component) do { \
   if(!base || cmp(base->component, p->component)) \
-    dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, dev_sprintf(fmt, p->component)); \
+    dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, dev_sprintf(fmt, p->component), p->comments); \
 } while(0)
 
 #define _if_n_partout(cmp, n, fmt, component) do { \
   if(!base || cmp(base->component, p->component, n)) \
-    dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, dev_sprintf(fmt, p->component)); \
+    dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, dev_sprintf(fmt, p->component), p->comments); \
 } while(0)
 
-// Result must be a malloc()ed string
+// Result must be a malloc'd string
 #define _partout_str(result, component) \
-  dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, result)
+  dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, result, p->comments)
 
-// Result must be a malloc()ed string
+// Result must be a malloc'd string
 #define _if_partout_str(cmp, result, component) do { \
   if(!base || cmp(base->component, p->component)) \
-    dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, result); \
+    dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, result, p->comments); \
 } while(0)
 
-// Result must be a malloc()ed string
+// Result must be a malloc'd string
 #define _if_n_partout_str(cmp, n, result, component) do { \
   if(!base || cmp(base->component, p->component, n)) \
-    dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, result); \
+    dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, result, p->comments); \
 } while(0)
 
 
 #define _memout(fmt, component) \
-  dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, dev_sprintf(fmt, m->component))
+  dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, dev_sprintf(fmt, m->component), m->comments)
 
 #define _if_memout(cmp, fmt, component) do { \
   if(!bm || cmp(bm->component, m->component)) \
-    dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, dev_sprintf(fmt, m->component)); \
+    dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, dev_sprintf(fmt, m->component), m->comments); \
 } while(0)
 
-// Result must be a malloc()ed string
+// Result must be a malloc'd string
 #define _memout_str(result, component) \
-  dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, result)
+  dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, result, m->comments)
 
-// Result must be a malloc()ed string
+// Result must be a malloc'd string
 #define _if_n_memout_str(cmp, n, result, component) do { \
   if(!bm || cmp(bm->component, m->component, n)) \
-    dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, result); \
+    dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, result, m->comments); \
 } while(0)
 
 #define _memout_yn(component) \
-  dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, cfg_strdup("_memout_yn()", m->component? "yes": "no"))
+  dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, cfg_strdup("_memout_yn()", m->component? "yes": "no"), m->comments)
 
 #define _if_memout_yn(component) do { \
   if(!bm || bm->component != m->component) \
-    dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, cfg_strdup("_if_memout_yn()", m->component? "yes": "no")); \
+    dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, cfg_strdup("_if_memout_yn()", m->component? "yes": "no"), m->comments); \
 } while(0)
 
 #define _flagout(mask, name) \
@@ -132,8 +133,8 @@ static int dev_message(int msglvl, const char *fmt, ...);
     _partout_str(cfg_strdup("_if_flagout()", p->flags & (mask)? "yes": "no"), name); \
 } while(0)
 
-// Result must be a malloc()ed string
+// Result must be a malloc'd string
 #define _cmderr(result, component) \
-  dev_part_strct_entry(tsv, ".cmderr", p->desc, m->desc, #component, result)
+  dev_part_strct_entry(tsv, ".cmderr", p->desc, m->desc, #component, result, NULL)
 
 #endif
diff --git a/src/lexer.l b/src/lexer.l
index 47c3cb25..a69130d3 100644
--- a/src/lexer.l
+++ b/src/lexer.l
@@ -39,6 +39,15 @@
 #define YYERRCODE 256
 #endif
 
+/* capture lvalue keywords to associate comments with that assignment */
+#define ccap() capture_lvalue_kw(yytext, cfg_lineno)
+
+static void adjust_cfg_lineno(const char *p) {
+  while(*p)
+    if(*p++ == '\n')
+      cfg_lineno++;
+}
+
 %}
 
 DIGIT    [0-9]
@@ -71,14 +80,19 @@ SIGN     [+-]
 
 0x{HEXDIGIT}+ { yylval = hexnumber(yytext); return TKN_NUMBER; }
 
-#[^\n]*\n+ { /* record and skip # comments */
-  capture_comment_str(yytext);
-  for(int i=0; yytext[i]; i++) {
-    if(yytext[i] == '\n')
-      cfg_lineno++;
-  }
+#\n#\ PROGRAMMER\ DEFINITIONS\n#\n+ { /* Record comments so far as prologue and skip */
+  cfg_capture_prologue();
+  adjust_cfg_lineno(yytext);
 }
 
+#\n#\ PART\ DEFINITIONS\n#\n+ { /* Ignore part definions header */
+  adjust_cfg_lineno(yytext);
+}
+
+[ \t]*#[^\n]*\n+ { /* Record and skip # comments including preceding white space */
+  capture_comment_str(yytext, cfg_lineno);
+  adjust_cfg_lineno(yytext);
+}
 
 "/*" {  /* The following eats multiline C style comments, they are not captured */
         int c;
@@ -108,137 +122,137 @@ SIGN     [+-]
 
 
 alias            { yylval=NULL; return K_ALIAS; }
-allowfullpagebitstream { yylval=NULL; return K_ALLOWFULLPAGEBITSTREAM; }
-avr910_devcode   { yylval=NULL; return K_AVR910_DEVCODE; }
+allowfullpagebitstream { yylval=NULL; ccap(); return K_ALLOWFULLPAGEBITSTREAM; }
+avr910_devcode   { yylval=NULL; ccap(); return K_AVR910_DEVCODE; }
 bank_size        { yylval=NULL; return K_PAGE_SIZE; }
 banked           { yylval=NULL; return K_PAGED; }
-baudrate         { yylval=NULL; return K_BAUDRATE; }
-blocksize        { yylval=NULL; return K_BLOCKSIZE; }
-bs2              { yylval=NULL; return K_BS2; }
-buff             { yylval=NULL; return K_BUFF; }
-bytedelay        { yylval=NULL; return K_BYTEDELAY; }
-chip_erase       { yylval=new_token(K_CHIP_ERASE); return K_CHIP_ERASE; }
-chip_erase_delay { yylval=NULL; return K_CHIP_ERASE_DELAY; }
-chiperasepolltimeout { yylval=NULL; return K_CHIPERASEPOLLTIMEOUT; }
-chiperasepulsewidth { yylval=NULL; return K_CHIPERASEPULSEWIDTH; }
-chiperasetime    { yylval=NULL; return K_CHIPERASETIME; }
-cmdexedelay      { yylval=NULL; return K_CMDEXEDELAY; }
-connection_type  { yylval=NULL; return K_CONNTYPE; }
+baudrate         { yylval=NULL; ccap(); return K_BAUDRATE; }
+blocksize        { yylval=NULL; ccap(); return K_BLOCKSIZE; }
+bs2              { yylval=NULL; ccap(); return K_BS2; }
+buff             { yylval=NULL; ccap(); return K_BUFF; }
+bytedelay        { yylval=NULL; ccap(); return K_BYTEDELAY; }
+chip_erase       { yylval=new_token(K_CHIP_ERASE); ccap(); return K_CHIP_ERASE; }
+chip_erase_delay { yylval=NULL; ccap(); return K_CHIP_ERASE_DELAY; }
+chiperasepolltimeout { yylval=NULL; ccap(); return K_CHIPERASEPOLLTIMEOUT; }
+chiperasepulsewidth { yylval=NULL; ccap(); return K_CHIPERASEPULSEWIDTH; }
+chiperasetime    { yylval=NULL; ccap(); return K_CHIPERASETIME; }
+cmdexedelay      { yylval=NULL; ccap(); return K_CMDEXEDELAY; }
+connection_type  { yylval=NULL; ccap(); return K_CONNTYPE; }
 dedicated        { yylval=new_token(K_DEDICATED); return K_DEDICATED; }
 default_bitclock { yylval=NULL; return K_DEFAULT_BITCLOCK; }
 default_parallel { yylval=NULL; return K_DEFAULT_PARALLEL; }
 default_programmer { yylval=NULL; return K_DEFAULT_PROGRAMMER; }
 default_serial   { yylval=NULL; return K_DEFAULT_SERIAL; }
 default_spi      { yylval=NULL; return K_DEFAULT_SPI; }
-delay            { yylval=NULL; return K_DELAY; }
-desc             { yylval=NULL; return K_DESC; }
-family_id        { yylval=NULL; return K_FAMILY_ID; }
-devicecode       { yylval=NULL; return K_DEVICECODE; }
-eecr             { yylval=NULL; return K_EECR; }
+delay            { yylval=NULL; ccap(); return K_DELAY; }
+desc             { yylval=NULL; ccap(); return K_DESC; }
+devicecode       { yylval=NULL; ccap(); return K_DEVICECODE; }
+eecr             { yylval=NULL; ccap(); return K_EECR; }
 eeprom           { yylval=NULL; return K_EEPROM; }
-eeprom_instr     { yylval=NULL; return K_EEPROM_INSTR; }
-enablepageprogramming { yylval=NULL; return K_ENABLEPAGEPROGRAMMING; }
-errled           { yylval=NULL; return K_ERRLED; }
+eeprom_instr     { yylval=NULL; ccap(); return K_EEPROM_INSTR; }
+enablepageprogramming { yylval=NULL; ccap(); return K_ENABLEPAGEPROGRAMMING; }
+errled           { yylval=NULL; ccap(); return K_ERRLED; }
+family_id        { yylval=NULL; ccap(); return K_FAMILY_ID; }
 flash            { yylval=NULL; return K_FLASH; }
-flash_instr      { yylval=NULL; return K_FLASH_INSTR; }
-has_debugwire    { yylval=NULL; return K_HAS_DW; }
-has_jtag         { yylval=NULL; return K_HAS_JTAG; }
-has_pdi          { yylval=NULL; return K_HAS_PDI; }
-has_tpi          { yylval=NULL; return K_HAS_TPI; }
-has_updi         { yylval=NULL; return K_HAS_UPDI; }
-hventerstabdelay { yylval=NULL; return K_HVENTERSTABDELAY; }
-hvleavestabdelay { yylval=NULL; return K_HVLEAVESTABDELAY; }
-hvsp_controlstack  { yylval=NULL; return K_HVSP_CONTROLSTACK; }
-hvspcmdexedelay  { yylval=NULL; return K_HVSPCMDEXEDELAY; }
-hvupdi_support   { yylval=NULL; return K_HVUPDI_SUPPORT; }
-hvupdi_variant   { yylval=NULL; return K_HVUPDI_VARIANT; }
-id               { yylval=NULL; return K_ID; }
-idr              { yylval=NULL; return K_IDR; }
+flash_instr      { yylval=NULL; ccap(); return K_FLASH_INSTR; }
+has_debugwire    { yylval=NULL; ccap(); return K_HAS_DW; }
+has_jtag         { yylval=NULL; ccap(); return K_HAS_JTAG; }
+has_pdi          { yylval=NULL; ccap(); return K_HAS_PDI; }
+has_tpi          { yylval=NULL; ccap(); return K_HAS_TPI; }
+has_updi         { yylval=NULL; ccap(); return K_HAS_UPDI; }
+hventerstabdelay { yylval=NULL; ccap(); return K_HVENTERSTABDELAY; }
+hvleavestabdelay { yylval=NULL; ccap(); return K_HVLEAVESTABDELAY; }
+hvsp_controlstack  { yylval=NULL; ccap(); return K_HVSP_CONTROLSTACK; }
+hvspcmdexedelay  { yylval=NULL; ccap(); return K_HVSPCMDEXEDELAY; }
+hvupdi_support   { yylval=NULL; ccap(); return K_HVUPDI_SUPPORT; }
+hvupdi_variant   { yylval=NULL; ccap(); return K_HVUPDI_VARIANT; }
+id               { yylval=NULL; ccap(); return K_ID; }
+idr              { yylval=NULL; ccap(); return K_IDR; }
 io               { yylval=new_token(K_IO); return K_IO; }
-is_at90s1200     { yylval=NULL; return K_IS_AT90S1200; }
-is_avr32         { yylval=NULL; return K_IS_AVR32; }
-latchcycles      { yylval=NULL; return K_LATCHCYCLES; }
-load_ext_addr    { yylval=new_token(K_LOAD_EXT_ADDR); return K_LOAD_EXT_ADDR; }
-loadpage_hi      { yylval=new_token(K_LOADPAGE_HI); return K_LOADPAGE_HI; }
-loadpage_lo      { yylval=new_token(K_LOADPAGE_LO); return K_LOADPAGE_LO; }
-max_write_delay  { yylval=NULL; return K_MAX_WRITE_DELAY; }
-mcu_base         { yylval=NULL; return K_MCU_BASE; }
-memory           { yylval=NULL; return K_MEMORY; }
-min_write_delay  { yylval=NULL; return K_MIN_WRITE_DELAY; }
-miso             { yylval=NULL; return K_MISO; }
-mode             { yylval=NULL; return K_MODE; }
-mosi             { yylval=NULL; return K_MOSI; }
+is_at90s1200     { yylval=NULL; ccap(); return K_IS_AT90S1200; }
+is_avr32         { yylval=NULL; ccap(); return K_IS_AVR32; }
+latchcycles      { yylval=NULL; ccap(); return K_LATCHCYCLES; }
+load_ext_addr    { yylval=new_token(K_LOAD_EXT_ADDR); ccap(); return K_LOAD_EXT_ADDR; }
+loadpage_hi      { yylval=new_token(K_LOADPAGE_HI); ccap(); return K_LOADPAGE_HI; }
+loadpage_lo      { yylval=new_token(K_LOADPAGE_LO); ccap(); return K_LOADPAGE_LO; }
+max_write_delay  { yylval=NULL; ccap(); return K_MAX_WRITE_DELAY; }
+mcu_base         { yylval=NULL; ccap(); return K_MCU_BASE; }
+memory           { yylval=NULL; ccap(); return K_MEMORY; }
+min_write_delay  { yylval=NULL; ccap(); return K_MIN_WRITE_DELAY; }
+miso             { yylval=NULL; ccap(); return K_MISO; }
+mode             { yylval=NULL; ccap(); return K_MODE; }
+mosi             { yylval=NULL; ccap(); return K_MOSI; }
 no               { yylval=new_token(K_NO); return K_NO; }
 NULL             { yylval=NULL; return K_NULL; }
 num_banks        { yylval=NULL; return K_NUM_PAGES; }
-num_pages        { yylval=NULL; return K_NUM_PAGES; }
-nvm_base         { yylval=NULL; return K_NVM_BASE; }
-ocd_base         { yylval=NULL; return K_OCD_BASE; }
-ocdrev           { yylval=NULL; return K_OCDREV; }
-offset           { yylval=NULL; return K_OFFSET; }
-page_size        { yylval=NULL; return K_PAGE_SIZE; }
-paged            { yylval=NULL; return K_PAGED; }
-pagel            { yylval=NULL; return K_PAGEL; }
-parallel         { yylval=NULL; return K_PARALLEL; }
+num_pages        { yylval=NULL; ccap(); return K_NUM_PAGES; }
+nvm_base         { yylval=NULL; ccap(); return K_NVM_BASE; }
+ocd_base         { yylval=NULL; ccap(); return K_OCD_BASE; }
+ocdrev           { yylval=NULL; ccap(); return K_OCDREV; }
+offset           { yylval=NULL; ccap(); return K_OFFSET; }
+paged            { yylval=NULL; ccap(); return K_PAGED; }
+pagel            { yylval=NULL; ccap(); return K_PAGEL; }
+page_size        { yylval=NULL; ccap(); return K_PAGE_SIZE; }
+parallel         { yylval=NULL; ccap(); return K_PARALLEL; }
 parent           { yylval=NULL; return K_PARENT; }
-part             { yylval=NULL; return K_PART; }
-pgm_enable       { yylval=new_token(K_PGM_ENABLE); return K_PGM_ENABLE; }
-pgmled           { yylval=NULL; return K_PGMLED; }
-pollindex        { yylval=NULL; return K_POLLINDEX; }
-pollmethod       { yylval=NULL; return K_POLLMETHOD; }
-pollvalue        { yylval=NULL; return K_POLLVALUE; }
-postdelay        { yylval=NULL; return K_POSTDELAY; }
-poweroffdelay    { yylval=NULL; return K_POWEROFFDELAY; }
-pp_controlstack  { yylval=NULL; return K_PP_CONTROLSTACK; }
-predelay         { yylval=NULL; return K_PREDELAY; }
-progmodedelay    { yylval=NULL; return K_PROGMODEDELAY; }
-programfusepolltimeout { yylval=NULL; return K_PROGRAMFUSEPOLLTIMEOUT; }
-programfusepulsewidth { yylval=NULL; return K_PROGRAMFUSEPULSEWIDTH; }
-programlockpolltimeout { yylval=NULL; return K_PROGRAMLOCKPOLLTIMEOUT; }
-programlockpulsewidth { yylval=NULL; return K_PROGRAMLOCKPULSEWIDTH; }
-programmer       { yylval=NULL; return K_PROGRAMMER; }
+part             { yylval=NULL; ccap(); return K_PART; }
+pgm_enable       { yylval=new_token(K_PGM_ENABLE); ccap(); return K_PGM_ENABLE; }
+pgmled           { yylval=NULL; ccap(); return K_PGMLED; }
+pollindex        { yylval=NULL; ccap(); return K_POLLINDEX; }
+pollmethod       { yylval=NULL; ccap(); return K_POLLMETHOD; }
+pollvalue        { yylval=NULL; ccap(); return K_POLLVALUE; }
+postdelay        { yylval=NULL; ccap(); return K_POSTDELAY; }
+poweroffdelay    { yylval=NULL; ccap(); return K_POWEROFFDELAY; }
+pp_controlstack  { yylval=NULL; ccap(); return K_PP_CONTROLSTACK; }
+predelay         { yylval=NULL; ccap(); return K_PREDELAY; }
+progmodedelay    { yylval=NULL; ccap(); return K_PROGMODEDELAY; }
+programfusepolltimeout { yylval=NULL; ccap(); return K_PROGRAMFUSEPOLLTIMEOUT; }
+programfusepulsewidth { yylval=NULL; ccap(); return K_PROGRAMFUSEPULSEWIDTH; }
+programlockpolltimeout { yylval=NULL; ccap(); return K_PROGRAMLOCKPOLLTIMEOUT; }
+programlockpulsewidth { yylval=NULL; ccap(); return K_PROGRAMLOCKPULSEWIDTH; }
+programmer       { yylval=NULL; ccap(); return K_PROGRAMMER; }
 pseudo           { yylval=new_token(K_PSEUDO); return K_PSEUDO; }
-pwroff_after_write { yylval=NULL; return K_PWROFF_AFTER_WRITE; }
-rampz            { yylval=NULL; return K_RAMPZ; }
-rdyled           { yylval=NULL; return K_RDYLED; }
-read             { yylval=new_token(K_READ); return K_READ; }
-read_hi          { yylval=new_token(K_READ_HI); return K_READ_HI; }
-read_lo          { yylval=new_token(K_READ_LO); return K_READ_LO; }
-readback         { yylval=NULL; return K_READBACK; }
-readback_p1      { yylval=NULL; return K_READBACK_P1; }
-readback_p2      { yylval=NULL; return K_READBACK_P2; }
-readsize        { yylval=NULL; return K_READSIZE; }
-reset            { yylval=new_token(K_RESET); return K_RESET; }
-resetdelay       { yylval=NULL; return K_RESETDELAY; }
-resetdelayms     { yylval=NULL; return K_RESETDELAYMS; }
-resetdelayus     { yylval=NULL; return K_RESETDELAYUS; }
-retry_pulse      { yylval=NULL; return K_RETRY_PULSE; }
-sck              { yylval=new_token(K_SCK); return K_SCK; }
-serial           { yylval=NULL; return K_SERIAL; }
-signature        { yylval=NULL; return K_SIGNATURE; }
-size             { yylval=NULL; return K_SIZE; }
+pwroff_after_write { yylval=NULL; ccap(); return K_PWROFF_AFTER_WRITE; }
+rampz            { yylval=NULL; ccap(); return K_RAMPZ; }
+rdyled           { yylval=NULL; ccap(); return K_RDYLED; }
+read             { yylval=new_token(K_READ); ccap(); return K_READ; }
+read_hi          { yylval=new_token(K_READ_HI); ccap(); return K_READ_HI; }
+read_lo          { yylval=new_token(K_READ_LO); ccap(); return K_READ_LO; }
+readback         { yylval=NULL; ccap(); return K_READBACK; }
+readback_p1      { yylval=NULL; ccap(); return K_READBACK_P1; }
+readback_p2      { yylval=NULL; ccap(); return K_READBACK_P2; }
+readsize        { yylval=NULL; ccap(); return K_READSIZE; }
+reset            { yylval=new_token(K_RESET); ccap(); return K_RESET; }
+resetdelay       { yylval=NULL; ccap(); return K_RESETDELAY; }
+resetdelayms     { yylval=NULL; ccap(); return K_RESETDELAYMS; }
+resetdelayus     { yylval=NULL; ccap(); return K_RESETDELAYUS; }
+retry_pulse      { yylval=NULL; ccap(); return K_RETRY_PULSE; }
+sck              { yylval=new_token(K_SCK); ccap(); return K_SCK; }
+serial           { yylval=NULL; ccap(); return K_SERIAL; }
+signature        { yylval=NULL; ccap(); return K_SIGNATURE; }
+size             { yylval=NULL; ccap(); return K_SIZE; }
 spi              { yylval=NULL; return K_SPI; }
-spmcr            { yylval=NULL; return K_SPMCR; }
-stabdelay        { yylval=NULL; return K_STABDELAY; }
-stk500_devcode   { yylval=NULL; return K_STK500_DEVCODE; }
-synchcycles      { yylval=NULL; return K_SYNCHCYCLES; }
-synchloops       { yylval=NULL; return K_SYNCHLOOPS; }
-timeout          { yylval=NULL; return K_TIMEOUT; }
-togglevtg        { yylval=NULL; return K_TOGGLEVTG; }
-type             { yylval=NULL; return K_TYPE; }
+spmcr            { yylval=NULL; ccap(); return K_SPMCR; }
+stabdelay        { yylval=NULL; ccap(); return K_STABDELAY; }
+stk500_devcode   { yylval=NULL; ccap(); return K_STK500_DEVCODE; }
+synchcycles      { yylval=NULL; ccap(); return K_SYNCHCYCLES; }
+synchloops       { yylval=NULL; ccap(); return K_SYNCHLOOPS; }
+timeout          { yylval=NULL; ccap(); return K_TIMEOUT; }
+togglevtg        { yylval=NULL; ccap(); return K_TOGGLEVTG; }
+type             { yylval=NULL; ccap(); return K_TYPE; }
 usb              { yylval=NULL; return K_USB; }
-usbdev           { yylval=NULL; return K_USBDEV; }
-usbpid           { yylval=NULL; return K_USBPID; }
-usbproduct       { yylval=NULL; return K_USBPRODUCT; }
-usbsn            { yylval=NULL; return K_USBSN; }
-usbvendor        { yylval=NULL; return K_USBVENDOR; }
-usbvid           { yylval=NULL; return K_USBVID; }
-vcc              { yylval=NULL; return K_VCC; }
-vfyled           { yylval=NULL; return K_VFYLED; }
-write            { yylval=new_token(K_WRITE); return K_WRITE; }
-write_hi         { yylval=new_token(K_WRITE_HI); return K_WRITE_HI; }
-write_lo         { yylval=new_token(K_WRITE_LO); return K_WRITE_LO; }
-writepage        { yylval=new_token(K_WRITEPAGE); return K_WRITEPAGE; }
+usbdev           { yylval=NULL; ccap(); return K_USBDEV; }
+usbpid           { yylval=NULL; ccap(); return K_USBPID; }
+usbproduct       { yylval=NULL; ccap(); return K_USBPRODUCT; }
+usbsn            { yylval=NULL; ccap(); return K_USBSN; }
+usbvendor        { yylval=NULL; ccap(); return K_USBVENDOR; }
+usbvid           { yylval=NULL; ccap(); return K_USBVID; }
+vcc              { yylval=NULL; ccap(); return K_VCC; }
+vfyled           { yylval=NULL; ccap(); return K_VFYLED; }
+write            { yylval=new_token(K_WRITE); ccap(); return K_WRITE; }
+write_hi         { yylval=new_token(K_WRITE_HI); ccap(); return K_WRITE_HI; }
+write_lo         { yylval=new_token(K_WRITE_LO); ccap(); return K_WRITE_LO; }
+writepage        { yylval=new_token(K_WRITEPAGE); ccap(); return K_WRITEPAGE; }
 yes              { yylval=new_token(K_YES); return K_YES; }
 
 ","       { yylval = NULL; pyytext(); return TKN_COMMA; }
diff --git a/src/libavrdude.h b/src/libavrdude.h
index cbbc8a79..d15677eb 100644
--- a/src/libavrdude.h
+++ b/src/libavrdude.h
@@ -215,7 +215,8 @@ typedef struct opcode {
 typedef struct avrpart {
   const char  * desc;               /* long part name */
   const char  * id;                 /* short part name */
-  const char  * parent_id;          /* parent id if set, for -p.../s */
+  LISTID        comments;           // Used by developer options -p*/[ASsr...]
+  const char  * parent_id;          /* Used by developer options */
   const char  * family_id;          /* family id in the SIB (avr8x) */
   int           hvupdi_variant;     /* HV pulse on UPDI pin, no pin or RESET pin */
   int           stk500_devcode;     /* stk500 device code */
@@ -285,6 +286,7 @@ typedef struct avrpart {
 #define AVR_MEMDESCLEN 64
 typedef struct avrmem {
   const char *desc;           /* memory description ("flash", "eeprom", etc) */
+  LISTID comments;            // Used by developer options -p*/[ASsr...]
   int paged;                  /* page addressed (e.g. ATmega flash) */
   int size;                   /* total memory size in bytes */
   int page_size;              /* size of memory page (if page addressed) */
@@ -684,7 +686,8 @@ typedef struct programmer_t {
   LISTID id;
   const char *desc;
   void (*initpgm)(struct programmer_t *pgm);
-  const char *parent_id;        // Used by developer options -c*/[sr...]
+  LISTID        comments;       // Used by developer options -c*/[ASsr...]
+  const char *parent_id;        // Used by developer options
   struct pindef_t pin[N_PINS];
   conntype_t conntype;
   int baudrate;
diff --git a/src/main.c b/src/main.c
index 2487faa7..ae9e5ea8 100644
--- a/src/main.c
+++ b/src/main.c
@@ -244,6 +244,14 @@ static void replace_backslashes(char *s)
   }
 }
 
+// Return 2 if string is * or starts with */, 1 if string contains /, 0 otherwise
+static int dev_opt(char *str) {
+  return
+    !str? 0:
+    !strcmp(str, "*") || !strncmp(str, "*/", 2)? 2:
+    !!strchr(str, '/');
+}
+
 
 /*
  * main routine
@@ -752,20 +760,14 @@ int main(int argc, char * argv [])
     bitclock = default_bitclock;
   }
 
+  // Developer options to print parts and/or programmer entries of avrdude.conf
+  int dev_opt_c = dev_opt(programmer); // -c <wildcard>/[sSArt]
+  int dev_opt_p = dev_opt(partdesc);   // -p <wildcard>/[dsSArcow*t]
 
-  int dev_opts = 0;
-  // Developer option -c <wildcard>/[ASsrt] prints programmer description(s) and exits
-  if(programmer && (strcmp(programmer, "*") == 0 || strchr(programmer, '/'))) {
-    dev_output_pgm_defs(cfg_strdup("main()", programmer));
-    dev_opts = 1;
-  }
-  // Developer option -p <wildcard>/[dASsrcow*t] prints part description(s) and exits
-  if(partdesc && (strcmp(partdesc, "*") == 0 || strchr(partdesc, '/'))) {
-    dev_output_part_defs(partdesc);
-    dev_opts = 1;
-  }
-  if(dev_opts)
+  if(dev_opt_c || dev_opt_p) {  // See -c/h and or -p/h
+    dev_output_pgm_part(dev_opt_c, programmer, dev_opt_p, partdesc);
     exit(0);
+  }
 
   avrdude_message(MSG_NOTICE, "\n");
 

From 8420b272335a0db7c86162ffe77f0692dfb18235 Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Fri, 12 Aug 2022 14:58:21 +0100
Subject: [PATCH 16/19] Address compiler warnings in 4 source files

---
 src/developer_opts.c |  9 +++------
 src/jtagmkII.c       |  2 --
 src/ser_win32.c      |  6 +++---
 src/stk500v2.c       | 10 +++++++---
 4 files changed, 13 insertions(+), 14 deletions(-)

diff --git a/src/developer_opts.c b/src/developer_opts.c
index e0c274b9..dc7a4063 100644
--- a/src/developer_opts.c
+++ b/src/developer_opts.c
@@ -402,14 +402,11 @@ static int avrpart_deep_copy(AVRPARTdeep *d, const AVRPART *p) {
 
   // Copy over desc, id, and family_id
   memset(d->descbuf, 0, sizeof d->descbuf);
-  if(d->descbuf)
-    strncpy(d->descbuf, p->desc, sizeof d->descbuf-1);
+  strncpy(d->descbuf, p->desc, sizeof d->descbuf-1);
   memset(d->idbuf, 0, sizeof d->idbuf);
-  if(d->idbuf)
-    strncpy(d->idbuf, p->id, sizeof d->idbuf-1);
+  strncpy(d->idbuf, p->id, sizeof d->idbuf-1);
   memset(d->family_idbuf, 0, sizeof d->family_idbuf);
-  if(d->family_idbuf)
-    strncpy(d->family_idbuf, p->family_id, sizeof d->family_idbuf-1);
+  strncpy(d->family_idbuf, p->family_id, sizeof d->family_idbuf-1);
 
   // Zap address values
   d->base.desc = NULL;
diff --git a/src/jtagmkII.c b/src/jtagmkII.c
index f52f3bc3..ef45ee22 100644
--- a/src/jtagmkII.c
+++ b/src/jtagmkII.c
@@ -488,7 +488,6 @@ static int jtagmkII_recv_frame(PROGRAMMER * pgm, unsigned char **msg,
   int rv;
   unsigned char c, *buf = NULL, header[8];
   unsigned short r_seqno = 0;
-  unsigned short checksum = 0;
 
   struct timeval tv;
   double timeoutval = 100;	/* seconds */
@@ -521,7 +520,6 @@ static int jtagmkII_recv_frame(PROGRAMMER * pgm, unsigned char **msg,
       if (serial_recv(&pgm->fd, &c, 1) != 0)
 	goto timedout;
     }
-    checksum ^= c;
 
     if (state < sDATA)
       header[headeridx++] = c;
diff --git a/src/ser_win32.c b/src/ser_win32.c
index fa4d7aba..970a3ecb 100644
--- a/src/ser_win32.c
+++ b/src/ser_win32.c
@@ -393,7 +393,7 @@ static int net_send(union filedescriptor *fd, const unsigned char * buf, size_t
 	}
 
 	while (len) {
-		rc = send(fd->ifd, p, (len > 1024) ? 1024 : len, 0);
+		rc = send(fd->ifd, (const char *) p, (len > 1024)? 1024: len, 0);
 		if (rc < 0) {
 			FormatMessage(
 				FORMAT_MESSAGE_ALLOCATE_BUFFER |
@@ -527,7 +527,7 @@ reselect:
 			}
 		}
 
-		rc = recv(fd->ifd, p, (buflen - len > 1024) ? 1024 : buflen - len, 0);
+		rc = recv(fd->ifd, (char *) p, (buflen - len > 1024)? 1024: buflen - len, 0);
 		if (rc < 0) {
 			FormatMessage(
 				FORMAT_MESSAGE_ALLOCATE_BUFFER |
@@ -693,7 +693,7 @@ static int net_drain(union filedescriptor *fd, int display)
 			}
 		}
 
-		rc = recv(fd->ifd, &buf, 1, 0);
+		rc = recv(fd->ifd, (char *) &buf, 1, 0);
 		if (rc < 0) {
 			FormatMessage(
 				FORMAT_MESSAGE_ALLOCATE_BUFFER |
diff --git a/src/stk500v2.c b/src/stk500v2.c
index a50fda4c..8f16ae41 100644
--- a/src/stk500v2.c
+++ b/src/stk500v2.c
@@ -3093,12 +3093,14 @@ static int stk500v2_setparm_real(PROGRAMMER * pgm, unsigned char parm, unsigned
 
 static int stk500v2_setparm(PROGRAMMER * pgm, unsigned char parm, unsigned char value)
 {
-  unsigned char current_value;
+  unsigned char current_value = value;
   int res;
 
   res = stk500v2_getparm(pgm, parm, &current_value);
-  if (res < 0)
+  if (res < 0) {
     avrdude_message(MSG_INFO, "%s: Unable to get parameter 0x%02x\n", progname, parm);
+    return -1;
+  }
 
   // don't issue a write if the correct value is already set.
   if (value == current_value) {
@@ -3245,7 +3247,7 @@ f_to_kHz_MHz(double f, const char **unit)
 
 static void stk500v2_print_parms1(PROGRAMMER * pgm, const char * p)
 {
-  unsigned char vtarget, vadjust, osc_pscale, osc_cmatch, sck_duration =0; //XXX 0 is not correct, check caller
+  unsigned char vtarget = 0, vadjust = 0, osc_pscale = 0, osc_cmatch = 0, sck_duration =0; //XXX 0 is not correct, check caller
   unsigned int sck_stk600, clock_conf, dac, oct, varef;
   unsigned char vtarget_jtag[4];
   int prescale;
@@ -3253,6 +3255,8 @@ static void stk500v2_print_parms1(PROGRAMMER * pgm, const char * p)
   const char *unit;
   void *mycookie;
 
+  memset(vtarget_jtag, 0, sizeof vtarget_jtag);
+
   if (PDATA(pgm)->pgmtype == PGMTYPE_JTAGICE_MKII) {
     mycookie = pgm->cookie;
     pgm->cookie = PDATA(pgm)->chained_pdata;

From 533feec4ede1fa0502de01183cfbcdd570e3005f Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Fri, 12 Aug 2022 15:52:51 +0100
Subject: [PATCH 17/19] Revert grammar to remove introduced shift/reduce
 conflicts

---
 src/config_gram.y | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/config_gram.y b/src/config_gram.y
index 2c3504f2..c4c144a7 100644
--- a/src/config_gram.y
+++ b/src/config_gram.y
@@ -1356,8 +1356,7 @@ yesno :
 mem_specs :
   mem_spec TKN_SEMI |
   mem_alias TKN_SEMI |
-  mem_specs mem_spec TKN_SEMI |
-  /* empty */
+  mem_specs mem_spec TKN_SEMI
 ;
 
 

From c9736a9db5f1c0aae4023a32ef905a6d9ea6bc1e Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Sat, 13 Aug 2022 20:51:12 +0100
Subject: [PATCH 18/19] Specifying the full memory name now always works

... even if a memory with longer name and same initial part exists
---
 src/avrpart.c | 61 +++++++++++++++------------------------------------
 1 file changed, 18 insertions(+), 43 deletions(-)

diff --git a/src/avrpart.c b/src/avrpart.c
index 526d0503..df514f1d 100644
--- a/src/avrpart.c
+++ b/src/avrpart.c
@@ -423,88 +423,63 @@ void avr_free_memalias(AVRMEM_ALIAS *m) {
 AVRMEM_ALIAS *avr_locate_memalias(const AVRPART *p, const char *desc) {
   AVRMEM_ALIAS * m, * match;
   LNODEID ln;
-  int matches;
+  int matches, exact;
   int l;
 
   if(!p || !desc || !p->mem_alias)
     return NULL;
 
   l = strlen(desc);
-  matches = 0;
+  matches = exact = 0;
   match = NULL;
   for (ln=lfirst(p->mem_alias); ln; ln=lnext(ln)) {
     m = ldata(ln);
-    if (strncmp(desc, m->desc, l) == 0) {
+    if (strncmp(m->desc, desc, l) == 0) { // Partial initial match
       match = m;
       matches++;
+      if(m->desc[l] == 0)       // Exact match between arg and memory
+        exact++;
     }
   }
 
-  if (matches == 1)
-    return match;
-
-  return NULL;
+  return exact == 1 || matches == 1? match: NULL;
 }
 
 AVRMEM *avr_locate_mem_noalias(const AVRPART *p, const char *desc) {
   AVRMEM * m, * match;
   LNODEID ln;
-  int matches;
+  int matches, exact;
   int l;
 
   if(!p || !desc || !p->mem)
     return NULL;
 
   l = strlen(desc);
-  matches = 0;
+  matches = exact = 0;
   match = NULL;
   for (ln=lfirst(p->mem); ln; ln=lnext(ln)) {
     m = ldata(ln);
-    if (strncmp(desc, m->desc, l) == 0) {
+    if (strncmp(m->desc, desc, l) == 0) { // Partial initial match
       match = m;
       matches++;
+      if(m->desc[l] == 0)       // Exact match between arg and memory
+        exact++;
     }
   }
 
-  if (matches == 1)
-    return match;
-
-  return NULL;
+  return exact == 1 || matches == 1? match: NULL;
 }
 
 
 AVRMEM *avr_locate_mem(const AVRPART *p, const char *desc) {
-  AVRMEM * m, * match;
-  AVRMEM_ALIAS * alias;
-  LNODEID ln;
-  int matches;
-  int l;
+  AVRMEM *m = avr_locate_mem_noalias(p, desc);
 
-  if(!p || !desc)
-    return NULL;
+  if(m)
+    return m;
 
-  l = strlen(desc);
-  matches = 0;
-  match = NULL;
-  if(p->mem) {
-    for (ln=lfirst(p->mem); ln; ln=lnext(ln)) {
-      m = ldata(ln);
-      if (strncmp(desc, m->desc, l) == 0) {
-        match = m;
-        matches++;
-      }
-    }
-  }
-
-  if (matches == 1)
-    return match;
-
-  /* not yet found: look for matching alias name */
-  alias = avr_locate_memalias(p, desc);
-  if (alias != NULL)
-    return alias->aliased_mem;
-
-  return NULL;
+  // Not yet found: look for matching alias name
+  AVRMEM_ALIAS *a = avr_locate_memalias(p, desc);
+  return a? a->aliased_mem: NULL;
 }
 
 AVRMEM_ALIAS *avr_find_memalias(const AVRPART *p, const AVRMEM *m_orig) {

From 297740db0e078e90387507e849cb5a7b5ded2c09 Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Sat, 13 Aug 2022 22:57:54 +0100
Subject: [PATCH 19/19] Stop listing programmers where id starts with .

---
 src/main.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/main.c b/src/main.c
index ae9e5ea8..c135ef00 100644
--- a/src/main.c
+++ b/src/main.c
@@ -140,6 +140,10 @@ static void list_programmers_callback(const char *name, const char *desc,
                                       void *cookie)
 {
     struct list_walk_cookie *c = (struct list_walk_cookie *)cookie;
+
+    if (*name == 0 || *name == '.')
+       return;
+
     if (verbose){
         fprintf(c->f, "%s%-16s = %-30s [%s:%d]\n",
                 c->prefix, name, desc, cfgname, cfglineno);