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