[alt.sources] Visual calculator "pac", Part 1 of 4

istvan@hhb.UUCP (Istvan Mohos) (07/14/89)

Pac is a slightly overambitious visual calculator for Unix systems.
I'm reposting pac (4 parts) in alt.sources in response to frequent
net requests for loan amortization programs.  In spite of its minor
structural problems, pac is the "everything you ever wanted in a
calculator" calculator, providing:

o   16 cells nonvolatile memory (checkbook balance, phone numbers, etc.)
o   asynchronous real-time clock
o   separate checkbook balancer window (for summing lists of numbers)
o   ASCII to decimal to octal to hex translation (find out what ^M is)
o   22 predefined conversions (metric, temperature, etc.)
o   user-definable, non-volatile conversions (dollar to yen, etc.)
o   compound interest/mortgage payment solver (play the "what if?" game)
o   amortization schedule (monthly payment list) file writer
o   function help window (type "help" or just "?" or "help [subject]")
o   optional automatic calculator session recording to files
o   trigonometric functions
o   number base conversions (independent in/out bases, between 2 and 16)
o   programmable precision: btw 0 and 32 digits past the decimal point
o   bitwise AND, OR, NOT, XOR, TWOSCOMP
o   fully automatic and accessible push/pop stack memory
o   pi, planck, parallax, parsec... 30 math/physical constants in all
o   macro tokens from "atto" to "exa" (for ex: "356 micro + 125 milli")
o   predefined percent calculations (for ex: "65.7 p[ercent]of 290")

The pac process forks pipes to the 'bc' Unix calculator.
The posted source has been successfully compiled and tested on:
Pyramid 90x, VAX 11-785, Apollo Domain-IX, Sun4, AT&T 6300+, DEC 8600.
Pac will NOT work on Sun2 or Sun3 systems.

-------------------------------CUT HERE---------------------------------
# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# pac.man README makefile defs.h maps.h

echo x - pac.man
cat > "pac.man" << '//E*O*F pac.man//'

.TH PAC 1 ""
.UC 4
.SH NAME
.nh
.nf
\fBpac\fR - panel calculator

.SH SYNOPSYS
.nf
.B pac

.fi
.SH DESCRIPTION
\fBpac\fR is an interactive calculator,
resembling a ``panel'' or ``page'' editor.
The interface is simple: the user enters
numbers and arithmetic operators, \fBpac\fR displays the result.
Alternate capabilities to the CALCULATOR include an
ASCII to decimal to octal to hexadecimal converter ATOI,
a checkbook balancer TOTAL,
a loan payment and amortization program LOAN, an
editor for the expandable ``conversions data base'' NEWCONVERSION,
time of day CLOCK; as well as session recording to files.
Operating the alternate capabilities is intuitively obvious
and is helped along by the format of the display.

The \fBpac\fR panel is a screen 23 lines by 78 characters,
partitioned by inverse video borders into
subwindows.  The CALCULATOR
is a rectangle 4 lines deep by 44 characters wide,
located in the upper left corner of the panel,
simulating a hand-held calculator: echoing numbers and
operators typed to the first three
lines, displaying the evaluated result
in the fourth line upon RETURN.  Control characters,
shown along the borders as first characters of mnemonics,
provide immediate state switching to and from alternate tools.

.SH LINE EDITING
A selected function comes to life with the cursor
in the home position of a subwindow managed by the function.
The user directs input to this portion of the screen under the
control of a primitive line editor.

Normally the typed characters are echoed and contribute to
the input string.  Characters which the
tool is not equipped to deal with are not echoed, and are ignored
by the editor.  Control characters (also not echoed)
select alternate tools, or
provide exit from the program.  A number of keys are commands to
the line editor only, allowing cursor positioning and input correction.
The editor keys and their effect may vary slightly,
depending on the currently active \fBpac\fR tool.

The first character echoed to a line,
normally erases the previous input string.
In the CALCULATOR function, the line editing \fI=\fR key
restores the old input string for editing.  A string beginning
with the \fI#\fR character is considered a comment.
Comments are helpful if the user is recording the session, or
for annotating scripts.
Another line editing character specific to the CALCULATOR
window is the pipe \fI|\fR, which
brings the cursor to the left edge of the following window line.

If the input string reaches the
right line boundary, additional characters overwrite the rightmost
character, except in the CALCULATOR function where the three lines of
the input window conceptually form a single string.
Typing RETURN triggers the interpretation of the input string.
Regardless of the cursor position at the time of RETURN,
all data on the line is evaluated.  If the user did not type
characters prior to RETURN, a repeat of the previous string,
or a tool-specific default string is the assumed input.

A summary of the edit keys is always visible in an inverse video bar
running vertically in the center of the screen.
Editing characters include \fIDELETE\fR and \fIBACKSPACE\fR,
both of which delete characters
under the cursor and move bytes at the right of the cursor
to the left.

\fIControl-C\fR in the CALCULATOR function, \fISPACE\fR in the
checkbook balancer function, clear a line from the cursor to
its end; in the checkbook balancer function a \fIControl-C\fR
clears (zeroes) the entire window.
Straight brackets \fI[  ]\fR move the
the cursor non-destructively to the left or right.
In the CALCULATOR function, curly braces \fI{  }\fR move the cursor
to the line above or below, while
the ``greater than''
sign \fI>\fR switches to \fIinsert mode\fR:
characters already to the right of the
cursor are pushed further to the right with each new character added.
The ``less than'' sign \fI<\fR, the \fIESCAPE\fR key or a
RETURN end the \fIinsert mode\fR.

.SH CALCULATOR OPERATION
Input begins at the top left byte of the CALCULATOR window,
and may consist of multiple statements separated
by semicolons.  After RETURN, the input is broken down
to a list of tokens.
Compound tokens contain one or more alphanumeric characters,
underscores and periods; simple tokens are single,
non-alphanumeric characters from the set:

    ; % + - * / ^ ( ) \\ ' ? ! #

A subset of simple tokens are the math operators
\fI%\fR, \fI+\fR or \fI-\fR, \fI*\fR or \fI/\fR, \fI^\fR
(in increasing order of operator precedence).
The percent sign stands for the ``modulo'' operation, the asterisk
stands for multiplication, the circumflex for exponentiation.
Only integer exponents are allowed.

Tokens are delimited by commas or spaces, by the appearance of the
next simple token, or by the end of the input line.

Numbers are a subset of compound tokens, formed entirely from
the digits 0-9, the hexadecimal digits a-f or A-F, and the
period character.

During parsing, commas and spaces are removed, and successive
numbers are concatenated to form a single number.

The resulting token list is passed to the interpreter, which
evaluates each token in a normal, left-to-right sequence.
A \fBpac\fR lookup table is searched for a match to the token.
A match proves the token to be a \fBpac\fR control word, processed
immediately.  This preprocessing may affect other tokens to
the left or right of the control word; and in most cases the
affected tokens (including the control word) are replaced
by a single number.

If the token does not match a control word, any lower case a-f
characters of the token are upcased.

From the interpreter, the string
is passed to the \fBbc\fR calculator
through an inter-process pipe.
Bc evaluates the math expression,
and returns the result to \fBpac\fR for
formatting and display.  \fBpac\fR
prints the result in the fourth (bottom)
line of the CALCULATOR window.

