2 * lib_calc - calc link library initialization and shutdown routines
4 * Copyright (C) 1999-2007 Landon Curt Noll
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: lib_calc.c,v 30.3 2007/07/10 17:57:04 chongo Exp $
22 * @(#) $Source: /usr/local/src/bin/calc/RCS/lib_calc.c,v $
24 * Under source code control: 1996/06/17 18:06:19
25 * File existed as early as: 1996
27 * chongo <was here> /\oo/\ http://www.isthe.com/chongo/
28 * Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
53 #include "have_strdup.h"
54 #if !defined(HAVE_STRDUP)
55 # define strdup(x) calc_strdup((CONST char *)(x))
56 #endif /* HAVE_STRDUP */
58 #include "have_unistd.h"
59 #if defined(HAVE_UNISTD_H)
63 #include "have_stdlib.h"
64 #if defined(HAVE_STDLIB_H)
69 #if defined(USE_TERMIOS)
72 typedef struct termios ttystruct
;
74 #elif defined(USE_TERMIO)
77 typedef struct termio ttystruct
;
79 #elif defined(USE_SGTTY)
81 # include <sys/ioctl.h>
82 typedef struct sgttyb ttystruct
;
86 typedef struct {int fd
;} ttystruct
;
91 # if !defined(USE_SGTTY) && !defined (USE_TERMIOS) && !defined(USE_TERMIO)
93 -=*#*=- A Windoz free system without termio, termios or sgtty!!! -=*#*=-
94 -=*#*=- We do not know how to compile for such a host, sorry!!!! -=*#*=-
100 * in case we do not have certain .h files
102 #if !defined(HAVE_STDLIB_H) && !defined(HAVE_UNISTD_H)
103 #if !defined(HAVE_UID_T) && !defined(_UID_T)
104 typedef unsigned short uid_t
;
106 E_FUNC
char *getenv();
107 E_FUNC uid_t
geteuid();
114 int use_old_std
= FALSE
; /* TRUE => use old classic configuration */
115 int abortlevel
; /* current level of aborts */
116 BOOL inputwait
; /* TRUE if in a terminal input wait */
117 char *program
= "calc"; /* our name */
118 char *base_name
= "calc"; /* basename of our name */
119 char cmdbuf
[MAXCMD
+1+1+1]; /* command line expression + "\n\0" + guard */
120 run run_state
= RUN_ZERO
; /* calc startup run state */
126 int allow_read
= TRUE
; /* FALSE => may not open any files for reading */
127 int allow_write
= TRUE
; /* FALSE => may not open any files for writing */
128 int allow_exec
= TRUE
; /* FALSE => may not execute any commands */
134 int p_flag
= FALSE
; /* TRUE => pipe mode */
135 int q_flag
= FALSE
; /* TRUE => don't execute rc files */
136 int u_flag
= FALSE
; /* TRUE => unbuffer stdin and stdout */
137 int d_flag
= FALSE
; /* TRUE => disable heading, resource_debug == 0 */
138 int c_flag
= FALSE
; /* TRUE => continue on error if permitted */
139 int i_flag
= FALSE
; /* TRUE => go interactive if permitted */
140 int s_flag
= FALSE
; /* TRUE => keep args as strings for argv() */
146 char *calcpath
= NULL
; /* $CALCPATH or default */
147 char *calcrc
= NULL
; /* $CALCRC or default */
148 char *calcbindings
= NULL
; /* $CALCBINDINGS or default */
149 char *home
= NULL
; /* $HOME or default */
150 char *pager
= NULL
; /* $PAGER or default */
151 char *shell
= NULL
; /* $SHELL or default */
152 int stdin_tty
= FALSE
; /* TRUE if stdin is a tty */
153 int havecommands
= FALSE
; /* TRUE if have one or more cmd args */
154 long stoponerror
= 0; /* >0 => stop, <0 => continue, ==0 => use -c */
155 BOOL abort_now
= FALSE
; /* TRUE => go interactive now, if permitted */
157 /* non-zero => calc_scanerr_jmpbuf ready */
158 int calc_use_scanerr_jmpbuf
= 0;
159 /* for scan and parse errors */
160 jmp_buf calc_scanerr_jmpbuf
;
162 /* non-zero => use calc_use_matherr_jmpbuf */
163 int calc_use_matherr_jmpbuf
= 0;
164 /* math_error() control jump point when calc_use_matherr_jmpbuf != 0 */
165 jmp_buf calc_matherr_jmpbuf
;
167 /* last calc error message, also parse/scan errors use this buffer */
168 char calc_err_msg
[MAXERROR
+1];
169 /* 0 ==> do not print parse/scan errors */
170 int calc_print_scanerr_msg
= 1;
172 /* last parse/scan warning message */
173 char calc_warn_msg
[MAXERROR
+1];
174 /* 0 ==> do not print parse/scan warnings */
175 int calc_print_scanwarn_msg
= 1;
176 /* number of parse/scan warnings found */
177 unsigned long calc_warn_cnt
= 0;
179 int argc_value
= 0; /* count of argv[] strings for argv() builtin */
180 char **argv_value
= NULL
; /* argv[] strings for argv() builtin */
182 int no_env
= FALSE
; /* TRUE (-e) => ignore env vars on startup */
183 long errmax
= ERRMAX
; /* if >= 0, maximum value for errcount */
185 NUMBER
*epsilon_default
; /* default allowed error for float calcs */
187 char *calc_debug
= NULL
; /* !=NULL => value of config("calc_debug") */
188 char *resource_debug
= NULL
; /* !=NULL => config("resource_debug") value */
189 char *user_debug
= NULL
; /* !=NULL => value of config("user_debug") */
193 * initialization functions
195 E_FUNC
void math_setfp(FILE *fp
);
196 E_FUNC
void file_init(void);
197 E_FUNC
void zio_init(void);
198 E_FUNC
void initialize(void);
199 E_FUNC
void reinitialize(void);
203 * static declarations
205 STATIC
int init_done
= 0; /* 1 => we already initialized */
206 STATIC
int *fd_setup
= NULL
; /* fd's setup for interaction or -1 */
207 STATIC
int fd_setup_len
= 0; /* number of fd's in fd_setup */
208 STATIC ttystruct
*fd_orig
= NULL
; /* fd original state */
209 STATIC ttystruct
*fd_cur
= NULL
; /* fd current state */
210 S_FUNC
void initenv(void); /* setup calc environment */
211 S_FUNC
int find_tty_state(int fd
); /* find slot for saved tty state */
212 STATIC BOOL initialized
= FALSE
; /* TRUE => initialize() has been run */
216 * libcalc_call_me_first - users of libcalc.a must call this function first!
218 * Anything that uses libcalc.a MUST call this function first before doing
219 * any other libcalc.a processing.
222 libcalc_call_me_first(void)
227 * do nothing if we are initialized already
234 * Disable SIGPIPE so that the pipe to the help file pager will
238 (void) signal(SIGPIPE
, SIG_IGN
);
242 * determine the basename
244 if (program
!= NULL
) {
245 p
= strrchr(program
, '/');
246 #if defined(_WIN32) || defined(__MSDOS__)
248 p
= strrchr(program
, '\\');
259 * initialize old and new configuration values
261 newstd
.epsilon
= &_qonesqbase_
; /* magic to fake early str2q() */
262 newstd
.program
= strdup(program
);
263 newstd
.base_name
= strdup(base_name
);
264 newstd
.version
= strdup(version());
265 conf
= config_copy(&newstd
); /* more magic to fake early str2q() */
266 conf
->tab_ok
= FALSE
;
267 newstd
.epsilon
= str2q(EPSILON_DEFAULT
);
268 oldstd
.epsilon
= str2q(EPSILON_DEFAULT
);
271 * make newstd our default config, unless -O
275 conf
= config_copy(&oldstd
);
277 conf
= config_copy(&newstd
);
281 * -d turns off resource_debug and tilde
284 conf
->resource_debug
= 0;
296 * -D flags can change calc_debug, resource_debug of user_debug
299 conf
->calc_debug
= strtol(calc_debug
, NULL
, 0);
301 if (resource_debug
) {
302 conf
->resource_debug
= strtol(resource_debug
, NULL
, 0);
305 conf
->user_debug
= strtol(user_debug
, NULL
, 0);
314 * ready to rock & roll ..
316 if (conf
->calc_debug
& CALCDBG_RUNSTATE
) {
317 printf("libcalc_call_me_first: run_state from %s to %s\n",
318 run_state_name(run_state
),
319 run_state_name(RUN_BEGIN
));
321 run_state
= RUN_BEGIN
;
328 * initialize - perform the required calc initializations
341 * ZVALUE io initialization
346 * process the environment
356 * initialize file I/O
361 * initialize calc internal data structures
369 * initialize calc math
371 math_cleardiversions();
373 math_setmode(MODE_INITIAL
);
374 math_setdigits(DISPLAY_DEFAULT
);
375 conf
->maxprint
= MAXPRINT_DEFAULT
;
379 * initialize custom registers
387 * note that we are done
394 * reinitialize - reinitialize after a longjmp
400 * process commands (from stdin, not the command line)
403 _math_abort_
= FALSE
;
405 math_cleardiversions();
410 (void) openterminal();
415 * cvmalloc_error - for users of the SGI Workshop Debugger
418 * message - error message passed along via libmalloc_cv.a
421 cvmalloc_error(char *message
)
424 if (message
== NULL
) {
425 fprintf(stderr
, "cvmalloc_error message is NULL\n");
429 /* print message and return */
430 fprintf(stderr
, "cvmalloc_error: %s\n", message
);
436 * initenv - obtain $CALCPATH, $CALCRC, $CALCBINDINGS, $HOME, $PAGER
439 * If $CALCPATH, $CALCRC, $CALCBINDINGS, $PAGER or $SHELL do not exist,
440 * use the default values. If $PAGER or $SHELL is an empty string, also
441 * use a default value. If $HOME does not exist, or is empty, use the home
442 * directory information from the password file.
448 struct passwd
*ent
; /* our password entry */
452 /* determine the $CALCPATH value */
453 c
= (no_env
? NULL
: getenv(CALCPATH
));
454 calcpath
= (c
? strdup(c
) : NULL
);
455 if (calcpath
== NULL
)
456 calcpath
= DEFAULTCALCPATH
;
458 /* determine the $CALCRC value */
459 c
= (no_env
? NULL
: getenv(CALCRC
));
460 calcrc
= (c
? strdup(c
) : NULL
);
462 calcrc
= DEFAULTCALCRC
;
463 if (strlen(calcrc
) > MAX_CALCRC
) {
464 math_error("The $CALCRC variable is longer than %d chars",
469 /* determine the $CALCBINDINGS value */
470 c
= (no_env
? NULL
: getenv(CALCBINDINGS
));
471 calcbindings
= (c
? strdup(c
) : NULL
);
472 if (calcbindings
== NULL
)
473 calcbindings
= DEFAULTCALCBINDINGS
;
475 /* determine the $HOME value */
476 c
= (no_env
? NULL
: getenv(HOME
));
477 home
= (c
? strdup(c
) : NULL
);
479 if (home
== NULL
|| home
[0] == '\0') {
480 /* just assume . is home if all else fails */
483 #else /* Windoz free systems */
484 if (home
== NULL
|| home
[0] == '\0') {
486 ent
= (struct passwd
*)getpwuid(geteuid());
488 /* just assume . is home if all else fails */
491 pw_dir_len
= strlen(ent
->pw_dir
);
492 home
= (char *)malloc(pw_dir_len
+1);
493 strncpy(home
, ent
->pw_dir
, pw_dir_len
+1);
495 #endif /* Windoz free systems */
497 /* determine the $PAGER value */
498 c
= (no_env
? NULL
: getenv(PAGER
));
499 pager
= (c
? strdup(c
) : NULL
);
500 if (pager
== NULL
|| *pager
== '\0')
501 pager
= DEFAULTCALCPAGER
;
503 /* determine the $SHELL value */
504 c
= (no_env
? NULL
: getenv(SHELL
));
505 shell
= (c
? strdup(c
) : NULL
);
506 if (shell
== NULL
|| *shell
== '\0')
507 shell
= DEFAULTSHELL
;
512 * libcalc_call_me_last - users of libcalc.a can call this function when done
514 * Anything that uses libcalc.a can call this function after they are
515 * completely finished with libcalc.a processing. The only effect of
516 * this function is to free storage that might otherwise go unused.
518 * NOTE: If, for any reason, you need to do more libcalc.a processing,
519 * then you will need to call libcalc_call_me_first() again.
522 libcalc_call_me_last(void)
529 if (init_done
== 0) {
534 * free the configuration
539 * free Blum generator state
541 random_libcalc_cleanup();
544 * restore all changed descriptor states
546 for (i
=0; i
< fd_setup_len
; ++i
) {
547 if (fd_setup
[i
] >= 0) {
548 if (conf
->calc_debug
& CALCDBG_TTY
)
549 printf("libcalc_call_me_last: fd %d "
550 "not in original state, "
551 "restoring it", fd_setup
[i
]);
552 orig_tty(fd_setup
[i
]);
565 * run_state_name - return a constant string given a run_state
568 run_state_name(run state
)
577 case RUN_PRE_CMD_ARGS
:
578 return "PRE_CMD_ARGS";
581 case RUN_PRE_TOP_LEVEL
:
582 return "PRE_TOP_LEVEL";
587 case RUN_EXIT_WITH_ERROR
:
588 return "EXIT_WITH_ERROR";
590 return "RUN_invalid";
595 * calc_strdup - calc interface to provide or simulate strdup()
598 calc_strdup(CONST
char *s1
)
600 #if defined(HAVE_STRDUP)
604 #else /* HAVE_STRDUP */
606 char *ret
; /* return string */
607 size_t s1_len
; /* length of string to duplicate */
617 * allocate duplicate storage
620 ret
= (char *)malloc(s1_len
+1);
623 * if we have storage, duplicate the string
626 strncpy(ret
, s1
, s1_len
+1);
630 * return the new string, or NULL if malloc failed
634 #endif /* HAVE_STRDUP */
639 * find_tty_state - establish a new tty state
642 * fd file descriptor to establish a new tty state for
645 * indx The index into fd_setup[], fd_orig[] and fd_cur[] to use or -1
648 find_tty_state(int fd
)
650 int *new_fd_setup
; /* new fd_setup array */
651 ttystruct
*new_fd_orig
; /* new fd_orig array */
652 ttystruct
*new_fd_cur
; /* new fd_cur array */
656 * firewall: must be > 0
664 * case: need to initially malloc some state
666 if (fd_setup_len
<= 0 || fd_setup
== NULL
|| fd_orig
== NULL
) {
668 /* setup for a single descriptor */
669 fd_setup
= (int *)malloc(sizeof(fd_setup
[0]));
670 if (fd_setup
== NULL
) {
674 fd_orig
= (ttystruct
*)malloc(sizeof(fd_orig
[0]));
675 if (fd_orig
== NULL
) {
678 fd_cur
= (ttystruct
*)malloc(sizeof(fd_orig
[0]));
679 if (fd_cur
== NULL
) {
686 * look for an existing tty state for the descriptor
688 for (i
=0; i
< fd_setup_len
; ++i
) {
690 /* case: found existing tty state, return index */
691 if (fd_setup
[i
] == fd
) {
697 * no tty state exists for the descriptor, look for empty slot
699 for (i
=0; i
< fd_setup_len
; ++i
) {
701 /* case: found an empty slot, so return it */
702 if (fd_setup
[i
] < 0) {
708 * no empty slots exist, realloc another slot
710 new_fd_setup
= (int *)realloc(fd_setup
, sizeof(fd_setup
[0]) *
712 if (new_fd_setup
== NULL
) {
715 new_fd_setup
[fd_setup_len
] = -1;
716 new_fd_orig
= (ttystruct
*)realloc(fd_setup
, sizeof(fd_orig
[0]) *
718 if (new_fd_orig
== NULL
) {
721 new_fd_cur
= (ttystruct
*)realloc(fd_cur
, sizeof(fd_cur
[0]) *
723 if (new_fd_cur
== NULL
) {
726 fd_setup
= new_fd_setup
;
727 fd_orig
= new_fd_orig
;
730 /* return the new slot */
731 return fd_setup_len
-1;
736 * calc_tty - setup a file descriptor for calc's interactive use
738 * Calc wants, in effect, cbreak on and echo off.
741 * fd the descriptor for calc's interactive use
744 * TRUE state change was successful
745 * FALSE unable to change state of descriptor for interactive use
750 int slot
; /* the saved descriptor slot or -1 */
753 * grab the saved slot for this descriptor
755 slot
= find_tty_state(fd
);
757 if (conf
->calc_debug
& CALCDBG_TTY
)
758 printf("calc_tty: Cannot get saved descriptor slot\n");
762 #if defined(USE_SGTTY)
765 * USE_SGTTY tty state method
767 /* save original state if needed */
768 if (fd_setup
[slot
] < 0 && ioctl(fd
, TIOCGETP
, fd_orig
+slot
) < 0) {
769 if (conf
->calc_debug
& CALCDBG_TTY
)
770 printf("calc_tty: Cannot TIOCGETP fd %d\n", fd
);
773 /* setup for new state */
774 fd_cur
[slot
] = fd_orig
[slot
];
775 fd_cur
[slot
].sg_flags
&= ~ECHO
;
776 fd_cur
[slot
].sg_flags
|= CBREAK
;
777 /* enable new state */
778 if (ioctl(fd
, TIOCSETP
, fd_cur
+slot
) < 0) {
779 if (conf
->calc_debug
& CALCDBG_TTY
)
780 printf("calc_tty: Cannot TIOCSETP fd %d\n", fd
);
783 if (conf
->calc_debug
& CALCDBG_TTY
)
784 printf("calc_tty: stty -ECHO +CBREAK: fd %d\n", fd
);
786 #elif defined(USE_TERMIO)
789 * USE_TERMIO tty state method
791 if (fd_setup
[slot
] < 0 && ioctl(fd
, TCGETA
, fd_orig
+slot
) < 0) {
792 if (conf
->calc_debug
& CALCDBG_TTY
)
793 printf("calc_tty: Cannot TCGETA fd %d\n", fd
);
796 /* setup for new state */
797 fd_cur
[slot
] = fd_orig
[slot
];
798 fd_cur
[slot
].c_lflag
&= ~(ECHO
| ECHOE
| ECHOK
);
799 fd_cur
[slot
].c_iflag
|= ISTRIP
;
800 fd_cur
[slot
].c_lflag
&= ~ICANON
;
801 fd_cur
[slot
].c_cc
[VMIN
] = 1;
802 fd_cur
[slot
].c_cc
[VTIME
] = 0;
803 /* enable new state */
804 if (ioctl(fd
, TCSETAW
, fd_cur
+slot
) < 0) {
805 if (conf
->calc_debug
& CALCDBG_TTY
)
806 printf("calc_tty: Cannot TCSETAW fd %d\n", fd
);
809 if (conf
->calc_debug
& CALCDBG_TTY
)
810 printf("calc_tty: stty -ECHO -ECHOE -ECHOK -ICANON +ISTRIP "
811 "VMIN=1 VTIME=0: fd %d\n", fd
);
813 #elif defined (USE_TERMIOS)
816 * USE_TERMIOS tty state method
818 if (fd_setup
[slot
] < 0 && tcgetattr(fd
, fd_orig
+slot
) < 0) {
819 if (conf
->calc_debug
& CALCDBG_TTY
)
820 printf("calc_tty: Cannot tcgetattr fd %d\n", fd
);
823 /* setup for new state */
824 fd_cur
[slot
] = fd_orig
[slot
];
825 fd_cur
[slot
].c_lflag
&= ~(ECHO
| ECHOE
| ECHOK
);
826 fd_cur
[slot
].c_iflag
|= ISTRIP
;
827 fd_cur
[slot
].c_lflag
&= ~ICANON
;
828 fd_cur
[slot
].c_cc
[VMIN
] = 1;
829 fd_cur
[slot
].c_cc
[VTIME
] = 0;
830 /* enable new state */
831 if (tcsetattr(fd
, TCSANOW
, fd_cur
+slot
) < 0) {
832 if (conf
->calc_debug
& CALCDBG_TTY
)
833 printf("calc_tty: Cannot tcsetattr fd %d\n", fd
);
836 if (conf
->calc_debug
& CALCDBG_TTY
)
837 printf("calc_tty: stty -ECHO -ECHOE -ECHOK -ICANON +ISTRIP "
838 "VMIN=1 VTIME=0: fd %d\n", fd
);
840 #else /* Using none of the above */
841 fd_cur
[slot
] = fd_orig
[slot
];
846 * note that the tty slot is on use
854 * orig_tty - restore the original state of a file descriptor
856 * This routine will restore the state of a descriptor to its calc
857 * startup value if it was set for interactive use by calc_tty().
860 * fd the descriptor to restore
863 * TRUE restore was successful
864 * FALSE unable to restore descriptor to original state
869 int slot
; /* the saved descriptor slot or -1 */
872 * find the saved slot for this descriptor
874 slot
= find_tty_state(fd
);
876 if (conf
->calc_debug
& CALCDBG_TTY
)
877 printf("orig_tty: Cannot get saved descriptor slot\n");
882 * do nothing if no state was saved for this descriptor
884 if (fd_setup
[slot
] < 0) {
885 if (conf
->calc_debug
& CALCDBG_TTY
)
886 printf("orig_tty: no state saved for fd %d\n", fd
);
890 #if defined(USE_SGTTY)
893 * USE_SGTTY tty state method
895 (void) ioctl(fd
, TIOCSETP
, fd_orig
+slot
);
896 if (conf
->calc_debug
& CALCDBG_TTY
)
897 printf("orig_tty: TIOCSETP restored fd %d\n", fd
);
899 #elif defined(USE_TERMIO)
902 * USE_TERMIO tty state method
904 (void) ioctl(fd
, TCSETAW
, fd_orig
+slot
);
905 if (conf
->calc_debug
& CALCDBG_TTY
)
906 printf("orig_tty: TCSETAW restored fd %d\n", fd
);
908 #elif defined (USE_TERMIOS)
911 * assume USE_SGTTY tty state method
913 (void) tcsetattr(fd
, TCSANOW
, fd_orig
+slot
);
914 if (conf
->calc_debug
& CALCDBG_TTY
)
915 printf("orig_tty: TCSANOW restored fd %d\n", fd
);
917 #else /* nothing assigned */
919 if (conf
->calc_debug
& CALCDBG_TTY
)
920 printf ("orig_tty: nothing restored to fd %d\n", fd
);
925 * note new current state
927 fd_cur
[slot
] = fd_orig
[slot
];
930 * Since current state is the original state, we can free up
931 * this slot. This also prevents functions such as the
932 * libcalc_call_me_last() function from re-restoring it.