From f95a1d34482da4387b3c0fdc677d7660361c4e0f Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Mon, 18 Jul 2022 18:10:09 +0100
Subject: [PATCH 1/3] Cache config_file components in AVRPART and PROGRAMMER
 structures

Some 90% of the space of AVRPART and some 50% of PROGRAMMER is occupied by a
4 kB array config_file[] that contains the configuration file name. In
preparation of developer options that output a raw dump of the part
descriptions, this commit changes the config_file components from a large
array, which is duplicated in each part and programmer description, to a
cached string for each config file allowing for smaller raw dumps.

This commit also changes the config file name to its realpath(), eg, shortens
unwarranted `/bin/../etc/` file name components. It also changes the global
variable names `infile` and `fileno` to cfg_infile and cfg_fileno for an ever
so slight improvement of code clarity.
---
 src/avrpart.c     |  2 +-
 src/config.c      | 65 +++++++++++++++++++++++++++++++++++++++--------
 src/config.h      |  6 +++--
 src/config_gram.y | 26 +++++++++----------
 src/lexer.l       |  8 +++---
 src/libavrdude.h  |  6 ++---
 src/pgm.c         |  2 +-
 7 files changed, 80 insertions(+), 35 deletions(-)

diff --git a/src/avrpart.c b/src/avrpart.c
index d862ae24..34608155 100644
--- a/src/avrpart.c
+++ b/src/avrpart.c
@@ -568,7 +568,7 @@ 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->config_file[0] = 0;
+  p->config_file = NULL;
   p->lineno = 0;
   memset(p->signature, 0xFF, 3);
   p->ctl_stack_type = CTL_STACK_NONE;
diff --git a/src/config.c b/src/config.c
index 00ac8b0e..4308407e 100644
--- a/src/config.c
+++ b/src/config.c
@@ -50,8 +50,8 @@ LISTID       part_list;
 LISTID       programmers;
 bool         is_alias;
 
-int    lineno;
-const char * infile;
+int    cfg_lineno;
+char * cfg_infile;
 
 extern char * yytext;
 
@@ -76,8 +76,8 @@ int init_config(void)
   programmers  = lcreat(NULL, 0);
   is_alias     = false;
 
-  lineno       = 1;
-  infile       = NULL;
+  cfg_lineno   = 1;
+  cfg_infile   = NULL;
 
   return 0;
 }
@@ -99,7 +99,7 @@ int yyerror(char * errmsg, ...)
   va_start(args, errmsg);
 
   vsnprintf(message, sizeof(message), errmsg, args);
-  avrdude_message(MSG_INFO, "%s: error at %s:%d: %s\n", progname, infile, lineno, message);
+  avrdude_message(MSG_INFO, "%s: error at %s:%d: %s\n", progname, cfg_infile, cfg_lineno, message);
 
   va_end(args);
 
@@ -116,7 +116,7 @@ int yywarning(char * errmsg, ...)
   va_start(args, errmsg);
 
   vsnprintf(message, sizeof(message), errmsg, args);
-  avrdude_message(MSG_INFO, "%s: warning at %s:%d: %s\n", progname, infile, lineno, message);
+  avrdude_message(MSG_INFO, "%s: warning at %s:%d: %s\n", progname, cfg_infile, cfg_lineno, message);
 
   va_end(args);
 
@@ -329,15 +329,22 @@ int read_config(const char * file)
   FILE * f;
   int r;
 