The \fIprecision\fR variable controls
the number of digits printed to the right of a decimal point.  This
variable can be set with the \fIprecision n\fR  or 
\fIpr n\fR command; n values may range from 0 to 32.
Results with greater than \fIprecision\fR fractional digits
are rounded.  Fractions too small to show up within the
precision range cause an underflow error.  User selectable number
formatting and justification controls the final
appearance of the result.

Examples of CALCULATOR input:

.nf
    77+88+99 - 66/(3 * 2)
    4.5  *  \fIpi\fR			---  using the constant \fIpi\fR
    12.785 + \fIs\fR			---  add 12.785 and stack variable \fIs\fR
    12.785 + \fIs\fR; \fIsto j\fR		---  as above; copy result into \fIj\fR
    f + a			---  add hex numbers (dec15 + dec10)
    \fIp\fR + \fIq\fR			---  add stack variables \fIp\fR and \fIq\fR
    3.3^4			---  the fourth power of 3.3
    27^ (-3)			---  1 / 19683
    7! - 99			---  factorial of 7, minus 99
    (-5) * 40			---  using the unary minus
    (998 \fImicro\fR + .000002)\fImega\fR	---  1000
    32/(16/(8/(4/(\fIm\fR * 2/\fIm\fR))))	---  8 (if stack variable \fIm\fR not equal 0)
    +10				---  add 10 to the previous result
    -10				---  subtract 10 from the previous result
    - 60 / 3			---  subtract 20 from the previous result
    * 5				---  multiply previous result by 5
.fi

\fBpac\fR stores results as digit strings, on 17 lines of screen
display.  Each of the strings is a \fBpac\fR variable.
The first of these strings, CURRES,
is the result shown in the CALCULATOR window.
CURRES is updated continuously and is
the result of the most recently evaluated expression.  This value
can be referenced by the \fI\\\fR (backslash) character:
\fI9 + \\\fR adds CURRES and 9.  Any operator that unsuccessfully
looks for an operand to its left, will automatically reference
CURRES
(as in the last four ``Examples of CALCULATOR input'' above).
Syntax errors clear CURRES to zero.

The other 16 strings constitute the \fIuser stack\fR,
a rectangular area directly below the CALCULATOR window.
The 16 lines of the stack are
labeled by the letters \fIh\fR to \fIw\fR
in the left inverse video border.
These labels are also \fBpac\fR tokens, and can be input to
the CALCULATOR instead of typing the string contained in a stack cell.
New results are automatically ``pushed'' on the stack, unless the
\fBpac\fR command \fIstack off\fR prohibits this.

\fBpac\fR key words for explicitly
manipulating stack contents are
\fIclr\fR, \fIdup\fR, \fIpop\fR, \fIpll\fR, \fIpsh\fR,
\fIsto\fR, \fIswp\fR.
These operators can be input to the CALCULATOR regardless
of whether the stack is \fIon\fR or \fIoff\fR.
A stack operator normally references the single-character
stack label immediately following the operator.
For example, \fIpop m\fR removes the number contained in \fIm\fR,
and moves all stack strings located below \fIm\fR up one position;
\fIclr p\fR substitutes the number in stack cell \fIp\fR with zero.
If a stack label is not given,
CURRES is the subject of the stack operation, except that \fIclr\fR,
replaces the value of each stack variable with 0.

The \fBpac\fR keyword \fIibase\fR (abbreviated version: \fIib\fR)
followed by an integer or a variable in the range of 2 through 16,
can select an input base other than decimal.
The command

    \fIib\fR 2; 1011 0100 0010

as an example, results in
the decimal display of ``2882''.

The effect of the \fIib\fR command lasts from the time it was
first encountered in the input, to the end of the
input string; then \fBpac\fR automatically
reverts to the original input base.
To permanently change the input base,
the command \fIstaybase on\fR
(abbreviated version: \fIsb\fR) should be issued.
The effect of \fIstaybase on\fR lasts until
a \fIstaybase off\fR commands cancels it.
Output radix is similarly controlled by the \fIobase n\fR
(or \fIob n\fR) command for n values 2-16; and
\fIstaybase\fR effects the permanence of input
and output radices in tandem.

Some useful CALCULATOR operators are:

.nf
    the bitwise operations	\fIand or xor not twoscomp\fR (abbr.:\fItc\fR)
    the percent operations	\fIpplus pminus pdiff pversus pequal pof\fR (abbr.: \fIpp pm pd pv pe po\fR)
    the conversion operator	\fIto\fR
.fi

Examples:

.nf
    Binary AND of 9999 and 122:
    9999 \fIand\fR 122					---  10

    Binary NOT of 9999 in 16 bit wide field:
    9999 \fInot\fR 16					---  55536

    Add 6.5 percent to 3500:
    3500 \fIpplus\fR 6.5				---  3727.5

    Percentage increase from 3300 to 3500:
    3300 \fIpdiff\fR 3500				---  6.06

    If 4778 is 100%, what % is 2389:
    4778 \fIpversus\fR 2389				---  50

    What is 75% of 4000:
    75 \fIpof\fR 4000					---  3000

    If 40% of a sum is 50, what is the sum:
    40 \fIpequal\fR 50					---  125

    Convert 1225 inches to centimeters:
    1225 \fIto cm\fR					---  3111.5
.fi

The \fBpac\fR tokens
\fIsin\fR(x), \fIcos\fR(x), \fIexp\fR(x), \fIlog\fR(x), \fIarct\fR(x)
are used as substitutes for the \fBbc\fR
``s(x), c(x), e(x), l(x), a(x)'' functions, respectively.
Input values and results are radians.


.SH THE CALCULATOR ENVIRONMENT
Environment control operators are used to customize
the CALCULATOR to the user's liking.
As with the other \fBpac\fR tokens, a certain redundancy is apparent
in naming the variables: two-character abbreviations are
generally accepted as input tokens.

When exiting \fBpac\fR, an attempt is made to
write a
``.pacrc'' file in the user's home\fR directory, unless the
\fIdontsave\fR command expressly forbids this.  In the .pacrc
file, \fBpac\fR keeps a record of the current panel environment.  The
file also contains the sixteen stack strings, and the
up-to-date ``units conversions list'' described under
EDITING THE CONVERSIONS LIST, with any modifications
effected by the user. Subsequent calls to
\fBpac\fR begin by reading and resetting internal parameters
from the file data.

To the right of the stack, the narrower GLOBALS window lists the
environment control operators and their current values.
The state-switching \fIControl-G\fR character
overlays the GLOBALS window with a STATUS OPTIONS window, which
shows the abbreviated name of each control operator token, followed
by the values that this operator may assume.
A second \fIControl-G\fR toggles the STATUS OPTIONS
window display back to GLOBALS,
unless the execution of a CALCULATOR string has already caused
the re-display of the GLOBALS window.

The variables \fIjustify\fR, \fIformat\fR,
\fIhardform\fR, and \fIautotime\fR
control the format of the \fBpac\fR display screen, and control
the format of the file records.
The command \fIjustify left\fR
(abbreviated: \fIju le\fR) instructs \fBpac\fR
to position the leftmost digit of the
result in the bottom leftmost position of the CALCULATOR window.
Conversely, \fIjustify right\fR
(abbreviated: \fIju ri\fR) specifies that the rightmost digit of the
result be placed in the bottom rightmost
CALCULATOR window position.
As a special case of \fIjustify right\fR,
\fIjustify fix\fR outputs the result
in a fixed decimal point format.  In fixed
justification, the number of digits displayed to
the right of the decimal point is constant: the value of the
\fIprecision\fR variable (0 through 32).
The \fIformat space\fR or \fIformat comma\fR
(abbreviated: \fIsp\fR and \fIcm\fR)
command specifies that CURRES values are
to be displayed with spaces or commas respectively,
between groups of digits;
this makes it easier to read long strings.

