echo x - COPYING
sed '/^X/s///' > COPYING << '/'
X
X		    GNU GENERAL PUBLIC LICENSE
X		       Version 2, June 1991
X
X Copyright (C) 1989, 1991 Free Software Foundation, Inc.
X                          675 Mass Ave, Cambridge, MA 02139, USA
X Everyone is permitted to copy and distribute verbatim copies
X of this license document, but changing it is not allowed.
X
X			    Preamble
X
X  The licenses for most software are designed to take away your
Xfreedom to share and change it.  By contrast, the GNU General Public
XLicense is intended to guarantee your freedom to share and change free
Xsoftware--to make sure the software is free for all its users.  This
XGeneral Public License applies to most of the Free Software
XFoundation's software and to any other program whose authors commit to
Xusing it.  (Some other Free Software Foundation software is covered by
Xthe GNU Library General Public License instead.)  You can apply it to
Xyour programs, too.
X
X  When we speak of free software, we are referring to freedom, not
Xprice.  Our General Public Licenses are designed to make sure that you
Xhave the freedom to distribute copies of free software (and charge for
Xthis service if you wish), that you receive source code or can get it
Xif you want it, that you can change the software or use pieces of it
Xin new free programs; and that you know you can do these things.
X
X  To protect your rights, we need to make restrictions that forbid
Xanyone to deny you these rights or to ask you to surrender the rights.
XThese restrictions translate to certain responsibilities for you if you
Xdistribute copies of the software, or if you modify it.
X
X  For example, if you distribute copies of such a program, whether
Xgratis or for a fee, you must give the recipients all the rights that
Xyou have.  You must make sure that they, too, receive or can get the
Xsource code.  And you must show them these terms so they know their
Xrights.
X
X  We protect your rights with two steps: (1) copyright the software, and
X(2) offer you this license which gives you legal permission to copy,
Xdistribute and/or modify the software.
X
X  Also, for each author's protection and ours, we want to make certain
Xthat everyone understands that there is no warranty for this free
Xsoftware.  If the software is modified by someone else and passed on, we
Xwant its recipients to know that what they have is not the original, so
Xthat any problems introduced by others will not reflect on the original
Xauthors' reputations.
X
X  Finally, any free program is threatened constantly by software
Xpatents.  We wish to avoid the danger that redistributors of a free
Xprogram will individually obtain patent licenses, in effect making the
Xprogram proprietary.  To prevent this, we have made it clear that any
Xpatent must be licensed for everyone's free use or not licensed at all.
X
X  The precise terms and conditions for copying, distribution and
Xmodification follow.
X
X		    GNU GENERAL PUBLIC LICENSE
X   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
X
X  0. This License applies to any program or other work which contains
Xa notice placed by the copyright holder saying it may be distributed
Xunder the terms of this General Public License.  The "Program", below,
Xrefers to any such program or work, and a "work based on the Program"
Xmeans either the Program or any derivative work under copyright law:
Xthat is to say, a work containing the Program or a portion of it,
Xeither verbatim or with modifications and/or translated into another
Xlanguage.  (Hereinafter, translation is included without limitation in
Xthe term "modification".)  Each licensee is addressed as "you".
X
XActivities other than copying, distribution and modification are not
Xcovered by this License; they are outside its scope.  The act of
Xrunning the Program is not restricted, and the output from the Program
Xis covered only if its contents constitute a work based on the
XProgram (independent of having been made by running the Program).
XWhether that is true depends on what the Program does.
X
X  1. You may copy and distribute verbatim copies of the Program's
Xsource code as you receive it, in any medium, provided that you
Xconspicuously and appropriately publish on each copy an appropriate
Xcopyright notice and disclaimer of warranty; keep intact all the
Xnotices that refer to this License and to the absence of any warranty;
Xand give any other recipients of the Program a copy of this License
Xalong with the Program.
X
XYou may charge a fee for the physical act of transferring a copy, and
Xyou may at your option offer warranty protection in exchange for a fee.
X
X  2. You may modify your copy or copies of the Program or any portion
Xof it, thus forming a work based on the Program, and copy and
Xdistribute such modifications or work under the terms of Section 1
Xabove, provided that you also meet all of these conditions:
X
X    a) You must cause the modified files to carry prominent notices
X    stating that you changed the files and the date of any change.
X
X    b) You must cause any work that you distribute or publish, that in
X    whole or in part contains or is derived from the Program or any
X    part thereof, to be licensed as a whole at no charge to all third
X    parties under the terms of this License.
X
X    c) If the modified program normally reads commands interactively
X    when run, you must cause it, when started running for such
X    interactive use in the most ordinary way, to print or display an
X    announcement including an appropriate copyright notice and a
X    notice that there is no warranty (or else, saying that you provide
X    a warranty) and that users may redistribute the program under
X    these conditions, and telling the user how to view a copy of this
X    License.  (Exception: if the Program itself is interactive but
X    does not normally print such an announcement, your work based on
X    the Program is not required to print an announcement.)
X
XThese requirements apply to the modified work as a whole.  If
Xidentifiable sections of that work are not derived from the Program,
Xand can be reasonably considered independent and separate works in
Xthemselves, then this License, and its terms, do not apply to those
Xsections when you distribute them as separate works.  But when you
Xdistribute the same sections as part of a whole which is a work based
Xon the Program, the distribution of the whole must be on the terms of
Xthis License, whose permissions for other licensees extend to the
Xentire whole, and thus to each and every part regardless of who wrote it.
X
XThus, it is not the intent of this section to claim rights or contest
Xyour rights to work written entirely by you; rather, the intent is to
Xexercise the right to control the distribution of derivative or
Xcollective works based on the Program.
X
XIn addition, mere aggregation of another work not based on the Program
Xwith the Program (or with a work based on the Program) on a volume of
Xa storage or distribution medium does not bring the other work under
Xthe scope of this License.
X
X  3. You may copy and distribute the Program (or a work based on it,
Xunder Section 2) in object code or executable form under the terms of
XSections 1 and 2 above provided that you also do one of the following:
X
X    a) Accompany it with the complete corresponding machine-readable
X    source code, which must be distributed under the terms of Sections
X    1 and 2 above on a medium customarily used for software interchange; or,
X
X    b) Accompany it with a written offer, valid for at least three
X    years, to give any third party, for a charge no more than your
X    cost of physically performing source distribution, a complete
X    machine-readable copy of the corresponding source code, to be
X    distributed under the terms of Sections 1 and 2 above on a medium
X    customarily used for software interchange; or,
X
X    c) Accompany it with the information you received as to the offer
X    to distribute corresponding source code.  (This alternative is
X    allowed only for noncommercial distribution and only if you
X    received the program in object code or executable form with such
X    an offer, in accord with Subsection b above.)
X
XThe source code for a work means the preferred form of the work for
Xmaking modifications to it.  For an executable work, complete source
Xcode means all the source code for all modules it contains, plus any
Xassociated interface definition files, plus the scripts used to
Xcontrol compilation and installation of the executable.  However, as a
Xspecial exception, the source code distributed need not include
Xanything that is normally distributed (in either source or binary
Xform) with the major components (compiler, kernel, and so on) of the
Xoperating system on which the executable runs, unless that component
Xitself accompanies the executable.
X
XIf distribution of executable or object code is made by offering
Xaccess to copy from a designated place, then offering equivalent
Xaccess to copy the source code from the same place counts as
Xdistribution of the source code, even though third parties are not
Xcompelled to copy the source along with the object code.
X
X  4. You may not copy, modify, sublicense, or distribute the Program
Xexcept as expressly provided under this License.  Any attempt
Xotherwise to copy, modify, sublicense or distribute the Program is
Xvoid, and will automatically terminate your rights under this License.
XHowever, parties who have received copies, or rights, from you under
Xthis License will not have their licenses terminated so long as such
Xparties remain in full compliance.
X
X  5. You are not required to accept this License, since you have not
Xsigned it.  However, nothing else grants you permission to modify or
Xdistribute the Program or its derivative works.  These actions are
Xprohibited by law if you do not accept this License.  Therefore, by
Xmodifying or distributing the Program (or any work based on the
XProgram), you indicate your acceptance of this License to do so, and
Xall its terms and conditions for copying, distributing or modifying
Xthe Program or works based on it.
X
X  6. Each time you redistribute the Program (or any work based on the
XProgram), the recipient automatically receives a license from the
Xoriginal licensor to copy, distribute or modify the Program subject to
Xthese terms and conditions.  You may not impose any further
Xrestrictions on the recipients' exercise of the rights granted herein.
XYou are not responsible for enforcing compliance by third parties to
Xthis License.
X
X  7. If, as a consequence of a court judgment or allegation of patent
Xinfringement or for any other reason (not limited to patent issues),
Xconditions are imposed on you (whether by court order, agreement or
Xotherwise) that contradict the conditions of this License, they do not
Xexcuse you from the conditions of this License.  If you cannot
Xdistribute so as to satisfy simultaneously your obligations under this
XLicense and any other pertinent obligations, then as a consequence you
Xmay not distribute the Program at all.  For example, if a patent
Xlicense would not permit royalty-free redistribution of the Program by
Xall those who receive copies directly or indirectly through you, then
Xthe only way you could satisfy both it and this License would be to
Xrefrain entirely from distribution of the Program.
X
XIf any portion of this section is held invalid or unenforceable under
Xany particular circumstance, the balance of the section is intended to
Xapply and the section as a whole is intended to apply in other
Xcircumstances.
X
XIt is not the purpose of this section to induce you to infringe any
Xpatents or other property right claims or to contest validity of any
Xsuch claims; this section has the sole purpose of protecting the
Xintegrity of the free software distribution system, which is
Ximplemented by public license practices.  Many people have made
Xgenerous contributions to the wide range of software distributed
Xthrough that system in reliance on consistent application of that
Xsystem; it is up to the author/donor to decide if he or she is willing
Xto distribute software through any other system and a licensee cannot
Ximpose that choice.
X
XThis section is intended to make thoroughly clear what is believed to
Xbe a consequence of the rest of this License.
X
X  8. If the distribution and/or use of the Program is restricted in
Xcertain countries either by patents or by copyrighted interfaces, the
Xoriginal copyright holder who places the Program under this License
Xmay add an explicit geographical distribution limitation excluding
Xthose countries, so that distribution is permitted only in or among
Xcountries not thus excluded.  In such case, this License incorporates
Xthe limitation as if written in the body of this License.
X
X  9. The Free Software Foundation may publish revised and/or new versions
Xof the General Public License from time to time.  Such new versions will
Xbe similar in spirit to the present version, but may differ in detail to
Xaddress new problems or concerns.
X
XEach version is given a distinguishing version number.  If the Program
Xspecifies a version number of this License which applies to it and "any
Xlater version", you have the option of following the terms and conditions
Xeither of that version or of any later version published by the Free
XSoftware Foundation.  If the Program does not specify a version number of
Xthis License, you may choose any version ever published by the Free Software
XFoundation.
X
X  10. If you wish to incorporate parts of the Program into other free
Xprograms whose distribution conditions are different, write to the author
Xto ask for permission.  For software which is copyrighted by the Free
XSoftware Foundation, write to the Free Software Foundation; we sometimes
Xmake exceptions for this.  Our decision will be guided by the two goals
Xof preserving the free status of all derivatives of our free software and
Xof promoting the sharing and reuse of software generally.
X
X			    NO WARRANTY
X
X  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
XFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
XOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
XPROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
XOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
XMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
XTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
XPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
XREPAIR OR CORRECTION.
X
X  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
XWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
XREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
XINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
XOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
XTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
XYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
XPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
XPOSSIBILITY OF SUCH DAMAGES.
X
X		     END OF TERMS AND CONDITIONS
X
X	Appendix: How to Apply These Terms to Your New Programs
X
X  If you develop a new program, and you want it to be of the greatest
Xpossible use to the public, the best way to achieve this is to make it
Xfree software which everyone can redistribute and change under these terms.
X
X  To do so, attach the following notices to the program.  It is safest
Xto attach them to the start of each source file to most effectively
Xconvey the exclusion of warranty; and each file should have at least
Xthe "copyright" line and a pointer to where the full notice is found.
X
X    <one line to give the program's name and a brief idea of what it does.>
X    Copyright (C) 19yy  <name of author>
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License, or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; if not, write to the Free Software
X    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
XAlso add information on how to contact you by electronic and paper mail.
X
XIf the program is interactive, make it output a short notice like this
Xwhen it starts in an interactive mode:
X
X    Gnomovision version 69, Copyright (C) 19yy name of author
X    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
X    This is free software, and you are welcome to redistribute it
X    under certain conditions; type `show c' for details.
X
XThe hypothetical commands `show w' and `show c' should show the appropriate
Xparts of the General Public License.  Of course, the commands you use may
Xbe called something other than `show w' and `show c'; they could even be
Xmouse-clicks or menu items--whatever suits your program.
X
XYou should also get your employer (if you work as a programmer) or your
Xschool, if any, to sign a "copyright disclaimer" for the program, if
Xnecessary.  Here is a sample; alter the names:
X
X  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
X  `Gnomovision' (which makes passes at compilers) written by James Hacker.
X
X  <signature of Ty Coon>, 1 April 1989
X  Ty Coon, President of Vice
X
XThis General Public License does not permit incorporating your program into
Xproprietary programs.  If your program is a subroutine library, you may
Xconsider it more useful to permit linking proprietary applications with the
Xlibrary.  If this is what you want to do, use the GNU Library General
XPublic License instead of this License.
X
/
echo x - Install
sed '/^X/s///' > Install << '/'
X
XINSTALLATION
X------------
X
X  a) Make sure you have a working vsprintf and vfprintf in your library.
X     (If your system does not have them in your library, you can get
X      a copy of the BSD4.3-tahoe stdio that is freely copyable.  It
X      is available from uunet.uu.net.  For Minix systems, a vsprintf.c
X      is included.)
X
X  b) EDIT the Makefile for the correct definitions.  Read the comments in
X     the the Makefile and make the changes needed.  (A "full" explanation
X     of all the C defines are given at the end of this file.)  If your are
X     on a MINIX machine, make sure the correct definitions for $O, $CFLAGS,
X     and $LDFLAGS are uncommented in the makefile.
X     NOTE: "configure" is a shell scritp that is automatically run by make
X     to configure the bc source to your operating system.  You should still
X     look at the Makefile.
X
X  d) "make derived"  (If you need it.)
X     The distribution contains the files bc.c.dist y.tab.h.dist and
X     scan.c.dist.  These are provided so that you do not need a working 
X     yacc and flex (lex).  This program was designed to work with the free
X     programs byacc (berkeley yacc) and flex.  It should work with other
X     versions of yacc and lex, but it is not guaranteed to work.
X
X     If you do not have yacc or flex, give the command to get the *.dist
X     files to their proper place.  This insures that make will not try
X     to make these derived files from their original source.  Use the
X     "make derived" before trying to compile bc.
X
X  e) "make"
X	compiles bc
X
X  f) "make install"
X	installs bc and libmath.b (if needed) in their directories.
X
X  g) Use bc!
X
X
XDEFINES
X-------
X
X  The following the are some defines that are automatically generated
X  by the configure script.  In most cases, you do not need to deal
X  with them.  The configure script is automatically run by make.
X
X    BC_MATH_FILE=$(LIBFILE)
X 	This defines the location of the math library file.  If you 
X 	use this definition, the math library is read from LIBFILE.
X 	If you do not define this, bc will include a "compiled" form
X 	of the math library with the executable and will not need to
X 	read any external file if the -l flag is given.
X
X    VARARGS
X        The variable args mechanism is assumed to be ANSI stdarg.
X        Use -DVARARGS for Ultrix, SUN-OS, BSD, and other UNIX versions that
X        use traditional varargs,
X        For MINIX and ANSI compilers (gcc et al) do NOT use -DVARARGS.
X
X    NO_LIMITS
X        Use this for systems without the /usr/include/limits.h file.
X        (BSD4.3 is one.) You should check the values of INT_MAX and 
X        LONG_MAX in the file const.h if you use -DNO_LIMITS.
X
X    NO_UNISTD
X        Use this for systems without /usr/include/unistd.h.
X
X    -DNO_STDLIB
X        Use this for systems without /usr/include/stdlib.h.
X
X    STRINGS_H
X	Include the file <strings.h> instead of <string.h>.
X
X
X  The following defines may need to be added by hand to the Makefile if
X  you want them.  They are not generated by the configure script.
X 
X    -DOLD_EQ_OP
X 	Causes bc to recognize =<op> as the same as <op>=.  The =<op> is
X        the old style.  It makes "a =- 1" ambiguous.  With OLD_EQ_OP defined
X        it is equivalent to "a -= 1" and with OLD_EQ_OP undefined it is
X        equivalent to "a = -1".
X
X    -DSMALL_BUF
X        Use this IF you are using flex AND you have a systems with limited
X        memory space.  If you use this, you should also use -DBC_MATH_FILE.
X        This is "standard" for MINIX systems running on the IBM_PC.
X
X    -DSHORTNAMES 
X	If your compiler keeps a limited number of characters from names,
X	you should include this define.  It is needed for the K&R compiler
X	on MINIX and is automatically added for MINIX on the IBM_PC.
X
X    -DDEBUG=n
X 	Compile the debugging code.  The higher the number, the more
X 	debugging code.  The degugging code is not very good.
X
X
XPOSSIBLE PROBLEMS
X-----------------
X
X  There might be some possible problems compiling due to different
X  versions of the header files.  scan.c may require to edit out some
X  definitions of malloc and free.  Others should compile with the
X  correct set of defines.
X
/
echo x - Makefile
sed '/^X/s///' > Makefile << '/'
X# Makefile for bc
X
X# A makefile for bc.  This is part of the bc/sbc distribution.
X#
X#
X#  Make sure these have the correct directories for your machine.
X#
X#  LIBDIR and BINDIR are where bc and libmath.b will be put.
X#
XPREFIX = /usr/local
XLIBDIR = $(PREFIX)/lib
XBINDIR = $(PREFIX)/bin
XMANDIR = $(PREFIX)/man/man1
X#
X#  INCLUDE is the directory from where header files are included.
X#
XINCLUDE = /usr/include
X#
X# This is the name of the library file, if needed.  This definition should
X# not be deleted.
X#
XLIBFILE = $(LIBDIR)/libmath.b
X#
X# Programs definitions for use by make.
X#
XSHELL = /bin/sh
XYACC = yacc
X#YACC = bison -y
XLEX = flex -I8
X#LEX = lex
XCC = cc
XUUENCODE = uue
XMAKE = make
X#
X#  The following are the standard definitions.  For MINIX PC, you need to
X#  comment these out and select one of the MINIX PC definition sets.
X#  Other configuration defines are generated automatically.  See the 
X#  README file for more details.
X#
X#O=o
X#CFLAGS = -O -D_POSIX_SOURCE
X#LDFLAGS = 
X#
X# For the PC version of MINIX (K&R compiler), use the following lines.
X# 
X#O=o
X#CFLAGS = -O -D_POSIX_SOURCE
X#LDFLAGS = -i
X#
X# For the PC version of MINIX (ANSI compiler), use the following lines:
X# 
XO=o
XCFLAGS = -m -D_POSIX_SOURCE
XLDFLAGS = -i
X#
X#
XOFILES = scan.$O util.$O main.$O number.$O storage.$O load.$O execute.$O 
X#
XDISTFILES = COPYING Makefile Install bc.y bcdefs.h const.h version.h \
X            execute.c global.c global.h load.c main.c number.c storage.c \
X	    number.h proto.h scan.l util.c vfprintf.c README bc.1 sbc.y \
X	    fix_math.h math.h libmath.b configure ChangeLog 
X#
XSUBDIRS = Examples Test
X#
XEXTRAFILES = bc.c.dist scan.c.dist y.tab.h.dist
X#
X
X
Xall: bc
Xbc: $& config.h bc.$O $(OFILES) global.$O
X	@rm -rf bc
X	@echo Start linking bc
X	$(CC) -o bc $(LDFLAGS) bc.$O $(OFILES) global.$O >/dev/null
X	@echo bc done.
X
Xsbc: sbc.$O $(OFILES) global.$O
X	$(CC) -o sbc $(LDFLAGS) sbc.$O $(OFILES) global.$O
X
Xconfig.h:
X	./configure $(INCLUDE) $(LIBFILE) $(CC)
X
Xmath.h: libmath.b
X	$(MAKE) fbc
X	./fbc -c libmath.b </dev/null >math.h
X	./fix_math.h
X	rm -f ./fbc
X
Xfbc: $(OFILES) bc.$O
X	echo \"\" > math.h
X	$(CC) -c $(CFLAGS) global.c
X	$(CC) -o fbc $(LDFLAGS) bc.$O $(OFILES) global.$O
X
Xinstall : bc libmath.b config.h
X	rm -f $(BINDIR)/bc
X	cp bc $(BINDIR)
X	chmod 555 $(BINDIR)/bc
X	if grep -s BC_MATH_FILE config.h; then rm -f $(LIBDIR)/libmath.b; \
X	cp libmath.b $(LIBDIR); chmod 444 $(LIBDIR)/libmath.b; else true; fi
X	cp bc.1 $(MANDIR)
X
Xdist: $(EXTRAFILES)
X	OF=`sed -n 's/.*\([0-9][0-9]*\.[0-9][0-9]*\).*/bc-\1/p' version.h` \
X	; rm -rf $$OF \
X	; mkdir $$OF \
X	; cd $$OF \
X	; for file in $(DISTFILES) $(EXTRAFILES); do ln ../$$file; done \
X	; mkdir $(SUBDIRS) \
X	; for dir in $(SUBDIRS); do cp ../$$dir/* $$dir; done \
X	; cd .. \
X	; tar cf $$OF.tar $$OF \
X	; compress $$OF.tar \
X	; rm -rf $$OF
X
Xshars: dist
X	OF=`sed -n 's/.*\([0-9][0-9]*\.[0-9][0-9]*\).*/bc-\1/p' version.h` \
X	; $(UUENCODE) $$OF.tar.Z - > $$OF.uue \
X	; split -775 $$OF.uue $$OF-u. \
X	; for file in $$OF-u.a?; do shar $$file > $$file.sh; done
X
Xminixdist: $(EXTRAFILES)
X	OF=`sed -n 's/.*\([0-9][0-9]*\.[0-9][0-9]*\).*/bc-\1/p' version.h` \
X	; rm -rf $$OF \
X	; mkdir $$OF \
X	; cd $$OF \
X	; for file in $(DISTFILES) $(EXTRAFILES); do ln ../$$file; done \
X	; cd .. \
X	; tar cf $$OF.tar $$OF \
X	; compress -b13 $$OF.tar \
X	; rm -rf $$OF
X
Xminixshars: minixdist
X	OF=`sed -n 's/.*\([0-9][0-9]*\.[0-9][0-9]*\).*/bc-\1/p' version.h` \
X	; $(UUENCODE) $$OF.tar.Z - > $$OF.uue \
X	; split -775 $$OF.uue $$OF-u. \
X	; for file in $$OF-u.a?; do shar $$file > $$file.sh; done
X
Xcsrshars: $(EXTRAFILES)
X	findsrc -dy $(DISTFILES) $(EXTRAFILES) $(SUBDIRS) | \
X	makekit -npart -oMANIFEST
X
Xbc.c.dist: bc.c
X	cp bc.c bc.c.dist
X
Xscan.c.dist: scan.c
X	cp scan.c scan.c.dist
X
Xy.tab.h.dist: y.tab.h
X	cp y.tab.h y.tab.h.dist
X
Xextra:  $(EXTRAFILES)
X
X# assumes that the extra files exist!
Xderived:
X	cp bc.c.dist bc.c
X	cp scan.c.dist scan.c
X	cp y.tab.h.dist y.tab.h
X
Xclean:
X	@rm -f *.o *.s *.bak core bc
X
Xdistclean: clean
X	rm -f scan.c y.tab.h bc.c sbc.c bc sbc bc-dist* config.h  bc-*.* \
X	part?? MANIFEST*
X
Xrealclean: distclean
X	rm -rf *.dist
X
Xscan.c: scan.l
X	$(LEX) scan.l
X	mv lex.yy.c scan.c
X
Xy.tab.h bc.c: bc.y
X	@echo "expect 1 shift/reduce conflict"
X	$(YACC) -d bc.y
X	mv y.tab.c bc.c
X
Xsbc.c: sbc.y
X	$(YACC) -d sbc.y
X	mv y.tab.c sbc.c
X
Xglobal.$O: bcdefs.h global.h math.h
Xbc.$O:	bcdefs.h global.h 
Xexecute.$O: bcdefs.h global.h
Xload.$O: bcdefs.h global.h 
Xmain.$O: bcdefs.h global.h version.h
Xnumber.$O: bcdefs.h Makefile
Xsbc.$O: bcdefs.h global.h 
Xscan.$O: y.tab.h bcdefs.h global.h
Xstorage.$O: bcdefs.h global.h
Xutil.$O: bcdefs.h global.h version.h
X
Xbcdefs.h: number.h const.h config.h
X	touch bcdefs.h
/
echo x - README
sed '/^X/s///' > README << '/'
XProgram:  GNU bc
XAuthor:   Philip A. Nelson
XE-mail:   phil@cs.wwu.edu
XOS:       UNIX (BSD, System V, MINIX, POSIX)
XCopying:  GNU GPL version 2
XCopyright holder: Free Software Foundation, Inc.
XVersion:  bc version 1.01
XRequired: vsprintf and vfprintf routines.
XMachines: It has been compiled and run on the following environments:
X	BSD4.3 (VAX 11)
X	MINIX 1.5 (IBM PC, both K&R and ANSI compilers)
X	MINIX 1.5 (pc532)
X	SUN-OS 4.1 (SUN 3 and SUN 4)
X	SVR3V5 (Motorola 68K)
X	SVR3.2 (3B2)
X	SVR4.0.2 (a 386 box)
X	ULTRIX 4.1 (DEC 5000)
X	UTS (Amdahl)
X
Xbc is an arbitrary precision numeric processing language.  Syntax is
Xsimilar to C, but differs in many substantial areas.  It supports
Xinteractive execution of statements.  bc is a utility included in the
XPOSIX P1003.2/D11 draft standard.  
X
XThis version was written to be a POSIX compliant bc processor with
Xseveral extensions to the draft standard.  Option flags are available
Xto cause warning or rejection of the extensions to the POSIX standard.
XFor those who want only POSIX bc with no extensions, a grammar is
Xprovided for exactly the language described in the POSIX document.
XThe grammar (sbc.y) comes from the POSIX document.  The Makefile
Xcontains rules to make sbc.  (for Standard BC)
X
XSince the POSIX document does not specify how bc must be implemented,
Xthis version does not use the historical method of having bc be a
Xcompiler for the dc calculator.  This version has a single executable
Xthat both compiles the language and runs the a resulting "byte code".
XThe "byte code" is NOT the dc language.
X
XAlso, included in the initial distribution is the library file
Xvfprintf.c for MINIX systems.  My minix 1.5 did not have this file.
XAlso, you should verify that vsprintf.c works correctly on your
Xsystem.
X
XThe extensions add some features I think are missing.  The major
Xchanges and additions for bc are (a) names are allowed to be full
Xidentifiers ([a-z][a-z0-9_]*), (b) addition of the &&, ||, and !
Xoperators, (c) allowing comparison and boolean operations in any
Xexpression, (d) addition of an else clause to the if statement, (e)
Xaddition of a new standard function "read()" that reads a number from
Xthe standard input under program control, (f) passing of arrays as
Xparameters by variable, (g) addition of the "halt" statement that is
Xan executable statement unlike the quit (i.e.  "if (1 == 0) quit" will
Xhalt bc but "if (1 == 0) halt" will not halt bc.), and (h) the
Xaddition of the special variable "last" that is assigned the value of
Xeach print as the number is printed.
/
echo x - bc.1
sed '/^X/s///' > bc.1 << '/'
X.\"
X.\" bc.1 - the *roff document processor source for the bc manual
X.\"
X.\" This file is part of bc written for MINIX.
X.\" Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X.\"
X.\" This program is free software; you can redistribute it and/or modify
X.\" it under the terms of the GNU General Public License as published by
X.\" the Free Software Foundation; either version 2 of the License , or
X.\" (at your option) any later version.
X.\"
X.\" This program is distributed in the hope that it will be useful,
X.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
X.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X.\" GNU General Public License for more details.
X.\"
X.\" You should have received a copy of the GNU General Public License
X.\" along with this program; see the file COPYING.  If not, write to
X.\" the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X.\"
X.\" You may contact the author by:
X.\" e-mail: phil@cs.wwu.edu
X.\" us-mail: Philip A. Nelson
X.\" Computer Science Department, 9062
X.\" Western Washington University
X.\" Bellingham, WA 98226-9062
X.\"
X.\"
X.TH bc 1 .\" "Command Manual" v1.02 "Feb 3, 1992"
X.SH NAME
Xbc - An arbitrary precision calculator language
X.SH SYNTAX
X\fBbc\fR [ \fB-lws\fR ] [ \fI file ...\fR ]
X.SH VERSION
XThis man page documents GNU bc version 1.02.
X.SH DESCRIPTION
X\fBbc\fR is a language that supports arbitrary precision numbers
Xwith interactive execution of statements.  There are some similarities
Xin the syntax to the C programming language. 
XA standard math library is available by command line option.
XIf requested, the math library is defined before processing any files.
X\fBbc\fR starts by processing code from all the files listed
Xon the command line in the order listed.  After all files have been
Xprocessed, \fBbc\fR reads from the standard input.  All code is
Xexecuted as it is read.  (If a file contains a command to halt the
Xprocessor, \fBbc\fR will never read from the standard input.)
X.PP
XThis version of \fBbc\fR contains several extensions beyond
Xtraditional \fBbc\fR implementations and the POSIX draft standard.
XCommand line options can cause these extensions to print a warning 
Xor to be rejected.  This 
Xdocument describes the language accepted by this processor.
XExtensions will be identified as such.
X.SS OPTIONS
X.IP -l
XDefine the standard math library.
X.IP -w
XGive warnings for extensions to POSIX \fBbc\fR.
X.IP -s
XProcess exactly the POSIX \fBbc\fR language.
X.SS NUMBERS
XThe most basic element in \fBbc\fR is the number.  Numbers are
Xarbitrary precision numbers.  This precision is both in the integer
Xpart and the fractional part.  All numbers are represented internally
Xin decimal and all computation is done in decimal.  (This version
Xtruncates results from divide and multiply operations.)  There are two
Xattributes of numbers, the length and the scale.  The length is the
Xtotal number of significant decimal digits in a number and the scale
Xis the total number of decimal digits after the decimal point.  For
Xexample:
X.nf
X.RS
X .000001 has a length of 6 and scale of 6.
X 1935.000 has a length of 7 and a scale of 3.
X.RE
X.fi
X.SS VARIABLES
XNumbers are stored in two types of variables, simple variables and
Xarrays.  Both simple variables and array variables are named.  Names
Xbegin with a letter followed by any number of letters, digits and
Xunderscores.  All letters must be lower case.  (Full alpha-numeric
Xnames are an extension. In POSIX \fBbc\fR all names are a single
Xlower case letter.)  The type of variable is clear by the context
Xbecause all array variable names will be followed by brackets ([]).
X.PP
XThere are four special variables, \fBscale, ibase, obase,\fR and
X\fBlast\fR.  \fBscale\fR defines how some operations use digits after the
Xdecimal point.  The default value of \fBscale\fR is 0. \fBibase\fR
Xand \fBobase\fR define the conversion base for input and output
Xnumbers.  The default for both input and output is base 10.
X\fBlast\fR (an extension) is a variable that has the value of the last
Xprinted number.  These will be discussed in further detail where
Xappropriate.  All of these variables may have values assigned to them
Xas well as used in expressions.
X.SS COMMENTS
XComments in \fBbc\fR start with the characters \fB/*\fR and end with
Xthe characters \fB*/\fR.  Comments may start anywhere and appear as a
Xsingle space in the input.  (This causes comments to delimit other
Xinput items.  For example, a comment can not be found in the middle of
Xa variable name.)  Comments include any newlines (end of line) between
Xthe start and the end of the comment.
X.SS EXPRESSIONS
XThe numbers are manipulated by expressions and statements.  Since
Xthe language was designed to be interactive, statements and expressions
Xare executed as soon as possible.  There is no "main" program.  Instead,
Xcode is executed as it is encountered.  (Functions, discussed in
Xdetail later, are defined when encountered.)
X.PP
XA simple expression is just a constant. \fBbc\fR converts constants
Xinto internal decimal numbers using the current input base, specified
Xby the variable \fBibase\fR. (There is an exception in functions.)
XThe legal values of \fBibase\fR are 2 through 16 (F).  Assigning a
Xvalue outside this range to \fBibase\fR will result in a value of 2
Xor 16.  Input numbers may contain the characters 0-9 and A-F. (Note:
XThey must be capitals.  Lower case letters are variable names.)
XSingle digit numbers always have the value of the digit regardless of
Xthe value of \fBibase\fR. (i.e. A = 10.)  For multi-digit numbers,
X\fBbc\fR changes all input digits greater or equal to ibase to the
Xvalue of \fBibase\fR-1.  This makes the number \fBFFF\fR always be
Xthe largest 3 digit number of the input base.
X.PP
XFull expressions are similar to many other high level languages.
XSince there is only one kind of number, there are no rules for mixing
Xtypes.  Instead, there are rules on the scale of expressions.  Every
Xexpression has a scale.  This is derived from the scale of original
Xnumbers, the operation performed and in many cases, the value of the
Xvariable \fBscale\fR. Legal values of the variable \fBscale\fR are
X0 to the maximum number representable by a C integer.
X.PP
XIn the following descriptions of legal expressions, "expr" refers to a
Xcomplete expression and "var" refers to a simple or an array variable.
XA simple variable is just a
X.RS
X\fIname\fR
X.RE
Xand an array variable is specified as
X.RS
X\fIname\fR[\fIexpr\fR]
X.RE
XUnless specifically
Xmentioned the scale of the result is the maximum scale of the
Xexpressions involved.
X.IP "- expr"
XThe result is the negation of the expression.
X.IP "++ var"
XThe variable is incremented by one and the new value is the result of
Xthe expression.
X.IP "-- var"
XThe variable
Xis decremented by one and the new value is the result of the
Xexpression.
X.IP "var ++"
X The result of the expression is the value of
Xthe variable and then the variable is incremented by one.
X.IP "var --"
XThe result of the expression is the value of the variable and then
Xthe variable is decremented by one.
X.IP "expr + expr"
XThe result of the expression is the sum of the two expressions.
X.IP "expr - expr"
XThe result of the expression is the difference of the two expressions.
X.IP "expr * expr"
XThe result of the expression is the product of the two expressions.
X.IP "expr / expr"
XThe result of the expression is the quotient of the two expressions.
XThe scale of the result is the value of the variable \fBscale\fR.
X.IP "expr % expr"
XThe result of the expression is the "remainder" and it is computed in the
Xfollowing way.  To compute a%b, first a/b is computed to \fBscale\fR
Xdigits.  That result is used to compute a-(a/b)*b to the scale of the
Xmaximum of \fBscale\fR+scale(b) and scale(a).  If \fBscale\fR is set
Xto zero and both expressions are integers this expression is the
Xinteger remainder function.
X.IP "expr ^ expr"
XThe result of the expression is the value of the first raised to the
Xsecond. The second expression must be an integer.  (If the second
Xexpression is not an integer, a warning is generated and the
Xexpression is truncated to get an integer value.)  The scale of the
Xresult is \fBscale\fR if the exponent is negative.  If the exponent
Xis positive the scale of the result is the minimum of the scale of the
Xfirst expression times the value of the exponent and the maximum of
X\fBscale\fR and the scale of the first expression.  (e.g. scale(a^b)
X= min(scale(a)*b, max( \fBscale,\fR scale(a))).)  It should be noted
Xthat expr^0 will always return the value of 1.
X.IP "( expr )"
XThis alters the standard precedence to force the evaluation of the
Xexpression.
X.IP "var = expr"
XThe variable is assigned the value of the expression.
X.IP "var <op>= expr"
XThis is equivalent to "var = var <op> expr" with the exception that
Xthe "var" part is evaluated only once.  This can make a difference if
X"var" is an array.
X.PP
X Relational expressions are a special kind of expression
Xthat always evaluate to 0 or 1, 0 if the relation is false and 1 if
Xthe relation is true.  These may appear in any legal expression.
X(POSIX bc requires that relational expressions are used only in if,
Xwhile, and for statements and that only one relational test may be
Xdone in them.)  The relational operators are
X.IP "expr1 < expr2"
XThe result is 1 if expr1 is strictly less than expr2.
X.IP "expr1 <= expr2"
XThe result is 1 if expr1 is less than or equal to expr2.
X.IP "expr1 > expr2"
XThe result is 1 if expr1 is strictly greater than expr2.
X.IP "expr1 >= expr2"
XThe result is 1 if expr1 is greater than or equal to expr2.
X.IP "expr1 == expr2"
XThe result is 1 if expr1 is equal to expr2.
X.IP "expr1 != expr2"
XThe result is 1 if expr1 is not equal to expr2.
X.PP
XBoolean operations are also legal.  (POSIX \fBbc\fR does NOT have
Xboolean operations). The result of all boolean operations are 0 and 1
X(for false and true) as in relational expressions.  The boolean
Xoperators are:
X.IP "!expr"
XThe result is 1 if expr is 0.
X.IP "expr && expr"
XThe result is 1 if both expressions are non-zero.
X.IP "expr || expr"
XThe result is 1 if either expression is non-zero.
X.PP
XThe expression precedence is as follows: (lowest to highest)
X.nf
X.RS
X|| operator, left associative
X&& operator, left associative
X! operator, nonassociative
XRelational operators, left associative
XAssignment operator, right associative
X+ and - operators, left associative
X*, / and % operators, left associative
X^ operator, right associative
Xunary - operator, nonassociative
X++ and -- operators, nonassociative
X.RE
X.fi
X.PP
XThis precedence was chosen so that POSIX compliant \fBbc\fR programs
Xwill run correctly. This will cause the use of the relational and
Xlogical operators to have some unusual behavior when used with
Xassignment expressions.  Consider the expression:
X.RS
Xa = 3 < 5
X.RE
X.PP
XMost C programmers would assume this would assign the result of "3 <
X5" (the value 1) to the variable "a".  What this does in \fBbc\fR is
Xassign the value 3 to the variable "a" and then compare 3 to 5.  It is
Xbest to use parenthesis when using relational and logical operators
Xwith the assignment operators.
X.PP
XThere are a few more special expressions that are provided in \fBbc\fR.
XThese have to do with user defined functions and standard
Xfunctions.  They all appear as "\fIname\fB(\fIparameters\fB)\fR".
XSee the section on functions for user defined functions.  The standard
Xfunctions are:
X.IP "length ( expression )"
XThe value of the length function is the number of significant digits in the
Xexpression.
X.IP "read ( )"
XThe read function (an extension) will read a number from the standard
Xinput, regardless of where the function occurs.   Beware, this can
Xcause problems with the mixing of data and program in the standard input.
XThe best use for this function is in a previously written program that
Xneeds input from the user, but never allows program code to be input
Xfrom the user.  The value of the read function is the number read from
Xthe standard input using the current value of the variable 
X\fBibase\fR for the conversion base.
X.IP "scale ( expression )"
XThe value of the scale function is the number of digits after the decimal
Xpoint in the expression.
X.IP "sqrt ( expression )"
XThe value of the sqrt function is the square root of the expression.  If
Xthe expression is negative, a run time error is generated.
X.SS STATEMENTS
XStatements (as in most algebraic languages) provide the sequencing of
Xexpression evaluation.  In \fBbc\fR statements are executed "as soon
Xas possible."  Execution happens when a newline in encountered and
Xthere is one or more complete statements.  Due to this immediate
Xexecution, newlines are very important in \fBbc\fR. In fact, both a
Xsemicolon and a newline are used as statement separators.  An
Ximproperly placed newline will cause a syntax error.  Because newlines
Xare statement separators, it is possible to hide a newline by using
Xthe backslash character.  The sequence "\e<nl>", where <nl> is the
Xnewline appears to \fBbc\fR as whitespace instead of a newline.  A
Xstatement list is a series of statements separated by semicolons and
Xnewlines.  The following is a list of \fBbc\fR statements and what
Xthey do: (Things enclosed in brackets ([]) are optional parts of the
Xstatement.)
X.IP "expression"
XThis statement does one of two things.  If the expression starts with
X"<variable> <assignment> ...", it is considered to be an assignment
Xstatement.  If the expression is not an assignment statement, the
Xexpression is evaluated and printed to the output.  After the number
Xis printed, a newline is printed.  For example, "a=1" is an assignment
Xstatement and "(a=1)" is an expression that has an embedded
Xassignment.  All numbers that are printed are printed in the base
Xspecified by the variable \fBobase\fR. The legal values for \fB
Xobase\fR are 2 through BC_BASE_MAX.  (See the section LIMITS.)  For
Xbases 2 through 16, the usual method of writing numbers is used.  For
Xbases greater than 16, \fBbc\fR uses a multi-character digit method
Xof printing the numbers where each higher base digit is printed as a
Xbase 10 number.  The multi-character digits are separated by spaces.
XEach digit contains the number of characters required to represent the
Xbase ten value of "obase-1".  Since numbers are of arbitrary
Xprecision, some numbers may not be printable on a single output line.
XThese long numbers will be split across lines using the "\e" as the
Xlast character on a line.  The maximum number of characters printed
Xper line is 70.  Due to the interactive nature of \fBbc\fR printing
Xa number cause the side effect of assigning the printed value the the
Xspecial variable \fBlast\fR. This allows the user to recover the
Xlast value printed without having to retype the expression that
Xprinted the number.  Assigning to \fBlast\fR is legal and will
Xoverwrite the last printed value with the assigned value.  The newly
Xassigned value will remain until the next number is printed or another
Xvalue is assigned to \fBlast\fR.
X.IP "string"
XThe string is printed to the output.  Strings start with a double quote
Xcharacter and contain all characters until the next double quote character.
XAll characters are take literally, including any newline.  No newline
Xcharacter is printed after the string.
X.IP "\fBprint\fR list"
XThe print statement (an extension) provides another method of output.
XThe "list" is a list of strings and expressions separated by commas.
XEach string or expression is printed in the order of the list.  No
Xterminating newline is printed.  Expressions are evaluated and their
Xvalue is printed and assigned the the variable \fBlast\fR. Strings
Xin the print statement are printed to the output and may contain
Xspecial characters.  Special characters start with the backslash
Xcharacter (\e).  The special characters recognized by \fBbc\fR are
X"b" (bell), "f" (form feed), "n" (newline), "r" (carriage return), "t"
X(tab), and "\e" (backslash).  Any other character following the
Xbackslash will be ignored.  This still does not allow the double quote
Xcharacter to be part of any string.
X.IP "{ statement_list }"
XThis is the compound statement.  It allows multiple statements to be
Xgrouped together for execution.
X.IP "\fBif\fR ( expression ) \fBthen\fR statement1 [\fBelse\fR statement2]"
XThe if statement evaluates the expression and executes statement1 or
Xstatement2 depending on the value of the expression.  If the expression
Xis non-zero, statement1 is executed.  If statement2 is present and
Xthe value of the expression is 0, then statement2 is executed.  (The
Xelse clause is an extension.)
X.IP "\fBwhile\fR ( expression ) statement"
XThe while statement will execute the statement while the expression
Xis non-zero.  It evaluates the expression before each execution of
Xthe statement.   Termination of the loop is caused by a zero
Xexpression value or the execution of a break statement.
X.IP "\fBfor\fR ( [expression1] ; [expression2] ; [expression3] ) statement"
XThe for statement controls repeated execution of the statement.  
XExpression1 is evaluated before the loop.  Expression2 is evaluated
Xbefore each execution of the statement.  If it is non-zero, the statement
Xis evaluated.  If it is zero, the loop is terminated.  After each
Xexecution of the statement, expression3 is evaluated before the reevaluation
Xof expression2.  If expression1 or expression3 are missing, nothing is
Xevaluated at the point they would be evaluated.
XIf expression2 is missing, it is the same as substituting
Xthe value 1 for expression2.  (The optional expressions are an
Xextension. POSIX \fBbc\fR requires all three expressions.)
XThe following is equivalent code for the for statement:
X.nf
X.RS
Xexpression1;
Xwhile (expression2) {
X   statement;
X   expression3;
X}
X.RE
X.fi
X.IP "\fBbreak\fR"
XThis statement causes a forced exit of the most recent enclosing while
Xstatement or for statement.
X.IP "\fBcontinue\fR"
XThe continue statement (an extension)  causes the most recent enclosing
Xfor statement to start the next iteration.
X.IP "\fBhalt\fR"
XThe halt statement (an extension) is an executed statement that causes
Xthe \fBbc\fR processor to quit only when it is executed.  For example,
X"if (0 == 1) halt" will not cause \fBbc\fR to terminate because the halt is
Xnot executed.
X.IP "\fBreturn\fR"
XReturn the value 0 from a function.  (See the section on functions.)
X.IP "\fBreturn\fR ( expression )"
XReturn the value of the expression from a function.  (See the section on 
Xfunctions.)
X.SS PSEUDO STATEMENTS
XThese statements are not statements in the traditional sense.  They are
Xnot executed statements.  Their function is performed at "compile" time.
X.IP "\fBlimits\fR"
XPrint the local limits enforced by the local version of \fBbc\fR.  This
Xis an extension.
X.IP "\fBquit\fR"
XWhen the quit statement is read, the \fBbc\fR processor
Xis terminated, regardless of where the quit statement is found.  For
Xexample, "if (0 == 1) quit" will cause \fBbc\fR to terminate.
X.IP "\fBwarranty\fR"
XPrint a longer warranty notice.  This is an extension.
X.SS FUNCTIONS
XFunctions provide a method of defining a computation that can be executed
Xlater.  Functions in 
X.B bc
Xalways compute a value and return it to the caller.  Function definitions
Xare "dynamic" in the sense that a function is undefined until a definition
Xis encountered in the input.  That definition is then used until another
Xdefinition function for the same name is encountered.  The new definition
Xthen replaces the older definition.  A function is defined as follows:
X.nf
X.RS
X\fBdefine \fIname \fB( \fIparameters \fB) { \fInewline
X\fI    auto_list   statement_list \fB}\fR
X.RE
X.fi
XA function call is just an expression of the form
X"\fIname\fB(\fIparameters\fB)\fR".
X.PP
XParameters are numbers or arrays (an extension).  In the function definition,
Xzero or more parameters are defined by listing their names separated by
Xcommas.  Numbers are only call by value parameters.  Arrays are only
Xcall by variable.  Arrays are specified in the parameter definition by
Xthe notation "\fIname\fB[]\fR".   In the function call, actual parameters
Xare full expressions for number parameters.  The same notation is used
Xfor passing arrays as for defining array parameters.  The named array is
Xpassed by variable to the function.  Since function definitions are dynamic,
Xparameter numbers and types are checked when a function is called.  Any
Xmismatch in number or types of parameters will cause a runtime error.
XA runtime error will also occur for the call to an undefined function.
X.PP
XThe \fIauto_list\f is an optional list of variables that are for
X"local" use.  The syntax of the auto list (if present) is "\fBauto
X\fIname\fR, ... ;".  (The semicolon is optional.)  Each \fIname\fR is
Xthe name of an auto variable.  Arrays may be specified by using the
Xsame notation as used in parameters.  These variables have their
Xvalues pushed onto a stack at the start of the function.  The
Xvariables are then initialized to zero and used throughout the
Xexecution of the function.  At function exit, these variables are
Xpopped so that the original value (at the time of the function call)
Xof these variables are restored.  The parameters are really auto
Xvariables that are initialized to a value provided in the function
Xcall.  Auto variables are different than traditional local variables
Xin the fact that if function A calls function B, B may access function
XA's auto variables by just using the same name, unless function B has
Xcalled them auto variables.  Due to the fact that auto variables and
Xparameters are pushed onto a stack, \fBbc\fR supports recursive functions.
X.PP
XThe function body is a list of \fBbc\fR statements.  Again, statements
Xare separated by semicolons or newlines.  Return statements cause the
Xtermination of a function and the return of a value.  There are two
Xversions of the return statement.  The first form, "\fBreturn\fR", returns
Xthe value 0 to the calling expression.  The second form, 
X"\fBreturn ( \fIexpression \fB)\fR", computes the value of the expression
Xand returns that value to the calling expression.  There is an implied
X"\fBreturn (0)\fR" at the end of every function.  This allows a function
Xto terminate and return 0 without an explicit return statement.
X.PP
XFunctions also change the usage of the variable \fBibase\fR.  All
Xconstants in the function body will be converted using the value of
X\fBibase\fR at the time of the function call.  Changes of \fBibase\fR
Xwill be ignored during the execution of the function except for the
Xstandard function \fBread\fR, which will always use the current value
Xof \fBibase\fR for conversion of numbers.
X.SS MATH LIBRARY
XIf \fBbc\fR is invoked with the \fB-l\fR option, a math library is preloaded
Xand the default scale is set to 20.   The math functions will calculate their
Xresults to the scale set at the time of their call.  
XThe math library defines the following functions:
X.IP "s (\fIx\fR)"
XThe sine of x in radians.
X.IP "c (\fIx\fR)"
XThe cosine of x in radians.
X.IP "a (\fIx\fR)"
XThe arctangent of x.
X.IP "l (\fIx\fR)"
XThe natural logarithm of x.
X.IP "e (\fIx\fR)"
XThe exponential function of raising e to the value x.
X.IP "j (\fIn,x\fR)"
XThe bessel function of integer order n of x.
X.SS EXAMPLES
XIn /bin/sh,  the following will assign the value of "pi" to the shell
Xvariable \fBpi\fR.
X.RS
X\f(CW
Xpi=$(echo "scale=10; 4*a(1)" | bc -l)
X\fR
X.RE
X.PP
XThe following is the definition of the exponential function used in the
Xmath library.  This function is written in POSIX \fBbc\fR.
X.nf
X.RS
X\f(CW
Xscale = 20
X
X/* Uses the fact that e^x = (e^(x/2))^2
X   When x is small enough, we use the series:
X     e^x = 1 + x + x^2/2! + x^3/3! + ...
X*/
X
Xdefine e(x) {
X  auto  a, d, e, f, i, m, v, z
X
X  /* Check the sign of x. */
X  if (x<0) {
X    m = 1
X    x = -x
X  } 
X
X  /* Precondition x. */
X  z = scale;
X  scale = 4 + z + .44*x;
X  while (x > 1) {
X    f += 1;
X    x /= 2;
X  }
X
X  /* Initialize the variables. */
X  v = 1+x
X  a = x
X  d = 1
X
X  for (i=2; 1; i++) {
X    e = (a *= x) / (d *= i)
X    if (e == 0) {
X      if (f>0) while (f--)  v = v*v;
X      scale = z
X      if (m) return (1/v);
X      return (v/1);
X    }
X    v += e
X  }
X}
X\fR
X.RE
X.fi
X.PP
XThe following is code that uses the extended features of \fBbc\fR to
Ximplement a simple program for calculating checkbook balances.  This
Xprogram is best kept in a file so that it can be used many times 
Xwithout having to retype it at every use.
X.nf
X.RS
X\f(CW
Xscale=2
Xprint "\enCheck book program!\en"
Xprint "  Remember, deposits are negative transactions.\en"
Xprint "  Exit by a 0 transaction.\en\en"
X
Xprint "Initial balance? "; bal = read()
Xbal /= 1
Xprint "\en"
Xwhile (1) {
X  "current balance = "; bal
X  "transaction? "; trans = read()
X  if (trans == 0) break;
X  bal -= trans
X  bal /= 1
X}
Xquit
X\fR
X.RE
X.fi
X.PP
XThe following is the definition of the recursive factorial function.
X.nf
X.RS
X\f(CW
Xdefine f (x) {
X  if (x <= 1) return (1);
X  return (f(x-1) * x);
X}
X\fR
X.RE
X.fi
X.SS DIFFERENCES
XThis version of 
X.B bc
Xwas implemented from the POSIX P1003.2/D11 draft and contains
Xseveral differences and extensions relative to the draft and
Xtraditional implementations.
XIt is not implemented in the traditional way using
X.I dc(1).
XThis version is a single process which parses and runs a byte code
Xtranslation of the program.  There is an "undocumented" option (-c)
Xthat causes the program to output the byte code to
Xthe standard output instead of running it.  It was mainly used for
Xdebugging the parser and preparing the math library.
X.PP
XA major source of differences is
Xextensions, where a feature is extended to add more functionality and
Xadditions, where new features are added. 
XThe following is the list of differences and extensions.
X.IP LANG environment
XThis version does not conform to the POSIX standard in the processing
Xof the LANG environment variable and all environment variables starting
Xwith LC_.
X.IP names
XTraditional and POSIX
X.B bc
Xhave single letter names for functions, variables and arrays.  They have
Xbeen extended to be multi-character names that start with a letter and
Xmay contain letters, numbers and the underscore character.
X.IP Strings
XStrings are not allowed to contain NUL characters.  POSIX says all characters
Xmust be included in strings.
X.IP last
XPOSIX \fBbc\fR does not have a \fBlast\fR variable.  Some implementations
Xof \fBbc\fR use the period (.) in a similar way.  
X.IP comparisons
XPOSIX \fBbc\fR allows comparisons only in the if statement, the while
Xstatement, and the second expression of the for statement.  Also, only
Xone relational operation is allowed in each of those statements.
X.IP "if statement, else clause"
XPOSIX \fBbc\fR does not have an else clause.
X.IP "for statement"
XPOSIX \fBbc\fR requires all expressions to be present in the for statement.
X.IP "&&, ||, !"
XPOSIX \fBbc\fR does not have the logical operators.
X.IP "read function"
XPOSIX \fBbc\fR does not have a read function.
X.IP "print statement"
XPOSIX \fBbc\fR does not have a print statement .
X.IP "continue statement"
XPOSIX \fBbc\fR does not have a continue statement.
X.IP "array parameters"
XPOSIX \fBbc\fR does not have array parameters.  Other implementations
Xof \fBbc\fR may have call by value array parameters.
X.IP "=+, =-, =*, =/, =%, =^"
XPOSIX \fBbc\fR does not require these "old style" assignment operators to
Xbe defined.  This version may allow these "old style" assignments.  Use
Xthe limits statement to see if the installed version supports them.  If
Xit does support the "old style" assignment operators, the statement
X"a =- 1" will decrement \fBa\fR by 1 instead of setting \fBa\fR to the
Xvalue -1.
X.IP "spaces in numbers"
XOther implementations of \fBbc\fR allow spaces in numbers.  For example,
X"x=1 3" would assign the value 13 to the variable x.  The same statement
Xwould cause a syntax error in this version of \fBbc\fR.
X.IP "errors and execution"
XThis implementation varies from other implementations in terms of what
Xcode will be executed when syntax and other errors are found in the
Xprogram.  If a syntax error is found in a function definition, error
Xrecovery tries to find the beginning of a statement and continue to
Xparse the function.  Once a syntax error is found in the function, the
Xfunction will not be callable and becomes undefined.
XSyntax errors in the interactive execution code will invalidate the
Xcurrent execution block.  The execution block is terminated by an
Xend of line that appears after a complete sequence of statements.
XFor example, 
X.nf
X.RS
Xa = 1
Xb = 2
X.RE
X.fi
Xhas two execution blocks and
X.nf
X.RS
X{ a = 1
X  b = 2 }
X.RE
X.fi
Xhas one execution block.  Any runtime error will terminate the execution
Xof the current execution block.  A runtime warning will not terminate the
Xcurrent execution block.
X.IP "Interrupts"
XDuring an interactive session, the SIGINT signal (usually generated by
Xthe control-C character from the terminal) will cause execution of the
Xcurrent execution block to be interrupted.  It will display a "runtime"
Xerror indicating which function was interrupted.  After all runtime
Xstructures have been cleaned up, a message will be printed to notify the
Xuser that \fBbc\fR is ready for more input.  All previously defined functions
Xremain defined and the value of all non-auto variables are the value at
Xthe point of interruption.  All auto variables and function parameters
Xare removed during the
Xclean up process.  During a non-interactive
Xsession, the SIGINT signal will terminate the entire run of \fBbc\fR.
X.SS LIMITS
XThe following are the limits currently in place for this 
X.B bc
Xprocessor.  Some of them may have been changed by an installation.
XUse the limits statement to see the actual values.
X.IP BC_BASE_MAX
XThe maximum output base is currently set at 999.  The maximum input base
Xis 16.
X.IP BC_DIM_MAX
XThis is currently an arbitrary limit of 65535 as distributed.  Your
Xinstallation may be different.
X.IP BC_SCALE_MAX
XThe number of digits after the decimal point is limited to INT_MAX digits.
XAlso, the number of digits before the decimal point is limited to INT_MAX
Xdigits.
X.IP BC_STRING_MAX
XThe limit on the number of characters in a string is INT_MAX characters.
X.IP exponent
XThe value of the exponent in the raise operation (^) is limited to LONG_MAX.
X.IP multiply
XThe multiply routine may yield incorrect results if a number
Xhas more than LONG_MAX / 90 total digits.  For 32 bit longs, this number is
X23,860,929 digits.
X.IP "code size"
XEach function and the "main" program are limited to 10240 bytes of
Xcompiled byte code each.  This limit (BC_MAX_SEGS) can be easily changed
Xto have more than 10 segments of 1024 bytes.
X.IP "variable names"
XThe current limit on the number of unique names is 32767 for each of
Xsimple variables, arrays and functions.
X.SH FILES
XIn most installations, \fBbc\fR is completely self-contained.
XWhere executable size is of importance or the C compiler does
Xnot deal with very long strings, \fBbc\fR will read
Xthe standard math library from the file /usr/local/lib/libmath.b.
X(The actual location may vary.  It may be /lib/libmath.b.)
X.SH DIAGNOSTICS
XIf any file on the command line can not be opened, \fBbc\fR will report
Xthat the file is unavailable and terminate.  Also, there are compile
Xand run time diagnostics that should be self-explanatory.
X.SH BUGS
XError recovery is not very good yet.
X.SH AUTHOR
X.nf
XPhilip A. Nelson
Xphil@cs.wwu.edu
X.fi
X.SH ACKNOWLEDGEMENTS
XThe author would like to thank Steve Sommars (sesv@iwtsf.att.com) for
Xhis extensive help in testing the implementation.  Many great suggestions
Xwere given.  This is a much better product due to his involvement.
/
echo x - bc.c
sed '/^X/s///' > bc.c << '/'
X#ifndef lint
Xstatic char yysccsid[] = "@(#)yaccpar	1.8 (Berkeley) 01/20/90";
X#endif
X#define YYBYACC 1
X#line 2 "bc.y"
X/* bc.y: The grammar for a POSIX compatable bc processor with some
X         extensions to the language. */
X
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X#include "bcdefs.h"
X#include "global.h"
X#include "proto.h"
X#line 38 "bc.y"
Xtypedef union {
X	char	 *s_value;
X	char	  c_value;
X	int	  i_value;
X	arg_list *a_value;
X       } YYSTYPE;
X#line 46 "y.tab.c"
X#define NEWLINE 257
X#define AND 258
X#define OR 259
X#define NOT 260
X#define STRING 261
X#define NAME 262
X#define NUMBER 263
X#define MUL_OP 264
X#define ASSIGN_OP 265
X#define REL_OP 266
X#define INCR_DECR 267
X#define Define 268
X#define Break 269
X#define Quit 270
X#define Length 271
X#define Return 272
X#define For 273
X#define If 274
X#define While 275
X#define Sqrt 276
X#define Else 277
X#define Scale 278
X#define Ibase 279
X#define Obase 280
X#define Auto 281
X#define Read 282
X#define Warranty 283
X#define Halt 284
X#define Last 285
X#define Continue 286
X#define Print 287
X#define Limits 288
X#define UNARY_MINUS 289
X#define YYERRCODE 256
Xshort yylhs[] = {                                        -1,
X    0,    0,   10,   10,   10,   11,   11,   11,   11,   12,
X   12,   12,   12,   12,   12,   15,   15,   13,   13,   13,
X   13,   13,   13,   13,   13,   13,   13,   16,   17,   18,
X   19,   13,   20,   13,   22,   23,   13,   13,   25,   13,
X   24,   24,   26,   26,   21,   27,   21,   28,   14,    5,
X    5,    6,    6,    6,    7,    7,    7,    7,    8,    8,
X    9,    9,    9,    9,    4,    4,    2,    2,   29,    1,
X   30,    1,   31,    1,    1,    1,    1,    1,    1,    1,
X    1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X    1,    3,    3,    3,    3,    3,    3,
X};
Xshort yylen[] = {                                         2,
X    0,    2,    2,    1,    2,    0,    1,    3,    2,    0,
X    1,    2,    3,    2,    3,    1,    2,    1,    1,    1,
X    1,    1,    1,    1,    1,    1,    4,    0,    0,    0,
X    0,   13,    0,    7,    0,    0,    7,    3,    0,    3,
X    1,    3,    1,    1,    0,    0,    3,    0,   12,    0,
X    1,    0,    3,    3,    1,    3,    3,    5,    0,    1,
X    1,    3,    3,    5,    0,    1,    0,    1,    0,    4,
X    0,    4,    0,    4,    2,    3,    3,    3,    3,    3,
X    2,    1,    1,    3,    4,    2,    2,    4,    4,    4,
X    3,    1,    4,    1,    1,    1,    1,
X};
Xshort yydefred[] = {                                      1,
X    0,    0,    0,   21,    0,   83,    0,    0,   22,   24,
X    0,    0,   28,    0,   35,    0,    0,   94,   95,    0,
X   18,   25,   97,   23,   39,   19,    0,    0,    0,    0,
X    0,    2,    0,   16,    4,    7,    5,   17,    0,    0,
X    0,    0,   96,   86,    0,    0,    0,    0,    0,    0,
X    0,    0,    0,    0,   81,    0,    0,    0,   11,   71,
X   73,    0,    0,    0,    0,    0,   69,   87,    3,    0,
X    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
X    0,    0,    0,    0,   91,   43,    0,   40,    0,   84,
X    0,    0,   38,    0,    0,    0,    0,    0,    0,    0,
X    0,    8,    0,   85,    0,   93,    0,    0,    0,   88,
X   27,    0,    0,   33,    0,   89,   90,    0,   13,   15,
X    0,    0,    0,   62,    0,    0,    0,    0,    0,   29,
X    0,    0,   42,    0,   56,    0,    0,    0,    0,    0,
X   64,    0,    0,    0,   46,   34,   37,    0,   48,   58,
X   30,    0,    0,    0,    0,   47,   53,   54,    0,    0,
X    0,   31,   49,    0,   32,
X};
Xshort yydgoto[] = {                                       1,
X   30,   79,   31,  113,  108,  149,  109,   73,   74,   32,
X   33,   58,   34,   35,   59,   48,  138,  155,  164,  131,
X  146,   50,  132,   88,   54,   89,  152,  154,  101,   94,
X   95,
X};
Xshort yysindex[] = {                                      0,
X   -7,   58,  212,    0,  -22,    0, -233, -241,    0,    0,
X   -8,   -5,    0,   -4,    0,    2,    4,    0,    0,    9,
X    0,    0,    0,    0,    0,    0,  212,  212,   91,  725,
X -240,    0,  -29,    0,    0,    0,    0,    0,   84,  245,
X  212,  -57,    0,    0,   10,  212,  212,   14,  212,   16,
X  212,  212,   23,  156,    0,  549,  127,  -52,    0,    0,
X    0,  212,  212,  212,  212,  212,    0,    0,    0,   91,
X  -17,  725,   24,   -3,  578, -205,  562,  725,   27,  212,
X  606,  212,  669,  716,    0,    0,  725,    0,   19,    0,
X   91,  127,    0,  212,  212,  -36,  -39,  -91,  -91,  -36,
X  212,    0,  166,    0,  277,    0,  -21,   36,   40,    0,
X    0,  725,   28,    0,  725,    0,    0,  156,    0,    0,
X   84,  540,  -39,    0,   -9,  725,   -2,  -37, -174,    0,
X  127,   48,    0,  346,    0, -167,    3,  212, -185,  127,
X    0, -188,    6,   37,    0,    0,    0, -205,    0,    0,
X    0,  127,  -42,   91,  212,    0,    0,    0,  -20,   54,
X   26,    0,    0,  127,    0,
X};
Xshort yyrindex[] = {                                      0,
X  -16,    0,    0,    0,  409,    0,    0,    0,    0,    0,
X    0,  -58,    0,    0,    0,    0,  426,    0,    0,    0,
X    0,    0,    0,    0,    0,    0,    0,    0,  -50,   46,
X  470,    0,    0,    0,    0,    0,    0,    0,  661,   56,
X    0,  525,    0,    0,    0,    0,   59,    0,    0,    0,
X    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
X    0,    0,    0,    0,    0,    0,    0,    0,    0,   -6,
X  705,    7,    0,   60,    0,   61,    0,   63,    0,   49,
X    0,    0,    0,    0,    0,    0,   17,    0,   78,    0,
X  -47,  -45,    0,    0,    0,  537,  440,  620,  637,  594,
X    0,    0,    0,    0,    0,    0,  -33,    0,   66,    0,
X    0,  -19,    0,    0,   68,    0,    0,    0,    0,    0,
X  667,  680,  508,    0,  705,   18,    0,    0,    0,    0,
X    0,    0,    0,    0,    0,    0,  -31,   49,  -44,    0,
X    0,  -40,    0,    0,    0,    0,    0,    0,    0,    0,
X    0,    0,    0,    1,   69,    0,    0,    0,    0,    0,
X   13,    0,    0,    0,    0,
X};
Xshort yygindex[] = {                                      0,
X  958,    0,  104, -118,    0,    0,  -35,    0,    0,    0,
X    0,  -34,   22,    0,   15,    0,    0,    0,    0,    0,
X    0,    0,    0,   -1,    0,    0,    0,    0,    0,    0,
X    0,
X};
X#define YYTABLESIZE 1113
Xshort yytable[] = {                                      52,
X   26,  129,   66,   64,   52,   65,   92,   55,   10,   57,
X   55,   12,   57,   14,   45,   36,  158,   40,   52,  144,
X   45,   66,   40,   38,   67,   55,   68,   57,   42,   70,
X   40,   46,   28,   41,   47,   49,  160,   27,   92,   66,
X  105,   51,    6,   52,   43,   18,   19,   61,   53,   76,
X   61,   23,    9,   80,   66,   82,  107,   66,   63,   10,
X   44,   63,  118,   85,  104,   28,   26,  111,   41,  127,
X   27,   12,   93,  103,   10,   44,  128,   12,   38,   14,
X   45,  134,   52,  129,  102,  136,  130,  137,  140,  142,
X  135,  145,  148,  143,  162,  151,   59,   28,  150,   67,
X   60,   50,   27,   68,   20,  119,   51,   65,   36,   65,
X   44,    0,  153,  120,    0,   29,  133,    0,    0,  159,
X    0,    0,    0,    0,    0,    0,   64,    0,   65,    0,
X   28,    0,    0,    0,    0,   27,   41,    0,    0,    0,
X    0,   44,    0,    0,    0,    0,    0,    0,   29,    0,
X  163,    0,  139,    0,    0,    0,    0,    0,    0,    0,
X    0,  147,    0,    0,    0,    0,   28,    0,    0,    0,
X   20,   27,   62,  156,    0,  119,    0,   66,    0,    0,
X   29,    0,    0,    0,    0,  165,    0,    0,    0,    0,
X    0,    0,    0,    0,    0,   28,    0,    0,   26,    0,
X   27,    0,   41,    0,   91,   28,   10,    0,    0,   12,
X   27,   14,   45,   29,  157,   52,   52,    0,   26,   52,
X   52,   52,   52,   55,   62,   57,   52,   69,   52,   52,
X   52,   52,   52,   52,   52,   52,  161,   52,   52,   52,
X    6,   52,   52,   52,   52,   52,   52,   52,    2,   29,
X    9,   28,    3,    4,    5,    6,   27,   10,  124,    7,
X    8,    9,   10,   11,   12,   13,   14,   15,   16,   12,
X   17,   18,   19,   44,   20,   21,   22,   23,   24,   25,
X   26,   57,    0,    0,   28,    3,    4,    5,    6,   27,
X    0,    0,    7,   44,    9,   10,   11,   12,   13,   14,
X   15,   16,   20,   17,   18,   19,    0,   20,   21,   22,
X   23,   24,   25,   26,   37,    0,   28,    3,    4,    5,
X    6,   27,   20,    0,    7,    0,    9,   10,   11,   12,
X   13,   14,   15,   16,   41,   17,   18,   19,    0,   20,
X   21,   22,   23,   24,   25,   26,   57,   62,    0,   63,
X    3,    4,    5,    6,   41,    0,    0,    7,    0,    9,
X   10,   11,   12,   13,   14,   15,   16,    0,   17,   18,
X   19,    0,   20,   21,   22,   23,   24,   25,   26,    0,
X    0,    0,    0,    0,    0,   28,    3,    4,    5,    6,
X   27,    0,    0,    7,    0,    9,   10,   11,   12,   13,
X   14,   15,   16,    0,   17,   18,   19,    0,   20,   21,
X   22,   23,   24,   25,   26,    3,   86,    5,    6,    0,
X    0,    0,    7,    0,    0,    3,   11,    5,    6,    0,
X    0,   16,    7,   17,   18,   19,   11,   20,  141,    0,
X   23,   16,    0,   17,   18,   19,    0,   20,    0,   92,
X   23,   92,   92,   92,    0,    0,    0,    0,    0,    0,
X    0,    0,    0,    0,    0,    0,   96,   92,   96,   96,
X   96,    3,    0,    5,    6,    0,    0,    0,    7,    0,
X   76,    0,   11,   76,   96,    0,    0,   16,    0,   17,
X   18,   19,    0,   20,    0,    0,   23,    0,   76,    0,
X    0,   92,   92,    0,    3,    0,   71,    6,    0,    0,
X   82,    7,   82,   82,   82,   11,    0,    0,   96,   96,
X   16,    0,   17,   18,   19,    0,   20,    0,   82,   23,
X    0,    0,   76,   92,    0,    0,    3,    0,  125,    6,
X    0,    0,    0,    7,    0,    0,    0,   11,   70,    0,
X   96,   70,   16,    0,   17,   18,   19,    0,   20,    0,
X    0,   23,   82,   82,   76,   92,   70,   92,   92,   92,
X    0,    0,    0,    0,    0,    0,    0,   79,    0,   79,
X   79,   79,   64,   92,   65,    0,    0,    0,    0,   90,
X    0,   64,    0,   65,   82,   79,    0,    0,    0,    0,
X   70,    0,  110,    0,   64,    3,   65,    5,    6,    0,
X    0,    0,    7,    0,    0,    0,   11,   92,   92,    0,
X   64,   16,   65,   17,   18,   19,    0,   20,    0,   79,
X   23,    0,   70,   66,   80,    0,   80,   80,   80,    0,
X    0,    0,   66,    0,    0,    0,  114,    0,   64,   92,
X   65,    0,   80,    0,    0,   66,    0,    0,    0,    0,
X   77,   79,   77,   77,   77,   92,   92,   92,    0,    0,
X  106,   66,   92,   92,   92,   92,    0,   78,   77,   78,
X   78,   78,   96,   96,   96,   92,   80,    0,    0,   96,
X   96,   96,   96,    0,    0,   78,   76,   76,   76,   66,
X    0,   75,   96,    0,   75,   76,    0,   72,    0,  116,
X   72,   64,   77,   65,    0,    0,   76,    0,   80,   75,
X   74,    0,    0,   74,    0,   72,   82,   82,   82,   78,
X    0,    0,    0,   82,    0,   82,    0,    0,   74,    0,
X    0,    0,    0,    0,   77,   92,   82,   92,   92,   92,
X    0,    0,    0,   75,    0,    0,  117,    0,   64,   72,
X   65,   78,   66,    0,   70,   70,   70,   64,    0,   65,
X    0,    0,   74,   70,    0,    0,    0,    0,    0,    0,
X    0,   92,   92,   92,   70,   75,    0,    0,   92,    0,
X   92,   72,    0,   79,   79,   79,    0,   60,   92,    0,
X   79,   92,   79,   62,   74,   63,   60,   61,    0,   66,
X    0,    0,   62,   79,   63,    0,    0,    0,   66,   60,
X   61,    0,    0,    0,    0,   62,    0,   63,    0,    0,
X    0,    0,    0,    0,    0,   60,   61,    0,    0,    0,
X    0,   62,    0,   63,    0,    0,    0,    0,    0,    0,
X   80,   80,   80,    0,    0,    0,    0,   80,    0,   80,
X    0,    0,    0,   60,   61,    0,    0,    0,    0,   62,
X   80,   63,    0,    0,    0,    0,   77,   77,   77,    0,
X    0,    0,    0,    0,    0,   77,    0,    0,    0,    0,
X    0,    0,    0,   78,   78,   78,   77,    0,    0,    0,
X    0,    0,   78,    0,    0,    0,    0,    0,    0,    0,
X    0,    0,    0,   78,    0,    0,    0,   75,   75,   75,
X    0,    0,    0,   72,   72,   72,   60,   61,    0,    0,
X    0,    0,   62,    0,   63,    0,   74,   75,   74,    0,
X    0,    0,    0,   72,    0,    0,    0,    0,    0,    0,
X    0,    0,    0,    0,    0,    0,   74,    0,    0,    0,
X   39,    0,   92,   92,    0,    0,    0,    0,   92,   92,
X   92,   92,    0,   60,   61,    0,    0,    0,    0,   62,
X    0,   63,   60,   61,   55,   56,    0,    0,   62,    0,
X   63,    0,    0,    0,    0,    0,    0,   72,   75,    0,
X    0,    0,    0,   77,   78,    0,   81,    0,   83,   84,
X    0,   87,    0,    0,    0,    0,    0,    0,    0,   96,
X   97,   98,   99,  100,    0,    0,    0,    0,    0,    0,
X    0,    0,    0,    0,    0,    0,    0,  112,    0,  115,
X    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
X    0,  121,  122,    0,    0,    0,    0,    0,  123,    0,
X   75,    0,  126,    0,    0,    0,    0,    0,    0,    0,
X    0,    0,    0,    0,    0,   87,    0,    0,    0,    0,
X    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
X    0,   75,    0,    0,    0,  112,    0,    0,    0,    0,
X    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
X    0,    0,  112,
X};
Xshort yycheck[] = {                                      40,
X   59,   44,   94,   43,   45,   45,   59,   41,   59,   41,
X   44,   59,   44,   59,   59,    1,   59,   40,   59,  138,
X  262,   41,   40,    2,  265,   59,  267,   59,  262,   59,
X   40,   40,   40,   91,   40,   40,  155,   45,   59,   59,
X   44,   40,   59,   40,  278,  279,  280,   41,   40,   40,
X   44,  285,   59,   40,   94,   40,  262,   94,   41,   59,
X   44,   44,   44,   41,   41,   40,  125,   41,   91,   91,
X   45,   59,  125,   91,  125,   59,   41,  125,   57,  125,
X  125,   91,  123,   44,   70,  123,   59,  262,   41,  257,
X   93,  277,  281,   91,   41,   59,   41,   40,   93,   41,
X   41,   41,   45,   41,   59,   91,   41,   59,   41,   41,
X    7,   -1,  148,   92,   -1,  123,  118,   -1,   -1,  154,
X   -1,   -1,   -1,   -1,   -1,   -1,   43,   -1,   45,   -1,
X   40,   -1,   -1,   -1,   -1,   45,   59,   -1,   -1,   -1,
X   -1,  125,   -1,   -1,   -1,   -1,   -1,   -1,  123,   -1,
X  125,   -1,  131,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
X   -1,  140,   -1,   -1,   -1,   -1,   40,   -1,   -1,   -1,
X  125,   45,  264,  152,   -1,  161,   -1,   94,   -1,   -1,
X  123,   -1,   -1,   -1,   -1,  164,   -1,   -1,   -1,   -1,
X   -1,   -1,   -1,   -1,   -1,   40,   -1,   -1,  257,   -1,
X   45,   -1,  125,   -1,  257,   40,  257,   -1,   -1,  257,
X   45,  257,  257,  123,  257,  256,  257,   -1,  277,  260,
X  261,  262,  263,  257,  264,  257,  267,  257,  269,  270,
X  271,  272,  273,  274,  275,  276,  257,  278,  279,  280,
X  257,  282,  283,  284,  285,  286,  287,  288,  256,  123,
X  257,   40,  260,  261,  262,  263,   45,  257,   93,  267,
X  268,  269,  270,  271,  272,  273,  274,  275,  276,  257,
X  278,  279,  280,  257,  282,  283,  284,  285,  286,  287,
X  288,  256,   -1,   -1,   40,  260,  261,  262,  263,   45,
X   -1,   -1,  267,  277,  269,  270,  271,  272,  273,  274,
X  275,  276,  257,  278,  279,  280,   -1,  282,  283,  284,
X  285,  286,  287,  288,  257,   -1,   40,  260,  261,  262,
X  263,   45,  277,   -1,  267,   -1,  269,  270,  271,  272,
X  273,  274,  275,  276,  257,  278,  279,  280,   -1,  282,
X  283,  284,  285,  286,  287,  288,  256,  264,   -1,  266,
X  260,  261,  262,  263,  277,   -1,   -1,  267,   -1,  269,
X  270,  271,  272,  273,  274,  275,  276,   -1,  278,  279,
X  280,   -1,  282,  283,  284,  285,  286,  287,  288,   -1,
X   -1,   -1,   -1,   -1,   -1,   40,  260,  261,  262,  263,
X   45,   -1,   -1,  267,   -1,  269,  270,  271,  272,  273,
X  274,  275,  276,   -1,  278,  279,  280,   -1,  282,  283,
X  284,  285,  286,  287,  288,  260,  261,  262,  263,   -1,
X   -1,   -1,  267,   -1,   -1,  260,  271,  262,  263,   -1,
X   -1,  276,  267,  278,  279,  280,  271,  282,   93,   -1,
X  285,  276,   -1,  278,  279,  280,   -1,  282,   -1,   41,
X  285,   43,   44,   45,   -1,   -1,   -1,   -1,   -1,   -1,
X   -1,   -1,   -1,   -1,   -1,   -1,   41,   59,   43,   44,
X   45,  260,   -1,  262,  263,   -1,   -1,   -1,  267,   -1,
X   41,   -1,  271,   44,   59,   -1,   -1,  276,   -1,  278,
X  279,  280,   -1,  282,   -1,   -1,  285,   -1,   59,   -1,
X   -1,   93,   94,   -1,  260,   -1,  262,  263,   -1,   -1,
X   41,  267,   43,   44,   45,  271,   -1,   -1,   93,   94,
X  276,   -1,  278,  279,  280,   -1,  282,   -1,   59,  285,
X   -1,   -1,   93,  125,   -1,   -1,  260,   -1,  262,  263,
X   -1,   -1,   -1,  267,   -1,   -1,   -1,  271,   41,   -1,
X  125,   44,  276,   -1,  278,  279,  280,   -1,  282,   -1,
X   -1,  285,   93,   94,  125,   41,   59,   43,   44,   45,
X   -1,   -1,   -1,   -1,   -1,   -1,   -1,   41,   -1,   43,
X   44,   45,   43,   59,   45,   -1,   -1,   -1,   -1,   41,
X   -1,   43,   -1,   45,  125,   59,   -1,   -1,   -1,   -1,
X   93,   -1,   41,   -1,   43,  260,   45,  262,  263,   -1,
X   -1,   -1,  267,   -1,   -1,   -1,  271,   93,   94,   -1,
X   43,  276,   45,  278,  279,  280,   -1,  282,   -1,   93,
X  285,   -1,  125,   94,   41,   -1,   43,   44,   45,   -1,
X   -1,   -1,   94,   -1,   -1,   -1,   41,   -1,   43,  125,
X   45,   -1,   59,   -1,   -1,   94,   -1,   -1,   -1,   -1,
X   41,  125,   43,   44,   45,  257,  258,  259,   -1,   -1,
X   93,   94,  264,  265,  266,  267,   -1,   41,   59,   43,
X   44,   45,  257,  258,  259,  277,   93,   -1,   -1,  264,
X  265,  266,  267,   -1,   -1,   59,  257,  258,  259,   94,
X   -1,   41,  277,   -1,   44,  266,   -1,   41,   -1,   41,
X   44,   43,   93,   45,   -1,   -1,  277,   -1,  125,   59,
X   41,   -1,   -1,   44,   -1,   59,  257,  258,  259,   93,
X   -1,   -1,   -1,  264,   -1,  266,   -1,   -1,   59,   -1,
X   -1,   -1,   -1,   -1,  125,   41,  277,   43,   44,   45,
X   -1,   -1,   -1,   93,   -1,   -1,   41,   -1,   43,   93,
X   45,  125,   94,   -1,  257,  258,  259,   43,   -1,   45,
X   -1,   -1,   93,  266,   -1,   -1,   -1,   -1,   -1,   -1,
X   -1,  257,  258,  259,  277,  125,   -1,   -1,  264,   -1,
X  266,  125,   -1,  257,  258,  259,   -1,  258,   94,   -1,
X  264,  277,  266,  264,  125,  266,  258,  259,   -1,   94,
X   -1,   -1,  264,  277,  266,   -1,   -1,   -1,   94,  258,
X  259,   -1,   -1,   -1,   -1,  264,   -1,  266,   -1,   -1,
X   -1,   -1,   -1,   -1,   -1,  258,  259,   -1,   -1,   -1,
X   -1,  264,   -1,  266,   -1,   -1,   -1,   -1,   -1,   -1,
X  257,  258,  259,   -1,   -1,   -1,   -1,  264,   -1,  266,
X   -1,   -1,   -1,  258,  259,   -1,   -1,   -1,   -1,  264,
X  277,  266,   -1,   -1,   -1,   -1,  257,  258,  259,   -1,
X   -1,   -1,   -1,   -1,   -1,  266,   -1,   -1,   -1,   -1,
X   -1,   -1,   -1,  257,  258,  259,  277,   -1,   -1,   -1,
X   -1,   -1,  266,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
X   -1,   -1,   -1,  277,   -1,   -1,   -1,  257,  258,  259,
X   -1,   -1,   -1,  257,  258,  259,  258,  259,   -1,   -1,
X   -1,   -1,  264,   -1,  266,   -1,  257,  277,  259,   -1,
X   -1,   -1,   -1,  277,   -1,   -1,   -1,   -1,   -1,   -1,
X   -1,   -1,   -1,   -1,   -1,   -1,  277,   -1,   -1,   -1,
X    3,   -1,  258,  259,   -1,   -1,   -1,   -1,  264,  265,
X  266,  267,   -1,  258,  259,   -1,   -1,   -1,   -1,  264,
X   -1,  266,  258,  259,   27,   28,   -1,   -1,  264,   -1,
X  266,   -1,   -1,   -1,   -1,   -1,   -1,   40,   41,   -1,
X   -1,   -1,   -1,   46,   47,   -1,   49,   -1,   51,   52,
X   -1,   54,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   62,
X   63,   64,   65,   66,   -1,   -1,   -1,   -1,   -1,   -1,
X   -1,   -1,   -1,   -1,   -1,   -1,   -1,   80,   -1,   82,
X   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
X   -1,   94,   95,   -1,   -1,   -1,   -1,   -1,  101,   -1,
X  103,   -1,  105,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
X   -1,   -1,   -1,   -1,   -1,  118,   -1,   -1,   -1,   -1,
X   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
X   -1,  134,   -1,   -1,   -1,  138,   -1,   -1,   -1,   -1,
X   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
X   -1,   -1,  155,
X};
X#define YYFINAL 1
X#ifndef YYDEBUG
X#define YYDEBUG 0
X#endif
X#define YYMAXTOKEN 289
X#if YYDEBUG
Xchar *yyname[] = {
X"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
X0,0,0,0,0,0,"'('","')'",0,"'+'","','","'-'",0,0,0,0,0,0,0,0,0,0,0,0,0,"';'",0,0,
X0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'['",0,"']'","'^'",0,
X0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'{'",0,"'}'",0,0,0,0,0,0,
X0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
X0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
X0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
X0,0,0,0,0,"NEWLINE","AND","OR","NOT","STRING","NAME","NUMBER","MUL_OP",
X"ASSIGN_OP","REL_OP","INCR_DECR","Define","Break","Quit","Length","Return",
X"For","If","While","Sqrt","Else","Scale","Ibase","Obase","Auto","Read",
X"Warranty","Halt","Last","Continue","Print","Limits","UNARY_MINUS",
X};
Xchar *yyrule[] = {
X"$accept : program",
X"program :",
X"program : program input_item",
X"input_item : semicolon_list NEWLINE",
X"input_item : function",
X"input_item : error NEWLINE",
X"semicolon_list :",
X"semicolon_list : statement_or_error",
X"semicolon_list : semicolon_list ';' statement_or_error",
X"semicolon_list : semicolon_list ';'",
X"statement_list :",
X"statement_list : statement_or_error",
X"statement_list : statement_list NEWLINE",
X"statement_list : statement_list NEWLINE statement_or_error",
X"statement_list : statement_list ';'",
X"statement_list : statement_list ';' statement",
X"statement_or_error : statement",
X"statement_or_error : error statement",
X"statement : Warranty",
X"statement : Limits",
X"statement : expression",
X"statement : STRING",
X"statement : Break",
X"statement : Continue",
X"statement : Quit",
X"statement : Halt",
X"statement : Return",
X"statement : Return '(' return_expression ')'",
X"$$1 :",
X"$$2 :",
X"$$3 :",
X"$$4 :",
X"statement : For $$1 '(' opt_expression ';' $$2 opt_expression ';' $$3 opt_expression ')' $$4 statement",
X"$$5 :",
X"statement : If '(' expression ')' $$5 statement opt_else",
X"$$6 :",
X"$$7 :",
X"statement : While $$6 '(' expression $$7 ')' statement",
X"statement : '{' statement_list '}'",
X"$$8 :",
X"statement : Print $$8 print_list",
X"print_list : print_element",
X"print_list : print_element ',' print_list",
X"print_element : STRING",
X"print_element : expression",
X"opt_else :",
X"$$9 :",
X"opt_else : Else $$9 statement",
X"$$10 :",
X"function : Define NAME '(' opt_parameter_list ')' '{' NEWLINE opt_auto_define_list $$10 statement_list NEWLINE '}'",
X"opt_parameter_list :",
X"opt_parameter_list : define_list",
X"opt_auto_define_list :",
X"opt_auto_define_list : Auto define_list NEWLINE",
X"opt_auto_define_list : Auto define_list ';'",
X"define_list : NAME",
X"define_list : NAME '[' ']'",
X"define_list : define_list ',' NAME",
X"define_list : define_list ',' NAME '[' ']'",
X"opt_argument_list :",
X"opt_argument_list : argument_list",
X"argument_list : expression",
X"argument_list : NAME '[' ']'",
X"argument_list : argument_list ',' expression",
X"argument_list : argument_list ',' NAME '[' ']'",
X"opt_expression :",
X"opt_expression : expression",
X"return_expression :",
X"return_expression : expression",
X"$$11 :",
X"expression : named_expression ASSIGN_OP $$11 expression",
X"$$12 :",
X"expression : expression AND $$12 expression",
X"$$13 :",
X"expression : expression OR $$13 expression",
X"expression : NOT expression",
X"expression : expression REL_OP expression",
X"expression : expression '+' expression",
X"expression : expression '-' expression",
X"expression : expression MUL_OP expression",
X"expression : expression '^' expression",
X"expression : '-' expression",
X"expression : named_expression",
X"expression : NUMBER",
X"expression : '(' expression ')'",
X"expression : NAME '(' opt_argument_list ')'",
X"expression : INCR_DECR named_expression",
X"expression : named_expression INCR_DECR",
X"expression : Length '(' expression ')'",
X"expression : Sqrt '(' expression ')'",
X"expression : Scale '(' expression ')'",
X"expression : Read '(' ')'",
X"named_expression : NAME",
X"named_expression : NAME '[' expression ']'",
X"named_expression : Ibase",
X"named_expression : Obase",
X"named_expression : Scale",
X"named_expression : Last",
X};
X#endif
X#define yyclearin (yychar=(-1))
X#define yyerrok (yyerrflag=0)
X#ifdef YYSTACKSIZE
X#ifndef YYMAXDEPTH
X#define YYMAXDEPTH YYSTACKSIZE
X#endif
X#else
X#ifdef YYMAXDEPTH
X#define YYSTACKSIZE YYMAXDEPTH
X#else
X#define YYSTACKSIZE 500
X#define YYMAXDEPTH 500
X#endif
X#endif
Xint yydebug;
Xint yynerrs;
Xint yyerrflag;
Xint yychar;
Xshort *yyssp;
XYYSTYPE *yyvsp;
XYYSTYPE yyval;
XYYSTYPE yylval;
Xshort yyss[YYSTACKSIZE];
XYYSTYPE yyvs[YYSTACKSIZE];
X#define yystacksize YYSTACKSIZE
X#define YYABORT goto yyabort
X#define YYACCEPT goto yyaccept
X#define YYERROR goto yyerrlab
Xint
Xyyparse()
X{
X    register int yym, yyn, yystate;
X#if YYDEBUG
X    register char *yys;
X    extern char *getenv();
X
X    if (yys = getenv("YYDEBUG"))
X    {
X        yyn = *yys;
X        if (yyn >= '0' && yyn <= '9')
X            yydebug = yyn - '0';
X    }
X#endif
X
X    yynerrs = 0;
X    yyerrflag = 0;
X    yychar = (-1);
X
X    yyssp = yyss;
X    yyvsp = yyvs;
X    *yyssp = yystate = 0;
X
Xyyloop:
X    if (yyn = yydefred[yystate]) goto yyreduce;
X    if (yychar < 0)
X    {
X        if ((yychar = yylex()) < 0) yychar = 0;
X#if YYDEBUG
X        if (yydebug)
X        {
X            yys = 0;
X            if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
X            if (!yys) yys = "illegal-symbol";
X            printf("yydebug: state %d, reading %d (%s)\n", yystate,
X                    yychar, yys);
X        }
X#endif
X    }
X    if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
X            yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
X    {
X#if YYDEBUG
X        if (yydebug)
X            printf("yydebug: state %d, shifting to state %d\n",
X                    yystate, yytable[yyn]);
X#endif
X        if (yyssp >= yyss + yystacksize - 1)
X        {
X            goto yyoverflow;
X        }
X        *++yyssp = yystate = yytable[yyn];
X        *++yyvsp = yylval;
X        yychar = (-1);
X        if (yyerrflag > 0)  --yyerrflag;
X        goto yyloop;
X    }
X    if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
X            yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
X    {
X        yyn = yytable[yyn];
X        goto yyreduce;
X    }
X    if (yyerrflag) goto yyinrecovery;
X#ifdef lint
X    goto yynewerror;
X#endif
Xyynewerror:
X    yyerror("syntax error");
X#ifdef lint
X    goto yyerrlab;
X#endif
Xyyerrlab:
X    ++yynerrs;
Xyyinrecovery:
X    if (yyerrflag < 3)
X    {
X        yyerrflag = 3;
X        for (;;)
X        {
X            if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&
X                    yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
X            {
X#if YYDEBUG
X                if (yydebug)
X                    printf("yydebug: state %d, error recovery shifting\
X to state %d\n", *yyssp, yytable[yyn]);
X#endif
X                if (yyssp >= yyss + yystacksize - 1)
X                {
X                    goto yyoverflow;
X                }
X                *++yyssp = yystate = yytable[yyn];
X                *++yyvsp = yylval;
X                goto yyloop;
X            }
X            else
X            {
X#if YYDEBUG
X                if (yydebug)
X                    printf("yydebug: error recovery discarding state %d\n",
X                            *yyssp);
X#endif
X                if (yyssp <= yyss) goto yyabort;
X                --yyssp;
X                --yyvsp;
X            }
X        }
X    }
X    else
X    {
X        if (yychar == 0) goto yyabort;
X#if YYDEBUG
X        if (yydebug)
X        {
X            yys = 0;
X            if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
X            if (!yys) yys = "illegal-symbol";
X            printf("yydebug: state %d, error recovery discards token %d (%s)\n",
X                    yystate, yychar, yys);
X        }
X#endif
X        yychar = (-1);
X        goto yyloop;
X    }
Xyyreduce:
X#if YYDEBUG
X    if (yydebug)
X        printf("yydebug: state %d, reducing by rule %d (%s)\n",
X                yystate, yyn, yyrule[yyn]);
X#endif
X    yym = yylen[yyn];
X    yyval = yyvsp[1-yym];
X    switch (yyn)
X    {
Xcase 1:
X#line 106 "bc.y"
X{
X			      yyval.i_value = 0;
X			      if (interactive)
X				{
X				  printf ("%s\n", BC_VERSION);
X				  welcome ();
X				}
X			    }
Xbreak;
Xcase 3:
X#line 117 "bc.y"
X{ run_code (); }
Xbreak;
Xcase 4:
X#line 119 "bc.y"
X{ run_code (); }
Xbreak;
Xcase 5:
X#line 121 "bc.y"
X{
X			      yyerrok;
X			      init_gen ();
X			    }
Xbreak;
Xcase 6:
X#line 127 "bc.y"
X{ yyval.i_value = 0; }
Xbreak;
Xcase 10:
X#line 133 "bc.y"
X{ yyval.i_value = 0; }
Xbreak;
Xcase 17:
X#line 142 "bc.y"
X{ yyval.i_value = yyvsp[0].i_value; }
Xbreak;
Xcase 18:
X#line 145 "bc.y"
X{ warranty (""); }
Xbreak;
Xcase 19:
X#line 147 "bc.y"
X{ limits (); }
Xbreak;
Xcase 20:
X#line 149 "bc.y"
X{
X			      if (yyvsp[0].i_value & 2)
X				warn ("comparison in expression");
X			      if (yyvsp[0].i_value & 1)
X				generate ("W");
X			      else 
X				generate ("p");
X			    }
Xbreak;
Xcase 21:
X#line 158 "bc.y"
X{
X			      yyval.i_value = 0;
X			      generate ("w");
X			      generate (yyvsp[0].s_value);
X			      free (yyvsp[0].s_value);
X			    }
Xbreak;
Xcase 22:
X#line 165 "bc.y"
X{
X			      if (break_label == 0)
X				yyerror ("Break outside a for/while");
X			      else
X				{
X				  sprintf (genstr, "J%1d:", break_label);
X				  generate (genstr);
X				}
X			    }
Xbreak;
Xcase 23:
X#line 175 "bc.y"
X{
X			      warn ("Continue statement");
X			      if (continue_label == 0)
X				yyerror ("Continue outside a for");
X			      else
X				{
X				  sprintf (genstr, "J%1d:", continue_label);
X				  generate (genstr);
X				}
X			    }
Xbreak;
Xcase 24:
X#line 186 "bc.y"
X{ exit (0); }
Xbreak;
Xcase 25:
X#line 188 "bc.y"
X{ generate ("h"); }
Xbreak;
Xcase 26:
X#line 190 "bc.y"
X{ generate ("0R"); }
Xbreak;
Xcase 27:
X#line 192 "bc.y"
X{ generate ("R"); }
Xbreak;
Xcase 28:
X#line 194 "bc.y"
X{
X			      yyvsp[0].i_value = break_label; 
X			      break_label = next_label++;
X			    }
Xbreak;
Xcase 29:
X#line 199 "bc.y"
X{
X			      if (yyvsp[-1].i_value > 1)
X				warn ("Comparison in first for expression");
X			      yyvsp[-1].i_value = next_label++;
X			      if (yyvsp[-1].i_value < 0)
X				sprintf (genstr, "N%1d:", yyvsp[-1].i_value);
X			      else
X				sprintf (genstr, "pN%1d:", yyvsp[-1].i_value);
X			      generate (genstr);
X			    }
Xbreak;
Xcase 30:
X#line 210 "bc.y"
X{
X			      if (yyvsp[-1].i_value < 0) generate ("1");
X			      yyvsp[-1].i_value = next_label++;
X			      sprintf (genstr, "B%1d:J%1d:", yyvsp[-1].i_value, break_label);
X			      generate (genstr);
X			      yyval.i_value = continue_label;
X			      continue_label = next_label++;
X			      sprintf (genstr, "N%1d:", continue_label);
X			      generate (genstr);
X			    }
Xbreak;
Xcase 31:
X#line 221 "bc.y"
X{
X			      if (yyvsp[-1].i_value > 1)
X				warn ("Comparison in third for expression");
X			      if (yyvsp[-1].i_value < 0)
X				sprintf (genstr, "J%1d:N%1d:", yyvsp[-7].i_value, yyvsp[-4].i_value);
X			      else
X				sprintf (genstr, "pJ%1d:N%1d:", yyvsp[-7].i_value, yyvsp[-4].i_value);
X			      generate (genstr);
X			    }
Xbreak;
Xcase 32:
X#line 231 "bc.y"
X{
X			      sprintf (genstr, "J%1d:N%1d:",
X				       continue_label, break_label);
X			      generate (genstr);
X			      break_label = yyvsp[-12].i_value;
X			      continue_label = yyvsp[-4].i_value;
X			    }
Xbreak;
Xcase 33:
X#line 239 "bc.y"
X{
X			      yyvsp[-1].i_value = if_label;
X			      if_label = next_label++;
X			      sprintf (genstr, "Z%1d:", if_label);
X			      generate (genstr);
X			    }
Xbreak;
Xcase 34:
X#line 246 "bc.y"
X{
X			      sprintf (genstr, "N%1d:", if_label); 
X			      generate (genstr);
X			      if_label = yyvsp[-4].i_value;
X			    }
Xbreak;
Xcase 35:
X#line 252 "bc.y"
X{
X			      yyvsp[0].i_value = next_label++;
X			      sprintf (genstr, "N%1d:", yyvsp[0].i_value);
X			      generate (genstr);
X			    }
Xbreak;
Xcase 36:
X#line 258 "bc.y"
X{
X			      yyvsp[0].i_value = break_label; 
X			      break_label = next_label++;
X			      sprintf (genstr, "Z%1d:", break_label);
X			      generate (genstr);
X			    }
Xbreak;
Xcase 37:
X#line 265 "bc.y"
X{
X			      sprintf (genstr, "J%1d:N%1d:", yyvsp[-6].i_value, break_label);
X			      generate (genstr);
X			      break_label = yyvsp[-3].i_value;
X			    }
Xbreak;
Xcase 38:
X#line 271 "bc.y"
X{ yyval.i_value = 0; }
Xbreak;
Xcase 39:
X#line 273 "bc.y"
X{  warn ("print statement"); }
Xbreak;
Xcase 43:
X#line 280 "bc.y"
X{
X			      generate ("O");
X			      generate (yyvsp[0].s_value);
X			      free (yyvsp[0].s_value);
X			    }
Xbreak;
Xcase 44:
X#line 286 "bc.y"
X{ generate ("P"); }
Xbreak;
Xcase 46:
X#line 290 "bc.y"
X{
X			      warn ("else clause in if statement");
X			      yyvsp[0].i_value = next_label++;
X			      sprintf (genstr, "J%d:N%1d:", yyvsp[0].i_value, if_label); 
X			      generate (genstr);
X			      if_label = yyvsp[0].i_value;
X			    }
Xbreak;
Xcase 48:
X#line 300 "bc.y"
X{
X			      /* Check auto list against parameter list? */
X			      check_params (yyvsp[-4].a_value,yyvsp[0].a_value);
X			      sprintf (genstr, "F%d,%s.%s[", lookup(yyvsp[-6].s_value,FUNCT), 
X				       arg_str (yyvsp[-4].a_value,TRUE), arg_str (yyvsp[0].a_value,TRUE));
X			      generate (genstr);
X			      free_args (yyvsp[-4].a_value);
X			      free_args (yyvsp[0].a_value);
X			      yyvsp[-7].i_value = next_label;
X			      next_label = 0;
X			    }
Xbreak;
Xcase 49:
X#line 312 "bc.y"
X{
X			      generate ("0R]");
X			      next_label = yyvsp[-11].i_value;
X			    }
Xbreak;
Xcase 50:
X#line 318 "bc.y"
X{ yyval.a_value = NULL; }
Xbreak;
Xcase 52:
X#line 322 "bc.y"
X{ yyval.a_value = NULL; }
Xbreak;
Xcase 53:
X#line 324 "bc.y"
X{ yyval.a_value = yyvsp[-1].a_value; }
Xbreak;
Xcase 54:
X#line 326 "bc.y"
X{ yyval.a_value = yyvsp[-1].a_value; }
Xbreak;
Xcase 55:
X#line 329 "bc.y"
X{ yyval.a_value = nextarg (NULL, lookup (yyvsp[0].s_value,SIMPLE)); }
Xbreak;
Xcase 56:
X#line 331 "bc.y"
X{ yyval.a_value = nextarg (NULL, lookup (yyvsp[-2].s_value,ARRAY)); }
Xbreak;
Xcase 57:
X#line 333 "bc.y"
X{ yyval.a_value = nextarg (yyvsp[-2].a_value, lookup (yyvsp[0].s_value,SIMPLE)); }
Xbreak;
Xcase 58:
X#line 335 "bc.y"
X{ yyval.a_value = nextarg (yyvsp[-4].a_value, lookup (yyvsp[-2].s_value,ARRAY)); }
Xbreak;
Xcase 59:
X#line 338 "bc.y"
X{ yyval.a_value = NULL; }
Xbreak;
Xcase 61:
X#line 342 "bc.y"
X{
X			      if (yyvsp[0].i_value > 1) warn ("comparison in argument");
X			      yyval.a_value = nextarg (NULL,0);
X			    }
Xbreak;
Xcase 62:
X#line 347 "bc.y"
X{
X			      sprintf (genstr, "K%d:", -lookup (yyvsp[-2].s_value,ARRAY));
X			      generate (genstr);
X			      yyval.a_value = nextarg (NULL,1);
X			    }
Xbreak;
Xcase 63:
X#line 353 "bc.y"
X{
X			      if (yyvsp[0].i_value > 1) warn ("comparison in argument");
X			      yyval.a_value = nextarg (yyvsp[-2].a_value,0);
X			    }
Xbreak;
Xcase 64:
X#line 358 "bc.y"
X{
X			      sprintf (genstr, "K%d:", -lookup (yyvsp[-2].s_value,ARRAY));
X			      generate (genstr);
X			      yyval.a_value = nextarg (yyvsp[-4].a_value,1);
X			    }
Xbreak;
Xcase 65:
X#line 365 "bc.y"
X{
X			      yyval.i_value = -1;
X			      warn ("Missing expression in for statement");
X			    }
Xbreak;
Xcase 67:
X#line 372 "bc.y"
X{
X			      yyval.i_value = 0;
X			      generate ("0");
X			    }
Xbreak;
Xcase 68:
X#line 377 "bc.y"
X{
X			      if (yyvsp[0].i_value > 1)
X				warn ("comparison in return expresion");
X			    }
Xbreak;
Xcase 69:
X#line 383 "bc.y"
X{
X			      if (yyvsp[0].c_value != '=')
X				{
X				  if (yyvsp[-1].i_value < 0)
X				    sprintf (genstr, "DL%d:", -yyvsp[-1].i_value);
X				  else
X				    sprintf (genstr, "l%d:", yyvsp[-1].i_value);
X				  generate (genstr);
X				}
X			    }
Xbreak;
Xcase 70:
X#line 394 "bc.y"
X{
X			      if (yyvsp[0].i_value > 1) warn("comparison in assignment");
X			      if (yyvsp[-2].c_value != '=')
X				{
X				  sprintf (genstr, "%c", yyvsp[-2].c_value);
X				  generate (genstr);
X				}
X			      if (yyvsp[-3].i_value < 0)
X				sprintf (genstr, "S%d:", -yyvsp[-3].i_value);
X			      else
X				sprintf (genstr, "s%d:", yyvsp[-3].i_value);
X			      generate (genstr);
X			      yyval.i_value = 0;
X			    }
Xbreak;
Xcase 71:
X#line 410 "bc.y"
X{
X			      warn("&& operator");
X			      yyvsp[0].i_value = next_label++;
X			      sprintf (genstr, "DZ%d:p", yyvsp[0].i_value);
X			      generate (genstr);
X			    }
Xbreak;
Xcase 72:
X#line 417 "bc.y"
X{
X			      sprintf (genstr, "DZ%d:p1N%d:", yyvsp[-2].i_value, yyvsp[-2].i_value);
X			      generate (genstr);
X			      yyval.i_value = yyvsp[-3].i_value | yyvsp[0].i_value;
X			    }
Xbreak;
Xcase 73:
X#line 423 "bc.y"
X{
X			      warn("|| operator");
X			      yyvsp[0].i_value = next_label++;
X			      sprintf (genstr, "B%d:", yyvsp[0].i_value);
X			      generate (genstr);
X			    }
Xbreak;
Xcase 74:
X#line 430 "bc.y"
X{
X			      int tmplab;
X			      tmplab = next_label++;
X			      sprintf (genstr, "B%d:0J%d:N%d:1N%d:",
X				       yyvsp[-2].i_value, tmplab, yyvsp[-2].i_value, tmplab);
X			      generate (genstr);
X			      yyval.i_value = yyvsp[-3].i_value | yyvsp[0].i_value;
X			    }
Xbreak;
Xcase 75:
X#line 439 "bc.y"
X{
X			      yyval.i_value = yyvsp[0].i_value;
X			      warn("! operator");
X			      generate ("!");
X			    }
Xbreak;
Xcase 76:
X#line 445 "bc.y"
X{
X			      yyval.i_value = 3;
X			      switch (*(yyvsp[-1].s_value))
X				{
X				case '=':
X				  generate ("=");
X				  break;
X
X				case '!':
X				  generate ("#");
X				  break;
X
X				case '<':
X				  if (yyvsp[-1].s_value[1] == '=')
X				    generate ("{");
X				  else
X				    generate ("<");
X				  break;
X
X				case '>':
X				  if (yyvsp[-1].s_value[1] == '=')
X				    generate ("}");
X				  else
X				    generate (">");
X				  break;
X				}
X			    }
Xbreak;
Xcase 77:
X#line 473 "bc.y"
X{
X			      generate ("+");
X			      yyval.i_value = yyvsp[-2].i_value | yyvsp[0].i_value;
X			    }
Xbreak;
Xcase 78:
X#line 478 "bc.y"
X{
X			      generate ("-");
X			      yyval.i_value = yyvsp[-2].i_value | yyvsp[0].i_value;
X			    }
Xbreak;
Xcase 79:
X#line 483 "bc.y"
X{
X			      genstr[0] = yyvsp[-1].c_value;
X			      genstr[1] = 0;
X			      generate (genstr);
X			      yyval.i_value = yyvsp[-2].i_value | yyvsp[0].i_value;
X			    }
Xbreak;
Xcase 80:
X#line 490 "bc.y"
X{
X			      generate ("^");
X			      yyval.i_value = yyvsp[-2].i_value | yyvsp[0].i_value;
X			    }
Xbreak;
Xcase 81:
X#line 495 "bc.y"
X{
X			      generate ("n");
X			      yyval.i_value = yyvsp[0].i_value;
X			    }
Xbreak;
Xcase 82:
X#line 500 "bc.y"
X{
X			      yyval.i_value = 1;
X			      if (yyvsp[0].i_value < 0)
X				sprintf (genstr, "L%d:", -yyvsp[0].i_value);
X			      else
X				sprintf (genstr, "l%d:", yyvsp[0].i_value);
X			      generate (genstr);
X			    }
Xbreak;
Xcase 83:
X#line 509 "bc.y"
X{
X			      int len = strlen(yyvsp[0].s_value);
X			      yyval.i_value = 1;
X			      if (len == 1 && *yyvsp[0].s_value == '0')
X				generate ("0");
X			      else if (len == 1 && *yyvsp[0].s_value == '1')
X				generate ("1");
X			      else
X				{
X				  generate ("K");
X				  generate (yyvsp[0].s_value);
X				  generate (":");
X				}
X			      free (yyvsp[0].s_value);
X			    }
Xbreak;
Xcase 84:
X#line 525 "bc.y"
X{ yyval.i_value = yyvsp[-1].i_value | 1; }
Xbreak;
Xcase 85:
X#line 527 "bc.y"
X{
X			      yyval.i_value = 1;
X			      if (yyvsp[-1].a_value != NULL)
X				{ 
X				  sprintf (genstr, "C%d,%s:",
X					   lookup (yyvsp[-3].s_value,FUNCT),
X					   arg_str (yyvsp[-1].a_value,FALSE));
X				  free_args (yyvsp[-1].a_value);
X				}
X			      else
X				{
X				  sprintf (genstr, "C%d:", lookup (yyvsp[-3].s_value,FUNCT));
X				}
X			      generate (genstr);
X			    }
Xbreak;
Xcase 86:
X#line 543 "bc.y"
X{
X			      yyval.i_value = 1;
X			      if (yyvsp[0].i_value < 0)
X				{
X				  if (yyvsp[-1].c_value == '+')
X				    sprintf (genstr, "DA%d:L%d:", -yyvsp[0].i_value, -yyvsp[0].i_value);
X				  else
X				    sprintf (genstr, "DM%d:L%d:", -yyvsp[0].i_value, -yyvsp[0].i_value);
X				}
X			      else
X				{
X				  if (yyvsp[-1].c_value == '+')
X				    sprintf (genstr, "i%d:l%d:", yyvsp[0].i_value, yyvsp[0].i_value);
X				  else
X				    sprintf (genstr, "d%d:l%d:", yyvsp[0].i_value, yyvsp[0].i_value);
X				}
X			      generate (genstr);
X			    }
Xbreak;
Xcase 87:
X#line 562 "bc.y"
X{
X			      yyval.i_value = 1;
X			      if (yyvsp[-1].i_value < 0)
X				{
X				  sprintf (genstr, "DL%d:x", -yyvsp[-1].i_value);
X				  generate (genstr); 
X				  if (yyvsp[0].c_value == '+')
X				    sprintf (genstr, "A%d:", -yyvsp[-1].i_value);
X				  else
X				      sprintf (genstr, "M%d:", -yyvsp[-1].i_value);
X				}
X			      else
X				{
X				  sprintf (genstr, "l%d:", yyvsp[-1].i_value);
X				  generate (genstr);
X				  if (yyvsp[0].c_value == '+')
X				    sprintf (genstr, "i%d:", yyvsp[-1].i_value);
X				  else
X				    sprintf (genstr, "d%d:", yyvsp[-1].i_value);
X				}
X			      generate (genstr);
X			    }
Xbreak;
Xcase 88:
X#line 585 "bc.y"
X{ generate ("cL"); yyval.i_value = 1;}
Xbreak;
Xcase 89:
X#line 587 "bc.y"
X{ generate ("cR"); yyval.i_value = 1;}
Xbreak;
Xcase 90:
X#line 589 "bc.y"
X{ generate ("cS"); yyval.i_value = 1;}
Xbreak;
Xcase 91:
X#line 591 "bc.y"
X{
X			      warn ("read function");
X			      generate ("cI"); yyval.i_value = 1;
X			    }
Xbreak;
Xcase 92:
X#line 597 "bc.y"
X{ yyval.i_value = lookup(yyvsp[0].s_value,SIMPLE); }
Xbreak;
Xcase 93:
X#line 599 "bc.y"
X{
X			      if (yyvsp[-1].i_value > 1) warn("comparison in subscript");
X			      yyval.i_value = lookup(yyvsp[-3].s_value,ARRAY);
X			    }
Xbreak;
Xcase 94:
X#line 604 "bc.y"
X{ yyval.i_value = 0; }
Xbreak;
Xcase 95:
X#line 606 "bc.y"
X{ yyval.i_value = 1; }
Xbreak;
Xcase 96:
X#line 608 "bc.y"
X{ yyval.i_value = 2; }
Xbreak;
Xcase 97:
X#line 610 "bc.y"
X{ yyval.i_value = 3; }
Xbreak;
X#line 1314 "y.tab.c"
X    }
X    yyssp -= yym;
X    yystate = *yyssp;
X    yyvsp -= yym;
X    yym = yylhs[yyn];
X    if (yystate == 0 && yym == 0)
X    {
X#if YYDEBUG
X        if (yydebug)
X            printf("yydebug: after reduction, shifting from state 0 to\
X state %d\n", YYFINAL);
X#endif
X        yystate = YYFINAL;
X        *++yyssp = YYFINAL;
X        *++yyvsp = yyval;
X        if (yychar < 0)
X        {
X            if ((yychar = yylex()) < 0) yychar = 0;
X#if YYDEBUG
X            if (yydebug)
X            {
X                yys = 0;
X                if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
X                if (!yys) yys = "illegal-symbol";
X                printf("yydebug: state %d, reading %d (%s)\n",
X                        YYFINAL, yychar, yys);
X            }
X#endif
X        }
X        if (yychar == 0) goto yyaccept;
X        goto yyloop;
X    }
X    if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
X            yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
X        yystate = yytable[yyn];
X    else
X        yystate = yydgoto[yym];
X#if YYDEBUG
X    if (yydebug)
X        printf("yydebug: after reduction, shifting from state %d \
Xto state %d\n", *yyssp, yystate);
X#endif
X    if (yyssp >= yyss + yystacksize - 1)
X    {
X        goto yyoverflow;
X    }
X    *++yyssp = yystate;
X    *++yyvsp = yyval;
X    goto yyloop;
Xyyoverflow:
X    yyerror("yacc stack overflow");
Xyyabort:
X    return (1);
Xyyaccept:
X    return (0);
X}
/
echo x - bc.y
sed '/^X/s///' > bc.y << '/'
X%{
X/* bc.y: The grammar for a POSIX compatable bc processor with some
X         extensions to the language. */
X
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X#include "bcdefs.h"
X#include "global.h"
X#include "proto.h"
X%}
X
X%start program
X
X%union {
X	char	 *s_value;
X	char	  c_value;
X	int	  i_value;
X	arg_list *a_value;
X       }
X
X/* Extensions over POSIX bc.
X   a) NAME was LETTER.  This grammer allows longer names.
X      Single letter names will still work.
X   b) Relational_expression allowed only one comparison.
X      This grammar has added boolean expressions with
X      && (and) || (or) and ! (not) and allowed all of them in
X      full expressions.
X   c) Added an else to the if.
X   d) Call by variable array parameters
X   e) read() procedure that reads a number under program control from stdin.
X   f) halt statement that halts the the program under program control.  It
X      is an executed statement.
X   g) continue statement for for loops.
X   h) optional expressions in the for loop.
X   i) print statement to print multiple numbers per line.
X   j) warranty statement to print an extended warranty notice.
X   j) limits statement to print the processor's limits.
X*/
X
X%token <i_value> NEWLINE AND OR NOT
X%token <s_value> STRING NAME NUMBER
X/*     '-', '+' are tokens themselves		*/
X%token <c_value> MUL_OP
X/*     '*', '/', '%' 				*/
X%token <c_value> ASSIGN_OP
X/*     '=', '+=',  '-=', '*=', '/=', '%=', '^=' */
X%token <s_value> REL_OP
X/*     '==', '<=', '>=', '!=', '<', '>' 	*/
X%token <c_value> INCR_DECR
X/*     '++', '--' 				*/
X%token <i_value> Define    Break    Quit    Length
X/*     'define', 'break', 'quit', 'length' 	*/
X%token <i_value> Return    For    If    While    Sqrt   Else
X/*     'return', 'for', 'if', 'while', 'sqrt', 'else' 	*/
X%token <i_value> Scale    Ibase    Obase    Auto  Read
X/*     'scale', 'ibase', 'obase', 'auto', 'read' 	*/
X%token <i_value> Warranty, Halt, Last, Continue, Print, Limits
X/*     'warranty', 'halt', 'last', 'continue', 'print', 'limits'   */
X
X/* Types of all other things. */
X%type <i_value> expression return_expression named_expression opt_expression
X%type <c_value> '+' '-' 
X%type <a_value> opt_parameter_list opt_auto_define_list define_list
X%type <a_value> opt_argument_list argument_list
X%type <i_value> program input_item semicolon_list statement_list
X%type <i_value> statement function   statement_or_error
X
X/* precedence */
X%left OR
X%left AND
X%nonassoc NOT
X%left REL_OP
X%right ASSIGN_OP
X%left '+' '-'
X%left MUL_OP
X%right '^'
X%nonassoc UNARY_MINUS
X%nonassoc INCR_DECR
X
X%%
Xprogram			: /* empty */
X			    {
X			      $$ = 0;
X			      if (interactive)
X				{
X				  printf ("%s\n", BC_VERSION);
X				  welcome ();
X				}
X			    }
X			| program input_item
X			;
Xinput_item		: semicolon_list NEWLINE
X			    { run_code (); }
X			| function
X			    { run_code (); }
X			| error NEWLINE
X			    {
X			      yyerrok;
X			      init_gen ();
X			    }
X			;
Xsemicolon_list		: /* empty */
X			    { $$ = 0; }
X			| statement_or_error
X			| semicolon_list ';' statement_or_error
X			| semicolon_list ';'
X			;
Xstatement_list		: /* empty */
X			    { $$ = 0; }
X			| statement_or_error
X			| statement_list NEWLINE
X			| statement_list NEWLINE statement_or_error
X			| statement_list ';'
X			| statement_list ';' statement
X			;
Xstatement_or_error	: statement
X  			| error statement
X			    { $$ = $2; }
X			;
Xstatement 		: Warranty
X			    { warranty (""); }
X			| Limits
X			    { limits (); }
X			| expression
X			    {
X			      if ($1 & 2)
X				warn ("comparison in expression");
X			      if ($1 & 1)
X				generate ("W");
X			      else 
X				generate ("p");
X			    }
X			| STRING
X			    {
X			      $$ = 0;
X			      generate ("w");
X			      generate ($1);
X			      free ($1);
X			    }
X			| Break
X			    {
X			      if (break_label == 0)
X				yyerror ("Break outside a for/while");
X			      else
X				{
X				  sprintf (genstr, "J%1d:", break_label);
X				  generate (genstr);
X				}
X			    }
X			| Continue
X			    {
X			      warn ("Continue statement");
X			      if (continue_label == 0)
X				yyerror ("Continue outside a for");
X			      else
X				{
X				  sprintf (genstr, "J%1d:", continue_label);
X				  generate (genstr);
X				}
X			    }
X			| Quit
X			    { exit (0); }
X			| Halt
X			    { generate ("h"); }
X			| Return
X			    { generate ("0R"); }
X			| Return '(' return_expression ')'
X			    { generate ("R"); }
X			| For 
X			    {
X			      $1 = break_label; 
X			      break_label = next_label++;
X			    }
X			  '(' opt_expression ';'
X			    {
X			      if ($4 > 1)
X				warn ("Comparison in first for expression");
X			      $4 = next_label++;
X			      if ($4 < 0)
X				sprintf (genstr, "N%1d:", $4);
X			      else
X				sprintf (genstr, "pN%1d:", $4);
X			      generate (genstr);
X			    }
X			  opt_expression ';'
X			    {
X			      if ($7 < 0) generate ("1");
X			      $7 = next_label++;
X			      sprintf (genstr, "B%1d:J%1d:", $7, break_label);
X			      generate (genstr);
X			      $<i_value>$ = continue_label;
X			      continue_label = next_label++;
X			      sprintf (genstr, "N%1d:", continue_label);
X			      generate (genstr);
X			    }
X			  opt_expression ')'
X			    {
X			      if ($10 > 1)
X				warn ("Comparison in third for expression");
X			      if ($10 < 0)
X				sprintf (genstr, "J%1d:N%1d:", $4, $7);
X			      else
X				sprintf (genstr, "pJ%1d:N%1d:", $4, $7);
X			      generate (genstr);
X			    }
X			  statement
X			    {
X			      sprintf (genstr, "J%1d:N%1d:",
X				       continue_label, break_label);
X			      generate (genstr);
X			      break_label = $1;
X			      continue_label = $<i_value>9;
X			    }
X			| If '(' expression ')' 
X			    {
X			      $3 = if_label;
X			      if_label = next_label++;
X			      sprintf (genstr, "Z%1d:", if_label);
X			      generate (genstr);
X			    }
X			  statement  opt_else
X			    {
X			      sprintf (genstr, "N%1d:", if_label); 
X			      generate (genstr);
X			      if_label = $3;
X			    }
X			| While 
X			    {
X			      $1 = next_label++;
X			      sprintf (genstr, "N%1d:", $1);
X			      generate (genstr);
X			    }
X			'(' expression 
X			    {
X			      $4 = break_label; 
X			      break_label = next_label++;
X			      sprintf (genstr, "Z%1d:", break_label);
X			      generate (genstr);
X			    }
X			')' statement
X			    {
X			      sprintf (genstr, "J%1d:N%1d:", $1, break_label);
X			      generate (genstr);
X			      break_label = $4;
X			    }
X			| '{' statement_list '}'
X			    { $$ = 0; }
X			| Print
X			    {  warn ("print statement"); }
X			  print_list
X			;
Xprint_list		: print_element
X 			| print_element ',' print_list
X			;
Xprint_element		: STRING
X			    {
X			      generate ("O");
X			      generate ($1);
X			      free ($1);
X			    }
X			| expression
X			    { generate ("P"); }
X 			;
Xopt_else		: /* nothing */
X			| Else 
X			    {
X			      warn ("else clause in if statement");
X			      $1 = next_label++;
X			      sprintf (genstr, "J%d:N%1d:", $1, if_label); 
X			      generate (genstr);
X			      if_label = $1;
X			    }
X			  statement
Xfunction 		: Define NAME '(' opt_parameter_list ')' '{'
X			  NEWLINE opt_auto_define_list 
X			    {
X			      /* Check auto list against parameter list? */
X			      check_params ($4,$8);
X			      sprintf (genstr, "F%d,%s.%s[", lookup($2,FUNCT), 
X				       arg_str ($4,TRUE), arg_str ($8,TRUE));
X			      generate (genstr);
X			      free_args ($4);
X			      free_args ($8);
X			      $1 = next_label;
X			      next_label = 0;
X			    }
X			  statement_list NEWLINE '}'
X			    {
X			      generate ("0R]");
X			      next_label = $1;
X			    }
X			;
Xopt_parameter_list	: /* empty */ 
X			    { $$ = NULL; }
X			| define_list
X			;
Xopt_auto_define_list 	: /* empty */ 
X			    { $$ = NULL; }
X			| Auto define_list NEWLINE
X			    { $$ = $2; } 
X			| Auto define_list ';'
X			    { $$ = $2; } 
X			;
Xdefine_list 		: NAME
X			    { $$ = nextarg (NULL, lookup ($1,SIMPLE)); }
X			| NAME '[' ']'
X			    { $$ = nextarg (NULL, lookup ($1,ARRAY)); }
X			| define_list ',' NAME
X			    { $$ = nextarg ($1, lookup ($3,SIMPLE)); }
X			| define_list ',' NAME '[' ']'
X			    { $$ = nextarg ($1, lookup ($3,ARRAY)); }
X			;
Xopt_argument_list	: /* empty */
X			    { $$ = NULL; }
X			| argument_list
X			;
Xargument_list 		: expression
X			    {
X			      if ($1 > 1) warn ("comparison in argument");
X			      $$ = nextarg (NULL,0);
X			    }
X			| NAME '[' ']'
X			    {
X			      sprintf (genstr, "K%d:", -lookup ($1,ARRAY));
X			      generate (genstr);
X			      $$ = nextarg (NULL,1);
X			    }
X			| argument_list ',' expression
X			    {
X			      if ($3 > 1) warn ("comparison in argument");
X			      $$ = nextarg ($1,0);
X			    }
X			| argument_list ',' NAME '[' ']'
X			    {
X			      sprintf (genstr, "K%d:", -lookup ($3,ARRAY));
X			      generate (genstr);
X			      $$ = nextarg ($1,1);
X			    }
X			;
Xopt_expression 		: /* empty */
X			    {
X			      $$ = -1;
X			      warn ("Missing expression in for statement");
X			    }
X			| expression
X			;
Xreturn_expression	: /* empty */
X			    {
X			      $$ = 0;
X			      generate ("0");
X			    }
X			| expression
X			    {
X			      if ($1 > 1)
X				warn ("comparison in return expresion");
X			    }
X			;
Xexpression		:  named_expression ASSIGN_OP 
X			    {
X			      if ($2 != '=')
X				{
X				  if ($1 < 0)
X				    sprintf (genstr, "DL%d:", -$1);
X				  else
X				    sprintf (genstr, "l%d:", $1);
X				  generate (genstr);
X				}
X			    }
X			  expression
X			    {
X			      if ($4 > 1) warn("comparison in assignment");
X			      if ($2 != '=')
X				{
X				  sprintf (genstr, "%c", $2);
X				  generate (genstr);
X				}
X			      if ($1 < 0)
X				sprintf (genstr, "S%d:", -$1);
X			      else
X				sprintf (genstr, "s%d:", $1);
X			      generate (genstr);
X			      $$ = 0;
X			    }
X			;
X			| expression AND 
X			    {
X			      warn("&& operator");
X			      $2 = next_label++;
X			      sprintf (genstr, "DZ%d:p", $2);
X			      generate (genstr);
X			    }
X			  expression
X			    {
X			      sprintf (genstr, "DZ%d:p1N%d:", $2, $2);
X			      generate (genstr);
X			      $$ = $1 | $4;
X			    }
X			| expression OR
X			    {
X			      warn("|| operator");
X			      $2 = next_label++;
X			      sprintf (genstr, "B%d:", $2);
X			      generate (genstr);
X			    }
X			  expression
X 			    {
X			      int tmplab;
X			      tmplab = next_label++;
X			      sprintf (genstr, "B%d:0J%d:N%d:1N%d:",
X				       $2, tmplab, $2, tmplab);
X			      generate (genstr);
X			      $$ = $1 | $4;
X			    }
X			| NOT expression
X			    {
X			      $$ = $2;
X			      warn("! operator");
X			      generate ("!");
X			    }
X			| expression REL_OP expression
X			    {
X			      $$ = 3;
X			      switch (*($2))
X				{
X				case '=':
X				  generate ("=");
X				  break;
X
X				case '!':
X				  generate ("#");
X				  break;
X
X				case '<':
X				  if ($2[1] == '=')
X				    generate ("{");
X				  else
X				    generate ("<");
X				  break;
X
X				case '>':
X				  if ($2[1] == '=')
X				    generate ("}");
X				  else
X				    generate (">");
X				  break;
X				}
X			    }
X			| expression '+' expression
X			    {
X			      generate ("+");
X			      $$ = $1 | $3;
X			    }
X			| expression '-' expression
X			    {
X			      generate ("-");
X			      $$ = $1 | $3;
X			    }
X			| expression MUL_OP expression
X			    {
X			      genstr[0] = $2;
X			      genstr[1] = 0;
X			      generate (genstr);
X			      $$ = $1 | $3;
X			    }
X			| expression '^' expression
X			    {
X			      generate ("^");
X			      $$ = $1 | $3;
X			    }
X			| '-' expression  %prec UNARY_MINUS
X			    {
X			      generate ("n");
X			      $$ = $2;
X			    }
X			| named_expression
X			    {
X			      $$ = 1;
X			      if ($1 < 0)
X				sprintf (genstr, "L%d:", -$1);
X			      else
X				sprintf (genstr, "l%d:", $1);
X			      generate (genstr);
X			    }
X			| NUMBER
X			    {
X			      int len = strlen($1);
X			      $$ = 1;
X			      if (len == 1 && *$1 == '0')
X				generate ("0");
X			      else if (len == 1 && *$1 == '1')
X				generate ("1");
X			      else
X				{
X				  generate ("K");
X				  generate ($1);
X				  generate (":");
X				}
X			      free ($1);
X			    }
X			| '(' expression ')'
X			    { $$ = $2 | 1; }
X			| NAME '(' opt_argument_list ')'
X			    {
X			      $$ = 1;
X			      if ($3 != NULL)
X				{ 
X				  sprintf (genstr, "C%d,%s:",
X					   lookup ($1,FUNCT),
X					   arg_str ($3,FALSE));
X				  free_args ($3);
X				}
X			      else
X				{
X				  sprintf (genstr, "C%d:", lookup ($1,FUNCT));
X				}
X			      generate (genstr);
X			    }
X			| INCR_DECR named_expression
X			    {
X			      $$ = 1;
X			      if ($2 < 0)
X				{
X				  if ($1 == '+')
X				    sprintf (genstr, "DA%d:L%d:", -$2, -$2);
X				  else
X				    sprintf (genstr, "DM%d:L%d:", -$2, -$2);
X				}
X			      else
X				{
X				  if ($1 == '+')
X				    sprintf (genstr, "i%d:l%d:", $2, $2);
X				  else
X				    sprintf (genstr, "d%d:l%d:", $2, $2);
X				}
X			      generate (genstr);
X			    }
X			| named_expression INCR_DECR
X			    {
X			      $$ = 1;
X			      if ($1 < 0)
X				{
X				  sprintf (genstr, "DL%d:x", -$1);
X				  generate (genstr); 
X				  if ($2 == '+')
X				    sprintf (genstr, "A%d:", -$1);
X				  else
X				      sprintf (genstr, "M%d:", -$1);
X				}
X			      else
X				{
X				  sprintf (genstr, "l%d:", $1);
X				  generate (genstr);
X				  if ($2 == '+')
X				    sprintf (genstr, "i%d:", $1);
X				  else
X				    sprintf (genstr, "d%d:", $1);
X				}
X			      generate (genstr);
X			    }
X			| Length '(' expression ')'
X			    { generate ("cL"); $$ = 1;}
X			| Sqrt '(' expression ')'
X			    { generate ("cR"); $$ = 1;}
X			| Scale '(' expression ')'
X			    { generate ("cS"); $$ = 1;}
X			| Read '(' ')'
X			    {
X			      warn ("read function");
X			      generate ("cI"); $$ = 1;
X			    }
X			;
Xnamed_expression	: NAME
X			    { $$ = lookup($1,SIMPLE); }
X			| NAME '[' expression ']'
X			    {
X			      if ($3 > 1) warn("comparison in subscript");
X			      $$ = lookup($1,ARRAY);
X			    }
X			| Ibase
X			    { $$ = 0; }
X			| Obase
X			    { $$ = 1; }
X			| Scale
X			    { $$ = 2; }
X			| Last
X			    { $$ = 3; }
X			;
X%%
/
echo x - bcdefs.h
sed '/^X/s///' > bcdefs.h << '/'
X/* bcdefs.h:  The single file to include all constants and type definitions. */
X
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X/* Include the configuration file. */
X#include "config.h"
X
X/* Standard includes for all files. */
X#include <stdio.h>
X#include <sys/types.h>
X#include <ctype.h>
X#ifdef STRINGS_H
X#include <strings.h>
X#else
X#include <string.h>
X#endif
X#ifndef NO_LIMITS
X#include <limits.h>
X#endif
X
X/* Include the other definitions. */
X#include "const.h"
X#include "number.h"
X
X
X/* These definitions define all the structures used in
X   code and data storage.  This includes the representation of
X   labels.   The "guiding" principle is to make structures that
X   take a minimum of space when unused but can be built to contain
X   the full structures.  */
X
X/* Labels are first.  Labels are generated sequentially in functions
X   and full code.  They just "point" to a single bye in the code.  The
X   "address" is the byte number.  The byte number is used to get an
X   actual character pointer. */
X
Xtypedef struct bc_label_group
X    {
X      long l_adrs [ BC_LABEL_GROUP ];
X      struct bc_label_group *l_next;
X    } bc_label_group;
X
X
X/* Each function has its own code segments and labels.  There can be
X   no jumps between functions so labels are unique to a function. */
X
Xtypedef struct arg_list
X    {
X      int av_name;
X      struct arg_list *next;
X    } arg_list;
X
Xtypedef struct 
X    {
X      char f_defined;   /* Is this function defined yet. */
X      char *f_body[BC_MAX_SEGS];
X      int   f_code_size;
X      bc_label_group *f_label;
X      arg_list *f_params;
X      arg_list *f_autos;
X    } bc_function;
X
X/* Code addresses. */
Xtypedef struct {
X      int pc_func;
X      int pc_addr;
X    } program_counter;
X
X
X/* Variables are "pushable" (auto) and thus we need a stack mechanism.
X   This is built into the variable record. */
X
Xtypedef struct bc_var
X    {
X      bc_num v_value;
X      struct bc_var *v_next;
X    }  bc_var;
X
X
X/* bc arrays can also be "auto" variables and thus need the same
X   kind of stacking mechanisms. */
X
Xtypedef struct bc_array_node
X    {
X      union
X	{
X	  bc_num n_num [NODE_SIZE];
X	  struct bc_array_node *n_down [NODE_SIZE];
X	} n_items;
X    } bc_array_node;
X
Xtypedef struct bc_array
X    {
X      bc_array_node *a_tree;
X      short a_depth;
X    } bc_array;
X
Xtypedef struct bc_var_array
X    {
X      bc_array *a_value;
X      char      a_param;
X      struct bc_var_array *a_next;
X    } bc_var_array;
X
X
X/* For the stacks, execution and function, we need records to allow
X   for arbitrary size. */
X
Xtypedef struct estack_rec {
X	bc_num s_num;
X	struct estack_rec *s_next;
X} estack_rec;
X
Xtypedef struct fstack_rec {
X	int  s_val;
X	struct fstack_rec *s_next;
X} fstack_rec;
X
X
X/* The following are for the name tree. */
X
Xtypedef struct id_rec {
X	char  *id;      /* The program name. */
X			/* A name == 0 => nothing assigned yet. */
X	int   a_name;   /* The array variable name (number). */
X	int   f_name;   /* The function name (number).  */
X	int   v_name;   /* The variable name (number).  */
X        short balance;  /* For the balanced tree. */
X	struct id_rec *left, *right; /* Tree pointers. */
X} id_rec;
/
echo x - config.h
sed '/^X/s///' > config.h << '/'
X/* config.h */
X#define SMALL_BUF
X#define BC_MATH_FILE "/usr/local/lib/libmath.b"
X#define SHORTNAMES
/
echo x - configure
sed '/^X/s///' > configure << '/'
X#!/bin/sh
X#
X# The shell script for bc to automatically create the config.h file.
X# 
X# Syntax:  configure [ include-dir [ lib-file [ c-compiler ]]]
X#
X# Set some standard things.
X#
XINCLUDE=${1-/usr/include}
XLIBFILE=${2-libmath.b}
XCC=${3-cc}
X#
X# Initialize the exit status
X#
XEXIT=0
X#
X# Remove the current copy and make sure there is at least an empty file
X#
Xrm -f config.h
Xecho "/* config.h */" > config.h
X#
X# Check for the argument passing mechanism.
X#
Xif test -r $INCLUDE/varargs.h
Xthen
Xecho "Using varargs.h for variable arguments (non ansi compilers). "
Xecho "#ifndef __STDC__" >> config.h
Xecho "#define VARARGS" >> config.h
Xecho "#endif" >> config.h
Xelse
Xif test -r $INCLUDE/stdarg.h
Xthen
Xecho "Using stdarg.h for variable arguments. "
Xelse
Xecho "Unknown variable argument mechanism. "
XEXIT=1
Xfi
Xfi
X#
X# Limits file?
X#
Xif test -r $INCLUDE/limits.h
Xthen
Xecho "You have a limits.h file."
Xelse
Xecho "You need to check the limits in const.h."
Xecho "#define NO_LIMITS" >> config.h
Xfi
X#
X# unistd?
X#
Xif test -r $INCLUDE/unistd.h
Xthen
Xecho "You have a unistd.h file."
Xelse
Xecho "#define NO_UNISTD" >> config.h
Xfi
X#
X# Stdlib?
X#
Xif test -r $INCLUDE/stdlib.h
Xthen
Xecho "You have a stdlib.h file."
Xelse
Xecho "#define NO_STDLIB" >> config.h
Xfi
X#
X# Strinog?
X#
Xif test -r $INCLUDE/string.h
Xthen
Xecho "You have a string.h file."
Xelse
Xecho "#define STRINGS_H" >> config.h
Xfi
X#
X# At least one BSD system did not define "extern int errno;" in errno.h
X#
Xif grep -s extern $INCLUDE/errno.h
Xthen
Xtrue
Xelse
Xecho "extern int errno;" >> config.h
Xfi
X#
X# On MINIX pc systems we want to define some extra files.
X#
Xif test -r $INCLUDE/minix
Xthen
X# We must be on a minix system.  Check for the machine type.
Xif test `machine` = IBM_PC
Xthen
Xecho "#define SMALL_BUF" >> config.h
Xecho "#define BC_MATH_FILE \"$LIBFILE\"" >> config.h
Xecho "#define SHORTNAMES" >> config.h
Xelse
Xecho "Your Minix is not on a PC.  The bc library will be loaded in the"
Xecho "executable file.  You will not have to install a library file."
Xfi
Xelse
X# Test to see if the compiler will compile long strings...
Xecho "char libmath[] =" > JUNK.c
Xecho "#include \"math.h\"" >> JUNK.c
Xecho ";" >> JUNK.c
Xif $CC -c JUNK.c >/dev/null 2>&1
Xthen
Xecho "The bc library will be loaded in the executable file.  You will"
Xecho "not have to install a library file."
Xelse
Xecho "Your C compiler does not like long strings.  You will have to"
Xecho "load the math library from a file."
Xecho "#define BC_MATH_FILE \"$LIBFILE\"" >> config.h
Xfi
Xrm -f JUNK.c JUNK.o
Xfi
X#
X# exit
X#
Xexit $EXIT
/
echo x - const.h
sed '/^X/s///' > const.h << '/'
X/* const.h: Constants for bc. */
X
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X
X/* Define INT_MAX and LONG_MAX if not defined.  Assuming 32 bits... */
X
X#ifdef NO_LIMITS
X#define INT_MAX 0x7FFFFFFF
X#define LONG_MAX 0x7FFFFFFF
X#endif
X
X
X/* Define constants in some reasonable size.  The next 4 constants are
X   POSIX constants. */
X
X#define BC_BASE_MAX   INT_MAX
X#define BC_SCALE_MAX  INT_MAX
X#define BC_STRING_MAX INT_MAX
X
X
X/* Definitions for arrays. */
X
X#define BC_DIM_MAX    65535       /* this should be NODE_SIZE^NODE_DEPTH-1 */
X
X#define   NODE_SIZE        16     /* Must be a power of 2. */
X#define   NODE_MASK       0xf     /* Must be NODE_SIZE-1. */
X#define   NODE_SHIFT        4     /* Number of 1 bits in NODE_MASK. */
X#define   NODE_DEPTH        4
X
X
X/* Other BC limits defined but not part of POSIX. */
X
X#define BC_LABEL_GROUP 64
X#define BC_LABEL_LOG    6
X#define BC_MAX_SEGS    16    /* Code segments. */
X#define BC_SEG_SIZE  1024
X#define BC_SEG_LOG     10
X
X/* Maximum number of variables, arrays and functions and the
X   allocation increment for the dynamic arrays. */
X
X#define MAX_STORE   32767
X#define STORE_INCR     32
X
X/* Other interesting constants. */
X
X#define FALSE 0
X#define TRUE  1
X#define SIMPLE 0
X#define ARRAY  1
X#define FUNCT  2
X#define EXTERN extern
X#ifdef __STDC__
X#define CONST const
X#define VOID  void
X#else
X#define CONST
X#define VOID
X#endif
X
X/* Include the version definition. */
X#include "version.h"
/
echo x - execute.c
sed '/^X/s///' > execute.c << '/'
X/* execute.c - run a bc program. */
X
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X#include "bcdefs.h"
X#include <signal.h>
X#include "global.h"
X#include "proto.h"
X
X
X/* The SIGINT interrupt handling routine. */
X
Xint had_sigint;
X
Xvoid
Xstop_execution (sig)
X     int sig;
X{
X  had_sigint = TRUE;
X  printf ("\n");
X  rt_error ("interrupted execution");
X}
X
X
X/* Get the current byte and advance the PC counter. */
X
Xunsigned char
Xbyte (pc)
X     program_counter *pc;
X{
X  int seg, offset;
X    
X  seg = pc->pc_addr >> BC_SEG_LOG;
X  offset = pc->pc_addr++ % BC_SEG_SIZE;
X  return (functions[pc->pc_func].f_body[seg][offset]);
X}
X
X
X/* The routine that actually runs the machine. */
X
Xvoid
Xexecute ()
X{
X  int label_num, l_gp, l_off;
X  bc_label_group *gp;
X  
X  char inst, ch;
X  int  new_func;
X  int  var_name;
X
X  int const_base;
X
X  bc_num temp_num;
X  arg_list *auto_list;
X
X  /* Initialize this run... */
X  pc.pc_func = 0;
X  pc.pc_addr = 0;
X  runtime_error = FALSE;
X  init_num (&temp_num);
X
X  /* Set up the interrupt mechanism for an interactive session. */
X  if (interactive)
X    {
X      signal (SIGINT, stop_execution);
X      had_sigint = FALSE;
X    }
X   
X  while (pc.pc_addr < functions[pc.pc_func].f_code_size && !runtime_error)
X    {
X      inst = byte(&pc);
X
X#if DEBUG > 3
X      { /* Print out address and the stack before each instruction.*/
X	int depth; estack_rec *temp = ex_stack;
X	
X	printf ("func=%d addr=%d inst=%c\n",pc.pc_func, pc.pc_addr, inst);
X	if (temp == NULL) printf ("empty stack.\n", inst);
X	else
X	  {
X	    depth = 1;
X	    while (temp != NULL)
X	      {
X		printf ("   %d = ", depth);
X		out_num (temp->s_num, 10, out_char);
X		depth++;
X		temp = temp->s_next;
X	      }
X	  }
X      }
X#endif
X
X    switch ( inst )
X      {
X
X      case 'A' : /* increment array variable (Add one). */
X	var_name = byte(&pc);
X	if ((var_name & 0x80) != 0)
X	  var_name = ((var_name << 8) & 0x7f) + byte(&pc);
X	incr_array (var_name);
X	break;
X
X      case 'B' : /* Branch to a label if TOS != 0. Remove value on TOS. */
X      case 'Z' : /* Branch to a label if TOS == 0. Remove value on TOS. */
X	c_code = !is_zero (ex_stack->s_num);
X	pop ();
X      case 'J' : /* Jump to a label. */
X	label_num = byte(&pc);  /* Low order bits first. */
X	label_num += byte(&pc) << 8;
X	if (inst == 'J' || (inst == 'B' && c_code)
X	    || (inst == 'Z' && !c_code)) {
X	  gp = functions[pc.pc_func].f_label;
X	  l_gp  = label_num >> BC_LABEL_LOG;
X	  l_off = label_num % BC_LABEL_GROUP;
X	  while (l_gp-- > 0) gp = gp->l_next;
X	  pc.pc_addr = gp->l_adrs[l_off];
X	}
X	break;
X
X      case 'C' : /* Call a function. */
X	/* Get the function number. */
X	new_func = byte(&pc);
X	if ((new_func & 0x80) != 0) 
X	  new_func = ((new_func << 8) & 0x7f) + byte(&pc);
X
X	/* Check to make sure it is defined. */
X	if (!functions[new_func].f_defined)
X	  {
X	    rt_error ("Function %s not defined.", f_names[new_func]);
X	    break;
X	  }
X
X	/* Check and push parameters. */
X	process_params (&pc, new_func);
X
X	/* Push auto variables. */
X	for (auto_list = functions[new_func].f_autos;
X	     auto_list != NULL;
X	     auto_list = auto_list->next)
X	  auto_var (auto_list->av_name);
X
X	/* Push pc and ibase. */
X	fpush (pc.pc_func);
X	fpush (pc.pc_addr);
X	fpush (i_base);
X
X	/* Reset pc to start of function. */
X	pc.pc_func = new_func;
X	pc.pc_addr = 0;
X	break;
X
X      case 'D' : /* Duplicate top of stack */
X	push_copy (ex_stack->s_num);
X	break;
X
X      case 'K' : /* Push a constant */
X	/* Get the input base and convert it to a bc number. */
X	if (pc.pc_func == 0) 
X	  const_base = i_base;
X	else
X	  const_base = fn_stack->s_val;
X	if (const_base == 10)
X	  push_b10_const (&pc);
X	else
X	  push_constant (prog_char, const_base);
X	break;
X
X      case 'L' : /* load array variable */
X	var_name = byte(&pc);
X	if ((var_name & 0x80) != 0)
X	  var_name = ((var_name << 8) & 0x7f) + byte(&pc);
X	load_array (var_name);
X	break;
X
X      case 'M' : /* decrement array variable (Minus!) */
X	var_name = byte(&pc);
X	if ((var_name & 0x80) != 0)
X	  var_name = ((var_name << 8) & 0x7f) + byte(&pc);
X	decr_array (var_name);
X	break;
X
X      case 'O' : /* Write a string to the output with processing. */
X	while ((ch = byte(&pc)) != '"')
X	  if (ch != '\\')
X	    out_char (ch);
X	  else
X	    {
X	      ch = byte(&pc);
X	      if (ch == '"') break;
X	      switch (ch)
X		{
X		case 'n':  out_char ('\n'); break;
X		case 't':  out_char ('\t'); break;
X		case 'r':  out_char ('\r'); break;
X		case 'b':  out_char (007); break;
X		case 'f':  out_char ('\f'); break;
X		case '\\': out_char ('\\'); break;
X		default:  break;
X		}
X	    }
X	if (interactive) fflush (stdout);
X	break;
X
X      case 'R' : /* Return from function */
X	if (pc.pc_func != 0)
X	  {
X	    /* "Pop" autos and parameters. */
X	    pop_vars(functions[pc.pc_func].f_autos);
X	    pop_vars(functions[pc.pc_func].f_params);
X	    /* reset the pc. */
X	    fpop ();
X	    pc.pc_addr = fpop ();
X	    pc.pc_func = fpop ();
X	  }
X	else
X	  rt_error ("Return from main program.");
X	break;
X
X      case 'S' : /* store array variable */
X	var_name = byte(&pc);
X	if ((var_name & 0x80) != 0)
X	  var_name = ((var_name << 8) & 0x7f) + byte(&pc);
X	store_array (var_name);
X	break;
X
X      case 'T' : /* Test tos for zero */
X	c_code = is_zero (ex_stack->s_num);
X	assign (c_code);
X	break;
X
X      case 'W' : /* Write the value on the top of the stack. */
X      case 'P' : /* Write the value on the top of the stack.  No newline. */
X	out_num (ex_stack->s_num, o_base, out_char);
X	if (inst == 'W') out_char ('\n');
X	store_var (3);  /* Special variable "last". */
X	if (interactive) fflush (stdout);
X	break;
X
X      case 'c' : /* Call special function. */
X	new_func = byte(&pc);
X
X      switch (new_func)
X	{
X	case 'L':  /* Length function. */
X	  /* For the number 0.xxxx,  0 is not significant. */
X	  if (ex_stack->s_num->n_len == 1 &&
X	      ex_stack->s_num->n_scale != 0 &&
X	      ex_stack->s_num->n_value[0] == 0 )
X	    int2num (&ex_stack->s_num, ex_stack->s_num->n_scale);
X	  else
X	    int2num (&ex_stack->s_num, ex_stack->s_num->n_len
X		     + ex_stack->s_num->n_scale);
X	  break;
X		
X	case 'S':  /* Scale function. */ 
X	  int2num (&ex_stack->s_num, ex_stack->s_num->n_scale);
X	  break;
X
X	case 'R':  /* Square Root function. */
X	  if (!bc_sqrt (&ex_stack->s_num, scale))
X	    rt_error ("Square root of a negative number");
X	  break;
X
X	case 'I': /* Read function. */
X	  push_constant (input_char, i_base);
X	  break;
X	}
X	break;
X
X      case 'd' : /* Decrement number */
X	var_name = byte(&pc);
X	if ((var_name & 0x80) != 0)
X	  var_name = ((var_name << 8) & 0x7f) + byte(&pc);
X	decr_var (var_name);
X	break;
X      
X      case 'h' : /* Halt the machine. */
X	exit (0);
X
X      case 'i' : /* increment number */
X	var_name = byte(&pc);
X	if ((var_name & 0x80) != 0)
X	  var_name = ((var_name << 8) & 0x7f) + byte(&pc);
X	incr_var (var_name);
X	break;
X
X      case 'l' : /* load variable */
X	var_name = byte(&pc);
X	if ((var_name & 0x80) != 0)
X	  var_name = ((var_name << 8) & 0x7f) + byte(&pc);
X	load_var (var_name);
X	break;
X
X      case 'n' : /* Negate top of stack. */
X	bc_sub (_zero_, ex_stack->s_num, &ex_stack->s_num);
X	break;
X
X      case 'p' : /* Pop the execution stack. */
X	pop ();
X	break;
X
X      case 's' : /* store variable */
X	var_name = byte(&pc);
X	if ((var_name & 0x80) != 0)
X	  var_name = ((var_name << 8) & 0x7f) + byte(&pc);
X	store_var (var_name);
X	break;
X
X      case 'w' : /* Write a string to the output. */
X	while ((ch = byte(&pc)) != '"') out_char (ch);
X	if (interactive) fflush (stdout);
X	break;
X		   
X      case 'x' : /* Exchange Top of Stack with the one under the tos. */
X	if (check_stack(2)) {
X	  bc_num temp = ex_stack->s_num;
X	  ex_stack->s_num = ex_stack->s_next->s_num;
X	  ex_stack->s_next->s_num = temp;
X	}
X	break;
X
X      case '0' : /* Load Constant 0. */
X	push_copy (_zero_);
X	break;
X
X      case '1' : /* Load Constant 0. */
X	push_copy (_one_);
X	break;
X
X      case '!' : /* Negate the boolean value on top of the stack. */
X	c_code = is_zero (ex_stack->s_num);
X	assign (c_code);
X	break;
X
X      case '&' : /* compare greater than */
X	if (check_stack(2))
X	  {
X	    c_code = !is_zero (ex_stack->s_next->s_num)
X	      && !is_zero (ex_stack->s_num);
X	    pop ();
X	    assign (c_code);
X	  }
X	break;
X
X      case '|' : /* compare greater than */
X	if (check_stack(2))
X	  {
X	    c_code = !is_zero (ex_stack->s_next->s_num)
X	      || !is_zero (ex_stack->s_num);
X	    pop ();
X	    assign (c_code);
X	  }
X	break;
X
X      case '+' : /* add */
X	if (check_stack(2))
X	  {
X	    bc_add (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num);
X	    pop();
X	    pop();
X	    push_num (temp_num);
X	    init_num (&temp_num);
X	  }
X	break;
X
X      case '-' : /* subtract */
X	if (check_stack(2))
X	  {
X	    bc_sub (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num);
X	    pop();
X	    pop();
X	    push_num (temp_num);
X	    init_num (&temp_num);
X	  }
X	break;
X
X      case '*' : /* multiply */
X	if (check_stack(2))
X	  {
X	    bc_multiply (ex_stack->s_next->s_num, ex_stack->s_num,
X			 &temp_num, scale);
X	    pop();
X	    pop();
X	    push_num (temp_num);
X	    init_num (&temp_num);
X	  }
X	break;
X
X      case '/' : /* divide */
X	if (check_stack(2))
X	  {
X	    if (bc_divide (ex_stack->s_next->s_num,
X			   ex_stack->s_num, &temp_num, scale) == 0)
X	      {
X		pop();
X		pop();
X		push_num (temp_num);
X		init_num (&temp_num);
X	      }
X	    else
X	      rt_error ("Divide by zero");
X	  }
X	break;
X
X      case '%' : /* remainder */
X	if (check_stack(2))
X	  {
X	    if (is_zero (ex_stack->s_num))
X	      rt_error ("Modulo by zero");
X	    else
X	      {
X		bc_modulo (ex_stack->s_next->s_num,
X			   ex_stack->s_num, &temp_num, scale);
X		pop();
X		pop();
X		push_num (temp_num);
X		init_num (&temp_num);
X	      }
X	  }
X	break;
X
X      case '^' : /* raise */
X	if (check_stack(2))
X	  {
X	    bc_raise (ex_stack->s_next->s_num,
X		      ex_stack->s_num, &temp_num, scale);
X	    if (is_zero (ex_stack->s_next->s_num) && is_neg (ex_stack->s_num))
X	      rt_error ("divide by zero");
X	    pop();
X	    pop();
X	    push_num (temp_num);
X	    init_num (&temp_num);
X	  }
X	break;
X
X      case '=' : /* compare equal */
X	if (check_stack(2))
X	  {
X	    c_code = bc_compare (ex_stack->s_next->s_num,
X				 ex_stack->s_num) == 0;
X	    pop ();
X	    assign (c_code);
X	  }
X	break;
X
X      case '#' : /* compare not equal */
X	if (check_stack(2))
X	  {
X	    c_code = bc_compare (ex_stack->s_next->s_num,
X				 ex_stack->s_num) != 0;
X	    pop ();
X	    assign (c_code);
X	  }
X	break;
X
X      case '<' : /* compare less than */
X	if (check_stack(2))
X	  {
X	    c_code = bc_compare (ex_stack->s_next->s_num,
X				 ex_stack->s_num) == -1;
X	    pop ();
X	    assign (c_code);
X	  }
X	break;
X
X      case '{' : /* compare less than or equal */
X	if (check_stack(2))
X	  {
X	    c_code = bc_compare (ex_stack->s_next->s_num,
X				 ex_stack->s_num) <= 0;
X	    pop ();
X	    assign (c_code);
X	  }
X	break;
X
X      case '>' : /* compare greater than */
X	if (check_stack(2))
X	  {
X	    c_code = bc_compare (ex_stack->s_next->s_num,
X				 ex_stack->s_num) == 1;
X	    pop ();
X	    assign (c_code);
X	  }
X	break;
X
X      case '}' : /* compare greater than or equal */
X	if (check_stack(2))
X	  {
X	    c_code = bc_compare (ex_stack->s_next->s_num,
X				 ex_stack->s_num) >= 0;
X	    pop ();
X	    assign (c_code);
X	  }
X	break;
X
X	default  : /* error! */
X	  rt_error ("bad instruction: inst=%c", inst);
X      }
X    }
X
X  /* Clean up the function stack and pop all autos/parameters. */
X  while (pc.pc_func != 0)
X    {
X      pop_vars(functions[pc.pc_func].f_autos);
X      pop_vars(functions[pc.pc_func].f_params);
X      fpop ();
X      pc.pc_addr = fpop ();
X      pc.pc_func = fpop ();
X    }
X
X  /* Clean up the execution stack. */ 
X  while (ex_stack != NULL) pop();
X
X  /* Clean up the interrupt stuff. */
X  if (interactive)
X    {
X      signal (SIGINT, use_quit);
X      if (had_sigint)
X	printf ("Interruption completed.\n");
X    }
X}
X
X
X/* Prog_char gets another byte from the program.  It is used for
X   conversion of text constants in the code to numbers. */
X
Xchar
Xprog_char ()
X{
X  return byte(&pc);
X}
X
X
X/* Read a character from the standard input.  This function is used
X   by the "read" function. */
X
Xchar
Xinput_char ()
X{
X  char in_ch;
X  
X  /* Get a character from the standard input for the read function. */
X  in_ch = getchar();
X
X  /* Check for a \ quoted newline. */
X  if (in_ch == '\\')
X    {
X      in_ch = getchar();
X      if (in_ch == '\n')
X	in_ch = getchar();
X    }
X
X  /* Classify and preprocess the input character. */
X  if (isdigit(in_ch))
X    return (in_ch - '0');
X  if (in_ch >= 'A' && in_ch <= 'F')
X    return (in_ch + 10 - 'A');
X  if (in_ch >= 'a' && in_ch <= 'f')
X    return (in_ch + 10 - 'a');
X  if (in_ch == '.' || in_ch == '+' || in_ch == '-')
X    return (in_ch);
X  if (in_ch <= ' ')
X    return (' ');
X  
X  return (':');
X}
X
X
X/* Push_constant converts a sequence of input characters as returned
X   by IN_CHAR into a number.  The number is pushed onto the execution
X   stack.  The number is converted as a number in base CONV_BASE. */
X
Xvoid
Xpush_constant (in_char, conv_base)
X   char (*in_char)(VOID);
X   int conv_base;
X{
X  int digits;
X  bc_num build, temp, result, mult, divisor;
X  char  in_ch, first_ch;
X  char  negative;
X
X  /* Initialize all bc numbers */
X  init_num (&temp);
X  init_num (&result);
X  init_num (&mult);
X  build = copy_num (_zero_);
X  negative = FALSE;
X
X  /* The conversion base. */
X  int2num (&mult, conv_base);
X  
X  /* Get things ready. */
X  in_ch = in_char();
X  while (in_ch == ' ')
X    in_ch = in_char();
X
X  if (in_ch == '+')
X    in_ch = in_char();
X  else
X    if (in_ch == '-')
X      {
X	negative = TRUE;
X	in_ch = in_char();
X      }
X
X  /* Check for the special case of a single digit. */
X  if (in_ch < 16)
X    {
X      first_ch = in_ch;
X      in_ch = in_char();
X      if (in_ch < 16 && first_ch >= conv_base)
X	first_ch = conv_base - 1;
X      int2num (&build, (int) first_ch);
X    }
X
X  /* Convert the integer part. */
X  while (in_ch < 16)
X    {
X      if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1;
X      bc_multiply (build, mult, &result, 0);
X      int2num (&temp, (int) in_ch);
X      bc_add (result, temp, &build);
X      in_ch = in_char();
X    }
X  if (in_ch == '.')
X    {
X      in_ch = in_char();
X      if (in_ch >= conv_base) in_ch = conv_base-1;
X      free_num (&result);
X      free_num (&temp);
X      divisor = copy_num (_one_);
X      result = copy_num (_zero_);
X      digits = 0;
X      while (in_ch < 16)
X	{
X	  bc_multiply (result, mult, &result, 0);
X	  int2num (&temp, (int) in_ch);
X	  bc_add (result, temp, &result);
X	  bc_multiply (divisor, mult, &divisor, 0);
X	  digits++;
X	  in_ch = in_char();
X	  if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1;
X	}
X      bc_divide (result, divisor, &result, digits);
X      bc_add (build, result, &build);
X    }
X  
X  /* Final work.  */
X  if (negative)
X    bc_sub (_zero_, build, &build);
X
X  push_num (build);
X  free_num (&temp);
X  free_num (&result);
X  free_num (&mult);
X}
X
X
X/* When converting base 10 constants from the program, we use this
X   more efficient way to convert them to numbers.  PC tells where
X   the constant starts and is expected to be advanced to after
X   the constant. */
X
Xvoid
Xpush_b10_const (pc)
X     program_counter *pc;
X{
X  bc_num build;
X  program_counter look_pc;
X  int kdigits, kscale;
X  char inchar;
X  char *ptr;
X  
X  /* Count the digits and get things ready. */
X  look_pc = *pc;
X  kdigits = 0;
X  kscale  = 0;
X  inchar = byte (&look_pc);
X  while (inchar != '.' && inchar != ':')
X    {
X      kdigits++;
X      inchar = byte(&look_pc);
X    }
X  if (inchar == '.' )
X    {
X      inchar = byte(&look_pc);
X      while (inchar != ':')
X	{
X	  kscale++;
X	  inchar = byte(&look_pc);
X	}
X    }
X
X  /* Get the first character again and move the pc. */
X  inchar = byte(pc);
X  
X  /* Secial cases of 0, 1, and A-F single inputs. */
X  if (kdigits == 1 && kscale == 0)
X    {
X      if (inchar == 0)
X	{
X	  push_copy (_zero_);
X	  inchar = byte(pc);
X	  return;
X	}
X      if (inchar == 1) {
X      push_copy (_one_);
X      inchar = byte(pc);
X      return;
X    }
X    if (inchar > 9)
X      {
X	init_num (&build);
X	int2num (&build, inchar);
X	push_num (build);
X	inchar = byte(pc);
X	return;
X      }
X    }
X
X  /* Build the new number. */
X  if (kdigits == 0)
X    {
X      build = new_num (1,kscale);
X      ptr = build->n_value;
X      *ptr++ = 0;
X    }
X  else
X    {
X      build = new_num (kdigits,kscale);
X      ptr = build->n_value;
X    }
X
X  while (inchar != ':')
X    {
X      if (inchar != '.')
X	if (inchar > 9)
X	  *ptr++ = 9;
X	else
X	  *ptr++ = inchar;
X      inchar = byte(pc);
X    }
X  push_num (build);
X}
X
X
X/* Put the correct value on the stack for C_CODE.  Frees TOS num. */
X
Xvoid
Xassign (c_code)
X     char c_code;
X{
X  free_num (&ex_stack->s_num);
X  if (c_code)
X    ex_stack->s_num = copy_num (_one_);
X  else
X    ex_stack->s_num = copy_num (_zero_);
X}
/
echo x - fix_math.h
sed '/^X/s///' > fix_math.h << '/'
Xed math.h <<EOS-EOS
X1,1s/^/"/
X1,\$s/\$/\\\\/
X\$,\$d
X\$,\$s/\\\\\$/"/
Xw
Xq
XEOS-EOS
/
echo x - global.c
sed '/^X/s///' > global.c << '/'
X/* global.c:  This defines the global variables. */
X
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X#include "bcdefs.h"
X
X/* Since we want to define them here, we use the following define. */
X#undef EXTERN
X#define EXTERN
X
X/* Define all the global variables for bc. */
X#include "global.h"
X
X#ifndef BC_MATH_FILE
XCONST char libmath[] = 
X#include "math.h"
X;
X#endif 
/
echo x - global.h
sed '/^X/s///' > global.h << '/'
X/* global.h:  The global variables for bc.  */
X
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X
X/* For the current "break level" and if statements. */
XEXTERN int break_label;
XEXTERN int if_label;
XEXTERN int continue_label;
X
X/* Label numbers. */
XEXTERN int next_label;
X
X/* Used for "code" generation. */
XEXTERN char genstr[80];
XEXTERN int out_count;
XEXTERN char did_gen;
X
X/* Interactive and other flags. */
XEXTERN char interactive;
XEXTERN char compile_only;
XEXTERN char use_math;
XEXTERN char warn_not_std;
XEXTERN char std_only;
X
X/* global variables for the bc machine. All will be dynamic in size.*/
X/* Function storage. main is (0) and functions (1-f_count) */
X
XEXTERN bc_function *functions;
XEXTERN char **f_names;
XEXTERN int  f_count;
X
X/* Variable stoarge and reverse names. */
X
XEXTERN bc_var **variables;
XEXTERN char **v_names;
XEXTERN int  v_count;
X
X/* Array Variable storage and reverse names. */
X
XEXTERN bc_var_array **arrays;
XEXTERN char **a_names;
XEXTERN int  a_count;
X
X/* Execution stack. */
XEXTERN estack_rec *ex_stack;
X
X/* Function return stack. */
XEXTERN fstack_rec *fn_stack;
X
X/* Other "storage". */
XEXTERN int i_base;
XEXTERN int o_base;
XEXTERN int scale;
XEXTERN char c_code;
XEXTERN int out_col;
XEXTERN char runtime_error;
XEXTERN program_counter pc;
X
X/* Input Line numbers and other error information. */
XEXTERN int line_no;
XEXTERN int had_error;
X
X/* For larger identifiers, a tree, and how many "storage" locations
X   have been allocated. */
X
XEXTERN int next_array;
XEXTERN int next_func;
XEXTERN int next_var;
X
XEXTERN id_rec *name_tree;
X
X/* For error message production */
XEXTERN char **g_argv;
XEXTERN int    g_argc;
XEXTERN char   is_std_in;
X
X/* defined in number.c */
Xextern bc_num _zero_;
Xextern bc_num _one_;
X
X/* For use with getopt.  Do not declare them here.*/
Xextern int optind;
X
/
echo x - libmath.b
sed '/^X/s///' > libmath.b << '/'
X/* libmath.b for bc for minix.  */
X
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X
Xscale = 20
X
X/* Uses the fact that e^x = (e^(x/2))^2
X   When x is small enough, we use the series:
X     e^x = 1 + x + x^2/2! + x^3/3! + ...
X*/
X
Xdefine e(x) {
X  auto  a, d, e, f, i, m, v, z
X
X  /* Check the sign of x. */
X  if (x<0) {
X    m = 1
X    x = -x
X  } 
X
X  /* Precondition x. */
X  z = scale;
X  scale = 4 + z + .44*x;
X  while (x > 1) {
X    f += 1;
X    x /= 2;
X  }
X
X  /* Initialize the variables. */
X  v = 1+x
X  a = x
X  d = 1
X
X  for (i=2; 1; i++) {
X    e = (a *= x) / (d *= i)
X    if (e == 0) {
X      if (f>0) while (f--)  v = v*v;
X      scale = z
X      if (m) return (1/v);
X      return (v/1);
X    }
X    v += e
X  }
X}
X
X/* Natural log. Uses the fact that ln(x^2) = 2*ln(x)
X    The series used is:
X       ln(x) = 2(a+a^3/3+a^5/5+...) where a=(x-1)/(x+1)
X*/
X
Xdefine l(x) {
X  auto e, f, i, m, n, v, z
X
X  /* return something for the special case. */
X  if (x <= 0) return (1 - 10^scale)
X
X  /* Precondition x to make .5 < x < 2.0. */
X  z = scale;
X  scale += 4;
X  f = 2;
X  i=0
X  while (x >= 2) {  /* for large numbers */
X    f *= 2;
X    x = sqrt(x);
X  }
X  while (x <= .5) {  /* for small numbers */
X    f *= 2;
X    x = sqrt(x);
X  }
X
X  /* Set up the loop. */
X  v = n = (x-1)/(x+1)
X  m = n*n
X
X  /* Sum the series. */
X  for (i=3; 1; i+=2) {
X    e = (n *= m) / i
X    if (e == 0) {
X      v = f*v
X      scale = z
X      return (v/1)
X    }
X    v += e
X  }
X}
X
X/* Sin(x)  uses the standard series:
X   sin(x) = x - x^3/3! + x^5/5! - x^7/7! ... */
X
Xdefine s(x) {
X  auto  e, i, m, n, s, v, z
X
X  /* precondition x. */
X  z = scale 
X  scale = 1.1*z + 1;
X  v = a(1)
X  if (x < 0) {
X    m = 1;
X    x = -x;
X  }
X  scale = 0
X  n = (x / v + 2 )/4
X  x = x - 4*n*v
X  if (n%2) x = -x
X
X  /* Do the loop. */
X  scale = z + 2;
X  v = e = x
X  s = -x*x
X  for (i=3; 1; i+=2) {
X    e *= s/(i*(i-1))
X    if (e == 0) {
X      scale = z
X      if (m) return (-v/1);
X      return (v/1);
X    }
X    v += e
X  }
X}
X
X/* Cosine : cos(x) = sin(x+pi/2) */
Xdefine c(x) {
X  auto v;
X  scale += 1;
X  v = s(x+a(1)*2);
X  scale -= 1;
X  return (v/1);
X}
X
X/* Arctan: Using the formula:
X     atan(x) = atan(c) + atan((x-c)/(1+xc)) for a small c (.2 here)
X   For under .2, use the series:
X     atan(x) = x - x^3/3 + x^5/5 - x^7/7 + ...   */
X
Xdefine a(x) {
X  auto a, e, f, i, m, n, s, v, z
X
X  /* Special case and for fast answers */
X  if (x==1) {
X    if (scale <= 25) return (.7853981633974483096156608/1)
X    if (scale <= 40) return (.7853981633974483096156608458198757210492/1)
X    if (scale <= 60) \
X      return (.785398163397448309615660845819875721049292349843776455243736/1)
X  }
X  if (x==.2) {
X    if (scale <= 25) return (.1973955598498807583700497/1)
X    if (scale <= 40) return (.1973955598498807583700497651947902934475/1)
X    if (scale <= 60) \
X      return (.197395559849880758370049765194790293447585103787852101517688/1)
X  }
X
X  /* Negative x? */
X  if (x<0) {
X    m = 1;
X    x = -x;
X  }
X
X  /* Save the scale. */
X  z = scale;
X
X  /* Note: a and f are known to be zero due to being auto vars. */
X  /* Calculate atan of a known number. */ 
X  if (x > .2)  {
X    scale = z+4;
X    a = a(.2);
X  }
X   
X  /* Precondition x. */
X  scale = z+2;
X  while (x > .2) {
X    f += 1;
X    x = (x-.2) / (1+x*.2);
X  }
X
X  /* Initialize the series. */
X  v = n = x;
X  s = -x*x;
X
X  /* Calculate the series. */
X  for (i=3; 1; i+=2) {
X    e = (n *= s) / i;
X    if (e == 0) {
X      scale = z;
X      if (m) return ((f*a+v)/-1);
X      return ((f*a+v)/1);
X    }
X    v += e
X  }
X}
X
X
X/* Bessel function of integer order.  Uses the following:
X   j(-n,x) = (-1)^n*j(n,x) 
X   j(n,x) = x^n/(2^n*n!) * (1 - x^2/(2^2*1!*(n+1)) + x^4/(2^4*2!*(n+1)*(n+2))
X            - x^6/(2^6*3!*(n+1)*(n+2)*(n+3)) .... )
X*/
Xdefine j(n,x) {
X  auto a, d, e, f, i, m, s, v, z
X
X  /* Make n an integer and check for negative n. */
X  z = scale;
X  scale = 0;
X  n = n/1;
X  if (n<0) {
X    n = -n;
X    if (n%2 == 1) m = 1;
X  }
X
X  /* Compute the factor of x^n/(2^n*n!) */
X  f = 1;
X  for (i=2; i<=n; i++) f = f*i;
X  scale = 1.5*z;
X  f = x^n / 2^n / f;
X
X  /* Initialize the loop .*/
X  v = e = 1;
X  s = -x*x/4
X  scale = 1.5*z
X
X  /* The Loop.... */
X  for (i=1; 1; i++) {
X    e =  e * s / i / (n+i);
X    if (e == 0) {
X       scale = z
X       if (m) return (-f*v/1);
X       return (f*v/1);
X    }
X    v += e;
X  }
X}
/
echo x - load.c
sed '/^X/s///' > load.c << '/'
X/* load.c:  This code "loads" code into the code segments. */
X
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X#include "bcdefs.h"
X#include "global.h"
X#include "proto.h"
X
X/* Load variables. */
X
Xprogram_counter load_adr;
Xchar load_str;
Xchar load_const;
X
X/* Initialize the load sequence. */
Xvoid
Xinit_load ()
X{
X  clear_func(0);
X  load_adr.pc_func = 0;
X  load_adr.pc_addr = 0;
X  load_str = FALSE;
X  load_const = FALSE;
X}
X
X/* addbyte adds one BYTE to the current code segment. */
Xvoid
Xaddbyte (byte)
X     char byte;
X{
X  int seg, offset, func;
X
X  /* If there was an error, don't continue. */
X  if (had_error) return;
X
X  /* Calculate the segment and offset. */
X  seg = load_adr.pc_addr >> BC_SEG_LOG;
X  offset = load_adr.pc_addr++ % BC_SEG_SIZE;
X  func = load_adr.pc_func;
X
X  if (seg >= BC_MAX_SEGS)
X    {
X      yyerror ("Function too big.");
X      return;
X    }
X
X  if (functions[func].f_body[seg] == NULL)
X    functions[func].f_body[seg] = (char *) bc_malloc (BC_SEG_SIZE);
X
X  /* Store the byte. */
X  functions[func].f_body[seg][offset] = byte;
X  functions[func].f_code_size++;
X}
X
X
X/* Define a label LAB to be the current program counter. */
X
Xvoid
Xdef_label (lab)
X     long lab;
X{
X  bc_label_group *temp;
X  int group, offset, func;
X    
X  /* Get things ready. */
X  group = lab >> BC_LABEL_LOG;
X  offset = lab % BC_LABEL_GROUP;
X  func = load_adr.pc_func;
X  
X  /* Make sure there is at least one label group. */
X  if (functions[func].f_label == NULL)
X    {
X      functions[func].f_label = 
X	(bc_label_group *) bc_malloc (sizeof(bc_label_group));
X      functions[func].f_label->l_next = NULL;
X    }
X
X  /* Add the label group. */
X  temp = functions[func].f_label;
X  while (group > 0)
X    {
X      if (temp->l_next == NULL)
X	{
X	  temp->l_next = (bc_label_group *) bc_malloc (sizeof(bc_label_group));
X	  temp->l_next->l_next = NULL;
X	}
X      temp = temp->l_next;
X      group --;
X    }
X
X  /* Define it! */
X  temp->l_adrs [offset] = load_adr.pc_addr;
X}
X
X/* Several instructions have integers in the code.  They
X   are all known to be legal longs.  So, no error code
X   is added.  STR is the pointer to the load string and
X   must be moved to the last non-digit character. */
X
Xlong
Xlong_val (str)
X     char **str;
X{ int  val = 0;
X  char neg = FALSE;
X
X  if (**str == '-')
X    {
X      neg = TRUE;
X      (*str)++;
X    }
X  while (isdigit(**str)) 
X    val = val*10 + *(*str)++ - '0';
X
X  if (neg)
X    return -val;
X  else
X    return val;
X}
X
X
X/* load_code loads the CODE into the machine. */
X
Xvoid
Xload_code (code)
X     char *code;
X{
X  char *str;
X  long  ap_name;	/* auto or parameter name. */
X  long  label_no;
X  long  vaf_name;	/* variable, array or function number. */
X  long  func;
X  program_counter save_adr;
X
X  /* Initialize. */
X  str = code;
X   
X  /* Scan the code. */
X  while (*str != 0)
X    {
X      /* If there was an error, don't continue. */
X      if (had_error) return;
X
X      if (load_str)
X	{
X	  if (*str == '"') load_str = FALSE;
X	  addbyte (*str++);
X	}
X      else
X	if (load_const)
X	  {
X	    if (*str == '\n') 
X	      str++;
X	    else
X	      {
X		if (*str == ':')
X		  {
X		    load_const = FALSE;
X		    addbyte (*str++);
X		  }
X		else
X		  if (*str == '.')
X		    addbyte (*str++);
X		  else
X		    if (*str >= 'A')
X		      addbyte (*str++ + 10 - 'A');
X		    else
X		      addbyte (*str++ - '0');
X	      }
X	  }
X	else
X	  {
X	    switch (*str)
X	      {
X
X	      case '"':	/* Starts a string. */
X		load_str = TRUE;
X		break;
X
X	      case 'N': /* A label */
X		str++;
X		label_no = long_val (&str);
X		def_label (label_no);
X		break;
X
X	      case 'B':  /* Branch to label. */
X	      case 'J':  /* Jump to label. */
X	      case 'Z':  /* Branch Zero to label. */
X		addbyte(*str++);
X		label_no = long_val (&str);
X		if (label_no > 65535L)
X		  {  /* Better message? */
X		    fprintf (stderr,"Program too big.\n");
X		    exit(1);
X		  }
X		addbyte ( (char) label_no & 0xFF);
X		addbyte ( (char) label_no >> 8);
X		break;
X
X	      case 'F':  /* A function, get the name and initialize it. */
X		str++;
X		func = long_val (&str);
X		clear_func (func);
X#if DEBUG > 2
X		printf ("Loading function number %d\n", func);
X#endif
X		/* get the parameters */
X		while (*str++ != '.')
X		  {
X		    if (*str == '.')
X		      {
X			str++;
X			break;
X		      }
X		    ap_name = long_val (&str);
X#if DEBUG > 2
X		    printf ("parameter number %d\n", ap_name);
X#endif
X		    functions[(int)func].f_params = 
X		      nextarg (functions[(int)func].f_params, ap_name);
X		  }
X
X		/* get the auto vars */
X		while (*str != '[')
X		  {
X		    if (*str == ',') str++;
X		    ap_name = long_val (&str);
X#if DEBUG > 2
X		    printf ("auto number %d\n", ap_name);
X#endif
X		    functions[(int)func].f_autos = 
X		      nextarg (functions[(int)func].f_autos, ap_name);
X		  }
X		save_adr = load_adr;
X		load_adr.pc_func = func;
X		load_adr.pc_addr = 0;
X		break;
X		
X	      case ']':  /* A function end */
X		functions[load_adr.pc_func].f_defined = TRUE;
X		load_adr = save_adr;
X		break;
X
X	      case 'C':  /* Call a function. */
X		addbyte (*str++);
X		func = long_val (&str);
X		if (func < 128)
X		  addbyte ( (char) func);
X		else
X		  {
X		    addbyte ((func >> 8) & 0xff | 0x80);
X		    addbyte (func & 0xff);
X		  }
X		if (*str == ',') str++;
X		while (*str != ':')
X		  addbyte (*str++);
X		addbyte (':');
X		break;
X		
X	      case 'c':  /* Call a special function. */
X		addbyte (*str++);
X		addbyte (*str);
X		break;
X
X	      case 'K':  /* A constant.... may have an "F" in it. */
X		addbyte (*str);
X		load_const = TRUE;
X		break;
X
X	      case 'd':  /* Decrement. */
X	      case 'i':  /* Increment. */
X	      case 'l':  /* Load. */
X	      case 's':  /* Store. */
X	      case 'A':  /* Array Increment */
X	      case 'M':  /* Array Decrement */
X	      case 'L':  /* Array Load */
X	      case 'S':  /* Array Store */
X		addbyte (*str++);
X		vaf_name = long_val (&str);
X		if (vaf_name < 128)
X		  addbyte (vaf_name);
X		else
X		  {
X		    addbyte ((vaf_name >> 8) & 0xff | 0x80);
X		    addbyte (vaf_name & 0xff);
X		  }
X		break;
X
X	      case '@':  /* A command! */
X		switch (*(++str))
X		  {
X		  case 'i':
X		    init_load ();
X		    break;
X		  case 'r':
X		    execute ();
X		    break;
X		  } 
X		break;
X
X	      case '\n':  /* Ignore the newlines */
X		break;
X		
X	      default:   /* Anything else */
X		addbyte (*str);	   
X	      }
X	    str++;
X	  }
X    }
X}
/
echo x - main.c
sed '/^X/s///' > main.c << '/'
X/* main.c: The main program for bc.  */
X
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X#include "bcdefs.h"
X#include <signal.h>
X#include "global.h"
X#include "proto.h"
X
X/* Variables for processing multiple files. */
Xchar   first_file;
Xextern FILE *yyin;
X
X
X/* The main program for bc. */
Xint
Xmain (argc, argv)
X     int argc;
X     char *argv[];
X{
X  int  ch; 
X  
X  /* Initialize many variables. */
X  compile_only = FALSE;
X  use_math = FALSE;
X  warn_not_std = FALSE;
X  std_only = FALSE;
X  if (isatty(0) && isatty(1)) 
X    interactive = TRUE;
X  else
X    interactive = FALSE;
X
X  /* Parse the command line */
X  ch = getopt (argc, argv, "lcisvw");
X  while (ch != EOF)
X    {
X      switch (ch)
X	{
X	case 'c':  /* compile only */
X	  compile_only = TRUE;
X	  break;
X	case 'l':  /* math lib */
X	  use_math = TRUE;
X	  break;
X	case 'i':  /* force interactive */
X	  interactive = TRUE;
X	  break;
X	case 'w':  /* Non standard features give warnings. */
X	  warn_not_std = TRUE;
X	  break;
X	case 's':  /* Non standard features give errors. */
X	  std_only = TRUE;
X	  break;
X	case 'v':  /* Print the version. */
X	  printf ("%s\n", BC_VERSION);
X	  break;
X	}
X      ch = getopt (argc, argv, "lcisvw");
X    }
X
X  /* Initialize the machine.  */
X  init_storage();
X  init_load();
X
X  /* Set up interrupts to print a message. */
X  if (interactive)
X    signal (SIGINT, use_quit);
X
X  /* Initialize the front end. */
X  init_tree();
X  init_gen ();
X  g_argv = argv;
X  g_argc = argc;
X  is_std_in = FALSE;
X  first_file = TRUE;
X  if (!open_new_file ())
X    exit (1);
X
X  /* Do the parse. */
X  yyparse ();
X
X  /* End the compile only output with a newline. */
X  if (compile_only)
X    printf ("\n");
X
X  exit (0);
X}
X
X
X/* This is the function that opens all the files. 
X   It returns TRUE if the file was opened, otherwise
X   it returns FALSE. */
X
Xint
Xopen_new_file ()
X{
X  FILE *new_file;
X
X  /* Set the line number. */
X  line_no = 1;
X
X  /* Check to see if we are done. */
X  if (is_std_in) return (FALSE);
X
X  /* Open the other files. */
X  if (use_math && first_file)
X    {
X#ifdef BC_MATH_FILE
X      /* Make the first file be the math library. */
X      new_file = fopen (BC_MATH_FILE, "r");
X      use_math = FALSE;
X      if (new_file != NULL)
X	{
X	  new_yy_file (new_file);
X	  return TRUE;
X	}	
X      else
X	{
X	  fprintf (stderr, "Math Library unavailable.\n");
X	  exit (1);
X	}
X#else
X      /* Load the code from a precompiled version of the math libarary. */
X      extern char libmath[];
X      char tmp;
X      /* These MUST be in the order of first mention of each function.
X	 That is why "a" comes before "c" even though "a" is defined after
X	 after "c".  "a" is used in "s"! */
X      tmp = lookup ("e", FUNCT);
X      tmp = lookup ("l", FUNCT);
X      tmp = lookup ("s", FUNCT);
X      tmp = lookup ("a", FUNCT);
X      tmp = lookup ("c", FUNCT);
X      tmp = lookup ("j", FUNCT);
X      load_code (libmath);
X#endif
X    }
X  
X  /* One of the argv values. */
X  while (optind < g_argc)
X    {
X      new_file = fopen (g_argv[optind], "r");
X      if (new_file != NULL)
X	{
X	  new_yy_file (new_file);
X	  optind++;
X	  return TRUE;
X	}
X      fprintf (stderr, "File %s is unavailable.\n", g_argv[optind++]);
X      exit (1);
X    }
X  
X  /* If we fall through to here, we should return stdin. */
X  new_yy_file (stdin);
X  is_std_in = TRUE;
X  return TRUE;
X}
X
X
X/* Set yyin to the new file. */
X
Xvoid
Xnew_yy_file (file)
X     FILE *file;
X{
X  if (!first_file) fclose (yyin);
X  yyin = file;
X  first_file = FALSE;
X}
X
X
X/* Message to use quit.  */
X
Xvoid
Xuse_quit (sig)
X     int sig;
X{
X  printf ("\n(interrupt) use quit to exit.\n");
X  signal (SIGINT, use_quit);
X}
/
echo x - math.h
sed '/^X/s///' > math.h << '/'
X""
/
echo x - number.c
sed '/^X/s///' > number.c << '/'
X/* number.c: Implements arbitrary precision numbers. */
X
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X#include "bcdefs.h"
X#include "proto.h"
X
X/* Storage used for special numbers. */
Xbc_num _zero_;
Xbc_num _one_;
Xbc_num _two_;
X
X
X/* "Frees" a bc_num NUM.  Actually decreases reference count and only
X   frees the storage if reference count is zero. */
X
Xvoid
Xfree_num (num)
X    bc_num *num;
X{
X  if (*num == NULL) return;
X  (*num)->n_refs--; 
X  if ((*num)->n_refs == 0) free(*num);
X  *num = NULL;
X}
X
X
X/* new_num allocates a number and sets fields to known values. */
X
Xbc_num
Xnew_num (length, scale)
X     int length, scale;
X{
X  bc_num temp;
X
X  temp = (bc_num) malloc (sizeof(bc_struct)+length+scale);
X  if (temp == NULL) out_of_memory ();
X  temp->n_sign = PLUS;
X  temp->n_len = length;
X  temp->n_scale = scale;
X  temp->n_refs = 1;
X  temp->n_value[0] = 0;
X  return temp;
X}
X
X
X/* Intitialize the number package! */
X
Xvoid
Xinit_numbers ()
X{
X  _zero_ = new_num (1,0);
X  _one_  = new_num (1,0);
X  _one_->n_value[0] = 1;
X  _two_  = new_num (1,0);
X  _two_->n_value[0] = 2;
X}
X
X
X/* Make a copy of a number!  Just increments the reference count! */
X
Xbc_num
Xcopy_num (num)
X     bc_num num;
X{
X  num->n_refs++;
X  return num;
X}
X
X
X/* Initialize a number NUM by making it a copy of zero. */
X
Xvoid
Xinit_num (num)
X     bc_num *num;
X{
X  *num = copy_num (_zero_);
X}
X
X
X/* Convert an integer VAL to a bc number NUM. */
X
Xvoid
Xint2num (num, val)
X     bc_num *num;
X     int val;
X{
X  char buffer[30];
X  char *bptr, *vptr;
X  int  ix = 1;
X  char neg = 0;
X  
X  /* Sign. */
X  if (val < 0)
X    {
X      neg = 1;
X      val = -val;
X    }
X  
X  /* Get things going. */
X  bptr = buffer;
X  *bptr++ = val % 10;
X  val = val / 10;
X  
X  /* Extract remaining digits. */
X  while (val != 0)
X    {
X      *bptr++ = val % 10;
X      val = val / 10;
X      ix++; 		/* Count the digits. */
X    }
X  
X  /* Make the number. */
X  free_num (num);
X  *num = new_num (ix, 0);
X  if (neg) (*num)->n_sign = MINUS;
X  
X  /* Assign the digits. */
X  vptr = (*num)->n_value;
X  while (ix-- > 0)
X    *vptr++ = *--bptr;
X}
X
X
X/* Convert a number NUM to a long.  The function returns only the integer 
X   part of the number.  For numbers that are too large to represent as
X   a long, this function returns a zero.  This can be detected by checking
X   the NUM for zero after having a zero returned. */
X
Xlong
Xnum2long (num)
X     bc_num num;
X{
X  long val;
X  char *nptr;
X  int  index;
X
X  /* Extract the int value, ignore the fraction. */
X  val = 0;
X  nptr = num->n_value;
X  for (index=num->n_len; (index>0) && (val<=(LONG_MAX/10)); index--)
X    val = val*10 + *nptr++;
X  
X  /* Check for overflow.  If overflow, return zero. */
X  if (index>0) val = 0;
X  if (val < 0) val = 0;
X 
X  /* Return the value. */
X  if (num->n_sign == PLUS)
X    return (val);
X  else
X    return (-val);
X}
X
X
X/* The following are some math routines for numbers. */
X_PROTOTYPE(static int _do_compare, (bc_num n1, bc_num n2, int use_sign,
X				    int ignore_last));
X_PROTOTYPE(static void _rm_leading_zeros, (bc_num num));
X_PROTOTYPE(static bc_num _do_add, (bc_num n1, bc_num n2));
X_PROTOTYPE(static bc_num _do_sub, (bc_num n1, bc_num n2));
X_PROTOTYPE(static void _one_mult, (unsigned char *num, int size, int digit,
X				   unsigned char *result));
X
X
X
X/* Compare two bc numbers.  Return value is 0 if equal, -1 if N1 is less
X   than N2 and +1 if N1 is greater than N2.  If USE_SIGN is false, just
X   compare the magnitudes. */
X
Xstatic int
X_do_compare (n1, n2, use_sign, ignore_last)
X     bc_num n1, n2;
X     int use_sign;
X     int ignore_last;
X{
X  char *n1ptr, *n2ptr;
X  int  count;
X  
X  /* First, compare signs. */
X  if (use_sign && n1->n_sign != n2->n_sign)
X    {
X      if (n1->n_sign == PLUS)
X	return (1);	/* Positive N1 > Negative N2 */
X      else
X	return (-1);	/* Negative N1 < Positive N1 */
X    }
X  
X  /* Now compare the magnitude. */
X  if (n1->n_len != n2->n_len)
X    {
X      if (n1->n_len > n2->n_len)
X	{
X	  /* Magnitude of n1 > n2. */
X	  if (!use_sign || n1->n_sign == PLUS)
X	    return (1);
X	  else
X	    return (-1);
X	}
X      else
X	{
X	  /* Magnitude of n1 < n2. */
X	  if (!use_sign || n1->n_sign == PLUS)
X	    return (-1);
X	  else
X	    return (1);
X	}
X    }
X
X  /* If we get here, they have the same number of integer digits.
X     check the integer part and the equal length part of the fraction. */
X  count = n1->n_len + MIN (n1->n_scale, n2->n_scale);
X  n1ptr = n1->n_value;
X  n2ptr = n2->n_value;
X
X  while ((count > 0) && (*n1ptr == *n2ptr))
X    {
X      n1ptr++;
X      n2ptr++;
X      count--;
X    }
X  if (ignore_last && count == 1 && n1->n_scale == n2->n_scale)
X    return (0);
X  if (count != 0)
X    {
X      if (*n1ptr > *n2ptr)
X	{
X	  /* Magnitude of n1 > n2. */
X	  if (!use_sign || n1->n_sign == PLUS)
X	    return (1);
X	  else
X	    return (-1);
X	}
X      else
X	{
X	  /* Magnitude of n1 < n2. */
X	  if (!use_sign || n1->n_sign == PLUS)
X	    return (-1);
X	  else
X	    return (1);
X	}
X    }
X
X  /* They are equal up to the last part of the equal part of the fraction. */
X  if (n1->n_scale != n2->n_scale) 
X    if (n1->n_scale > n2->n_scale)
X      {
X	for (count = n1->n_scale-n2->n_scale; count>0; count--)
X	  if (*n1ptr++ != 0)
X	    {
X	      /* Magnitude of n1 > n2. */
X	      if (!use_sign || n1->n_sign == PLUS)
X		return (1);
X	      else
X		return (-1);
X	    }
X      }
X    else
X      {
X	for (count = n2->n_scale-n1->n_scale; count>0; count--)
X	  if (*n2ptr++ != 0)
X	    {
X	      /* Magnitude of n1 < n2. */
X	      if (!use_sign || n1->n_sign == PLUS)
X		return (-1);
X	      else
X		return (1);
X	    }
X      }
X  
X  /* They must be equal! */
X  return (0);
X}
X
X
X/* This is the "user callable" routine to compare numbers N1 and N2. */
X
Xint
Xbc_compare (n1, n2)
X     bc_num n1, n2;
X{
X  return _do_compare (n1, n2, TRUE, FALSE);
X}
X
X
X/* In some places we need to check if the number NUM is zero. */
X
Xchar
Xis_zero (num)
X     bc_num num;
X{
X  int  count;
X  char *nptr;
X
X  /* Quick check. */
X  if (num == _zero_) return TRUE;
X
X  /* Initialize */
X  count = num->n_len + num->n_scale;
X  nptr = num->n_value;
X
X  /* The check */
X  while ((count > 0) && (*nptr++ == 0)) count--;
X
X  if (count != 0)
X    return FALSE;
X  else 
X    return TRUE;
X}
X
X
X/* In some places we need to check if the number is negative. */
X
Xchar
Xis_neg (num)
X     bc_num num;
X{
X  return num->n_sign == MINUS;
X}
X
X
X/* For many things, we may have leading zeros in a number NUM.
X   _rm_leading_zeros just moves the data to the correct
X   place and adjusts the length. */
X
Xstatic void
X_rm_leading_zeros (num)
X     bc_num num;
X{
X  int bytes;
X  char *dst, *src;
X
X  /* Do a quick check to see if we need to do it. */
X  if (*num->n_value != 0) return;
X
X  /* The first digit is 0, find the first non-zero digit in the 10's or
X     greater place. */
X  bytes = num->n_len;
X  src = num->n_value;
X  while (bytes > 1 && *src == 0) src++, bytes--;
X  num->n_len = bytes;
X  bytes += num->n_scale;
X  dst = num->n_value;
X  while (bytes-- > 0) *dst++ = *src++;
X  
X}
X
X
X/* Perform addition: N1 is added to N2 and the value is
X   returned.  The signs of N1 and N2 are ignored. */
X
Xstatic bc_num
X_do_add (n1, n2)
X     bc_num n1, n2;
X{
X  bc_num sum;
X  int sum_scale, sum_digits;
X  char *n1ptr, *n2ptr, *sumptr;
X  int carry, n1bytes, n2bytes;
X
X  /* Prepare sum. */
X  sum_scale = MAX (n1->n_scale, n2->n_scale);
X  sum_digits = MAX (n1->n_len, n2->n_len) + 1;
X  sum = new_num (sum_digits,sum_scale);
X
X  /* Start with the fraction part.  Initialize the pointers. */
X  n1bytes = n1->n_scale;
X  n2bytes = n2->n_scale;
X  n1ptr = (char *) (n1->n_value + n1->n_len + n1bytes - 1);
X  n2ptr = (char *) (n2->n_value + n2->n_len + n2bytes - 1);
X  sumptr = (char *) (sum->n_value + sum_scale + sum_digits - 1);
X
X  /* Add the fraction part.  First copy the longer fraction.*/
X  if (n1bytes != n2bytes)
X    {
X      if (n1bytes > n2bytes)
X	while (n1bytes>n2bytes)
X	  { *sumptr-- = *n1ptr--; n1bytes--;}
X      else
X	while (n2bytes>n1bytes)
X	  { *sumptr-- = *n2ptr--; n2bytes--;}
X    }
X
X  /* Now add the remaining fraction part and equal size integer parts. */
X  n1bytes += n1->n_len;
X  n2bytes += n2->n_len;
X  carry = 0;
X  while ((n1bytes > 0) && (n2bytes > 0))
X    {
X      *sumptr = *n1ptr-- + *n2ptr-- + carry;
X      if (*sumptr > 9)
X	{
X	   carry = 1;
X	   *sumptr -= 10;
X	}
X      else
X	carry = 0;
X      sumptr--;
X      n1bytes--;
X      n2bytes--;
X    }
X
X  /* Now add carry the longer integer part. */
X  if (n1bytes == 0)
X    { n1bytes = n2bytes; n1ptr = n2ptr; }
X  while (n1bytes-- > 0)
X    {
X      *sumptr = *n1ptr-- + carry;
X      if (*sumptr > 9)
X	{
X	   carry = 1;
X	   *sumptr -= 10;
X	 }
X      else
X	carry = 0;
X      sumptr--;
X    }
X
X  /* Set final carry. */
X  if (carry == 1)
X    *sumptr += 1;
X  
X  /* Adjust sum and return. */
X  _rm_leading_zeros (sum);
X  return sum;  
X}
X
X
X/* Perform subtraction: N2 is subtracted from N1 and the value is
X   returned.  The signs of N1 and N2 are ignored.  Also, N1 is
X   assumed to be larger than N2.  */
X
Xstatic bc_num
X_do_sub (n1, n2)
X     bc_num n1, n2;
X{
X  bc_num diff;
X  int diff_scale, diff_len;
X  int min_scale, min_len;
X  char *n1ptr, *n2ptr, *diffptr;
X  int borrow, count, val;
X
X  /* Allocate temporary storage. */
X  diff_len = MAX (n1->n_len, n2->n_len);
X  diff_scale = MAX (n1->n_scale, n2->n_scale);
X  min_len = MIN  (n1->n_len, n2->n_len);
X  min_scale = MIN (n1->n_scale, n2->n_scale);
X  diff = new_num (diff_len, diff_scale);
X
X  /* Initialize the subtract. */
X  n1ptr = (char *) (n1->n_value + n1->n_len + n1->n_scale -1);
X  n2ptr = (char *) (n2->n_value + n2->n_len + n2->n_scale -1);
X  diffptr = (char *) (diff->n_value + diff_len + diff_scale -1);
X
X  /* Subtract the numbers. */
X  borrow = 0;
X  
X  /* Take care of the longer scaled number. */
X  if (n1->n_scale != min_scale)
X    {
X      /* n1 has the longer scale */
X      for (count = n1->n_scale - min_scale; count > 0; count--)
X	*diffptr-- = *n1ptr--;
X    }
X  else
X    {
X      /* n2 has the longer scale */
X      for (count = n2->n_scale - min_scale; count > 0; count--)
X	{
X	  val = - *n2ptr-- - borrow;
X	  if (val < 0)
X	    {
X	      val += 10;
X	      borrow = 1;
X	    }
X	  else
X	    borrow = 0;
X	  *diffptr-- = val;
X	}
X    }
X  
X  /* Now do the equal length scale and integer parts. */
X  
X  for (count = 0; count < min_len + min_scale; count++)
X    {
X      val = *n1ptr-- - *n2ptr-- - borrow;
X      if (val < 0)
X	{
X	  val += 10;
X	  borrow = 1;
X	}
X      else
X	borrow = 0;
X      *diffptr-- = val;
X    }
X
X  /* If n1 has more digits then n2, we now do that subtract. */
X  if (diff_len != min_len)
X    {
X      for (count = diff_len - min_len; count > 0; count--)
X	{
X	  val = *n1ptr-- - borrow;
X	  if (val < 0)
X	    {
X	      val += 10;
X	      borrow = 1;
X	    }
X	  else
X	    borrow = 0;
X	  *diffptr-- = val;
X	}
X    }
X
X  /* Clean up and return. */
X  _rm_leading_zeros (diff);
X  return diff;
X}
X
X
X/* Here is the full add routine that takes care of negative numbers.
X   N1 is added to N2 and the result placed into RESULT. */
X
Xvoid
Xbc_add ( n1, n2, result)
X     bc_num n1, n2, *result;
X{
X  bc_num sum;
X  int cmp_res;
X
X  if (n1->n_sign == n2->n_sign)
X    {
X      sum = _do_add (n1, n2);
X      sum->n_sign = n1->n_sign;
X    }
X  else
X    {
X      /* subtraction must be done. */
X      cmp_res = _do_compare (n1, n2, FALSE, FALSE);  /* Compare magnitudes. */
X      switch (cmp_res)
X	{
X	case -1:
X	  /* n1 is less than n2, subtract n1 from n2. */
X	  sum = _do_sub (n2, n1);
X	  sum->n_sign = n2->n_sign;
X	  break;
X	case  0:
X	  /* They are equal! return zero! */
X	  sum = copy_num (_zero_);   
X	  break;
X	case  1:
X	  /* n2 is less than n1, subtract n2 from n1. */
X	  sum = _do_sub (n1, n2);
X	  sum->n_sign = n1->n_sign;
X	}
X    }
X
X  /* Clean up and return. */
X  free_num (result);
X  *result = sum;
X}
X
X
X/* Here is the full subtract routine that takes care of negative numbers.
X   N2 is subtracted from N1 and the result placed in RESULT. */
X
Xvoid
Xbc_sub ( n1, n2, result)
X     bc_num n1, n2, *result;
X{
X  bc_num diff;
X  int cmp_res;
X
X  if (n1->n_sign != n2->n_sign)
X    {
X      diff = _do_add (n1, n2);
X      diff->n_sign = n1->n_sign;
X    }
X  else
X    {
X      /* subtraction must be done. */
X      cmp_res = _do_compare (n1, n2, FALSE, FALSE);  /* Compare magnitudes. */
X      switch (cmp_res)
X	{
X	case -1:
X	  /* n1 is less than n2, subtract n1 from n2. */
X	  diff = _do_sub (n2, n1);
X	  diff->n_sign = (n2->n_sign == PLUS ? MINUS : PLUS);
X	  break;
X	case  0:
X	  /* They are equal! return zero! */
X	  diff = copy_num (_zero_);   
X	  break;
X	case  1:
X	  /* n2 is less than n1, subtract n2 from n1. */
X	  diff = _do_sub (n1, n2);
X	  diff->n_sign = n1->n_sign;
X	  break;
X	}
X    }
X  
X  /* Clean up and return. */
X  free_num (result);
X  *result = diff;
X}
X
X
X/* The multiply routine.  N2 time N1 is put int PROD with the scale of
X   the result being MIN(N2 scale+N1 scale, MAX (SCALE, N2 scale, N1 scale)).
X   */
X
Xvoid
Xbc_multiply (n1, n2, prod, scale)
X     bc_num n1, n2, *prod;
X     int scale;
X{
X  bc_num pval;			/* For the working storage. */
X  char *n1ptr, *n2ptr, *pvptr;	/* Work pointers. */
X  char *n1end, *n2end;		/* To the end of n1 and n2. */
X
X  int indx;
X  int len1, len2, total_digits;
X  long sum;
X  int full_scale, prod_scale;
X  int toss;
X
X  /* Initialize things. */
X  len1 = n1->n_len + n1->n_scale;
X  len2 = n2->n_len + n2->n_scale;
X  total_digits = len1 + len2;
X  full_scale = n1->n_scale + n2->n_scale;
X  prod_scale = MIN(full_scale,MAX(scale,MAX(n1->n_scale,n2->n_scale)));
X  toss = full_scale - prod_scale;
X  pval =  new_num (total_digits-full_scale, prod_scale);
X  pval->n_sign = ( n1->n_sign == n2->n_sign ? PLUS : MINUS );
X  n1end = (char *) (n1->n_value + len1 - 1);
X  n2end = (char *) (n2->n_value + len2 - 1);
X  pvptr = (char *) (pval->n_value + total_digits - toss - 1);
X  sum = 0;
X
X  /* Here are the loops... */
X  for (indx = 0; indx < toss; indx++)
X    {
X      n1ptr = (char *) (n1end - MAX(0, indx-len2+1));
X      n2ptr = (char *) (n2end - MIN(indx, len2-1));
X      while ((n1ptr >= n1->n_value) && (n2ptr <= n2end))
X	sum += *n1ptr-- * *n2ptr++;
X      sum = sum / 10;
X    }
X  for ( ; indx < total_digits-1; indx++)
X    {
X      n1ptr = (char *) (n1end - MAX(0, indx-len2+1));
X      n2ptr = (char *) (n2end - MIN(indx, len2-1));
X      while ((n1ptr >= n1->n_value) && (n2ptr <= n2end))
X	sum += *n1ptr-- * *n2ptr++;
X      *pvptr-- = sum % 10;
X      sum = sum / 10;
X    }
X  *pvptr-- = sum;
X
X  /* Assign to prod and clean up the number. */
X  free_num (prod);
X  *prod = pval;
X  _rm_leading_zeros (*prod);
X  if (is_zero (*prod)) 
X    (*prod)->n_sign = PLUS;
X}
X
X
X/* Some utility routines for the divide:  First a one digit multiply.
X   NUM (with SIZE digits) is multiplied by DIGIT and the result is
X   placed into RESULT.  It is written so that NUM and RESULT can be
X   the same pointers.  */
X
Xstatic void
X_one_mult (num, size, digit, result)
X     unsigned char *num;
X     int size, digit;
X     unsigned char *result;
X{
X  int carry, value;
X  unsigned char *nptr, *rptr;
X
X  if (digit == 0)
X    memset (result, 0, size);
X  else
X    {
X      if (digit == 1)
X	memcpy (result, num, size);
X      else
X	{
X	  /* Initialize */
X	  nptr = (unsigned char *) (num+size-1);
X	  rptr = (unsigned char *) (result+size-1);
X	  carry = 0;
X
X	  while (size-- > 0)
X	    {
X	      value = *nptr-- * digit + carry;
X	      *rptr-- = value % 10;
X	      carry = value / 10;
X	    }
X  
X	  if (carry != 0) *rptr = carry;
X	}
X    }
X}
X
X
X/* The full division routine. This computes N1 / N2.  It returns
X   0 if the division is ok and the result is in QUOT.  The number of
X   digits after the decimal point is SCALE. It returns -1 if division
X   by zero is tried.  The algorithm is found in Knuth Vol 2. p237. */
X
Xint
Xbc_divide (n1, n2, quot, scale)
X     bc_num n1, n2, *quot;
X     int scale;
X{ 
X  bc_num qval;
X  unsigned char *num1, *num2;
X  unsigned char *ptr1, *ptr2, *n2ptr, *qptr;
X  int  scale1, val;
X  unsigned int  len1, len2, scale2, qdigits, extra, count;
X  unsigned int  qdig, qguess, borrow, carry;
X  unsigned char *mval;
X  char zero;
X  unsigned int  norm;
X
X  /* Test for divide by zero. */
X  if (is_zero (n2)) return -1;
X
X  /* Test for divide by 1.  If it is we must truncate. */
X  if (n2->n_scale == 0)
X    {
X      if (n2->n_len == 1 && *n2->n_value == 1)
X	{
X	  qval = new_num (n1->n_len, scale);
X	  qval->n_sign = (n1->n_sign == n2->n_sign ? PLUS : MINUS);
X	  memset (&qval->n_value[n1->n_len],0,scale);
X	  memcpy (qval->n_value, n1->n_value,
X		  n1->n_len + MIN(n1->n_scale,scale));
X	  free_num (quot);
X	  *quot = qval;
X	}
X    }
X  
X  /* Set up the divide.  Move the decimal point on n1 by n2's scale.
X     Remember, zeros on the end of num2 are wasted effort for dividing. */
X  scale2 = n2->n_scale;
X  n2ptr = (unsigned char *) n2->n_value+n2->n_len+scale2-1;
X  while ((scale2 > 0) && (*n2ptr-- == 0)) scale2--;
X
X  len1 = n1->n_len + scale2;
X  scale1 = n1->n_scale - scale2;
X  if (scale1 < scale)
X    extra = scale - scale1;
X  else
X    extra = 0;
X  num1 = (unsigned char *) malloc (n1->n_len+n1->n_scale+extra+2);
X  if (num1 == NULL) out_of_memory();
X  memset (num1, 0, n1->n_len+n1->n_scale+extra+2);
X  memcpy (num1+1, n1->n_value, n1->n_len+n1->n_scale);
X
X  len2 = n2->n_len + scale2;
X  num2 = (unsigned char *) malloc (len2+1);
X  if (num2 == NULL) out_of_memory();
X  memcpy (num2, n2->n_value, len2);
X  *(num2+len2) = 0;
X  n2ptr = num2;
X  while (*n2ptr == 0)
X    {
X      n2ptr++;
X      len2--;
X    }
X
X  /* Calculate the number of quotient digits. */
X  if (len2 > len1+scale)
X    {
X      qdigits = scale+1;
X      zero = TRUE;
X    }
X  else
X    {
X      zero = FALSE;
X      if (len2>len1)
X	qdigits = scale+1;  	/* One for the zero integer part. */
X      else
X	qdigits = len1-len2+scale+1;
X    }
X
X  /* Allocate and zero the storage for the quotient. */
X  qval = new_num (qdigits-scale,scale);
X  memset (qval->n_value, 0, qdigits);
X
X  /* Allocate storage for the temporary storage mval. */
X  mval = (unsigned char *) malloc (len2+1);
X  if (mval == NULL) out_of_memory ();
X
X  /* Now for the full divide algorithm. */
X  if (!zero)
X    {
X      /* Normalize */
X      norm =  10 / ((int)*n2ptr + 1);
X      if (norm != 1)
X	{
X	  _one_mult (num1, len1+scale1+extra+1, norm, num1);
X	  _one_mult (n2ptr, len2, norm, n2ptr);
X	}
X
X      /* Initialize divide loop. */
X      qdig = 0;
X      if (len2 > len1)
X	qptr = (unsigned char *) qval->n_value+len2-len1;
X      else
X	qptr = (unsigned char *) qval->n_value;
X
X      /* Loop */
X      while (qdig <= len1+scale-len2)
X	{
X	  /* Calculate the quotient digit guess. */
X	  if (*n2ptr == num1[qdig])
X	    qguess = 9;
X	  else
X	    qguess = (num1[qdig]*10 + num1[qdig+1]) / *n2ptr;
X
X	  /* Test qguess. */
X	  if (n2ptr[1]*qguess >
X	      (num1[qdig]*10 + num1[qdig+1] - *n2ptr*qguess)*10
X	       + num1[qdig+2])
X	    {
X	      qguess--;
X	      /* And again. */
X	      if (n2ptr[1]*qguess >
X		  (num1[qdig]*10 + num1[qdig+1] - *n2ptr*qguess)*10
X		  + num1[qdig+2])
X		qguess--;
X	    }
X 
X	  /* Multiply and subtract. */
X	  borrow = 0;
X	  if (qguess != 0)
X	    {
X	      *mval = 0;
X	      _one_mult (n2ptr, len2, qguess, mval+1);
X	      ptr1 = (unsigned char *) num1+qdig+len2;
X	      ptr2 = (unsigned char *) mval+len2;
X	      for (count = 0; count < len2+1; count++)
X		{
X		  val = (int) *ptr1 - (int) *ptr2-- - borrow;
X		  if (val < 0)
X		    {
X		      val += 10;
X		      borrow = 1;
X		    }
X		  else
X		    borrow = 0;
X		  *ptr1-- = val;
X		}
X	    }
X
X	  /* Test for negative result. */
X	  if (borrow == 1)
X	    {
X	      qguess--;
X	      ptr1 = (unsigned char *) num1+qdig+len2;
X	      ptr2 = (unsigned char *) n2ptr+len2-1;
X	      carry = 0;
X	      for (count = 0; count < len2; count++)
X		{
X		  val = (int) *ptr1 + (int) *ptr2-- + carry;
X		  if (val > 9)
X		    {
X		      val -= 10;
X		      carry = 1;
X		    }
X		  else
X		    carry = 0;
X		  *ptr1-- = val;
X		}
X	      if (carry == 1) *ptr1 = (*ptr1 + 1) % 10;
X	    }
X       
X	  /* We now know the quotient digit. */
X	  *qptr++ =  qguess;
X	  qdig++;
X	}
X    }
X
X  /* Clean up and return the number. */
X  qval->n_sign = ( n1->n_sign == n2->n_sign ? PLUS : MINUS );
X  if (is_zero (qval)) qval->n_sign = PLUS;
X  _rm_leading_zeros (qval);
X  free_num (quot);
X  *quot = qval;
X
X  /* Clean up temporary storage. */
X  free (mval);
X  free (num1);
X  free (num2);
X
X  return 0;	/* Everything is OK. */
X}
X
X
X/* Modulo for numbers.  This computes NUM1 % NUM2  and puts the
X   result in RESULT.   */
X
Xint
Xbc_modulo (num1, num2, result, scale)
X     bc_num num1, num2, *result;
X     int scale;
X{
X  bc_num temp;
X  int rscale;
X
X  /* Check for correct numbers. */
X  if (is_zero (num2)) return -1;
X
X  /* Calculate final scale. */
X  rscale = MAX (num1->n_scale, num2->n_scale+scale);
X  init_num (&temp);
X  
X  /* Calculate it. */
X  bc_divide (num1, num2, &temp, scale);
X  bc_multiply (temp, num2, &temp, rscale);
X  bc_sub (num1, temp, result);
X  free_num (&temp);
X
X  return 0;	/* Everything is OK. */
X}
X
X
X/* Raise NUM1 to the NUM2 power.  The result is placed in RESULT.
X   Maximum exponent is LONG_MAX.  If a NUM2 is not an integer,
X   only the integer part is used.  */
X
Xvoid
Xbc_raise (num1, num2, result, scale)
X     bc_num num1, num2, *result;
X     int scale;
X{
X   bc_num temp, power;
X   long exponent;
X   int rscale;
X   char neg;
X
X   /* Check the exponent for scale digits and convert to a long. */
X   if (num2->n_scale != 0)
X     rt_warn ("non-zero scale in exponent");
X   exponent = num2long (num2);
X   if (exponent == 0 && (num2->n_len > 1 || num2->n_value[0] != 0))
X       rt_error ("exponent too large in raise");
X
X   /* Special case if exponent is a zero. */
X   if (exponent == 0)
X     {
X       free_num (result);
X       *result = copy_num (_one_);
X       return;
X     }
X
X   /* Other initializations. */
X   if (exponent < 0)
X     {
X       neg = TRUE;
X       exponent = -exponent;
X       rscale = scale;
X     }
X   else
X     {
X       neg = FALSE;
X       rscale = MIN (num1->n_scale*exponent, MAX(scale, num1->n_scale));
X     }
X   temp = copy_num (_one_);
X   power = copy_num (num1);
X
X   /* Do the calculation. */
X   while (exponent != 0)
X     {
X       if (exponent & 1 != 0) 
X	 bc_multiply (temp, power, &temp, rscale);
X       bc_multiply (power, power, &power, rscale);
X       exponent = exponent >> 1;
X     }
X   
X   /* Assign the value. */
X   if (neg)
X     {
X       bc_divide (_one_, temp, result, rscale);
X       free_num (&temp);
X     }
X   else
X     {
X       free_num (result);
X       *result = temp;
X     }
X   free_num (&power);
X}
X
X
X/* Take the square root NUM and return it in NUM with SCALE digits
X   after the decimal place. */
X
Xint 
Xbc_sqrt (num, scale)
X     bc_num *num;
X     int scale;
X{
X  int rscale, cmp_res, done;
X  int cscale;
X  bc_num guess, guess1, point5;
X
X  /* Initial checks. */
X  cmp_res = bc_compare (*num, _zero_);
X  if (cmp_res < 0)
X    return 0;		/* error */
X  else
X    {
X      if (cmp_res == 0)
X	{
X	  free_num (num);
X	  *num = copy_num (_zero_);
X	  return 1;
X	}
X    }
X  cmp_res = bc_compare (*num, _one_);
X  if (cmp_res == 0)
X    {
X      free_num (num);
X      *num = copy_num (_one_);
X      return 1;
X    }
X
X  /* Initialize the variables. */
X  rscale = MAX (scale, (*num)->n_scale);
X  cscale = rscale + 2;
X  init_num (&guess);
X  init_num (&guess1);
X  point5 = new_num (1,1);
X  point5->n_value[1] = 5;
X  
X  
X  /* Calculate the initial guess. */
X  if (cmp_res < 0)
X    /* The number is between 0 and 1.  Guess should start at 1. */
X    guess = copy_num (_one_);
X  else
X    {
X      /* The number is greater than 1.  Guess should start at 10^(exp/2). */
X      int2num (&guess,10);
X      int2num (&guess1,(*num)->n_len);
X      bc_multiply (guess1, point5, &guess1, rscale);
X      guess1->n_scale = 0;
X      bc_raise (guess, guess1, &guess, rscale);
X      free_num (&guess1);
X    }
X  
X  /* Find the square root using Newton's algorithm. */
X  done = FALSE;
X  while (!done)
X    {
X      free_num (&guess1);
X      guess1 = copy_num (guess);
X      bc_divide (*num,guess,&guess,cscale);
X      bc_add (guess,guess1,&guess);
X      bc_multiply (guess,point5,&guess,cscale);
X      cmp_res = _do_compare (guess,guess1,FALSE,TRUE);
X      if (cmp_res == 0) done = TRUE;
X    }
X  
X  /* Assign the number and clean up. */
X  free_num (num);
X  bc_divide (guess,_one_,num,rscale);
X  free_num (&guess);
X  free_num (&guess1);
X  free_num (&point5);
X  return 1;
X}
X
X
X/* The following routines provide output for bcd numbers package
X   using the rules of POSIX bc for output. */
X
X/* This structure is used for saving digits in the conversion process. */
Xtypedef struct stk_rec {
X	long  digit;
X	struct stk_rec *next;
X} stk_rec;
X
X/* The reference string for digits. */
Xchar ref_str[] = "0123456789ABCDEF";
X
X
X/* A special output routine for "multi-character digits."  Exactly
X   SIZE characters must be output for the value VAL.  If SPACE is
X   non-zero, we must output one space before the number.  OUT_CHAR
X   is the actual routine for writing the characters. */
X
Xvoid
Xout_long (val, size, space, out_char)
X     long val;
X     int size, space;
X#ifdef __STDC__
X     void (*out_char)(int);
X#else
X     void (*out_char)();
X#endif
X{
X  char digits[40];
X  int len, ix;
X
X  if (space) (*out_char) (' ');
X  sprintf (digits, "%ld", val);
X  len = strlen (digits);
X  while (size > len)
X    {
X      (*out_char) ('0');
X      size--;
X    }
X  for (ix=0; ix < len; ix++)
X    (*out_char) (digits[ix]);
X}
X
X/* Output of a bcd number.  NUM is written in base O_BASE using OUT_CHAR
X   as the routine to do the actual output of the characters. */
X
Xvoid
Xout_num (num, o_base, out_char)
X     bc_num num;
X     int o_base;
X#ifdef __STDC__
X     void (*out_char)(int);
X#else
X     void (*out_char)();
X#endif
X{
X  char *nptr;
X  int  index, fdigit, pre_space;
X  stk_rec *digits, *temp;
X  bc_num int_part, frac_part, base, cur_dig, t_num, max_o_digit;
X
X  /* The negative sign if needed. */
X  if (num->n_sign == MINUS) (*out_char) ('-');
X
X  /* Output the number. */
X  if (is_zero (num))
X    (*out_char) ('0');
X  else
X    if (o_base == 10)
X      {
X	/* The number is in base 10, do it the fast way. */
X	nptr = num->n_value;
X	if (num->n_len > 1 || *nptr != 0)
X	  for (index=num->n_len; index>0; index--)
X	    (*out_char) (BCD_CHAR(*nptr++));
X	else
X	  nptr++;
X	
X	/* Now the fraction. */
X	if (num->n_scale > 0)
X	  {
X	    (*out_char) ('.');
X	    for (index=0; index<num->n_scale; index++)
X	      (*out_char) (BCD_CHAR(*nptr++));
X	  }
X      }
X    else
X      {
X	/* The number is some other base. */
X	digits = NULL;
X	init_num (&int_part);
X	bc_divide (num, _one_, &int_part, 0);
X	init_num (&frac_part);
X	init_num (&cur_dig);
X	init_num (&base);
X	bc_sub (num, int_part, &frac_part);
X	int2num (&base, o_base);
X	init_num (&max_o_digit);
X	int2num (&max_o_digit, o_base-1);
X
X
X	/* Get the digits of the integer part and push them on a stack. */
X	while (!is_zero (int_part))
X	  {
X	    bc_modulo (int_part, base, &cur_dig, 0);
X	    temp = (stk_rec *) malloc (sizeof(stk_rec));
X	    if (temp == NULL) out_of_memory();
X	    temp->digit = num2long (cur_dig);
X	    temp->next = digits;
X	    digits = temp;
X	    bc_divide (int_part, base, &int_part, 0);
X	  }
X
X	/* Print the digits on the stack. */
X	if (digits != NULL)
X	  {
X	    /* Output the digits. */
X	    while (digits != NULL)
X	      {
X		temp = digits;
X		digits = digits->next;
X		if (o_base <= 16) 
X		  (*out_char) (ref_str[ (int) temp->digit]);
X		else
X		  out_long (temp->digit, max_o_digit->n_len, 1, out_char);
X		free (temp);
X	      }
X	  }
X
X	/* Get and print the digits of the fraction part. */
X	if (num->n_scale > 0)
X	  {
X	    (*out_char) ('.');
X	    pre_space = 0;
X	    t_num = copy_num (_one_);
X	    while (t_num->n_len <= num->n_scale) {
X	      bc_multiply (frac_part, base, &frac_part, num->n_scale);
X	      fdigit = num2long (frac_part);
X	      int2num (&int_part, fdigit);
X	      bc_sub (frac_part, int_part, &frac_part);
X	      if (o_base <= 16)
X		(*out_char) (ref_str[fdigit]);
X	      else {
X		out_long (fdigit, max_o_digit->n_len, pre_space, out_char);
X		pre_space = 1;
X	      }
X	      bc_multiply (t_num, base, &t_num, 0);
X	    }
X	  }
X    
X	/* Clean up. */
X	free_num (&int_part);
X	free_num (&frac_part);
X	free_num (&base);
X	free_num (&cur_dig);
X      }
X}
X
X
X#if DEBUG > 0
X
X/* Debugging procedures.  Some are just so one can call them from the
X   debugger.  */
X
X/* p_n prints the number NUM in base 10. */
X
Xvoid
Xp_n (num)
X     bc_num num;
X{
X  out_num (num, 10, out_char);
X  return 0;
X}
X
X
X/* p_b prints a character array as if it was a string of bcd digits. */
Xvoid
Xp_v (name, num, len)
X     char *name;
X     unsigned char *num;
X     int len;
X{
X  int i;
X  printf ("%s=", name);
X  for (i=0; i<len; i++) printf ("%c",BCD_CHAR(num[i]));
X  printf ("\n");
X}
X
X
X/* Convert strings to bc numbers.  Base 10 only.*/
X
Xvoid
Xstr2num (num, str, scale)
X     bc_num *num;
X     char *str;
X     int scale;
X{
X  int digits, strscale;
X  char *ptr, *nptr;
X  char zero_int;
X
X  /* Prepare num. */
X  free_num (num);
X
X  /* Check for valid number and count digits. */
X  ptr = str;
X  digits = 0;
X  strscale = 0;
X  zero_int = FALSE;
X  if ( (*ptr == '+') || (*ptr == '-'))  ptr++;  /* Sign */
X  while (*ptr == '0') ptr++;			/* Skip leading zeros. */
X  while (isdigit(*ptr)) ptr++, digits++;	/* digits */
X  if (*ptr == '.') ptr++;			/* decimal point */
X  while (isdigit(*ptr)) ptr++, strscale++;	/* digits */
X  if ((*ptr != '\0') || (digits+strscale == 0))
X    {
X      *num = copy_num (_zero_);
X      return;
X    }
X
X  /* Adjust numbers and allocate storage and initialize fields. */
X  strscale = MIN(strscale, scale);
X  if (digits == 0)
X    {
X      zero_int = TRUE;
X      digits = 1;
X    }
X  *num = new_num (digits, strscale);
X
X  /* Build the whole number. */
X  ptr = str;
X  if (*ptr == '-')
X    {
X      (*num)->n_sign = MINUS;
X      ptr++;
X    }
X  else
X    {
X      (*num)->n_sign = PLUS;
X      if (*ptr == '+') ptr++;
X    }
X  while (*ptr == '0') ptr++;			/* Skip leading zeros. */
X  nptr = (*num)->n_value;
X  if (zero_int)
X    {
X      *nptr++ = 0;
X      digits = 0;
X    }
X  for (;digits > 0; digits--)
X    *nptr++ = CH_VAL(*ptr++);
X
X  
X  /* Build the fractional part. */
X  if (strscale > 0)
X    {
X      ptr++;  /* skip the decimal point! */
X      for (;strscale > 0; strscale--)
X	*nptr++ = CH_VAL(*ptr++);
X    }
X}
X
X/* Convert a numbers to a string.  Base 10 only.*/
X
Xchar
X*num2str (num)
X      bc_num num;
X{
X  char *str, *sptr;
X  char *nptr;
X  int  index, signch;
X
X  /* Allocate the string memory. */
X  signch = ( num->n_sign == PLUS ? 0 : 1 );  /* Number of sign chars. */
X  if (num->n_scale > 0)
X    str = (char *) malloc (num->n_len + num->n_scale + 2 + signch);
X  else
X    str = (char *) malloc (num->n_len + 1 + signch);
X  if (str == NULL) out_of_memory();
X
X  /* The negative sign if needed. */
X  sptr = str;
X  if (signch) *sptr++ = '-';
X
X  /* Load the whole number. */
X  nptr = num->n_value;
X  for (index=num->n_len; index>0; index--)
X    *sptr++ = BCD_CHAR(*nptr++);
X
X  /* Now the fraction. */
X  if (num->n_scale > 0)
X    {
X      *sptr++ = '.';
X      for (index=0; index<num->n_scale; index++)
X	*sptr++ = BCD_CHAR(*nptr++);
X    }
X
X  /* Terminate the string and return it! */
X  *sptr = '\0';
X  return (str);
X}
X#endif
/
echo x - number.h
sed '/^X/s///' > number.h << '/'
X/* number.h: Arbitrary precision numbers header file. */
X
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X
Xtypedef enum {PLUS, MINUS} sign;
X
Xtypedef struct
X    {
X      sign n_sign;
X      int  n_len;	/* The number of digits before the decimal point. */
X      int  n_scale;	/* The number of digits after the decimal point. */
X      int  n_refs;      /* The number of pointers to this number. */
X      char n_value[1];  /* The storage. Not zero char terminated. It is 
X      			   allocated with all other fields.  */
X    } bc_struct;
X
Xtypedef bc_struct *bc_num;
X
X/*  Some useful macros and constants. */
X
X#define CH_VAL(c)     (c - '0')
X#define BCD_CHAR(d)   (d + '0')
X
X#ifdef MIN
X#undef MIN
X#undef MAX
X#endif
X#define MAX(a,b)      (a>b?a:b)
X#define MIN(a,b)      (a>b?b:a)
X#define ODD(a)        (a&1)
X
X#ifndef TRUE
X#define TRUE 1
X#define FALSE 0
X#endif
/
echo x - proto.h
sed '/^X/s///' > proto.h << '/'
X/* proto.h: Prototype function definitions for "external" functions. */
X
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X/* For the pc version using k&r ACK. (minix1.5 and earlier.) */
X#ifdef SHORTNAMES
X#define init_numbers i_numbers
X#define push_constant push__constant
X#define load_const in_load_const
X#define yy_get_next_buffer yyget_next_buffer
X#define yy_init_buffer yyinit_buffer
X#define yy_last_accepting_state yylast_accepting_state
X#define arglist1 arg1list
X#endif
X
X/* Include the standard library header files. */
X#ifndef NO_UNISTD
X#include <unistd.h>
X#endif
X#ifndef NO_STDLIB
X#ifdef __STDC__
X#include <stdlib.h>
X#endif
X#endif
X
X/* Define the _PROTOTYPE macro if it is needed. */
X
X#ifndef _PROTOTYPE
X#ifdef __STDC__
X#define _PROTOTYPE(func, args) func args
X#else
X#define _PROTOTYPE(func, args) func()
X#endif
X#endif
X
X/* From execute.c */
X_PROTOTYPE(void stop_execution, (int));
X_PROTOTYPE(unsigned char byte, (program_counter *pc));
X_PROTOTYPE(void execute, (void));
X_PROTOTYPE(char prog_char, (void));
X_PROTOTYPE(char input_char, (void));
X_PROTOTYPE(void push_constant, (char (*in_char)(void), int conv_base));
X_PROTOTYPE(void push_b10_const, (program_counter *pc));
X_PROTOTYPE(void assign, (int c_code));
X
X/* From util.c */
X_PROTOTYPE(char *strcopyof, (char *str));
X_PROTOTYPE(arg_list *nextarg, (arg_list *args, int val));
X_PROTOTYPE(char *arg_str, (arg_list *args, int));
X_PROTOTYPE(void free_args, (arg_list *args));
X_PROTOTYPE(void check_params, (arg_list *params, arg_list *autos));
X_PROTOTYPE(void init_gen, (void));
X_PROTOTYPE(void generate, (char *str));
X_PROTOTYPE(void run_code, (void));
X_PROTOTYPE(void out_char, (int ch));
X_PROTOTYPE(id_rec *find_id, (id_rec *tree, char *id));
X_PROTOTYPE(int insert_id_rec, (id_rec **root, id_rec *new_id));
X_PROTOTYPE(void init_tree, (void));
X_PROTOTYPE(int lookup, (char *name, int namekind));
X_PROTOTYPE(char *bc_malloc, (int));
X_PROTOTYPE(void out_of_memory, (void));
X_PROTOTYPE(void welcome, (void));
X_PROTOTYPE(void warranty, (char *));
X_PROTOTYPE(void limits, (void));
X_PROTOTYPE(void yyerror, (char *str ,...));
X_PROTOTYPE(void warn, (char *mesg ,...));
X_PROTOTYPE(void rt_error, (char *mesg ,...));
X_PROTOTYPE(void rt_warn, (char *mesg ,...));
X
X/* From load.c */
X_PROTOTYPE(void init_load, (void));
X_PROTOTYPE(void addbyte, (int byte));
X_PROTOTYPE(void def_label, (long lab));
X_PROTOTYPE(long long_val, (char **str));
X_PROTOTYPE(void load_code, (char *code));
X
X/* From main.c */
X_PROTOTYPE(int main, (int argc , char *argv []));
X_PROTOTYPE(int open_new_file, (void));
X_PROTOTYPE(void new_yy_file, (FILE *file));
X_PROTOTYPE(void use_quit, (int));
X
X/* From number.c */
X_PROTOTYPE(void free_num, (bc_num *num));
X_PROTOTYPE(bc_num new_num, (int length, int scale));
X_PROTOTYPE(void init_numbers, (void));
X_PROTOTYPE(bc_num copy_num, (bc_num num));
X_PROTOTYPE(void init_num, (bc_num *num));
X_PROTOTYPE(void str2num, (bc_num *num, char *str, int scale));
X_PROTOTYPE(char *num2str, (bc_num num));
X_PROTOTYPE(void int2num, (bc_num *num, int val));
X_PROTOTYPE(long num2long, (bc_num num));
X_PROTOTYPE(int bc_compare, (bc_num n1, bc_num n2));
X_PROTOTYPE(char is_zero, (bc_num num));
X_PROTOTYPE(char is_neg, (bc_num num));
X_PROTOTYPE(void bc_add, (bc_num n1, bc_num n2, bc_num *result));
X_PROTOTYPE(void bc_sub, (bc_num n1, bc_num n2, bc_num *result));
X_PROTOTYPE(void bc_multiply, (bc_num n1, bc_num n2, bc_num *prod, int scale));
X_PROTOTYPE(int bc_divide, (bc_num n1, bc_num n2, bc_num *quot, int scale));
X_PROTOTYPE(int bc_modulo, (bc_num num1, bc_num num2, bc_num *result, int scale));
X_PROTOTYPE(void bc_raise, (bc_num num1, bc_num num2, bc_num *result, int scale));
X_PROTOTYPE(int bc_sqrt, (bc_num *num, int scale));
X_PROTOTYPE(void out_long, (long val, int size, int space,
X			   void (*out_char)(int)));
X_PROTOTYPE(void out_num, (bc_num num, int o_base, void (* out_char)(int)));
X
X
X/* From storage.c */
X_PROTOTYPE(void init_storage, (void));
X_PROTOTYPE(void more_functions, (void));
X_PROTOTYPE(void more_variables, (void));
X_PROTOTYPE(void more_arrays, (void));
X_PROTOTYPE(void clear_func, (int func ));
X_PROTOTYPE(int fpop, (void));
X_PROTOTYPE(void fpush, (int val ));
X_PROTOTYPE(void pop, (void));
X_PROTOTYPE(void push_copy, (bc_num num ));
X_PROTOTYPE(void push_num, (bc_num num ));
X_PROTOTYPE(char check_stack, (int depth ));
X_PROTOTYPE(bc_var *get_var, (int var_name ));
X_PROTOTYPE(bc_num *get_array_num, (int var_index, long index ));
X_PROTOTYPE(void store_var, (int var_name ));
X_PROTOTYPE(void store_array, (int var_name ));
X_PROTOTYPE(void load_var, (int var_name ));
X_PROTOTYPE(void load_array, (int var_name ));
X_PROTOTYPE(void decr_var, (int var_name ));
X_PROTOTYPE(void decr_array, (int var_name ));
X_PROTOTYPE(void incr_var, (int var_name ));
X_PROTOTYPE(void incr_array, (int var_name ));
X_PROTOTYPE(void auto_var, (int name ));
X_PROTOTYPE(void free_a_tree, (bc_array_node *root, int depth ));
X_PROTOTYPE(void pop_vars, (arg_list *list ));
X_PROTOTYPE(void process_params, (program_counter *pc, int func ));
X
X/* For the scanner and parser.... */
X_PROTOTYPE(int yyparse, (void));
X_PROTOTYPE(int yylex, (void)); 
X
X/* Other things... */
X_PROTOTYPE (int getopt, (int, char *[], CONST char *));
X
/
echo x - sbc.y
sed '/^X/s///' > sbc.y << '/'
X, %{
X/* sbc.y: A POSIX bc processor written for minix with no extensions.  */
X 
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X#include "bcdefs.h"
X#include "global.h"     /* To get the global variables. */
X#include "proto.h"
X%}
X
X%start program
X
X%union {
X	char *s_value;
X	char  c_value;
X	int   i_value;
X	arg_list *a_value;
X       }
X
X%token <i_value> NEWLINE AND OR NOT
X%token <s_value> STRING NAME NUMBER
X/*     '-', '+' are tokens themselves		*/
X%token <c_value> MUL_OP
X/*     '*', '/', '%' 				*/
X%token <c_value> ASSIGN_OP
X/*     '=', '+=',  '-=', '*=', '/=', '%=', '^=' */
X%token <s_value> REL_OP
X/*     '==', '<=', '>=', '!=', '<', '>' 	*/
X%token <c_value> INCR_DECR
X/*     '++', '--' 				*/
X%token <i_value> Define    Break    Quit    Length
X/*     'define', 'break', 'quit', 'length' 	*/
X%token <i_value> Return    For    If    While    Sqrt  Else
X/*     'return', 'for', 'if', 'while', 'sqrt',  'else' 	*/
X%token <i_value> Scale    Ibase    Obase    Auto  Read
X/*     'scale', 'ibase', 'obase', 'auto', 'read' 	*/
X%token <i_value> Warranty, Halt, Last, Continue, Print, Limits
X/*     'warranty', 'halt', 'last', 'continue', 'print', 'limits'  */
X
X/* The types of all other non-terminals. */
X%type <i_value> expression named_expression return_expression
X%type <a_value> opt_parameter_list parameter_list opt_auto_define_list
X%type <a_value> define_list opt_argument_list argument_list
X%type <i_value> program input_item semicolon_list statement_list
X%type <i_value> statement_or_error statement function relational_expression 
X
X/* precedence */
X%nonassoc REL_OP
X%right ASSIGN_OP
X%left '+' '-'
X%left MUL_OP
X%right '^'
X%nonassoc UNARY_MINUS
X%nonassoc INCR_DECR
X
X%%
Xprogram			: /* empty */
X			    {
X			      $$ = 0;
X			      std_only = TRUE;
X			      if (interactive)
X				{
X				  printf ("s%s\n", BC_VERSION);
X				  welcome();
X				}
X			    }
X			| program input_item
X			;
Xinput_item		: semicolon_list NEWLINE
X			    { run_code(); }
X			| function
X			    { run_code(); }
X			| error NEWLINE
X			    {
X			      yyerrok; 
X			      init_gen() ;
X			    }
X			;
Xsemicolon_list		: /* empty */
X			    { $$ = 0; }
X			| statement_or_error
X			| semicolon_list ';' statement_or_error
X			| semicolon_list ';'
X			;
Xstatement_list		: /* empty */
X			    { $$ = 0; }
X			| statement
X			| statement_list NEWLINE
X			| statement_list NEWLINE statement
X			| statement_list ';'
X			| statement_list ';' statement
X			;
Xstatement_or_error	: statement
X			| error statement
X			    { $$ = $2; }
X			;
Xstatement 		: Warranty
X			    { warranty("s"); }
X			| expression
X			    {
X			      if ($1 & 1)
X				generate ("W");
X			      else
X				generate ("p");
X			    }
X			| STRING
X			    {
X			      $$ = 0;
X			      generate ("w");
X			      generate ($1);
X			      free ($1);
X			    }
X			| Break
X			    {
X			      if (break_label == 0)
X				yyerror ("Break outside a for/while");
X			      else
X				{
X				  sprintf (genstr, "J%1d:", break_label);
X				  generate (genstr);
X				}
X			    }
X			| Quit
X			    { exit(0); }
X			| Return
X			    { generate ("0R"); }
X			| Return '(' return_expression ')'
X			    { generate ("R"); }
X			| For 
X			    {
X			      $1 = break_label; 
X			      break_label = next_label++;
X			    }
X			  '(' expression ';'
X			    {
X			      $4 = next_label++;
X			      sprintf (genstr, "pN%1d:", $4);
X			      generate (genstr);
X			    }
X			  relational_expression ';'
X			    {
X			      $7 = next_label++;
X			      sprintf (genstr, "B%1d:J%1d:", $7, break_label);
X			      generate (genstr);
X			      $<i_value>$ = next_label++;
X			      sprintf (genstr, "N%1d:", $<i_value>$);
X			      generate (genstr);
X			    }
X			  expression ')'
X			    {
X			      sprintf (genstr, "pJ%1d:N%1d:", $4, $7);
X			      generate (genstr);
X			    }
X			  statement
X			    {
X			      sprintf (genstr, "J%1d:N%1d:", $<i_value>9,
X				       break_label);
X			      generate (genstr);
X			      break_label = $1;
X			    }
X			| If '(' relational_expression ')' 
X			    {
X			      $3 = next_label++;
X			      sprintf (genstr, "Z%1d:", $3);
X			      generate (genstr);
X			    }
X			  statement
X			    {
X			      sprintf (genstr, "N%1d:", $3); 
X			      generate (genstr);
X			    }
X			| While 
X			    {
X			      $1 = next_label++;
X			      sprintf (genstr, "N%1d:", $1);
X			      generate (genstr);
X			    }
X			'(' relational_expression 
X			    {
X			      $4 = break_label; 
X			      break_label = next_label++;
X			      sprintf (genstr, "Z%1d:", break_label);
X			      generate (genstr);
X			    }
X			')' statement
X			    {
X			      sprintf (genstr, "J%1d:N%1d:", $1, break_label);
X			      generate (genstr);
X			      break_label = $4;
X			    }
X			| '{' statement_list '}'
X			    { $$ = 0; }
X			;
Xfunction 		: Define NAME '(' opt_parameter_list ')' '{'
X       			  NEWLINE opt_auto_define_list 
X			    {
X			      check_params ($4,$8);
X			      sprintf (genstr, "F%d,%s.%s[", lookup($2,FUNCT),
X				       arg_str ($4,TRUE), arg_str ($8,TRUE));
X			      generate (genstr);
X			      free_args ($4);
X			      free_args ($8);
X			      $1 = next_label;
X			      next_label = 0;
X			    }
X			  statement_list NEWLINE '}'
X			    {
X			      generate ("0R]");
X			      next_label = $1;
X			    }
X			;
Xopt_parameter_list	: /* empty */ 
X			    { $$ = NULL; }
X			| parameter_list
X			;
Xparameter_list 		: NAME
X			    { $$ = nextarg (NULL, lookup($1,SIMPLE)); }
X			| define_list ',' NAME
X			    { $$ = nextarg ($1, lookup($3,SIMPLE)); }
X			;
Xopt_auto_define_list 	: /* empty */ 
X			    { $$ = NULL; }
X			| Auto define_list NEWLINE
X			    { $$ = $2; } 
X			| Auto define_list ';'
X			    { $$ = $2; } 
X			;
Xdefine_list 		: NAME
X			    { $$ = nextarg (NULL, lookup($1,SIMPLE)); }
X			| NAME '[' ']'
X			    { $$ = nextarg (NULL, lookup($1,ARRAY)); }
X			| define_list ',' NAME
X			    { $$ = nextarg ($1, lookup($3,SIMPLE)); }
X			| define_list ',' NAME '[' ']'
X			    { $$ = nextarg ($1, lookup($3,ARRAY)); }
X			;
Xopt_argument_list	: /* empty */
X			    { $$ = NULL; }
X			| argument_list
X			;
Xargument_list 		: expression
X			    { $$ = nextarg (NULL,0); }
X			| argument_list ',' expression
X			    { $$ = nextarg ($1,0); }
X			;
Xrelational_expression	: expression
X			    { $$ = 0; }
X			| expression REL_OP expression
X			    {
X			      $$ = 0;
X			      switch (*($2))
X				{
X				case '=':
X				  generate ("=");
X				  break;
X				case '!':
X				  generate ("#");
X				  break;
X				case '<':
X				  if ($2[1] == '=')
X				    generate ("{");
X				  else
X				    generate ("<");
X				  break;
X				case '>':
X				  if ($2[1] == '=')
X				    generate ("}");
X				  else
X				    generate (">");
X				  break;
X				}
X			    }
X			;
Xreturn_expression	: /* empty */
X			    {
X			      $$ = 0;
X			      generate ("0");
X			    }
X			| expression
X			;
Xexpression		: named_expression ASSIGN_OP 
X			    {
X			      if ($2 != '=')
X				{
X				  if ($1 < 0)
X				    sprintf (genstr, "DL%d:", -$1);
X				  else
X				    sprintf (genstr, "l%d:", $1);
X				  generate (genstr);
X				}
X			    }
X			  expression
X			    {
X			      $$ = 0;
X			      if ($2 != '=')
X				{
X				  sprintf (genstr, "%c", $2);
X				  generate (genstr);
X				}
X			      if ($1 < 0)
X				sprintf (genstr, "S%d:", -$1);
X			      else
X				sprintf (genstr, "s%d:", $1);
X			      generate (genstr);
X			    }
X			| expression '+' expression
X			    { generate ("+"); }
X			| expression '-' expression
X			    { generate ("-"); }
X			| expression MUL_OP expression
X			    {
X			      genstr[0] = $2;
X			      genstr[1] = 0;
X			      generate (genstr);
X			    }
X			| expression '^' expression
X			    { generate ("^"); }
X			| '-' expression           %prec UNARY_MINUS
X			    { generate ("n"); $$ = 1;}
X			| named_expression
X			    {
X			      $$ = 1;
X			      if ($1 < 0)
X				sprintf (genstr, "L%d:", -$1);
X			      else
X				sprintf (genstr, "l%d:", $1);
X			      generate (genstr);
X			    }
X			| NUMBER
X			    {
X			      int len = strlen($1);
X			      $$ = 1;
X			      if (len == 1 && *$1 == '0')
X				generate ("0");
X			      else
X				{
X				  if (len == 1 && *$1 == '1')
X				    generate ("1");
X				  else
X				    {
X				      generate ("K");
X				      generate ($1);
X				      generate (":");
X				    }
X				  free ($1);
X				}
X			    }
X			| '(' expression ')'
X			    { $$ = 1; }
X			| NAME '(' opt_argument_list ')'
X			    {
X			      $$ = 1;
X			      if ($3 != NULL)
X				{ 
X				  sprintf (genstr, "C%d,%s:", lookup($1,FUNCT),
X					   arg_str ($3,FALSE));
X				  free_args ($3);
X				}
X			      else
X				  sprintf (genstr, "C%d:", lookup($1,FUNCT));
X			      generate (genstr);
X			    }
X			| INCR_DECR named_expression
X			    {
X			      $$ = 1;
X			      if ($2 < 0)
X				{
X				  if ($1 == '+')
X				    sprintf (genstr, "DA%d:L%d:", -$2, -$2);
X				  else
X				    sprintf (genstr, "DM%d:L%d:", -$2, -$2);
X				}
X			      else
X				{
X				  if ($1 == '+')
X				    sprintf (genstr, "i%d:l%d:", $2, $2);
X				  else
X				    sprintf (genstr, "d%d:l%d:", $2, $2);
X				}
X			      generate (genstr);
X			    }
X			| named_expression INCR_DECR
X			    {
X			      $$ = 1;
X			      if ($1 < 0)
X				{
X				  sprintf (genstr, "DL%d:x", -$1);
X				  generate (genstr); 
X				  if ($2 == '+')
X				    sprintf (genstr, "A%d:", -$1);
X				  else
X				    sprintf (genstr, "M%d:", -$1);
X				}
X			      else
X				{
X				  sprintf (genstr, "l%d:", $1);
X				  generate (genstr);
X				  if ($2 == '+')
X				    sprintf (genstr, "i%d:", $1);
X				  else
X				    sprintf (genstr, "d%d:", $1);
X				}
X			      generate (genstr);
X			    }
X			| Length '(' expression ')'
X			    { generate ("cL"); $$ = 1;}
X			| Sqrt '(' expression ')'
X			    { generate ("cR"); $$ = 1;}
X			| Scale '(' expression ')'
X			    { generate ("cS"); $$ = 1;}
X			;
Xnamed_expression	: NAME
X			    { $$ = lookup($1,SIMPLE); }
X			| NAME '[' expression ']'
X			    { $$ = lookup($1,ARRAY); }
X			| Ibase
X			    { $$ = 0; }
X			| Obase
X			    { $$ = 1; }
X			| Scale
X			    { $$ = 2; }
X			;
X
X%%
/
echo x - scan.c
sed '/^X/s///' > scan.c << '/'
X/* A lexical scanner generated by flex */
X
X/* scanner skeleton version:
X * $Header: /usr/fsys/odin/a/vern/flex/RCS/flex.skel,v 2.16 90/08/03 14:09:36 vern Exp $
X */
X
X#define FLEX_SCANNER
X
X#include <stdio.h>
X
X
X/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */
X#ifdef c_plusplus
X#ifndef __cplusplus
X#define __cplusplus
X#endif
X#endif
X
X
X#ifdef __cplusplus
X
X#include <stdlib.h>
X#include <osfcn.h>
X
X/* use prototypes in function declarations */
X#define YY_USE_PROTOS
X
X/* the "const" storage-class-modifier is valid */
X#define YY_USE_CONST
X
X#else	/* ! __cplusplus */
X
X#ifdef __STDC__
X
X#ifdef __GNUC__
X#include <stddef.h>
Xvoid *malloc( size_t );
Xvoid free( void* );
X#else
X#include <stdlib.h>
X#endif	/* __GNUC__ */
X
X#define YY_USE_PROTOS
X#define YY_USE_CONST
X
X#endif	/* __STDC__ */
X#endif	/* ! __cplusplus */
X
X
X#ifdef __TURBOC__
X#define YY_USE_CONST
X#endif
X
X
X#ifndef YY_USE_CONST
X#define const
X#endif
X
X
X#ifdef YY_USE_PROTOS
X#define YY_PROTO(proto) proto
X#else
X#define YY_PROTO(proto) ()
X/* we can't get here if it's an ANSI C compiler, or a C++ compiler,
X * so it's got to be a K&R compiler, and therefore there's no standard
X * place from which to include these definitions
X */
X/* char *malloc();
Xint free(); */
Xint read();
X#endif
X
X
X/* amount of stuff to slurp up with each read */
X#ifndef YY_READ_BUF_SIZE
X#define YY_READ_BUF_SIZE 8192
X#endif
X
X/* returned upon end-of-file */
X#define YY_END_TOK 0
X
X/* copy whatever the last rule matched to the standard output */
X
X/* cast to (char *) is because for 8-bit chars, yytext is (unsigned char *) */
X/* this used to be an fputs(), but since the string might contain NUL's,
X * we now use fwrite()
X */
X#define ECHO (void) fwrite( (char *) yytext, yyleng, 1, yyout )
X
X/* gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
X * is returned in "result".
X */
X#define YY_INPUT(buf,result,max_size) \
X	if ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \
X	    YY_FATAL_ERROR( "read() in flex scanner failed" );
X#define YY_NULL 0
X
X/* no semi-colon after return; correct usage is to write "yyterminate();" -
X * we don't want an extra ';' after the "return" because that will cause
X * some compilers to complain about unreachable statements.
X */
X#define yyterminate() return ( YY_NULL )
X
X/* report a fatal error */
X
X/* The funky do-while is used to turn this macro definition into
X * a single C statement (which needs a semi-colon terminator).
X * This avoids problems with code like:
X *
X * 	if ( something_happens )
X *		YY_FATAL_ERROR( "oops, the something happened" );
X *	else
X *		everything_okay();
X *
X * Prior to using the do-while the compiler would get upset at the
X * "else" because it interpreted the "if" statement as being all
X * done when it reached the ';' after the YY_FATAL_ERROR() call.
X */
X
X#define YY_FATAL_ERROR(msg) \
X	do \
X		{ \
X		(void) fputs( msg, stderr ); \
X		(void) putc( '\n', stderr ); \
X		exit( 1 ); \
X		} \
X	while ( 0 )
X
X/* default yywrap function - always treat EOF as an EOF */
X#define yywrap() 1
X
X/* enter a start condition.  This macro really ought to take a parameter,
X * but we do it the disgusting crufty way forced on us by the ()-less
X * definition of BEGIN
X */
X#define BEGIN yy_start = 1 + 2 *
X
X/* action number for EOF rule of a given start state */
X#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
X
X/* special action meaning "start processing a new file" */
X#define YY_NEW_FILE \
X	do \
X		{ \
X		yy_init_buffer( yy_current_buffer, yyin ); \
X		yy_load_buffer_state(); \
X		} \
X	while ( 0 )
X
X/* default declaration of generated scanner - a define so the user can
X * easily add parameters
X */
X#define YY_DECL int yylex YY_PROTO(( void )) 
X
X/* code executed at the end of each rule */
X#define YY_BREAK break;
X
X#define YY_END_OF_BUFFER_CHAR 0
X
X#ifndef YY_BUF_SIZE
X#define YY_BUF_SIZE (YY_READ_BUF_SIZE * 2) /* size of default input buffer */
X#endif
X
Xtypedef struct yy_buffer_state *YY_BUFFER_STATE;
X
X#define YY_CHAR unsigned char
X# line 1 "scan.l"
X#define INITIAL 0
X# line 2 "scan.l"
X/* scan.l: the (f)lex description file for the scanner. */
X
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X#include "bcdefs.h"
X#include "y.tab.h"
X#include "global.h"
X#include "proto.h"
X
X/* Using flex, we can ask for a smaller input buffer.  With lex, this
X   does nothing! */
X
X#ifdef SMALL_BUF
X#undef YY_READ_BUF_SIZE
X#define YY_READ_BUF_SIZE 512
X#endif
X
X/* We want to define our own yywrap. */
X#undef yywrap
X_PROTOTYPE(int yywrap, (void));
X
X/* MINIX returns from read with < 0 if SIGINT is  encountered.
X   In flex, we can redefine YY_INPUT to the following.  In lex, this
X   does nothing! */
X#include <errno.h>
X#undef  YY_INPUT
X#define YY_INPUT(buf,result,max_size) \
X	while ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \
X	    if (errno != EINTR) \
X		YY_FATAL_ERROR( "read() in flex scanner failed" );
X
X# line 60 "scan.l"
X
X/* done after the current pattern has been matched and before the
X * corresponding action - sets up yytext
X */
X#define YY_DO_BEFORE_ACTION \
X	yytext = yy_bp; \
X	yyleng = yy_cp - yy_bp; \
X	yy_hold_char = *yy_cp; \
X	*yy_cp = '\0'; \
X	yy_c_buf_p = yy_cp;
X
X#define EOB_ACT_CONTINUE_SCAN 0
X#define EOB_ACT_END_OF_FILE 1
X#define EOB_ACT_LAST_MATCH 2
X
X/* return all but the first 'n' matched characters back to the input stream */
X#define yyless(n) \
X	do \
X		{ \
X		/* undo effects of setting up yytext */ \
X		*yy_cp = yy_hold_char; \
X		yy_c_buf_p = yy_cp = yy_bp + n; \
X		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
X		} \
X	while ( 0 )
X
X#define unput(c) yyunput( c, yytext )
X
X
Xstruct yy_buffer_state
X    {
X    FILE *yy_input_file;
X
X    YY_CHAR *yy_ch_buf;		/* input buffer */
X    YY_CHAR *yy_buf_pos;	/* current position in input buffer */
X
X    /* size of input buffer in bytes, not including room for EOB characters*/
X    int yy_buf_size;	
X
X    /* number of characters read into yy_ch_buf, not including EOB characters */
X    int yy_n_chars;
X
X    int yy_eof_status;		/* whether we've seen an EOF on this buffer */
X#define EOF_NOT_SEEN 0
X    /* "pending" happens when the EOF has been seen but there's still
X     * some text process
X     */
X#define EOF_PENDING 1
X#define EOF_DONE 2
X    };
X
Xstatic YY_BUFFER_STATE yy_current_buffer;
X
X/* we provide macros for accessing buffer states in case in the
X * future we want to put the buffer states in a more general
X * "scanner state"
X */
X#define YY_CURRENT_BUFFER yy_current_buffer
X
X
X/* yy_hold_char holds the character lost when yytext is formed */
Xstatic YY_CHAR yy_hold_char;
X
Xstatic int yy_n_chars;		/* number of characters read into yy_ch_buf */
X
X
X
X#ifndef YY_USER_ACTION
X#define YY_USER_ACTION
X#endif
X
X#ifndef YY_USER_INIT
X#define YY_USER_INIT
X#endif
X
Xextern YY_CHAR *yytext;
Xextern int yyleng;
Xextern FILE *yyin, *yyout;
X
XYY_CHAR *yytext;
Xint yyleng;
X
XFILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
X
X#define YY_END_OF_BUFFER 40
Xtypedef int yy_state_type;
Xstatic const short int yy_accept[144] =
X    {   0,
X        0,    0,   40,   38,   33,   31,   25,   38,   26,   38,
X       22,   26,   22,   22,   38,   26,   37,   29,   27,   29,
X       38,   22,   35,   35,   35,   35,   35,   35,   35,   35,
X       35,   35,   35,   35,   35,   35,   35,   35,   38,   33,
X       29,    0,   36,   27,   23,   30,   37,    0,   34,   37,
X       37,    0,   28,   32,   35,   35,   35,   35,   35,   35,
X       35,   35,   35,    7,   35,   35,   35,   35,   35,   35,
X       35,   35,   35,   35,   35,   24,   37,    0,    0,   37,
X        0,   35,   35,   35,   35,   35,    6,   35,   35,   35,
X       35,   35,   35,   35,   35,   35,   35,   35,   35,   35,
X
X       35,   13,   35,   35,   35,   14,   16,   35,   17,   35,
X       35,   35,   35,    3,   15,   35,   35,    9,   35,   35,
X        2,   35,   35,   11,   35,   35,   12,   20,   35,   10,
X       35,    8,   35,    1,    4,   21,    5,   35,   35,   35,
X       19,   18,    0
X    } ;
X
Xstatic const YY_CHAR yy_ec[256] =
X    {   0,
X        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    2,    4,    5,    1,    1,    6,    7,    1,    8,
X        9,   10,   11,   12,   13,   14,   15,   16,   16,   16,
X       16,   16,   16,   16,   16,   16,   16,    1,   17,   18,
X       19,   20,    1,    1,   21,   21,   21,   21,   21,   21,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X       22,   23,   24,   25,   26,    1,   27,   28,   29,   30,
X
X       31,   32,   33,   34,   35,   36,   37,   38,   39,   40,
X       41,   42,   43,   44,   45,   46,   47,   36,   48,   36,
X       49,   36,   50,   51,   52,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1
X    } ;
X
Xstatic const YY_CHAR yy_meta[53] =
X    {   0,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    2,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    2,    2,    2,    2,    2,
X        2,    2,    2,    2,    2,    2,    2,    2,    2,    2,
X        2,    2,    2,    2,    2,    2,    2,    2,    2,    1,
X        1,    1
X    } ;
X
Xstatic const short int yy_base[146] =
X    {   0,
X        0,    0,  193,  194,  190,  194,  172,  185,  170,  181,
X      194,  168,   42,   41,   41,   46,   52,  167,   61,  166,
X      181,  164,  135,  137,  139,  148,  140,  136,    0,  149,
X       27,   50,  147,  130,  126,  141,   40,   36,  120,  168,
X      194,  164,  194,  194,  194,  194,   66,  165,  194,   72,
X       76,  164,  194,  194,    0,  120,  134,  124,  131,  117,
X      117,  122,  132,    0,  113,  117,  117,  128,  119,  118,
X       52,  125,  107,  106,  114,  194,   80,  145,   84,   88,
X      144,  105,  118,   98,  108,  111,    0,   95,   95,   93,
X      105,  102,   91,   95,   88,  103,   85,   93,   84,   85,
X
X       90,    0,   90,   91,   85,    0,    0,   93,    0,   77,
X       76,   90,   74,    0,    0,   75,   87,    0,   90,   85,
X        0,   75,   83,    0,   76,   63,    0,    0,   66,    0,
X       62,    0,   47,    0,    0,    0,    0,   45,   53,   29,
X        0,    0,  194,  111,   56
X    } ;
X
Xstatic const short int yy_def[146] =
X    {   0,
X      143,    1,  143,  143,  143,  143,  143,  144,  143,  143,
X      143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
X      143,  143,  145,  145,  145,  145,  145,  145,  145,  145,
X      145,  145,  145,  145,  145,  145,  145,  145,  143,  143,
X      143,  144,  143,  143,  143,  143,  143,  143,  143,  143,
X      143,  143,  143,  143,  145,  145,  145,  145,  145,  145,
X      145,  145,  145,  145,  145,  145,  145,  145,  145,  145,
X      145,  145,  145,  145,  145,  143,  143,  143,  143,  143,
X      143,  145,  145,  145,  145,  145,  145,  145,  145,  145,
X      145,  145,  145,  145,  145,  145,  145,  145,  145,  145,
X
X      145,  145,  145,  145,  145,  145,  145,  145,  145,  145,
X      145,  145,  145,  145,  145,  145,  145,  145,  145,  145,
X      145,  145,  145,  145,  145,  145,  145,  145,  145,  145,
X      145,  145,  145,  145,  145,  145,  145,  145,  145,  145,
X      145,  145,    0,  143,  143
X    } ;
X
Xstatic const short int yy_nxt[247] =
X    {   0,
X        4,    5,    6,    7,    8,    9,   10,   11,   11,   12,
X       13,   11,   14,   15,   16,   17,   11,   18,   19,   20,
X       17,   11,   21,   11,   22,    4,   23,   24,   25,   26,
X       27,   28,   29,   30,   31,   29,   29,   32,   29,   29,
X       33,   34,   35,   36,   37,   29,   29,   38,   29,   11,
X       39,   11,   46,   46,   63,   49,   47,   55,   64,   44,
X       44,   47,   74,   48,   44,   50,   53,   51,   72,   75,
X       53,   53,   51,   53,   52,   53,   65,  142,   96,   41,
X       66,   77,   73,  141,   67,   53,   77,   80,   78,   50,
X      140,   51,   80,  139,   81,   77,   51,   97,   52,   47,
X
X       77,  138,   78,   80,   47,  137,   48,  136,   80,  135,
X       81,   42,   42,  134,  133,  132,  131,  130,  129,  128,
X      127,  126,  125,  124,  123,  122,  121,  120,  119,  118,
X      117,  116,  115,  114,  113,  112,  111,  110,  109,  108,
X      107,  106,  105,  104,  103,  102,   80,   77,  101,  100,
X       99,   98,   95,   94,   93,   92,   91,   90,   89,   88,
X       87,   86,   85,   84,   83,   82,   51,   79,   43,   40,
X       76,   71,   70,   69,   68,   62,   61,   60,   59,   58,
X       57,   56,   44,   54,   41,   41,   44,   45,   44,   43,
X       41,   40,  143,    3,  143,  143,  143,  143,  143,  143,
X
X      143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
X      143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
X      143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
X      143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
X      143,  143,  143,  143,  143,  143
X    } ;
X
Xstatic const short int yy_chk[247] =
X    {   0,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
X        1,    1,   13,   14,   31,   16,   15,  145,   31,   14,
X       13,   15,   38,   15,   16,   17,   19,   17,   37,   38,
X       19,   19,   17,   19,   17,   19,   32,  140,   71,   19,
X       32,   47,   37,  139,   32,   19,   47,   50,   47,   51,
X      138,   51,   50,  133,   50,   77,   51,   71,   51,   79,
X
X       77,  131,   77,   80,   79,  129,   79,  126,   80,  125,
X       80,  144,  144,  123,  122,  120,  119,  117,  116,  113,
X      112,  111,  110,  108,  105,  104,  103,  101,  100,   99,
X       98,   97,   96,   95,   94,   93,   92,   91,   90,   89,
X       88,   86,   85,   84,   83,   82,   81,   78,   75,   74,
X       73,   72,   70,   69,   68,   67,   66,   65,   63,   62,
X       61,   60,   59,   58,   57,   56,   52,   48,   42,   40,
X       39,   36,   35,   34,   33,   30,   28,   27,   26,   25,
X       24,   23,   22,   21,   20,   18,   12,   10,    9,    8,
X        7,    5,    3,  143,  143,  143,  143,  143,  143,  143,
X
X      143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
X      143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
X      143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
X      143,  143,  143,  143,  143,  143,  143,  143,  143,  143,
X      143,  143,  143,  143,  143,  143
X    } ;
X
Xstatic yy_state_type yy_last_accepting_state;
Xstatic YY_CHAR *yy_last_accepting_cpos;
X
X/* the intent behind this definition is that it'll catch
X * any uses of REJECT which flex missed
X */
X#define REJECT reject_used_but_not_detected
X#define yymore() yymore_used_but_not_detected
X#define YY_MORE_ADJ 0
X
X/* these variables are all declared out here so that section 3 code can
X * manipulate them
X */
X/* points to current character in buffer */
Xstatic YY_CHAR *yy_c_buf_p = (YY_CHAR *) 0;
Xstatic int yy_init = 1;		/* whether we need to initialize */
Xstatic int yy_start = 0;	/* start state number */
X
X/* flag which is used to allow yywrap()'s to do buffer switches
X * instead of setting up a fresh yyin.  A bit of a hack ...
X */
Xstatic int yy_did_buffer_switch_on_eof;
X
Xstatic yy_state_type yy_get_previous_state YY_PROTO(( void ));
Xstatic yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state ));
Xstatic int yy_get_next_buffer YY_PROTO(( void ));
Xstatic void yyunput YY_PROTO(( YY_CHAR c, YY_CHAR *buf_ptr ));
Xvoid yyrestart YY_PROTO(( FILE *input_file ));
Xvoid yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer ));
Xvoid yy_load_buffer_state YY_PROTO(( void ));
XYY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size ));
Xvoid yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b ));
Xvoid yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file ));
X
X#define yy_new_buffer yy_create_buffer
X
X#ifdef __cplusplus
Xstatic int yyinput YY_PROTO(( void ));
X#else
Xstatic int input YY_PROTO(( void ));
X#endif
X
XYY_DECL
X    {
X    register yy_state_type yy_current_state;
X    register YY_CHAR *yy_cp, *yy_bp;
X    register int yy_act;
X
X
X
X    if ( yy_init )
X	{
X	YY_USER_INIT;
X
X	if ( ! yy_start )
X	    yy_start = 1;	/* first start state */
X
X	if ( ! yyin )
X	    yyin = stdin;
X
X	if ( ! yyout )
X	    yyout = stdout;
X
X	if ( yy_current_buffer )
X	    yy_init_buffer( yy_current_buffer, yyin );
X	else
X	    yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE );
X
X	yy_load_buffer_state();
X
X	yy_init = 0;
X	}
X
X    while ( 1 )		/* loops until end-of-file is reached */
X	{
X	yy_cp = yy_c_buf_p;
X
X	/* support of yytext */
X	*yy_cp = yy_hold_char;
X
X	/* yy_bp points to the position in yy_ch_buf of the start of the
X	 * current run.
X	 */
X	yy_bp = yy_cp;
X
X	yy_current_state = yy_start;
Xyy_match:
X	do
X	    {
X	    register YY_CHAR yy_c = yy_ec[*yy_cp];
X	    if ( yy_accept[yy_current_state] )
X		{
X		yy_last_accepting_state = yy_current_state;
X		yy_last_accepting_cpos = yy_cp;
X		}
X	    while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
X		{
X		yy_current_state = yy_def[yy_current_state];
X		if ( yy_current_state >= 144 )
X		    yy_c = yy_meta[yy_c];
X		}
X	    yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
X	    ++yy_cp;
X	    }
X	while ( yy_base[yy_current_state] != 194 );
X
Xyy_find_action:
X	yy_act = yy_accept[yy_current_state];
X
X	YY_DO_BEFORE_ACTION;
X	YY_USER_ACTION;
X
Xdo_action:	/* this label is used only to access EOF actions */
X
X
X	switch ( yy_act )
X	    {
X	    case 0: /* must backtrack */
X	    /* undo the effects of YY_DO_BEFORE_ACTION */
X	    *yy_cp = yy_hold_char;
X	    yy_cp = yy_last_accepting_cpos;
X	    yy_current_state = yy_last_accepting_state;
X	    goto yy_find_action;
X
Xcase 1:
X# line 61 "scan.l"
Xreturn(Define);
X	YY_BREAK
Xcase 2:
X# line 62 "scan.l"
Xreturn(Break);
X	YY_BREAK
Xcase 3:
X# line 63 "scan.l"
Xreturn(Quit);
X	YY_BREAK
Xcase 4:
X# line 64 "scan.l"
Xreturn(Length);
X	YY_BREAK
Xcase 5:
X# line 65 "scan.l"
Xreturn(Return);
X	YY_BREAK
Xcase 6:
X# line 66 "scan.l"
Xreturn(For);
X	YY_BREAK
Xcase 7:
X# line 67 "scan.l"
Xreturn(If);
X	YY_BREAK
Xcase 8:
X# line 68 "scan.l"
Xreturn(While);
X	YY_BREAK
Xcase 9:
X# line 69 "scan.l"
Xreturn(Sqrt);
X	YY_BREAK
Xcase 10:
X# line 70 "scan.l"
Xreturn(Scale);
X	YY_BREAK
Xcase 11:
X# line 71 "scan.l"
Xreturn(Ibase);
X	YY_BREAK
Xcase 12:
X# line 72 "scan.l"
Xreturn(Obase);
X	YY_BREAK
Xcase 13:
X# line 73 "scan.l"
Xreturn(Auto);
X	YY_BREAK
Xcase 14:
X# line 74 "scan.l"
Xreturn(Else);
X	YY_BREAK
Xcase 15:
X# line 75 "scan.l"
Xreturn(Read);
X	YY_BREAK
Xcase 16:
X# line 76 "scan.l"
Xreturn(Halt);
X	YY_BREAK
Xcase 17:
X# line 77 "scan.l"
Xreturn(Last);
X	YY_BREAK
Xcase 18:
X# line 78 "scan.l"
Xreturn(Warranty);
X	YY_BREAK
Xcase 19:
X# line 79 "scan.l"
Xreturn(Continue);
X	YY_BREAK
Xcase 20:
X# line 80 "scan.l"
Xreturn(Print);
X	YY_BREAK
Xcase 21:
X# line 81 "scan.l"
Xreturn(Limits);
X	YY_BREAK
Xcase 22:
X# line 82 "scan.l"
X{ yylval.c_value = yytext[0]; 
X					      return((int)yytext[0]); }
X	YY_BREAK
Xcase 23:
X# line 84 "scan.l"
X{ return(AND); }
X	YY_BREAK
Xcase 24:
X# line 85 "scan.l"
X{ return(OR); }
X	YY_BREAK
Xcase 25:
X# line 86 "scan.l"
X{ return(NOT); }
X	YY_BREAK
Xcase 26:
X# line 87 "scan.l"
X{ yylval.c_value = yytext[0]; return(MUL_OP); }
X	YY_BREAK
Xcase 27:
X# line 88 "scan.l"
X{ yylval.c_value = yytext[0]; return(ASSIGN_OP); }
X	YY_BREAK
Xcase 28:
X# line 89 "scan.l"
X{ 
X#ifdef OLD_EQ_OP
X			 char warn_save;
X			 warn_save = warn_not_std;
X			 warn_not_std = TRUE;
X			 warn ("Old fashioned =<op>");
X			 warn_not_std = warn_save;
X			 yylval.c_value = yytext[1];
X#else
X			 yylval.c_value = '=';
X			 yyless (1);
X#endif
X			 return(ASSIGN_OP);
X		       }
X	YY_BREAK
Xcase 29:
X# line 103 "scan.l"
X{ yylval.s_value = strcopyof(yytext); return(REL_OP); }
X	YY_BREAK
Xcase 30:
X# line 104 "scan.l"
X{ yylval.c_value = yytext[0]; return(INCR_DECR); }
X	YY_BREAK
Xcase 31:
X# line 105 "scan.l"
X{ line_no++; return(NEWLINE); }
X	YY_BREAK
Xcase 32:
X# line 106 "scan.l"
X{  line_no++;  /* ignore a "quoted" newline */ }
X	YY_BREAK
Xcase 33:
X# line 107 "scan.l"
X{ /* ignore spaces and tabs */ }
X	YY_BREAK
Xcase 34:
X# line 108 "scan.l"
X{
X	int c;
X
X	for (;;)
X	  {
X	    while ( ((c=input()) != '*') && (c != EOF)) 
X	      /* eat it */
X	      if (c == '\n') line_no++;
X	    if (c == '*')
X 	      {
X		while ( (c=input()) == '*') /* eat it*/;
X		if (c == '/') break; /* at end of comment */
X		if (c == '\n') line_no++;
X	      }
X	    if (c == EOF)
X	      {
X		fprintf (stderr,"EOF encountered in a comment.\n");
X		break;
X	      }
X	  }
X      }
X	YY_BREAK
Xcase 35:
X# line 129 "scan.l"
X{ yylval.s_value = strcopyof(yytext); return(NAME); }
X	YY_BREAK
Xcase 36:
X# line 130 "scan.l"
X{
X 	      unsigned char *look;
X	      int count = 0;
X	      yylval.s_value = strcopyof(yytext);
X	      for (look = yytext; *look != 0; look++)
X		{
X		  if (*look == '\n') line_no++;
X		  if (*look == '"')  count++;
X		}
X	      if (count != 2) yyerror ("NUL character in string.");
X	      return(STRING);
X	    }
X	YY_BREAK
Xcase 37:
X# line 142 "scan.l"
X{
X	      unsigned char *src, *dst;
X	      int len;
X	      /* remove a trailing decimal point. */
X	      len = strlen(yytext);
X	      if (yytext[len-1] == '.')
X	        yytext[len-1] = 0;
X	      /* remove leading zeros. */
X	      src = yytext;
X	      dst = yytext;
X	      while (*src == '0') src++;
X	      if (*src == 0) src--;
X	      /* Copy strings removing the newlines. */
X	      while (*src != 0)
X		{
X	          if (*src == '\\')
X		    {
X		      src++; src++;
X		      line_no++;
X		    }
X		  else
X		    *dst++ = *src++;
X	        }
X	      *dst = 0;
X	      yylval.s_value = strcopyof(yytext); 
X	      return(NUMBER);
X	    }
X	YY_BREAK
Xcase 38:
X# line 169 "scan.l"
X{
X	  if (yytext[0] < ' ')
X	    yyerror ("illegal character: ^%c",yytext[0] + '@');
X	  else
X	    if (yytext[0] > '~')
X	      yyerror ("illegal character: \\%3d", (int) yytext[0]);
X	    else
X	      yyerror ("illegal character: %s",yytext);
X	}
X	YY_BREAK
Xcase 39:
X# line 178 "scan.l"
XECHO;
X	YY_BREAK
Xcase YY_STATE_EOF(INITIAL):
X    yyterminate();
X
X	    case YY_END_OF_BUFFER:
X		{
X		/* amount of text matched not including the EOB char */
X		int yy_amount_of_matched_text = yy_cp - yytext - 1;
X
X		/* undo the effects of YY_DO_BEFORE_ACTION */
X		*yy_cp = yy_hold_char;
X
X		/* note that here we test for yy_c_buf_p "<=" to the position
X		 * of the first EOB in the buffer, since yy_c_buf_p will
X		 * already have been incremented past the NUL character
X		 * (since all states make transitions on EOB to the end-
X		 * of-buffer state).  Contrast this with the test in yyinput().
X		 */
X		if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] )
X		    /* this was really a NUL */
X		    {
X		    yy_state_type yy_next_state;
X
X		    yy_c_buf_p = yytext + yy_amount_of_matched_text;
X
X		    yy_current_state = yy_get_previous_state();
X
X		    /* okay, we're now positioned to make the
X		     * NUL transition.  We couldn't have
X		     * yy_get_previous_state() go ahead and do it
X		     * for us because it doesn't know how to deal
X		     * with the possibility of jamming (and we
X		     * don't want to build jamming into it because
X		     * then it will run more slowly)
X		     */
X
X		    yy_next_state = yy_try_NUL_trans( yy_current_state );
X
X		    yy_bp = yytext + YY_MORE_ADJ;
X
X		    if ( yy_next_state )
X			{
X			/* consume the NUL */
X			yy_cp = ++yy_c_buf_p;
X			yy_current_state = yy_next_state;
X			goto yy_match;
X			}
X
X		    else
X			{
X			goto yy_find_action;
X			}
X		    }
X
X		else switch ( yy_get_next_buffer() )
X		    {
X		    case EOB_ACT_END_OF_FILE:
X			{
X			yy_did_buffer_switch_on_eof = 0;
X
X			if ( yywrap() )
X			    {
X			    /* note: because we've taken care in
X			     * yy_get_next_buffer() to have set up yytext,
X			     * we can now set up yy_c_buf_p so that if some
X			     * total hoser (like flex itself) wants
X			     * to call the scanner after we return the
X			     * YY_NULL, it'll still work - another YY_NULL
X			     * will get returned.
X			     */
X			    yy_c_buf_p = yytext + YY_MORE_ADJ;
X
X			    yy_act = YY_STATE_EOF((yy_start - 1) / 2);
X			    goto do_action;
X			    }
X
X			else
X			    {
X			    if ( ! yy_did_buffer_switch_on_eof )
X				YY_NEW_FILE;
X			    }
X			}
X			break;
X
X		    case EOB_ACT_CONTINUE_SCAN:
X			yy_c_buf_p = yytext + yy_amount_of_matched_text;
X
X			yy_current_state = yy_get_previous_state();
X
X			yy_cp = yy_c_buf_p;
X			yy_bp = yytext + YY_MORE_ADJ;
X			goto yy_match;
X
X		    case EOB_ACT_LAST_MATCH:
X			yy_c_buf_p =
X			    &yy_current_buffer->yy_ch_buf[yy_n_chars];
X
X			yy_current_state = yy_get_previous_state();
X
X			yy_cp = yy_c_buf_p;
X			yy_bp = yytext + YY_MORE_ADJ;
X			goto yy_find_action;
X		    }
X		break;
X		}
X
X	    default:
X#ifdef FLEX_DEBUG
X		printf( "action # %d\n", yy_act );
X#endif
X		YY_FATAL_ERROR(
X			"fatal flex scanner internal error--no action found" );
X	    }
X	}
X    }
X
X
X/* yy_get_next_buffer - try to read in a new buffer
X *
X * synopsis
X *     int yy_get_next_buffer();
X *     
X * returns a code representing an action
X *     EOB_ACT_LAST_MATCH - 
X *     EOB_ACT_CONTINUE_SCAN - continue scanning from current position
X *     EOB_ACT_END_OF_FILE - end of file
X */
X
Xstatic int yy_get_next_buffer()
X
X    {
X    register YY_CHAR *dest = yy_current_buffer->yy_ch_buf;
X    register YY_CHAR *source = yytext - 1; /* copy prev. char, too */
X    register int number_to_move, i;
X    int ret_val;
X
X    if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] )
X	YY_FATAL_ERROR(
X		"fatal flex scanner internal error--end of buffer missed" );
X
X    /* try to read more data */
X
X    /* first move last chars to start of buffer */
X    number_to_move = yy_c_buf_p - yytext;
X
X    for ( i = 0; i < number_to_move; ++i )
X	*(dest++) = *(source++);
X
X    if ( yy_current_buffer->yy_eof_status != EOF_NOT_SEEN )
X	/* don't do the read, it's not guaranteed to return an EOF,
X	 * just force an EOF
X	 */
X	yy_n_chars = 0;
X
X    else
X	{
X	int num_to_read = yy_current_buffer->yy_buf_size - number_to_move - 1;
X
X	if ( num_to_read > YY_READ_BUF_SIZE )
X	    num_to_read = YY_READ_BUF_SIZE;
X
X	else if ( num_to_read <= 0 )
X	    YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" );
X
X	/* read in more data */
X	YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]),
X		  yy_n_chars, num_to_read );
X	}
X
X    if ( yy_n_chars == 0 )
X	{
X	if ( number_to_move == 1 )
X	    {
X	    ret_val = EOB_ACT_END_OF_FILE;
X	    yy_current_buffer->yy_eof_status = EOF_DONE;
X	    }
X
X	else
X	    {
X	    ret_val = EOB_ACT_LAST_MATCH;
X	    yy_current_buffer->yy_eof_status = EOF_PENDING;
X	    }
X	}
X
X    else
X	ret_val = EOB_ACT_CONTINUE_SCAN;
X
X    yy_n_chars += number_to_move;
X    yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;
X    yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
X
X    /* yytext begins at the second character in yy_ch_buf; the first
X     * character is the one which preceded it before reading in the latest
X     * buffer; it needs to be kept around in case it's a newline, so
X     * yy_get_previous_state() will have with '^' rules active
X     */
X
X    yytext = &yy_current_buffer->yy_ch_buf[1];
X
X    return ( ret_val );
X    }
X
X
X/* yy_get_previous_state - get the state just before the EOB char was reached
X *
X * synopsis
X *     yy_state_type yy_get_previous_state();
X */
X
Xstatic yy_state_type yy_get_previous_state()
X
X    {
X    register yy_state_type yy_current_state;
X    register YY_CHAR *yy_cp;
X
X    yy_current_state = yy_start;
X
X    for ( yy_cp = yytext + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp )
X	{
X	register YY_CHAR yy_c = (*yy_cp ? yy_ec[*yy_cp] : 1);
X	if ( yy_accept[yy_current_state] )
X	    {
X	    yy_last_accepting_state = yy_current_state;
X	    yy_last_accepting_cpos = yy_cp;
X	    }
X	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
X	    {
X	    yy_current_state = yy_def[yy_current_state];
X	    if ( yy_current_state >= 144 )
X		yy_c = yy_meta[yy_c];
X	    }
X	yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
X	}
X
X    return ( yy_current_state );
X    }
X
X
X/* yy_try_NUL_trans - try to make a transition on the NUL character
X *
X * synopsis
X *     next_state = yy_try_NUL_trans( current_state );
X */
X
X#ifdef YY_USE_PROTOS
Xstatic yy_state_type yy_try_NUL_trans( register yy_state_type yy_current_state )
X#else
Xstatic yy_state_type yy_try_NUL_trans( yy_current_state )
Xregister yy_state_type yy_current_state;
X#endif
X
X    {
X    register int yy_is_jam;
X    register YY_CHAR *yy_cp = yy_c_buf_p;
X
X    register YY_CHAR yy_c = 1;
X    if ( yy_accept[yy_current_state] )
X	{
X	yy_last_accepting_state = yy_current_state;
X	yy_last_accepting_cpos = yy_cp;
X	}
X    while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
X	{
X	yy_current_state = yy_def[yy_current_state];
X	if ( yy_current_state >= 144 )
X	    yy_c = yy_meta[yy_c];
X	}
X    yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
X    yy_is_jam = (yy_current_state == 143);
X
X    return ( yy_is_jam ? 0 : yy_current_state );
X    }
X
X
X#ifdef YY_USE_PROTOS
Xstatic void yyunput( YY_CHAR c, register YY_CHAR *yy_bp )
X#else
Xstatic void yyunput( c, yy_bp )
XYY_CHAR c;
Xregister YY_CHAR *yy_bp;
X#endif
X
X    {
X    register YY_CHAR *yy_cp = yy_c_buf_p;
X
X    /* undo effects of setting up yytext */
X    *yy_cp = yy_hold_char;
X
X    if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
X	{ /* need to shift things up to make room */
X	register int number_to_move = yy_n_chars + 2; /* +2 for EOB chars */
X	register YY_CHAR *dest =
X	    &yy_current_buffer->yy_ch_buf[yy_current_buffer->yy_buf_size + 2];
X	register YY_CHAR *source =
X	    &yy_current_buffer->yy_ch_buf[number_to_move];
X
X	while ( source > yy_current_buffer->yy_ch_buf )
X	    *--dest = *--source;
X
X	yy_cp += dest - source;
X	yy_bp += dest - source;
X	yy_n_chars = yy_current_buffer->yy_buf_size;
X
X	if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
X	    YY_FATAL_ERROR( "flex scanner push-back overflow" );
X	}
X
X    if ( yy_cp > yy_bp && yy_cp[-1] == '\n' )
X	yy_cp[-2] = '\n';
X
X    *--yy_cp = c;
X
X    /* note: the formal parameter *must* be called "yy_bp" for this
X     *       macro to now work correctly
X     */
X    YY_DO_BEFORE_ACTION; /* set up yytext again */
X    }
X
X
X#ifdef __cplusplus
Xstatic int yyinput()
X#else
Xstatic int input()
X#endif
X
X    {
X    int c;
X    YY_CHAR *yy_cp = yy_c_buf_p;
X
X    *yy_cp = yy_hold_char;
X
X    if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
X	{
X	/* yy_c_buf_p now points to the character we want to return.
X	 * If this occurs *before* the EOB characters, then it's a
X	 * valid NUL; if not, then we've hit the end of the buffer.
X	 */
X	if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] )
X	    /* this was really a NUL */
X	    *yy_c_buf_p = '\0';
X
X	else
X	    { /* need more input */
X	    yytext = yy_c_buf_p;
X	    ++yy_c_buf_p;
X
X	    switch ( yy_get_next_buffer() )
X		{
X		case EOB_ACT_END_OF_FILE:
X		    {
X		    if ( yywrap() )
X			{
X			yy_c_buf_p = yytext + YY_MORE_ADJ;
X			return ( EOF );
X			}
X
X		    YY_NEW_FILE;
X
X#ifdef __cplusplus
X		    return ( yyinput() );
X#else
X		    return ( input() );
X#endif
X		    }
X		    break;
X
X		case EOB_ACT_CONTINUE_SCAN:
X		    yy_c_buf_p = yytext + YY_MORE_ADJ;
X		    break;
X
X		case EOB_ACT_LAST_MATCH:
X#ifdef __cplusplus
X		    YY_FATAL_ERROR( "unexpected last match in yyinput()" );
X#else
X		    YY_FATAL_ERROR( "unexpected last match in input()" );
X#endif
X		}
X	    }
X	}
X
X    c = *yy_c_buf_p;
X    yy_hold_char = *++yy_c_buf_p;
X
X    return ( c );
X    }
X
X
X#ifdef YY_USE_PROTOS
Xvoid yyrestart( FILE *input_file )
X#else
Xvoid yyrestart( input_file )
XFILE *input_file;
X#endif
X
X    {
X    yy_init_buffer( yy_current_buffer, input_file );
X    yy_load_buffer_state();
X    }
X
X
X#ifdef YY_USE_PROTOS
Xvoid yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
X#else
Xvoid yy_switch_to_buffer( new_buffer )
XYY_BUFFER_STATE new_buffer;
X#endif
X
X    {
X    if ( yy_current_buffer == new_buffer )
X	return;
X
X    if ( yy_current_buffer )
X	{
X	/* flush out information for old buffer */
X	*yy_c_buf_p = yy_hold_char;
X	yy_current_buffer->yy_buf_pos = yy_c_buf_p;
X	yy_current_buffer->yy_n_chars = yy_n_chars;
X	}
X
X    yy_current_buffer = new_buffer;
X    yy_load_buffer_state();
X
X    /* we don't actually know whether we did this switch during
X     * EOF (yywrap()) processing, but the only time this flag
X     * is looked at is after yywrap() is called, so it's safe
X     * to go ahead and always set it.
X     */
X    yy_did_buffer_switch_on_eof = 1;
X    }
X
X
X#ifdef YY_USE_PROTOS
Xvoid yy_load_buffer_state( void )
X#else
Xvoid yy_load_buffer_state()
X#endif
X
X    {
X    yy_n_chars = yy_current_buffer->yy_n_chars;
X    yytext = yy_c_buf_p = yy_current_buffer->yy_buf_pos;
X    yyin = yy_current_buffer->yy_input_file;
X    yy_hold_char = *yy_c_buf_p;
X    }
X
X
X#ifdef YY_USE_PROTOS
XYY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
X#else
XYY_BUFFER_STATE yy_create_buffer( file, size )
XFILE *file;
Xint size;
X#endif
X
X    {
X    YY_BUFFER_STATE b;
X
X    b = (YY_BUFFER_STATE) malloc( sizeof( struct yy_buffer_state ) );
X
X    if ( ! b )
X	YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
X
X    b->yy_buf_size = size;
X
X    /* yy_ch_buf has to be 2 characters longer than the size given because
X     * we need to put in 2 end-of-buffer characters.
X     */
X    b->yy_ch_buf = (YY_CHAR *) malloc( (unsigned) (b->yy_buf_size + 2) );
X
X    if ( ! b->yy_ch_buf )
X	YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
X
X    yy_init_buffer( b, file );
X
X    return ( b );
X    }
X
X
X#ifdef YY_USE_PROTOS
Xvoid yy_delete_buffer( YY_BUFFER_STATE b )
X#else
Xvoid yy_delete_buffer( b )
XYY_BUFFER_STATE b;
X#endif
X
X    {
X    if ( b == yy_current_buffer )
X	yy_current_buffer = (YY_BUFFER_STATE) 0;
X
X    free( (char *) b->yy_ch_buf );
X    free( (char *) b );
X    }
X
X
X#ifdef YY_USE_PROTOS
Xvoid yy_init_buffer( YY_BUFFER_STATE b, FILE *file )
X#else
Xvoid yy_init_buffer( b, file )
XYY_BUFFER_STATE b;
XFILE *file;
X#endif
X
X    {
X    b->yy_input_file = file;
X
X    /* we put in the '\n' and start reading from [1] so that an
X     * initial match-at-newline will be true.
X     */
X
X    b->yy_ch_buf[0] = '\n';
X    b->yy_n_chars = 1;
X
X    /* we always need two end-of-buffer characters.  The first causes
X     * a transition to the end-of-buffer state.  The second causes
X     * a jam in that state.
X     */
X    b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
X    b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR;
X
X    b->yy_buf_pos = &b->yy_ch_buf[1];
X
X    b->yy_eof_status = EOF_NOT_SEEN;
X    }
X# line 178 "scan.l"
X
X
X
X
X/* This is the way to get multiple files input into lex. */
X
Xint
Xyywrap()
X{
X  if (!open_new_file ()) return (1);	/* EOF on standard in. */
X  return (0);				/* We have more input. */
X}
/
echo x - scan.l
sed '/^X/s///' > scan.l << '/'
X%{
X/* scan.l: the (f)lex description file for the scanner. */
X
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X#include "bcdefs.h"
X#include "y.tab.h"
X#include "global.h"
X#include "proto.h"
X
X/* Using flex, we can ask for a smaller input buffer.  With lex, this
X   does nothing! */
X
X#ifdef SMALL_BUF
X#undef YY_READ_BUF_SIZE
X#define YY_READ_BUF_SIZE 512
X#endif
X
X/* We want to define our own yywrap. */
X#undef yywrap
X_PROTOTYPE(int yywrap, (void));
X
X/* MINIX returns from read with < 0 if SIGINT is  encountered.
X   In flex, we can redefine YY_INPUT to the following.  In lex, this
X   does nothing! */
X#include <errno.h>
X#undef  YY_INPUT
X#define YY_INPUT(buf,result,max_size) \
X	while ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \
X	    if (errno != EINTR) \
X		YY_FATAL_ERROR( "read() in flex scanner failed" );
X
X%}
XDIGIT [0-9A-F]
XLETTER [a-z]
X%%
Xdefine return(Define);
Xbreak  return(Break);
Xquit   return(Quit);
Xlength return(Length);
Xreturn return(Return);
Xfor    return(For);
Xif     return(If);
Xwhile  return(While);
Xsqrt   return(Sqrt);
Xscale  return(Scale);
Xibase  return(Ibase);
Xobase  return(Obase);
Xauto   return(Auto);
Xelse   return(Else);
Xread   return(Read);
Xhalt   return(Halt);
Xlast   return(Last);
Xwarranty return(Warranty);
Xcontinue return(Continue);
Xprint  return(Print);
Xlimits return(Limits);
X"+"|"-"|";"|"("|")"|"{"|"}"|"["|"]"|","|"^" { yylval.c_value = yytext[0]; 
X					      return((int)yytext[0]); }
X&& { return(AND); }
X\|\| { return(OR); }
X"!" { return(NOT); }
X"*"|"/"|"%" { yylval.c_value = yytext[0]; return(MUL_OP); }
X"="|\+=|-=|\*=|\/=|%=|\^=  { yylval.c_value = yytext[0]; return(ASSIGN_OP); }
X=\+|=-|=\*|=\/|=%|=\^  { 
X#ifdef OLD_EQ_OP
X			 char warn_save;
X			 warn_save = warn_not_std;
X			 warn_not_std = TRUE;
X			 warn ("Old fashioned =<op>");
X			 warn_not_std = warn_save;
X			 yylval.c_value = yytext[1];
X#else
X			 yylval.c_value = '=';
X			 yyless (1);
X#endif
X			 return(ASSIGN_OP);
X		       }
X==|\<=|\>=|\!=|"<"|">" { yylval.s_value = strcopyof(yytext); return(REL_OP); }
X\+\+|-- { yylval.c_value = yytext[0]; return(INCR_DECR); }
X"\n" { line_no++; return(NEWLINE); }
X\\\n {  line_no++;  /* ignore a "quoted" newline */ }
X[ \t]+  { /* ignore spaces and tabs */ }
X"/*"  {
X	int c;
X
X	for (;;)
X	  {
X	    while ( ((c=input()) != '*') && (c != EOF)) 
X	      /* eat it */
X	      if (c == '\n') line_no++;
X	    if (c == '*')
X 	      {
X		while ( (c=input()) == '*') /* eat it*/;
X		if (c == '/') break; /* at end of comment */
X		if (c == '\n') line_no++;
X	      }
X	    if (c == EOF)
X	      {
X		fprintf (stderr,"EOF encountered in a comment.\n");
X		break;
X	      }
X	  }
X      }
X[a-z][a-z0-9_]* { yylval.s_value = strcopyof(yytext); return(NAME); }
X\"[^\"]*\"  {
X 	      unsigned char *look;
X	      int count = 0;
X	      yylval.s_value = strcopyof(yytext);
X	      for (look = yytext; *look != 0; look++)
X		{
X		  if (*look == '\n') line_no++;
X		  if (*look == '"')  count++;
X		}
X	      if (count != 2) yyerror ("NUL character in string.");
X	      return(STRING);
X	    }
X{DIGIT}({DIGIT}|\\\n)*("."({DIGIT}|\\\n)*)?|"."(\\\n)*{DIGIT}({DIGIT}|\\\n)* {
X	      unsigned char *src, *dst;
X	      int len;
X	      /* remove a trailing decimal point. */
X	      len = strlen(yytext);
X	      if (yytext[len-1] == '.')
X	        yytext[len-1] = 0;
X	      /* remove leading zeros. */
X	      src = yytext;
X	      dst = yytext;
X	      while (*src == '0') src++;
X	      if (*src == 0) src--;
X	      /* Copy strings removing the newlines. */
X	      while (*src != 0)
X		{
X	          if (*src == '\\')
X		    {
X		      src++; src++;
X		      line_no++;
X		    }
X		  else
X		    *dst++ = *src++;
X	        }
X	      *dst = 0;
X	      yylval.s_value = strcopyof(yytext); 
X	      return(NUMBER);
X	    }
X.       {
X	  if (yytext[0] < ' ')
X	    yyerror ("illegal character: ^%c",yytext[0] + '@');
X	  else
X	    if (yytext[0] > '~')
X	      yyerror ("illegal character: \\%3d", (int) yytext[0]);
X	    else
X	      yyerror ("illegal character: %s",yytext);
X	}
X%%
X
X
X
X/* This is the way to get multiple files input into lex. */
X
Xint
Xyywrap()
X{
X  if (!open_new_file ()) return (1);	/* EOF on standard in. */
X  return (0);				/* We have more input. */
X}
/
echo x - storage.c
sed '/^X/s///' > storage.c << '/'
X/* storage.c:  Code and data storage manipulations.  This includes labels. */
X
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X#include "bcdefs.h"
X#include "global.h"
X#include "proto.h"
X
X
X/* Initialize the storage at the beginning of the run. */
X
Xvoid
Xinit_storage ()
X{
X
X  /* Functions: we start with none and ask for more. */
X  f_count = 0;
X  more_functions ();
X  f_names[0] = "(main)";
X
X  /* Variables. */
X  v_count = 0;
X  more_variables ();
X  
X  /* Arrays. */
X  a_count = 0;
X  more_arrays ();
X
X  /* Other things... */
X  ex_stack = NULL;
X  fn_stack = NULL;
X  i_base = 10;
X  o_base = 10;
X  scale  = 0;
X  c_code = FALSE;
X  init_numbers();
X}
X
X/* Three functions for increasing the number of functions, variables, or
X   arrays that are needed.  This adds another 32 of the requested object. */
X
Xvoid
Xmore_functions (VOID)
X{
X  int old_count;
X  int indx1, indx2;
X  bc_function *old_f;
X  bc_function *f;
X  char **old_names;
X
X  /* Save old information. */
X  old_count = f_count;
X  old_f = functions;
X  old_names = f_names;
X
X  /* Add a fixed amount and allocate new space. */
X  f_count += STORE_INCR;
X  functions = (bc_function *) bc_malloc (f_count*sizeof (bc_function));
X  f_names = (char **) bc_malloc (f_count*sizeof (char *));
X
X  /* Copy old ones. */
X  for (indx1 = 0; indx1 < old_count; indx1++)
X    {
X      functions[indx1] = old_f[indx1];
X      f_names[indx1] = old_names[indx1];
X    }
X
X  /* Initialize the new ones. */
X  for (; indx1 < f_count; indx1++)
X    {
X      f = &functions[indx1];
X      f->f_defined = FALSE;
X      for (indx2 = 0; indx2 < BC_MAX_SEGS; indx2++)
X	f->f_body [indx2] = NULL;
X      f->f_code_size = 0;
X      f->f_label = NULL;
X      f->f_autos = NULL;
X      f->f_params = NULL;
X    }
X
X  /* Free the old elements. */
X  if (old_count != 0)
X    {
X      free (old_f);
X      free (old_names);
X    }
X}
X
Xvoid
Xmore_variables ()
X{
X  int indx;
X  int old_count;
X  bc_var **old_var;
X  char **old_names;
X
X  /* Save the old values. */
X  old_count = v_count;
X  old_var = variables;
X  old_names = v_names;
X
X  /* Increment by a fixed amount and allocate. */
X  v_count += STORE_INCR;
X  variables = (bc_var **) bc_malloc (v_count*sizeof(bc_var *));
X  v_names = (char **) bc_malloc (v_count*sizeof(char *));
X
X  /* Copy the old variables. */
X  for (indx = 3; indx < old_count; indx++)
X    variables[indx] = old_var[indx];
X
X  /* Initialize the new elements. */
X  for (; indx < v_count; indx++)
X    variables[indx] = NULL;
X
X  /* Free the old elements. */
X  if (old_count != 0)
X    {
X      free (old_var);
X      free (old_names);
X    }
X}
X
Xvoid
Xmore_arrays ()
X{
X  int indx;
X  int old_count;
X  bc_var_array **old_ary;
X  char **old_names;
X
X  /* Save the old values. */
X  old_count = a_count;
X  old_ary = arrays;
X  old_names = a_names;
X
X  /* Increment by a fixed amount and allocate. */
X  a_count += STORE_INCR;
X  arrays = (bc_var_array **) bc_malloc (a_count*sizeof(bc_var_array *));
X  a_names = (char **) bc_malloc (a_count*sizeof(char *));
X
X  /* Copy the old arrays. */
X  for (indx = 1; indx < old_count; indx++)
X    arrays[indx] = old_ary[indx];
X
X
X  /* Initialize the new elements. */
X  for (; indx < v_count; indx++)
X    arrays[indx] = NULL;
X
X  /* Free the old elements. */
X  if (old_count != 0)
X    {
X      free (old_ary);
X      free (old_names);
X    }
X}
X
X
X/* clear_func clears out function FUNC and makes it ready to redefine. */
X
Xvoid
Xclear_func (func)
X     char func;
X{
X  bc_function *f;
X  int indx;
X  bc_label_group *lg;
X
X  /* Set the pointer to the function. */
X  f = &functions[func];
X  f->f_defined = FALSE;
X
X  /* Clear the code segments. */
X  for (indx = 0; indx < BC_MAX_SEGS; indx++)
X    {
X      if (f->f_body[indx] != NULL)
X	{
X	  free (f->f_body[indx]);
X	  f->f_body[indx] = NULL;
X	}
X    }
X
X  f->f_code_size = 0;
X  if (f->f_autos != NULL)
X    {
X      free_args (f->f_autos);
X      f->f_autos = NULL;
X    }
X  if (f->f_params != NULL)
X    {
X      free_args (f->f_params);
X      f->f_params = NULL;
X    }
X  while (f->f_label != NULL)
X    {
X      lg = f->f_label->l_next;
X      free (f->f_label);
X      f->f_label = lg;
X    }
X}
X
X
X/*  Pop the function execution stack and return the top. */
X
Xint
Xfpop()
X{
X  fstack_rec *temp;
X  int retval;
X  
X  if (fn_stack != NULL)
X    {
X      temp = fn_stack;
X      fn_stack = temp->s_next;
X      retval = temp->s_val;
X      free (temp);
X    }
X  return (retval);
X}
X
X
X/* Push VAL on to the function stack. */
X
Xvoid
Xfpush (val)
X     int val;
X{
X  fstack_rec *temp;
X  
X  temp = (fstack_rec *) bc_malloc (sizeof (fstack_rec));
X  temp->s_next = fn_stack;
X  temp->s_val = val;
X  fn_stack = temp;
X}
X
X
X/* Pop and discard the top element of the regular execution stack. */
X
Xvoid
Xpop ()
X{
X  estack_rec *temp;
X  
X  if (ex_stack != NULL)
X    {
X      temp = ex_stack;
X      ex_stack = temp->s_next;
X      free_num (&temp->s_num);
X      free (temp);
X    }
X}
X
X
X/* Push a copy of NUM on to the regular execution stack. */
X
Xvoid
Xpush_copy (num)
X     bc_num num;
X{
X  estack_rec *temp;
X
X  temp = (estack_rec *) bc_malloc (sizeof (estack_rec));
X  temp->s_num = copy_num (num);
X  temp->s_next = ex_stack;
X  ex_stack = temp;
X}
X
X
X/* Push NUM on to the regular execution stack.  Do NOT push a copy. */
X
Xvoid
Xpush_num (num)
X     bc_num num;
X{
X  estack_rec *temp;
X
X  temp = (estack_rec *) bc_malloc (sizeof (estack_rec));
X  temp->s_num = num;
X  temp->s_next = ex_stack;
X  ex_stack = temp;
X}
X
X
X/* Make sure the ex_stack has at least DEPTH elements on it.
X   Return TRUE if it has at least DEPTH elements, otherwise
X   return FALSE. */
X
Xchar
Xcheck_stack (depth)
X     int depth;
X{
X  estack_rec *temp;
X
X  temp = ex_stack;
X  while ((temp != NULL) && (depth > 0))
X    {
X      temp = temp->s_next;
X      depth--;
X    }
X  if (depth > 0)
X    {
X      rt_error ("Stack error.");
X      return FALSE;
X    }
X  return TRUE;
X}
X
X
X/* The following routines manipulate simple variables and
X   array variables. */
X
X/* get_var returns a pointer to the variable VAR_NAME.  If one does not
X   exist, one is created. */
X
Xbc_var *
Xget_var (var_name)
X     int var_name;
X{
X  bc_var *var_ptr;
X
X  var_ptr = variables[var_name];
X  if (var_ptr == NULL)
X    {
X      var_ptr = variables[var_name] = (bc_var *) bc_malloc (sizeof (bc_var));
X      init_num (&var_ptr->v_value);
X    }
X  return var_ptr;
X}
X
X
X/* get_array_num returns the address of the bc_num in the array
X   structure.  If more structure is requried to get to the index,
X   this routine does the work to create that structure. VAR_INDEX
X   is a zero based index into the arrays storage array. INDEX is
X   the index into the bc array. */
X
Xbc_num *
Xget_array_num (var_index, index)
X     int var_index;
X     long  index;
X{
X  bc_var_array *ary_ptr;
X  bc_array *a_var;
X  bc_array_node *temp;
X  int log, ix, ix1;
X  int sub [NODE_DEPTH];
X
X  /* Get the array entry. */
X  ary_ptr = arrays[var_index];
X  if (ary_ptr == NULL)
X    {
X      ary_ptr = arrays[var_index] =
X	(bc_var_array *) bc_malloc (sizeof (bc_var_array));
X      ary_ptr->a_value = NULL;
X      ary_ptr->a_next = NULL;
X      ary_ptr->a_param = FALSE;
X    }
X
X  a_var = ary_ptr->a_value;
X  if (a_var == NULL) {
X    a_var = ary_ptr->a_value = (bc_array *) bc_malloc (sizeof (bc_array));
X    a_var->a_tree = NULL;
X    a_var->a_depth = 0;
X  }
X
X  /* Get the index variable. */
X  sub[0] = index & NODE_MASK;
X  ix = index >> NODE_SHIFT;
X  log = 1;
X  while (ix > 0 || log < a_var->a_depth)
X    {
X      sub[log] = ix & NODE_MASK;
X      ix >>= NODE_SHIFT;
X      log++;
X    }
X  
X  /* Build any tree that is necessary. */
X  while (log > a_var->a_depth)
X    {
X      temp = (bc_array_node *) bc_malloc (sizeof(bc_array_node));
X      if (a_var->a_depth != 0)
X	{
X	  temp->n_items.n_down[0] = a_var->a_tree;
X	  for (ix=1; ix < NODE_SIZE; ix++)
X	    temp->n_items.n_down[ix] = NULL;
X	}
X      else
X	{
X	  for (ix=0; ix < NODE_SIZE; ix++)
X	    temp->n_items.n_num[ix] = copy_num(_zero_);
X	}
X      a_var->a_tree = temp;
X      a_var->a_depth++;
X    }
X  
X  /* Find the indexed variable. */
X  temp = a_var->a_tree;
X  while ( log-- > 1)
X    {
X      ix1 = sub[log];
X      if (temp->n_items.n_down[ix1] == NULL)
X	{
X	  temp->n_items.n_down[ix1] =
X	    (bc_array_node *) bc_malloc (sizeof(bc_array_node));
X	  temp = temp->n_items.n_down[ix1];
X	  if (log > 1)
X	    for (ix=0; ix < NODE_SIZE; ix++)
X	      temp->n_items.n_down[ix] = NULL;
X	  else
X	    for (ix=0; ix < NODE_SIZE; ix++)
X	      temp->n_items.n_num[ix] = copy_num(_zero_);
X	}
X      else
X	temp = temp->n_items.n_down[ix1];
X    }
X  
X  /* Return the address of the indexed variable. */
X  return &(temp->n_items.n_num[sub[0]]);
X}
X
X
X/* Store the top of the execution stack into VAR_NAME.  
X   This includes the special variables ibase, obase, and scale. */
X
Xvoid
Xstore_var (var_name)
X     int var_name;
X{
X  bc_var *var_ptr;
X  long temp;
X  char toobig;
X
X  if (var_name > 2)
X    {
X      /* It is a simple variable. */
X      var_ptr = get_var (var_name);
X      if (var_ptr != NULL)
X	{
X	  free_num(&var_ptr->v_value);
X	  var_ptr->v_value = copy_num (ex_stack->s_num);
X	}
X    }
X  else
X    {
X      /* It is a special variable... */
X      toobig = FALSE;
X      if (is_neg (ex_stack->s_num))
X	{
X	  switch (var_name)
X	    {
X	    case 0:
X	      rt_warn ("negative ibase, set to 2");
X	      temp = 2;
X	      break;
X	    case 1:
X	      rt_warn ("negative obase, set to 2");
X	      temp = 2;
X	      break;
X	    case 2:
X	      rt_warn ("negative scale, set to 0");
X	      temp = 0;
X	      break;
X	    }
X	}
X      else
X	{
X	  temp = num2long (ex_stack->s_num);
X	  if (!is_zero (ex_stack->s_num) && temp == 0)
X	    toobig = TRUE;
X	}
X      switch (var_name)
X	{
X	case 0:
X	  if (temp < 2 && !toobig)
X	    {
X	      i_base = 2;
X	      rt_warn ("ibase too small, set to 2");
X	    }
X	  else
X	    if (temp > 16 || toobig)
X	      {
X		i_base = 16;
X		rt_warn ("ibase too large, set to 16");
X	      }
X	    else
X	      i_base = (int) temp;
X	  break;
X
X	case 1:
X	  if (temp < 2 && !toobig)
X	    {
X	      o_base = 2;
X	      rt_warn ("obase too small, set to 2");
X	    }
X	  else
X	    if (temp > BC_BASE_MAX || toobig)
X	      {
X		o_base = BC_BASE_MAX;
X		rt_warn ("obase too large, set to %d", BC_BASE_MAX);
X	      }
X	    else
X	      o_base = (int) temp;
X	  break;
X
X	case 2:
X	  /*  WARNING:  The following if statement may generate a compiler
X	      warning if INT_MAX == LONG_MAX.  This is NOT a problem. */
X	  if (temp > BC_SCALE_MAX || toobig )
X	    {
X	      scale = BC_SCALE_MAX;
X	      rt_warn ("scale too large, set to %d", BC_SCALE_MAX);
X	    }
X	  else
X	    scale = (int) temp;
X	}
X    }
X}
X
X
X/* Store the top of the execution stack into array VAR_NAME. 
X   VAR_NAME is the name of an array, and the next to the top
X   of stack for the index into the array. */
X
Xvoid
Xstore_array (var_name)
X     int var_name;
X{
X  bc_num *num_ptr;
X  long index;
X
X  if (!check_stack(2)) return;
X  index = num2long (ex_stack->s_next->s_num);
X  if (index < 0 || index > BC_DIM_MAX ||
X      (index == 0 && !is_zero(ex_stack->s_next->s_num))) 
X    rt_error ("Array %s subscript out of bounds.", a_names[var_name]);
X  else
X    {
X      num_ptr = get_array_num (var_name, index);
X      if (num_ptr != NULL)
X	{
X	  free_num (num_ptr);
X	  *num_ptr = copy_num (ex_stack->s_num);
X	  free_num (&ex_stack->s_next->s_num);
X	  ex_stack->s_next->s_num = ex_stack->s_num;
X	  init_num (&ex_stack->s_num);
X	  pop();
X	}
X    }
X}
X
X
X/*  Load a copy of VAR_NAME on to the execution stack.  This includes
X    the special variables ibase, obase and scale.  */
X
Xvoid
Xload_var (var_name)
X     int var_name;
X{
X  bc_var *var_ptr;
X
X  switch (var_name)
X    {
X
X    case 0:
X      /* Special variable ibase. */
X      push_copy (_zero_);
X      int2num (&ex_stack->s_num, i_base);
X      break;
X
X    case 1:
X      /* Special variable obase. */
X      push_copy (_zero_);
X      int2num (&ex_stack->s_num, o_base);
X      break;
X
X    case 2:
X      /* Special variable scale. */
X      push_copy (_zero_);
X      int2num (&ex_stack->s_num, scale);
X      break;
X
X    default:
X      /* It is a simple variable. */
X      var_ptr = variables[var_name];
X      if (var_ptr != NULL)
X	push_copy (var_ptr->v_value);
X      else
X	push_copy (_zero_);
X    }
X}
X
X
X/*  Load a copy of VAR_NAME on to the execution stack.  This includes
X    the special variables ibase, obase and scale.  */
X
Xvoid
Xload_array (var_name)
X     int var_name;
X{
X  bc_num *num_ptr;
X  long   index;
X
X  if (!check_stack(1)) return;
X  index = num2long (ex_stack->s_num);
X  if (index < 0 || index > BC_DIM_MAX ||
X     (index == 0 && !is_zero(ex_stack->s_num))) 
X    rt_error ("Array %s subscript out of bounds.", a_names[var_name]);
X  else
X    {
X      num_ptr = get_array_num (var_name, index);
X      if (num_ptr != NULL)
X	{
X	  pop();
X	  push_copy (*num_ptr);
X	}
X    }
X}
X
X
X/* Decrement VAR_NAME by one.  This includes the special variables
X   ibase, obase, and scale. */
X
Xvoid
Xdecr_var (var_name)
X     int var_name;
X{
X  bc_var *var_ptr;
X
X  switch (var_name)
X    {
X
X    case 0: /* ibase */
X      if (i_base > 2)
X	i_base--;
X      else
X	rt_warn ("ibase too small in --");
X      break;
X      
X    case 1: /* obase */
X      if (o_base > 2)
X	o_base--;
X      else
X	rt_warn ("obase too small in --");
X      break;
X
X    case 2: /* scale */
X      if (scale > 0)
X	scale--;
X      else
X	rt_warn ("scale can not be negative in -- ");
X      break;
X
X    default: /* It is a simple variable. */
X      var_ptr = get_var (var_name);
X      if (var_ptr != NULL)
X	bc_sub (var_ptr->v_value,_one_,&var_ptr->v_value);
X    }
X}
X
X
X/* Decrement VAR_NAME by one.  VAR_NAME is an array, and the top of
X   the execution stack is the index and it is popped off the stack. */
X
Xvoid
Xdecr_array (var_name)
X     char var_name;
X{
X  bc_num *num_ptr;
X  long   index;
X
X  /* It is an array variable. */
X  if (!check_stack (1)) return;
X  index = num2long (ex_stack->s_num);
X  if (index < 0 || index > BC_DIM_MAX ||
X     (index == 0 && !is_zero (ex_stack->s_num))) 
X    rt_error ("Array %s subscript out of bounds.", a_names[var_name]);
X  else
X    {
X      num_ptr = get_array_num (var_name, index);
X      if (num_ptr != NULL)
X	{
X	  pop ();
X	  bc_sub (*num_ptr, _one_, num_ptr);
X	}
X    }
X}
X
X
X/* Increment VAR_NAME by one.  This includes the special variables
X   ibase, obase, and scale. */
X
Xvoid
Xincr_var (var_name)
X     int var_name;
X{
X  bc_var *var_ptr;
X
X  switch (var_name)
X    {
X
X    case 0: /* ibase */
X      if (i_base < 16)
X	i_base++;
X      else
X	rt_warn ("ibase too big in ++");
X      break;
X
X    case 1: /* obase */
X      if (o_base < BC_BASE_MAX)
X	o_base++;
X      else
X	rt_warn ("obase too big in ++");
X      break;
X
X    case 2:
X      if (scale < BC_SCALE_MAX)
X	scale++;
X      else
X	rt_warn ("Scale too big in ++");
X      break;
X
X    default:  /* It is a simple variable. */
X      var_ptr = get_var (var_name);
X      if (var_ptr != NULL)
X	bc_add (var_ptr->v_value, _one_, &var_ptr->v_value);
X
X    }
X}
X
X
X/* Increment VAR_NAME by one.  VAR_NAME is an array and top of
X   execution stack is the index and is popped off the stack. */
X
Xvoid
Xincr_array (var_name)
X     int var_name;
X{
X  bc_num *num_ptr;
X  long   index;
X
X  if (!check_stack (1)) return;
X  index = num2long (ex_stack->s_num);
X  if (index < 0 || index > BC_DIM_MAX ||
X      (index == 0 && !is_zero (ex_stack->s_num))) 
X    rt_error ("Array %s subscript out of bounds.", a_names[var_name]);
X  else
X    {
X      num_ptr = get_array_num (var_name, index);
X      if (num_ptr != NULL)
X	{
X	  pop ();
X	  bc_add (*num_ptr, _one_, num_ptr);
X	}
X    }
X}
X
X
X/* Routines for processing autos variables and parameters. */
X
X/* NAME is an auto variable that needs to be pushed on its stack. */
X
Xvoid
Xauto_var (name)
X     int name;
X{
X  bc_var *v_temp;
X  bc_var_array *a_temp;
X  int ix;
X
X  if (name > 0)
X    {
X      /* A simple variable. */
X      ix = name;
X      v_temp = (bc_var *) bc_malloc (sizeof (bc_var));
X      v_temp->v_next = variables[ix];
X      init_num (&v_temp->v_value);
X      variables[ix] = v_temp;
X    }
X  else
X    {
X      /* An array variable. */
X      ix = -name;
X      a_temp = (bc_var_array *) bc_malloc (sizeof (bc_var_array));
X      a_temp->a_next = arrays[ix];
X      a_temp->a_value = NULL;
X      a_temp->a_param = FALSE;
X      arrays[ix] = a_temp;
X    } 
X}
X
X
X/* Free_a_tree frees everything associated with an array variable tree.
X   This is used when popping an array variable off its auto stack.  */
X
Xvoid
Xfree_a_tree ( root, depth )
X     bc_array_node *root;
X     int depth;
X{
X  int ix;
X
X  if (root != NULL)
X    {
X      if (depth > 1)
X	for (ix = 0; ix < NODE_SIZE; ix++)
X	  free_a_tree (root->n_items.n_down[ix], depth-1);
X      else
X	for (ix = 0; ix < NODE_SIZE; ix++)
X	  free_num ( &(root->n_items.n_num[ix]));
X      free (root);
X    }
X}
X
X
X/* LIST is an NULL terminated list of varible names that need to be
X   popped off their auto stacks. */
X
Xvoid
Xpop_vars (list)
X     arg_list *list;
X{
X  bc_var *v_temp;
X  bc_var_array *a_temp;
X  int    ix;
X
X  while (list != NULL)
X    {
X      ix = list->av_name;
X      if (ix > 0)
X	{
X	  /* A simple variable. */
X	  v_temp = variables[ix];
X	  if (v_temp != NULL)
X	    {
X	      variables[ix] = v_temp->v_next;
X	      free_num (&v_temp->v_value);
X	      free (v_temp);
X	    }
X	}
X      else
X	{
X	  /* An array variable. */
X	  ix = -ix;
X	  a_temp = arrays[ix];
X	  if (a_temp != NULL)
X	    {
X	      arrays[ix] = a_temp->a_next;
X	      if (!a_temp->a_param && a_temp->a_value != NULL)
X		{
X		  free_a_tree (a_temp->a_value->a_tree,
X			       a_temp->a_value->a_depth);
X		  free (a_temp->a_value);
X		}
X	      free (a_temp);
X	    }
X	} 
X      list = list->next;
X    }
X}
X
X
X/* A call is being made to FUNC.  The call types are at PC.  Process
X   the parameters by doing an auto on the parameter variable and then
X   store the value at the new variable or put a pointer the the array
X   variable. */
X
Xvoid
Xprocess_params (pc, func)
X     program_counter *pc;
X     int func;
X{
X  char ch;
X  arg_list *params;
X  char warned = FALSE;
X  int ix, ix1;
X  bc_var *v_temp;
X  bc_var_array *a_src, *a_dest;
X  bc_num *n_temp;
X  
X  /* Get the parameter names from the function. */
X  params = functions[func].f_params;
X
X  while ((ch = byte(pc)) != ':')
X    {
X      if (params != NULL)
X	{
X	  if ((ch == '0') && params->av_name > 0)
X	    {
X	      /* A simple variable. */
X	      ix = params->av_name;
X	      v_temp = (bc_var *) bc_malloc (sizeof(bc_var));
X	      v_temp->v_next = variables[ix];
X	      v_temp->v_value = ex_stack->s_num;
X	      init_num (&ex_stack->s_num);
X	      variables[ix] = v_temp;
X	    }
X	  else
X	    if ((ch == '1') && (params->av_name < 0))
X	      {
X		/* The variables is an array variable. */
X	
X		/* Compute source index and make sure some structure exists. */
X		ix = (int) num2long (ex_stack->s_num);
X		n_temp = get_array_num (ix, 0);    
X	
X		/* Push a new array and Compute Destination index */
X		auto_var (params->av_name);  
X		ix1 = -params->av_name;
X
X		/* Set up the correct pointers in the structure. */
X		if (ix == ix1) 
X		  a_src = arrays[ix]->a_next;
X		else
X		  a_src = arrays[ix];
X		a_dest = arrays[ix1];
X		a_dest->a_param = TRUE;
X		a_dest->a_value = a_src->a_value;
X	      }
X	    else
X	      {
X		if (params->av_name < 0)
X		  rt_error ("Parameter type mismatch parameter %s.",
X			    a_names[-params->av_name]);
X		else
X		  rt_error ("Parameter type mismatch, parameter %s.",
X			    v_names[params->av_name]);
X		params++;
X	      }
X	  pop ();
X	}
X      else
X	{
X	  if (!warned)
X	    {
X	      rt_error ("Parameter number mismatch");
X	      warned = TRUE;
X	    }
X	}
X      params = params->next;
X    }
X  if (params != NULL) 
X    rt_error ("Parameter number mismatch");
X}
/
echo x - util.c
sed '/^X/s///' > util.c << '/'
X/* util.c: Utility routines for bc. */
X
X/*  This file is part of bc written for MINIX.
X    Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License , or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; see the file COPYING.  If not, write to
X    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
X    You may contact the author by:
X       e-mail:  phil@cs.wwu.edu
X      us-mail:  Philip A. Nelson
X                Computer Science Department, 9062
X                Western Washington University
X                Bellingham, WA 98226-9062
X       
X*************************************************************************/
X
X
X#include "bcdefs.h"
X#ifndef VARARGS
X#include <stdarg.h>
X#else
X#include <varargs.h>
X#endif
X#include "global.h"
X#include "proto.h"
X
X
X/* strcopyof mallocs new memory and copies a string to to the new
X   memory. */
X
Xchar *
Xstrcopyof (str)
X     char *str;
X{
X  char *temp;
X
X  temp = (char *) bc_malloc (strlen (str)+1);
X  return (strcpy (temp,str));
X}
X
X
X/* nextarg adds another value to the list of arguments. */
X
Xarg_list *
Xnextarg (args, val)
X     arg_list *args;
X     char val;
X{ arg_list *temp;
X
X  temp = (arg_list *) bc_malloc (sizeof (arg_list));
X  temp->av_name = val;
X  temp->next = args;
X 
X  return (temp);
X}
X
X
X/* For generate, we must produce a string in the form
X    "val,val,...,val".  We also need a couple of static variables
X   for retaining old generated strings.  It also uses a recursive
X   function that builds the string. */
X
Xstatic char *arglist1 = NULL, *arglist2 = NULL;
X
X
X/* make_arg_str does the actual construction of the argument string.
X   ARGS is the pointer to the list and LEN is the maximum number of
X   characters needed.  1 char is the minimum needed. COMMAS tells
X   if each number should be seperated by commas.*/
X
X_PROTOTYPE (static char *make_arg_str, (arg_list *args, int len, int commas));
X
Xstatic char *
Xmake_arg_str (args, len, commas)
X      arg_list *args;
X      int len;
X      int commas;
X{
X  char *temp;
X  char sval[20];
X
X  /* Recursive call. */
X  if (args != NULL)
X    temp = make_arg_str (args->next, len+11, commas);
X  else
X    {
X      temp = (char *) bc_malloc (len);
X      *temp = 0;
X      return temp;
X    }
X
X  /* Add the current number to the end of the string. */
X  if (len != 1 && commas) 
X    sprintf (sval, "%d,", args->av_name);
X  else
X    sprintf (sval, "%d", args->av_name);
X  temp = strcat (temp, sval);
X  return (temp);
X}
X
Xchar *
Xarg_str (args, commas)
X     arg_list *args;
X     int commas;
X{
X  if (arglist2 != NULL) 
X    free (arglist2);
X  arglist2 = arglist1;
X  arglist1 = make_arg_str (args, 1, commas);
X  return (arglist1);
X}
X
X
X/* free_args frees an argument list ARGS. */
X
Xvoid
Xfree_args (args)
X      arg_list *args;
X{ 
X  arg_list *temp;
X 
X  temp = args;
X  while (temp != NULL)
X    {
X      args = args->next;
X      free (temp);
X      temp = args;
X    }
X}
X
X
X/* Check for valid parameter (PARAMS) and auto (AUTOS) lists.
X   There must be no duplicates any where.  Also, this is where
X   warnings are generated for array parameters. */
X
Xvoid
Xcheck_params ( params, autos )
X     arg_list *params, *autos;
X{
X  arg_list *tmp1, *tmp2;
X
X  /* Check for duplicate parameters. */
X  if (params != NULL)
X    {
X      tmp1 = params;
X      while (tmp1 != NULL)
X	{
X	  tmp2 = tmp1->next;
X	  while (tmp2 != NULL)
X	    {
X	      if (tmp2->av_name == tmp1->av_name) 
X		yyerror ("duplicate parameter names");
X	      tmp2 = tmp2->next;
X	    }
X	  if (tmp1->av_name < 0)
X	    warn ("Array parameter");
X	  tmp1 = tmp1->next;
X	}
X    }
X
X  /* Check for duplicate autos. */
X  if (autos != NULL)
X    {
X      tmp1 = autos;
X      while (tmp1 != NULL)
X	{
X	  tmp2 = tmp1->next;
X	  while (tmp2 != NULL)
X	    {
X	      if (tmp2->av_name == tmp1->av_name) 
X		yyerror ("duplicate auto variable names");
X	      tmp2 = tmp2->next;
X	    }
X	  tmp1 = tmp1->next;
X	}
X    }
X
X  /* Check for duplicate between parameters and autos. */
X  if ((params != NULL) && (autos != NULL))
X    {
X      tmp1 = params;
X      while (tmp1 != NULL)
X	{
X	  tmp2 = autos;
X	  while (tmp2 != NULL)
X	    {
X	      if (tmp2->av_name == tmp1->av_name) 
X		yyerror ("variable in both parameter and auto lists");
X	      tmp2 = tmp2->next;
X	    }
X	  tmp1 = tmp1->next;
X	}
X    }
X}
X
X
X/* Initialize the code generator the parser. */
X
Xvoid
Xinit_gen ()
X{
X  /* Get things ready. */
X  break_label = 0;
X  continue_label = 0;
X  next_label  = 1;
X  out_count = 2;
X  if (compile_only) 
X    printf ("@i");
X  else
X    init_load ();
X  had_error = FALSE;
X  did_gen = FALSE;
X}
X
X
X/* generate code STR for the machine. */
X
Xvoid
Xgenerate (str)
X      char *str;
X{
X  did_gen = TRUE;
X  if (compile_only)
X    {
X      printf ("%s",str);
X      out_count += strlen(str);
X      if (out_count > 60)
X	{
X	  printf ("\n");
X	  out_count = 0;
X	}
X    }
X  else
X    load_code (str);
X}
X
X
X/* Execute the current code as loaded. */
X
Xvoid
Xrun_code()
X{
X  /* If no compile errors run the current code. */
X  if (!had_error && did_gen)
X    {
X      if (compile_only)
X	{
X	  printf ("@r\n"); 
X	  out_count = 0;
X	}
X      else
X	execute ();
X    }
X
X  /* Reinitialize the code generation and machine. */
X  if (did_gen)
X    init_gen();
X  else
X    had_error = FALSE;
X}
X
X
X/* Output routines: Write a character CH to the standard output.
X   It keeps track of the number of characters output and may
X   break the output with a "\<cr>". */
X
Xvoid
Xout_char (ch)
X     char ch;
X{
X  if (ch == '\n')
X    {
X      out_col = 0;
X      putchar ('\n');
X    }
X  else
X    {
X      out_col++;
X      if (out_col == 70)
X	{
X	  putchar ('\\');
X	  putchar ('\n');
X	  out_col = 1;
X	}
X      putchar (ch);
X    }
X}
X
X
X/* The following are "Symbol Table" routines for the parser. */
X
X/*  find_id returns a pointer to node in TREE that has the correct
X    ID.  If there is no node in TREE with ID, NULL is returned. */
X
Xid_rec *
Xfind_id (tree, id)
X     id_rec *tree;
X     char   *id;
X{
X  int cmp_result;
X  
X  /* Check for an empty tree. */
X  if (tree == NULL)
X    return NULL;
X
X  /* Recursively search the tree. */
X  cmp_result = strcmp (id, tree->id);
X  if (cmp_result == 0)
X    return tree;  /* This is the item. */
X  else if (cmp_result < 0)
X    return find_id (tree->left, id);
X  else
X    return find_id (tree->right, id);  
X}
X
X
X/* insert_id_rec inserts a NEW_ID rec into the tree whose ROOT is
X   provided.  insert_id_rec returns TRUE if the tree height from
X   ROOT down is increased otherwise it returns FALSE.  This is a
X   recursive balanced binary tree insertion algorithm. */
X
Xint insert_id_rec (root, new_id)
X     id_rec **root;
X     id_rec *new_id;
X{
X  id_rec *A, *B;
X
X  /* If root is NULL, this where it is to be inserted. */
X  if (*root == NULL)
X    {
X      *root = new_id;
X      new_id->left = NULL;
X      new_id->right = NULL;
X      new_id->balance = 0;
X      return (TRUE);
X    }
X
X  /* We need to search for a leaf. */
X  if (strcmp (new_id->id, (*root)->id) < 0)
X    {
X      /* Insert it on the left. */
X      if (insert_id_rec (&((*root)->left), new_id))
X	{
X	  /* The height increased. */
X	  (*root)->balance --;
X	  
X      switch ((*root)->balance)
X	{
X	case  0:  /* no height increase. */
X	  return (FALSE);
X	case -1:  /* height increase. */
X	  return (FALSE);
X	case -2:  /* we need to do a rebalancing act. */
X	  A = *root;
X	  B = (*root)->left;
X	  if (B->balance <= 0)
X	    {
X	      /* Single Rotate. */
X	      A->left = B->right;
X	      B->right = A;
X	      *root = B;
X	      A->balance = 0;
X	      B->balance = 0;
X	    }
X	  else
X	    {
X	      /* Double Rotate. */
X	      *root = B->right;
X	      B->right = (*root)->left;
X	      A->left = (*root)->right;
X	      (*root)->left = B;
X	      (*root)->right = A;
X	      switch ((*root)->balance)
X		{
X		case -1:
X		  A->balance = 1;
X		  B->balance = 0;
X		  break;
X		case  0:
X		  A->balance = 0;
X		  B->balance = 0;
X		  break;
X		case  1:
X		  A->balance = 0;
X		  B->balance = -1;
X		  break;
X		}
X	      (*root)->balance = 0;
X	    }
X	}     
X	} 
X    }
X  else
X    {
X      /* Insert it on the right. */
X      if (insert_id_rec (&((*root)->right), new_id))
X	{
X	  /* The height increased. */
X	  (*root)->balance ++;
X	  switch ((*root)->balance)
X	    {
X	    case 0:  /* no height increase. */
X	      return (FALSE);
X	    case 1:  /* height increase. */
X	      return (FALSE);
X	    case 2:  /* we need to do a rebalancing act. */
X	      A = *root;
X	      B = (*root)->right;
X	      if (B->balance >= 0)
X		{
X		  /* Single Rotate. */
X		  A->right = B->left;
X		  B->left = A;
X		  *root = B;
X		  A->balance = 0;
X		  B->balance = 0;
X		}
X	      else
X		{
X		  /* Double Rotate. */
X		  *root = B->left;
X		  B->left = (*root)->right;
X		  A->right = (*root)->left;
X		  (*root)->left = A;
X		  (*root)->right = B;
X		  switch ((*root)->balance)
X		    {
X		    case -1:
X		      A->balance = 0;
X		      B->balance = 1;
X		      break;
X		    case  0:
X		      A->balance = 0;
X		      B->balance = 0;
X		      break;
X		    case  1:
X		      A->balance = -1;
X		      B->balance = 0;
X		      break;
X		    }
X		  (*root)->balance = 0;
X		}
X	    }     
X	} 
X    }
X  
X  /* If we fall through to here, the tree did not grow in height. */
X  return (FALSE);
X}
X
X
X/* Initialize variables for the symbol table tree. */
X
Xvoid
Xinit_tree()
X{
X  name_tree  = NULL;
X  next_array = 1;
X  next_func  = 1;
X  next_var   = 4;  /* 0 => ibase, 1 => obase, 2 => scale, 3 => last. */
X}
X
X
X/* Lookup routines for symbol table names. */
X
Xint
Xlookup (name, namekind)
X     char *name;
X     int  namekind;
X{
X  id_rec *id;
X
X  /* Warn about non-standard name. */
X  if (strlen(name) != 1)
X    warn ("multiple letter name - %s", name);
X
X  /* Look for the id. */
X  id = find_id (name_tree, name);
X  if (id == NULL)
X    {
X      /* We need to make a new item. */
X      id = (id_rec *) bc_malloc (sizeof (id_rec));
X      id->id = strcopyof (name);
X      id->a_name = 0;
X      id->f_name = 0;
X      id->v_name = 0;
X      insert_id_rec (&name_tree, id);
X    }
X
X  /* Return the correct value. */
X  switch (namekind)
X    {
X      
X    case ARRAY:
X      /* ARRAY variable numbers are returned as negative numbers. */
X      if (id->a_name != 0)
X	{
X	  free (name);
X	  return (-id->a_name);
X	}
X      id->a_name = next_array++;
X      a_names[id->a_name] = name;
X      if (id->a_name < MAX_STORE)
X	{
X	  if (id->a_name >= a_count)
X	    more_arrays ();
X	  return (-id->a_name);
X	}
X      yyerror ("Too many array variables");
X      exit (1);
X
X    case FUNCT:
X      if (id->f_name != 0)
X	{
X	  free(name);
X	  return (id->f_name);
X	}
X      id->f_name = next_func++;
X      f_names[id->f_name] = name;
X      if (id->f_name < MAX_STORE)
X	{
X	  if (id->f_name >= f_count)
X	    more_functions ();
X	  return (id->f_name);
X	}
X      yyerror ("Too many functions");
X      exit (1);
X
X    case SIMPLE:
X      if (id->v_name != 0)
X	{
X	  free(name);
X	  return (id->v_name);
X	}
X      id->v_name = next_var++;
X      v_names[id->v_name - 1] = name;
X      if (id->v_name <= MAX_STORE)
X	{
X	  if (id->v_name >= v_count)
X	    more_variables ();
X	  return (id->v_name);
X	}
X      yyerror ("Too many variables");
X      exit (1);
X    }
X}
X
X
X/* Print the welcome banner. */
X
Xvoid 
Xwelcome()
X{
X  printf ("This is free software with ABSOLUTELY NO WARRANTY.\n");
X  printf ("For details type `warranty'. \n");
X}
X
X
X/* Print out the warranty information. */
X
Xvoid 
Xwarranty(prefix)
X     char *prefix;
X{
X  printf ("\n%s%s\n\n", prefix, BC_VERSION);
X  printf ("%s%s%s%s%s%s%s%s%s%s%s",
X"    This program is free software; you can redistribute it and/or modify\n",
X"    it under the terms of the GNU General Public License as published by\n",
X"    the Free Software Foundation; either version 2 of the License , or\n",
X"    (at your option) any later version.\n\n",
X"    This program is distributed in the hope that it will be useful,\n",
X"    but WITHOUT ANY WARRANTY; without even the implied warranty of\n",
X"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n",
X"    GNU General Public License for more details.\n\n",
X"    You should have received a copy of the GNU General Public License\n",
X"    along with this program. If not, write to the Free Software\n",
X"    Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.\n\n");
X}
X
X/* Print out the limits of this program. */
X
Xvoid
Xlimits()
X{
X  printf ("BC_BASE_MAX     = %d\n",  BC_BASE_MAX);
X  printf ("BC_DIM_MAX      = %ld\n", (long) BC_DIM_MAX);
X  printf ("BC_SCALE_MAX    = %d\n",  BC_SCALE_MAX);
X  printf ("BC_STRING_MAX   = %d\n",  BC_STRING_MAX);
X  printf ("MAX Exponent    = %ld\n", (long) LONG_MAX);
X  printf ("MAX code        = %ld\n", (long) BC_MAX_SEGS * (long) BC_SEG_SIZE);
X  printf ("multiply digits = %ld\n", (long) LONG_MAX / (long) 90);
X  printf ("Number of vars  = %ld\n", (long) MAX_STORE);
X#ifdef OLD_EQ_OP
X  printf ("Old assignment operatiors are valid. (=-, =+, ...)\n");
X#endif 
X}
X
X/* bc_malloc will check the return value so all other places do not
X   have to do it!  SIZE is the number of types to allocate. */
X
Xchar *
Xbc_malloc (size)
X     int size;
X{
X  char *ptr;
X
X  ptr = (char *) malloc (size);
X  if (ptr == NULL)
X    out_of_memory ();
X
X  return ptr;
X}
X
X
X/* The following routines are error routines for various problems. */
X
X/* Malloc could not get enought memory. */
X
Xvoid
Xout_of_memory()
X{
X  fprintf (stderr, "Fatal error: Out of memory for malloc.\n");
X  exit (1);
X}
X
X
X
X/* The standard yyerror routine.  Built with variable number of argumnets. */
X
X#ifndef VARARGS
X#ifdef __STDC__
Xvoid
Xyyerror (char *str, ...)
X#else
Xvoid
Xyyerror (str)
X     char *str;
X#endif
X#else
Xvoid
Xyyerror (str, va_alist)
X     char *str;
X#endif
X{
X  char *name;
X  va_list args;
X
X#ifndef VARARGS   
X   va_start (args, str);
X#else
X   va_start (args);
X#endif
X  if (is_std_in)
X    name = "(standard_in)";
X  else
X    name = g_argv[optind-1];
X  fprintf (stderr,"%s %d: ",name,line_no);
X  vfprintf (stderr, str, args);
X  fprintf (stderr, "\n");
X  had_error = TRUE;
X  va_end (args);
X}
X
X
X/* The routine to produce warnings about non-standard features
X   found during parsing. */
X
X#ifndef VARARGS
X#ifdef __STDC__
Xvoid 
Xwarn (char *mesg, ...)
X#else
Xvoid
Xwarn (mesg)
X     char *mesg;
X#endif
X#else
Xvoid
Xwarn (mesg, va_alist)
X     char *mesg;
X#endif
X{
X  char *name;
X  va_list args;
X
X#ifndef VARARGS   
X  va_start (args, mesg);
X#else
X  va_start (args);
X#endif
X  if (std_only)
X    {
X      if (is_std_in)
X	name = "(standard_in)";
X      else
X	name = g_argv[optind-1];
X      fprintf (stderr,"%s %d: ",name,line_no);
X      vfprintf (stderr, mesg, args);
X      fprintf (stderr, "\n");
X      had_error = TRUE;
X    }
X  else
X    if (warn_not_std)
X      {
X	if (is_std_in)
X	  name = "(standard_in)";
X	else
X	  name = g_argv[optind-1];
X	fprintf (stderr,"%s %d: (Warning) ",name,line_no);
X	vfprintf (stderr, mesg, args);
X	fprintf (stderr, "\n");
X      }
X  va_end (args);
X}
X
X/* Runtime error will  print a message and stop the machine. */
X
X#ifndef VARARGS
X#ifdef __STDC__
Xvoid
Xrt_error (char *mesg, ...)
X#else
Xvoid
Xrt_error (mesg)
X     char *mesg;
X#endif
X#else
Xvoid
Xrt_error (mesg, va_alist)
X     char *mesg;
X#endif
X{
X  va_list args;
X  char error_mesg [255];
X
X#ifndef VARARGS   
X  va_start (args, mesg);
X#else
X  va_start (args);
X#endif
X  vsprintf (error_mesg, mesg, args);
X  va_end (args);
X  
X  fprintf (stderr, "Runtime error (func=%s, adr=%d): %s\n",
X	   f_names[pc.pc_func], pc.pc_addr, error_mesg);
X  runtime_error = TRUE;
X}
X
X
X/* A runtime warning tells of some action taken by the processor that
X   may change the program execution but was not enough of a problem
X   to stop the execution. */
X
X#ifndef VARARGS
X#ifdef __STDC__
Xvoid
Xrt_warn (char *mesg, ...)
X#else
Xvoid
Xrt_warn (mesg)
X     char *mesg;
X#endif
X#else
Xvoid
Xrt_warn (mesg, va_alist)
X     char *mesg;
X#endif
X{
X  va_list args;
X  char error_mesg [255];
X
X#ifndef VARARGS   
X  va_start (args, mesg);
X#else
X  va_start (args);
X#endif
X  vsprintf (error_mesg, mesg, args);
X  va_end (args);
X
X  fprintf (stderr, "Runtime warning (func=%s, adr=%d): %s\n",
X	   f_names[pc.pc_func], pc.pc_addr, error_mesg);
X}
/
echo x - version.h
sed '/^X/s///' > version.h << '/'
X#define BC_VERSION  \
X "bc 1.02 (Mar 3, 92) Copyright (C) 1991, 1992 Free Software Foundation, Inc."
X
/
echo x - vfprintf.c
sed '/^X/s///' > vfprintf.c << '/'
X#include <lib.h>
X#include <stdarg.h>
X#include <stdio.h>
X
Xint vfprintf(file, format, argp)
XFILE *file;
X_CONST char *format;
Xva_list argp;
X{
X  _doprintf(file, format, argp);
X  if (testflag(file, PERPRINTF)) fflush(file);
X  return 0;
X}
/
echo x - y.tab.h
sed '/^X/s///' > y.tab.h << '/'
X#define NEWLINE 257
X#define AND 258
X#define OR 259
X#define NOT 260
X#define STRING 261
X#define NAME 262
X#define NUMBER 263
X#define MUL_OP 264
X#define ASSIGN_OP 265
X#define REL_OP 266
X#define INCR_DECR 267
X#define Define 268
X#define Break 269
X#define Quit 270
X#define Length 271
X#define Return 272
X#define For 273
X#define If 274
X#define While 275
X#define Sqrt 276
X#define Else 277
X#define Scale 278
X#define Ibase 279
X#define Obase 280
X#define Auto 281
X#define Read 282
X#define Warranty 283
X#define Halt 284
X#define Last 285
X#define Continue 286
X#define Print 287
X#define Limits 288
X#define UNARY_MINUS 289
Xtypedef union {
X	char	 *s_value;
X	char	  c_value;
X	int	  i_value;
X	arg_list *a_value;
X       } YYSTYPE;
Xextern YYSTYPE yylval;
/
