1 /*
2  * input - nested input source file reader
3  *
4  * Copyright (C) 1999-2007,2014,2018,2021  David I. Bell
5  *
6  * Calc is open software; you can redistribute it and/or modify it under
7  * the terms of the version 2.1 of the GNU Lesser General Public License
8  * as published by the Free Software Foundation.
9  *
10  * Calc is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU Lesser General
13  * Public License for more details.
14  *
15  * A copy of version 2.1 of the GNU Lesser General Public License is
16  * distributed with calc under the filename COPYING-LGPL.  You should have
17  * received a copy with calc; if not, write to Free Software Foundation, Inc.
18  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  *
20  * Under source code control:	1990/02/15 01:48:16
21  * File existed as early as:	before 1990
22  *
23  * Share and enjoy!  :-)	http://www.isthe.com/chongo/tech/comp/calc/
24  */
25 
26 /*
27  * Nested input source file reader.
28  * For terminal input, this also provides a simple command stack.
29  */
30 
31 
32 #include <stdio.h>
33 #include <ctype.h>
34 #if !defined(_WIN32) && !defined(_WIN64)
35 # include <pwd.h>
36 #else
37 # include <stdlib.h>
38 #endif
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 
42 #include "have_unistd.h"
43 #if defined(HAVE_UNISTD_H)
44 #include <unistd.h>
45 #endif
46 
47 #if defined(_WIN32) || defined(_WIN64)
48 #include <limits.h>
49 #define _fullpath(f,n,s) (_fixpath(n,f),f)
50 #endif
51 
52 #include "calc.h"
53 #include "conf.h"
54 #include "hist.h"
55 #include "strl.h"
56 
57 
58 #include "banned.h"	/* include after system header <> includes */
59 
60 
61 EXTERN int stdin_tty;		/* TRUE if stdin is a tty */
62 E_FUNC FILE *f_open(char *name, char *mode);
63 E_FUNC FILE *curstream(void);
64 
65 
66 #define TTYSIZE		8191	/* reallocation size for terminal buffers */
67 #define MAXDEPTH	255	/* maximum depth of input */
68 #define IS_READ		1	/* reading normally */
69 #define IS_REREAD	2	/* reread current character */
70 #define chartoint(ch)	((ch) & 0xff)	/* make sure char is not negative */
71 #define READSET_ALLOC	8	/* readset to allocate chunk size */
72 
73 
74 typedef struct {
75 	int i_state;		/* state (read, reread) */
76 	int i_char;		/* currently read char */
77 	long i_line;		/* line number */
78 	char *i_cp;		/* pointer to string character to be read */
79 	char *i_str;		/* start of string copy to be read, or NULL */
80 	long i_num;		/* number of string characters remaining */
81 	char *i_ttystr;		/* current character of tty line (or NULL) */
82 	FILE *i_fp;		/* current file for input (if not NULL) */
83 	char *i_name;		/* file name if known */
84 } INPUT;
85 
86 
87 /* files that calc has read or included */
88 typedef struct {
89 	int active;		/* != 0 => active entry, 0 => unused entry */
90 	char *name;		/* name used to read file */
91 	char *path;		/* real path used to open file */
92 	struct stat inode;	/* inode information for file */
93 } READSET;
94 
95 STATIC READSET *readset = NULL;		/* array of files read */
96 STATIC int maxreadset = 0;		/* length of readset */
97 
98 STATIC int linesize;		/* current max size of input line */
99 STATIC char *linebuf;		/* current input line buffer */
100 STATIC char *prompt;		/* current prompt for terminal */
101 STATIC BOOL noprompt;		/* TRUE if should not print prompt */
102 
103 STATIC int depth;		/* current input depth */
104 STATIC INPUT *cip;		/* current input source */
105 STATIC INPUT inputs[MAXDEPTH];	/* input sources */
106 
107 
108 S_FUNC int openfile(char *name);
109 S_FUNC int ttychar(void);
110 S_FUNC int isinoderead(struct stat *sbuf);
111 S_FUNC int findfreeread(void);
112 S_FUNC int addreadset(char *name, char *path, struct stat *sbuf);
113 S_FUNC char *homeexpand(char *name);
114 
115 
116 /*
117  * Open an input file by possibly searching through a path list
118  * and also possibly applying the specified extension.	For example:
119  *
120  *	opensearchfile("barf", ".:/tmp", ".c", rd_once)
121  *
122  * searches in order for the files:
123  *
124  *	"./barf", "./barf.c", "/tmp/barf", and "/tmp/barf.c".
125  *
126  * Returns -1 if we could not open a file or error.
127  * Returns 1 if file was opened and added to/updated in the readset
128  * Returns 0 if file was already in the readset and reopen was 0.
129  *
130  * given:
131  *	name		file name to be read
132  *	pathlist	list of colon separated paths (or NULL)
133  *	extension	extra extension to try (or NULL)
134  *	rd_once		TRUE => do not reread a file
135  */
136 int
opensearchfile(char * name,char * pathlist,char * extension,int rd_once)137 opensearchfile(char *name, char *pathlist, char *extension, int rd_once)
138 {
139 	int i;
140 	char *cp;
141 	char *path;		/* name being searched for */
142 	size_t path_alloc;	/* length of malloced path */
143 	struct stat statbuf;	/* stat of the path */
144 	size_t namelen;	/* length of name */
145 	size_t extlen;	/* length of the extension if non-NULL or 0 */
146 	size_t pathlen;	/* length of the pathlist if non-NULL or 0 */
147 
148 	/* firewall */
149 	if (name == NULL) {
150 		math_error("NULL name given to opensearchfile");
151 		/*NOTREACHED*/
152 	}
153 
154 	/*
155 	 * We ignore the pathlist of the file is absolute, dot-relative
156 	 * or tilde-relative or if there is no search path.
157 	 */
158 	if (name[0] == PATHCHAR ||
159 	    name[0] == HOMECHAR ||
160 	    (name[0] == DOTCHAR && name[1] == '\0') ||
161 	    (name[0] == DOTCHAR && name[1] == DOTCHAR && name[2] == '\0') ||
162 	    (name[0] == DOTCHAR && name[1] == PATHCHAR) ||
163 	    (name[0] == DOTCHAR && name[1] == DOTCHAR && name[2] == PATHCHAR) ||
164 	    pathlist == NULL) {
165 		pathlist = "";
166 	}
167 
168 	/*
169 	 * allocate storage for the longest name being searched for
170 	 *
171 	 * We will allocate more than we could ever want/need.
172 	 * The longest we could ever need would be:
173 	 *
174 	 *	pathlist (as a single long string)
175 	 *	/
176 	 *	name
177 	 *	.
178 	 *	extension
179 	 *	\0
180 	 *	guard byte
181 	 */
182 	namelen = strlen(name);
183 	if (extension != NULL) {
184 	    extlen = strlen(extension);
185 	} else {
186 	    extlen = 0;
187 	}
188 	pathlen = strlen(pathlist);
189 	path_alloc = pathlen+1 + 1 + namelen+1 + extlen+1 + 1 + 1;
190 	path = malloc(path_alloc+1);
191 	if (path == NULL) {
192 		math_error("Cannot allocate filename path buffer");
193 		/*NOTREACHED*/
194 	}
195 	path[0] = '\0';	/* paranoia */
196 	path[path_alloc] = '\0';	/* paranoia */
197 
198 	/*
199 	 * Don't try the extension if the filename already contains it.
200 	 */
201 	if (extension != NULL && namelen >= extlen &&
202 	    strcmp(&name[namelen-extlen], extension) == 0) {
203 		extension = NULL;
204 	}
205 
206 	/*
207 	 * Search through the path list for the file
208 	 */
209 	pathlist--;
210 	do {
211 		pathlist++;
212 		cp = path;
213 		while (*pathlist && (*pathlist != LISTCHAR))
214 			*cp++ = *pathlist++;
215 		if (cp != path)
216 			*cp++ = PATHCHAR;
217 		strlcpy(cp, name, namelen+1);
218 		i = openfile(path);
219 		if ((i < 0) &&
220 		    (extension != NULL && extension[0] != '\0')) {
221 			strlcat(path, extension, path_alloc+1);
222 			i = openfile(path);
223 		}
224 	} while ((i < 0) && *pathlist);
225 
226 	/* examine what our search produced */
227 	if (i < 0) {
228 		free(path);
229 		return i;
230 	}
231 	if (cip->i_fp == NULL) {
232 		/* cannot find a file to open */
233 		free(path);
234 		return -3;
235 	}
236 	if (fstat(fileno(cip->i_fp), &statbuf) < 0) {
237 		/* unable to fstat the open file */
238 		free(path);
239 		return -4;
240 	}
241 
242 	/* note if we will reopen a file and if that is allowed */
243 	if (rd_once == TRUE && isinoderead(&statbuf) >= 0) {
244 		/* file is in readset and reopen is false */
245 		closeinput();
246 		free(path);
247 		return 1;
248 	}
249 
250 	/* add this name to the readset if allowed */
251 	if (addreadset(name, path, &statbuf) < 0) {
252 		/* cannot add to readset */
253 		closeinput();
254 		free(path);
255 		return -1;
256 	}
257 
258 	/* file was added to/updated in readset */
259 	free(path);
260 	return 0;
261 }
262 
263 
264 /*
265  * f_pathopen - open an absolute or relative filename along a search path
266  *
267  * Open a file by possibly searching through a path list.  For example:
268  *
269  *	f_pathopen("curds", ".:/tmp:~/pub", "r", NULL)
270  *
271  * searches in order for a file that it can open for reading:
272  *
273  *	"./curds", "/tmp/curds", "~/pub/curds"
274  *
275  * 	NOTE: ~ is expanded accordingly (see homeexpand() below).
276  *
277  * However is the file is /absolue/path/name or a ./dot/relative/name, or
278  * a ~/home/dir/name, or a ~user/home/name, then the pathlist is ignored
279  * we just attempt to open name.
280  *
281  * and opens the first one that exists and allows the mode.
282  *
283  *	name		file name to be read
284  *	mode		fopen() mode argument
285  *			  (one of "r", "w", "a", "r+", "w+", "a+")
286  *	pathlist	list of colon separated paths (or NULL)
287  *	openpath	if non-NULL, and file was opened, set to malloced
288  *		 	  path used to open
289  *
290  * returns:
291  *	open file stream, NULL ==> file was not found or error
292  *	If file was open and openpath was non-NULL, changed to point
293  *	to path used to open
294  */
295 FILE *
f_pathopen(char * name,char * mode,char * pathlist,char ** openpath)296 f_pathopen(char *name, char *mode, char *pathlist, char **openpath)
297 {
298 	char *cp;
299 	char *path;		/* name being searched for */
300 	size_t namelen;		/* length of name */
301 	size_t pathlen;		/* length of the pathlist if non-NULL or 0 */
302 	FILE *ret;		/* return open stream or NULL */
303 
304 	/* firewall */
305 	if (name == NULL) {
306 		math_error("NULL name given to f_pathopen");
307 		/*NOTREACHED*/
308 	}
309 	if (mode == NULL) {
310 		math_error("NULL mode given to f_pathopen");
311 		/*NOTREACHED*/
312 	}
313 
314 	/*
315 	 * We ignore the pathlist of the file is absolute, dot-relative
316 	 * or tilde-relative or if there is no search path.
317 	 */
318 	if (name[0] == PATHCHAR ||
319 	    name[0] == HOMECHAR ||
320 	    (name[0] == DOTCHAR && name[1] == '\0') ||
321 	    (name[0] == DOTCHAR && name[1] == DOTCHAR && name[2] == '\0') ||
322 	    (name[0] == DOTCHAR && name[1] == PATHCHAR) ||
323 	    (name[0] == DOTCHAR && name[1] == DOTCHAR && name[2] == PATHCHAR) ||
324 	    pathlist == NULL) {
325 		pathlist = "";
326 	}
327 
328 	/*
329 	 * allocate storage for the longest name being searched for
330 	 *
331 	 * We will allocate more than we could ever want/need.
332 	 * The longest we could ever need would be:
333 	 *
334 	 *	pathlist (as a single long string)
335 	 *	/
336 	 *	name
337 	 *	\0
338 	 *	guard byte
339 	 */
340 	namelen = strlen(name);
341 	pathlen = strlen(pathlist);
342 	path = malloc(pathlen+1 + 1 + namelen+1 + 1 + 1);
343 	if (path == NULL) {
344 		math_error("Cannot allocate f_pathopen buffer");
345 		/*NOTREACHED*/
346 	}
347 
348 	/*
349 	 * Search through the path list for the file
350 	 */
351 	pathlist--;
352 	do {
353 		pathlist++;
354 		cp = path;
355 		while (*pathlist && (*pathlist != LISTCHAR))
356 			*cp++ = *pathlist++;
357 		if (cp != path)
358 			*cp++ = PATHCHAR;
359 		strlcpy(cp, name, namelen+1);
360 		ret = f_open(path, mode);
361 	} while ((ret == NULL) && *pathlist);
362 
363 	/* if caller wants to know the path, malloc it and return it */
364 	if (openpath != NULL && ret != NULL) {
365 		if (path[0] == HOMECHAR) {
366 			*openpath = homeexpand(path);
367 		} else {
368 			*openpath = strdup(path);
369 		}
370 		if (*openpath == NULL) {
371 			free(path);
372 			if ((conf->calc_debug & CALCDBG_TTY) && ret == stdin) {
373 				printf("f_pathopen: closing stdin "
374 				       "on malloc return error\n");
375 			}
376 			fclose(ret);
377 			math_error("cannot malloc return openpath buffer");
378 			/*NOTREACHED*/
379 		}
380 	}
381 	free(path);
382 
383 	/* return open file or NULL */
384 	return ret;
385 }
386 
387 
388 /*
389  * Given a filename with a leading ~, expand it into a home directory for
390  * that user.  This function will malloc the space for the expanded path.
391  *
392  * If the path is just ~, or begins with ~/, expand it to the home
393  * directory of the current user.  If the environment variable $HOME
394  * is known, it will be used, otherwise the password file will be
395  * consulted.
396  *
397  * If the path is just ~username, or ~username/, expand it to the home
398  * directory of that user by looking it up in the password file.
399  *
400  * If the password file must be consulted and the username is not found
401  * a NULL pointer is returned.
402  *
403  * given:
404  *	name		a filename with a leading ~
405  */
406 S_FUNC char *
homeexpand(char * name)407 homeexpand(char *name)
408 {
409 #if defined(_WIN32) || defined(_WIN64)
410 
411 	return NULL;
412 
413 #else /* Windows free systems */
414 
415 	struct passwd *ent;	/* password entry */
416 	char *home2;		/* fullpath of the home directory */
417 	char *fullpath;		/* the malloced expanded path */
418 	char *after;		/* after the ~user or ~ */
419 	char *username;		/* extracted username */
420 	size_t fullpath_len;	/* length of fullpath */
421 	size_t snprintf_len;	/* malloced snprintf buffer length */
422 
423 	/* firewall */
424 	if (name[0] != HOMECHAR)
425 		return NULL;
426 
427 	/*
428 	 * obtain the home directory component
429 	 */
430 	switch (name[1]) {
431 	case PATHCHAR:		/* ~/... */
432 	case '\0':		/* ~ */
433 		home2 = home;
434 		after = name+1;
435 		break;
436 	default:		/* ~username or ~username/... */
437 
438 		/* extract the username after the ~ */
439 		after = (char *)strchr(name+2, PATHCHAR);
440 		if (after == NULL) {
441 			/* path is just ~username */
442 			ent = (struct passwd *)getpwnam(name+1);
443 			if (ent == NULL) {
444 				/* unknown user */
445 				return NULL;
446 			}
447 			/* just malloc the home directory and return it */
448 			fullpath_len = strlen(ent->pw_dir);
449 			fullpath = (char *)malloc(fullpath_len+1);
450 			if (fullpath == NULL) {
451 				return NULL;
452 			}
453 			strlcpy(fullpath, ent->pw_dir, fullpath_len+1);
454 			return fullpath;
455 		}
456 		username = (char *) malloc(after-name + 1 + 1);
457 		if (username == NULL) {
458 			/* failed to malloc username */
459 			return NULL;
460 		}
461 		strlcpy(username, name+1, after-name+1+1);
462 
463 		/* get that user's home directory */
464 		ent = (struct passwd *)getpwnam(username);
465 		free(username);
466 		if (ent == NULL) {
467 			/* unknown user */
468 			return NULL;
469 		}
470 		home2 = ent->pw_dir;
471 		break;
472 	}
473 
474 	/*
475 	 * build the fullpath given the home directory
476 	 */
477 	snprintf_len = strlen(home2)+strlen(after) + 1;
478 	fullpath = (char *)malloc(snprintf_len+1);
479 	if (fullpath == NULL) {
480 		return NULL;
481 	}
482 	snprintf(fullpath, snprintf_len, "%s%s", home2, after);
483 	fullpath[snprintf_len] = '\0';	/* paranoia */
484 	return fullpath;
485 #endif /* Windows free systems */
486 }
487 
488 
489 /*
490  * f_open - ~-expand a filename and fopen() it
491  *
492  * given:
493  *	name		the filename to open
494  *	mode		fopen() mode argument
495  *			    (one of "r", "w", "a", "r+", "w+", "a+")
496  */
497 FILE *
f_open(char * name,char * mode)498 f_open(char *name, char *mode)
499 {
500 	FILE *fp;		/* open file descriptor */
501 	char *fullname;		/* file name with HOMECHAR expansion */
502 
503 	/*
504 	 * be sore we are allowed to open a file in this mode
505 	 */
506 	if (!allow_read && !allow_write) {
507 		/* no reads and no writes means no opens! */
508 		if (run_state > RUN_BEGIN) {
509 			fprintf(stderr,
510 				"open of %s mode %s - %s\n", name, mode,
511 				"open for read or write disallowed by -m\n");
512 		}
513 		return NULL;
514 	} else if (!allow_read && strchr(mode, 'r') != NULL) {
515 		/* reading new files disallowed */
516 		if (run_state > RUN_BEGIN) {
517 			fprintf(stderr,
518 				"open of %s mode %s - %s\n", name, mode,
519 				"open for read disallowed by -m\n");
520 		}
521 		return NULL;
522 	} else if (!allow_write &&
523 		   (strchr(mode, 'w') != NULL ||
524 		    strchr(mode, 'a') != NULL ||
525 		    strchr(mode, '+') != NULL)) {
526 		/* writing new files disallowed */
527 		if (run_state > RUN_BEGIN) {
528 			fprintf(stderr,
529 				"open of %s mode %s - %s\n", name, mode,
530 				"open for write disallowed by -m\n");
531 		}
532 		return NULL;
533 	}
534 
535 	/*
536 	 * expand ~ if needed
537 	 */
538 	if (name[0] == HOMECHAR) {
539 		fullname = homeexpand(name);
540 		if (fullname == NULL)
541 			return NULL;
542 		fp = fopen(fullname, mode);
543 		free(fullname);
544 	} else {
545 		fp = fopen(name, mode);
546 	}
547 	return fp;
548 }
549 
550 
551 /*
552  * Setup for reading from a input file.
553  * Returns -1 if file could not be opened.
554  *
555  * given:
556  *	name		file name to be read
557  */
558 S_FUNC int
openfile(char * name)559 openfile(char *name)
560 {
561 	FILE *fp;		/* open file descriptor */
562 	size_t namelen;
563 
564 	if (depth >= MAXDEPTH)
565 		 return -2;
566 	fp = f_open(name, "r");
567 	if (fp == NULL)
568 		 return -1;
569 	cip = inputs + depth++;
570 	cip->i_state = IS_READ;
571 	cip->i_char = '\0';
572 	cip->i_str = NULL;
573 	cip->i_ttystr = NULL;
574 	cip->i_fp = fp;
575 	cip->i_line = 1;
576 	namelen = strlen(name);
577 	cip->i_name = (char *)malloc(namelen+1);
578 	if (cip->i_name == NULL) {
579 		 return -1;
580 	}
581 	strlcpy(cip->i_name, name, namelen+1);
582 	return 0;
583 }
584 
585 
586 /*
587  * Return the current input file stream, or NULL if none.
588  */
589 FILE *
curstream(void)590 curstream(void)
591 {
592 	if (depth <= 0 || depth > MAXDEPTH)
593 		return NULL;
594 	return cip->i_fp;
595 }
596 
597 
598 /*
599  * Open a string for scanning, num characters to be read.
600  * String is copied into local memory so it can be trashed afterwards.
601  * Returns -1 if cannot open string.
602  *
603  * given:
604  *	str		string to be opened
605  *	num		length of string to open
606  */
607 int
openstring(char * str,size_t num)608 openstring(char *str, size_t num)
609 {
610 	char *cp;		/* copied string */
611 
612 	if ((depth >= MAXDEPTH) || (str == NULL))
613 		 return -2;
614 	cp = (char *) malloc(num + 1);
615 	if (cp == NULL)
616 		 return -1;
617 	strlcpy(cp, str, num+1);
618 	cip = inputs + depth++;
619 	cip->i_state = IS_READ;
620 	cip->i_char = '\0';
621 	cip->i_cp = cp;
622 	cip->i_str = cp;
623 	cip->i_num = num;
624 	cip->i_fp = NULL;
625 	cip->i_name = NULL;
626 	cip->i_ttystr = NULL;
627 	cip->i_line = 1;
628 	return 0;
629 }
630 
631 
632 /*
633  * Set to read input from the terminal.
634  * Returns -1 if there is no more depth for input.
635  */
636 int
openterminal(void)637 openterminal(void)
638 {
639 	if (depth >= MAXDEPTH)
640 		 return -2;
641 	cip = inputs + depth++;
642 	cip->i_state = IS_READ;
643 	cip->i_char = '\0';
644 	cip->i_str = NULL;
645 	cip->i_ttystr = NULL;
646 	cip->i_fp = NULL;
647 	cip->i_name = NULL;
648 	cip->i_line = 1;
649 	return 0;
650 }
651 
652 
653 /*
654  * Close the current input source.
655  */
656 void
closeinput(void)657 closeinput(void)
658 {
659 	if (depth <= 0)
660 		return;
661 	if (cip->i_str)
662 		free(cip->i_str);
663 	if (cip->i_fp) {
664 		if ((conf->calc_debug & CALCDBG_TTY) && cip->i_fp == stdin) {
665 			printf("closeinput: closing stdin "
666 			        "at depth: %d\n", depth);
667 		}
668 		fclose(cip->i_fp);
669 	}
670 	if (cip->i_name)
671 		free(cip->i_name);
672 	depth--;
673 	cip = depth ? &inputs[depth - 1] : NULL;
674 }
675 
676 
677 /*
678  * Reset the input sources back to the initial state.
679  */
680 void
resetinput(void)681 resetinput(void)
682 {
683 	while (depth > 0)
684 		closeinput();
685 	noprompt = FALSE;
686 }
687 
688 
689 /*
690  * Set the prompt for terminal input.
691  */
692 void
setprompt(char * str)693 setprompt(char *str)
694 {
695 	prompt = str;
696 	noprompt = FALSE;
697 }
698 
699 
700 /*
701  * Read the next character from the current input source.
702  * End of file closes current input source, and returns EOF character.
703  */
704 int
nextchar(void)705 nextchar(void)
706 {
707 	int ch;			/* current input character */
708 
709 	if (depth == 0)		/* input finished */
710 		 return EOF;
711 	if (cip->i_state == IS_REREAD) {	/* rereading current char */
712 		 ch = cip->i_char;
713 		 cip->i_state = IS_READ;
714 		 if (ch == '\n')
715 			cip->i_line++;
716 		 return ch;
717 	}
718 	if (cip->i_str) {		/* from string */
719 		if (cip->i_num) {
720 			ch = chartoint(*cip->i_cp++);
721 			cip->i_num--;
722 		} else {
723 			ch = EOF;
724 		}
725 	} else if (cip->i_fp) {		/* from file */
726 		ch = fgetc(cip->i_fp);
727 	} else if (!stdin_tty) {		/* from file */
728 		ch = fgetc(stdin);
729 	} else {			/* from terminal */
730 		ch = ttychar();
731 	}
732 	if (depth > 0)
733 		cip->i_char = ch;	/* save for rereads */
734 	if (ch == '\n')
735 		cip->i_line++;
736 	return ch;
737 }
738 
739 
740 /*
741  * Read in the next line of input from the current input source.
742  * The line is terminated with a null character, and does not contain
743  * the final newline character.	 The returned string is only valid
744  * until the next such call, and so must be copied if necessary.
745  * Returns NULL on end of file.
746  */
747 char *
nextline(void)748 nextline(void)
749 {
750 	char *cp;
751 	int ch;
752 	int len;
753 
754 	cp = linebuf;
755 	if (linesize == 0) {
756 		cp = (char *)malloc(TTYSIZE + 1);
757 		if (cp == NULL) {
758 			math_error("Cannot allocate line buffer");
759 			/*NOTREACHED*/
760 		}
761 		linebuf = cp;
762 		linesize = TTYSIZE;
763 	}
764 	len = 0;
765 	for (;;) {
766 		noprompt = TRUE;
767 		ch = nextchar();
768 		noprompt = FALSE;
769 		if (ch == EOF)
770 			return NULL;
771 		if (ch == '\0')
772 			continue;
773 		if (ch == '\n')
774 			break;
775 		if (len >= linesize) {
776 			cp = (char *)realloc(cp, linesize + TTYSIZE + 1);
777 			if (cp == NULL) {
778 				math_error("Cannot realloc line buffer");
779 				/*NOTREACHED*/
780 			}
781 			linebuf = cp;
782 			linesize += TTYSIZE;
783 		}
784 		cp[len++] = (char)ch;
785 	}
786 	cp[len] = '\0';
787 	return linebuf;
788 }
789 
790 
791 /*
792  * Read the next character from the terminal.
793  * The routines in the history module are called so that the user
794  * can use a command history and emacs-like editing of the line.
795  */
796 S_FUNC int
ttychar(void)797 ttychar(void)
798 {
799 	int ch;			/* current char */
800 	int len;		/* length of current command */
801 	STATIC char charbuf[256*1024];
802 
803 	/*
804 	 * If we have more to read from the saved command line, then do that.
805 	 * When we see a newline character, then clear the pointer so we will
806 	 * read a new line on the next call.
807 	 */
808 	if (cip->i_ttystr) {
809 		ch = chartoint(*cip->i_ttystr++);
810 		if (ch == '\n')
811 			cip->i_ttystr = NULL;
812 		return ch;
813 	}
814 
815 	/*
816 	 * We need another complete line.
817 	 */
818 	abortlevel = 0;
819 	inputwait = TRUE;
820 	len = hist_getline(noprompt ? "" : prompt, charbuf, sizeof(charbuf));
821 	if (len == 0) {
822 		inputwait = FALSE;
823 		return EOF;
824 	}
825 	inputwait = FALSE;
826 
827 	/*
828 	 * Handle shell escape if present
829 	 */
830 	if (charbuf[0] == '!') {		/* do a shell command */
831 		char *cmd;
832 		int ret;
833 
834 		cmd = charbuf + 1;
835 		if (*cmd == '\0' || *cmd == '\n')
836 			cmd = shell;
837 		if (allow_exec) {
838 			if (conf->calc_debug & CALCDBG_SYSTEM) {
839 				printf("%s\n", cmd);
840 			}
841 			ret = system(cmd);
842 			if (ret < 0) {
843 				fprintf(stderr, "error in cmd: %s\n", cmd);
844 			}
845 		} else {
846 			fprintf(stderr, "execution disallowed by -m flag\n");
847 		}
848 		return '\n';
849 	}
850 	hist_saveline(charbuf, len);
851 
852 	/*
853 	 * Return the first character of the line, and set up to
854 	 * return the rest of it with later calls.
855 	 */
856 	ch = chartoint(charbuf[0]);
857 	if (ch != '\n')
858 		cip->i_ttystr = charbuf + 1;
859 	return ch;
860 }
861 
862 
863 /*
864  * Return whether or not the input source is the terminal.
865  */
866 BOOL
inputisterminal(void)867 inputisterminal(void)
868 {
869 	return ((depth <= 0) || ((cip->i_str == NULL) && (cip->i_fp == NULL)));
870 }
871 
872 
873 /*
874  * Return depth of current input source
875  */
876 int
inputlevel(void)877 inputlevel(void)
878 {
879 	return depth - 1;
880 }
881 
882 
883 /*
884  * Return the name of the current input file.
885  * Returns NULL for terminal or strings.
886  */
887 char *
inputname(void)888 inputname(void)
889 {
890 	if (depth <= 0)
891 		return NULL;
892 	return cip->i_name;
893 }
894 
895 
896 /*
897  * Return the current line number.
898  */
899 long
linenumber(void)900 linenumber(void)
901 {
902 	if (depth > 0)
903 		return cip->i_line;
904 	return 1;
905 }
906 
907 
908 /*
909  * Restore the next character to be read again on the next nextchar call.
910  */
911 void
reread(void)912 reread(void)
913 {
914 	if ((depth <= 0) || (cip->i_state == IS_REREAD))
915 		return;
916 	cip->i_state = IS_REREAD;
917 	if (cip->i_char == '\n')
918 		cip->i_line--;
919 }
920 
921 
922 /*
923  * Process all startup files found in the $CALCRC path.
924  */
925 void
runrcfiles(void)926 runrcfiles(void)
927 {
928 	char path[MAX_CALCRC+1+1];	/* name being searched for */
929 	char *cp;
930 	char *p;
931 
932 	/* execute each file in the list */
933 	while (calcrc != NULL && *calcrc) {
934 		cp = calcrc;
935 		calcrc = (char *) strchr(calcrc + 1, LISTCHAR);
936 
937 		/* load file name into the path */
938 		if (calcrc == NULL) {
939 			strlcpy(path, cp, MAX_CALCRC+1);
940 		} else {
941 			strlcpy(path, cp, calcrc - cp + 1);
942 		}
943 
944 		/* find the start of the path */
945 		p = (path[0] == ':') ? path + 1 : path;
946 		if (p[0] == '\0') {
947 			continue;
948 		}
949 
950 		/* process the current file in the list */
951 		if (openfile(p) < 0) {
952 			/* Unable to open rcfile */
953 			if (c_flag && !d_flag)
954 				 fprintf(stderr,
955 					"Unable to open rcfile \"%s\"\n", p);
956 			continue;
957 		}
958 		getcommands(FALSE);
959 		closeinput();
960 	}
961 }
962 
963 
964 /*
965  * isinoderead - determine if we have read a given dev/inode
966  *
967  * This function returns the index of the readset element that matches
968  * a given device/inode, -1 otherwise.
969  *
970  *
971  * _WIN32 and _WIN64 NOTE:
972  *
973  *	This function likely will not work under _WIN32 or _WIN64.
974  *
975  *	The sbuf->st_ino is always zero because the FAT and NTFS filesystems
976  *	do not support inodes.  Those filesystems don't support links, which
977  *	is why you need this function under UNIX. For _WIN32 or _WIN64, use
978  *	_fullpath() to determine if you have already opened a file.
979  *
980  * given:
981  *	sbuf		stat of the inode in question
982  */
983 
984 S_FUNC int
isinoderead(struct stat * sbuf)985 isinoderead(struct stat *sbuf)
986 {
987 	int i;
988 
989 	/* deal with the empty case */
990 	if (readset == NULL || maxreadset <= 0) {
991 		/* readset is empty */
992 		return -1;
993 	}
994 
995 	/* scan the entire readset */
996 	for (i=0; i < maxreadset; ++i) {
997 #if defined(_WIN32) || defined(_WIN64)
998 		char tmp[MAX_PATH+1];
999 		tmp[MAX_PATH] = '\0';
1000 		if (_fullpath(tmp, cip->i_name, MAX_PATH) &&
1001 		    readset[i].active &&
1002 		    strcasecmp(readset[i].path, tmp) == 0) {
1003 			/* found a match */
1004 			return i;
1005 		}
1006 #else /* Windows free systems */
1007 		if (readset[i].active &&
1008 		    sbuf->st_dev == readset[i].inode.st_dev &&
1009 		    sbuf->st_ino == readset[i].inode.st_ino) {
1010 			/* found a match */
1011 			return i;
1012 		}
1013 #endif /* Windows free systems */
1014 	}
1015 
1016 	/* no match found */
1017 	return -1;
1018 }
1019 
1020 
1021 /*
1022  * findfreeread - find the next free readset element
1023  *
1024  * This function will return the index of the next free readset element.
1025  * If needed, this function will allocate new readset elements.
1026  *
1027  * This function returns the index of the next free element, or -1.
1028  */
1029 S_FUNC int
findfreeread(void)1030 findfreeread(void)
1031 {
1032 	int i;
1033 
1034 	/* deal with an empty readset case */
1035 	if (readset == NULL || maxreadset <= 0) {
1036 
1037 		/* malloc a new readset */
1038 		readset = (READSET *)malloc((READSET_ALLOC+1)*sizeof(READSET));
1039 		if (readset == NULL) {
1040 			return -1;
1041 		}
1042 		maxreadset = READSET_ALLOC;
1043 		for (i=0; i < READSET_ALLOC; ++i) {
1044 			readset[i].active = 0;
1045 		}
1046 
1047 		/* return first entry */
1048 		return 0;
1049 	}
1050 
1051 	/* try to find a free readset entry */
1052 	for (i=0; i < maxreadset; ++i) {
1053 		if (readset[i].active == 0) {
1054 			/* found a free readset entry */
1055 			return i;
1056 		}
1057 	}
1058 
1059 	/* all readset entries are in use, allocate more */
1060 	readset = (READSET *)realloc(readset,
1061 	    (maxreadset+READSET_ALLOC) * sizeof(READSET));
1062 	if (readset == NULL) {
1063 		return -1;
1064 	}
1065 	for (i=0; i < READSET_ALLOC; ++i) {
1066 		readset[i+maxreadset].active = 0;
1067 	}
1068 	maxreadset += READSET_ALLOC;
1069 
1070 	/* return the first newly allocated free entry */
1071 	return maxreadset-READSET_ALLOC;
1072 }
1073 
1074 
1075 /*
1076  * addreadset - add a entry to the readset array if it is not already there
1077  *
1078  * This function attempts to add a file into the readset.  If the readset
1079  * has an entry with a matching dev/inode, then that entry is updated with
1080  * the new name and path.  If no such readset entry is found, a new entry
1081  * is added.
1082  *
1083  * This function returns the index of the readset entry, or -1 if error.
1084  *
1085  * given:
1086  *	name		name given to read or include
1087  *	path		full pathname of file
1088  *	sbuf		stat of the path
1089  */
1090 S_FUNC int
addreadset(char * name,char * path,struct stat * sbuf)1091 addreadset(char *name, char *path, struct stat *sbuf)
1092 {
1093 	int ret;		/* index to return */
1094 	size_t name_len;	/* length of read set name */
1095 	size_t path_len;	/* length of path to read set name */
1096 
1097 	/* find the inode */
1098 	ret = isinoderead(sbuf);
1099 	if (ret < 0) {
1100 		/* not in readset, find a free node */
1101 		ret = findfreeread();
1102 		if (ret < 0) {
1103 			/* cannot find/form a free readset entry */
1104 			return -1;
1105 		}
1106 	} else {
1107 		/* found an readset entry, free old readset data */
1108 		if (readset[ret].name != NULL) {
1109 			free(readset[ret].name);
1110 		}
1111 		if (readset[ret].path != NULL) {
1112 			free(readset[ret].path);
1113 		}
1114 	}
1115 
1116 	/* load our information into the readset entry */
1117 	name_len = strlen(name);
1118 	readset[ret].name = (char *)malloc(name_len+1);
1119 	if (readset[ret].name == NULL) {
1120 		return -1;
1121 	}
1122 	strlcpy(readset[ret].name, name, name_len+1);
1123 #if defined(_WIN32) || defined(_WIN64)
1124 	/*
1125 	 * For _WIN32 or _WIN64, _fullpath expands the path to a fully qualified
1126 	 * path name, which under _WIN32 or _WIN64 FAT and NTFS is unique, just
1127 	 * like UNIX inodes. _fullpath also allocated the memory for
1128 	 * this new longer path name.
1129 	 */
1130 	 {
1131 		readset[ret].path = _fullpath(NULL, path, MAX_PATH);
1132 		if (readset[ret].path == NULL) {
1133 			return -1;
1134 		}
1135 	 }
1136 #else /* Windows free systems */
1137 	path_len = strlen(path);
1138 	readset[ret].path = (char *)malloc(path_len+1);
1139 	if (readset[ret].path == NULL) {
1140 		return -1;
1141 	}
1142 	strlcpy(readset[ret].path, path, path_len+1);
1143 #endif /* Windows free systems */
1144 	readset[ret].inode = *sbuf;
1145 	readset[ret].active = 1;
1146 
1147 	/* return index of the newly added entry */
1148 	return ret;
1149 }
1150