The \fIhardform\fR command controls the format of a
recording file which can be opened for writing with the \fIControl-F\fR
(File)
character, or for appending with the \fIControl-P\fR
(Postpend)
character.  The user is prompted for a file name; ``hardcopy'' is the
default.  Having opened the file, a new
\fIControl-F\fR or \fIControl-P\fR will close it, otherwise the file
will record calculator transactions until the exit from \fBpac\fR.
The command \fIhardform verbose\fR (abbreviated: \fIver\fR) resets
to the default file output format, saving
both input and results, with
records separated by horizontal lines of dashes.
The commands \fIhardform terse\fR
(\fIte\fR) and \fIhardform xterse\fR (\fIxt\fR) specify
successively denser session recording.

The command: \fIautoconv\fR automatically applies the selected
unit conversion formula to each new evaluation (CURRES)
prior to displaying the result.  For example, a list of dollar figures
may be converted to pounds just by entering each dollar value.

The macro command: \fIinit\fR resets the format, hardform,
ibase, obase, justify, precision, stack, staybase, and
autoconv parameters to their default values.

The special combination \fIhelp [token]\fR
(or its abbreviated form \fI? [token]\fR)
overlays the stack area with a portion of the token list,
highlighting the selected token.  To the right of each
token  of the list, a very brief summary
of the token action is printed.  Stack display
resumes at the first subsequent stack activity.

.SH ASCII CONVERSIONS
Directly to the right of the CALCULATOR window, a small rectangle
provides immediate conversion between ASCII characters and their
decimal, octal, or hexadecimal values, on a per-character basis.
The window is titled ATOI, and initially contains the following prompts:

.nf
    ^ A  asc
    ^ D  dec
    ^ O  oct
    ^ X  hex
.fi

The characters preceded by the caret \fI^\fR stand for the control
characters which cause temporary
transition from the CALCULATOR state
to the ATOI window.  \fIControl-A\fR prompts the user to enter
any character on the top line of the ATOI window,
and immediately displays the decimal, octal,
and hex equivalents of this character beneath the input.
Context then returns to the CALCULATOR.
\fIControl-D\fR similarly, prompts for a decimal value
in the top line, in the 0-127 range.  Following a RETURN,
the ASCII equivalent of the decimal
value, as well as the octal and hexadecimal versions are printed.
\fIControl-O\fR prompts for an octal number
between \\000 and \\177; \fIControl-X\fR
prompts for a hexadecimal value between 0x0 and 0x7f.
Values out of the
ASCII range are ignored, and the cursor stays on the first
line of the ATOI window until a legal value is selected.

.SH AMORTIZATION
\fIControl-L\fR (LOAN) changes context and moves the cursor
to the loan amortization window below ATOI.
A second \fIControl-L\fR returns the user to the previous context.
The top three lines of the LOAN window are labeled
``AMT'', ``%'', and ``YRS'' in the inverse video border to the
left.  The user is expected to input a decimal string
as the ``loan amount'' (principal)
on the ``AMT'' line, an amortization (percent) rate in the second line,
and the life term of the loan (in years) on the third line.
Up to two digits past the decimal point are significant
for both the principal and the life term values,
three digits past the decimal point are significant for
the percent rate.

When all
three lines contain non-zero values, the monthly payment on the proposed
loan is immediately displayed in the fourth line of the window, and the
cursor wraps back to the first line.  The user can now
repeatedly adjust the amount, rate, or term values in an infinite loop,
by simply
typing over the old values.  Each change causes an immediate update of
the monthly payment figure.

To dump the actual monthly amortization schedule to a file,
\fIControl-B\fR
moves the cursor to the last line of the LOAN window, where the
month/year date of the first payment is entered; then the user is
prompted for a file name on the bottom of the screen.  The amortization
file creation involves substantial floating point math and string
formatting, and takes a few seconds to complete.

The author has gleaned the amortization algorithm from
examining actual bank loan printouts.
While the \fBpac\fR method duplicates
the bank samples exactly, different methods of rounding
may possibly be employed by other lending institutions, resulting in
minor discrepancies between the two sets of figures.

.SH CHECKBOOK BALANCER
Cashier's lists, checkbook tallies, score keeping, and
a number of book keeping activities involve entering and summing
values in columnar format.
\fIControl-T\fR switches \fBpac\fR to the TOTAL window
directly overlaying the conversions\fR list.
Initially, the TOTAL window consists of 19 lines
of zero values, and a bottom ``total'' line.
The lines are labeled in the
right inverse video border by the lower case letters \fIa\fR through
\fIs\fR, and the ``total''
line is labeled by \fIt\fR.  As the
user types in decimal values (followed by RETURN)
the cursor advances to the next lower line, and the entered values are
automatically summed to ``total''.

At each RETURN, the most recently typed value is re-formatted
right justified, with the number of fractional digits
dictated by the user environment'
precision value.  Negative values
are subtracted from the total.  The line may also
contain a single \fI*\fR or \fI@\fR character, to effect
price multiplication by item count.  As an example, ``64 @ 9.17''
is immediately converted to ``586.88''.

When nineteen items have already been entered and the cursor is
in line \fIs\fR, the input window wipes
itself clean and copies the current total
to the top line.  The cursor is then positioned at the beginning of the
second line, and the entry process can continue indefinitely.  It is
convenient to refer to this paging
event as a ``subtotal''.  At subtotal,
as well as at the exit from the TOTAL function,
the current total is pushed into stack cell \fIh\fR (the top
visible element of the user stack on the left side).  If the user stack
is \fIon\fR, its elements are shifted down one line, potentially
retaining a sequence of subtotal values.  The calculator
CURRES value
is not affected by subtotals; the user must explicitly reference the
appropriate stack cells to transfer values to the
CALCULATOR window for input.

Similarly to the ``hardcopy''
file used for keeping an automatic record of CALCULATOR
activities, a separate ``hardtotal'' file may be opened
for \fIwrite\fR or \fIappend\fR
while the checkbook balancer is active.
``Hardtotal'' is supplied as the default file name, unless
the user types in another name.
The file continues receiving snapshots of the entire TOTAL window at
every subtotal (except receiving only the subtotal values
if the \fBpac\fR \fIhardform\fR variable is set to \fIxterse\fR),
and a final snapshot on exit from the TOTAL window.

TOTAL interprets decimal numbers only. In
addition to digits, \fI+ - * @\fR and the normal line editing
characters, the TOTAL line editor also accepts
the letters \fIa\fR through \fIt\fR and \fIA\fR through \fIT\fR.
The lower case letters cause the value in the corresponding line to
be replicated in the line of the cursor
(\fIt\fR replicates the current total).  Upper case 
alphabetics \fIA\fR through \fIS\fR relocate the cursor
to the line labeled by the corresponding
lower case letter; input then continues from this line.
When the cursor is relocated this way, the digit string
already in the line is not cleared; the new input will be
prepended to it.  A space typed clears the line from the cursor
to its end.
Upper case \fIT\fR forces a stack push of the current total,
and the snapshot of the window written to an open ``hardtotal'' file.
The \fI#\fR character
is again a comment delimiter,
signaling the editor to ignore it and any further input
until the RETURN.
A second \fIControl-T\fR provides return to the previous context.  A
``hardtotal''
file may be explicitly closed prior to exiting TOTAL, or
it may be left open, awaiting recording of further TOTAL activities.
Even after exiting TOTAL, the window remains visible over the
conversions\fR list until a CALCULATOR conversion takes place.

