1 /* $Header: /var/cvs/mbdyn/mbdyn/mbdyn-1.0/libraries/libmbutil/parsinc.cc,v 1.59 2017/01/12 14:44:05 masarati Exp $ */
2 /*
3 * MBDyn (C) is a multibody analysis code.
4 * http://www.mbdyn.org
5 *
6 * Copyright (C) 1996-2017
7 *
8 * Pierangelo Masarati <masarati@aero.polimi.it>
9 * Paolo Mantegazza <mantegazza@aero.polimi.it>
10 *
11 * Dipartimento di Ingegneria Aerospaziale - Politecnico di Milano
12 * via La Masa, 34 - 20156 Milano, Italy
13 * http://www.aero.polimi.it
14 *
15 * Changing this copyright notice is forbidden.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation (version 2 of the License).
20 *
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 */
31
32 /* parser con include */
33
34 #include "mbconfig.h" /* This goes first in every *.c,*.cc file */
35
36 #include <cstring>
37 #include <unistd.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #ifdef HAVE_PWD_H
41 #include <pwd.h>
42 #endif /* HAVE_PWD_H */
43 #include <errno.h>
44
45 #include "parsinc.h"
46 #include "filename.h"
47
48 #ifndef PATH_MAX
49 #define PATH_MAX 4096
50 #endif // !PATH_MAX
51
52
53
54 struct IncludeDR : public DescRead {
55 bool Read(HighParser& HP);
56 };
57
58 bool
Read(HighParser & HP)59 IncludeDR::Read(HighParser& HP)
60 {
61 IncludeParser *pIP = dynamic_cast<IncludeParser *>(&HP);
62 ASSERT(pIP != 0);
63 return pIP->Include_int();
64 }
65
66 struct ChDirDR : public DescRead {
67 bool Read(HighParser& HP);
68 };
69
70 bool
Read(HighParser & HP)71 ChDirDR::Read(HighParser& HP)
72 {
73 if (!HP.IsArg()) {
74 silent_cerr("Parser error "
75 "in ChDir::Read(), "
76 "colon expected at line " << HP.GetLineData()
77 << std::endl);
78 throw HighParser::ErrColonExpected(MBDYN_EXCEPT_ARGS);
79 }
80
81 IncludeParser *pIP = dynamic_cast<IncludeParser *>(&HP);
82 ASSERT(pIP != 0);
83
84 const char* sfname = pIP->GetFileName();
85
86 if (chdir(sfname)) {
87 silent_cerr("Error in chdir, path=\"" << sfname << "\" at line "
88 << HP.GetLineData() << std::endl);
89 throw ErrFileSystem(MBDYN_EXCEPT_ARGS);
90 }
91
92 return true;
93 }
94
95 static unsigned desc_done;
96
97 static void
InitDescData(void)98 InitDescData(void)
99 {
100 // NOTE: data will be destroyed when the underlying HighParser is destroyed (is this what we want?)
101 if (::desc_done++ > 0) {
102 return;
103 }
104
105 SetDescData("include", new IncludeDR);
106 SetDescData("chdir", new ChDirDR);
107
108 /* NOTE: add here initialization of new built-in descriptions;
109 * alternative ways to register new custom descriptions are:
110 * - call SetDescData() from anywhere in the code
111 * - write a module that calls SetDescData() from inside a function
112 * called module_init(), and run-time load it using "module load"
113 * in the input file.
114 */
115 }
116
117
118 /* IncludeParser - begin */
119
IncludeParser(MathParser & MP,InputStream & streamIn,const char * sInitialFile)120 IncludeParser::IncludeParser(MathParser& MP,
121 InputStream& streamIn,
122 const char *sInitialFile)
123 : HighParser(MP, streamIn),
124 sCurrPath(NULL),
125 sInitialPath(NULL),
126 sCurrFile(NULL)
127 {
128 ASSERT(sInitialFile != NULL);
129 #ifdef USE_INCLUDE_PARSER
130 char s[PATH_MAX];
131 if (getcwd(s, sizeof(s)) == NULL) {
132 silent_cerr("Error in getcwd()" << std::endl);
133 throw ErrFileSystem(MBDYN_EXCEPT_ARGS);
134 }
135 SAFESTRDUP(sCurrPath, s);
136 sInitialPath = sCurrPath;
137 DEBUGCOUT("Current directory is \"" << sCurrPath << "\"" << std::endl);
138
139 SAFESTRDUP(sCurrFile, sInitialFile);
140 #else /* !USE_INCLUDE_PARSER */
141 NO_OP;
142 #endif /* !USE_INCLUDE_PARSER */
143
144 // NOTE: data will be destroyed when the underlying HighParser is destroyed (is this what we want?)
145 InitDescData();
146 }
147
148
~IncludeParser(void)149 IncludeParser::~IncludeParser(void)
150 {
151 IncludeParser::Close();
152 }
153
154
Close(void)155 void IncludeParser::Close(void)
156 {
157 MyInput* pmi = NULL;
158 if (!myinput.empty()) {
159 pmi = myinput.top();
160 ASSERT(pmi != NULL);
161 /* Nota: deve esserci solo l'ultimo file */
162 ASSERT(pf != NULL);
163 ASSERT(pIn != NULL);
164
165 #ifdef USE_INCLUDE_PARSER
166 ASSERT(sCurrPath != NULL);
167 ASSERT(sCurrFile != NULL);
168 #endif /* USE_INCLUDE_PARSER */
169
170 if (pf != NULL) {
171 SAFEDELETE(pf);
172 }
173 if (pIn != NULL) {
174 SAFEDELETE(pIn);
175 }
176
177 #ifdef USE_INCLUDE_PARSER
178 DEBUGCOUT("Leaving directory <" << sCurrPath
179 << ">, file <" << sCurrFile << '>' << std::endl);
180 if (sCurrPath != NULL) {
181 SAFEDELETEARR(sCurrPath);
182 sCurrPath = NULL;
183 }
184 if (sCurrFile != NULL) {
185 SAFEDELETEARR(sCurrFile);
186 sCurrFile = NULL;
187 }
188 #endif /* USE_INCLUDE_PARSER */
189
190 pf = pmi->pfile;
191 pIn = pmi->pis;
192
193 #ifdef USE_INCLUDE_PARSER
194 sCurrPath = pmi->sPath;
195 sCurrFile = pmi->sFile;
196 DEBUGCOUT("Entering directory \"" << sCurrPath
197 << "\", file \"" << sCurrFile << "\"" << std::endl);
198 if (chdir(sCurrPath)) {
199 silent_cerr("Error in chdir, path=\""
200 << sCurrPath << "\"" << std::endl);
201 throw ErrFileSystem(MBDYN_EXCEPT_ARGS);
202 }
203 #endif /* USE_INCLUDE_PARSER */
204
205 /* pmi must be non NULL */
206 SAFEDELETE(pmi);
207
208 myinput.pop();
209 }
210
211 /* sCurrPath can be NULL if Close() has been already called */
212 #ifdef USE_INCLUDE_PARSER
213 if (sCurrPath != NULL) {
214 SAFEDELETEARR(sCurrPath);
215 sCurrPath = NULL;
216 }
217 if (sCurrFile != NULL) {
218 SAFEDELETEARR(sCurrFile);
219 sCurrFile = NULL;
220 }
221 #endif /* USE_INCLUDE_PARSER */
222 }
223
224 flag
fCheckStack(void)225 IncludeParser::fCheckStack(void)
226 {
227 MyInput* pmi = NULL;
228 if (!myinput.empty()) {
229 pmi = myinput.top();
230
231 ASSERT(pmi != NULL);
232 /*
233 * Nota: se la stack e' piena, allora sia pf che pIn
234 * devono essere diversi da NULL; viceversa, se la stack
235 * e' vuota, pf deve essere NULL.
236 */
237 ASSERT(pf != NULL);
238 ASSERT(pIn != NULL);
239 #ifdef USE_INCLUDE_PARSER
240 ASSERT(sCurrPath != NULL);
241 ASSERT(sCurrFile != NULL);
242 #endif /* USE_INCLUDE_PARSER */
243
244 SAFEDELETE(pf);
245 SAFEDELETE(pIn);
246 #ifdef USE_INCLUDE_PARSER
247 DEBUGCOUT("Leaving directory <" << sCurrPath
248 << ">, file <" << sCurrFile << '>' << std::endl);
249 SAFEDELETEARR(sCurrPath);
250 sCurrPath = NULL;
251 SAFEDELETEARR(sCurrFile);
252 sCurrFile = NULL;
253 #endif /* USE_INCLUDE_PARSER */
254
255 pf = pmi->pfile;
256 pIn = pmi->pis;
257 #ifdef USE_INCLUDE_PARSER
258 sCurrPath = pmi->sPath;
259 sCurrFile = pmi->sFile;
260 DEBUGCOUT("Entering directory \"" << sCurrPath
261 << "\", file \"" << sCurrFile << "\"" << std::endl);
262 if (chdir(sCurrPath)) {
263 silent_cerr("Error in chdir, path=\""
264 << sCurrPath << "\"" << std::endl);
265 throw ErrFileSystem(MBDYN_EXCEPT_ARGS);
266 }
267 #endif /* USE_INCLUDE_PARSER */
268
269 SAFEDELETE(pmi);
270
271 myinput.pop();
272
273 return flag(1);
274
275 } else {
276 return flag(0);
277 }
278 }
279
280 bool
Include_int()281 IncludeParser::Include_int()
282 {
283 // if (FirstToken() == UNKNOWN) {
284 if (!IsArg()) {
285 silent_cerr("Parser error in IncludeParser::Include_int(),"
286 " colon expected at line " << GetLineData()
287 << std::endl);
288 throw HighParser::ErrColonExpected(MBDYN_EXCEPT_ARGS);
289 }
290
291 const char* sfname = GetFileName();
292
293 if (sfname != 0) {
294 struct stat s;
295
296 if (stat(sfname, &s)) {
297 int save_errno = errno;
298
299 silent_cerr("Cannot stat file <" << sfname << "> "
300 "at line " << GetLineData() << ": "
301 << save_errno
302 << " (" << strerror(save_errno) << ")"
303 << std::endl);
304 throw ErrFile(MBDYN_EXCEPT_ARGS);
305 }
306
307 if (!S_ISREG(s.st_mode)) {
308 silent_cerr("File <" << sfname << "> "
309 "at line " << GetLineData() << ": "
310 "not a regular file?" << std::endl);
311 throw ErrFile(MBDYN_EXCEPT_ARGS);
312 }
313
314 if (!(s.st_mode & S_IRUSR)) {
315 silent_cerr("File <" << sfname << "> "
316 "at line " << GetLineData() << ": "
317 "no read permissions?" << std::endl);
318 throw ErrFile(MBDYN_EXCEPT_ARGS);
319 }
320 }
321
322 std::ifstream *pf_old = pf;
323 InputStream *pIn_old = pIn;
324 char *sOldPath = sCurrPath;
325 char *sOldFile = sCurrFile;
326
327 pf = NULL;
328 pIn = NULL;
329
330 #ifdef _WIN32
331 // open the file in non translated mode in order not to break seek operations
332 SAFENEWWITHCONSTRUCTOR(pf, std::ifstream, std::ifstream(sfname, std::ios::binary));
333 #else
334 SAFENEWWITHCONSTRUCTOR(pf, std::ifstream, std::ifstream(sfname));
335 #endif
336 if (!(*pf)) {
337 #ifdef DEBUG
338 char *buf = getcwd(NULL, 0);
339 if (buf != NULL) {
340 DEBUGCERR("Current directory \"" << buf << "\""
341 << std::endl);
342 free(buf);
343 }
344 #endif /* DEBUG */
345
346 /* restore */
347 pf = pf_old;
348 pIn = pIn_old;
349 sCurrPath = sOldPath;
350 sCurrFile = sOldFile;
351
352 silent_cerr("Invalid file <" << sfname << "> "
353 "at line " << GetLineData() << std::endl);
354 throw ErrFile(MBDYN_EXCEPT_ARGS);
355 }
356
357 SAFENEWWITHCONSTRUCTOR(pIn, InputStream, InputStream(*pf));
358
359 /* Cambio di directory */
360 #ifdef USE_INCLUDE_PARSER
361 sCurrPath = NULL;
362 sCurrFile = NULL;
363 char* stmp = NULL;
364 SAFESTRDUP(stmp, sfname);
365 char* s = (char*)stmp + strlen(sfname);
366 while (--s >= stmp) {
367 if (s[0] == DIR_SEP) {
368 char c = s[1];
369 s[1] = '\0';
370 if (chdir(stmp)) {
371 silent_cerr("Error in chdir, path="
372 << stmp << std::endl);
373 throw ErrFileSystem(MBDYN_EXCEPT_ARGS);
374 }
375 char p[PATH_MAX];
376 if (getcwd(p, sizeof(p)) == NULL) {
377 silent_cerr("Error in getcwd()" << std::endl);
378 SAFEDELETEARR(stmp);
379 throw ErrFileSystem(MBDYN_EXCEPT_ARGS);
380 }
381 SAFESTRDUP(sCurrPath, p);
382 DEBUGCOUT("Current directory is \"" << sCurrPath
383 << "\"" << std::endl);
384
385 s[1] = c;
386 break;
387 }
388 }
389 s++;
390 SAFESTRDUP(sCurrFile, s);
391 DEBUGCOUT("Opening file <" << sCurrFile << '>' << std::endl);
392
393 SAFEDELETEARR(stmp);
394
395 if (sCurrPath == NULL) {
396 char s[PATH_MAX];
397 if (getcwd(s, sizeof(s)) == NULL) {
398 silent_cerr("Error in getcwd()" << std::endl);
399 throw ErrFileSystem(MBDYN_EXCEPT_ARGS);
400 }
401 SAFESTRDUP(sCurrPath, s);
402 DEBUGCOUT("Current directory is \"" << sCurrPath
403 << "\"" << std::endl);
404 }
405 #endif /* USE_INCLUDE_PARSER */
406
407 MyInput* pmi = NULL;
408 #ifdef USE_INCLUDE_PARSER
409 SAFENEWWITHCONSTRUCTOR(pmi,
410 MyInput,
411 MyInput(pf_old, pIn_old, sOldPath, sOldFile));
412 #else /* !USE_INCLUDE_PARSER */
413 SAFENEWWITHCONSTRUCTOR(pmi, MyInput, MyInput(pf_old, pIn_old));
414 #endif /* !USE_INCLUDE_PARSER */
415
416 myinput.push(pmi);
417
418 /* FIXME: mettere un test se c'e' il punto e virgola? */
419 CurrToken = HighParser::DESCRIPTION;
420
421 return true;
422 }
423
424 void
Eof(void)425 IncludeParser::Eof(void)
426 {
427 if (!fCheckStack()) {
428 throw EndOfFile(MBDYN_EXCEPT_ARGS);
429 }
430 }
431
432 /*
433 * returns a dynamically allocated string with environment variables expanded
434 */
435 static char *
expand_environment(const char * in)436 expand_environment(const char *in)
437 {
438 char *out = NULL;
439 #define MAXSUBST 10
440 struct {
441 unsigned start;
442 unsigned end;
443 const char *value;
444 unsigned length;
445 } subst[MAXSUBST];
446 unsigned cnt = 0, c;
447
448 DEBUGCOUT(">> expand_environment: " << in << std::endl);
449
450 subst[cnt].start = 0;
451 subst[cnt].end = 0;
452 subst[cnt].value = NULL;
453 subst[cnt].length = 0;
454 for (c = 0; in[c]; c++) {
455 if (in[c] == '$') {
456 if (cnt >= MAXSUBST - 2) {
457 silent_cerr("too many substitutions in \""
458 << in << "\"" << std::endl);
459 return NULL;
460 }
461
462 subst[cnt].end = c;
463 if (in[c + 1] == '$') {
464 c++;
465 subst[cnt].start = c;
466 subst[cnt].value = "";
467 subst[cnt].length = 0;
468 continue;
469 }
470
471 c++;
472 unsigned namepos = c;
473 if (in[c] == '{') {
474 const char *end = std::strchr(&in[c], '}');
475
476 if (end == NULL) {
477 silent_cerr("missing trailing \"}\" "
478 "in \"" << in << "\""
479 << std::endl);
480 return NULL;
481 }
482
483 namepos++;
484 unsigned l = end - &in[namepos];
485 char buf[l + 1];
486 memcpy(buf, &in[namepos], l);
487 buf[l] = '\0';
488 subst[cnt].value = getenv(buf);
489 if (subst[cnt].value == NULL) {
490 silent_cerr("unable to find "
491 "environment "
492 "variable \""
493 << buf << "\""
494 << std::endl);
495 return NULL;
496 }
497
498 c = end - &in[0] + 1;
499
500 } else {
501 if (in[c] != '_' && !isalpha(in[c])) {
502 silent_cerr("illegal leading char "
503 "in environment "
504 "variable name in \""
505 << in << "\""
506 << std::endl);
507 return NULL;
508 }
509
510 for (c++; in[c]; c++) {
511 if (in[c] != '_' && !isalnum(in[c])) {
512 break;
513 }
514 }
515
516 unsigned l = &in[c] - &in[namepos];
517 char buf[l + 1];
518 memcpy(buf, &in[namepos], l);
519 buf[l] = '\0';
520
521 subst[cnt].value = getenv(buf);
522 if (subst[cnt].value == NULL) {
523 silent_cerr("unable to find "
524 "environment "
525 "variable \""
526 << buf << "\""
527 << std::endl);
528 return NULL;
529 }
530 }
531
532 /* can't be NULL */
533 subst[cnt].length = strlen(subst[cnt].value);
534
535 cnt++;
536 subst[cnt].start = c;
537
538 /* because it's incremented again by "for" */
539 c--;
540 }
541 }
542 subst[cnt].end = c;
543 subst[cnt].value = NULL;
544 subst[cnt].length = 0;
545
546 unsigned len = 0;
547 for (c = 0; c < cnt; c++) {
548 len += (subst[c].end - subst[c].start) + subst[c].length;
549 }
550 len += subst[c].end - subst[c].start;
551
552 SAFENEWARR(out, char, len + 1);
553
554 unsigned p = 0;
555 for (c = 0; c < cnt; c++) {
556 unsigned l = subst[c].end - subst[c].start;
557 if (l > 0) {
558 memcpy(&out[p], &in[subst[c].start], l);
559 p += l;
560 }
561 if (subst[c].length > 0) {
562 memcpy(&out[p], subst[c].value, subst[c].length);
563 p += subst[c].length;
564 }
565 }
566 unsigned l = subst[c].end - subst[c].start;
567 if (l > 0) {
568 memcpy(&out[p], &in[subst[c].start], l);
569 p += l;
570 }
571 out[p] = '\0';
572
573 DEBUGCOUT("<< expand_environment: " << out << std::endl);
574
575 return out;
576 }
577
578 static char *
resolve_filename(const char * filename_in)579 resolve_filename(const char *filename_in)
580 {
581 char *res = NULL,
582 *filename = NULL;;
583
584 if (strchr(filename_in, '$')) {
585 filename = expand_environment(filename_in);
586 if (filename == NULL) {
587 goto error_return;
588 }
589 } else {
590 filename = (char *)filename_in;
591 }
592
593 if (filename[0] == '~') {
594 filename++;
595 if (filename[0] == DIR_SEP) {
596 /* do environment stuff */
597 char *home;
598
599 home = getenv("HOME");
600 if (home == NULL) {
601 goto error_return;
602 }
603
604 char *s = NULL;
605 int l, ll;
606
607 l = strlen(home);
608 ll = l + strlen(filename) + 1;
609 SAFENEWARR(s, char, ll);
610
611 strncpy(s, home, l);
612 strcpy(s + l, filename);
613
614 res = s;
615 goto error_return;
616
617 #if defined(HAVE_PWD_H)
618 } else {
619 const char *p;
620
621 p = std::strchr(filename, DIR_SEP);
622 if (p == NULL) {
623 goto error_return;
624 }
625
626 int l = p - filename;
627
628 char buf[l + 1];
629 memcpy(buf, filename, l);
630 buf[l] = '\0';
631
632 /* do passwd stuff */
633 struct passwd *pw;
634
635 pw = getpwnam(buf);
636
637 if (pw == NULL ) {
638 goto error_return;
639 }
640
641 l = strlen(pw->pw_dir);
642 int ll = l + strlen(p) + 1;
643 char *s = NULL;
644 SAFENEWARR(s, char, ll);
645 strncpy(s, pw->pw_dir, l);
646 strcpy(s + l, p);
647
648 res = s;
649 goto error_return;
650 #endif /* HAVE_PWD_H */
651 }
652 }
653
654 error_return:;
655 if (filename != NULL) {
656 if (res == NULL) {
657 if (filename != filename_in) {
658 res = filename;
659 } else {
660 SAFESTRDUP(res, filename_in);
661 }
662 } else {
663 if (filename != filename_in) {
664 SAFEDELETEARR(filename);
665 }
666 }
667 }
668
669 return res;
670 }
671
672 const char*
GetFileName(enum Delims Del)673 IncludeParser::GetFileName(enum Delims Del)
674 {
675 const char *s = GetStringWithDelims(Del);
676 if (s == 0) {
677 return 0;
678 }
679
680 const char *stmp = resolve_filename(s);
681 if (stmp == NULL) {
682 return 0;
683
684 } else {
685 if (strlen(stmp) >= iDefaultBufSize) {
686 SAFEDELETEARR(stmp);
687 return 0;
688 }
689 strcpy(sStringBuf, stmp);
690 SAFEDELETEARR(stmp);
691 }
692
693 return sStringBuf;
694 }
695
696 HighParser::ErrOut
GetLineData(void) const697 IncludeParser::GetLineData(void) const
698 {
699 ErrOut LineData;
700 LineData.sFileName = sCurrFile;
701 LineData.sPathName = (strcmp(sCurrPath, sInitialPath) == 0) ? 0 : sCurrPath;
702 LineData.iLineNumber = GetLineNumber();
703 return LineData;
704 }
705
706 /* IncludeParser - end */
707
708
709