EGSnrc C++ class library  Report PIRS-898 (2021)
Iwan Kawrakow, Ernesto Mainegra-Hing, Frederic Tessier, Reid Townson and Blake Walters
egs_input.cpp
Go to the documentation of this file.
1 /*
2 ###############################################################################
3 #
4 # EGSnrc egs++ input
5 # Copyright (C) 2015 National Research Council Canada
6 #
7 # This file is part of EGSnrc.
8 #
9 # EGSnrc is free software: you can redistribute it and/or modify it under
10 # the terms of the GNU Affero General Public License as published by the
11 # Free Software Foundation, either version 3 of the License, or (at your
12 # option) any later version.
13 #
14 # EGSnrc is distributed in the hope that it will be useful, but WITHOUT ANY
15 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
17 # more details.
18 #
19 # You should have received a copy of the GNU Affero General Public License
20 # along with EGSnrc. If not, see <http://www.gnu.org/licenses/>.
21 #
22 ###############################################################################
23 #
24 # Author: Iwan Kawrakow, 2005
25 #
26 # Contributors: Ernesto Mainegra-Hing
27 # Frederic Tessier
28 # Hubert Ho
29 # Reid Townson
30 #
31 ###############################################################################
32 */
33 
34 
40 #include "egs_input.h"
41 #include "egs_functions.h"
42 
43 #ifdef NO_SSTREAM
44  #include <strstream>
45  #define S_STREAM std::istrstream
46 #else
47  #include <sstream>
48  #define S_STREAM std::istringstream
49 #endif
50 
51 #include <algorithm>
52 #include <cctype>
53 #include <fstream>
54 #include <vector>
55 #include <string>
56 #include <sstream>
57 
58 using namespace std;
59 
60 static char start_key_begin[] = ":START";
61 static char stop_key_begin[] = ":STOP";
62 static char start_key_end[] = ":";
63 static char stop_key_end[] = ":";
64 static char indent_space[] = " ";
65 
66 #ifndef SKIP_DOXYGEN
71 class EGS_LOCAL EGS_InputPrivate {
72 public:
73  string key;
74  string value;
75  vector<EGS_InputPrivate *> children;
76  int nref;
77 
78  EGS_InputPrivate() : nref(0) {};
79  EGS_InputPrivate(const string &Key, const string &Val = "") : key(Key),
80  value(Val), children(), nref(0) { };
81  EGS_InputPrivate(const EGS_InputPrivate &p, bool deep=false) :
82  key(p.key), value(p.value), children(), nref(0) {
83  for (unsigned int j=0; j<p.children.size(); j++) {
84  if (deep) {
85  children.push_back(new EGS_InputPrivate(*p.children[j],deep));
86  }
87  else {
88  children.push_back(p.children[j]);
89  children[j]->nref++;
90  }
91  }
92  }
93 
94  ~EGS_InputPrivate() {
95  for (unsigned int j=0; j<children.size(); j++) {
96  deleteItem(children[j]);
97  }
98  };
99 
100  int replace(const string &replace_what, const string &replace_with);
101 
102  int setContentFromFile(const char *fname);
103  int setContentFromString(string &input);
104  int setContent(istream &input);
105 
106  int addContentFromFile(const char *fname);
107  int addContentFromString(string &input);
108  int addContent(istream &input);
109  string getCleanInputString(istream &input);
110 
111  void processInputLoop(EGS_InputPrivate *p);
112 
113  void addItem(EGS_InputPrivate *p) {
114  p->nref++;
115  children.push_back(p);
116  };
117 
118  EGS_InputPrivate *takeInputItem(const string &key, bool self=true) {
119  if (self && isA(key)) {
120  return this;
121  }
122  for (vector<EGS_InputPrivate *>::iterator it=children.begin();
123  it != children.end(); it++) {
124  if (compareKeys((*it)->key,key)) {
125  EGS_InputPrivate *res = *it;
126  children.erase(it);
127  return res;
128  }
129  //if( (*it)->key == key ) {
130  // EGS_InputPrivate *res = *it;
131  // children.erase(it); return res;
132  //}
133  }
134  return 0;
135  };
136 
137  EGS_InputPrivate *getInputItem(const string &Key) {
138  if (isA(Key)) {
139  return this;
140  }
141  for (unsigned int j=0; j<children.size(); j++)
142  if (compareKeys(children[j]->key,Key)) {
143  return children[j];
144  }
145  return 0;
146  };
147 
148  bool isA(const string &Key) const {
149  return compareKeys(key,Key);
150  };
151 
152  static void removeComment(const string &start, const string &end,
153  string &input, bool newline);
154 
155  static int findStart(int start, int stop,
156  const string &start_key, const string &end_key,
157  const string &input, string &what, int &end);
158  static int findStop(int start, const string &start_string,
159  const string &end_string, const string &input, int &ie);
160 
161  static bool compareKeys(const string &s1, const string &s2);
162 
163  void print(int nind, ostream &) const;
164 
165  void removeEmptyLines(string &input);
166 
167  static void deleteItem(EGS_InputPrivate *p) {
168  if (p) {
169  if (!p->nref) {
170  delete p;
171  }
172  else {
173  p->nref--;
174  }
175  }
176  };
177 
178 };
179 #endif
180 
182  p = 0;
183 }
184 
186  if (input.p) {
187  p = input.p;
188  p->nref++;
189  }
190  else {
191  p = 0;
192  }
193 }
194 
195 EGS_Input::EGS_Input(const string &name, const string &value) {
196  p = new EGS_InputPrivate(name,value);
197 }
198 
200  EGS_InputPrivate::deleteItem(p);
201 }
202 
203 int EGS_Input::setContentFromFile(const char *fname) {
204  EGS_InputPrivate::deleteItem(p);
205  p = new EGS_InputPrivate;
206  return p->setContentFromFile(fname);
207 }
208 
210  EGS_InputPrivate::deleteItem(p);
211  p = new EGS_InputPrivate;
212  return p->setContentFromString(input);
213 }
214 
215 int EGS_Input::addContentFromFile(const char *fname) {
216  if (!p) {
217  p = new EGS_InputPrivate;
218  }
219  return p->addContentFromFile(fname);
220 }
221 
223  if (!p) {
224  p = new EGS_InputPrivate;
225  }
226  return p->addContentFromString(input);
227 }
228 
229 EGS_Input *EGS_Input::takeInputItem(const string &key, bool self) {
230  if (!p) {
231  return 0;
232  }
233  EGS_InputPrivate *item = p->takeInputItem(key,self);
234  if (!item) {
235  return 0;
236  }
237  if (item == p && !self) {
238  return 0;
239  }
240  EGS_Input *result = new EGS_Input;
241  result->p = item;
242  if (item == p) {
243  p = 0;
244  }
245  return result;
246 }
247 
248 EGS_Input *EGS_Input::getInputItem(const string &key) const {
249  if (!p) {
250  return 0;
251  }
252  EGS_InputPrivate *item = p->getInputItem(key);
253  if (!item) {
254  return 0;
255  }
256  item->nref++;
257  EGS_Input *result = new EGS_Input;
258  result->p = item;
259  return result;
260 }
261 
262 void EGS_Input::addInputItem(const EGS_Input &input) {
263  if (!input.p) {
264  return;
265  }
266  if (!p) {
267  p = new EGS_InputPrivate(*input.p);
268  }
269  else {
270  p->addItem(input.p);
271  }
272 }
273 
274 const char *EGS_Input::name() const {
275  if (!p) {
276  return 0;
277  }
278  return p->key.c_str();
279 }
280 
281 bool EGS_Input::isA(const string &key) const {
282  if (!p) {
283  return false;
284  }
285  return p->isA(key);
286 }
287 
288 template <class T> int EGS_LOCAL
289 get_input(const EGS_InputPrivate *p, const string &key, vector<T> &values) {
290  if (!p) {
291  return -1;
292  }
293  const EGS_InputPrivate *p1;
294  if (!p->children.size()) {
295  if (!p->isA(key)) {
296  return -1;
297  }
298  p1 = p;
299  }
300  else {
301  for (unsigned int j=0; j<p->children.size(); j++) {
302  p1 = p->children[j];
303  if (p1->isA(key)) {
304  break;
305  }
306  p1 = 0;
307  }
308  if (!p1) {
309  return -1;
310  }
311  }
312  values.erase(values.begin(),values.end());
313  S_STREAM in(p1->value.c_str());
314  int error = 0;
315  for (EGS_I64 loopCount=0; loopCount<=loopMax; ++loopCount) {
316  if (loopCount == loopMax) {
317  egsFatal("EGS_InputPrivate::findStart: Too many iterations were required! Input may be invalid, or consider increasing loopMax.");
318  return 2;
319  }
320  T tmp;
321  in >> tmp;
322  if (!in.fail()) {
323  values.push_back(tmp);
324  }
325  if (in.eof()) {
326  if (values.size() <= 0) {
327  error = 1;
328  }
329  break;
330  }
331  if (!in.good()) {
332  if (values.size() <= 0) {
333  error = 2;
334  }
335  break;
336  }
337  }
338  return error;
339 }
340 
341 int EGS_Input::getInput(const string &key, vector<string> &values) const {
342  return get_input(p,key,values);
343 }
344 
345 int EGS_Input::getInput(const string &key, vector<EGS_Float> &values) const {
346  return get_input(p,key,values);
347 }
348 
349 int EGS_Input::getInput(const string &key, vector<int> &values) const {
350  return get_input(p,key,values);
351 }
352 
353 template <class T> int EGS_LOCAL
354 get_input(const EGS_InputPrivate *p, const string &key, T &value) {
355  if (!p) {
356  return -1;
357  }
358  const EGS_InputPrivate *p1;
359  if (!p->children.size()) {
360  if (!p->isA(key)) {
361  return -1;
362  }
363  p1 = p;
364  }
365  else {
366  for (unsigned int j=0; j<p->children.size(); j++) {
367  p1 = p->children[j];
368  if (p1->isA(key)) {
369  break;
370  }
371  p1 = 0;
372  }
373  if (!p1) {
374  return -1;
375  }
376  }
377  S_STREAM in(p1->value.c_str());
378  T tmp;
379  in >> tmp;
380  if (!in.fail()) {
381  value = tmp;
382  return 0;
383  }
384  return 1;
385 }
386 
387 int EGS_Input::getInput(const string &key, string &value) const {
388  vector<string> v;
389  //int err = get_input(p,key,v);
390  int err = getInput(key,v);
391  if (err) {
392  return err;
393  }
394  value = v[0];
395  for (unsigned int j=1; j<v.size(); j++) {
396  value += " ";
397  value += v[j];
398  }
399  return 0;
400 }
401 
402 //int EGS_Input::getInput(const string &key, string &value) const {
403 // return get_input(p,key,value);
404 //}
405 
406 int EGS_Input::getInput(const string &key, float &value) const {
407  return get_input(p,key,value);
408 }
409 
410 int EGS_Input::getInput(const string &key, double &value) const {
411  return get_input(p,key,value);
412 }
413 
414 int EGS_Input::getInput(const string &key, int &value) const {
415  return get_input(p,key,value);
416 }
417 
418 // If the stupid MS compiler would support the >> operator on 64 bit
419 // integers, we would just use the template as in the other functions.
420 // But as it doesn't, we need a separate implementation (which is pretty
421 // simple-minded, but it should do for now).
422 int EGS_Input::getInput(const string &key, EGS_I64 &value) const {
423  vector<string> aux;
424  int err = getInput(key,aux);
425  if (err) {
426  return err;
427  }
428  if (aux.size() > 1) {
429  return 1;
430  }
431  if (aux[0].size() < 10) {
432  // if it is less than 10 chars long, it is guaranteed to be
433  // less then 1e9 => will fit into a 32 bit integer.
434  int n;
435  err = getInput(key,n);
436  if (err) {
437  return err;
438  }
439  value = n;
440  return 0;
441  }
442  int nfirst = 0;
443  bool neg = false;
444  if (aux[0][0] == '+') {
445  nfirst = 1;
446  }
447  else if (aux[0][0] == '-') {
448  nfirst = 1;
449  neg = true;
450  };
451  EGS_I64 fac=1, res = 0;
452  for (int j=aux[0].size()-1; j>=nfirst; j--) {
453  if (!isdigit(aux[0][j])) {
454  return 2;
455  }
456  EGS_I64 c = aux[0][j]-48;
457  res += c*fac;
458  fac *= 10;
459  }
460  if (neg) {
461  res *= (-1);
462  }
463  value = res;
464  return 0;
465 }
466 
467 
468 int EGS_Input::getInput(const string &key, const vector<string> &allowed,
469  int def, bool *found) const {
470  string res;
471  int err = getInput(key,res);
472  if (!err) {
473  for (unsigned int j=0; j<allowed.size(); j++) {
474  if (EGS_InputPrivate::compareKeys(res,allowed[j])) {
475  if (found) {
476  *found = true;
477  }
478  return j;
479  }
480  }
481  }
482  if (found) {
483  *found = false;
484  }
485  return def;
486 }
487 
488 #ifndef SKIP_DOXYGEN
489 int EGS_InputPrivate::setContentFromFile(const char *fname) {
490  ifstream in(fname);
491  if (!in) {
492  return -1;
493  }
494  return setContent(in);
495 }
496 
497 int EGS_InputPrivate::addContentFromFile(const char *fname) {
498  // the following removes white space at the beginning of file names,
499  // which may come from the file name being defined in an "include file"
500  // key-value pair
501  const char *s = fname;
502  while (isspace(*s) && (*s)) {
503  ++s;
504  }
505  ifstream in(s);
506  if (!in) {
507  return -1;
508  }
509  return addContent(in);
510 }
511 
512 int EGS_InputPrivate::setContentFromString(string &input) {
513  S_STREAM in(input.c_str());
514  return setContent(in);
515 }
516 
517 int EGS_InputPrivate::addContentFromString(string &input) {
518  S_STREAM in(input.c_str());
519  return addContent(in);
520 }
521 
522 void EGS_InputPrivate::removeComment(const string &start, const string &end,
523  string &input, bool newline) {
524  string::size_type spos=0, epos=0;
525  while ((epos = input.find(end,spos)) < input.size()) {
526  spos = input.find(start,spos);
527  if (spos < epos) {
528  string::size_type len = epos - spos;
529  if (!newline) {
530  len += end.size();
531  }
532  input.erase(spos,len);
533  if (newline) {
534  spos += end.size();
535  }
536  }
537  else {
538  spos = epos+1;
539  }
540  }
541 }
542 
543 int EGS_InputPrivate::setContent(istream &in) {
544  for (unsigned int j=0; j<children.size(); j++) {
545  delete children[j];
546  }
547  children.erase(children.begin(),children.end());
548  return addContent(in);
549 }
550 
551 bool EGS_InputPrivate::compareKeys(const string &s1, const string &s2) {
552  string t1, t2;
553  unsigned int j;
554  for (j=0; j<s1.size(); j++)
555  if (!isspace(s1[j])) {
556  t1 += ::toupper(s1[j]);
557  }
558  for (j=0; j<s2.size(); j++)
559  if (!isspace(s2[j])) {
560  t2 += ::toupper(s2[j]);
561  }
562  return (t1 == t2);
563 
564 }
565 
566 #ifdef INPUT_DEBUG
567  #include <iostream>
568 #endif
569 
570 int EGS_InputPrivate::replace(const string &replace_what,
571  const string &replace_with) {
572  string::size_type pos = 0;
573  int nr = 0;
574  while ((pos = key.find(replace_what,pos)) < key.size()) {
575  key.replace(pos,replace_what.size(),replace_with);
576  ++nr;
577  }
578  pos = 0;
579  while ((pos = value.find(replace_what,pos)) < value.size()) {
580  value.replace(pos,replace_what.size(),replace_with);
581  ++nr;
582  }
583  for (int j=0; j<children.size(); j++) {
584  nr += children[j]->replace(replace_what,replace_with);
585  }
586  return nr;
587 }
588 #endif
589 
671 class EGS_LOCAL EGS_InputLoopVariable {
672 public:
673  bool is_list;
674  string vname,
675  vr;
676  char buf[128];
677  EGS_InputLoopVariable(const string &var) : is_list(false), vname(var) {
678  vr = "$(";
679  vr += vname;
680  vr += ")";
681  };
682  virtual ~EGS_InputLoopVariable() {};
683  const char *getVarNameReplacement() const {
684  return vr.c_str();
685  };
686  const char *getVarReplacement() const {
687  return buf;
688  };
689  virtual void setVarReplacement(int) = 0;
690  static EGS_InputLoopVariable *getInputLoopVariable(const char *input);
691 };
692 
698 public:
699  int vmin, vdelta;
700  EGS_IntegerInputLoopVariable(int Vmin, int Vdelta, const string &var) :
701  EGS_InputLoopVariable(var), vmin(Vmin), vdelta(Vdelta) {};
702  void setVarReplacement(int i) {
703  int v = vmin + vdelta*i;
704  sprintf(buf,"%d",v);
705  };
706 };
707 
713 public:
714  double vmin, vdelta;
715  string format;
716  EGS_FloatInputLoopVariable(double Vmin, double Vdel, const string &var, const string &Format) :
717  EGS_InputLoopVariable(var), vmin(Vmin), vdelta(Vdel), format(Format) {};
718  void setVarReplacement(int i) {
719  double v = vmin + vdelta*i;
720  sprintf(buf,format.c_str(),v);
721  };
722 };
724 public:
725  vector<string> list;
726  EGS_ListInputLoopVariable(vector<string> List, const string &var) :
727  EGS_InputLoopVariable(var), list(List) {
728  is_list = true;
729  };
730  void setVarReplacement(int i) {
731  string str = list[i];
732  sprintf(buf,"%s",str.c_str());
733  };
734  int list_size() {
735  return list.size();
736  }
737 };
738 EGS_InputLoopVariable *EGS_InputLoopVariable::getInputLoopVariable(
739  const char *input) {
740  if (!input) {
741  return 0;
742  }
743  S_STREAM in(input);
744  string name;
745  int type;
746  in >> type >> name;
747  if (in.fail() || !in.good()) {
748  egsWarning("Failed reading type and name from %s\n",input);
749  return 0;
750  }
751  if (type < 0 || type > 2) {
752  egsFatal("Invalid loop type in input: %s\n"
753  "Only integer [0], float [1] and list [2] are valid types!\n",
754  input);
755  }
756  EGS_InputLoopVariable *result=0;
757  if (type == 0) {
758  int vmin, vdelta;
759  in >> vmin >> vdelta;
760  result = new EGS_IntegerInputLoopVariable(vmin,vdelta,name);
761  }
762  else if (type == 1) {
763  double vmin, vdelta;
764  in >> vmin >> vdelta;
765  string format;
766  in >> format;
767  if (format.empty()) {
768  format = "%lg";
769  if (in.fail()) {
770  in.clear();
771  }
772  }
773  result = new EGS_FloatInputLoopVariable(vmin,vdelta,name,format);
774  }
775  else if (type == 2) {
776  vector<string> vstr;
777  string str, s_tmp;
778  while (!in.eof()) {
779  in >> s_tmp;
780  if (!in.fail()) {
781  vstr.push_back(s_tmp);
782  }
783  }
784  result = new EGS_ListInputLoopVariable(vstr,name);
785  if (in.fail() && in.eof()) { // possibly white spaces at end of line
786  if (!vstr.size()) { // end-of-line reached and no list-item found
787  delete result;
788  result = 0;
789  egsFatal("No list-items found reading loop-input: %s\n",input);
790  }
791  //Found white spaces at the end of loop-input list which is ok.
792  return result;
793  }
794  if (in.bad()) {
795  delete result;
796  result = 0;
797  egsFatal("Fatal error reading loop input list from %s\n",input);
798  }
799  }
800  if (in.fail()) {
801  egsWarning("Failed reading vmin vdelta from %s\n",input);
802  delete result;
803  result = 0;
804  }
805  return result;
806 }
807 
808 #ifndef SKIP_DOXYGEN
809 void EGS_InputPrivate::processInputLoop(EGS_InputPrivate *p) {
810  egsWarning("Processing input loop\n");
811  EGS_InputPrivate *ic = p->takeInputItem("loop count");
812  if (!ic) {
813  egsWarning("processInputLoop: no 'loop count' input\n");
814  return;
815  }
816  int nloop = -1;
817  int err = get_input(ic,"loop count",nloop);
818  delete ic;
819  if (err || nloop < 1) {
820  egsWarning("processInputLoop: got %d for loop count, expecting 1 or "
821  "more\n",nloop);
822  return;
823  }
824  EGS_InputPrivate *iv;
825  vector<EGS_InputLoopVariable *> ivars;
826  while ((iv = p->takeInputItem("loop variable")) != 0) {
828  EGS_InputLoopVariable::getInputLoopVariable(iv->value.c_str());
829  if (v->is_list) {
830  if (((EGS_ListInputLoopVariable *)v)->list_size()<nloop) {
831  egsFatal("procesInputLoop: loop size (%d) larger than list size (%d)!\n"
832  "This will cause a segmentation fault error, aborting ....\n",
833  nloop, ((EGS_ListInputLoopVariable *)v)->list_size());
834  }
835  }
836  if (!v) egsWarning("processInputLoop: failed to create loop variable"
837  " based on the input %s\n",iv->value.c_str());
838  else {
839  ivars.push_back(v);
840  }
841  delete iv;
842  }
843  if (!ivars.size()) {
844  egsWarning("processInputLoop: no loop variables\n");
845  return;
846  }
847  int nvar = ivars.size();
848  int j;
849  /*
850  for(j=0; j<p->children.size(); j++) {
851  for(int iloop=0; iloop<nloop; iloop++) {
852  EGS_InputPrivate *pnew = new EGS_InputPrivate(*p->children[j],true);
853  for(int ivar=0; ivar<nvar; ivar++) {
854  ivars[ivar]->setVarReplacement(iloop);
855  pnew->replace(ivars[ivar]->getVarNameReplacement(),
856  ivars[ivar]->getVarReplacement());
857  }
858  children.push_back(pnew);
859  }
860  }
861  */
862  for (int iloop=0; iloop<nloop; iloop++) {
863  for (j=0; j<p->children.size(); j++) {
864  EGS_InputPrivate *pnew = new EGS_InputPrivate(*p->children[j],true);
865  for (int ivar=0; ivar<nvar; ivar++) {
866  ivars[ivar]->setVarReplacement(iloop);
867  pnew->replace(ivars[ivar]->getVarNameReplacement(),
868  ivars[ivar]->getVarReplacement());
869  }
870  children.push_back(pnew);
871  }
872  }
873 
874  for (j=0; j<ivars.size(); j++) {
875  delete ivars[j];
876  }
877 }
878 
879 int EGS_InputPrivate::addContent(istream &in) {
880  string input = getCleanInputString(in);
881 
882  // Add content from include files first
883  // Just replace the include line in the input string
884  // with the content from the external file
885  string::size_type p1;
886  int p = 0;
887  while ((p1=input.find('\n',p)) < input.size()) {
888  string::size_type p2 = input.find('=',p);
889  if (p2 < p1) {
890  string what;
891  what.assign(input,p,p2-p);
892  if (compareKeys(what,"includefile")) {
893  string value;
894  value.assign(input,p2+1,p1-p2-1);
895 
896 
897  const char *s = value.c_str();
898  while (isspace(*s) && (*s)) {
899  ++s;
900  }
901  ifstream in2(s);
902  if (!in2) {
903  egsFatal("EGS_Input: failed to add content from "
904  "include file %s\n",value.c_str());
905  }
906 
907  string input2 = getCleanInputString(in2);
908 
909  input.erase(p, p1 - p);
910  input.insert(p, input2);
911  p += input2.size();
912  }
913  }
914  p = p1+1;
915  }
916 
917  // Now build the hierarchy structure of input blocks
918  vector<string> start_keys, stop_keys;
919  int ep = input.size();
920  string what;
921  int ie;
922  p = 0;
923  while ((p=findStart(p,ep,start_key_begin,start_key_end,input,what,ie))>=0) {
924  string the_start = start_key_begin;
925  string the_end = stop_key_begin;
926  for (int j=0; j<what.size(); j++) {
927  char c = ::toupper(what[j]);
928  if (!isspace(c)) {
929  the_start += c;
930  the_end += c;
931  }
932  }
933  the_start += start_key_end;
934  the_end += stop_key_end;
935  int p1;
936  int ep = findStop(ie+1,the_start,the_end,input,p1);
937  if (ep > ie+1) {
938  EGS_InputPrivate *ip = new EGS_InputPrivate(what);
939  string content;
940  content.assign(input,ie+1,p1-ie-1);
941  input.erase(p,ep-p);
942  ip->setContentFromString(content);
943  if (ip->isA("input loop")) {
944  processInputLoop(ip);
945  delete ip;
946  }
947  else {
948  children.push_back(ip);
949  }
950  }
951  else {
952  egsWarning("No matching stop delimiter for %s\n",what.c_str());
953  return -1;
954  }
955  }
956 
957  // Replace commas and backslashes in the input value with spaces
958  p = 0;
959  while ((p1=input.find('\n',p)) < input.size()) {
960  int j=p1;
961  while (--j > p && isspace(input[j]));
962  if (j > p) {
963  if (input[j] == ',' || input[j] == '\\') {
964  input[p1] = ' ';
965  input[j] = ' ';
966  }
967  }
968  p = p1+1;
969  }
970 
971  // Parse the single input values
972  p = 0;
973  while ((p1=input.find('\n',p)) < input.size()) {
974  string::size_type p2 = input.find('=',p);
975  if (p2 < p1) {
976  string what;
977  what.assign(input,p,p2-p);
978  string value;
979  value.assign(input,p2+1,p1-p2-1);
980  for (int j=0; j<value.size(); j++) {
981  if (value[j] == ',') {
982  value[j] = ' ';
983  }
984  }
985 
986  EGS_InputPrivate *ip = new EGS_InputPrivate(what,value);
987  children.push_back(ip);
988  }
989  p = p1+1;
990  }
991 
992 #ifdef INPUT_DEBUG
993  print(0,std::cout);
994 #endif
995 
996  return 0;
997 
998 }
999 
1000 string EGS_InputPrivate::getCleanInputString(istream &in) {
1001  string input;
1002  bool last_was_space = false;
1003  for (EGS_I64 loopCount=0; loopCount<=loopMax; ++loopCount) {
1004  if (loopCount == loopMax) {
1005  egsFatal("EGS_InputPrivate::addContent: Too many iterations were required! Input may be invalid, or consider increasing loopMax.");
1006  }
1007  char c;
1008  in.get(c);
1009  if (in.eof() || !in.good()) {
1010  break;
1011  }
1012  bool take_it = true;
1013  if (isspace(c)) {
1014  if (last_was_space && c != '\n') {
1015  take_it = false;
1016  }
1017  last_was_space = true;
1018  }
1019  else {
1020  last_was_space = false;
1021  }
1022  if (take_it) {
1023  input += c;
1024  }
1025  }
1026  removeComment("#","\n",input,true);
1027  removeComment("!","\n",input,true);
1028  removeComment("//","\n",input,true);
1029  removeComment("/*","*/",input,false);
1030  removeEmptyLines(input);
1031 
1032  return input;
1033 }
1034 
1035 int EGS_InputPrivate::findStop(int start, const string &the_start,
1036  const string &the_end, const string &input,
1037  int &ie) {
1038  int ns=0, ne=0;
1039  int have_start=1, have_end=0;
1040  int end_started = start;
1041  for (int j=start; j<input.size(); j++) {
1042  if (!isspace(input[j])) {
1043  char c = ::toupper(input[j]);
1044  if (the_start[ns] == c) {
1045  ns++;
1046  }
1047  else {
1048  ns=0;
1049  if (the_start[ns] == c) {
1050  ns++;
1051  }
1052  }
1053  if (ns == the_start.size()) {
1054  ns=0;
1055  have_start++;
1056  }
1057  if (!ne) {
1058  end_started = j;
1059  }
1060  if (the_end[ne] == c) {
1061  ne++;
1062  }
1063  else {
1064  ne=0;
1065  if (the_end[ne] == c) {
1066  end_started = j;
1067  ne++;
1068  }
1069  }
1070  if (ne == the_end.size()) {
1071  ne = 0;
1072  have_end++;
1073  if (have_end == have_start) {
1074  ie = end_started;
1075  return j;
1076  }
1077  end_started = j+1;
1078  }
1079  }
1080  }
1081  return -1;
1082 }
1083 
1084 int EGS_InputPrivate::findStart(int start, int stop, const string &start_key,
1085  const string &end_key, const string &input,
1086  string &what, int &end) {
1087  string::size_type pos = start;
1088  unsigned int ns=0;
1089  for (EGS_I64 loopCount=0; loopCount<=loopMax; ++loopCount) {
1090  if (loopCount == loopMax) {
1091  egsFatal("EGS_InputPrivate::findStart: Too many iterations were required! Input may be invalid, or consider increasing loopMax.");
1092  return -2;
1093  }
1094  if (pos >= stop) {
1095  return -1;
1096  }
1097  char c = ::toupper(input[pos++]);
1098  if (start_key[ns] == c) {
1099  ns++;
1100  }
1101  else {
1102  ns=0;
1103  if (start_key[ns] == c) {
1104  ns++;
1105  }
1106  }
1107  if (ns == start_key.size()) {
1108  break;
1109  }
1110  }
1111  string::size_type epos = input.find(end_key,pos);
1112  if (epos < stop) {
1113  what.assign(input,pos,epos-pos);
1114  end = epos + end_key.size();
1115  return pos-start_key.size();
1116  }
1117  return -2;
1118 }
1119 
1120 void EGS_InputPrivate::print(int indent, ostream &out) const {
1121  if (children.size() > 0) {
1122  if (key.size() > 0) {
1123  for (int j=0; j<indent; j++) {
1124  out << indent_space;
1125  }
1126  out << start_key_begin << " " << key << start_key_end << endl;
1127  }
1128  indent += 1;
1129  for (int i=0; i<children.size(); i++) {
1130  children[i]->print(indent,out);
1131  }
1132  indent -= 1;
1133  if (key.size() > 0) {
1134  for (int j=0; j<indent; j++) {
1135  out << indent_space;
1136  }
1137  out << stop_key_begin << " " << key << stop_key_end << endl;
1138  }
1139  }
1140  else {
1141  for (int j=0; j<indent; j++) {
1142  out << indent_space;
1143  }
1144  out << key << " = " << value << endl;
1145  }
1146 }
1147 
1148 void EGS_InputPrivate::removeEmptyLines(string &input) {
1149  int pos=0, pos1;
1150  while ((pos1=input.find('\n',pos)) < input.size()) {
1151  if (pos1 == pos) {
1152  input.erase(pos,1);
1153  }
1154  else {
1155  bool is_only_space = true;
1156  for (int j=pos; j<pos1; j++) {
1157  if (!isspace(input[j])) {
1158  is_only_space = false;
1159  break;
1160  }
1161  }
1162  if (is_only_space) {
1163  input.erase(pos,pos1+1-pos);
1164  }
1165  else {
1166  pos = pos1+1;
1167  }
1168  }
1169  }
1170 }
1171 #endif
1172 
1173 bool EGS_Input::compare(const string &s1, const string &s2) {
1174  return EGS_InputPrivate::compareKeys(s1,s2);
1175 }
1176 
1177 void EGS_Input::print(int nind,ostream &out) {
1178  if (p) {
1179  p->print(nind,out);
1180  }
1181 }
1182 
1183 // egsTokenize (free function)
1184 vector<string> egsTokenize(const string &str) {
1185 
1186  // handle empty string
1187  if (str.empty()) {
1188  return {};
1189  }
1190 
1191  // replace commas with spaces
1192  string copy = str;
1193  replace(copy.begin(), copy.end(), ',', ' ');
1194  istringstream iss(copy);
1195 
1196  // tokenize string
1197  vector<string> tokens;
1198  string token;
1199  while (iss >> token) {
1200  tokens.push_back(token);
1201  }
1202 
1203  return tokens;
1204 }
1205 
1206 // egsParseIntegerRanges (free function)
1207 vector<int> egsParseIntegerRanges(vector<string>::const_iterator begin,
1208  vector<string>::const_iterator end) {
1209 
1210  vector<int> integers;
1211 
1212  // loop over tokens
1213  for (auto it = begin; it != end; ++it) {
1214 
1215  string token = *it;
1216 
1217  // try single number first (most common case)
1218  {
1219  istringstream iss(token);
1220  int number;
1221  if ((iss >> number) && iss.eof()) {
1222  integers.push_back(number);
1223  continue;
1224  }
1225  }
1226 
1227  // try range format (e.g., "14-27")
1228  {
1229  istringstream iss(token);
1230  int start, stop;
1231  char dash;
1232  if ((iss >> start >> dash >> stop) && dash == '-' && iss.eof()) {
1233  for (int i = min(start, stop); i <= max(start, stop); i++) {
1234  integers.push_back(i);
1235  }
1236  }
1237  }
1238  }
1239 
1240  // sort and unique
1241  sort(integers.begin(), integers.end());
1242  integers.erase(unique(integers.begin(), integers.end()), integers.end());
1243 
1244  return integers;
1245 }
1246 
1247 
1248 #ifdef TEST
1249 
1250 #include <iostream>
1251 #include <cstdio>
1252 #include <cstdarg>
1253 #include <cstdlib>
1254 #include <cstring>
1255 #include <cctype>
1256 
1257 void egs_warning(const char *msg,...) {
1258  va_list ap;
1259  va_start(ap, msg);
1260  vfprintf(stderr, msg, ap);
1261  va_end(ap);
1262 }
1263 
1264 EGS_InfoFunction egsWarning = egs_warning;
1265 
1266 int main() {
1267 
1268  EGS_InputPrivate p("test");
1269  p.setContent(cin);
1270  p.print(0,cout);
1271  return 0;
1272 }
1273 
1274 #endif
A base class for real valued input loops. Basic functionality for input loops is provided by this cla...
Definition: egs_input.cpp:712
A base class for input loops. Basic functionality for input loops is provided by this class....
Definition: egs_input.cpp:671
string vr
Loop variable replacement string.
Definition: egs_input.cpp:675
bool is_list
True if input loop type is 2, else it is set to false.
Definition: egs_input.cpp:673
string vname
Loop variable name.
Definition: egs_input.cpp:674
A class for storing information in a tree-like structure of key-value pairs. This class is used throu...
Definition: egs_input.h:182
EGS_Input * takeInputItem(const string &key, bool self=true)
Get the property named key.
Definition: egs_input.cpp:229
void print(int nind, ostream &)
Used for debugging purposes.
Definition: egs_input.cpp:1177
void addInputItem(const EGS_Input &i)
Add the input i to this property.
Definition: egs_input.cpp:262
EGS_Input()
Create an empty property (no key and no value).
Definition: egs_input.cpp:181
int addContentFromFile(const char *fname)
Add the content of the file fname to this EGS_Input object.
Definition: egs_input.cpp:215
int addContentFromString(string &input)
Definition: egs_input.cpp:222
const char * name() const
Get the name of this property.
Definition: egs_input.cpp:274
~EGS_Input()
Destructor.
Definition: egs_input.cpp:199
int setContentFromString(string &input)
Definition: egs_input.cpp:209
static bool compare(const string &s1, const string &s2)
Definition: egs_input.cpp:1173
bool isA(const string &key) const
Definition: egs_input.cpp:281
EGS_Input * getInputItem(const string &key) const
Same as the previous function but now ownership remains with the EGS_Input object.
Definition: egs_input.cpp:248
int setContentFromFile(const char *fname)
Set the property from the input file fname (which is considered to be an absolute file name)
Definition: egs_input.cpp:203
int getInput(const string &key, vector< string > &values) const
Assign values to an array of strings from an input identified by key.
Definition: egs_input.cpp:341
A base class for integer valued input loops. Basic functionality for input loops is provided by this ...
Definition: egs_input.cpp:697
Global egspp functions header file.
void(* EGS_InfoFunction)(const char *,...)
Defines a function printf-like prototype for functions to be used to report info, warnings,...
vector< string > egsTokenize(const string &str)
Tokenize a string into individual tokens.
Definition: egs_input.cpp:1184
vector< int > egsParseIntegerRanges(vector< string >::const_iterator begin, vector< string >::const_iterator end)
Parse integers and integer ranges from string tokens.
Definition: egs_input.cpp:1207
EGS_Input class header file.
int main(int argc, char **argv)
A main program for egspp applications.
Definition: egspp.cpp:58
EGS_InfoFunction EGS_EXPORT egsFatal
Always use this function for reporting fatal errors.
const EGS_I64 loopMax
The maximum number of iterations for near-infinite loops.
Definition: egs_functions.h:96
EGS_InfoFunction EGS_EXPORT egsWarning
Always use this function for reporting warnings.