.SH EDITING THE CONVERSIONS LIST
Unit conversion operations of the CALCULATOR
are based on a conversions data base, shadowed from a static list
into dynamically allocated memory at initialization, and
modifiable by the user.
The newconversion\fR editor for the conversions database
is entered with a \fIControl-N\fR,
typically to add new conversions defined by the user,
but occasionally to remove existing conversions, or to alter current
conversion factors.
\fBpac\fR conversions are unidirectional,
converting from units \fIA\fR to units \fIB\fR.
Separate conversions must be defined to convert from dollars to
pounds, and from pounds to dollars, for example.
An altered exchange rate between the dollar and the
pound may prompt the user to update the dollar-to-pound
and pound-to-dollar formulae.

Editing the conversions data base begins in a temporary window
overlaying the CALCULATOR window.
The user is prompted to select between adding, removing
or altering a conversion.  Next the user enters a key
up to three characters long.

If the removal or the alteration
of an existing conversion is selected,
the ``conversion keys list'' is searched for a matching key;
the key may be re-entered on error until a match is found.  When
changing an already existing conversion, the user enters a new key
(up to three bytes long) as the altered key value.
For the pound-to-dollar conversion, let's assume the key \fIus\fR.
A new conversion key is inserted in
the key list in alphabetical order.
A thirteen characters long reference label is typed in next (typically
a ``from-units to-units'' pair), as a visual
reminder of the meaning of the key.  In our example, this may simply be
\fIpound  dollar\fR.

The new formula is entered at the bottom inverse video
bar.  The character set of the formula is limited to
digits, math operators \fI+ - * / ^ %\fR,
decimal point, left and right parentheses,
and the backslash character \fI\\\fR.
Normal line editing characters may be
used to correct errors.  All numbers are assumed to be decimal.
Given the exchange rate of 1.75 dollars to the pound,
the ``X pounds = Y dollars'' equation could be stated as
``Y = X * 1.75'' and entered as

    \\ * 1.75

into the data base.  When the formula is invoked in a later
CALCULATOR conversion such as ``900 \fIto us\fR'',
the backslash standing for X in the solution, will be substituted by
``900'', followed by the multiplication by the constant to get
the new (converted) value.  Though the conversion list
is stored in decimal format,
during calculations the constant strings are
automatically re-cast into the input radix in effect.
The converse operation: dollars to pounds, would have a different key,
a different label, and the following conversion formula:

    \\ / 1.75

More complex formulae are feasible; the formula need not start with a
backslash; multiple backslashes may occur.
Having finished with the editing of the conversion, the most recently
added conversion is shown selected in the center
of the conversions\fR window.  Context reverts to the calling tool.

.SH CLOCK
\fIControl-K\fR installs a date/hour/minute clock in the
right corner of the top inverse video border.
Time is kept in a twelve-hour format; a period
following the minutes denotes PM.  The clock operates asynchronously
and without affecting the rest of the program.  A second \fIControl-K\fR
turns off the clock.
The \fBpac\fR environment variable \fIautotime\fR may be enabled,
with the effect that the clock appears automatically on entry to
the program.

.SH EXIT
\fIControl-E\fR resets
the terminal to cooked mode, writes the .pacrc file
(containing the \fBpac\fR environment variables, stack, and the
conversion list) in the user's HOME directory;
and returns the user to the shell.
The cursor is moved to the line below the \fBpac\fR screen.
From the CALCULATOR function, either \fIquit\fR or \fIexit\fR
has the same effect.  The action of the
\fITAB\fR character (\fIControl-I\fR)
or the equivalent token \fIbye\fR
is different in that the CALCULATOR stack is
cleared (all 0 values are saved in the .pacrc file), and
the screen is cleared as well.

.SH BUGS
Physical constants are defined to a few digits of precision only,
not to the 32 digits maximum capability of the calculator.
There is
no guarantee as to the accuracy of the constant definitions;
the program must be re-compiled to make improvements or changes.

Parentheses are passed through the preprocessor to \fBbc\fR
instead of being handled immediately.  For this reason,
one should avoid using \fBpac\fR operators (multi-byte non-numeric
words, and the factorial operator \fI!\fR) inside open
parentheses.  Also, logical (bit) and percent operators should not
directly precede a left parenthesis.

These same \fBpac\fR operators may force partial evaluations of the
input string, often changing CURRES faster than it is possible
to visually update the result.  Explicit use of the CURRES token
\fIBACKSLASH\fR is therefore discouraged, except during the
editing of conversion formulae.

Though \fBpac\fR traps error messages from \fBbc\fR, if the operating
system is running with a broken or incorrectly compiled/linked
\fBbc\fR, the buggy executable may ``surprize'' the \fBpac\fR
process by deadlocking the pipes.

Due to its interactive nature, \fBpac\fR is not optimized for speed;
shell scripts take too long to execute.
//E*O*F pac.man//

echo x - README
cat > "README" << '//E*O*F README//'

If you are running under sysV, make with the -DREALUNIX flag.
Version.c is created by the makefile, it is ok to loose it.
The target terminal should have at least 80 columns and 24 lines.

If the shell complains about printer problems in response to the
'pac' command, it has found the printer accounting /etc/pac instead.

The man pages are in "pac.m", use "troff -man" to print a copy.
//E*O*F README//

echo x - makefile
cat > "makefile" << '//E*O*F makefile//'
#
CC=/bin/cc

# for bsd:
CFLAGS=-c -O
LFLAGS=-ltermlib
MFLAGS=

# for sysV:
# CFLAGS=-c -O -DREALUNIX
# LFLAGS=
# MFLAGS=

# for AT&T 6300+:
# CFLAGS=-c -Ml -s -DREALUNIX
# LFLAGS=
# MFLAGS=-Ml -s

#.SILENT:

all: version pac

version:
	@/bin/rm -f version.c
	@echo 'char *version = "'`date`'";' > version.c

pac: pac.o pipes.o file.o system.o work.o display.o error.o \
	interpret.o pactok.o onlay.o conv.o ledit.o amort.o \
	atoi.o total.o bitwise.o time.o stack.o version.o help.o \
	ierror.o convbase.o
	${CC} ${MFLAGS} pac.o pipes.o system.o work.o display.o file.o \
	error.o onlay.o interpret.o pactok.o conv.o ledit.o amort.o \
	atoi.o total.o bitwise.o time.o stack.o version.o help.o \
	ierror.o convbase.o \
	 -lcurses ${LFLAGS} -lm -o pac

version.o:;${CC} ${CFLAGS} version.c

pac.o: defs.h maps.h pac.c
	${CC} ${CFLAGS} pac.c

pipes.o: defs.h pipes.c
	${CC} ${CFLAGS} pipes.c

pactok.o: defs.h toktab.h pactok.c
	${CC} ${CFLAGS} pactok.c

file.o: defs.h maps.h file.c
	${CC} ${CFLAGS} file.c

system.o: defs.h system.c
	${CC} ${CFLAGS} system.c

