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