From ce5f174281847098dbc36f0eb7d285a6668bd722 Mon Sep 17 00:00:00 2001
From: sjplimp <sjplimp@f3b2605a-c512-4ea7-a41b-209d697bcdaa>
Date: Mon, 1 Aug 2016 20:35:57 +0000
Subject: [PATCH] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@15414
 f3b2605a-c512-4ea7-a41b-209d697bcdaa

---
 src/dump_cfg.cpp     |  75 +++++++++++-------------
 src/dump_custom.cpp  |  49 ++++++++++++----
 src/dump_custom.h    |   3 +
 src/fix_ave_atom.cpp | 130 +++++++++++++++++++++++------------------
 src/fix_ave_time.cpp | 134 +++++++++++++++---------------------------
 src/fix_ave_time.h   |   2 +-
 src/input.cpp        | 136 +++++++++++++++++++++++++++++++++++++++++++
 src/input.h          |   1 +
 src/thermo.cpp       |  35 +++++++----
 9 files changed, 356 insertions(+), 209 deletions(-)

diff --git a/src/dump_cfg.cpp b/src/dump_cfg.cpp
index 9005c3924a..477cdf3b71 100755
--- a/src/dump_cfg.cpp
+++ b/src/dump_cfg.cpp
@@ -46,59 +46,52 @@ DumpCFG::DumpCFG(LAMMPS *lmp, int narg, char **arg) :
 {
   multifile_override = 0;
 
-  if (narg < 10 ||
-      strcmp(arg[5],"mass") != 0 || strcmp(arg[6],"type") != 0 ||
-      (strcmp(arg[7],"xs") != 0 && strcmp(arg[7],"xsu") != 0) ||
-      (strcmp(arg[8],"ys") != 0 && strcmp(arg[8],"ysu") != 0) ||
-      (strcmp(arg[9],"zs") != 0 && strcmp(arg[9],"zsu") != 0))
+  // use earg instead of original arg since it includes expanded wildcards
+  // earg was created by parent DumpCustom
+
+  if (nfield < 5 ||
+      strcmp(earg[0],"mass") != 0 || strcmp(earg[1],"type") != 0 ||
+      (strcmp(earg[2],"xs") != 0 && strcmp(earg[2],"xsu") != 0) ||
+      (strcmp(earg[3],"ys") != 0 && strcmp(earg[3],"ysu") != 0) ||
+      (strcmp(earg[4],"zs") != 0 && strcmp(earg[4],"zsu") != 0))
     error->all(FLERR,"Dump cfg arguments must start with "
                "'mass type xs ys zs' or 'mass type xsu ysu zsu'");
 
-  if (strcmp(arg[7],"xs") == 0)
-    if (strcmp(arg[8],"ysu") == 0 || strcmp(arg[9],"zsu") == 0)
+  if (strcmp(earg[2],"xs") == 0) {
+    if (strcmp(earg[3],"ysu") == 0 || strcmp(earg[4],"zsu") == 0)
       error->all(FLERR,
                  "Dump cfg arguments can not mix xs|ys|zs with xsu|ysu|zsu");
-    else unwrapflag = 0;
-  else if (strcmp(arg[8],"ys") == 0 || strcmp(arg[9],"zs") == 0)
-    error->all(FLERR,
-               "Dump cfg arguments can not mix xs|ys|zs with xsu|ysu|zsu");
-  else unwrapflag = 1;
+    unwrapflag = 0;
+  } else {
+    if (strcmp(earg[3],"ys") == 0 || strcmp(earg[4],"zs") == 0)
+      error->all(FLERR,
+                 "Dump cfg arguments can not mix xs|ys|zs with xsu|ysu|zsu");
+    unwrapflag = 1;
+  }
 
   // setup auxiliary property name strings
-  // convert 'X_ID[m]' (X=c,f,v) to 'ID_m'
+  // convert 'X_ID[m]' (X=c,f,v) to 'X_ID_m'
 
-  if (narg > 10) auxname = new char*[narg-10];
+  if (nfield > 5) auxname = new char*[nfield];
   else auxname = NULL;
 
   int i = 0;
-  for (int iarg = 10; iarg < narg; iarg++, i++) {
-    if (strncmp(arg[iarg],"c_",2) == 0 ||
-        strncmp(arg[iarg],"f_",2) == 0 ||
-        strncmp(arg[iarg],"v_",2) == 0) {
-      int n = strlen(arg[iarg]);
-      char *suffix = new char[n];
-      strcpy(suffix,&arg[iarg][2]);
-
-      char *ptr = strchr(suffix,'[');
-      if (ptr) {
-        if (suffix[strlen(suffix)-1] != ']')
-          error->all(FLERR,"Invalid keyword in dump cfg command");
-        *ptr = '\0';
-        *(ptr+2) = '\0';
-        auxname[i] = new char[strlen(suffix) + 3];
-        strcpy(auxname[i],suffix);
-        strcat(auxname[i],"_");
-        strcat(auxname[i],ptr+1);
-      } else {
-        auxname[i] = new char[strlen(suffix) + 1];
-        strcpy(auxname[i],suffix);
-      }
-
-      delete [] suffix;
+  for (int iarg = 5; iarg < nfield; iarg++, i++) {
+    if ((strncmp(earg[iarg],"c_",2) == 0 ||
+         strncmp(earg[iarg],"f_",2) == 0 ||
+         strncmp(earg[iarg],"v_",2) == 0) && strchr(earg[iarg],'[')) {
+      char *ptr = strchr(earg[iarg],'[');
+      char *ptr2 = strchr(ptr,']');
+      auxname[i] = new char[strlen(earg[iarg])];
+      *ptr = '\0';
+      *ptr2 = '\0';
+      strcpy(auxname[i],earg[iarg]);
+      strcat(auxname[i],"_");
+      strcat(auxname[i],ptr+1);
 
     } else {
-      auxname[i] = new char[strlen(arg[iarg]) + 1];
-      strcpy(auxname[i],arg[iarg]);
+      auxname[i] = new char[strlen(earg[iarg]) + 1];
+      strcpy(auxname[i],earg[iarg]);
     }
   }
 }
@@ -108,7 +101,7 @@ DumpCFG::DumpCFG(LAMMPS *lmp, int narg, char **arg) :
 DumpCFG::~DumpCFG()
 {
   if (auxname) {
-    for (int i = 0; i < nfield-5; i++) delete [] auxname[i];
+    for (int i = 0; i < nfield; i++) delete [] auxname[i];
     delete [] auxname;
   }
 }
diff --git a/src/dump_custom.cpp b/src/dump_custom.cpp
index 1e7356d0a1..d48e6270e4 100644
--- a/src/dump_custom.cpp
+++ b/src/dump_custom.cpp
@@ -62,9 +62,17 @@ DumpCustom::DumpCustom(LAMMPS *lmp, int narg, char **arg) :
   nevery = force->inumeric(FLERR,arg[3]);
   if (nevery <= 0) error->all(FLERR,"Illegal dump custom command");
 
-  // size_one may be shrunk below if additional optional args exist
+  // expand args if any have wildcard character "*"
+  // ok to include trailing optional args,
+  //   so long as they do not have "*" between square brackets
+  // nfield may be shrunk below if extra optional args exist
+
+  expand = 0;
+  nfield = input->expand_args(narg-5,&arg[5],1,earg);
+  if (earg != &arg[5]) expand = 1;
+
+  // allocate field vectors
 
-  size_one = nfield = narg - 5;
   pack_choice = new FnPtrPack[nfield];
   vtype = new int[nfield];
 
@@ -100,15 +108,24 @@ DumpCustom::DumpCustom(LAMMPS *lmp, int narg, char **arg) :
   flag_custom = NULL;
 
   // process attributes
-  // ioptional = start of additional optional args
-  // only dump image and dump movie styles process optional args
+  // ioptional = start of additional optional args in expanded args
 
-  ioptional = parse_fields(narg,arg);
+  ioptional = parse_fields(nfield,earg);
 
-  if (ioptional < narg &&
+  if (ioptional < nfield &&
       strcmp(style,"image") != 0 && strcmp(style,"movie") != 0)
     error->all(FLERR,"Invalid attribute in dump custom command");
-  size_one = nfield = ioptional - 5;
+
+  // noptional = # of optional args
+  // reset nfield to subtract off optional args
+  // reset ioptional to what it would be in original arg list
+  // only dump image and dump movie styles process optional args,
+  //   they do not use expanded earg list
+
+  int noptional = nfield - ioptional;
+  nfield -= noptional;
+  size_one = nfield;
+  ioptional = narg - noptional;
 
   // atom selection arrays
 
@@ -140,11 +157,11 @@ DumpCustom::DumpCustom(LAMMPS *lmp, int narg, char **arg) :
   // setup column string
 
   int n = 0;
-  for (int iarg = 5; iarg < narg; iarg++) n += strlen(arg[iarg]) + 2;
+  for (int iarg = 0; iarg < nfield; iarg++) n += strlen(earg[iarg]) + 2;
   columns = new char[n];
   columns[0] = '\0';
-  for (int iarg = 5; iarg < narg; iarg++) {
-    strcat(columns,arg[iarg]);
+  for (int iarg = 0; iarg < nfield; iarg++) {
+    strcat(columns,earg[iarg]);
     strcat(columns," ");
   }
 }
@@ -153,6 +170,14 @@ DumpCustom::DumpCustom(LAMMPS *lmp, int narg, char **arg) :
 
 DumpCustom::~DumpCustom()
 {
+  // if wildcard expansion occurred, free earg memory from expand_args()
+  // could not do in constructor, b/c some derived classes process earg
+
+  if (expand) {
+    for (int i = 0; i < nfield; i++) delete [] earg[i];
+    memory->sfree(earg);
+  }
+
   delete [] pack_choice;
   delete [] vtype;
   memory->destroy(field2index);
@@ -1018,8 +1043,8 @@ int DumpCustom::parse_fields(int narg, char **arg)
   // customize by adding to if statement
 
   int i;
-  for (int iarg = 5; iarg < narg; iarg++) {
-    i = iarg-5;
+  for (int iarg = 0; iarg < narg; iarg++) {
+    i = iarg;
 
     if (strcmp(arg[iarg],"id") == 0) {
       pack_choice[i] = &DumpCustom::pack_id;
diff --git a/src/dump_custom.h b/src/dump_custom.h
index 79e90c1d85..3239a3283f 100644
--- a/src/dump_custom.h
+++ b/src/dump_custom.h
@@ -38,6 +38,9 @@ class DumpCustom : public Dump {
   int *thresh_op;            // threshhold operation for each nthresh
   double *thresh_value;      // threshhold value for each nthresh
 
+  int expand;                // flag for whether field args were expanded
+  char **earg;               // field names with wildcard expansion
+
   int *vtype;                // type of each vector (INT, DOUBLE)
   char **vformat;            // format string for each vector element
 
diff --git a/src/fix_ave_atom.cpp b/src/fix_ave_atom.cpp
index 96169b7083..9b0375baec 100644
--- a/src/fix_ave_atom.cpp
+++ b/src/fix_ave_atom.cpp
@@ -43,76 +43,92 @@ FixAveAtom::FixAveAtom(LAMMPS *lmp, int narg, char **arg) :
   nrepeat = force->inumeric(FLERR,arg[4]);
   peratom_freq = force->inumeric(FLERR,arg[5]);
 
-  // parse remaining values
-
-  which = new int[narg-6];
-  argindex = new int[narg-6];
-  ids = new char*[narg-6];
-  value2index = new int[narg-6];
-  nvalues = 0;
-
-  int iarg = 6;
-  while (iarg < narg) {
-    ids[nvalues] = NULL;
-
-    if (strcmp(arg[iarg],"x") == 0) {
-      which[nvalues] = X;
-      argindex[nvalues++] = 0;
-    } else if (strcmp(arg[iarg],"y") == 0) {
-      which[nvalues] = X;
-      argindex[nvalues++] = 1;
-    } else if (strcmp(arg[iarg],"z") == 0) {
-      which[nvalues] = X;
-      argindex[nvalues++] = 2;
-
-    } else if (strcmp(arg[iarg],"vx") == 0) {
-      which[nvalues] = V;
-      argindex[nvalues++] = 0;
-    } else if (strcmp(arg[iarg],"vy") == 0) {
-      which[nvalues] = V;
-      argindex[nvalues++] = 1;
-    } else if (strcmp(arg[iarg],"vz") == 0) {
-      which[nvalues] = V;
-      argindex[nvalues++] = 2;
-
-    } else if (strcmp(arg[iarg],"fx") == 0) {
-      which[nvalues] = F;
-      argindex[nvalues++] = 0;
-    } else if (strcmp(arg[iarg],"fy") == 0) {
-      which[nvalues] = F;
-      argindex[nvalues++] = 1;
-    } else if (strcmp(arg[iarg],"fz") == 0) {
-      which[nvalues] = F;
-      argindex[nvalues++] = 2;
-
-    } else if (strncmp(arg[iarg],"c_",2) == 0 ||
-               strncmp(arg[iarg],"f_",2) == 0 ||
-               strncmp(arg[iarg],"v_",2) == 0) {
-      if (arg[iarg][0] == 'c') which[nvalues] = COMPUTE;
-      else if (arg[iarg][0] == 'f') which[nvalues] = FIX;
-      else if (arg[iarg][0] == 'v') which[nvalues] = VARIABLE;
-
-      int n = strlen(arg[iarg]);
+  nvalues = narg - 6;
+
+  // expand args if any have wildcard character "*"
+  // this can reset nvalues
+
+  int expand = 0;
+  char **earg,**arghold;
+  nvalues = input->expand_args(nvalues,&arg[6],1,earg);
+
+  if (earg != &arg[6]) expand = 1;
+  arghold = arg;
+  arg = earg;
+
+  // parse values
+
+  which = new int[nvalues];
+  argindex = new int[nvalues];
+  ids = new char*[nvalues];
+  value2index = new int[nvalues];
+
+  for (int i = 0; i < nvalues; i++) {
+    ids[i] = NULL;
+
+    if (strcmp(arg[i],"x") == 0) {
+      which[i] = X;
+      argindex[i++] = 0;
+    } else if (strcmp(arg[i],"y") == 0) {
+      which[i] = X;
+      argindex[i++] = 1;
+    } else if (strcmp(arg[i],"z") == 0) {
+      which[i] = X;
+      argindex[i++] = 2;
+
+    } else if (strcmp(arg[i],"vx") == 0) {
+      which[i] = V;
+      argindex[i++] = 0;
+    } else if (strcmp(arg[i],"vy") == 0) {
+      which[i] = V;
+      argindex[i++] = 1;
+    } else if (strcmp(arg[i],"vz") == 0) {
+      which[i] = V;
+      argindex[i++] = 2;
+
+    } else if (strcmp(arg[i],"fx") == 0) {
+      which[i] = F;
+      argindex[i++] = 0;
+    } else if (strcmp(arg[i],"fy") == 0) {
+      which[i] = F;
+      argindex[i++] = 1;
+    } else if (strcmp(arg[i],"fz") == 0) {
+      which[i] = F;
+      argindex[i++] = 2;
+
+    } else if (strncmp(arg[i],"c_",2) == 0 ||
+               strncmp(arg[i],"f_",2) == 0 ||
+               strncmp(arg[i],"v_",2) == 0) {
+      if (arg[i][0] == 'c') which[i] = COMPUTE;
+      else if (arg[i][0] == 'f') which[i] = FIX;
+      else if (arg[i][0] == 'v') which[i] = VARIABLE;
+
+      int n = strlen(arg[i]);
       char *suffix = new char[n];
-      strcpy(suffix,&arg[iarg][2]);
+      strcpy(suffix,&arg[i][2]);
 
       char *ptr = strchr(suffix,'[');
       if (ptr) {
         if (suffix[strlen(suffix)-1] != ']')
           error->all(FLERR,"Illegal fix ave/atom command");
-        argindex[nvalues] = atoi(ptr+1);
+        argindex[i] = atoi(ptr+1);
         *ptr = '\0';
-      } else argindex[nvalues] = 0;
+      } else argindex[i] = 0;
 
       n = strlen(suffix) + 1;
-      ids[nvalues] = new char[n];
-      strcpy(ids[nvalues],suffix);
-      nvalues++;
+      ids[i] = new char[n];
+      strcpy(ids[i],suffix);
       delete [] suffix;
 
     } else error->all(FLERR,"Illegal fix ave/atom command");
+  }
+
+  // if wildcard expansion occurred, free earg memory from exapnd_args()
 
-    iarg++;
+  if (expand) {
+    for (int i = 0; i < nvalues; i++) delete [] earg[i];
+    memory->sfree(earg);
+    arg = arghold;
   }
 
   // setup and error check
diff --git a/src/fix_ave_time.cpp b/src/fix_ave_time.cpp
index 588aca4738..56a3900f0e 100644
--- a/src/fix_ave_time.cpp
+++ b/src/fix_ave_time.cpp
@@ -73,89 +73,46 @@ FixAveTime::FixAveTime(LAMMPS *lmp, int narg, char **arg) :
 
   if (nvalues == 0) error->all(FLERR,"No values in fix ave/time command");
 
-  options(narg,arg);
+  options(iarg,narg,arg);
 
-  // parse values until one isn't recognized
-  // if mode = VECTOR and value is a global array:
-  //   expand it as if columns listed one by one
-  //   adjust nvalues accordingly via maxvalues
+  // expand args if any have wildcard character "*"
+  // this can reset nvalues
 
-  which = argindex = value2index = offcol = varlen = NULL;
-  ids = NULL;
-  int maxvalues = nvalues;
-  allocate_values(maxvalues);
-  nvalues = 0;
+  int expand = 0;
+  char **earg,**arghold;
+  nvalues = input->expand_args(nvalues,&arg[6],mode,earg);
 
-  iarg = 6;
-  while (iarg < narg) {
-    if (strncmp(arg[iarg],"c_",2) == 0 ||
-        strncmp(arg[iarg],"f_",2) == 0 ||
-        strncmp(arg[iarg],"v_",2) == 0) {
-      if (arg[iarg][0] == 'c') which[nvalues] = COMPUTE;
-      else if (arg[iarg][0] == 'f') which[nvalues] = FIX;
-      else if (arg[iarg][0] == 'v') which[nvalues] = VARIABLE;
-
-      int n = strlen(arg[iarg]);
-      char *suffix = new char[n];
-      strcpy(suffix,&arg[iarg][2]);
-
-      char *ptr = strchr(suffix,'[');
-      if (ptr) {
-        if (suffix[strlen(suffix)-1] != ']')
-          error->all(FLERR,"Illegal fix ave/time command");
-        argindex[nvalues] = atoi(ptr+1);
-        *ptr = '\0';
-      } else argindex[nvalues] = 0;
-
-      n = strlen(suffix) + 1;
-      ids[nvalues] = new char[n];
-      strcpy(ids[nvalues],suffix);
-      delete [] suffix;
-
-      if (mode == VECTOR && which[nvalues] == COMPUTE &&
-          argindex[nvalues] == 0) {
-        int icompute = modify->find_compute(ids[nvalues]);
-        if (icompute < 0)
-          error->all(FLERR,"Compute ID for fix ave/time does not exist");
-        if (modify->compute[icompute]->array_flag) {
-          int ncols = modify->compute[icompute]->size_array_cols;
-          maxvalues += ncols-1;
-          allocate_values(maxvalues);
-          argindex[nvalues] = 1;
-          for (int icol = 1; icol < ncols; icol++) {
-            which[nvalues+icol] = which[nvalues];
-            argindex[nvalues+icol] = icol+1;
-            n = strlen(ids[nvalues]) + 1;
-            ids[nvalues+icol] = new char[n];
-            strcpy(ids[nvalues+icol],ids[nvalues]);
-          }
-          nvalues += ncols-1;
-        }
+  if (earg != &arg[6]) expand = 1;
+  arghold = arg;
+  arg = earg;
 
-      } else if (mode == VECTOR && which[nvalues] == FIX &&
-                 argindex[nvalues] == 0) {
-        int ifix = modify->find_fix(ids[nvalues]);
-        if (ifix < 0)
-          error->all(FLERR,"Fix ID for fix ave/time does not exist");
-        if (modify->fix[ifix]->array_flag) {
-          int ncols = modify->fix[ifix]->size_array_cols;
-          maxvalues += ncols-1;
-          allocate_values(maxvalues);
-          argindex[nvalues] = 1;
-          for (int icol = 1; icol < ncols; icol++) {
-            which[nvalues+icol] = which[nvalues];
-            argindex[nvalues+icol] = icol+1;
-            n = strlen(ids[nvalues]) + 1;
-            ids[nvalues+icol] = new char[n];
-            strcpy(ids[nvalues+icol],ids[nvalues]);
-          }
-          nvalues += ncols-1;
-        }
-      }
+  // parse values
 
-      nvalues++;
-      iarg++;
-    } else break;
+  which = argindex = value2index = offcol = varlen = NULL;
+  ids = NULL;
+  allocate_values(nvalues);
+
+  for (int i = 0; i < nvalues; i++) {
+    if (arg[i][0] == 'c') which[i] = COMPUTE;
+    else if (arg[i][0] == 'f') which[i] = FIX;
+    else if (arg[i][0] == 'v') which[i] = VARIABLE;
+
+    int n = strlen(arg[i]);
+    char *suffix = new char[n];
+    strcpy(suffix,&arg[i][2]);
+
+    char *ptr = strchr(suffix,'[');
+    if (ptr) {
+      if (suffix[strlen(suffix)-1] != ']')
+        error->all(FLERR,"Illegal fix ave/time command");
+      argindex[i] = atoi(ptr+1);
+      *ptr = '\0';
+    } else argindex[i] = 0;
+    
+    n = strlen(suffix) + 1;
+    ids[i] = new char[n];
+    strcpy(ids[i],suffix);
+    delete [] suffix;
   }
 
   // set off columns now that nvalues is finalized
@@ -311,18 +268,13 @@ FixAveTime::FixAveTime(LAMMPS *lmp, int narg, char **arg) :
     if (title2) fprintf(fp,"%s\n",title2);
     else if (mode == SCALAR) {
       fprintf(fp,"# TimeStep");
-      for (int i = 0; i < nvalues; i++) fprintf(fp," %s",arg[6+i]);
+      for (int i = 0; i < nvalues; i++) fprintf(fp," %s",earg[i]);
       fprintf(fp,"\n");
     } else fprintf(fp,"# TimeStep Number-of-rows\n");
     if (title3 && mode == VECTOR) fprintf(fp,"%s\n",title3);
     else if (mode == VECTOR) {
       fprintf(fp,"# Row");
-      for (int i = 0; i < nvalues; i++) {
-        if (which[i] == COMPUTE) fprintf(fp," c_%s",ids[i]);
-        else if (which[i] == FIX) fprintf(fp," f_%s",ids[i]);
-        else if (which[i] == VARIABLE) fprintf(fp," v_%s",ids[i]);
-        if (argindex[i]) fprintf(fp,"[%d]",argindex[i]);
-      }
+      for (int i = 0; i < nvalues; i++) fprintf(fp," %s",earg[i]);
       fprintf(fp,"\n");
     }
     if (ferror(fp))
@@ -335,6 +287,15 @@ FixAveTime::FixAveTime(LAMMPS *lmp, int narg, char **arg) :
   delete [] title2;
   delete [] title3;
 
+  // if wildcard expansion occurred, free earg memory from expand_args()
+  // wait to do this after file comment lines are printed
+
+  if (expand) {
+    for (int i = 0; i < nvalues; i++) delete [] earg[i];
+    memory->sfree(earg);
+    arg = arghold;
+  }
+
   // allocate memory for averaging
 
   vector = vector_total = NULL;
@@ -1049,7 +1010,7 @@ double FixAveTime::compute_array(int i, int j)
    parse optional args
 ------------------------------------------------------------------------- */
 
-void FixAveTime::options(int narg, char **arg)
+void FixAveTime::options(int iarg, int narg, char **arg)
 {
   // option defaults
 
@@ -1068,7 +1029,6 @@ void FixAveTime::options(int narg, char **arg)
 
   // optional args
 
-  int iarg = 6 + nvalues;
   while (iarg < narg) {
     if (strcmp(arg[iarg],"file") == 0) {
       if (iarg+2 > narg) error->all(FLERR,"Illegal fix ave/time command");
diff --git a/src/fix_ave_time.h b/src/fix_ave_time.h
index 98b665140c..f2f79f1276 100644
--- a/src/fix_ave_time.h
+++ b/src/fix_ave_time.h
@@ -69,7 +69,7 @@ class FixAveTime : public Fix {
   int column_length(int);
   void invoke_scalar(bigint);
   void invoke_vector(bigint);
-  void options(int, char **);
+  void options(int, int, char **);
   void allocate_values(int);
   void allocate_arrays();
   bigint nextvalid();
diff --git a/src/input.cpp b/src/input.cpp
index 7983f81532..dda64856dc 100644
--- a/src/input.cpp
+++ b/src/input.cpp
@@ -36,6 +36,7 @@
 #include "min.h"
 #include "modify.h"
 #include "compute.h"
+#include "fix.h"
 #include "bond.h"
 #include "angle.h"
 #include "dihedral.h"
@@ -583,6 +584,141 @@ void Input::substitute(char *&str, char *&str2, int &max, int &max2, int flag)
   strcpy(str,str2);
 }
 
+/* ----------------------------------------------------------------------
+   expand arg to earg, for arguments with syntax c_ID[*] or f_ID[*]
+   fields to consider in input arg range from iarg to narg
+   return new expanded # of values, and copy them w/out "*" into earg
+   if any expansion occurs, earg is new allocation, must be freed by caller
+   if no expansion occurs, earg just points to arg, caller need not free
+------------------------------------------------------------------------- */
+
+int Input::expand_args(int narg, char **arg, int mode, char **&earg)
+{
+  int n,iarg,index,nlo,nhi,nmax,which,expandflag,icompute,ifix;
+  char *ptr1,*ptr2,*str;
+
+  ptr1 = NULL;
+  for (iarg = 0; iarg < narg; iarg++) {
+    ptr1 = strchr(arg[iarg],'*');
+    if (ptr1) break;
+  }
+
+  if (!ptr1) {
+    earg = arg;
+    return narg;
+  }
+
+  // maxarg should always end up equal to newarg, so caller can free earg
+
+  int maxarg = narg-iarg;
+  earg = (char **) memory->smalloc(maxarg*sizeof(char *),"input:earg");
+
+  int newarg = 0;
+  for (iarg = 0; iarg < narg; iarg++) {
+    expandflag = 0;
+
+    if (strncmp(arg[iarg],"c_",2) == 0 ||
+        strncmp(arg[iarg],"f_",2) == 0) {
+
+      ptr1 = strchr(&arg[iarg][2],'[');
+      if (ptr1) {
+	ptr2 = strchr(ptr1,']');
+	if (ptr2) {
+	  *ptr2 = '\0';
+	  if (strchr(ptr1,'*')) {
+	    if (arg[iarg][0] == 'c') {
+	      *ptr1 = '\0';
+	      icompute = modify->find_compute(&arg[iarg][2]);
+	      *ptr1 = '[';
+
+              // check for global vector/array, peratom array, local array
+
+	      if (icompute >= 0) {
+		if (mode == 0 && modify->compute[icompute]->vector_flag) {
+		  nmax = modify->compute[icompute]->size_vector;
+		  expandflag = 1;
+		} else if (mode == 1 && modify->compute[icompute]->array_flag) {
+		  nmax = modify->compute[icompute]->size_array_cols;
+		  expandflag = 1;
+                } else if (modify->compute[icompute]->peratom_flag && 
+                           modify->compute[icompute]->size_peratom_cols) {
+		  nmax = modify->compute[icompute]->size_peratom_cols;
+		  expandflag = 1;
+                } else if (modify->compute[icompute]->local_flag && 
+                           modify->compute[icompute]->size_local_cols) {
+		  nmax = modify->compute[icompute]->size_local_cols;
+		  expandflag = 1;
+		}
+	      }	      
+	    } else if (arg[iarg][0] == 'f') {
+	      *ptr1 = '\0';
+	      ifix = modify->find_fix(&arg[iarg][2]);
+	      *ptr1 = '[';
+
+              // check for global vector/array, peratom array, local array
+
+	      if (ifix >= 0) {
+		if (mode == 0 && modify->fix[ifix]->vector_flag) {
+		  nmax = modify->fix[ifix]->size_vector;
+		  expandflag = 1;
+		} else if (mode == 1 && modify->fix[ifix]->array_flag) {
+		  nmax = modify->fix[ifix]->size_array_cols;
+		  expandflag = 1;
+                } else if (modify->fix[ifix]->peratom_flag && 
+                           modify->fix[ifix]->size_peratom_cols) {
+		  nmax = modify->fix[ifix]->size_peratom_cols;
+		  expandflag = 1;
+                } else if (modify->fix[ifix]->local_flag && 
+                           modify->fix[ifix]->size_local_cols) {
+		  nmax = modify->fix[ifix]->size_local_cols;
+		  expandflag = 1;
+		}
+	      }
+	    }
+	  }
+	  *ptr2 = ']';
+	}
+      }
+    }
+
+    if (expandflag) {
+      *ptr2 = '\0';
+      force->bounds(ptr1+1,nmax,nlo,nhi);
+      *ptr2 = ']';
+      if (newarg+nhi-nlo+1 > maxarg) {
+	maxarg += nhi-nlo+1;
+	earg = (char **) 
+          memory->srealloc(earg,maxarg*sizeof(char *),"input:earg");
+      }
+      for (index = nlo; index <= nhi; index++) {
+	n = strlen(arg[iarg]) + 16;   // 16 = space for large inserted integer
+	str = earg[newarg] = new char[n];
+	strncpy(str,arg[iarg],ptr1+1-arg[iarg]);
+	sprintf(&str[ptr1+1-arg[iarg]],"%d",index);
+	strcat(str,ptr2);
+        newarg++;
+      }
+
+    } else {
+      if (newarg == maxarg) {
+	maxarg++;
+	earg = (char **) 
+          memory->srealloc(earg,maxarg*sizeof(char *),"input:earg");
+      }
+      n = strlen(arg[iarg]) + 1;
+      earg[newarg] = new char[n];
+      strcpy(earg[newarg],arg[iarg]);
+      newarg++;
+    }
+  }
+
+  //printf("NEWARG %d\n",newarg);
+  //for (int i = 0; i < newarg; i++)
+  //  printf("  arg %d: %s\n",i,earg[i]);
+
+  return newarg;
+}
+
 /* ----------------------------------------------------------------------
    return number of triple quotes in line
 ------------------------------------------------------------------------- */
diff --git a/src/input.h b/src/input.h
index ccda3f49b1..c1947fee03 100644
--- a/src/input.h
+++ b/src/input.h
@@ -35,6 +35,7 @@ class Input : protected Pointers {
   char *one(const char *);       // process a single command
   void substitute(char *&, char *&, int &, int &, int);
                                  // substitute for variables in a string
+  int expand_args(int, char **, int, char **&);  // expand args due to wildcard
 
  private:
   int me;                      // proc ID
diff --git a/src/thermo.cpp b/src/thermo.cpp
index 5ecc161c8f..1b0fc3c68a 100644
--- a/src/thermo.cpp
+++ b/src/thermo.cpp
@@ -102,7 +102,7 @@ Thermo::Thermo(LAMMPS *lmp, int narg, char **arg) : Pointers(lmp)
   flushflag = 0;
 
   // set style and corresponding lineflag
-  // custom style builds its own line of keywords
+  // custom style builds its own line of keywords, including wildcard expansion
   // customize a new thermo style by adding to if statement
   // allocate line string used for 3 tasks
   //   concat of custom style args
@@ -121,14 +121,29 @@ Thermo::Thermo(LAMMPS *lmp, int narg, char **arg) : Pointers(lmp)
 
   } else if (strcmp(style,"custom") == 0) {
     if (narg == 1) error->all(FLERR,"Illegal thermo style custom command");
+
+    // expand args if any have wildcard character "*"
+
+    int expand = 0;
+    char **earg;
+    int nvalues = input->expand_args(narg-1,&arg[1],0,earg);
+    if (earg != &arg[1]) expand = 1;
+
     line = new char[256+narg*64];
     line[0] = '\0';
-    for (int iarg = 1; iarg < narg; iarg++) {
-      strcat(line,arg[iarg]);
+    for (int iarg = 0; iarg < nvalues; iarg++) {
+      strcat(line,earg[iarg]);
       strcat(line," ");
     }
     line[strlen(line)-1] = '\0';
 
+    // if wildcard expansion occurred, free earg memory from exapnd_args()
+
+    if (expand) {
+      for (int i = 0; i < nvalues; i++) delete [] earg[i];
+      memory->sfree(earg);
+    }
+
   } else error->all(FLERR,"Illegal thermo style command");
 
   // ptrs, flags, IDs for compute objects thermo may use or create
@@ -573,7 +588,7 @@ void Thermo::allocate()
   int n = nfield_initial + 1;
 
   keyword = new char*[n];
-  for (int i = 0; i < n; i++) keyword[i] = new char[32];
+  for (int i = 0; i < n; i++) keyword[i] = NULL;
   vfunc = new FnPtr[n];
   vtype = new int[n];
 
@@ -821,7 +836,6 @@ void Thermo::parse_fields(char *str)
 
     // compute value = c_ID, fix value = f_ID, variable value = v_ID
     // count trailing [] and store int arguments
-    // copy = at most 8 chars of ID to pass to addfield
 
     } else if ((strncmp(word,"c_",2) == 0) || (strncmp(word,"f_",2) == 0) ||
                (strncmp(word,"v_",2) == 0)) {
@@ -829,9 +843,6 @@ void Thermo::parse_fields(char *str)
       int n = strlen(word);
       char *id = new char[n];
       strcpy(id,&word[2]);
-      char copy[9];
-      strncpy(copy,id,8);
-      copy[8] = '\0';
 
       // parse zero or one or two trailing brackets from ID
       // argindex1,argindex2 = int inside each bracket pair, 0 if no bracket
@@ -878,7 +889,7 @@ void Thermo::parse_fields(char *str)
           field2index[nfield] = add_compute(id,VECTOR);
         else
           field2index[nfield] = add_compute(id,ARRAY);
-        addfield(copy,&Thermo::compute_compute,FLOAT);
+        addfield(word,&Thermo::compute_compute,FLOAT);
 
       } else if (word[0] == 'f') {
         n = modify->find_fix(id);
@@ -903,7 +914,7 @@ void Thermo::parse_fields(char *str)
         }
 
         field2index[nfield] = add_fix(id);
-        addfield(copy,&Thermo::compute_fix,FLOAT);
+        addfield(word,&Thermo::compute_fix,FLOAT);
 
       } else if (word[0] == 'v') {
         n = input->variable->find(id);
@@ -919,7 +930,7 @@ void Thermo::parse_fields(char *str)
           error->all(FLERR,"Thermo custom variable cannot have two indices");
 
         field2index[nfield] = add_variable(id);
-        addfield(copy,&Thermo::compute_variable,FLOAT);
+        addfield(word,&Thermo::compute_variable,FLOAT);
       }
 
       delete [] id;
@@ -936,6 +947,8 @@ void Thermo::parse_fields(char *str)
 
 void Thermo::addfield(const char *key, FnPtr func, int typeflag)
 {
+  int n = strlen(key) + 1;
+  keyword[nfield] = new char[n];
   strcpy(keyword[nfield],key);
   vfunc[nfield] = func;
   vtype[nfield] = typeflag;
-- 
GitLab