work.o: defs.h work.c
	${CC} ${CFLAGS} work.c

display.o: defs.h display.c
	${CC} ${CFLAGS} display.c

interpret.o: defs.h toktab.h maps.h interpret.c
	${CC} ${CFLAGS} interpret.c

onlay.o: defs.h onlay.c
	${CC} ${CFLAGS} onlay.c

conv.o: defs.h maps.h conv.c
	${CC} ${CFLAGS} conv.c

error.o: defs.h error.c
	${CC} ${CFLAGS} error.c

ledit.o: defs.h ledit.c
	${CC} ${CFLAGS} ledit.c

amort.o: defs.h maps.h amort.c
	${CC} ${CFLAGS} amort.c

atoi.o: defs.h maps.h atoi.c
	${CC} ${CFLAGS} atoi.c

total.o: defs.h maps.h total.c
	${CC} ${CFLAGS} total.c

bitwise.o: defs.h toktab.h bitwise.c
	${CC} ${CFLAGS} bitwise.c

time.o: defs.h time.c
	${CC} ${CFLAGS} time.c

stack.o: defs.h stack.c
	${CC} ${CFLAGS} stack.c

help.o: defs.h toktab.h help.c
	${CC} ${CFLAGS} help.c

ierror.o: ierror.c
	${CC} ${CFLAGS} ierror.c

convbase.o: defs.h toktab.h maps.h convbase.c
	${CC} ${CFLAGS} convbase.c

//E*O*F makefile//

echo x - defs.h
cat > "defs.h" << '//E*O*F defs.h//'
/* defs.h */
/**********************************************************************
*    File Name     : defs.h
*    Function      : definitions, declarations
*                  :    this header is included in all .c files of pac
*    Author        : Istvan Mohos, 1987
***********************************************************************/

#include <curses.h>
#ifdef REALUNIX
#include <fcntl.h>
#else
#include <sys/file.h>
#endif
#include <ctype.h>
#include <signal.h>
#include <math.h>

#ifdef TRACE
#define _TR   if (Trace) fprintf(Tf,">%s%s\n",&Ttabs[Tlev--],fid);
#define TR_   if (Trace) fprintf(Tf,"<%s%s\n",&Ttabs[++Tlev],fid);
#else
#define _TR
#define TR_
#endif

#define ZERO  (char *)0

#define LBOUND    1            /* left (inverse) edge of screen */
#define RBOUND    78           /* right (inverse) edge of screen */
#define TOP       1            /* top (inverse) edge of screen */
#define BOT       23           /* bottom (inverse) edge of screen */
#define UTOP      (TOP + 1)    /* user top of calculator window */
#define UBOT      (TOP + 3)    /* user bottom of calculator window */
#define ACCUM     (TOP + 4)    /* result line under calculator window */
#define ACCUMAX   44           /* max calculator,result width */
#define URIGHT    46           /* right user boundary of calculator */
#define DIGMAX    32           /* max calculator (numeric) digits */
#define ULEFT     3            /* left user calculator boundary */
#define MSG       (TOP + 5)    /* vertical location of error message */
#define MSGLEFT   4            /* left boundary of error message */
#define STACKLEFT 3            /* left boundary of stack (base) */
#define STACKTOP  (TOP + 6)    /* upper boundary of stack */
#define STACKBOT  (TOP + 21)   /* lower boundary of stack */
#define STACKMAX  36           /* max stack width (including base) */
#define STACKDEEP 16           /* max number of stack cells */
#define MYBUF    300           /* scratch display buffer */

#define ATOIX      50          /* leftmost char printed into ATOI */
#define AMTY       7           /* vert location of loan AMT */
#define PRCY       8           /* percent */
#define YRY        9           /* years */
#define PAYY      10           /* payment */
#define DATY      11           /* start date */
#define LOANX      44          /* leftmost char printed into LOAN */
#define LOANSIZ    13          /* max width of LOAN line */
#define LOANR      (LOANX + LOANSIZ - 1)
#define STATMSG    44          /* leftmost char printed into STATUS */
#define STATX      54          /* left edge of 3 char status flag */
#define STATY      (TOP + 12)  /* ver location of first status string */

                               /* status defaults */
#define PREC_DFLT  2           /* precision (# of digits past dp) */
#define IB_DFLT    10          /* input base */
#define OB_DFLT    10          /* output base */
#define DISA       0
#define ENA        1
#define AP         2
#define STACK_DFLT ENA         /* new result gets pushed on stack */
#define COMMA_      1
#define SPACE_      2
#define FORM_DFLT  SPACE_       /* put commas or spaces in numbers */
#define ED_DFLT    ENA         /* unimplemented */
#define JL         0
#define JF         1
#define JR         2
#define JUS_DFLT   JR          /* right, left, or fixed dp on right */
#define SB_DFLT    DISA        /* radix change effects current string */
#define SHOW_DFLT  DISA        /* don't pfresh until done */
#define FVER       0
#define FTER       1
#define FXTER      2
#define HF_DFLT    FVER        /* file dump verbose, terse or xterse */

#define REGLEFT    3           /* leftmost char of stack cell (base) */
#define CONVLEFT   60          /* leftmost char of CONV. labels */
#define KEYLEFT    74          /* leftmost char of CONV. 3 char key */
#define FITCONV    (BOT - TOP - 1) /* max conv strings visible */
#define CONVCOUNT  24          /* number of predefined conversions */
#define FROMTOSIZ  14          /* conv. label size + \0 */
#define KEYSIZ     4           /* conv key size + \0 */
#define CONVMAX    (FROMTOSIZ + KEYSIZ - 1) /* conv string window siz */
#define CENTER     11          /* non_curses pos. in conv window */
#define CONVSEL    (CENTER - 1)/* original (default) selection */

#define TREQ       2           /* update requests to print top line */
#define BREQ       9           /* update requests to print bottom line */
#define MSGLEN    13
#define FBOUND    (ULEFT + MSGLEN) /* where the screen fname starts */
#define BUFSTOP   (FBOUND - ULEFT) /* where fname starts in buffer */
#define TITSIZ    (RBOUND - ULEFT + 1)
#define TALYREQ    1
#define FILEREQ    2
#define POSTREQ    3
#define TOTLREQ    4
#define TAPPREQ    5
#define LOANREQ    6
#define CONVREQ    7
#define EDITREQ    8

#define NOTINLIST -1           /* not recognized by pac tokenizer */
#define PIPEMAX   4096         /* guaranteed min size by UNIX */
#define WINLINE   81           /* max screen line + \0 */
#define LINEMAX   256          /* rough line size */

#define INIT        0          /* prior to curses, to die properly */

#define CYX       pyp=CY, pxp=CX
#define PYX       move(CY=pyp, CX=pxp)
/* all functions must return cursor to starting CY, CX coordinates */

#define E_SYNTAX    1          /* bc errors */
#define E_DIVBY0    2
#define E_EXPONENT  3
#define E_OVERFLOW  4
#define E_BCEXEC    5

struct stk_cell {
    char cell[STACKMAX + 1];
    struct stk_cell *link;
};

struct stk_cell *find();
char *pactok();                /* seems to be missing in 4.2BSD */
char *bitwise();
char *substivar();
char *numform();
char *getenv();
char *calloc(), *malloc();
char *ctime();
char *strcat(), *strncat(), *strcpy(), *strncpy();
char *gets();
FILE *fopen();
double atof();                    /* amortization variables */
long time(), lseek();
unsigned alarm();
int  go_away();
int  cdate();

