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