mirror of
https://github.com/lcn2/calc.git
synced 2025-08-16 01:03:29 +03:00
Converted all ASCII tabs to ASCII spaces using a 8 character tab stop, for all files, except for all Makefiles (plus rpm.mk). The `git diff -w` reports no changes.
1041 lines
29 KiB
C
1041 lines
29 KiB
C
/*
|
|
* lib_calc - calc link library initialization and shutdown routines
|
|
*
|
|
* Copyright (C) 1999-2007,2018,2021-2023 Landon Curt Noll
|
|
*
|
|
* Calc is open software; you can redistribute it and/or modify it under
|
|
* the terms of the version 2.1 of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* Calc is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
|
|
* Public License for more details.
|
|
*
|
|
* A copy of version 2.1 of the GNU Lesser General Public License is
|
|
* distributed with calc under the filename COPYING-LGPL. You should have
|
|
* received a copy with calc; if not, write to Free Software Foundation, Inc.
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Under source code control: 1996/06/17 18:06:19
|
|
* File existed as early as: 1996
|
|
*
|
|
* chongo <was here> /\oo/\ http://www.isthe.com/chongo/
|
|
* Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <setjmp.h>
|
|
#include <signal.h>
|
|
|
|
#if !defined(_WIN32) && !defined(_WIN64)
|
|
# include <pwd.h>
|
|
#endif
|
|
|
|
#include "lib_calc.h"
|
|
#include "calc.h"
|
|
#include "zmath.h"
|
|
#include "zrandom.h"
|
|
#include "conf.h"
|
|
#include "token.h"
|
|
#include "symbol.h"
|
|
#include "func.h"
|
|
#include "strl.h"
|
|
|
|
#if defined(CUSTOM)
|
|
#include "custom.h"
|
|
#endif /* CUSTOM */
|
|
|
|
#include "have_strdup.h"
|
|
#if !defined(HAVE_STRDUP)
|
|
# define strdup(x) calc_strdup((CONST char *)(x))
|
|
#endif /* HAVE_STRDUP */
|
|
|
|
#include "have_unistd.h"
|
|
#if defined(HAVE_UNISTD_H)
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "have_stdlib.h"
|
|
#if defined(HAVE_STDLIB_H)
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
#include "terminal.h"
|
|
#if defined(USE_TERMIOS)
|
|
|
|
# include <termios.h>
|
|
typedef struct termios ttystruct;
|
|
|
|
#elif defined(USE_TERMIO)
|
|
|
|
# include <termio.h>
|
|
typedef struct termio ttystruct;
|
|
|
|
#elif defined(USE_SGTTY)
|
|
|
|
# include <sys/ioctl.h>
|
|
typedef struct sgttyb ttystruct;
|
|
|
|
#else
|
|
|
|
typedef struct {int fd;} ttystruct;
|
|
|
|
#endif
|
|
|
|
#if !defined(_WIN32) && !defined(_WIN64)
|
|
# if !defined(USE_SGTTY) && !defined (USE_TERMIOS) && !defined(USE_TERMIO)
|
|
|
|
-=*#*=- A Windows free system without termio, termios or sgtty!!! -=*#*=-
|
|
-=*#*=- We do not know how to compile for such a host, sorry!!!! -=*#*=-
|
|
|
|
# endif
|
|
#endif /* Windows */
|
|
|
|
|
|
#include "errtbl.h"
|
|
#include "banned.h" /* include after system header <> includes */
|
|
|
|
|
|
/*
|
|
* in case we do not have certain .h files
|
|
*/
|
|
#if !defined(HAVE_STDLIB_H) && !defined(HAVE_UNISTD_H)
|
|
#if !defined(HAVE_UID_T) && !defined(_UID_T)
|
|
typedef unsigned short uid_t;
|
|
#endif
|
|
E_FUNC char *getenv();
|
|
E_FUNC uid_t geteuid();
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Common definitions
|
|
*/
|
|
int use_old_std = false; /* true => use old classic configuration */
|
|
int abortlevel; /* current level of aborts */
|
|
bool inputwait; /* true if in a terminal input wait */
|
|
char *program = "calc"; /* our name */
|
|
char *base_name = "calc"; /* basename of our name */
|
|
char cmdbuf[MAXCMD+1+1+1]; /* command line expression + "\n\0" + guard */
|
|
run run_state = RUN_ZERO; /* calc startup run state */
|
|
char *script_name = NULL; /* program name or -f filename arg or NULL */
|
|
|
|
|
|
/*
|
|
* global permissions
|
|
*/
|
|
int allow_read = true; /* false => may not open any files for reading */
|
|
int allow_write = true; /* false => may not open any files for writing */
|
|
int allow_exec = true; /* false => may not execute any commands */
|
|
|
|
|
|
/*
|
|
* global flags
|
|
*/
|
|
int p_flag = false; /* true => pipe mode */
|
|
int q_flag = false; /* true => don't execute rc files */
|
|
int u_flag = false; /* true => unbuffer stdin and stdout */
|
|
int d_flag = false; /* true => disable heading, resource_debug == 0 */
|
|
int c_flag = false; /* true => continue on error if permitted */
|
|
int i_flag = false; /* true => go interactive if permitted */
|
|
int s_flag = false; /* true => keep args as strings for argv() */
|
|
|
|
|
|
/*
|
|
* global values
|
|
*/
|
|
char *calcpath = NULL; /* $CALCPATH or default */
|
|
char *calcrc = NULL; /* $CALCRC or default */
|
|
char *calcbindings = NULL; /* $CALCBINDINGS or default */
|
|
char *home = NULL; /* $HOME or default */
|
|
char *pager = NULL; /* $PAGER or default */
|
|
char *shell = NULL; /* $SHELL or default */
|
|
char *calc_history = NULL; /* $CALCHISTFILE or ~/.calc_history */
|
|
char *calc_helpdir = NULL; /* $CALCHELP or /usr/local/share/calc/help */
|
|
/* $CALCCUSTOMHELP or /usr/local/share/calc/custhelp */
|
|
char *calc_customhelpdir = NULL;
|
|
int stdin_tty = false; /* true if stdin is a tty */
|
|
int havecommands = false; /* true if have one or more cmd args */
|
|
long stoponerror = 0; /* >0 => stop, <0 => continue, ==0 => use -c */
|
|
bool abort_now = false; /* true => go interactive now, if permitted */
|
|
|
|
/* non-zero => calc_scanerr_jmpbuf ready */
|
|
int calc_use_scanerr_jmpbuf = 0;
|
|
/* for scan and parse errors */
|
|
jmp_buf calc_scanerr_jmpbuf;
|
|
|
|
/* non-zero => use calc_use_matherr_jmpbuf */
|
|
int calc_use_matherr_jmpbuf = 0;
|
|
/* math_error() control jump point when calc_use_matherr_jmpbuf != 0 */
|
|
jmp_buf calc_matherr_jmpbuf;
|
|
|
|
/* last calc error message, also parse/scan errors use this buffer */
|
|
char calc_err_msg[MAXERROR+1];
|
|
/* 0 ==> do not print parse/scan errors */
|
|
int calc_print_scanerr_msg = 1;
|
|
|
|
/* last parse/scan warning message */
|
|
char calc_warn_msg[MAXERROR+1];
|
|
/* 0 ==> do not print parse/scan warnings */
|
|
int calc_print_scanwarn_msg = 1;
|
|
/* number of parse/scan warnings found */
|
|
unsigned long calc_warn_cnt = 0;
|
|
|
|
int argc_value = 0; /* count of argv[] strings for argv() builtin */
|
|
char **argv_value = NULL; /* argv[] strings for argv() builtin */
|
|
|
|
int no_env = false; /* true (-e) => ignore env vars on startup */
|
|
long errmax = ERRMAX; /* if >= 0, maximum value for errcount */
|
|
|
|
NUMBER *epsilon_default; /* default allowed error for float calcs */
|
|
|
|
char *calc_debug = NULL; /* !=NULL => value of config("calc_debug") */
|
|
char *resource_debug = NULL; /* !=NULL => config("resource_debug") value */
|
|
char *user_debug = NULL; /* !=NULL => value of config("user_debug") */
|
|
|
|
|
|
/*
|
|
* initialization functions
|
|
*/
|
|
E_FUNC void math_setfp(FILE *fp);
|
|
E_FUNC void file_init(void);
|
|
E_FUNC void zio_init(void);
|
|
E_FUNC void initialize(void);
|
|
E_FUNC void reinitialize(void);
|
|
|
|
|
|
/*
|
|
* static declarations
|
|
*/
|
|
STATIC int init_done = 0; /* 1 => we already initialized */
|
|
STATIC int *fd_setup = NULL; /* fd's setup for interaction or -1 */
|
|
STATIC int fd_setup_len = 0; /* number of fd's in fd_setup */
|
|
STATIC ttystruct *fd_orig = NULL; /* fd original state */
|
|
STATIC ttystruct *fd_cur = NULL; /* fd current state */
|
|
S_FUNC void initenv(void); /* setup calc environment */
|
|
S_FUNC int find_tty_state(int fd); /* find slot for saved tty state */
|
|
STATIC bool initialized = false; /* true => initialize() has been run */
|
|
|
|
|
|
/*
|
|
* libcalc_call_me_first - users of libcalc.a must call this function first!
|
|
*
|
|
* Anything that uses libcalc.a MUST call this function first before doing
|
|
* any other libcalc.a processing.
|
|
*/
|
|
void
|
|
libcalc_call_me_first(void)
|
|
{
|
|
char *p;
|
|
|
|
/*
|
|
* do nothing if we are initialized already
|
|
*/
|
|
if (init_done) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Disable SIGPIPE so that the pipe to the help file pager will
|
|
* not stop calc.
|
|
*/
|
|
#if !defined(_WIN32) && !defined(_WIN64)
|
|
(void) signal(SIGPIPE, SIG_IGN);
|
|
#endif
|
|
|
|
/*
|
|
* determine the basename
|
|
*/
|
|
if (program != NULL) {
|
|
p = strrchr(program, '/');
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
if (p == NULL) {
|
|
p = strrchr(program, '\\');
|
|
}
|
|
#endif
|
|
if (p == NULL) {
|
|
base_name = program;
|
|
} else {
|
|
base_name = p+1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* initialize old and new configuration values
|
|
*/
|
|
newstd.epsilon = &_qonesqbase_; /* magic to fake early str2q() */
|
|
if (program == NULL) {
|
|
fprintf(stderr, "libcalc_call_me_first: FATAL: "
|
|
"program is NULL\n");
|
|
exit(80);
|
|
}
|
|
newstd.program = strdup(program);
|
|
if (newstd.program == NULL) {
|
|
fprintf(stderr, "libcalc_call_me_first: FATAL: "
|
|
"cannot strdup program string\n");
|
|
exit(81);
|
|
}
|
|
if (base_name == NULL) {
|
|
fprintf(stderr, "libcalc_call_me_first: FATAL: "
|
|
"base_name is NULL\n");
|
|
exit(82);
|
|
}
|
|
newstd.base_name = strdup(base_name);
|
|
if (newstd.base_name == NULL) {
|
|
fprintf(stderr, "libcalc_call_me_first: FATAL: "
|
|
"cannot strdup base_name string\n");
|
|
exit(83);
|
|
}
|
|
p = version();
|
|
if (p == NULL) {
|
|
fprintf(stderr, "libcalc_call_me_first: FATAL: "
|
|
"version() returned NULL\n");
|
|
exit(84);
|
|
}
|
|
newstd.version = strdup(p);
|
|
if (newstd.version == NULL) {
|
|
fprintf(stderr, "libcalc_call_me_first: FATAL: "
|
|
"cannot strdup return from version()\n");
|
|
exit(85);
|
|
}
|
|
conf = config_copy(&newstd); /* more magic to fake early str2q() */
|
|
conf->tab_ok = false;
|
|
newstd.epsilon = str2q(EPSILON_DEFAULT);
|
|
oldstd.epsilon = str2q(EPSILON_DEFAULT);
|
|
|
|
/*
|
|
* make newstd our default config, unless -O
|
|
*/
|
|
config_free(conf);
|
|
if (use_old_std) {
|
|
conf = config_copy(&oldstd);
|
|
} else {
|
|
conf = config_copy(&newstd);
|
|
}
|
|
|
|
/*
|
|
* -d turns off resource_debug and tilde
|
|
*/
|
|
if (d_flag) {
|
|
conf->resource_debug = 0;
|
|
conf->tilde_ok = 0;
|
|
conf->tilde_space = 0;
|
|
}
|
|
|
|
/*
|
|
* -p turns off tab
|
|
*/
|
|
if (p_flag) {
|
|
conf->tab_ok = 0;
|
|
}
|
|
|
|
/*
|
|
* -D flags can change calc_debug, resource_debug of user_debug
|
|
*/
|
|
if (calc_debug) {
|
|
conf->calc_debug = strtol(calc_debug, NULL, 0);
|
|
}
|
|
if (resource_debug) {
|
|
conf->resource_debug = strtol(resource_debug, NULL, 0);
|
|
}
|
|
if (user_debug) {
|
|
conf->user_debug = strtol(user_debug, NULL, 0);
|
|
}
|
|
|
|
/*
|
|
* initialize
|
|
*/
|
|
initialize();
|
|
|
|
/*
|
|
* verify error_table[] array and setup private_error_alias[] array
|
|
*/
|
|
verify_error_table();
|
|
|
|
/*
|
|
* ready to rock & roll ..
|
|
*/
|
|
if (conf->calc_debug & CALCDBG_RUNSTATE) {
|
|
printf("libcalc_call_me_first: run_state from %s to %s\n",
|
|
run_state_name(run_state),
|
|
run_state_name(RUN_BEGIN));
|
|
}
|
|
run_state = RUN_BEGIN;
|
|
init_done = 1;
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* initialize - perform the required calc initializations
|
|
*/
|
|
void
|
|
initialize(void)
|
|
{
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (initialized) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* ZVALUE io initialization
|
|
*/
|
|
zio_init();
|
|
|
|
/*
|
|
* process the environment
|
|
*/
|
|
initenv();
|
|
|
|
/*
|
|
* initialize I/O
|
|
*/
|
|
file_init();
|
|
|
|
/*
|
|
* initialize file I/O
|
|
*/
|
|
resetinput();
|
|
|
|
/*
|
|
* initialize calc internal data structures
|
|
*/
|
|
inittokens();
|
|
initglobals();
|
|
initfunctions();
|
|
initstack();
|
|
|
|
/*
|
|
* initialize calc math
|
|
*/
|
|
math_cleardiversions();
|
|
math_setfp(stdout);
|
|
math_setmode(MODE_INITIAL);
|
|
math_setdigits(DISPLAY_DEFAULT);
|
|
conf->maxprint = MAXPRINT_DEFAULT;
|
|
|
|
#if defined(CUSTOM)
|
|
/*
|
|
* initialize custom registers
|
|
*/
|
|
if (allow_custom) {
|
|
init_custreg();
|
|
}
|
|
#endif /* CUSTOM */
|
|
|
|
/*
|
|
* note that we are done
|
|
*/
|
|
initialized = true;
|
|
}
|
|
|
|
|
|
/*
|
|
* reinitialize - reinitialize after a longjmp
|
|
*/
|
|
void
|
|
reinitialize(void)
|
|
{
|
|
/*
|
|
* process commands (from stdin, not the command line)
|
|
*/
|
|
abortlevel = 0;
|
|
_math_abort_ = false;
|
|
inputwait = false;
|
|
math_cleardiversions();
|
|
math_setfp(stdout);
|
|
resetscopes();
|
|
resetinput();
|
|
inittokens();
|
|
(void) openterminal();
|
|
}
|
|
|
|
|
|
/*
|
|
* cvmalloc_error - for users of the SGI Workshop Debugger
|
|
*
|
|
* usage:
|
|
* message - error message passed along via libmalloc_cv.a
|
|
*/
|
|
void
|
|
cvmalloc_error(char *message)
|
|
{
|
|
/* firewall */
|
|
if (message == NULL) {
|
|
fprintf(stderr, "cvmalloc_error message is NULL\n");
|
|
return;
|
|
}
|
|
|
|
/* print message and return */
|
|
fprintf(stderr, "cvmalloc_error: %s\n", message);
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* initenv - obtain $CALCPATH, $CALCRC, $CALCBINDINGS, $HOME, $PAGER
|
|
* and $SHELL values
|
|
*
|
|
* If $CALCPATH, $CALCRC, $CALCBINDINGS, $PAGER or $SHELL do not exist,
|
|
* use the default values. If $PAGER or $SHELL is an empty string, also
|
|
* use a default value. If $HOME does not exist, or is empty, use the home
|
|
* directory information from the password file.
|
|
*/
|
|
S_FUNC void
|
|
initenv(void)
|
|
{
|
|
#if !defined(_WIN32) && !defined(_WIN64)
|
|
struct passwd *ent; /* our password entry */
|
|
#endif
|
|
char *c;
|
|
|
|
/* determine the $CALCPATH value */
|
|
c = (no_env ? NULL : getenv(CALCPATH));
|
|
calcpath = (c ? strdup(c) : NULL);
|
|
if (calcpath == NULL)
|
|
calcpath = DEFAULTCALCPATH;
|
|
|
|
/* determine the $CALCRC value */
|
|
c = (no_env ? NULL : getenv(CALCRC));
|
|
calcrc = (c ? strdup(c) : NULL);
|
|
if (calcrc == NULL)
|
|
calcrc = DEFAULTCALCRC;
|
|
if (strlen(calcrc) > MAX_CALCRC) {
|
|
math_error("The $CALCRC variable is longer than %d chars",
|
|
MAX_CALCRC);
|
|
not_reached();
|
|
}
|
|
|
|
/* determine the $CALCBINDINGS value */
|
|
c = (no_env ? NULL : getenv(CALCBINDINGS));
|
|
calcbindings = (c ? strdup(c) : NULL);
|
|
if (calcbindings == NULL)
|
|
calcbindings = DEFAULTCALCBINDINGS;
|
|
|
|
/* determine the $HOME value */
|
|
c = (no_env ? NULL : getenv(HOME));
|
|
home = (c ? strdup(c) : NULL);
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
if (home == NULL || home[0] == '\0') {
|
|
/* free home if it was previously allocated, but empty */
|
|
if (home != NULL) {
|
|
free(home);
|
|
}
|
|
/* just assume . is home if all else fails */
|
|
home = strdup(".");
|
|
}
|
|
#else /* Windows free systems */
|
|
if (home == NULL || home[0] == '\0') {
|
|
/* free home if it was previously allocated, but empty */
|
|
if (home != NULL) {
|
|
free(home);
|
|
}
|
|
/* try using the home directory of current effective UID */
|
|
ent = (struct passwd *)getpwuid(geteuid());
|
|
if (ent == NULL || ent->pw_dir == NULL ||
|
|
ent->pw_dir[0] == '\0') {
|
|
/* just assume . is home if all else fails */
|
|
home = strdup(".");
|
|
} else {
|
|
/* use home directory of current effective UID */
|
|
home = strdup(ent->pw_dir);
|
|
}
|
|
}
|
|
#endif /* Windows free systems */
|
|
/* paranoia */
|
|
if (home == NULL) {
|
|
math_error("Unable to allocate string for $HOME");
|
|
not_reached();
|
|
}
|
|
|
|
/* determine the $PAGER value */
|
|
c = (no_env ? NULL : getenv(PAGER));
|
|
pager = (c ? strdup(c) : NULL);
|
|
if (pager == NULL || *pager == '\0')
|
|
pager = DEFAULTCALCPAGER;
|
|
|
|
/* determine the $SHELL value */
|
|
c = (no_env ? NULL : getenv(SHELL));
|
|
shell = (c ? strdup(c) : NULL);
|
|
if (shell == NULL || *shell == '\0')
|
|
shell = DEFAULTSHELL;
|
|
|
|
/* determine the $CALCHISTFILE value */
|
|
c = (no_env ? NULL : getenv(CALCHISTFILE));
|
|
calc_history = (c ? strdup(c) : NULL);
|
|
if (calc_history == NULL || *calc_history == '\0')
|
|
calc_history = NULL; /* will use ~/.calc_history */
|
|
|
|
/* determine the $CALCHELP value */
|
|
c = (no_env ? NULL : getenv(CALCHELP));
|
|
calc_helpdir = (c ? strdup(c) : NULL);
|
|
if (calc_helpdir == NULL || *calc_helpdir == '\0') {
|
|
/* will use /usr/local/share/calc/help */
|
|
calc_helpdir = HELPDIR;
|
|
}
|
|
|
|
#if defined(CUSTOM)
|
|
/* determine the $CALCCUSTOMHELP value */
|
|
c = (no_env ? NULL : getenv(CALCCUSTOMHELP));
|
|
calc_customhelpdir = (c ? strdup(c) : NULL);
|
|
if (calc_customhelpdir == NULL || *calc_customhelpdir == '\0') {
|
|
/* will use /usr/local/share/calc/custhelp */
|
|
calc_customhelpdir = CUSTOMHELPDIR;
|
|
}
|
|
#endif /* CUSTOM */
|
|
}
|
|
|
|
|
|
/*
|
|
* libcalc_call_me_last - users of libcalc.a can call this function when done
|
|
*
|
|
* Anything that uses libcalc.a can call this function after they are
|
|
* completely finished with libcalc.a processing. The only effect of
|
|
* this function is to free storage that might otherwise go unused.
|
|
*
|
|
* NOTE: If, for any reason, you need to do more libcalc.a processing,
|
|
* then you will need to call libcalc_call_me_first() again.
|
|
*/
|
|
void
|
|
libcalc_call_me_last(void)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (init_done == 0) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* free the configuration
|
|
*/
|
|
config_free(conf);
|
|
|
|
/*
|
|
* free Blum generator state
|
|
*/
|
|
random_libcalc_cleanup();
|
|
|
|
/*
|
|
* free script_name
|
|
*/
|
|
if (script_name != NULL) {
|
|
free(script_name);
|
|
script_name = NULL;
|
|
}
|
|
|
|
/*
|
|
* restore all changed descriptor states
|
|
*/
|
|
for (i=0; i < fd_setup_len; ++i) {
|
|
if (fd_setup[i] >= 0) {
|
|
if (conf->calc_debug & CALCDBG_TTY)
|
|
printf("libcalc_call_me_last: fd %d "
|
|
"not in original state, "
|
|
"restoring it", fd_setup[i]);
|
|
orig_tty(fd_setup[i]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* free all globals
|
|
*/
|
|
freeglobals();
|
|
|
|
/*
|
|
* all done
|
|
*/
|
|
init_done = 0;
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* run_state_name - return a constant string given a run_state
|
|
*/
|
|
char *
|
|
run_state_name(run state)
|
|
{
|
|
switch (state) {
|
|
case RUN_ZERO:
|
|
return "ZERO";
|
|
case RUN_BEGIN:
|
|
return "BEGIN";
|
|
case RUN_RCFILES:
|
|
return "RCFILES";
|
|
case RUN_PRE_CMD_ARGS:
|
|
return "PRE_CMD_ARGS";
|
|
case RUN_CMD_ARGS:
|
|
return "CMD_ARGS";
|
|
case RUN_PRE_TOP_LEVEL:
|
|
return "PRE_TOP_LEVEL";
|
|
case RUN_TOP_LEVEL:
|
|
return "TOP_LEVEL";
|
|
case RUN_EXIT:
|
|
return "EXIT";
|
|
case RUN_EXIT_WITH_ERROR:
|
|
return "EXIT_WITH_ERROR";
|
|
}
|
|
return "RUN_invalid";
|
|
}
|
|
|
|
|
|
/*
|
|
* calc_strdup - calc interface to provide or simulate strdup()
|
|
*/
|
|
char *
|
|
calc_strdup(CONST char *s1)
|
|
{
|
|
#if defined(HAVE_STRDUP)
|
|
|
|
return strdup(s1);
|
|
|
|
#else /* HAVE_STRDUP */
|
|
|
|
char *ret; /* return string */
|
|
size_t s1_len; /* length of string to duplicate */
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if (s1 == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* allocate duplicate storage
|
|
*/
|
|
s1_len = strlen(s1);
|
|
ret = (char *)malloc(s1_len+1);
|
|
|
|
/*
|
|
* if we have storage, duplicate the string
|
|
*/
|
|
if (ret != NULL) {
|
|
strlcpy(ret, s1, s1_len+1);
|
|
}
|
|
|
|
/*
|
|
* return the new string, or NULL if malloc failed
|
|
*/
|
|
return ret;
|
|
|
|
#endif /* HAVE_STRDUP */
|
|
}
|
|
|
|
|
|
/*
|
|
* find_tty_state - establish a new tty state
|
|
*
|
|
* Given:
|
|
* fd file descriptor to establish a new tty state for
|
|
*
|
|
* Returns:
|
|
* indx The index into fd_setup[], fd_orig[] and fd_cur[] to use or -1
|
|
*/
|
|
S_FUNC int
|
|
find_tty_state(int fd)
|
|
{
|
|
int *new_fd_setup; /* new fd_setup array */
|
|
ttystruct *new_fd_orig; /* new fd_orig array */
|
|
ttystruct *new_fd_cur; /* new fd_cur array */
|
|
int i;
|
|
|
|
/*
|
|
* firewall: must be > 0
|
|
*/
|
|
if (fd < 0) {
|
|
/* bad descriptor */
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* case: need to initially malloc some state
|
|
*/
|
|
if (fd_setup_len <= 0 || fd_setup == NULL || fd_orig == NULL) {
|
|
|
|
/* setup for a single descriptor */
|
|
fd_setup = (int *)malloc(sizeof(fd_setup[0]));
|
|
if (fd_setup == NULL) {
|
|
return -1;
|
|
}
|
|
fd_setup[0] = -1;
|
|
fd_orig = (ttystruct *)malloc(sizeof(fd_orig[0]));
|
|
if (fd_orig == NULL) {
|
|
return -1;
|
|
}
|
|
fd_cur = (ttystruct *)malloc(sizeof(fd_cur[0]));
|
|
if (fd_cur == NULL) {
|
|
return -1;
|
|
}
|
|
fd_setup_len = 1;
|
|
}
|
|
|
|
/*
|
|
* look for an existing tty state for the descriptor
|
|
*/
|
|
for (i=0; i < fd_setup_len; ++i) {
|
|
|
|
/* case: found existing tty state, return index */
|
|
if (fd_setup[i] == fd) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* no tty state exists for the descriptor, look for empty slot
|
|
*/
|
|
for (i=0; i < fd_setup_len; ++i) {
|
|
|
|
/* case: found an empty slot, so return it */
|
|
if (fd_setup[i] < 0) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* no empty slots exist, realloc another slot
|
|
*/
|
|
/* expand fd_orig as an original pre-modified copy of fd_setup */
|
|
new_fd_orig = (ttystruct *)realloc(fd_orig, sizeof(fd_orig[0]) *
|
|
(fd_setup_len+1));
|
|
if (new_fd_orig == NULL) {
|
|
return -1;
|
|
}
|
|
fd_orig = new_fd_orig;
|
|
memcpy(fd_orig, fd_setup, sizeof(fd_orig[0]) * (fd_setup_len+1));
|
|
|
|
/* expand fd_setup */
|
|
new_fd_setup = (int *)realloc(fd_setup, sizeof(fd_setup[0]) *
|
|
(fd_setup_len+1));
|
|
if (new_fd_setup == NULL) {
|
|
return -1;
|
|
}
|
|
fd_setup = new_fd_setup;
|
|
new_fd_setup[fd_setup_len] = -1;
|
|
|
|
/* expand fd_cur */
|
|
new_fd_cur = (ttystruct *)realloc(fd_cur, sizeof(fd_cur[0]) *
|
|
(fd_setup_len+1));
|
|
if (new_fd_cur == NULL) {
|
|
return -1;
|
|
}
|
|
fd_cur = new_fd_cur;
|
|
|
|
/* expand fd setup length */
|
|
++fd_setup_len;
|
|
|
|
/* return the new slot */
|
|
return fd_setup_len-1;
|
|
}
|
|
|
|
|
|
/*
|
|
* calc_tty - setup a file descriptor for calc's interactive use
|
|
*
|
|
* Calc wants, in effect, cbreak on and echo off.
|
|
*
|
|
* Given:
|
|
* fd the descriptor for calc's interactive use
|
|
*
|
|
* Returns:
|
|
* true state change was successful
|
|
* false unable to change state of descriptor for interactive use
|
|
*/
|
|
bool
|
|
calc_tty(int fd)
|
|
{
|
|
int slot; /* the saved descriptor slot or -1 */
|
|
|
|
/*
|
|
* grab the saved slot for this descriptor
|
|
*/
|
|
slot = find_tty_state(fd);
|
|
if (slot < 0) {
|
|
if (conf->calc_debug & CALCDBG_TTY)
|
|
printf("calc_tty: Cannot get saved descriptor slot\n");
|
|
return false;
|
|
}
|
|
|
|
#if defined(USE_SGTTY)
|
|
|
|
/*
|
|
* USE_SGTTY tty state method
|
|
*/
|
|
/* save original state if needed */
|
|
if (fd_setup[slot] < 0 && ioctl(fd, TIOCGETP, fd_orig+slot) < 0) {
|
|
if (conf->calc_debug & CALCDBG_TTY)
|
|
printf("calc_tty: Cannot TIOCGETP fd %d\n", fd);
|
|
return false;
|
|
}
|
|
/* setup for new state */
|
|
fd_cur[slot] = fd_orig[slot];
|
|
fd_cur[slot].sg_flags &= ~ECHO;
|
|
fd_cur[slot].sg_flags |= CBREAK;
|
|
/* enable new state */
|
|
if (ioctl(fd, TIOCSETP, fd_cur+slot) < 0) {
|
|
if (conf->calc_debug & CALCDBG_TTY)
|
|
printf("calc_tty: Cannot TIOCSETP fd %d\n", fd);
|
|
return false;
|
|
}
|
|
if (conf->calc_debug & CALCDBG_TTY)
|
|
printf("calc_tty: stty -ECHO +CBREAK: fd %d\n", fd);
|
|
|
|
#elif defined(USE_TERMIO)
|
|
|
|
/*
|
|
* USE_TERMIO tty state method
|
|
*/
|
|
if (fd_setup[slot] < 0 && ioctl(fd, TCGETA, fd_orig+slot) < 0) {
|
|
if (conf->calc_debug & CALCDBG_TTY)
|
|
printf("calc_tty: Cannot TCGETA fd %d\n", fd);
|
|
return false;
|
|
}
|
|
/* setup for new state */
|
|
fd_cur[slot] = fd_orig[slot];
|
|
fd_cur[slot].c_lflag &= ~(ECHO | ECHOE | ECHOK);
|
|
fd_cur[slot].c_iflag |= ISTRIP;
|
|
fd_cur[slot].c_lflag &= ~ICANON;
|
|
fd_cur[slot].c_cc[VMIN] = 1;
|
|
fd_cur[slot].c_cc[VTIME] = 0;
|
|
/* enable new state */
|
|
if (ioctl(fd, TCSETAW, fd_cur+slot) < 0) {
|
|
if (conf->calc_debug & CALCDBG_TTY)
|
|
printf("calc_tty: Cannot TCSETAW fd %d\n", fd);
|
|
return false;
|
|
}
|
|
if (conf->calc_debug & CALCDBG_TTY)
|
|
printf("calc_tty: stty -ECHO -ECHOE -ECHOK -ICANON +ISTRIP "
|
|
"VMIN=1 VTIME=0: fd %d\n", fd);
|
|
|
|
#elif defined (USE_TERMIOS)
|
|
|
|
/*
|
|
* USE_TERMIOS tty state method
|
|
*/
|
|
if (fd_setup[slot] < 0 && tcgetattr(fd, fd_orig+slot) < 0) {
|
|
if (conf->calc_debug & CALCDBG_TTY)
|
|
printf("calc_tty: Cannot tcgetattr fd %d\n", fd);
|
|
return false;
|
|
}
|
|
/* setup for new state */
|
|
fd_cur[slot] = fd_orig[slot];
|
|
fd_cur[slot].c_lflag &= ~(ECHO | ECHOE | ECHOK);
|
|
fd_cur[slot].c_iflag |= ISTRIP;
|
|
fd_cur[slot].c_lflag &= ~ICANON;
|
|
fd_cur[slot].c_cc[VMIN] = 1;
|
|
fd_cur[slot].c_cc[VTIME] = 0;
|
|
/* enable new state */
|
|
if (tcsetattr(fd, TCSANOW, fd_cur+slot) < 0) {
|
|
if (conf->calc_debug & CALCDBG_TTY)
|
|
printf("calc_tty: Cannot tcsetattr fd %d\n", fd);
|
|
return false;
|
|
}
|
|
if (conf->calc_debug & CALCDBG_TTY)
|
|
printf("calc_tty: stty -ECHO -ECHOE -ECHOK -ICANON +ISTRIP "
|
|
"VMIN=1 VTIME=0: fd %d\n", fd);
|
|
|
|
#else /* Using none of the above */
|
|
fd_cur[slot] = fd_orig[slot];
|
|
|
|
#endif
|
|
|
|
/*
|
|
* note that the tty slot is on use
|
|
*/
|
|
fd_setup[slot] = fd;
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* orig_tty - restore the original state of a file descriptor
|
|
*
|
|
* This routine will restore the state of a descriptor to its calc
|
|
* startup value if it was set for interactive use by calc_tty().
|
|
*
|
|
* Given:
|
|
* fd the descriptor to restore
|
|
*
|
|
* Returns:
|
|
* true restore was successful
|
|
* false unable to restore descriptor to original state
|
|
*/
|
|
bool
|
|
orig_tty(int fd)
|
|
{
|
|
int slot; /* the saved descriptor slot or -1 */
|
|
|
|
/*
|
|
* find the saved slot for this descriptor
|
|
*/
|
|
slot = find_tty_state(fd);
|
|
if (slot < 0) {
|
|
if (conf->calc_debug & CALCDBG_TTY)
|
|
printf("orig_tty: Cannot get saved descriptor slot\n");
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* do nothing if no state was saved for this descriptor
|
|
*/
|
|
if (fd_setup[slot] < 0) {
|
|
if (conf->calc_debug & CALCDBG_TTY)
|
|
printf("orig_tty: no state saved for fd %d\n", fd);
|
|
return false;
|
|
}
|
|
|
|
#if defined(USE_SGTTY)
|
|
|
|
/*
|
|
* USE_SGTTY tty state method
|
|
*/
|
|
(void) ioctl(fd, TIOCSETP, fd_orig+slot);
|
|
if (conf->calc_debug & CALCDBG_TTY)
|
|
printf("orig_tty: TIOCSETP restored fd %d\n", fd);
|
|
|
|
#elif defined(USE_TERMIO)
|
|
|
|
/*
|
|
* USE_TERMIO tty state method
|
|
*/
|
|
(void) ioctl(fd, TCSETAW, fd_orig+slot);
|
|
if (conf->calc_debug & CALCDBG_TTY)
|
|
printf("orig_tty: TCSETAW restored fd %d\n", fd);
|
|
|
|
#elif defined (USE_TERMIOS)
|
|
|
|
/*
|
|
* assume USE_SGTTY tty state method
|
|
*/
|
|
(void) tcsetattr(fd, TCSANOW, fd_orig+slot);
|
|
if (conf->calc_debug & CALCDBG_TTY)
|
|
printf("orig_tty: TCSANOW restored fd %d\n", fd);
|
|
|
|
#else /* nothing assigned */
|
|
|
|
if (conf->calc_debug & CALCDBG_TTY)
|
|
printf ("orig_tty: nothing restored to fd %d\n", fd);
|
|
|
|
#endif
|
|
|
|
/*
|
|
* note new current state
|
|
*/
|
|
fd_cur[slot] = fd_orig[slot];
|
|
|
|
/*
|
|
* Since current state is the original state, we can free up
|
|
* this slot. This also prevents functions such as the
|
|
* libcalc_call_me_last() function from re-restoring it.
|
|
*/
|
|
fd_setup[slot] = -1;
|
|
return true;
|
|
}
|