#ifdef MAIN                    /* globals are defined in pac.c,
                                  declared as externs elsewhere */

FILE *Tf = NULL;
FILE *Dfp = NULL;
char *Home, Rcfile[LINEMAX];      /* pac environment */
int  Rcfd, Rcerr;

char *Cb[] = {
"pac          PANEL CALCULATOR                    ATOI    ",
"pac          PANEL CALCULATOR                    ATOI    "
};
char *Sb[] = {
"pac          PANEL CALCULATOR                    ATOI     CONVERSIONS  TO   ",
"pac          PANEL CALCULATOR                    ATOI          TOTAL        "
};
char *Titlq[TREQ];
char *Basq[BREQ];
char *Bb[] = {
"TAB bye  <CTRL> Exit Redraw File Postpend Klock Globals Loan Newconv Total  ",
"<CTRL> File Postpend Toggle_back  copy: a...t  up_down: A...S  transfer: T  ",
"file  write: hardcopy                                                       ",
"file  contd: hardcopy                                                       ",
"total write: hardtotal                                                      ",
"total contd: hardtotal                                                      ",
"amort write: hardamort                                                      ",
"new  value =                                                                ",
"new formula:                                                                "
};
char *Hardname =
"hardcopy                                                       ";
char *Totname =
"hardtotal                                                      ";
char *Amortname =
"hardamort                                                      ";

char Spreadbuf[PIPEMAX * 2];      /* expand user input for tokenizing */
char Tokbuf[PIPEMAX];             /* strcomp against key list */
char Mainbuf[PIPEMAX];            /* Mainbuf (result) from bc */
char Convbuf[PIPEMAX];            /* converter result from bc */
char Ubuf[PIPEMAX];               /* unrecognized tokens, to bc */
char Controlbuf[PIPEMAX];         /* recognized tokens effect control */
char *Tokp[PIPEMAX], *Last;       /* Spreadbuf pointers */
char Rebuf[PIPEMAX];              /* work buffers */
char Tmpbuf[PIPEMAX];
char Mop[PIPEMAX];
char Uwin_copy[(UBOT - UTOP + 1) * (URIGHT - ULEFT + 1)];

int Context   = INIT;             /* initial status values */
int Justify   = JUS_DFLT;
int Format    = FORM_DFLT;
int Stack     = STACK_DFLT;
int Hide      = 0;
int Dontsave  = 0;
int Clockstat = DISA;
int Show      = SHOW_DFLT;
int Autotime  = DISA;             /* no clock until user asks */
int Hf        = HF_DFLT;
int Ibase     = IB_DFLT;
int Obase     = OB_DFLT;
int Oldib     = IB_DFLT;
int Oldob     = OB_DFLT;
int Lastob    = OB_DFLT;
int Staybase  = DISA;
int Precision = PREC_DFLT;
int Edhelp    = ED_DFLT;
int Autoconv  = DISA;              /* Autoconverter feature */
int Convcount = CONVCOUNT;
int Convsel   = CONVSEL;
int Hardcopy  = DISA;
int Totcopy   = DISA;
int Painted   = FALSE;            /* don't push init vals on Stack */

double Amt;
double Years;
double Rate;
double Pymt;
double Showpymt;
double Intrst;
double I_mo;
double Months;

int Negative;
int Too_big;
int Has_dp;

char *Base_str = "$$23456789 bcdefx";  /* formatting aids */
char *Sp44 = "                                            ";
char *Sp34 = "                                  ";
char *Sp13 = "             ";
char *Fix32 = "00000000000000000000000000000000";
char *Emptycell = "  0                                 ";

char Separator = ' ';             /* must agree with FORM_DFLT */

int A_ret = 0;
int B_ret = 0;
int A_write[2], A_read[2];        /* main bc process pipes */
int Nread;                        /* main bc process globals */
int B_write[2], B_read[2];        /* bc converter process pipes */
int Hc = -1;                      /* Hardcopy file fd */
int Tc = -1;                      /* Total file fd */
int Tqlev = 0;
char *Thisyear;
int Do_conv, O_conv;              /* activate selected conv function */
int Convread;                     /* bc converter process */
int Topconv;                      /* top conversion of conv vindow */
char *Convhom;                    /* conv malloc pointer */
char Eq_buf[WINLINE];             /* new conv string input by user */
char K_buf[KEYSIZ];               /* 3 char new key input by user */
char L_buf[FROMTOSIZ];            /* new conv label input by user */
unsigned int Convhsiz;

                                  /* predefined user conversions */
char *Convlist[256][3] = {
{"km2      acre", "acr", "\\*247.1"},
{"gram avoir_oz", "av ", "\\*.035274"},
{"fahr. celsius", "c  ", "(\\-32)*.55555555555555555555555555555555"},
{"fluid_oz   cc", "cc ", "\\*29.573"},
{"inch       cm", "cm ", "\\*2.54"},
{"radian degree", "deg", "\\*57.295779513082321"},
{"celsius fahr.", "f  ", "\\*1.8+32"},
{"meter    foot", "ft ", "\\*3.2808"},
{"avoir_oz gram", "g  ", "\\*28.3495"},
{"liter  gallon", "gal", "\\*.26418"},
{"cm       inch", "in ", "\\*.3937"},
{"inversion    ", "inv", "1/\\"},
{"pound      kg", "kg ", "\\*.45359"},
{"mile       km", "km ", "\\*1.6093"},
{"acre      km2", "km2", "\\*.004046"},
{"kg      pound", "lb ", "\\*2.2046"},
{"gallon  liter", "lit", "\\*3.7854"},
{"foot    meter", "m  ", "\\*.3048"},
{"km       mile", "mi ", "\\*.6214"},
{"mile nautical", "nau", "\\*.8689784566"},
{"nautical mile", "nmi", "\\*1.150776515"},
{"cc   fluid_oz", "oz ", "\\*.03381"},
{"degree radian", "rad", "\\*.01745329251994329"},
{"sentinel     ", "|||", "1"}};        /* sentinel; do not remove */

int Status;                            /* exit for fatal */
int CX, CY;                            /* global cursor control */
char Onebuf[STACKMAX + 1];             /* temp store for 1 stack cell */
int Statopts;                          /* Status display window flag */
struct stk_cell Stk[STACKDEEP + 1];
int Bc_error;
int Trace;
int Tlev;
char Ttabs[] = {"\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"};
int (*Save_sig[8])();
char ierbuf[256];

#else

extern FILE *Tf;
extern FILE *Dfp;
extern char *Titlq[];
extern char *Basq[];
extern char *Cb[], *Sb[];
extern char *Bb[];
extern char Spreadbuf[];
extern char Tokbuf[];
extern char Controlbuf[];
extern char *Tokp[], *Last;
extern int Negative, Too_big, Has_dp;
extern char Rebuf[];
extern char Tmpbuf[];
extern char Mop[];
extern char Uwin_copy[];

extern int Context;
extern int Justify;
extern int Format, Stack, Hide, Dontsave;