-  f = fopen(file, "r");
-  if (f == NULL) {
-    avrdude_message(MSG_INFO, "%s: can't open config file \"%s\": %s\n",
+  if(!(cfg_infile = realpath(file, NULL))) {
+    avrdude_message(MSG_INFO, "%s: can't determine realpath() of config file \"%s\": %s\n",
             progname, file, strerror(errno));
     return -1;
   }
 
-  lineno = 1;
-  infile = file;
+  f = fopen(cfg_infile, "r");
+  if (f == NULL) {
+    avrdude_message(MSG_INFO, "%s: can't open config file \"%s\": %s\n",
+            progname, cfg_infile, strerror(errno));
+    free(cfg_infile);
+    cfg_infile = NULL;
+    return -1;
+  }
+
+  cfg_lineno = 1;
   yyin   = f;
 
   r = yyparse();
@@ -349,5 +356,41 @@ int read_config(const char * file)
 
   fclose(f);
 
+  if(cfg_infile) {
+    free(cfg_infile);
+    cfg_infile = NULL;
+  }
+
   return r;
 }
+
+
+// Linear-search cache for a few often-referenced strings
+char *cache_string(const char *file) {
+  static char **fnames;
+  static int n=0;
+
+  if(!file)
+    return NULL;
+
+  // Exists in cache?
+  for(int i=0; i<n; i++)
+    if(strcmp(fnames[i], file) == 0)
+      return fnames[i];
+
+  // Expand cache?
+  if(n%128 == 0) {
+    if(!(fnames = realloc(fnames, (n+128)*sizeof*fnames))) {
+      yyerror("cache_string(): out of memory");
+      return NULL;
+    }
+  }
+
+  fnames[n] = strdup(file);
+  if(!fnames[n]) {
+    yyerror("cache_string(): out of memory");
+    return NULL;
+  }
+
+  return fnames[n++];
+}
diff --git a/src/config.h b/src/config.h
index 914dda54..4d777c5e 100644
--- a/src/config.h
+++ b/src/config.h
@@ -50,8 +50,8 @@ extern FILE       * yyin;
 extern PROGRAMMER * current_prog;
 extern AVRPART    * current_part;
 extern AVRMEM     * current_mem;
-extern int          lineno;
-extern const char * infile;
+extern int          cfg_lineno;
+extern char       * cfg_infile;
 extern LISTID       string_list;
 extern LISTID       number_list;
 extern bool         is_alias; // current entry is alias
@@ -97,6 +97,8 @@ 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 d8c51ca2..2c6c1f46 100644
--- a/src/config_gram.y
+++ b/src/config_gram.y
@@ -288,10 +288,10 @@ prog_def :
       existing_prog = locate_programmer(programmers, id);
       if (existing_prog) {
         { /* temporarily set lineno to lineno of programmer start */
-          int temp = lineno; lineno = current_prog->lineno;
+          int temp = cfg_lineno; cfg_lineno = current_prog->lineno;
           yywarning("programmer %s overwrites previous definition %s:%d.",
                 id, existing_prog->config_file, existing_prog->lineno);
-          lineno = temp;
+          cfg_lineno = temp;
         }
         lrmv_d(programmers, existing_prog);
         pgm_free(existing_prog);
@@ -311,8 +311,8 @@ prog_decl :
         yyerror("could not create pgm instance");
         YYABORT;
       }
-      strcpy(current_prog->config_file, infile);
-      current_prog->lineno = lineno;
+      current_prog->config_file = cache_string(cfg_infile);
+      current_prog->lineno = cfg_lineno;
     }
     |
   K_PROGRAMMER K_PARENT TKN_STRING
@@ -329,8 +329,8 @@ prog_decl :
         free_token($3);
         YYABORT;
       }
-      strcpy(current_prog->config_file, infile);
-      current_prog->lineno = lineno;
+      current_prog->config_file = cache_string(cfg_infile);
+      current_prog->lineno = cfg_lineno;
       free_token($3);
     }
 ;
@@ -380,11 +380,11 @@ part_def :
       existing_part = locate_part(part_list, current_part->id);
       if (existing_part) {
         { /* temporarily set lineno to lineno of part start */
-          int temp = lineno; lineno = current_part->lineno;
+          int temp = cfg_lineno; cfg_lineno = current_part->lineno;
           yywarning("part %s overwrites previous definition %s:%d.",
                 current_part->id,
                 existing_part->config_file, existing_part->lineno);
-          lineno = temp;
+          cfg_lineno = temp;
         }
         lrmv_d(part_list, existing_part);
         avr_free_part(existing_part);
@@ -402,8 +402,8 @@ part_decl :
         yyerror("could not create part instance");
         YYABORT;
       }
-      strcpy(current_part->config_file, infile);
-      current_part->lineno = lineno;
+      current_part->config_file = cache_string(cfg_infile);
+      current_part->lineno = cfg_lineno;
     } |
   K_PART K_PARENT TKN_STRING 
     {
@@ -420,8 +420,8 @@ part_decl :
         free_token($3);
         YYABORT;
       }
-      strcpy(current_part->config_file, infile);
-      current_part->lineno = lineno;
+      current_part->config_file = cache_string(cfg_infile);
+      current_part->lineno = cfg_lineno;
 
       free_token($3);
     }
@@ -1362,7 +1362,7 @@ mem_spec :
       if (ps <= 0)
         avrdude_message(MSG_INFO,
                         "%s, line %d: invalid page size %d, ignored\n",
-                        infile, lineno, ps);
+                        cfg_infile, cfg_lineno, ps);
       else
         current_mem->page_size = ps;
       free_token($3);
diff --git a/src/lexer.l b/src/lexer.l
index 38d988db..4db95f6d 100644
--- a/src/lexer.l
+++ b/src/lexer.l
@@ -73,19 +73,19 @@ SIGN     [+-]
 #   { /* The following eats '#' style comments to end of line */
        BEGIN(comment); }
 <comment>[^\n] { /* eat comments */ }
