/* * Copyright (c) 1997 David I. Bell * Permission is granted to use, distribute, or modify this source, * provided that this copyright notice remains intact. * * Arbitrary precision calculator. */ #include #include #include #include #include #include #define CALC_C #include "calc.h" #include "hist.h" #include "func.h" #include "opcodes.h" #include "conf.h" #include "token.h" #include "symbol.h" #include "have_uid_t.h" #include "have_const.h" #include "custom.h" #include "math_error.h" #include "args.h" #include "have_unistd.h" #if defined(HAVE_UNISTD_H) #include #endif #include "have_stdlib.h" #if defined(HAVE_STDLIB_H) #include #endif /* * external definitions and functions */ extern int abortlevel; /* current level of aborts */ extern BOOL inputwait; /* TRUE if in a terminal input wait */ extern jmp_buf jmpbuf; /* for errors */ extern int isatty(int tty); /* TRUE if fd is a tty */ extern int p_flag; /* TRUE => pipe mode */ extern int q_flag; /* TRUE => don't execute rc files */ extern int u_flag; /* TRUE => unbuffer stdin and stdout */ extern char *pager; /* $PAGER or default */ extern int stdin_tty; /* TRUE if stdin is a tty */ extern int interactive; /* TRUE if interactive session (no cmd args) */ extern char *program; /* our name */ extern char cmdbuf[]; /* command line expression */ extern char *version(void); /* return version string */ /* * static definitions and functions */ static char *usage = "usage: %s [-C] [-e] [-h] [-i] [-m mode] [-n] [-p]\n" "\t[-q] [-u] [[--] calc_cmd ...]\n"; static void intint(int arg); /* interrupt routine */ /* * Top level calculator routine. */ int main(int argc, char **argv) { int want_defhelp = 0; /* 1=> we only want the default help */ int cmdlen; /* length of the command string */ extern char *optarg; /* option argument */ extern int optind; /* option index */ int c; /* option */ long i; /* * parse args */ program = argv[0]; while ((c = getopt(argc, argv, "Cehim:npquv")) != -1) { switch (c) { case 'C': #if defined(CUSTOM) allow_custom = TRUE; break; #else /* CUSTOM */ /* * we are too early in processing to call * libcalc_call_me_last() - nothing to cleanup */ fprintf(stderr, "%s: calc was built with custom functions " "disabled, -C usage is disallowed\n", program); exit(1); #endif /* CUSTOM */ case 'e': no_env = TRUE; break; case 'h': want_defhelp = 1; break; case 'i': ign_errmax = TRUE; break; case 'm': if (optarg[1] == '\0' || *optarg<'0' || *optarg>'7') { /* * we are too early in processing to * call libcalc_call_me_last() * nothing to cleanup */ fprintf(stderr, "%s: unknown -m arg\n", program); exit(1); } allow_read = (((*optarg-'0') & 04) > 0); allow_write = (((*optarg-'0') & 02) > 0); allow_exec = (((*optarg-'0') & 01) > 0); break; case 'n': new_std = TRUE; break; case 'p': p_flag = TRUE; break; case 'q': q_flag = TRUE; break; case 'u': u_flag = TRUE; break; case 'v': /* * we are too early in processing to call * libcalc_call_me_last() - nothing to cleanup */ printf("%s (version %s)\n", CALC_TITLE, version()); exit(0); default: /* * we are too early in processing to call * libcalc_call_me_last() - nothing to cleanup */ fprintf(stderr, usage, program); exit(1); } } interactive = (optind >= argc); /* * look at the length of any trailing command args * * We make room for the trailing '\0\n' as well as an extra guard byte. */ for (cmdlen=0, i=optind; i < argc; ++i) { /* argument + space separator */ cmdlen += strlen(argv[i]) + 1; } if (i > MAXCMD) { /* * we are too early in processing to call * libcalc_call_me_last() - nothing to cleanup */ fprintf(stderr, "%s: command in arg list is too long\n", program); exit(1); } /* * We will form a command the remaining args separated by spaces. */ cmdbuf[0] = '\0'; if (optind < argc) { strcpy(cmdbuf, argv[optind]); cmdlen = strlen(argv[optind]); for (i=optind+1; i < argc; ++i) { cmdbuf[cmdlen++] = ' '; strcpy(cmdbuf+cmdlen, argv[i]); cmdlen += strlen(argv[i]); } cmdbuf[cmdlen++] = '\n'; cmdbuf[cmdlen] = '\0'; } /* * unbuffered mode */ if (u_flag) { setbuf(stdin, NULL); setbuf(stdout, NULL); } /* * initialize */ libcalc_call_me_first(); stdin_tty = TRUE; /* assume internactive default */ conf->tab_ok = TRUE; /* assume internactive default */ if (want_defhelp) { givehelp(DEFAULTCALCHELP); libcalc_call_me_last(); exit(0); } /* * if allowed or needed, print version and setup bindings */ if (interactive) { /* * check for pipe mode and/or non-tty stdin */ if (!p_flag) { stdin_tty = isatty(0); /* assume stdin is on fd 0 */ } /* * if tty, setup bindings */ if (stdin_tty) { printf("%s (version %s)\n", CALC_TITLE, version()); printf("[%s]\n\n", "Type \"exit\" to exit, or \"help\" for help."); switch (hist_init(calcbindings)) { case HIST_NOFILE: fprintf(stderr, "%s: Cannot open bindings file \"%s\", " "fancy editing disabled.\n", program, calcbindings); break; case HIST_NOTTY: fprintf(stderr, "%s: Cannot set terminal modes, " "fancy editing disabled\n", program); break; } } } /* * establish error longjump point with initial conditions */ if (setjmp(jmpbuf) == 0) { /* * reset/initialize the computing environment */ if (post_init) { initialize(); } else { /* initialize already done, jmpbuf is ready */ post_init = TRUE; } /* * if arg mode or non-tty mode, just do the work and be gone */ if (!interactive || !stdin_tty) { if (q_flag == FALSE && allow_read) { runrcfiles(); q_flag = TRUE; } if (interactive) (void) openterminal(); else (void) openstring(cmdbuf); start_done = TRUE; getcommands(FALSE); libcalc_call_me_last(); exit(0); } } /* if in arg mode, we should not get here */ if (!interactive) { libcalc_call_me_last(); exit(1); } /* * process commands */ if (!start_done) { reinitialize(); } (void) signal(SIGINT, intint); start_done = TRUE; getcommands(TRUE); /* * all done */ libcalc_call_me_last(); /* exit(0); */ return 0; } /* * Interrupt routine. * * given: * arg to keep ANSI C happy */ /*ARGSUSED*/ static void intint(int arg) { (void) signal(SIGINT, intint); if (inputwait || (++abortlevel >= ABORT_NOW)) { math_error("\nABORT"); /*NOTREACHED*/ } if (abortlevel >= ABORT_MATH) _math_abort_ = TRUE; printf("\n[Abort level %d]\n", abortlevel); } /* * Routine called on any runtime error, to complain about it (with possible * arguments), and then longjump back to the top level command scanner. */ void math_error(char *fmt, ...) { va_list ap; char buf[MAXERROR+1]; if (funcname && (*funcname != '*')) fprintf(stderr, "\"%s\": ", funcname); if (funcline && ((funcname && (*funcname != '*')) || !inputisterminal())) fprintf(stderr, "line %ld: ", funcline); va_start(ap, fmt); vsprintf(buf, fmt, ap); va_end(ap); fprintf(stderr, "%s\n", buf); funcname = NULL; if (post_init) { longjmp(jmpbuf, 1); } else { fprintf(stderr, "It is too early provide a command line prompt " "so we must simply exit. Sorry!\n"); /* * don't call libcalc_call_me_last() -- we might loop * and besides ... this is an unusual internal error case */ exit(3); } }