summaryrefslogtreecommitdiffstats
path: root/examples/scripts/bcalc
blob: bc7e2b40e039e40e4e47aba1182bd3c04a9e29c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#! /bin/bash
#
# bcalc - a coproc example that uses bc to evaluate floating point expressions
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   This program 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 General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# If supplied command-line arguments, it uses them as the expression to have
# bc evaluate, and exits after reading the result. Otherwise, it enters an
# interactive mode, reading expressions and passing them to bc for evaluation,
# with line editing and history.
#
# You could even use this to write bc programs, but you'd have to rework the
# single-line REPL a little bit to do that (and get over the annoying timeout
# on the read)
#
# Chet Ramey
# chet.ramey@case.edu

# we force stderr to avoid synchronization issues on calculation errors, even
# with the read timeout
init()
{
	coproc BC { bc -q 2>&1; }
	# set scale
	printf "scale = 10\n" >&${BC[1]}
	# bash automatically sets BC_PID to the coproc pid; we store it so we
	# can be sure to use it even after bash reaps the coproc and unsets
	# the variables
	coproc_pid=$BC_PID
}

# not strictly necessary; the pipes will be closed when the program exits
# but we can use it in reset() below
fini()
{
	eval exec "${BC[1]}>&- ${BC[0]}<&-"
}

reset()
{
	fini	# close the old pipes

	sleep 1
	kill -1 $coproc_pid >/dev/null 2>&1	# make sure the coproc is dead
	unset coproc_pid

	init		
}
	
# set a read timeout of a half second to avoid synchronization problems
calc()
{
	printf "%s\n" "$1" >&${BC[1]}
	read -t 0.5 ANSWER <&${BC[0]}
}

init

# if we have command line options, process them as a single expression and
# print the result. we could just run `bc <<<"scale = 10 ; $*"' and be done
# with it, but we init the coproc before this and run the calculation through
# the pipes in case we want to do something else with the answer

if [ $# -gt 0 ] ; then
	calc "$*"
	printf "%s\n" "$ANSWER"
	fini
	exit 0
fi

# we don't want to save the history anywhere
unset HISTFILE

while read -e -p 'equation: ' EQN
do
	case "$EQN" in
	'')		continue ;;
	exit|quit)	break ;;
	reset)		reset ; continue ;;
	esac

	# save to the history list
	history -s "$EQN"

	# run it through bc
	calc "$EQN"
	if [ -n "$ANSWER" ] ; then
		printf "%s\n" "$ANSWER"
	fi
done
fini

exit 0