-<comment>\n { lineno++; BEGIN(INITIAL); }
+<comment>\n { cfg_lineno++; BEGIN(INITIAL); }
 
 
 "/*" {  /* The following eats multiline C style comments */
         int c;
         int comment_start;
         
-        comment_start = lineno;
+        comment_start = cfg_lineno;
         while (1) {
           while (((c = input()) != '*') && (c != EOF)) {
             /* eat up text of comment, but keep counting lines */
             if (c == '\n')
-              lineno++;
+              cfg_lineno++;
           }
           
           if (c == '*') {
@@ -256,7 +256,7 @@ yes              { yylval=new_token(K_YES); return K_YES; }
 "("       { yylval = NULL; pyytext(); return TKN_LEFT_PAREN; }
 ")"       { yylval = NULL; pyytext(); return TKN_RIGHT_PAREN; }
 
-"\n"      { lineno++; }
+"\n"      { cfg_lineno++; }
 [ \r\t]+  { /* ignore whitespace */ }
 
 c: { yyerror("possible old-style config file entry\n"
diff --git a/src/libavrdude.h b/src/libavrdude.h
index 36ad9aa8..887c5227 100644
--- a/src/libavrdude.h
+++ b/src/libavrdude.h
@@ -277,8 +277,8 @@ typedef struct avrpart {
 
   LISTID        mem;                /* avr memory definitions */
   LISTID        mem_alias;          /* memory alias definitions */
-  char          config_file[PATH_MAX]; /* config file where defined */
-  int           lineno;                /* config file line number */
+  char          *config_file;       /* config file where defined */
+  int           lineno;             /* config file line number */
 } AVRPART;
 
 #define AVR_MEMDESCLEN 64
@@ -726,7 +726,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[PATH_MAX]; /* config file where defined */
+  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 */
diff --git a/src/pgm.c b/src/pgm.c
index 4580cbbd..d85f35e4 100644
--- a/src/pgm.c
+++ b/src/pgm.c
@@ -79,7 +79,7 @@ PROGRAMMER * pgm_new(void)
   pgm->usbpid = lcreat(NULL, 0);
   pgm->desc[0] = 0;
   pgm->type[0] = 0;
-  pgm->config_file[0] = 0;
+  pgm->config_file = NULL;
   pgm->lineno = 0;
   pgm->baudrate = 0;
   pgm->initpgm = NULL;

From eba67e56fccbb6ebf73c187f8753d3cfd37b3858 Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Tue, 19 Jul 2022 07:42:44 +0100
Subject: [PATCH 2/3] Make realpath() available for MSC and MINGW32

---
 src/config_gram.y | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/config_gram.y b/src/config_gram.y
index 2c6c1f46..fa74bec9 100644
--- a/src/config_gram.y
+++ b/src/config_gram.y
@@ -30,6 +30,10 @@
 #include "libavrdude.h"
 #include "config.h"
 
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#define realpath(N,R) _fullpath((R), (N), PATH_MAX)
+#endif
+
 #if defined(WIN32)
 #define strtok_r( _s, _sep, _lasts ) \
     ( *(_lasts) = strtok( (_s), (_sep) ) )

From e52bd2b99bcf592caccf84ec7790b479a27cfc68 Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Tue, 19 Jul 2022 08:05:42 +0100
Subject: [PATCH 3/3] Move realpath() compatibility definition from
 config_gram.y to config.h

---
 src/config.h      | 4 ++++
 src/config_gram.y | 4 ----
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/config.h b/src/config.h
index 4d777c5e..a20af733 100644
--- a/src/config.h
+++ b/src/config.h
@@ -25,6 +25,10 @@
 
 #include "libavrdude.h"
 
+#if defined(WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
+#define realpath(N,R) _fullpath((R), (N), PATH_MAX)
+#endif
+
 
 #define MAX_STR_CONST 1024
 
diff --git a/src/config_gram.y b/src/config_gram.y
index fa74bec9..2c6c1f46 100644
--- a/src/config_gram.y
+++ b/src/config_gram.y
@@ -30,10 +30,6 @@
 #include "libavrdude.h"
 #include "config.h"
 
-#if defined(_MSC_VER) || defined(__MINGW32__)
-#define realpath(N,R) _fullpath((R), (N), PATH_MAX)
-#endif
-
 #if defined(WIN32)
 #define strtok_r( _s, _sep, _lasts ) \
     ( *(_lasts) = strtok( (_s), (_sep) ) )