2 * input - nested input source file reader
4 * Copyright (C) 1999-2007 David I. Bell
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.
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.
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.
20 * @(#) $Revision: 30.3 $
21 * @(#) $Id: input.c,v 30.3 2013/08/11 08:41:38 chongo Exp $
22 * @(#) $Source: /usr/local/src/bin/calc/RCS/input.c,v $
24 * Under source code control: 1990/02/15 01:48:16
25 * File existed as early as: before 1990
27 * Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
31 * Nested input source file reader.
32 * For terminal input, this also provides a simple command stack.
43 #include <sys/types.h>
46 #include "have_unistd.h"
47 #if defined(HAVE_UNISTD_H)
51 #if defined(__MSDOS__)
53 #define _fullpath(f,n,s) (_fixpath(n,f),f)
54 #define _MAX_PATH PATH_MAX
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);
66 #define TTYSIZE 100 /* reallocation size for terminal buffers */
67 #define MAXDEPTH 10 /* 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 */
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 */
87 /* files that calc has read or included */
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 */
95 STATIC READSET
*readset
= NULL
; /* array of files read */
96 STATIC
int maxreadset
= 0; /* length of readset */
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 */
103 STATIC
int depth
; /* current input depth */
104 STATIC INPUT
*cip
; /* current input source */
105 STATIC INPUT inputs
[MAXDEPTH
]; /* input sources */
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
);
117 * Open an input file by possibly searching through a path list
118 * and also possibly applying the specified extension. For example:
120 * opensearchfile("barf", ".:/tmp", ".c", rd_once)
122 * searches in order for the files:
124 * "./barf", "./barf.c", "/tmp/barf", and "/tmp/barf.c".
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.
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
137 opensearchfile(char *name
, char *pathlist
, char *extension
, int rd_once
)
141 char *path
; /* name being searched for */
142 struct stat statbuf
; /* stat of the path */
143 size_t namelen
; /* length of name */
144 size_t extlen
; /* length of the extension if non-NULL or 0 */
145 size_t pathlen
; /* length of the pathlist if non-NULL or 0 */
149 math_error("NULL name given to opensearchfile");
154 * We ignore the pathlist of the file is absolute, dot-relative
155 * or tilde-relative or if there is no search path.
157 if (name
[0] == PATHCHAR
||
158 name
[0] == HOMECHAR
||
159 (name
[0] == DOTCHAR
&& name
[1] == '\0') ||
160 (name
[0] == DOTCHAR
&& name
[1] == DOTCHAR
&& name
[2] == '\0') ||
161 (name
[0] == DOTCHAR
&& name
[1] == PATHCHAR
) ||
162 (name
[0] == DOTCHAR
&& name
[1] == DOTCHAR
&& name
[2] == PATHCHAR
) ||
168 * allocate storage for the longest name being searched for
170 * We will allocate more than we could ever want/need.
171 * The longest we could ever need would be:
173 * pathlist (as a single long string)
181 namelen
= strlen(name
);
182 if (extension
!= NULL
) {
183 extlen
= strlen(extension
);
187 pathlen
= strlen(pathlist
);
188 path
= malloc(pathlen
+1 + 1 + namelen
+1 + extlen
+1 + 1 + 1);
190 math_error("Cannot allocate filename path buffer");
195 * Don't try the extension if the filename already contains it.
197 if (extension
!= NULL
&& namelen
>= extlen
&&
198 strcmp(&name
[namelen
-extlen
], extension
) == 0) {
203 * Search through the path list for the file
209 while (*pathlist
&& (*pathlist
!= LISTCHAR
))
213 strncpy(cp
, name
, namelen
+1);
216 (extension
!= NULL
&& extension
[0] != '\0')) {
217 strcat(path
, extension
);
220 } while ((i
< 0) && *pathlist
);
222 /* examine what our search produced */
227 if (cip
->i_fp
== NULL
) {
228 /* cannot find a file to open */
232 if (fstat(fileno(cip
->i_fp
), &statbuf
) < 0) {
233 /* unable to fstat the open file */
238 /* note if we will reopen a file and if that is allowed */
239 if (rd_once
== TRUE
&& isinoderead(&statbuf
) >= 0) {
240 /* file is in readset and reopen is false */
246 /* add this name to the readset if allowed */
247 if (addreadset(name
, path
, &statbuf
) < 0) {
248 /* cannot add to readset */
254 /* file was added to/updated in readset */
261 * f_pathopen - open an absolute or relative filename along a search path
263 * Open a file by possibly searching through a path list. For example:
265 * f_pathopen("curds", ".:/tmp:~/pub", "r", NULL)
267 * searches in order for a file that it can open for reading:
269 * "./curds", "/tmp/curds", "~/pub/curds"
271 * NOTE: ~ is expanded accordingly (see homeexpand() below).
273 * However is the file is /absolue/path/name or a ./dot/relative/name, or
274 * a ~/home/dir/name, or a ~user/home/name, then the pathlist is ignored
275 * we just attempt to open name.
277 * and opens the first one that exists and allows the mode.
279 * name file name to be read
280 * mode fopen() mode argument
281 * (one of "r", "w", "a", "r+", "w+", "a+")
282 * pathlist list of colon separated paths (or NULL)
283 * openpath if non-NULL, and file was opened, set to malloced
287 * open file stream, NULL ==> file was not found or error
288 * If file was open and openpath was non-NULL, changed to point
289 * to path used to open
292 f_pathopen(char *name
, char *mode
, char *pathlist
, char **openpath
)
295 char *path
; /* name being searched for */
296 size_t namelen
; /* length of name */
297 size_t pathlen
; /* length of the pathlist if non-NULL or 0 */
298 FILE *ret
; /* return open stream or NULL */
302 math_error("NULL name given to f_pathopen");
306 math_error("NULL mode given to f_pathopen");
311 * We ignore the pathlist of the file is absolute, dot-relative
312 * or tilde-relative or if there is no search path.
314 if (name
[0] == PATHCHAR
||
315 name
[0] == HOMECHAR
||
316 (name
[0] == DOTCHAR
&& name
[1] == '\0') ||
317 (name
[0] == DOTCHAR
&& name
[1] == DOTCHAR
&& name
[2] == '\0') ||
318 (name
[0] == DOTCHAR
&& name
[1] == PATHCHAR
) ||
319 (name
[0] == DOTCHAR
&& name
[1] == DOTCHAR
&& name
[2] == PATHCHAR
) ||
325 * allocate storage for the longest name being searched for
327 * We will allocate more than we could ever want/need.
328 * The longest we could ever need would be:
330 * pathlist (as a single long string)
336 namelen
= strlen(name
);
337 pathlen
= strlen(pathlist
);
338 path
= malloc(pathlen
+1 + 1 + namelen
+1 + 1 + 1);
340 math_error("Cannot allocate f_pathopen buffer");
345 * Search through the path list for the file
351 while (*pathlist
&& (*pathlist
!= LISTCHAR
))
355 strncpy(cp
, name
, namelen
+1);
356 ret
= f_open(path
, mode
);
357 } while ((ret
== NULL
) && *pathlist
);
359 /* if caller wants to know the path, malloc it and return it */
360 if (openpath
!= NULL
&& ret
!= NULL
) {
361 if (path
[0] == HOMECHAR
) {
362 *openpath
= homeexpand(path
);
364 *openpath
= strdup(path
);
366 if (*openpath
== NULL
) {
369 math_error("cannot malloc return openpath buffer");
375 /* return open file or NULL */
381 * Given a filename with a leading ~, expand it into a home directory for
382 * that user. This function will malloc the space for the expanded path.
384 * If the path is just ~, or begins with ~/, expand it to the home
385 * directory of the current user. If the environment variable $HOME
386 * is known, it will be used, otherwise the password file will be
389 * If the path is just ~username, or ~username/, expand it to the home
390 * directory of that user by looking it up in the password file.
392 * If the password file must be consulted and the username is not found
393 * a NULL pointer is returned.
396 * name a filename with a leading ~
399 homeexpand(char *name
)
405 #else /* Windoz free systems */
407 struct passwd
*ent
; /* password entry */
408 char *home2
; /* fullpath of the home directory */
409 char *fullpath
; /* the malloced expanded path */
410 char *after
; /* after the ~user or ~ */
411 char *username
; /* extracted username */
412 size_t fullpath_len
; /* length of fullpath */
415 if (name
[0] != HOMECHAR
)
419 * obtain the home directory component
422 case PATHCHAR
: /* ~/... */
427 default: /* ~username or ~username/... */
429 /* extract the username after the ~ */
430 after
= (char *)strchr(name
+2, PATHCHAR
);
432 /* path is just ~username */
433 ent
= (struct passwd
*)getpwnam(name
+1);
438 /* just malloc the home directory and return it */
439 fullpath_len
= strlen(ent
->pw_dir
);
440 fullpath
= (char *)malloc(fullpath_len
+1);
441 if (fullpath
== NULL
) {
444 strncpy(fullpath
, ent
->pw_dir
, fullpath_len
+1);
447 username
= (char *) malloc(after
-name
+ 1 + 1);
448 if (username
== NULL
) {
449 /* failed to malloc username */
452 strncpy(username
, name
+1, after
-name
-1);
453 username
[after
-name
-1] = '\0';
455 /* get that user's home directory */
456 ent
= (struct passwd
*)getpwnam(username
);
467 * build the fullpath given the home directory
469 fullpath
= (char *)malloc(strlen(home2
)+strlen(after
)+1);
470 if (fullpath
== NULL
) {
473 sprintf(fullpath
, "%s%s", home2
, after
);
475 #endif /* Windoz free systems */
480 * f_open - ~-expand a filename and fopen() it
483 * name the filename to open
484 * mode fopen() mode argument
485 * (one of "r", "w", "a", "r+", "w+", "a+")
488 f_open(char *name
, char *mode
)
490 FILE *fp
; /* open file descriptor */
491 char *fullname
; /* file name with HOMECHAR expansion */
494 * be sore we are allowed to open a file in this mode
496 if (!allow_read
&& !allow_write
) {
497 /* no reads and no writes means no opens! */
498 if (run_state
> RUN_BEGIN
) {
500 "open of %s mode %s - %s\n", name
, mode
,
501 "open for read or write disallowed by -m\n");
504 } else if (!allow_read
&& strchr(mode
, 'r') != NULL
) {
505 /* reading new files disallowed */
506 if (run_state
> RUN_BEGIN
) {
508 "open of %s mode %s - %s\n", name
, mode
,
509 "open for read disallowed by -m\n");
512 } else if (!allow_write
&&
513 (strchr(mode
, 'w') != NULL
||
514 strchr(mode
, 'a') != NULL
||
515 strchr(mode
, '+') != NULL
)) {
516 /* writing new files disallowed */
517 if (run_state
> RUN_BEGIN
) {
519 "open of %s mode %s - %s\n", name
, mode
,
520 "open for write disallowed by -m\n");
528 if (name
[0] == HOMECHAR
) {
529 fullname
= homeexpand(name
);
530 if (fullname
== NULL
)
532 fp
= fopen(fullname
, mode
);
535 fp
= fopen(name
, mode
);
542 * Setup for reading from a input file.
543 * Returns -1 if file could not be opened.
546 * name file name to be read
551 FILE *fp
; /* open file descriptor */
554 if (depth
>= MAXDEPTH
)
556 fp
= f_open(name
, "r");
559 cip
= inputs
+ depth
++;
560 cip
->i_state
= IS_READ
;
563 cip
->i_ttystr
= NULL
;
566 namelen
= strlen(name
);
567 cip
->i_name
= (char *)malloc(namelen
+1);
568 if (cip
->i_name
== NULL
) {
571 strncpy(cip
->i_name
, name
, namelen
+1);
577 * Return the current input file stream, or NULL if none.
582 if (depth
<= 0 || depth
> MAXDEPTH
)
589 * Open a string for scanning, num characters to be read.
590 * String is copied into local memory so it can be trashed afterwards.
591 * Returns -1 if cannot open string.
594 * str string to be opened
595 * num lengh of string to open
598 openstring(char *str
, size_t num
)
600 char *cp
; /* copied string */
602 if ((depth
>= MAXDEPTH
) || (str
== NULL
))
604 cp
= (char *) malloc(num
+ 1);
607 strncpy(cp
, str
, num
);
608 cp
[num
] = '\0'; /* firewall */
609 cip
= inputs
+ depth
++;
610 cip
->i_state
= IS_READ
;
617 cip
->i_ttystr
= NULL
;
624 * Set to read input from the terminal.
625 * Returns -1 if there is no more depth for input.
630 if (depth
>= MAXDEPTH
)
632 cip
= inputs
+ depth
++;
633 cip
->i_state
= IS_READ
;
636 cip
->i_ttystr
= NULL
;
645 * Close the current input source.
659 cip
= depth
? &inputs
[depth
- 1] : NULL
;
664 * Reset the input sources back to the initial state.
676 * Set the prompt for terminal input.
687 * Read the next character from the current input source.
688 * End of file closes current input source, and returns EOF character.
693 int ch
; /* current input character */
695 if (depth
== 0) /* input finished */
697 if (cip
->i_state
== IS_REREAD
) { /* rereading current char */
699 cip
->i_state
= IS_READ
;
704 if (cip
->i_str
) { /* from string */
706 ch
= chartoint(*cip
->i_cp
++);
711 } else if (cip
->i_fp
) { /* from file */
712 ch
= fgetc(cip
->i_fp
);
713 } else if (!stdin_tty
) { /* from file */
715 } else { /* from terminal */
719 cip
->i_char
= ch
; /* save for rereads */
727 * Read in the next line of input from the current input source.
728 * The line is terminated with a null character, and does not contain
729 * the final newline character. The returned string is only valid
730 * until the next such call, and so must be copied if necessary.
731 * Returns NULL on end of file.
742 cp
= (char *)malloc(TTYSIZE
+ 1);
744 math_error("Cannot allocate line buffer");
761 if (len
>= linesize
) {
762 cp
= (char *)realloc(cp
, linesize
+ TTYSIZE
+ 1);
764 math_error("Cannot realloc line buffer");
770 cp
[len
++] = (char)ch
;
778 * Read the next character from the terminal.
779 * The routines in the history module are called so that the user
780 * can use a command history and emacs-like editing of the line.
785 int ch
; /* current char */
786 int len
; /* length of current command */
787 STATIC
char charbuf
[1024];
790 * If we have more to read from the saved command line, then do that.
791 * When we see a newline character, then clear the pointer so we will
792 * read a new line on the next call.
795 ch
= chartoint(*cip
->i_ttystr
++);
797 cip
->i_ttystr
= NULL
;
802 * We need another complete line.
806 len
= hist_getline(noprompt
? "" : prompt
, charbuf
, sizeof(charbuf
));
814 * Handle shell escape if present
816 if (charbuf
[0] == '!') { /* do a shell command */
820 if (*cmd
== '\0' || *cmd
== '\n')
823 if (conf
->calc_debug
& CALCDBG_SYSTEM
) {
828 fprintf(stderr
, "execution disallowed by -m flag\n");
832 hist_saveline(charbuf
, len
);
835 * Return the first character of the line, and set up to
836 * return the rest of it with later calls.
838 ch
= chartoint(charbuf
[0]);
840 cip
->i_ttystr
= charbuf
+ 1;
846 * Return whether or not the input source is the terminal.
849 inputisterminal(void)
851 return ((depth
<= 0) || ((cip
->i_str
== NULL
) && (cip
->i_fp
== NULL
)));
856 * Return depth of current input source
866 * Return the name of the current input file.
867 * Returns NULL for terminal or strings.
879 * Return the current line number.
891 * Restore the next character to be read again on the next nextchar call.
896 if ((depth
<= 0) || (cip
->i_state
== IS_REREAD
))
898 cip
->i_state
= IS_REREAD
;
899 if (cip
->i_char
== '\n')
905 * Process all startup files found in the $CALCRC path.
910 char path
[MAX_CALCRC
+1+1]; /* name being searched for */
914 /* execute each file in the list */
915 while (calcrc
!= NULL
&& *calcrc
) {
917 calcrc
= (char *) strchr(calcrc
+ 1, LISTCHAR
);
919 /* load file name into the path */
920 if (calcrc
== NULL
) {
921 strncpy(path
, cp
, MAX_CALCRC
+1);
923 strncpy(path
, cp
, calcrc
- cp
);
924 path
[calcrc
- cp
] = '\0';
927 /* find the start of the path */
928 p
= (path
[0] == ':') ? path
+ 1 : path
;
933 /* process the current file in the list */
934 if (openfile(p
) < 0) {
935 /* Unable to open rcfile */
936 if (c_flag
&& !d_flag
)
938 "Unable to open rcfile \"%s\"\n", p
);
948 * isinoderead - determine if we have read a given dev/inode
950 * This function returns the index of the readset element that matches
951 * a given device/inode, -1 otherwise.
956 * This function does not work under WIN32. The sbuf->st_ino is always
957 * zero because the FAT and NTFS filesystems do not support inodes.
958 * They also don't support links, which is why you need this function
959 * under UNIX. For WIN32, use _fullpath() to determine if you have
960 * already opened a file.
963 * sbuf stat of the inode in question
967 isinoderead(struct stat
*sbuf
)
971 /* deal with the empty case */
972 if (readset
== NULL
|| maxreadset
<= 0) {
973 /* readset is empty */
977 /* scan the entire readset */
978 for (i
=0; i
< maxreadset
; ++i
) {
979 #if defined(_WIN32) || defined(__MSDOS__)
980 char tmp
[_MAX_PATH
+1];
981 tmp
[_MAX_PATH
] = '\0';
982 if (_fullpath(tmp
, cip
->i_name
, _MAX_PATH
) &&
984 strcasecmp(readset
[i
].path
, tmp
) == 0) {
988 #else /* Windoz free systems */
989 if (readset
[i
].active
&&
990 sbuf
->st_dev
== readset
[i
].inode
.st_dev
&&
991 sbuf
->st_ino
== readset
[i
].inode
.st_ino
) {
995 #endif /* Windoz free systems */
1004 * findfreeread - find the next free readset element
1006 * This function will return the index of the next free readset element.
1007 * If needed, this function will allocate new readset elements.
1009 * This function returns the index of the next free element, or -1.
1016 /* deal with an empty readset case */
1017 if (readset
== NULL
|| maxreadset
<= 0) {
1019 /* malloc a new readset */
1020 readset
= (READSET
*)malloc((READSET_ALLOC
+1)*sizeof(READSET
));
1021 if (readset
== NULL
) {
1024 maxreadset
= READSET_ALLOC
;
1025 for (i
=0; i
< READSET_ALLOC
; ++i
) {
1026 readset
[i
].active
= 0;
1029 /* return first entry */
1033 /* try to find a free readset entry */
1034 for (i
=0; i
< maxreadset
; ++i
) {
1035 if (readset
[i
].active
== 0) {
1036 /* found a free readset entry */
1041 /* all readset entries are in use, allocate more */
1042 readset
= (READSET
*)realloc(readset
,
1043 (maxreadset
+READSET_ALLOC
) * sizeof(READSET
));
1044 if (readset
== NULL
) {
1047 for (i
=0; i
< READSET_ALLOC
; ++i
) {
1048 readset
[i
+maxreadset
].active
= 0;
1050 maxreadset
+= READSET_ALLOC
;
1052 /* return the first newly allocated free entry */
1053 return maxreadset
-READSET_ALLOC
;
1058 * addreadset - add a entry to the readset array if it is not already there
1060 * This function attempts to add a file into the readset. If the readset
1061 * has an entry with a matching dev/inode, then that entry is updated with
1062 * the new name and path. If no such readset entry is found, a new entry
1065 * This function returns the index of the readset entry, or -1 if error.
1068 * name name given to read or include
1069 * path full pathname of file
1070 * sbuf stat of the path
1073 addreadset(char *name
, char *path
, struct stat
*sbuf
)
1075 int ret
; /* index to return */
1076 size_t name_len
; /* length of read set name */
1077 size_t path_len
; /* length of path to read set name */
1079 /* find the inode */
1080 ret
= isinoderead(sbuf
);
1082 /* not in readset, find a free node */
1083 ret
= findfreeread();
1085 /* cannot find/form a free readset entry */
1089 /* found an readset entry, free old readset data */
1090 if (readset
[ret
].name
!= NULL
) {
1091 free(readset
[ret
].name
);
1093 if (readset
[ret
].path
!= NULL
) {
1094 free(readset
[ret
].path
);
1098 /* load our information into the readset entry */
1099 name_len
= strlen(name
);
1100 readset
[ret
].name
= (char *)malloc(name_len
+1);
1101 if (readset
[ret
].name
== NULL
) {
1104 strncpy(readset
[ret
].name
, name
, name_len
+1);
1105 #if defined(_WIN32) || defined(__MSDOS__)
1107 * For WIN32, _fullpath expands the path to a fully qualified
1108 * path name, which under WIN32 FAT and NTFS is unique, just
1109 * like UNIX inodes. _fullpath also allocated the memory for
1110 * this new longer path name.
1113 readset
[ret
].path
= _fullpath(NULL
, path
, _MAX_PATH
);
1114 if (readset
[ret
].path
== NULL
) {
1118 #else /* Windoz free systems */
1119 path_len
= strlen(path
);
1120 readset
[ret
].path
= (char *)malloc(path_len
+1);
1121 if (readset
[ret
].path
== NULL
) {
1124 strncpy(readset
[ret
].path
, path
, path_len
+1);
1125 #endif /* Windoz free systems */
1126 readset
[ret
].inode
= *sbuf
;
1127 readset
[ret
].active
= 1;
1129 /* return index of the newly added entry */