extern int  Ibase, Obase, Oldib, Oldob, Lastob, Staybase, Precision;
extern int  Show, Clockstat, Autotime;
extern char *Base_str;
extern int  CX, CY;
extern char Ubuf[], Mainbuf[], Convbuf[];
extern char *Sp44, *Sp34, *Sp13, *Fix32, *Emptycell;
extern char Separator;
extern int  A_write[], A_read[], A_ret, Nread;
extern int  B_write[], B_read[], B_ret, Convread;
extern int  Status, Painted;
extern char *Home, Rcfile[];
extern int  Rcfd, Rcerr;
extern char Onebuf[STACKMAX + 1];
extern int  Hardcopy, Hc, Hf;
extern char *Hardname;
extern char *Amortname;
extern char *Totname;
extern int  Totcopy, Tc, Tqlev;
extern int  Autoconv;
extern int  Convcount;
extern int  Convsel;
extern int  Do_conv, O_conv;
extern int  Topconv;
extern char *Convlist[][3];
extern char *Convhom;
extern char *Thisyear;
extern char Eq_buf[];
extern char K_buf[], L_buf[];
extern unsigned int Convhsiz;
extern int  Statopts;
extern struct stk_cell Stk[];
extern int  Edhelp;
extern int  Bc_error;
extern int  Trace;
extern int  Tlev;
extern char Ttabs[];

extern double atof();
extern double Amt;
extern double Years;
extern double Rate;
extern double Pymt;
extern double Showpymt;
extern double Intrst;
extern double I_mo;
extern double Months;

extern int (*Save_sig[])();
extern char ierbuf[];

#endif
//E*O*F defs.h//

echo x - maps.h
cat > "maps.h" << '//E*O*F maps.h//'
/* maps.h */
/**********************************************************************
*    File Name     : maps.h
*    Function      : input character mapping to line edit functions
*    Author        : Istvan Mohos, 1987
***********************************************************************/

#ifdef AMORTMAP
static char loan_map[128] = {
/*   NL  ^A  ^B  ^C  ^D  ^E  ^F  ^G  ^H  ^I  ^J  ^K  ^L  ^M  ^N  ^O   */
      0,  0,  9, 10,  0,  1,  0,  0, 11,  7,  0,  0,  8,  0,  0,  0,  
/*   ^P  ^Q  ^R  ^S  ^T  ^U  ^V  ^W  ^X  ^Y  ^Z  ^[  ^|  ^]  ^`  ^/   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
/*   SP   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   */
     10,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  2,  0,  
/*    0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  0,  0,  0,  0,  0,  0,  
/*    @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
/*    P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  3,  0,  4,  0,  0,  
/*    `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
/*    p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~  DL  */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 11
};

static char date_map[128] = {
/*   NL  ^A  ^B  ^C  ^D  ^E  ^F  ^G  ^H  ^I  ^J  ^K  ^L  ^M  ^N  ^O   */
      0,  0,  0, 10,  0,  1,  0,  0, 11,  7,  0,  0,  0,  0,  0,  0,  
/*   ^P  ^Q  ^R  ^S  ^T  ^U  ^V  ^W  ^X  ^Y  ^Z  ^[  ^|  ^]  ^`  ^/   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
/*   SP   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   */
     10,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  2,  0,  2,  2,  
/*    0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  0,  0,  0,  0,  0,  
/*    @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   */
      0,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  
/*    P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  3,  0,  4,  0,  0,  
/*    `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   */
      0,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  
/*    p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~  DL   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  0,  0,  0,  0, 11
};

static char amort_map[128] = {
/*   NL  ^A  ^B  ^C  ^D  ^E  ^F  ^G  ^H  ^I  ^J  ^K  ^L  ^M  ^N  ^O   */
      0,  0,  0, 10,  0,  1,  0,  0, 11,  7,  0,  0,  0,  0,  0,  0,  
/*   ^P  ^Q  ^R  ^S  ^T  ^U  ^V  ^W  ^X  ^Y  ^Z  ^[  ^|  ^]  ^`  ^/   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
/*   SP   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   */
     10,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  2,  0,  2,  2,  2,  
/*    0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  0,  0,  2,  0,  0,  
/*    @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   */
      0,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  
/*    P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  3,  2,  4,  0,  2,  
/*    `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   */
      0,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  
/*    p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~  DL   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  0,  0,  0,  0, 11
};
#endif

#ifdef ATOIMAP
static char a_ed_map[128] = {
/*   NL  ^A  ^B  ^C  ^D  ^E  ^F  ^G  ^H  ^I  ^J  ^K  ^L  ^M  ^N  ^O   */
      0,  0,  0,  0,  0,  1,  0,  0, 11,  7,  0,  0,  0,  0,  0,  0,  
/*   ^P  ^Q  ^R  ^S  ^T  ^U  ^V  ^W  ^X  ^Y  ^Z  ^[  ^|  ^]  ^`  ^/   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
/*   SP   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   */
     10,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
/*    0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  0,  0,  0,  0,  0,  0,  
/*    @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   */
      0,  2,  2,  2,  2,  2,  2,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
/*    P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  3,  0,  4,  0,  0,  
/*    `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   */
      0,  2,  2,  2,  2,  2,  2,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
/*    p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~  DL   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 11
};

char *lotab[33] = {"asc NUL", "SOH  ^A", "STX  ^B", "ETX  ^C",
                   "EOT  ^D", "ENQ  ^E", "ACK  ^F", "BEL  ^G",
                   "BS   ^H", "HT   ^I", "LF   ^J", "VT   ^K",
                   "NP   ^L", "CR   ^M", "SO   ^N", "SI   ^O",
                   "DLE  ^P", "DC1  ^Q", "DC2  ^R", "DC3  ^S",
                   "DC4  ^T", "NAK  ^U", "SYN  ^V", "ETB  ^W",
                   "CAN  ^X", "EM   ^Y", "SUB  ^Z", "ESC  ^[",
                   "FS   ^|", "GS   ^]", "RS   ^`", "US   ^/",
                   "asc  SP"
};

#endif

#ifdef CONVMAP
static char c_ed_map[128] = {
/*   NL  ^A  ^B  ^C  ^D  ^E  ^F  ^G  ^H  ^I  ^J  ^K  ^L  ^M  ^N  ^O   */
      0,  0,  0, 10,  0,  1,  0,  0, 11,  7,  0,  0,  0,  0,  0,  0,  
/*   ^P  ^Q  ^R  ^S  ^T  ^U  ^V  ^W  ^X  ^Y  ^Z  ^[  ^|  ^]  ^`  ^/   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 20,  0,  0,  0,  0,  
/*   SP   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   */
      2,  0,  0,  0,  0,  2,  0,  0,  2,  2,  2,  2,  0,  2,  2,  2,  
/*    0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  0,  2, 20,  0, 19,  0,  
/*    @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   */
      0,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  
/*    P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  3,  2,  4,  2,  2,  
/*    `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   */
      0,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  
/*    p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~  DL   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  0,  0,  0,  0, 11
};

static char eq_ed_map[128] = {
/*   NL  ^A  ^B  ^C  ^D  ^E  ^F  ^G  ^H  ^I  ^J  ^K  ^L  ^M  ^N  ^O   */
      0,  0,  0, 10,  0,  1,  0,  0, 11,  7,  0,  0,  0,  0,  0,  0,  
/*   ^P  ^Q  ^R  ^S  ^T  ^U  ^V  ^W  ^X  ^Y  ^Z  ^[  ^|  ^]  ^`  ^/   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 20,  0,  0,  0,  0,  
/*   SP   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   */
      2,  0,  0,  0,  0,  2,  0,  0,  2,  2,  2,  2,  0,  2,  2,  2,  
/*    0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  0,  0, 20,  0, 19,  0,  
/*    @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
/*    P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  3,  2,  4,  2,  0,  
/*    `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
/*    p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~  DL   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 11
};

static char c_sel_map[128] = {
/*   NL  ^A  ^B  ^C  ^D  ^E  ^F  ^G  ^H  ^I  ^J  ^K  ^L  ^M  ^N  ^O   */
      0,  0,  0,  0,  0,  1,  0,  0, 11,  7,  0,  0,  0,  0,  0,  0,  
/*   ^P  ^Q  ^R  ^S  ^T  ^U  ^V  ^W  ^X  ^Y  ^Z  ^[  ^|  ^]  ^`  ^/   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
/*   SP   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
/*    0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
/*    @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   */
      0,  0,  0, 17,  0,  0,  0,  0,  0, 17,  0,  0,  0,  0,  0,  0,  
/*    P   Q  17   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
/*    `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   */
      0,  0,  0, 17,  0,  0,  0,  0,  0, 17,  0,  0,  0,  0,  0,  0,  
/*    p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~  DL   */
      0,  0, 17,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 11
};
#endif

