2 * calc - arbitrary precision calculator
4 * Copyright (C) 1999-2013 David I. Bell, Landon Curt Noll and Ernest Bowen
6 * Primary author: David I. Bell
8 * Calc is open software; you can redistribute it and/or modify it under
9 * the terms of the version 2.1 of the GNU Lesser General Public License
10 * as published by the Free Software Foundation.
12 * Calc is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
15 * Public License for more details.
17 * A copy of version 2.1 of the GNU Lesser General Public License is
18 * distributed with calc under the filename COPYING-LGPL. You should have
19 * received a copy with calc; if not, write to Free Software Foundation, Inc.
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 * @(#) $Revision: 30.6 $
23 * @(#) $Id: calc.c,v 30.6 2013/03/25 21:39:57 chongo Exp $
24 * @(#) $Source: /usr/local/src/bin/calc/RCS/calc.c,v $
26 * Under source code control: 1990/02/15 01:48:11
27 * File existed as early as: before 1990
29 * Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
40 #include <sys/types.h>
45 # if !defined(NOTCYGWIN)
47 * getopt.h file is from the Cygwin GNU library
50 * http://sources.redhat.com/cygwin/
52 # include "../getopt/getopt.h"
54 # define strdup _strdup
55 # define isatty _isatty
66 #include "have_uid_t.h"
67 #include "have_const.h"
73 #include "have_unistd.h"
74 #if defined(HAVE_UNISTD_H)
78 #include "have_stdlib.h"
79 #if defined(HAVE_STDLIB_H)
83 #include "have_strdup.h"
84 #if !defined(HAVE_STRDUP)
85 # define strdup(x) calc_strdup((CONST char *)(x))
86 #endif /* HAVE_STRDUP */
88 #include "have_unused.h"
92 * S_FUNC definitions and functions
94 S_FUNC
void intint(int arg
); /* interrupt routine */
95 S_FUNC
void calc_interrupt(char *fmt
, ...);
96 S_FUNC
int nextcp(char **cpp
, int *ip
, int argc
, char **argv
, BOOL haveendstr
);
97 S_FUNC
void set_run_state(run state
);
100 * Top level calculator routine.
103 main(int argc
, char **argv
)
105 int want_defhelp
= 0; /* 1=> we only want the default help */
106 int cmdlen
; /* length of the command string */
127 /* process command line options */
132 havecommands
= FALSE
;
133 while (index
< maxindex
&& !done
) {
156 if (c
== '\0' || c
== ' ')
165 * we are too early in processing to
166 * call libcalc_call_me_last() -
170 "%s: calc was built with custom"
171 " functions disabled, -C usage is"
172 " disallowed\n", program
);
186 while (*cp
== ' ' || *cp
== '\t')
199 if (*cp
< '0' || *cp
> '7') {
201 * we are too early in
203 * libcalc_call_me_last()
207 "%s: unknown -m arg\n",
211 allow_read
= (((*cp
-'0') & 04) > 0);
212 allow_write
= (((*cp
-'0') & 02) > 0);
213 allow_exec
= (((*cp
-'0') & 01) > 0);
215 if (*cp
!= ' ' && *cp
!= '\0') {
216 fprintf(stderr
, "??? m-arg");
223 * -n is deprecated and may be reused
224 * for another purpose in the future
247 * we are too early in processing to
248 * call libcalc_call_me_last() -
251 fputs(CALC_TITLE
, stdout
);
253 fputs(" w/custom functions", stdout
);
255 fputs(" w/o custom functions", stdout
);
257 printf(" (version %s)\n", version());
266 * calc_debug:resource_debug
267 * calc_debug:resource_debug:user_debug
269 if (nextcp(&cp
, &index
, argc
, argv
,
272 "-D expects argument\n");
277 if (*cp
< '0' || *cp
> '9') {
284 (void) strtol(cp
, &endcp
, 10);
287 *cp
!= ' ' && *cp
!= ':') {
294 if (nextcp(&cp
, &index
,
301 if (nextcp(&cp
, &index
, argc
, argv
,
309 if (*cp
< '0' || *cp
> '9') {
316 (void) strtol(cp
, &endcp
, 10);
319 *cp
!= ' ' && *cp
!= ':') {
326 if (nextcp(&cp
, &index
,
334 if (nextcp(&cp
, &index
, argc
, argv
,
336 fprintf(stderr
, "-D : : expects"
340 if (*cp
< '0' || *cp
> '9') {
341 fprintf(stderr
, "-D :: expects"
346 (void) strtol(cp
, &endcp
, 10);
348 if (*cp
!= '\0' && *cp
!= ' ') {
349 fprintf(stderr
, "Bad syntax in"
355 haveendstr
= (cp
[1] == '\0');
356 if (nextcp(&cp
, &index
, argc
, argv
,
358 fprintf(stderr
, "-f expects"
370 cmdbuf
[cmdlen
++] = ' ';
371 strcpy(cmdbuf
+ cmdlen
, "read ");
373 if (strncmp(cp
, "-once", 5) == 0 &&
374 (cp
[5] == '\0' || cp
[5] == ' ')) {
376 haveendstr
= (*cp
== '\0');
377 strcpy(cmdbuf
+cmdlen
, "-once ");
379 if (nextcp(&cp
, &index
, argc
,
381 fprintf(stderr
, "-f -once"
387 bp
= cmdbuf
+ cmdlen
;
396 if (cmdlen
+ len
+ 2 > MAXCMD
) {
402 /* XXX What if *cp = '\''? */
404 strncpy(bp
, cp
, len
+1);
411 if (cmdlen
> MAXCMD
) {
419 } while (*cp
!= '\0' &&
427 s_flag
= TRUE
; /* -f implies -s */
432 maxindex
= index
+ 1;
436 * we are too early in processing to
437 * call libcalc_call_me_last() -
440 fprintf(stderr
, "Illegal option -%c\n",
443 "usage: %s [-c] [-C] [-d] [-e] [-h] [-i] [-m mode]\n"
444 "\t[-D calc_debug[:resource_debug[:user_debug]]]\n"
445 "\t[-O] [-p] [-q] [-s] [-u] [-v] "
446 "[--] [calc_cmd ...]\n"
447 "usage: %s ... -f filename\n"
448 "1st cscript line: #/path/to/calc ... -f\n",
465 while (index
< maxindex
) {
469 cmdbuf
[cmdlen
++] = ' ';
471 newcmdlen
= cmdlen
+ cplen
;
472 if (newcmdlen
> MAXCMD
) {
474 "%s: commands too long\n",
478 strncpy(cmdbuf
+ cmdlen
, cp
, cplen
+1);
481 if (index
< maxindex
)
485 havecommands
= (cmdlen
> 0);
488 cmdbuf
[cmdlen
++] = '\n';
489 cmdbuf
[cmdlen
] = '\0';
491 perror("main(): fclose(stdin) failed:");
495 argc_value
= argc
- maxindex
;
496 argv_value
= argv
+ maxindex
;
503 setbuf(stdout
, NULL
);
510 libcalc_call_me_first();
511 stdin_tty
= isatty(0); /* assume stdin is on fd 0 */
512 if (conf
->calc_debug
& CALCDBG_TTY
)
513 printf("main: stdin_tty is %d\n", stdin_tty
);
515 givehelp(DEFAULTCALCHELP
);
516 libcalc_call_me_last();
521 * if allowed or needed, print version and setup bindings
523 if (!havecommands
&& stdin_tty
) {
525 printf("%s (version %s)\n", CALC_TITLE
, version());
526 printf("Calc is open software. For license details "
527 "type: help copyright\n");
529 "Type \"exit\" to exit, or \"help\" for help.");
531 switch (hist_init(calcbindings
)) {
534 "%s: Cannot open bindings file \"%s\", "
535 "fancy editing disabled.\n",
536 program
, calcbindings
);
541 "%s: Cannot set terminal modes, "
542 "fancy editing disabled\n", program
);
548 * establish error longjump point with initial conditions
550 if (setjmp(calc_scanerr_jmpbuf
) == 0) {
553 * reset/initialize the computing environment
556 calc_use_scanerr_jmpbuf
= 1;
560 * (re)establish the interrupt handler
562 (void) signal(SIGINT
, intint
);
565 * execute calc code based on the run state
567 if (run_state
== RUN_BEGIN
) {
568 if (!q_flag
&& allow_read
) {
569 set_run_state(RUN_RCFILES
);
572 set_run_state(RUN_PRE_CMD_ARGS
);
575 while (run_state
== RUN_RCFILES
) {
576 fprintf(stderr
, "Error in rcfiles\n");
577 if ((c_flag
&& !stoponerror
) || stoponerror
< 0) {
579 if (inputlevel() == 0) {
582 set_run_state(RUN_PRE_CMD_ARGS
);
587 if ((havecommands
&& !i_flag
) || !stdin_tty
) {
588 set_run_state(RUN_EXIT_WITH_ERROR
);
590 set_run_state(RUN_PRE_CMD_ARGS
);
595 if (run_state
== RUN_PRE_CMD_ARGS
) {
597 set_run_state(RUN_CMD_ARGS
);
598 (void) openstring(cmdbuf
, strlen(cmdbuf
));
602 set_run_state(RUN_PRE_TOP_LEVEL
);
605 while (run_state
== RUN_CMD_ARGS
) {
606 fprintf(stderr
, "Error in commands\n");
607 if ((c_flag
&& !stoponerror
) || stoponerror
< 0) {
609 if (inputlevel() == 0)
610 set_run_state(RUN_PRE_TOP_LEVEL
);
614 if (!stdin_tty
|| !i_flag
|| p_flag
) {
615 set_run_state(RUN_EXIT_WITH_ERROR
);
617 set_run_state(RUN_PRE_TOP_LEVEL
);
622 if (run_state
== RUN_PRE_TOP_LEVEL
) {
624 (((havecommands
) && !i_flag
) || p_flag
)) {
625 set_run_state(RUN_EXIT
);
633 set_run_state(RUN_TOP_LEVEL
);
636 if (p_flag
|| (!i_flag
&& havecommands
))
637 set_run_state(RUN_EXIT
);
640 while (run_state
== RUN_TOP_LEVEL
) {
641 if (conf
->calc_debug
& CALCDBG_RUNSTATE
)
642 printf("main: run_state = TOP_LEVEL\n");
643 if ((c_flag
&& !stoponerror
) || stoponerror
< 0) {
645 if (!inputisterminal()) {
649 if (!p_flag
&& i_flag
&& !stdin_tty
) {
651 if(!freopen("/dev/tty", "r", stdin
)) {
654 "/dev/tty does not exist on "
655 "this operating system. "
656 "Change operating systems\n"
657 "or don't use this calc mode "
658 "in the future, sorry!\n");
659 #else /* Windoz free systems */
661 "Unable to associate stdin"
663 #endif /* Windoz free systems */
664 set_run_state(RUN_EXIT_WITH_ERROR
);
669 if (conf
->calc_debug
& CALCDBG_TTY
)
670 printf("main: stdin_tty is %d\n",
678 } else if (inputisterminal() &&
679 !p_flag
&& (!havecommands
||i_flag
)) {
681 if(!freopen("/dev/tty", "r", stdin
)) {
684 "/dev/tty does not exist on "
685 "this operating system. "
686 "Change operating systems\n"
687 "or don't use this calc mode "
688 "in the future, sorry!\n");
689 #else /* Windoz free systems */
691 "Unable to associate stdin"
693 #endif /* Windoz free systems */
694 set_run_state(RUN_EXIT_WITH_ERROR
);
698 if (conf
->calc_debug
& CALCDBG_TTY
)
699 printf("main: stdin_tty is %d\n",
703 set_run_state(RUN_EXIT_WITH_ERROR
);
707 if (conf
->calc_debug
& CALCDBG_RUNSTATE
)
708 printf("main: run_state = %s\n", run_state_name(run_state
));
713 libcalc_call_me_last();
714 return (run_state
== RUN_EXIT_WITH_ERROR
||
715 run_state
== RUN_ZERO
) ? 1 : 0;
723 * arg to keep ANSI C happy
727 intint(int UNUSED arg
)
729 (void) signal(SIGINT
, intint
);
730 if (inputwait
|| (++abortlevel
>= ABORT_NOW
)) {
731 calc_interrupt("\nABORT");
734 if (abortlevel
>= ABORT_MATH
)
736 printf("\n[Abort level %d]\n", abortlevel
);
741 * report an interrupt
744 calc_interrupt(char *fmt
, ...)
748 if (funcname
&& (*funcname
!= '*'))
749 fprintf(stderr
, "\"%s\": ", funcname
);
750 if (funcline
&& ((funcname
&& (*funcname
!= '*')) ||
752 fprintf(stderr
, "line %ld: ", funcline
);
754 vsnprintf(calc_err_msg
, MAXERROR
, fmt
, ap
);
756 calc_err_msg
[MAXERROR
] = '\0';
757 fprintf(stderr
, "%s\n\n", calc_err_msg
);
759 if (calc_use_scanerr_jmpbuf
!= 0) {
760 longjmp(calc_scanerr_jmpbuf
, 22);
762 fprintf(stderr
, "It is too early provide a command line prompt "
763 "so we must simply exit. Sorry!\n");
765 * don't call libcalc_call_me_last() -- we might loop
766 * and besides ... this is an unusual internal error case
773 nextcp(char **cpp
, int *ip
, int argc
, char **argv
, BOOL haveendstr
)
812 set_run_state(run state
)
814 if (conf
->calc_debug
& CALCDBG_RUNSTATE
)
815 printf("main: run_state from %s to %s\n",
816 run_state_name(run_state
),
817 run_state_name(state
));