#ifdef FILEMAP
static char f_ed_map[128] = {
/*   NL  ^A  ^B  ^C  ^D  ^E  ^F  ^G  ^H  ^I  ^J  ^K  ^L  ^M  ^N  ^O   */
      0,  0,  0, 10,  0,  1,  0,  0, 11,  7,  0,  0,  0,  0,  0,  0,  
/*   ^P  ^Q  ^R  ^S  ^T  ^U  ^V  ^W  ^X  ^Y  ^Z  ^[  ^|  ^]  ^`  ^/   */
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 20,  0,  0,  0,  0,  
/*   SP   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   */
     10,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  2,  0,  2,  2,  2,  
/*    0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  0, 20,  2, 19,  0,  
/*    @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   */
      0,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  
/*    P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  3,  2,  4,  0,  2,  
/*    `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   */
      0,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  
/*    p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~  DL   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  0,  0,  0,  0, 11
};
#endif

#ifdef PACMAP
static char cal_map[128] = {
/*   NL  ^A  ^B  ^C  ^D  ^E  ^F  ^G  ^H  ^I  ^J  ^K  ^L  ^M  ^N  ^O   */
      0, 17, 17, 10, 17,  1, 17, 17, 11,  7, 17, 17, 17, 17, 17, 17,  
#ifdef DESIGN
/*   ^P  ^Q  ^R  ^S  ^T  ^U  ^V  ^W  ^X  ^Y  ^Z  ^[  ^|  ^]  ^`  ^/   */
     17,  0, 18,  0, 17, 17,  0,  0, 17, 17,  0, 20,  0,  0,  0,  0,  
#else
     17,  0, 18, 17, 17,  0,  0,  0, 17,  0,  0, 20,  0,  0,  0,  0,  
#endif
/*   SP   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   */
      2,  2,  0,  2,  0,  2,  0,  2,  2,  2,  2,  2,  2,  2,  2,  2,  
/*    0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  0,  2, 20, 17, 19,  2,  
/*    @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   */
      0,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  
/*    P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  3,  2,  4,  2,  2,  
/*    `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   */
      0,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  
/*    p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~  DL  */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2, 13, 15, 14,  0, 16
};
#endif

#ifdef TOTALMAP
static char tot_map[128] = {
/*   NL  ^A  ^B  ^C  ^D  ^E  ^F  ^G  ^H  ^I  ^J  ^K  ^L  ^M  ^N  ^O   */
      0,  0,  0, 12,  0,  1, 12,  0, 11,  7,  0,  0,  0,  0,  0,  0,  
/*   ^P  ^Q  ^R  ^S  ^T  ^U  ^V  ^W  ^X  ^Y  ^Z  ^[  ^|  ^]  ^`  ^/   */
     12,  0,  0,  0,  8,  0,  0,  0,  0,  0,  0, 20,  0,  0,  0,  0,  
/*   SP   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   */
     10,  0,  0, 21,  0,  0,  0,  0,  0,  0,  2,  2,  0,  2,  2,  0,  
/*    0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?   */
      2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  0,  0, 20,  0, 19,  0,  
/*    @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   */
      2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,  
/*    P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _   */
     12, 12, 12, 12, 12,  0,  0,  0,  0,  0,  0,  3,  0,  4,  0,  0,  
/*    `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   */
      0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,  
/*    p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~  DL  */
     12, 12, 12, 12, 12,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 11
};
#endif

#ifdef INTERMAP
static char *hundred[] = {
       "",        "", "1100100", "10201",
       "1210", "400", "244",     "202",
       "144",  "121", "100",     "91",
       "84",   "79",  "72",      "6A",
       "64" };

static char *sixteen[] = {
       "",    "",   "10000",  "121",
       "100", "31", "24",     "22",
       "20",  "17", "16",     "15",
       "14",  "13", "12",     "11",
       "10" };

 static char *factab[] = {
/*0*/ "x 0",
/*1*/ "x 1",
/*2*/ "x 2",
/*3*/ "x 6",
/*4*/ "x 18",
/*5*/ "x 78",
/*6*/ "x 2D0",
/*7*/ "x 13B0",
/*8*/ "x 9D80",
/*9*/ "x 58980",
/*10*/ "x 375F00",
/*11*/ "x 2611500",
/*12*/ "x 1C8CFC00",
/*13*/ "x 17328CC00",
/*14*/ "x 144C3B2800",
/*15*/ "x 13077775800",
/*16*/ "x 130777758000",
/*17*/ "x 1437EEECD8000",
/*18*/ "x 16BEECCA730000",
/*19*/ "x 1B02B9306890000",
/*20*/ "x 21C3677C82B40000",
/*21*/ "x 2C5077D36B8C40000",
/*22*/ "x 3CEEA4C2B3E0D80000",
/*23*/ "x 57970CD7E2933680000",
/*24*/ "x 83629343D3DCD1C00000",
/*25*/ "x CD4A0619FB0907BC00000",
/*26*/ "x 14D9849EA37EEAC91800000",
/*27*/ "x 232F0FCBB3E62C3358800000",
/*28*/ "x 3D925BA47AD2CD59DAE000000",
/*29*/ "x 6F99461A1E9E1432DCB6000000",
/*30*/ "x D13F6370F96865DF5DD54000000",
/*31*/ "x 1956AD0AAE33A4560C5CD2C000000",
/*32*/ "x 32AD5A155C6748AC18B9A580000000",
/*33*/ "x 688589CC0E9505E2F2FEE5580000000",
/*34*/ "x DE1BC4D19EFCAC82445DA75B00000000",
/*35*/ "x FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
};
#endif
//E*O*F maps.h//

echo Possible errors detected by \'wc\' [hopefully none]:
temp=/tmp/shar$$
trap "rm -f $temp; exit" 0 1 2 3 15
cat > $temp <<\!!!
     647    4247   27033 pac.man
       9      69     389 README
     101     249    1952 makefile
     374    1787   13508 defs.h
     279    3077   14314 maps.h
    1410    9429   57196 total
!!!
wc  pac.man README makefile defs.h maps.h | sed 's=[^ ]*/==' | diff -b $temp -
exit 0
-- 
        Istvan Mohos
        {ihnp4,decvax,allegra}!philabs!hhb!istvan
        HHB Systems 1000 Wyckoff Ave. Mahwah NJ 07430 201-848-8000
====================================================================