summaryrefslogtreecommitdiff
path: root/dc
diff options
context:
space:
mode:
Diffstat (limited to 'dc')
-rw-r--r--dc/Makefile.am14
-rw-r--r--dc/Makefile.in296
-rw-r--r--dc/array.c122
-rw-r--r--dc/dc-proto.h90
-rw-r--r--dc/dc-regdef.h43
-rw-r--r--dc/dc.c183
-rw-r--r--dc/dc.h82
-rw-r--r--dc/eval.c682
-rw-r--r--dc/misc.c179
-rw-r--r--dc/numeric.c600
-rw-r--r--dc/stack.c494
-rw-r--r--dc/string.c211
12 files changed, 2996 insertions, 0 deletions
diff --git a/dc/Makefile.am b/dc/Makefile.am
new file mode 100644
index 0000000..99164ba
--- /dev/null
+++ b/dc/Makefile.am
@@ -0,0 +1,14 @@
+## Process this file with automake to produce Makefile.in
+bin_PROGRAMS = dc
+
+dc_SOURCES = dc.c misc.c eval.c stack.c array.c numeric.c string.c
+noinst_HEADERS = dc.h dc-proto.h dc-regdef.h
+
+INCLUDES = -I$(srcdir)/.. -I$(srcdir)/../h
+LDADD = ../lib/libbc.a
+
+MAINTAINERCLEANFILES = Makefile.in
+
+CFLAGS = @CFLAGS@ -Wall -funsigned-char
+
+$(PROGRAMS): $(LDADD)
diff --git a/dc/Makefile.in b/dc/Makefile.in
new file mode 100644
index 0000000..0772dd6
--- /dev/null
+++ b/dc/Makefile.in
@@ -0,0 +1,296 @@
+# Makefile.in generated automatically by automake 1.4 from Makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+CC = @CC@
+LEX = @LEX@
+MAKEINFO = @MAKEINFO@
+PACKAGE = @PACKAGE@
+RANLIB = @RANLIB@
+READLINELIB = @READLINELIB@
+VERSION = @VERSION@
+YACC = @YACC@
+
+bin_PROGRAMS = dc
+
+dc_SOURCES = dc.c misc.c eval.c stack.c array.c numeric.c string.c
+noinst_HEADERS = dc.h dc-proto.h dc-regdef.h
+
+INCLUDES = -I$(srcdir)/.. -I$(srcdir)/../h
+LDADD = ../lib/libbc.a
+
+MAINTAINERCLEANFILES = Makefile.in
+
+CFLAGS = @CFLAGS@ -Wall -funsigned-char
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = ../config.h
+CONFIG_CLEAN_FILES =
+PROGRAMS = $(bin_PROGRAMS)
+
+
+DEFS = @DEFS@ -I. -I$(srcdir) -I..
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+dc_OBJECTS = dc.o misc.o eval.o stack.o array.o numeric.o string.o
+dc_LDADD = $(LDADD)
+dc_DEPENDENCIES = ../lib/libbc.a
+dc_LDFLAGS =
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
+HEADERS = $(noinst_HEADERS)
+
+DIST_COMMON = Makefile.am Makefile.in
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = tar
+GZIP_ENV = --best
+SOURCES = $(dc_SOURCES)
+OBJECTS = $(dc_OBJECTS)
+
+all: all-redirect
+.SUFFIXES:
+.SUFFIXES: .S .c .o .s
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps dc/Makefile
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+
+mostlyclean-binPROGRAMS:
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+distclean-binPROGRAMS:
+
+maintainer-clean-binPROGRAMS:
+
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(bindir)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ if test -f $$p; then \
+ echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \
+ $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ list='$(bin_PROGRAMS)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+ done
+
+.c.o:
+ $(COMPILE) -c $<
+
+.s.o:
+ $(COMPILE) -c $<
+
+.S.o:
+ $(COMPILE) -c $<
+
+mostlyclean-compile:
+ -rm -f *.o core *.core
+
+clean-compile:
+
+distclean-compile:
+ -rm -f *.tab.c
+
+maintainer-clean-compile:
+
+dc: $(dc_OBJECTS) $(dc_DEPENDENCIES)
+ @rm -f dc
+ $(LINK) $(dc_LDFLAGS) $(dc_OBJECTS) $(dc_LDADD) $(LIBS)
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP)
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ here=`pwd` && cd $(srcdir) \
+ && mkid -f$$here/ID $$unique $(LISP)
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
+ || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS)
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+ -rm -f TAGS ID
+
+maintainer-clean-tags:
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+subdir = dc
+
+distdir: $(DISTFILES)
+ @for file in $(DISTFILES); do \
+ d=$(srcdir); \
+ if test -d $$d/$$file; then \
+ cp -pr $$/$$file $(distdir)/$$file; \
+ else \
+ test -f $(distdir)/$$file \
+ || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $$d/$$file $(distdir)/$$file || :; \
+ fi; \
+ done
+array.o: array.c ../config.h dc.h dc-proto.h dc-regdef.h
+dc.o: dc.c ../config.h ../h/getopt.h dc.h dc-proto.h
+eval.o: eval.c ../config.h dc.h dc-proto.h
+misc.o: misc.c ../config.h ../h/getopt.h dc.h dc-proto.h
+numeric.o: numeric.c ../config.h ../h/number.h dc.h dc-proto.h
+stack.o: stack.c ../config.h dc.h dc-proto.h dc-regdef.h
+string.o: string.c ../config.h dc.h dc-proto.h
+
+info-am:
+info: info-am
+dvi-am:
+dvi: dvi-am
+check-am: all-am
+check: check-am
+installcheck-am:
+installcheck: installcheck-am
+install-exec-am: install-binPROGRAMS
+install-exec: install-exec-am
+
+install-data-am:
+install-data: install-data-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-am
+uninstall-am: uninstall-binPROGRAMS
+uninstall: uninstall-am
+all-am: Makefile $(PROGRAMS) $(HEADERS)
+all-redirect: all-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
+installdirs:
+ $(mkinstalldirs) $(DESTDIR)$(bindir)
+
+
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -rm -f Makefile $(CONFIG_CLEAN_FILES)
+ -rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+mostlyclean-am: mostlyclean-binPROGRAMS mostlyclean-compile \
+ mostlyclean-tags mostlyclean-generic
+
+mostlyclean: mostlyclean-am
+
+clean-am: clean-binPROGRAMS clean-compile clean-tags clean-generic \
+ mostlyclean-am
+
+clean: clean-am
+
+distclean-am: distclean-binPROGRAMS distclean-compile distclean-tags \
+ distclean-generic clean-am
+
+distclean: distclean-am
+
+maintainer-clean-am: maintainer-clean-binPROGRAMS \
+ maintainer-clean-compile maintainer-clean-tags \
+ maintainer-clean-generic distclean-am
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-am
+
+.PHONY: mostlyclean-binPROGRAMS distclean-binPROGRAMS clean-binPROGRAMS \
+maintainer-clean-binPROGRAMS uninstall-binPROGRAMS install-binPROGRAMS \
+mostlyclean-compile distclean-compile clean-compile \
+maintainer-clean-compile tags mostlyclean-tags distclean-tags \
+clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi check \
+check-am installcheck-am installcheck install-exec-am install-exec \
+install-data-am install-data install-am install uninstall-am uninstall \
+all-redirect all-am all installdirs mostlyclean-generic \
+distclean-generic clean-generic maintainer-clean-generic clean \
+mostlyclean distclean maintainer-clean
+
+
+$(PROGRAMS): $(LDADD)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/dc/array.c b/dc/array.c
new file mode 100644
index 0000000..d97f716
--- /dev/null
+++ b/dc/array.c
@@ -0,0 +1,122 @@
+/*
+ * implement arrays for dc
+ *
+ * Copyright (C) 1994, 1997, 1998 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can either send email to this
+ * program's author (see below) or write to:
+ *
+ * The Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330
+ * Boston, MA 02111 USA
+ */
+
+/* This module is the only one that knows what arrays look like. */
+
+#include "config.h"
+
+#include <stdio.h> /* "dc-proto.h" wants this */
+#ifdef HAVE_STDLIB_H
+/* get size_t definition from "almost ANSI" compiling environments. */
+#include <stdlib.h>
+#endif
+#include "dc.h"
+#include "dc-proto.h"
+#include "dc-regdef.h"
+
+/* what's most useful: quick access or sparse arrays? */
+/* I'll go with sparse arrays for now */
+struct dc_array {
+ int Index;
+ dc_data value;
+ struct dc_array *next;
+};
+
+
+/* initialize the arrays */
+void
+dc_array_init DC_DECLVOID()
+{
+}
+
+/* store value into array_id[Index] */
+void
+dc_array_set DC_DECLARG((array_id, Index, value))
+ int array_id DC_DECLSEP
+ int Index DC_DECLSEP
+ dc_data value DC_DECLEND
+{
+ struct dc_array *cur;
+ struct dc_array *prev=NULL;
+ struct dc_array *newentry;
+
+ cur = dc_get_stacked_array(array_id);
+ while (cur && cur->Index < Index){
+ prev = cur;
+ cur = cur->next;
+ }
+ if (cur && cur->Index == Index){
+ if (cur->value.dc_type == DC_NUMBER)
+ dc_free_num(&cur->value.v.number);
+ else if (cur->value.dc_type == DC_STRING)
+ dc_free_str(&cur->value.v.string);
+ else
+ dc_garbage(" in array", array_id);
+ cur->value = value;
+ }else{
+ newentry = dc_malloc(sizeof *newentry);
+ newentry->Index = Index;
+ newentry->value = value;
+ newentry->next = cur;
+ if (prev)
+ prev->next = newentry;
+ else
+ dc_set_stacked_array(array_id, newentry);
+ }
+}
+
+/* retrieve a dup of a value from array_id[Index] */
+/* A zero value is returned if the specified value is unintialized. */
+dc_data
+dc_array_get DC_DECLARG((array_id, Index))
+ int array_id DC_DECLSEP
+ int Index DC_DECLEND
+{
+ struct dc_array *cur;
+
+ for (cur=dc_get_stacked_array(array_id); cur; cur=cur->next)
+ if (cur->Index == Index)
+ return dc_dup(cur->value);
+ return dc_int2data(0);
+}
+
+/* free an array chain */
+void
+dc_array_free DC_DECLARG((a_head))
+ struct dc_array *a_head DC_DECLEND
+{
+ struct dc_array *cur;
+ struct dc_array *next;
+
+ for (cur=a_head; cur; cur=next) {
+ next = cur->next;
+ if (cur->value.dc_type == DC_NUMBER)
+ dc_free_num(&cur->value.v.number);
+ else if (cur->value.dc_type == DC_STRING)
+ dc_free_str(&cur->value.v.string);
+ else
+ dc_garbage("in stack", -1);
+ free(cur);
+ }
+}
diff --git a/dc/dc-proto.h b/dc/dc-proto.h
new file mode 100644
index 0000000..f0ac28b
--- /dev/null
+++ b/dc/dc-proto.h
@@ -0,0 +1,90 @@
+/*
+ * prototypes of all externally visible dc functions
+ *
+ * Copyright (C) 1994, 1997, 1998 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can either send email to this
+ * program's author (see below) or write to:
+ *
+ * The Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330
+ * Boston, MA 02111 USA
+ */
+
+extern const char *dc_str2charp DC_PROTO((dc_str));
+extern const char *dc_system DC_PROTO((const char *));
+extern void *dc_malloc DC_PROTO((size_t));
+extern struct dc_array *dc_get_stacked_array DC_PROTO((int));
+
+extern void dc_array_set DC_PROTO((int, int, dc_data));
+extern void dc_array_free DC_PROTO((struct dc_array *));
+extern void dc_array_init DC_PROTO((void));
+extern void dc_binop DC_PROTO((int (*)(dc_num, dc_num, int, dc_num *), int));
+extern void dc_binop2 DC_PROTO((int (*)(dc_num, dc_num, int,
+ dc_num *, dc_num *), int));
+extern void dc_triop DC_PROTO((int (*)(dc_num, dc_num, dc_num, int,
+ dc_num *), int));
+extern void dc_clear_stack DC_PROTO((void));
+extern void dc_dump_num(dc_num, dc_discard);
+extern void dc_free_num DC_PROTO((dc_num *));
+extern void dc_free_str DC_PROTO((dc_str *));
+extern void dc_garbage DC_PROTO((const char *, int));
+extern void dc_math_init DC_PROTO((void));
+extern void dc_memfail DC_PROTO((void));
+extern void dc_out_num DC_PROTO((dc_num, int, dc_newline, dc_discard));
+extern void dc_out_str DC_PROTO((dc_str, dc_newline, dc_discard));
+extern void dc_print DC_PROTO((dc_data, int, dc_newline, dc_discard));
+extern void dc_printall DC_PROTO((int));
+extern void dc_push DC_PROTO((dc_data));
+extern void dc_register_init DC_PROTO((void));
+extern void dc_register_push DC_PROTO((int, dc_data));
+extern void dc_register_set DC_PROTO((int, dc_data));
+extern void dc_set_stacked_array DC_PROTO((int, struct dc_array *));
+extern void dc_show_id DC_PROTO((FILE *, int, const char *));
+extern void dc_string_init DC_PROTO((void));
+
+extern int dc_cmpop DC_PROTO((void));
+extern int dc_compare DC_PROTO((dc_num, dc_num));
+extern int dc_evalfile DC_PROTO((FILE *));
+extern int dc_evalstr DC_PROTO((dc_data));
+extern int dc_num2int DC_PROTO((dc_num, dc_discard));
+extern int dc_numlen DC_PROTO((dc_num));
+extern int dc_pop DC_PROTO((dc_data *));
+extern int dc_register_get DC_PROTO((int, dc_data *));
+extern int dc_register_pop DC_PROTO((int, dc_data *));
+extern int dc_tell_length DC_PROTO((dc_data, dc_discard));
+extern int dc_tell_scale DC_PROTO((dc_num, dc_discard));
+extern int dc_tell_stackdepth DC_PROTO((void));
+extern int dc_top_of_stack DC_PROTO((dc_data *));
+
+extern size_t dc_strlen DC_PROTO((dc_str));
+
+extern dc_data dc_array_get DC_PROTO((int, int));
+extern dc_data dc_dup DC_PROTO((dc_data));
+extern dc_data dc_dup_num DC_PROTO((dc_num));
+extern dc_data dc_dup_str DC_PROTO((dc_str));
+extern dc_data dc_getnum DC_PROTO((int (*)(void), int, int *));
+extern dc_data dc_int2data DC_PROTO((int));
+extern dc_data dc_makestring DC_PROTO((const char *, size_t));
+extern dc_data dc_readstring DC_PROTO((FILE *, int , int));
+
+extern int dc_add DC_PROTO((dc_num, dc_num, int, dc_num *));
+extern int dc_div DC_PROTO((dc_num, dc_num, int, dc_num *));
+extern int dc_divrem DC_PROTO((dc_num, dc_num, int, dc_num *, dc_num *));
+extern int dc_exp DC_PROTO((dc_num, dc_num, int, dc_num *));
+extern int dc_modexp DC_PROTO((dc_num, dc_num, dc_num, int, dc_num *));
+extern int dc_mul DC_PROTO((dc_num, dc_num, int, dc_num *));
+extern int dc_rem DC_PROTO((dc_num, dc_num, int, dc_num *));
+extern int dc_sub DC_PROTO((dc_num, dc_num, int, dc_num *));
+extern int dc_sqrt DC_PROTO((dc_num, int, dc_num *));
diff --git a/dc/dc-regdef.h b/dc/dc-regdef.h
new file mode 100644
index 0000000..540268c
--- /dev/null
+++ b/dc/dc-regdef.h
@@ -0,0 +1,43 @@
+/*
+ * definitions for dc's "register" declarations
+ *
+ * Copyright (C) 1994 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can either send email to this
+ * program's author (see below) or write to:
+ *
+ * The Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330
+ * Boston, MA 02111 USA
+ */
+
+#ifdef HAVE_LIMITS_H
+# include <limits.h> /* UCHAR_MAX */
+#endif
+
+/* determine how many register stacks there are */
+#ifndef DC_REGCOUNT
+# ifndef UCHAR_MAX
+# define DC_REGCOUNT 256
+# else
+# define DC_REGCOUNT (UCHAR_MAX+1)
+# endif
+#endif /* not DC_REGCOUNT */
+
+/* efficiency hack for masking arbritrary integers to 0..(DC_REGCOUNT-1) */
+#if (DC_REGCOUNT & (DC_REGCOUNT-1)) == 0 /* DC_REGCOUNT is power of 2 */
+# define regmap(r) ((r) & (DC_REGCOUNT-1))
+#else
+# define regmap(r) ((r) % DC_REGCOUNT)
+#endif
diff --git a/dc/dc.c b/dc/dc.c
new file mode 100644
index 0000000..a72644c
--- /dev/null
+++ b/dc/dc.c
@@ -0,0 +1,183 @@
+/*
+ * implement the "dc" Desk Calculator language.
+ *
+ * Copyright (C) 1994, 1997, 1998, 2000 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can either send email to this
+ * program's author (see below) or write to:
+ * The Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330
+ * Boston, MA 02111 USA
+ */
+
+/* Written with strong hiding of implementation details
+ * in their own specialized modules.
+ */
+/* This module contains the argument processing/main functions.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# ifdef HAVE_STRINGS_H
+# include <strings.h>
+# endif
+#endif
+#include <getopt.h>
+#include "dc.h"
+#include "dc-proto.h"
+
+#ifndef EXIT_SUCCESS /* C89 <stdlib.h> */
+# define EXIT_SUCCESS 0
+#endif
+#ifndef EXIT_FAILURE /* C89 <stdlib.h> */
+# define EXIT_FAILURE 1
+#endif
+
+const char *progname; /* basename of program invocation */
+
+static void
+bug_report_info DC_DECLVOID()
+{
+ printf("Email bug reports to: bug-dc@gnu.org .\n");
+}
+
+static void
+show_version DC_DECLVOID()
+{
+ printf("dc (GNU %s %s) %s\n", PACKAGE, VERSION, DC_VERSION);
+ printf("\n%s\n\
+This is free software; see the source for copying conditions. There is NO\n\
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE,\n\
+to the extent permitted by law.\n", DC_COPYRIGHT);
+}
+
+/* your generic usage function */
+static void
+usage DC_DECLARG((f))
+ FILE *f DC_DECLEND
+{
+ fprintf(f, "\
+Usage: %s [OPTION] [file ...]\n\
+ -e, --expression=EXPR evaluate expression\n\
+ -f, --file=FILE evaluate contents of file\n\
+ -h, --help display this help and exit\n\
+ -V, --version output version information and exit\n\
+\n\
+", progname);
+ bug_report_info();
+}
+
+/* returns a pointer to one past the last occurance of c in s,
+ * or s if c does not occur in s.
+ */
+static char *
+r1bindex DC_DECLARG((s, c))
+ char *s DC_DECLSEP
+ int c DC_DECLEND
+{
+ char *p = strrchr(s, c);
+
+ if (!p)
+ return s;
+ return p + 1;
+}
+
+static void
+try_file(const char *filename)
+{
+ FILE *input;
+
+ if (strcmp(filename, "-") == 0) {
+ input = stdin;
+ } else if ( !(input=fopen(filename, "r")) ) {
+ fprintf(stderr, "Could not open file ");
+ perror(filename);
+ exit(EXIT_FAILURE);
+ }
+ if (dc_evalfile(input))
+ exit(EXIT_FAILURE);
+ if (input != stdin)
+ fclose(input);
+}
+
+
+int
+main DC_DECLARG((argc, argv))
+ int argc DC_DECLSEP
+ char **argv DC_DECLEND
+{
+ static struct option const long_opts[] = {
+ {"expression", required_argument, NULL, 'e'},
+ {"file", required_argument, NULL, 'f'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+ };
+ int did_eval = 0;
+ int c;
+
+ progname = r1bindex(*argv, '/');
+#ifdef HAVE_SETVBUF
+ /* attempt to simplify interaction with applications such as emacs */
+ (void) setvbuf(stdout, NULL, _IOLBF, 0);
+#endif
+ dc_math_init();
+ dc_string_init();
+ dc_register_init();
+ dc_array_init();
+
+ while ((c = getopt_long(argc, argv, "hVe:f:", long_opts, (int *)0)) != EOF) {
+ switch (c) {
+ case 'e':
+ { dc_data string = dc_makestring(optarg, strlen(optarg));
+ if (dc_evalstr(string))
+ return EXIT_SUCCESS;
+ dc_free_str(&string.v.string);
+ did_eval = 1;
+ }
+ break;
+ case 'f':
+ try_file(optarg);
+ did_eval = 1;
+ break;
+ case 'h':
+ usage(stdout);
+ return EXIT_SUCCESS;
+ case 'V':
+ show_version();
+ return EXIT_SUCCESS;
+ default:
+ usage(stderr);
+ return EXIT_FAILURE;
+ }
+ }
+
+ for (; optind < argc; ++optind) {
+ try_file(argv[optind]);
+ did_eval = 1;
+ }
+ if (!did_eval) {
+ /* if no -e commands and no command files, then eval stdin */
+ if (dc_evalfile(stdin))
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/dc/dc.h b/dc/dc.h
new file mode 100644
index 0000000..3300f44
--- /dev/null
+++ b/dc/dc.h
@@ -0,0 +1,82 @@
+/*
+ * Header file for dc routines
+ *
+ * Copyright (C) 1994, 1997, 1998 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can either send email to this
+ * program's author (see below) or write to:
+ *
+ * The Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330
+ * Boston, MA 02111 USA
+ */
+
+#ifndef DC_DEFS_H
+#define DC_DEFS_H
+
+/* 'I' is a command, and bases 17 and 18 are quite
+ * unusual, so we limit ourselves to bases 2 to 16
+ */
+#define DC_IBASE_MAX 16
+
+#define DC_SUCCESS 0
+#define DC_DOMAIN_ERROR 1
+#define DC_FAIL 2 /* generic failure */
+
+
+#ifndef __STDC__
+# define DC_PROTO(x) ()
+# define DC_DECLVOID() ()
+# define DC_DECLARG(arglist) arglist
+# define DC_DECLSEP ;
+# define DC_DECLEND ;
+#else /* __STDC__ */
+# define DC_PROTO(x) x
+# define DC_DECLVOID() (void)
+# define DC_DECLARG(arglist) (
+# define DC_DECLSEP ,
+# define DC_DECLEND )
+#endif /* __STDC__ */
+
+
+typedef enum {DC_TOSS, DC_KEEP} dc_discard;
+typedef enum {DC_NONL, DC_WITHNL} dc_newline;
+
+
+/* type discriminant for dc_data */
+typedef enum {DC_UNINITIALIZED, DC_NUMBER, DC_STRING} dc_value_type;
+
+/* only numeric.c knows what dc_num's *really* look like */
+typedef struct dc_number *dc_num;
+
+/* only string.c knows what dc_str's *really* look like */
+typedef struct dc_string *dc_str;
+
+
+/* except for the two implementation-specific modules, all
+ * dc functions only know of this one generic type of object
+ */
+typedef struct {
+ dc_value_type dc_type; /* discriminant for union */
+ union {
+ dc_num number;
+ dc_str string;
+ } v;
+} dc_data;
+
+
+/* This is dc's only global variable: */
+extern const char *progname; /* basename of program invocation */
+
+#endif /* not DC_DEFS_H */
diff --git a/dc/eval.c b/dc/eval.c
new file mode 100644
index 0000000..21592d9
--- /dev/null
+++ b/dc/eval.c
@@ -0,0 +1,682 @@
+/*
+ * evaluate the dc language, from a FILE* or a string
+ *
+ * Copyright (C) 1994, 1997, 1998, 2000 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can either send email to this
+ * program's author (see below) or write to:
+ * The Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330
+ * Boston, MA 02111 USA
+ */
+
+/* This is the only module which knows about the dc input language */
+
+#include "config.h"
+
+#include <stdio.h>
+#ifdef HAVE_STRING_H
+# include <string.h> /* memchr */
+#else
+# ifdef HAVE_MEMORY_H
+# include <memory.h> /* memchr, maybe */
+# else
+# ifdef HAVE_STRINGS_H
+# include <strings.h> /* memchr, maybe */
+# endif
+#endif
+#endif
+#include "dc.h"
+#include "dc-proto.h"
+
+typedef enum {DC_FALSE, DC_TRUE} dc_boolean;
+
+typedef enum {
+ DC_OKAY = DC_SUCCESS, /* no further intervention needed for this command */
+ DC_EATONE, /* caller needs to eat the lookahead char */
+ DC_QUIT, /* quit out of unwind_depth levels of evaluation */
+
+ /* with the following return values, the caller does not have to
+ * fret about stdin_lookahead's value
+ */
+ DC_INT, /* caller needs to parse a dc_num from input stream */
+ DC_STR, /* caller needs to parse a dc_str from input stream */
+ DC_SYSTEM, /* caller needs to run a system() on next input line */
+ DC_COMMENT, /* caller needs to skip to the next input line */
+ DC_NEGCMP, /* caller needs to re-call dc_func() with `negcmp' set */
+
+ DC_EOF_ERROR /* unexpected end of input; abort current eval */
+} dc_status;
+
+static int dc_ibase=10; /* input base, 2 <= dc_ibase <= DC_IBASE_MAX */
+static int dc_obase=10; /* output base, 2 <= dc_obase */
+static int dc_scale=0; /* scale (see user documentaton) */
+
+/* for Quitting evaluations */
+static int unwind_depth=0;
+
+/* if true, active Quit will not exit program */
+static dc_boolean unwind_noexit=DC_FALSE;
+
+/*
+ * Used to synchronize lookahead on stdin for '?' command.
+ * If set to EOF then lookahead is used up.
+ */
+static int stdin_lookahead=EOF;
+
+
+/* input_fil and input_str are passed as arguments to dc_getnum */
+
+/* used by the input_* functions: */
+static FILE *input_fil_fp;
+static const char *input_str_string;
+
+/* Since we have a need for two characters of pushback, and
+ * ungetc() only guarantees one, we place the second pushback here
+ */
+static int input_pushback;
+
+/* passed as an argument to dc_getnum */
+static int
+input_fil DC_DECLVOID()
+{
+ if (input_pushback != EOF){
+ int c = input_pushback;
+ input_pushback = EOF;
+ return c;
+ }
+ return getc(input_fil_fp);
+}
+
+/* passed as an argument to dc_getnum */
+static int
+input_str DC_DECLVOID()
+{
+ if (!*input_str_string)
+ return EOF;
+ return *input_str_string++;
+}
+
+
+
+/* takes a string and evals it; frees the string when done */
+/* Wrapper around dc_evalstr to avoid duplicating the free call
+ * at all possible return points.
+ */
+static int
+dc_eval_and_free_str DC_DECLARG((string))
+ dc_data string DC_DECLEND
+{
+ dc_status status;
+
+ status = dc_evalstr(string);
+ if (string.dc_type == DC_STRING)
+ dc_free_str(&string.v.string);
+ return status;
+}
+
+
+/* dc_func does the grunt work of figuring out what each input
+ * character means; used by both dc_evalstr and dc_evalfile
+ *
+ * c -> the "current" input character under consideration
+ * peekc -> the lookahead input character
+ * negcmp -> negate comparison test (for <,=,> commands)
+ */
+static dc_status
+dc_func DC_DECLARG((c, peekc, negcmp))
+ int c DC_DECLSEP
+ int peekc DC_DECLSEP
+ int negcmp DC_DECLEND
+{
+ /* we occasionally need these for temporary data */
+ /* Despite the GNU coding standards, it is much easier
+ * to have these declared once here, since this function
+ * is just one big switch statement.
+ */
+ dc_data datum;
+ int tmpint;
+
+ switch (c){
+ case '_': case '.':
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9': case 'A': case 'B':
+ case 'C': case 'D': case 'E': case 'F':
+ return DC_INT;
+ case ' ':
+ case '\t':
+ case '\n':
+ /* standard command separators */
+ break;
+
+ case '+': /* add top two stack elements */
+ dc_binop(dc_add, dc_scale);
+ break;
+ case '-': /* subtract top two stack elements */
+ dc_binop(dc_sub, dc_scale);
+ break;
+ case '*': /* multiply top two stack elements */
+ dc_binop(dc_mul, dc_scale);
+ break;
+ case '/': /* divide top two stack elements */
+ dc_binop(dc_div, dc_scale);
+ break;
+ case '%':
+ /* take the remainder from division of the top two stack elements */
+ dc_binop(dc_rem, dc_scale);
+ break;
+ case '~':
+ /* Do division on the top two stack elements. Return the
+ * quotient as next-to-top of stack and the remainder as
+ * top-of-stack.
+ */
+ dc_binop2(dc_divrem, dc_scale);
+ break;
+ case '|':
+ /* Consider the top three elements of the stack as (base, exp, mod),
+ * where mod is top-of-stack, exp is next-to-top, and base is
+ * second-from-top. Mod must be non-zero, exp must be non-negative,
+ * and all three must be integers. Push the result of raising
+ * base to the exp power, reduced modulo mod. If we had base in
+ * register b, exp in register e, and mod in register m then this
+ * is conceptually equivalent to "lble^lm%", but it is implemented
+ * in a more efficient manner, and can handle arbritrarily large
+ * values for exp.
+ */
+ dc_triop(dc_modexp, dc_scale);
+ break;
+ case '^': /* exponientiation of the top two stack elements */
+ dc_binop(dc_exp, dc_scale);
+ break;
+ case '<':
+ /* eval register named by peekc if
+ * less-than holds for top two stack elements
+ */
+ if (peekc == EOF)
+ return DC_EOF_ERROR;
+ if ( (dc_cmpop() < 0) == !negcmp )
+ if (dc_register_get(peekc, &datum) == DC_SUCCESS)
+ if (dc_eval_and_free_str(datum) == DC_QUIT)
+ return DC_QUIT;
+ return DC_EATONE;
+ case '=':
+ /* eval register named by peekc if
+ * equal-to holds for top two stack elements
+ */
+ if (peekc == EOF)
+ return DC_EOF_ERROR;
+ if ( (dc_cmpop() == 0) == !negcmp )
+ if (dc_register_get(peekc, &datum) == DC_SUCCESS)
+ if (dc_eval_and_free_str(datum) == DC_QUIT)
+ return DC_QUIT;
+ return DC_EATONE;
+ case '>':
+ /* eval register named by peekc if
+ * greater-than holds for top two stack elements
+ */
+ if (peekc == EOF)
+ return DC_EOF_ERROR;
+ if ( (dc_cmpop() > 0) == !negcmp )
+ if (dc_register_get(peekc, &datum) == DC_SUCCESS)
+ if (dc_eval_and_free_str(datum) == DC_QUIT)
+ return DC_QUIT;
+ return DC_EATONE;
+ case '?': /* read a line from standard-input and eval it */
+ if (stdin_lookahead != EOF){
+ ungetc(stdin_lookahead, stdin);
+ stdin_lookahead = EOF;
+ }
+ if (dc_eval_and_free_str(dc_readstring(stdin, '\n', '\n')) == DC_QUIT)
+ return DC_QUIT;
+ return DC_OKAY;
+ case '[': /* read to balancing ']' into a dc_str */
+ return DC_STR;
+ case '!': /* read to newline and call system() on resulting string */
+ if (peekc == '<' || peekc == '=' || peekc == '>')
+ return DC_NEGCMP;
+ return DC_SYSTEM;
+ case '#': /* comment; skip remainder of current line */
+ return DC_COMMENT;
+
+ case 'a': /* Convert top of stack to an ascii character. */
+ if (dc_pop(&datum) == DC_SUCCESS){
+ char tmps;
+ if (datum.dc_type == DC_NUMBER){
+ tmps = (char) dc_num2int(datum.v.number, DC_TOSS);
+ }else if (datum.dc_type == DC_STRING){
+ tmps = *dc_str2charp(datum.v.string);
+ dc_free_str(&datum.v.string);
+ }else{
+ dc_garbage("at top of stack", -1);
+ }
+ dc_push(dc_makestring(&tmps, 1));
+ }
+ break;
+ case 'c': /* clear whole stack */
+ dc_clear_stack();
+ break;
+ case 'd': /* duplicate the datum on the top of stack */
+ if (dc_top_of_stack(&datum) == DC_SUCCESS)
+ dc_push(dc_dup(datum));
+ break;
+ case 'f': /* print list of all stack items */
+ dc_printall(dc_obase);
+ break;
+ case 'i': /* set input base to value on top of stack */
+ if (dc_pop(&datum) == DC_SUCCESS){
+ tmpint = 0;
+ if (datum.dc_type == DC_NUMBER)
+ tmpint = dc_num2int(datum.v.number, DC_TOSS);
+ if ( ! (2 <= tmpint && tmpint <= DC_IBASE_MAX) )
+ fprintf(stderr,
+ "%s: input base must be a number \
+between 2 and %d (inclusive)\n",
+ progname, DC_IBASE_MAX);
+ else
+ dc_ibase = tmpint;
+ }
+ break;
+ case 'k': /* set scale to value on top of stack */
+ if (dc_pop(&datum) == DC_SUCCESS){
+ tmpint = -1;
+ if (datum.dc_type == DC_NUMBER)
+ tmpint = dc_num2int(datum.v.number, DC_TOSS);
+ if ( ! (tmpint >= 0) )
+ fprintf(stderr,
+ "%s: scale must be a nonnegative number\n",
+ progname);
+ else
+ dc_scale = tmpint;
+ }
+ break;
+ case 'l': /* "load" -- push value on top of register stack named
+ * by peekc onto top of evaluation stack; does not
+ * modify the register stack
+ */
+ if (peekc == EOF)
+ return DC_EOF_ERROR;
+ if (dc_register_get(peekc, &datum) == DC_SUCCESS)
+ dc_push(datum);
+ return DC_EATONE;
+ case 'n': /* print the value popped off of top-of-stack;
+ * do not add a trailing newline
+ */
+ if (dc_pop(&datum) == DC_SUCCESS)
+ dc_print(datum, dc_obase, DC_NONL, DC_TOSS);
+ break;
+ case 'o': /* set output base to value on top of stack */
+ if (dc_pop(&datum) == DC_SUCCESS){
+ tmpint = 0;
+ if (datum.dc_type == DC_NUMBER)
+ tmpint = dc_num2int(datum.v.number, DC_TOSS);
+ if ( ! (tmpint > 1) )
+ fprintf(stderr,
+ "%s: output base must be a number greater than 1\n",
+ progname);
+ else
+ dc_obase = tmpint;
+ }
+ break;
+ case 'p': /* print the datum on the top of stack,
+ * with a trailing newline
+ */
+ if (dc_top_of_stack(&datum) == DC_SUCCESS)
+ dc_print(datum, dc_obase, DC_WITHNL, DC_KEEP);
+ break;
+ case 'q': /* quit two levels of evaluation, posibly exiting program */
+ unwind_depth = 1; /* the return below is the first level of returns */
+ unwind_noexit = DC_FALSE;
+ return DC_QUIT;
+ case 'r': /* rotate (swap) the top two elements on the stack
+ */
+ if (dc_pop(&datum) == DC_SUCCESS) {
+ dc_data datum2;
+ int two_status;
+ two_status = dc_pop(&datum2);
+ dc_push(datum);
+ if (two_status == DC_SUCCESS)
+ dc_push(datum2);
+ }
+ break;
+ case 's': /* "store" -- replace top of register stack named
+ * by peekc with the value popped from the top
+ * of the evaluation stack
+ */
+ if (peekc == EOF)
+ return DC_EOF_ERROR;
+ if (dc_pop(&datum) == DC_SUCCESS)
+ dc_register_set(peekc, datum);
+ return DC_EATONE;
+ case 'v': /* replace top of stack with its square root */
+ if (dc_pop(&datum) == DC_SUCCESS){
+ dc_num tmpnum;
+ if (datum.dc_type != DC_NUMBER){
+ fprintf(stderr,
+ "%s: square root of nonnumeric attempted\n",
+ progname);
+ }else if (dc_sqrt(datum.v.number, dc_scale, &tmpnum) == DC_SUCCESS){
+ dc_free_num(&datum.v.number);
+ datum.v.number = tmpnum;
+ dc_push(datum);
+ }
+ }
+ break;
+ case 'x': /* eval the datum popped from top of stack */
+ if (dc_pop(&datum) == DC_SUCCESS){
+ if (datum.dc_type == DC_STRING){
+ if (dc_eval_and_free_str(datum) == DC_QUIT)
+ return DC_QUIT;
+ }else if (datum.dc_type == DC_NUMBER){
+ dc_push(datum);
+ }else{
+ dc_garbage("at top of stack", -1);
+ }
+ }
+ break;
+ case 'z': /* push the current stack depth onto the top of stack */
+ dc_push(dc_int2data(dc_tell_stackdepth()));
+ break;
+
+ case 'I': /* push the current input base onto the stack */
+ dc_push(dc_int2data(dc_ibase));
+ break;
+ case 'K': /* push the current scale onto the stack */
+ dc_push(dc_int2data(dc_scale));
+ break;
+ case 'L': /* pop a value off of register stack named by peekc
+ * and push it onto the evaluation stack
+ */
+ if (peekc == EOF)
+ return DC_EOF_ERROR;
+ if (dc_register_pop(peekc, &datum) == DC_SUCCESS)
+ dc_push(datum);
+ return DC_EATONE;
+ case 'O': /* push the current output base onto the stack */
+ dc_push(dc_int2data(dc_obase));
+ break;
+ case 'P':
+ /* Pop the value off the top of a stack. If it is
+ * a number, dump out the integer portion of its
+ * absolute value as a "base UCHAR_MAX+1" byte stream;
+ * if it is a string, just print it.
+ * In either case, do not append a trailing newline.
+ */
+ if (dc_pop(&datum) == DC_SUCCESS){
+ if (datum.dc_type == DC_NUMBER)
+ dc_dump_num(datum.v.number, DC_TOSS);
+ else if (datum.dc_type == DC_STRING)
+ dc_out_str(datum.v.string, DC_NONL, DC_TOSS);
+ else
+ dc_garbage("at top of stack", -1);
+ }
+ break;
+ case 'Q': /* quit out of top-of-stack nested evals;
+ * pops value from stack;
+ * does not exit program (stops short if necessary)
+ */
+ if (dc_pop(&datum) == DC_SUCCESS){
+ unwind_depth = 0;
+ unwind_noexit = DC_TRUE;
+ if (datum.dc_type == DC_NUMBER)
+ unwind_depth = dc_num2int(datum.v.number, DC_TOSS);
+ if (unwind_depth-- > 0)
+ return DC_QUIT;
+ unwind_depth = 0; /* paranoia */
+ fprintf(stderr,
+ "%s: Q command requires a number >= 1\n",
+ progname);
+ }
+ break;
+#if 0
+ case 'R': /* pop a value off of the evaluation stack,;
+ * rotate the top
+ remaining stack elements that many
+ * places forward (negative numbers mean rotate
+ * backward).
+ */
+ if (dc_pop(&datum) == DC_SUCCESS){
+ tmpint = 0;
+ if (datum.dc_type == DC_NUMBER)
+ tmpint = dc_num2int(datum.v.number, DC_TOSS);
+ dc_stack_rotate(tmpint);
+ }
+ break;
+#endif
+ case 'S': /* pop a value off of the evaluation stack
+ * and push it onto the register stack named by peekc
+ */
+ if (peekc == EOF)
+ return DC_EOF_ERROR;
+ if (dc_pop(&datum) == DC_SUCCESS)
+ dc_register_push(peekc, datum);
+ return DC_EATONE;
+ case 'X': /* replace the number on top-of-stack with its scale factor */
+ if (dc_pop(&datum) == DC_SUCCESS){
+ tmpint = 0;
+ if (datum.dc_type == DC_NUMBER)
+ tmpint = dc_tell_scale(datum.v.number, DC_TOSS);
+ dc_push(dc_int2data(tmpint));
+ }
+ break;
+ case 'Z': /* replace the datum on the top-of-stack with its length */
+ if (dc_pop(&datum) == DC_SUCCESS)
+ dc_push(dc_int2data(dc_tell_length(datum, DC_TOSS)));
+ break;
+
+ case ':': /* store into array */
+ if (peekc == EOF)
+ return DC_EOF_ERROR;
+ if (dc_pop(&datum) == DC_SUCCESS){
+ tmpint = -1;
+ if (datum.dc_type == DC_NUMBER)
+ tmpint = dc_num2int(datum.v.number, DC_TOSS);
+ if (dc_pop(&datum) == DC_SUCCESS){
+ if (tmpint < 0)
+ fprintf(stderr,
+ "%s: array index must be a nonnegative integer\n",
+ progname);
+ else
+ dc_array_set(peekc, tmpint, datum);
+ }
+ }
+ return DC_EATONE;
+ case ';': /* retreive from array */
+ if (peekc == EOF)
+ return DC_EOF_ERROR;
+ if (dc_pop(&datum) == DC_SUCCESS){
+ tmpint = -1;
+ if (datum.dc_type == DC_NUMBER)
+ tmpint = dc_num2int(datum.v.number, DC_TOSS);
+ if (tmpint < 0)
+ fprintf(stderr,
+ "%s: array index must be a nonnegative integer\n",
+ progname);
+ else
+ dc_push(dc_array_get(peekc, tmpint));
+ }
+ return DC_EATONE;
+
+ default: /* What did that user mean? */
+ fprintf(stderr, "%s: ", progname);
+ dc_show_id(stdout, c, " unimplemented\n");
+ break;
+ }
+ return DC_OKAY;
+}
+
+
+/* takes a string and evals it */
+int
+dc_evalstr DC_DECLARG((string))
+ dc_data string DC_DECLEND
+{
+ const char *s;
+ const char *end;
+ const char *p;
+ size_t len;
+ int c;
+ int peekc;
+ int count;
+ int negcmp;
+ int next_negcmp = 0;
+
+ if (string.dc_type != DC_STRING){
+ fprintf(stderr,
+ "%s: eval called with non-string argument\n",
+ progname);
+ return DC_OKAY;
+ }
+ s = dc_str2charp(string.v.string);
+ end = s + dc_strlen(string.v.string);
+ while (s < end){
+ c = *(const unsigned char *)s++;
+ peekc = EOF;
+ if (s < end)
+ peekc = *(const unsigned char *)s;
+ negcmp = next_negcmp;
+ next_negcmp = 0;
+ switch (dc_func(c, peekc, negcmp)){
+ case DC_OKAY:
+ break;
+ case DC_EATONE:
+ if (peekc != EOF)
+ ++s;
+ break;
+ case DC_QUIT:
+ if (unwind_depth > 0){
+ --unwind_depth;
+ return DC_QUIT;
+ }
+ return DC_OKAY;
+
+ case DC_INT:
+ input_str_string = s - 1;
+ dc_push(dc_getnum(input_str, dc_ibase, &peekc));
+ s = input_str_string;
+ if (peekc != EOF)
+ --s;
+ break;
+ case DC_STR:
+ count = 1;
+ for (p=s; p<end && count>0; ++p)
+ if (*p == ']')
+ --count;
+ else if (*p == '[')
+ ++count;
+ len = p - s;
+ dc_push(dc_makestring(s, len-1));
+ s = p;
+ break;
+ case DC_SYSTEM:
+ s = dc_system(s);
+ case DC_COMMENT:
+ s = memchr(s, '\n', (size_t)(end-s));
+ if (!s)
+ s = end;
+ else
+ ++s;
+ break;
+ case DC_NEGCMP:
+ next_negcmp = 1;
+ break;
+
+ case DC_EOF_ERROR:
+ fprintf(stderr, "%s: unexpected EOS\n", progname);
+ return DC_OKAY;
+ }
+ }
+ return DC_OKAY;
+}
+
+
+/* This is the main function of the whole DC program.
+ * Reads the file described by fp, calls dc_func to do
+ * the dirty work, and takes care of dc_func's shortcomings.
+ */
+int
+dc_evalfile DC_DECLARG((fp))
+ FILE *fp DC_DECLEND
+{
+ int c;
+ int peekc;
+ int negcmp;
+ int next_negcmp = 0;
+ dc_data datum;
+
+ stdin_lookahead = EOF;
+ for (c=getc(fp); c!=EOF; c=peekc){
+ peekc = getc(fp);
+ /*
+ * The following if() is the only place where ``stdin_lookahead''
+ * might be set to other than EOF:
+ */
+ if (fp == stdin)
+ stdin_lookahead = peekc;
+ negcmp = next_negcmp;
+ next_negcmp = 0;
+ switch (dc_func(c, peekc, negcmp)){
+ case DC_OKAY:
+ if (stdin_lookahead != peekc && fp == stdin)
+ peekc = getc(fp);
+ break;
+ case DC_EATONE:
+ peekc = getc(fp);
+ break;
+ case DC_QUIT:
+ if (unwind_noexit != DC_TRUE)
+ return DC_SUCCESS;
+ fprintf(stderr,
+ "%s: Q command argument exceeded string execution depth\n",
+ progname);
+ if (stdin_lookahead != peekc && fp == stdin)
+ peekc = getc(fp);
+ break;
+
+ case DC_INT:
+ input_fil_fp = fp;
+ input_pushback = c;
+ ungetc(peekc, fp);
+ dc_push(dc_getnum(input_fil, dc_ibase, &peekc));
+ break;
+ case DC_STR:
+ ungetc(peekc, fp);
+ datum = dc_readstring(fp, '[', ']');
+ dc_push(datum);
+ peekc = getc(fp);
+ break;
+ case DC_SYSTEM:
+ ungetc(peekc, fp);
+ datum = dc_readstring(stdin, '\n', '\n');
+ (void)dc_system(dc_str2charp(datum.v.string));
+ dc_free_str(&datum.v.string);
+ peekc = getc(fp);
+ break;
+ case DC_COMMENT:
+ while (peekc!=EOF && peekc!='\n')
+ peekc = getc(fp);
+ if (peekc != EOF)
+ peekc = getc(fp);
+ break;
+ case DC_NEGCMP:
+ next_negcmp = 1;
+ break;
+
+ case DC_EOF_ERROR:
+ fprintf(stderr, "%s: unexpected EOF\n", progname);
+ return DC_FAIL;
+ }
+ }
+ return DC_SUCCESS;
+}
diff --git a/dc/misc.c b/dc/misc.c
new file mode 100644
index 0000000..fa94de1
--- /dev/null
+++ b/dc/misc.c
@@ -0,0 +1,179 @@
+/*
+ * misc. functions for the "dc" Desk Calculator language.
+ *
+ * Copyright (C) 1994, 1997, 1998, 2000 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can either send email to this
+ * program's author (see below) or write to:
+ * The Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330
+ * Boston, MA 02111 USA
+ */
+
+/* This module contains miscelaneous functions that have no
+ * special knowledge of any private data structures.
+ * They could all be moved to their own separate modules, but
+ * are agglomerated here for convenience.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# ifdef HAVE_STRINGS_H
+# include <strings.h>
+# endif
+#endif
+#include <ctype.h>
+#ifndef isgraph
+# ifndef HAVE_ISGRAPH
+# define isgraph isprint
+# endif
+#endif
+#include <getopt.h>
+#include "dc.h"
+#include "dc-proto.h"
+
+#ifndef EXIT_FAILURE /* C89 <stdlib.h> */
+# define EXIT_FAILURE 1
+#endif
+
+
+/* print an "out of memory" diagnostic and exit program */
+void
+dc_memfail DC_DECLVOID()
+{
+ fprintf(stderr, "%s: out of memory\n", progname);
+ exit(EXIT_FAILURE);
+}
+
+/* malloc or die */
+void *
+dc_malloc DC_DECLARG((len))
+ size_t len DC_DECLEND
+{
+ void *result = malloc(len);
+
+ if (!result)
+ dc_memfail();
+ return result;
+}
+
+
+/* print the id in a human-understandable form
+ * fp is the output stream to place the output on
+ * id is the name of the register (or command) to be printed
+ * suffix is a modifier (such as "stack") to be printed
+ */
+void
+dc_show_id DC_DECLARG((fp, id, suffix))
+ FILE *fp DC_DECLSEP
+ int id DC_DECLSEP
+ const char *suffix DC_DECLEND
+{
+ if (isgraph(id))
+ fprintf(fp, "'%c' (%#o)%s", id, id, suffix);
+ else
+ fprintf(fp, "%#o%s", id, suffix);
+}
+
+
+/* report that corrupt data has been detected;
+ * use the msg and regid (if nonnegative) to give information
+ * about where the garbage was found,
+ *
+ * will abort() so that a debugger might be used to help find
+ * the bug
+ */
+/* If this routine is called, then there is a bug in the code;
+ * i.e. it is _not_ a data or user error
+ */
+void
+dc_garbage DC_DECLARG((msg, regid))
+ const char *msg DC_DECLSEP
+ int regid DC_DECLEND
+{
+ if (regid < 0) {
+ fprintf(stderr, "%s: garbage %s\n", progname, msg);
+ } else {
+ fprintf(stderr, "%s:%s register ", progname, msg);
+ dc_show_id(stderr, regid, " is garbage\n");
+ }
+ abort();
+}
+
+
+/* call system() with the passed string;
+ * if the string contains a newline, terminate the string
+ * there before calling system.
+ * Return a pointer to the first unused character in the string
+ * (i.e. past the '\n' if there was one, to the '\0' otherwise).
+ */
+const char *
+dc_system DC_DECLARG((s))
+ const char *s DC_DECLEND
+{
+ const char *p;
+ char *tmpstr;
+ size_t len;
+
+ p = strchr(s, '\n');
+ if (p) {
+ len = p - s;
+ tmpstr = dc_malloc(len + 1);
+ strncpy(tmpstr, s, len);
+ tmpstr[len] = '\0';
+ system(tmpstr);
+ free(tmpstr);
+ return p + 1;
+ }
+ system(s);
+ return s + strlen(s);
+}
+
+
+/* print out the indicated value */
+void
+dc_print DC_DECLARG((value, obase, newline_p, discard_p))
+ dc_data value DC_DECLSEP
+ int obase DC_DECLSEP
+ dc_newline newline_p DC_DECLSEP
+ dc_discard discard_p DC_DECLEND
+{
+ if (value.dc_type == DC_NUMBER) {
+ dc_out_num(value.v.number, obase, newline_p, discard_p);
+ } else if (value.dc_type == DC_STRING) {
+ dc_out_str(value.v.string, newline_p, discard_p);
+ } else {
+ dc_garbage("in data being printed", -1);
+ }
+}
+
+/* return a duplicate of the passed value, regardless of type */
+dc_data
+dc_dup DC_DECLARG((value))
+ dc_data value DC_DECLEND
+{
+ if (value.dc_type!=DC_NUMBER && value.dc_type!=DC_STRING)
+ dc_garbage("in value being duplicated", -1);
+ if (value.dc_type == DC_NUMBER)
+ return dc_dup_num(value.v.number);
+ /*else*/
+ return dc_dup_str(value.v.string);
+}
diff --git a/dc/numeric.c b/dc/numeric.c
new file mode 100644
index 0000000..6086be5
--- /dev/null
+++ b/dc/numeric.c
@@ -0,0 +1,600 @@
+/*
+ * interface dc to the bc numeric routines
+ *
+ * Copyright (C) 1994, 1997, 1998, 2000 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can either send email to this
+ * program's author (see below) or write to:
+ * The Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330
+ * Boston, MA 02111 USA
+ */
+
+/* This should be the only module that knows the internals of type dc_num */
+/* In this particular implementation we just slather out some glue and
+ * make use of bc's numeric routines.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#else
+# define UCHAR_MAX ((unsigned char)~0)
+#endif
+#include <stdlib.h>
+#include "number.h"
+#include "dc.h"
+#include "dc-proto.h"
+
+#ifdef __GNUC__
+# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__-0 >= 7)
+# define ATTRIB(x) __attribute__(x)
+# endif
+#endif
+#ifndef ATTRIB
+# define ATTRIB(x)
+#endif
+
+/* Forward prototype */
+static void out_char (int);
+
+/* there is no POSIX standard for dc, so we'll take the GNU definitions */
+int std_only = FALSE;
+
+/* convert an opaque dc_num into a real bc_num */
+#define CastNum(x) ((bc_num)(x))
+
+/* add two dc_nums, place into *result;
+ * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error
+ */
+int
+dc_add DC_DECLARG((a, b, kscale, result))
+ dc_num a DC_DECLSEP
+ dc_num b DC_DECLSEP
+ int kscale ATTRIB((unused)) DC_DECLSEP
+ dc_num *result DC_DECLEND
+{
+ bc_init_num((bc_num *)result);
+ bc_add(CastNum(a), CastNum(b), (bc_num *)result, 0);
+ return DC_SUCCESS;
+}
+
+/* subtract two dc_nums, place into *result;
+ * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error
+ */
+int
+dc_sub DC_DECLARG((a, b, kscale, result))
+ dc_num a DC_DECLSEP
+ dc_num b DC_DECLSEP
+ int kscale ATTRIB((unused)) DC_DECLSEP
+ dc_num *result DC_DECLEND
+{
+ bc_init_num((bc_num *)result);
+ bc_sub(CastNum(a), CastNum(b), (bc_num *)result, 0);
+ return DC_SUCCESS;
+}
+
+/* multiply two dc_nums, place into *result;
+ * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error
+ */
+int
+dc_mul DC_DECLARG((a, b, kscale, result))
+ dc_num a DC_DECLSEP
+ dc_num b DC_DECLSEP
+ int kscale DC_DECLSEP
+ dc_num *result DC_DECLEND
+{
+ bc_init_num((bc_num *)result);
+ bc_multiply(CastNum(a), CastNum(b), (bc_num *)result, kscale);
+ return DC_SUCCESS;
+}
+
+/* divide two dc_nums, place into *result;
+ * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error
+ */
+int
+dc_div DC_DECLARG((a, b, kscale, result))
+ dc_num a DC_DECLSEP
+ dc_num b DC_DECLSEP
+ int kscale DC_DECLSEP
+ dc_num *result DC_DECLEND
+{
+ bc_init_num((bc_num *)result);
+ if (bc_divide(CastNum(a), CastNum(b), (bc_num *)result, kscale)){
+ fprintf(stderr, "%s: divide by zero\n", progname);
+ return DC_DOMAIN_ERROR;
+ }
+ return DC_SUCCESS;
+}
+
+/* divide two dc_nums, place quotient into *quotient and remainder
+ * into *remainder;
+ * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error
+ */
+int
+dc_divrem DC_DECLARG((a, b, kscale, quotient, remainder))
+ dc_num a DC_DECLSEP
+ dc_num b DC_DECLSEP
+ int kscale DC_DECLSEP
+ dc_num *quotient DC_DECLSEP
+ dc_num *remainder DC_DECLEND
+{
+ bc_init_num((bc_num *)quotient);
+ bc_init_num((bc_num *)remainder);
+ if (bc_divmod(CastNum(a), CastNum(b),
+ (bc_num *)quotient, (bc_num *)remainder, kscale)){
+ fprintf(stderr, "%s: divide by zero\n", progname);
+ return DC_DOMAIN_ERROR;
+ }
+ return DC_SUCCESS;
+}
+
+/* place the reminder of dividing a by b into *result;
+ * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error
+ */
+int
+dc_rem DC_DECLARG((a, b, kscale, result))
+ dc_num a DC_DECLSEP
+ dc_num b DC_DECLSEP
+ int kscale DC_DECLSEP
+ dc_num *result DC_DECLEND
+{
+ bc_init_num((bc_num *)result);
+ if (bc_modulo(CastNum(a), CastNum(b), (bc_num *)result, kscale)){
+ fprintf(stderr, "%s: remainder by zero\n", progname);
+ return DC_DOMAIN_ERROR;
+ }
+ return DC_SUCCESS;
+}
+
+int
+dc_modexp DC_DECLARG((base, expo, mod, kscale, result))
+ dc_num base DC_DECLSEP
+ dc_num expo DC_DECLSEP
+ dc_num mod DC_DECLSEP
+ int kscale DC_DECLSEP
+ dc_num *result DC_DECLEND
+{
+ bc_init_num((bc_num *)result);
+ if (bc_raisemod(CastNum(base), CastNum(expo), CastNum(mod),
+ (bc_num *)result, kscale)){
+ if (bc_is_zero(CastNum(mod)))
+ fprintf(stderr, "%s: remainder by zero\n", progname);
+ return DC_DOMAIN_ERROR;
+ }
+ return DC_SUCCESS;
+}
+
+/* place the result of exponentiationg a by b into *result;
+ * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error
+ */
+int
+dc_exp DC_DECLARG((a, b, kscale, result))
+ dc_num a DC_DECLSEP
+ dc_num b DC_DECLSEP
+ int kscale DC_DECLSEP
+ dc_num *result DC_DECLEND
+{
+ bc_init_num((bc_num *)result);
+ bc_raise(CastNum(a), CastNum(b), (bc_num *)result, kscale);
+ return DC_SUCCESS;
+}
+
+/* take the square root of the value, place into *result;
+ * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error
+ */
+int
+dc_sqrt DC_DECLARG((value, kscale, result))
+ dc_num value DC_DECLSEP
+ int kscale DC_DECLSEP
+ dc_num *result DC_DECLEND
+{
+ bc_num tmp;
+
+ tmp = bc_copy_num(CastNum(value));
+ if (!bc_sqrt(&tmp, kscale)){
+ fprintf(stderr, "%s: square root of negative number\n", progname);
+ bc_free_num(&tmp);
+ return DC_DOMAIN_ERROR;
+ }
+ *((bc_num *)result) = tmp;
+ return DC_SUCCESS;
+}
+
+/* compare dc_nums a and b;
+ * return a negative value if a < b;
+ * return a positive value if a > b;
+ * return zero value if a == b
+ */
+int
+dc_compare DC_DECLARG((a, b))
+ dc_num a DC_DECLSEP
+ dc_num b DC_DECLEND
+{
+ return bc_compare(CastNum(a), CastNum(b));
+}
+
+/* attempt to convert a dc_num to its corresponding int value
+ * If discard_p is DC_TOSS then deallocate the value after use.
+ */
+int
+dc_num2int DC_DECLARG((value, discard_p))
+ dc_num value DC_DECLSEP
+ dc_discard discard_p DC_DECLEND
+{
+ long result;
+
+ result = bc_num2long(CastNum(value));
+ if (discard_p == DC_TOSS)
+ dc_free_num(&value);
+ return (int)result;
+}
+
+/* convert a C integer value into a dc_num */
+/* For convenience of the caller, package the dc_num
+ * into a dc_data result.
+ */
+dc_data
+dc_int2data DC_DECLARG((value))
+ int value DC_DECLEND
+{
+ dc_data result;
+
+ bc_init_num((bc_num *)&result.v.number);
+ bc_int2num((bc_num *)&result.v.number, value);
+ result.dc_type = DC_NUMBER;
+ return result;
+}
+
+/* get a dc_num from some input stream;
+ * input is a function which knows how to read the desired input stream
+ * ibase is the input base (2<=ibase<=DC_IBASE_MAX)
+ * *readahead will be set to the readahead character consumed while
+ * looking for the end-of-number
+ */
+/* For convenience of the caller, package the dc_num
+ * into a dc_data result.
+ */
+dc_data
+dc_getnum DC_DECLARG((input, ibase, readahead))
+ int (*input) DC_PROTO((void)) DC_DECLSEP
+ int ibase DC_DECLSEP
+ int *readahead DC_DECLEND
+{
+ bc_num base;
+ bc_num result;
+ bc_num build;
+ bc_num tmp;
+ bc_num divisor;
+ dc_data full_result;
+ int negative = 0;
+ int digit;
+ int decimal;
+ int c;
+
+ bc_init_num(&tmp);
+ bc_init_num(&build);
+ bc_init_num(&base);
+ result = bc_copy_num(_zero_);
+ bc_int2num(&base, ibase);
+ c = (*input)();
+ while (isspace(c))
+ c = (*input)();
+ if (c == '_' || c == '-'){
+ negative = c;
+ c = (*input)();
+ }else if (c == '+'){
+ c = (*input)();
+ }
+ while (isspace(c))
+ c = (*input)();
+ for (;;){
+ if (isdigit(c))
+ digit = c - '0';
+ else if ('A' <= c && c <= 'F')
+ digit = 10 + c - 'A';
+ else
+ break;
+ c = (*input)();
+ bc_int2num(&tmp, digit);
+ bc_multiply(result, base, &result, 0);
+ bc_add(result, tmp, &result, 0);
+ }
+ if (c == '.'){
+ bc_free_num(&build);
+ bc_free_num(&tmp);
+ divisor = bc_copy_num(_one_);
+ build = bc_copy_num(_zero_);
+ decimal = 0;
+ for (;;){
+ c = (*input)();
+ if (isdigit(c))
+ digit = c - '0';
+ else if ('A' <= c && c <= 'F')
+ digit = 10 + c - 'A';
+ else
+ break;
+ bc_int2num(&tmp, digit);
+ bc_multiply(build, base, &build, 0);
+ bc_add(build, tmp, &build, 0);
+ bc_multiply(divisor, base, &divisor, 0);
+ ++decimal;
+ }
+ bc_divide(build, divisor, &build, decimal);
+ bc_add(result, build, &result, 0);
+ }
+ /* Final work. */
+ if (negative)
+ bc_sub(_zero_, result, &result, 0);
+
+ bc_free_num(&tmp);
+ bc_free_num(&build);
+ bc_free_num(&base);
+ if (readahead)
+ *readahead = c;
+ full_result.v.number = (dc_num)result;
+ full_result.dc_type = DC_NUMBER;
+ return full_result;
+}
+
+
+/* return the "length" of the number */
+int
+dc_numlen DC_DECLARG((value))
+ dc_num value DC_DECLEND
+{
+ bc_num num = CastNum(value);
+
+ /* is this right??? */
+ return num->n_len + num->n_scale - (*num->n_value == '\0');
+}
+
+/* return the scale factor of the passed dc_num
+ * If discard_p is DC_TOSS then deallocate the value after use.
+ */
+int
+dc_tell_scale DC_DECLARG((value, discard_p))
+ dc_num value DC_DECLSEP
+ dc_discard discard_p DC_DECLEND
+{
+ int kscale;
+
+ kscale = CastNum(value)->n_scale;
+ if (discard_p == DC_TOSS)
+ dc_free_num(&value);
+ return kscale;
+}
+
+
+/* initialize the math subsystem */
+void
+dc_math_init DC_DECLVOID()
+{
+ bc_init_numbers();
+}
+
+/* print out a dc_num in output base obase to stdout;
+ * if newline_p is DC_WITHNL, terminate output with a '\n';
+ * if discard_p is DC_TOSS then deallocate the value after use
+ */
+void
+dc_out_num DC_DECLARG((value, obase, newline_p, discard_p))
+ dc_num value DC_DECLSEP
+ int obase DC_DECLSEP
+ dc_newline newline_p DC_DECLSEP
+ dc_discard discard_p DC_DECLEND
+{
+ out_char('\0'); /* clear the column counter */
+ bc_out_num(CastNum(value), obase, out_char, 0);
+ if (newline_p == DC_WITHNL)
+ putchar ('\n');
+ if (discard_p == DC_TOSS)
+ dc_free_num(&value);
+}
+
+/* dump out the absolute value of the integer part of a
+ * dc_num as a byte stream, without any line wrapping;
+ * if discard_p is DC_TOSS then deallocate the value after use
+ */
+void
+dc_dump_num DC_DECLARG((dcvalue, discard_p))
+ dc_num dcvalue DC_DECLSEP
+ dc_discard discard_p DC_DECLEND
+{
+ struct digit_stack { int digit; struct digit_stack *link;};
+ struct digit_stack *top_of_stack = NULL;
+ struct digit_stack *cur;
+ struct digit_stack *next;
+ bc_num value;
+ bc_num obase;
+ bc_num digit;
+
+ bc_init_num(&value);
+ bc_init_num(&obase);
+ bc_init_num(&digit);
+
+ /* we only handle the integer portion: */
+ bc_divide(CastNum(dcvalue), _one_, &value, 0);
+ /* we only handle the absolute value: */
+ value->n_sign = PLUS;
+ /* we're done with the dcvalue parameter: */
+ if (discard_p == DC_TOSS)
+ dc_free_num(&dcvalue);
+
+ bc_int2num(&obase, 1+UCHAR_MAX);
+ do {
+ (void) bc_divmod(value, obase, &value, &digit, 0);
+ cur = dc_malloc(sizeof *cur);
+ cur->digit = (int)bc_num2long(digit);
+ cur->link = top_of_stack;
+ top_of_stack = cur;
+ } while (!bc_is_zero(value));
+
+ for (cur=top_of_stack; cur; cur=next) {
+ putchar(cur->digit);
+ next = cur->link;
+ free(cur);
+ }
+
+ bc_free_num(&digit);
+ bc_free_num(&obase);
+ bc_free_num(&value);
+}
+
+/* deallocate an instance of a dc_num */
+void
+dc_free_num DC_DECLARG((value))
+ dc_num *value DC_DECLEND
+{
+ bc_free_num((bc_num *)value);
+}
+
+/* return a duplicate of the number in the passed value */
+/* The mismatched data types forces the caller to deal with
+ * bad dc_type'd dc_data values, and makes it more convenient
+ * for the caller to not have to do the grunge work of setting
+ * up a dc_type result.
+ */
+dc_data
+dc_dup_num DC_DECLARG((value))
+ dc_num value DC_DECLEND
+{
+ dc_data result;
+
+ ++CastNum(value)->n_refs;
+ result.v.number = value;
+ result.dc_type = DC_NUMBER;
+ return result;
+}
+
+
+
+/*---------------------------------------------------------------------------\
+| The rest of this file consists of stubs for bc routines called by numeric.c|
+| so as to minimize the amount of bc code needed to build dc. |
+| The bulk of the code was just lifted straight out of the bc source. |
+\---------------------------------------------------------------------------*/
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+
+int out_col = 0;
+
+/* Output routines: Write a character CH to the standard output.
+ It keeps track of the number of characters output and may
+ break the output with a "\<cr>". */
+
+static void
+out_char (ch)
+ int ch;
+{
+
+ if (ch == '\0')
+ {
+ out_col = 0;
+ }
+ else
+ {
+ out_col++;
+ if (out_col == 70)
+ {
+ putchar ('\\');
+ putchar ('\n');
+ out_col = 1;
+ }
+ putchar (ch);
+ }
+}
+
+/* Malloc could not get enough memory. */
+
+void
+out_of_memory()
+{
+ dc_memfail();
+}
+
+/* Runtime error will print a message and stop the machine. */
+
+#ifdef HAVE_STDARG_H
+#ifdef __STDC__
+void
+rt_error (char *mesg, ...)
+#else
+void
+rt_error (mesg)
+ char *mesg;
+#endif
+#else
+void
+rt_error (mesg, va_alist)
+ char *mesg;
+#endif
+{
+ va_list args;
+
+ fprintf (stderr, "Runtime error: ");
+#ifdef HAVE_STDARG_H
+ va_start (args, mesg);
+#else
+ va_start (args);
+#endif
+ vfprintf (stderr, mesg, args);
+ va_end (args);
+ fprintf (stderr, "\n");
+}
+
+
+/* A runtime warning tells of some action taken by the processor that
+ may change the program execution but was not enough of a problem
+ to stop the execution. */
+
+#ifdef HAVE_STDARG_H
+#ifdef __STDC__
+void
+rt_warn (char *mesg, ...)
+#else
+void
+rt_warn (mesg)
+ char *mesg;
+#endif
+#else
+void
+rt_warn (mesg, va_alist)
+ char *mesg;
+#endif
+{
+ va_list args;
+
+ fprintf (stderr, "Runtime warning: ");
+#ifdef HAVE_STDARG_H
+ va_start (args, mesg);
+#else
+ va_start (args);
+#endif
+ vfprintf (stderr, mesg, args);
+ va_end (args);
+ fprintf (stderr, "\n");
+}
diff --git a/dc/stack.c b/dc/stack.c
new file mode 100644
index 0000000..1d8a9bf
--- /dev/null
+++ b/dc/stack.c
@@ -0,0 +1,494 @@
+/*
+ * implement stack functions for dc
+ *
+ * Copyright (C) 1994, 1997, 1998 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can either send email to this
+ * program's author (see below) or write to:
+ *
+ * The Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330
+ * Boston, MA 02111 USA
+ */
+
+/* This module is the only one that knows what stacks (both the
+ * regular evaluation stack and the named register stacks)
+ * look like.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#include "dc.h"
+#include "dc-proto.h"
+#include "dc-regdef.h"
+
+/* an oft-used error message: */
+#define Empty_Stack fprintf(stderr, "%s: stack empty\n", progname)
+
+
+/* simple linked-list implementaion suffices: */
+struct dc_list {
+ dc_data value;
+ struct dc_array *array; /* opaque */
+ struct dc_list *link;
+};
+typedef struct dc_list dc_list;
+
+/* the anonymous evaluation stack */
+static dc_list *dc_stack=NULL;
+
+/* the named register stacks */
+static dc_list *dc_register[DC_REGCOUNT];
+
+
+/* allocate a new dc_list item */
+static dc_list *
+dc_alloc DC_DECLVOID()
+{
+ dc_list *result;
+
+ result = dc_malloc(sizeof *result);
+ result->value.dc_type = DC_UNINITIALIZED;
+ result->array = NULL;
+ result->link = NULL;
+ return result;
+}
+
+
+/* check that there are two numbers on top of the stack,
+ * then call op with the popped numbers. Construct a dc_data
+ * value from the dc_num returned by op and push it
+ * on the stack.
+ * If the op call doesn't return DC_SUCCESS, then leave the stack
+ * unmodified.
+ */
+void
+dc_binop DC_DECLARG((op, kscale))
+ int (*op)DC_PROTO((dc_num, dc_num, int, dc_num *)) DC_DECLSEP
+ int kscale DC_DECLEND
+{
+ dc_data a;
+ dc_data b;
+ dc_data r;
+
+ if (!dc_stack || !dc_stack->link){
+ Empty_Stack;
+ return;
+ }
+ if (dc_stack->value.dc_type!=DC_NUMBER
+ || dc_stack->link->value.dc_type!=DC_NUMBER){
+ fprintf(stderr, "%s: non-numeric value\n", progname);
+ return;
+ }
+ (void)dc_pop(&b);
+ (void)dc_pop(&a);
+ if ((*op)(a.v.number, b.v.number, kscale, &r.v.number) == DC_SUCCESS){
+ r.dc_type = DC_NUMBER;
+ dc_push(r);
+ dc_free_num(&a.v.number);
+ dc_free_num(&b.v.number);
+ }else{
+ /* op failed; restore the stack */
+ dc_push(a);
+ dc_push(b);
+ }
+}
+
+/* check that there are two numbers on top of the stack,
+ * then call op with the popped numbers. Construct two dc_data
+ * values from the dc_num's returned by op and push them
+ * on the stack.
+ * If the op call doesn't return DC_SUCCESS, then leave the stack
+ * unmodified.
+ */
+void
+dc_binop2 DC_DECLARG((op, kscale))
+ int (*op)DC_PROTO((dc_num, dc_num, int, dc_num *, dc_num *)) DC_DECLSEP
+ int kscale DC_DECLEND
+{
+ dc_data a;
+ dc_data b;
+ dc_data r1;
+ dc_data r2;
+
+ if (!dc_stack || !dc_stack->link){
+ Empty_Stack;
+ return;
+ }
+ if (dc_stack->value.dc_type!=DC_NUMBER
+ || dc_stack->link->value.dc_type!=DC_NUMBER){
+ fprintf(stderr, "%s: non-numeric value\n", progname);
+ return;
+ }
+ (void)dc_pop(&b);
+ (void)dc_pop(&a);
+ if ((*op)(a.v.number, b.v.number, kscale,
+ &r1.v.number, &r2.v.number) == DC_SUCCESS){
+ r1.dc_type = DC_NUMBER;
+ dc_push(r1);
+ r2.dc_type = DC_NUMBER;
+ dc_push(r2);
+ dc_free_num(&a.v.number);
+ dc_free_num(&b.v.number);
+ }else{
+ /* op failed; restore the stack */
+ dc_push(a);
+ dc_push(b);
+ }
+}
+
+/* check that there are two numbers on top of the stack,
+ * then call dc_compare with the popped numbers.
+ * Return negative, zero, or positive based on the ordering
+ * of the two numbers.
+ */
+int
+dc_cmpop DC_DECLVOID()
+{
+ int result;
+ dc_data a;
+ dc_data b;
+
+ if (!dc_stack || !dc_stack->link){
+ Empty_Stack;
+ return 0;
+ }
+ if (dc_stack->value.dc_type!=DC_NUMBER
+ || dc_stack->link->value.dc_type!=DC_NUMBER){
+ fprintf(stderr, "%s: non-numeric value\n", progname);
+ return 0;
+ }
+ (void)dc_pop(&b);
+ (void)dc_pop(&a);
+ result = dc_compare(b.v.number, a.v.number);
+ dc_free_num(&a.v.number);
+ dc_free_num(&b.v.number);
+ return result;
+}
+
+/* check that there are three numbers on top of the stack,
+ * then call op with the popped numbers. Construct a dc_data
+ * value from the dc_num returned by op and push it
+ * on the stack.
+ * If the op call doesn't return DC_SUCCESS, then leave the stack
+ * unmodified.
+ */
+void
+dc_triop DC_DECLARG((op, kscale))
+ int (*op)DC_PROTO((dc_num, dc_num, dc_num, int, dc_num *)) DC_DECLSEP
+ int kscale DC_DECLEND
+{
+ dc_data a;
+ dc_data b;
+ dc_data c;
+ dc_data r;
+
+ if (!dc_stack || !dc_stack->link || !dc_stack->link->link){
+ Empty_Stack;
+ return;
+ }
+ if (dc_stack->value.dc_type!=DC_NUMBER
+ || dc_stack->link->value.dc_type!=DC_NUMBER
+ || dc_stack->link->link->value.dc_type!=DC_NUMBER){
+ fprintf(stderr, "%s: non-numeric value\n", progname);
+ return;
+ }
+ (void)dc_pop(&c);
+ (void)dc_pop(&b);
+ (void)dc_pop(&a);
+ if ((*op)(a.v.number, b.v.number, c.v.number,
+ kscale, &r.v.number) == DC_SUCCESS){
+ r.dc_type = DC_NUMBER;
+ dc_push(r);
+ dc_free_num(&a.v.number);
+ dc_free_num(&b.v.number);
+ dc_free_num(&c.v.number);
+ }else{
+ /* op failed; restore the stack */
+ dc_push(a);
+ dc_push(b);
+ dc_push(c);
+ }
+}
+
+
+/* initialize the register stacks to their initial values */
+void
+dc_register_init DC_DECLVOID()
+{
+ int i;
+
+ for (i=0; i<DC_REGCOUNT; ++i)
+ dc_register[i] = NULL;
+}
+
+/* clear the evaluation stack */
+void
+dc_clear_stack DC_DECLVOID()
+{
+ dc_list *n;
+ dc_list *t;
+
+ for (n=dc_stack; n; n=t){
+ t = n->link;
+ if (n->value.dc_type == DC_NUMBER)
+ dc_free_num(&n->value.v.number);
+ else if (n->value.dc_type == DC_STRING)
+ dc_free_str(&n->value.v.string);
+ else
+ dc_garbage("in stack", -1);
+ dc_array_free(n->array);
+ free(n);
+ }
+ dc_stack = NULL;
+}
+
+/* push a value onto the evaluation stack */
+void
+dc_push DC_DECLARG((value))
+ dc_data value DC_DECLEND
+{
+ dc_list *n = dc_alloc();
+
+ if (value.dc_type!=DC_NUMBER && value.dc_type!=DC_STRING)
+ dc_garbage("in data being pushed", -1);
+ n->value = value;
+ n->link = dc_stack;
+ dc_stack = n;
+}
+
+/* push a value onto the named register stack */
+void
+dc_register_push DC_DECLARG((stackid, value))
+ int stackid DC_DECLSEP
+ dc_data value DC_DECLEND
+{
+ dc_list *n = dc_alloc();
+
+ stackid = regmap(stackid);
+ n->value = value;
+ n->link = dc_register[stackid];
+ dc_register[stackid] = n;
+}
+
+/* set *result to the value on the top of the evaluation stack */
+/* The caller is responsible for duplicating the value if it
+ * is to be maintained as anything more than a transient identity.
+ *
+ * DC_FAIL is returned if the stack is empty (and *result unchanged),
+ * DC_SUCCESS is returned otherwise
+ */
+int
+dc_top_of_stack DC_DECLARG((result))
+ dc_data *result DC_DECLEND
+{
+ if (!dc_stack){
+ Empty_Stack;
+ return DC_FAIL;
+ }
+ if (dc_stack->value.dc_type!=DC_NUMBER
+ && dc_stack->value.dc_type!=DC_STRING)
+ dc_garbage("at top of stack", -1);
+ *result = dc_stack->value;
+ return DC_SUCCESS;
+}
+
+/* set *result to a dup of the value on the top of the named register stack */
+/*
+ * DC_FAIL is returned if the named stack is empty (and *result unchanged),
+ * DC_SUCCESS is returned otherwise
+ */
+int
+dc_register_get DC_DECLARG((regid, result))
+ int regid DC_DECLSEP
+ dc_data *result DC_DECLEND
+{
+ dc_list *r;
+
+ regid = regmap(regid);
+ r = dc_register[regid];
+ if ( ! r ){
+ fprintf(stderr, "%s: register ", progname);
+ dc_show_id(stderr, regid, " is empty\n");
+ return DC_FAIL;
+ }
+ *result = dc_dup(r->value);
+ return DC_SUCCESS;
+}
+
+/* set the top of the named register stack to the indicated value */
+/* If the named stack is empty, craft a stack entry to enter the
+ * value into.
+ */
+void
+dc_register_set DC_DECLARG((regid, value))
+ int regid DC_DECLSEP
+ dc_data value DC_DECLEND
+{
+ dc_list *r;
+
+ regid = regmap(regid);
+ r = dc_register[regid];
+ if ( ! r )
+ dc_register[regid] = dc_alloc();
+ else if (r->value.dc_type == DC_NUMBER)
+ dc_free_num(&r->value.v.number);
+ else if (r->value.dc_type == DC_STRING)
+ dc_free_str(&r->value.v.string);
+ else if (r->value.dc_type == DC_UNINITIALIZED)
+ ;
+ else
+ dc_garbage("", regid);
+ dc_register[regid]->value = value;
+}
+
+/* pop from the evaluation stack
+ *
+ * DC_FAIL is returned if the stack is empty (and *result unchanged),
+ * DC_SUCCESS is returned otherwise
+ */
+int
+dc_pop DC_DECLARG((result))
+ dc_data *result DC_DECLEND
+{
+ dc_list *r;
+
+ r = dc_stack;
+ if (!r){
+ Empty_Stack;
+ return DC_FAIL;
+ }
+ if (r->value.dc_type!=DC_NUMBER && r->value.dc_type!=DC_STRING)
+ dc_garbage("at top of stack", -1);
+ *result = r->value;
+ dc_stack = r->link;
+ dc_array_free(r->array);
+ free(r);
+ return DC_SUCCESS;
+}
+
+/* pop from the named register stack
+ *
+ * DC_FAIL is returned if the named stack is empty (and *result unchanged),
+ * DC_SUCCESS is returned otherwise
+ */
+int
+dc_register_pop DC_DECLARG((stackid, result))
+ int stackid DC_DECLSEP
+ dc_data *result DC_DECLEND
+{
+ dc_list *r;
+
+ stackid = regmap(stackid);
+ r = dc_register[stackid];
+ if (!r){
+ fprintf(stderr, "%s: stack register ", progname);
+ dc_show_id(stderr, stackid, " is empty\n");
+ return DC_FAIL;
+ }
+ if (r->value.dc_type!=DC_NUMBER && r->value.dc_type!=DC_STRING)
+ dc_garbage(" stack", stackid);
+ *result = r->value;
+ dc_register[stackid] = r->link;
+ dc_array_free(r->array);
+ free(r);
+ return DC_SUCCESS;
+}
+
+
+/* tell how many entries are currently on the evaluation stack */
+int
+dc_tell_stackdepth DC_DECLVOID()
+{
+ dc_list *n;
+ int depth=0;
+
+ for (n=dc_stack; n; n=n->link)
+ ++depth;
+ return depth;
+}
+
+
+/* return the length of the indicated data value;
+ * if discard_p is DC_TOSS, the deallocate the value when done
+ *
+ * The definition of a datum's length is deligated to the
+ * appropriate module.
+ */
+int
+dc_tell_length DC_DECLARG((value, discard_p))
+ dc_data value DC_DECLSEP
+ dc_discard discard_p DC_DECLEND
+{
+ int length;
+
+ if (value.dc_type == DC_NUMBER){
+ length = dc_numlen(value.v.number);
+ if (discard_p == DC_TOSS)
+ dc_free_num(&value.v.number);
+ } else if (value.dc_type == DC_STRING) {
+ length = dc_strlen(value.v.string);
+ if (discard_p == DC_TOSS)
+ dc_free_str(&value.v.string);
+ } else {
+ dc_garbage("in tell_length", -1);
+ /*NOTREACHED*/
+ length = 0; /*just to suppress spurious compiler warnings*/
+ }
+ return length;
+}
+
+
+
+/* print out all of the values on the evaluation stack */
+void
+dc_printall DC_DECLARG((obase))
+ int obase DC_DECLEND
+{
+ dc_list *n;
+
+ for (n=dc_stack; n; n=n->link)
+ dc_print(n->value, obase, DC_WITHNL, DC_KEEP);
+}
+
+
+
+
+/* get the current array head for the named array */
+struct dc_array *
+dc_get_stacked_array DC_DECLARG((array_id))
+ int array_id DC_DECLEND
+{
+ dc_list *r = dc_register[regmap(array_id)];
+ return r ? r->array : NULL;
+}
+
+/* set the current array head for the named array */
+void
+dc_set_stacked_array DC_DECLARG((array_id, new_head))
+ int array_id DC_DECLSEP
+ struct dc_array *new_head DC_DECLEND
+{
+ dc_list *r;
+
+ array_id = regmap(array_id);
+ r = dc_register[array_id];
+ if ( ! r )
+ r = dc_register[array_id] = dc_alloc();
+ r->array = new_head;
+}
diff --git a/dc/string.c b/dc/string.c
new file mode 100644
index 0000000..a7f79a4
--- /dev/null
+++ b/dc/string.c
@@ -0,0 +1,211 @@
+/*
+ * implement string functions for dc
+ *
+ * Copyright (C) 1994, 1997, 1998 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can either send email to this
+ * program's author (see below) or write to:
+ *
+ * The Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330
+ * Boston, MA 02111 USA
+ */
+
+/* This should be the only module that knows the internals of type dc_string */
+
+#include "config.h"
+
+#include <stdio.h>
+#ifdef HAVE_STDDEF_H
+# include <stddef.h> /* ptrdiff_t */
+#else
+# define ptrdiff_t size_t
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+# include <string.h> /* memcpy */
+#else
+# ifdef HAVE_MEMORY_H
+# include <memory.h> /* memcpy, maybe */
+# else
+# ifdef HAVE_STRINGS_H
+# include <strings.h> /* memcpy, maybe */
+# endif
+# endif
+#endif
+#include "dc.h"
+#include "dc-proto.h"
+
+/* here is the completion of the dc_string type: */
+struct dc_string {
+ char *s_ptr; /* pointer to base of string */
+ size_t s_len; /* length of counted string */
+ int s_refs; /* reference count to cut down on memory use by duplicates */
+};
+
+
+/* return a duplicate of the string in the passed value */
+/* The mismatched data types forces the caller to deal with
+ * bad dc_type'd dc_data values, and makes it more convenient
+ * for the caller to not have to do the grunge work of setting
+ * up a dc_type result.
+ */
+dc_data
+dc_dup_str DC_DECLARG((value))
+ dc_str value DC_DECLEND
+{
+ dc_data result;
+
+ ++value->s_refs;
+ result.v.string = value;
+ result.dc_type = DC_STRING;
+ return result;
+}
+
+/* free an instance of a dc_str value */
+void
+dc_free_str DC_DECLARG((value))
+ dc_str *value DC_DECLEND
+{
+ struct dc_string *string = *value;
+
+ if (--string->s_refs < 1){
+ free(string->s_ptr);
+ free(string);
+ }
+}
+
+/* Output a dc_str value.
+ * Add a trailing newline if "newline" is set.
+ * Free the value after use if discard_flag is set.
+ */
+void
+dc_out_str DC_DECLARG((value, newline, discard_flag))
+ dc_str value DC_DECLSEP
+ dc_newline newline DC_DECLSEP
+ dc_discard discard_flag DC_DECLEND
+{
+ fwrite(value->s_ptr, value->s_len, sizeof *value->s_ptr, stdout);
+ if (newline == DC_WITHNL)
+ putchar('\n');
+ if (discard_flag == DC_TOSS)
+ dc_free_str(&value);
+}
+
+/* make a copy of a string (base s, length len)
+ * into a dc_str value; return a dc_data result
+ * with this value
+ */
+dc_data
+dc_makestring DC_DECLARG((s, len))
+ const char *s DC_DECLSEP
+ size_t len DC_DECLEND
+{
+ dc_data result;
+ struct dc_string *string;
+
+ string = dc_malloc(sizeof *string);
+ string->s_ptr = dc_malloc(len+1);
+ memcpy(string->s_ptr, s, len);
+ string->s_ptr[len] = '\0'; /* nul terminated for those who need it */
+ string->s_len = len;
+ string->s_refs = 1;
+ result.v.string = string;
+ result.dc_type = DC_STRING;
+ return result;
+}
+
+/* read a dc_str value from FILE *fp;
+ * if ldelim == rdelim, then read until a ldelim char or EOF is reached;
+ * if ldelim != rdelim, then read until a matching rdelim for the
+ * (already eaten) first ldelim is read.
+ * Return a dc_data result with the dc_str value as its contents.
+ */
+dc_data
+dc_readstring DC_DECLARG((fp, ldelim, rdelim))
+ FILE *fp DC_DECLSEP
+ int ldelim DC_DECLSEP
+ int rdelim DC_DECLEND
+{
+ static char *line_buf = NULL; /* a buffer to build the string in */
+ static size_t buflen = 0; /* the current size of line_buf */
+ int depth=1;
+ int c;
+ char *p;
+ const char *end;
+
+ if (!line_buf){
+ /* initial buflen should be large enough to handle most cases */
+ buflen = 2016;
+ line_buf = dc_malloc(buflen);
+ }
+ p = line_buf;
+ end = line_buf + buflen;
+ for (;;){
+ c = getc(fp);
+ if (c == EOF)
+ break;
+ else if (c == rdelim && --depth < 1)
+ break;
+ else if (c == ldelim)
+ ++depth;
+ if (p >= end){
+ ptrdiff_t offset = p - line_buf;
+ /* buflen increment should be big enough
+ * to avoid execessive reallocs:
+ */
+ buflen += 2048;
+ line_buf = realloc(line_buf, buflen);
+ if (!line_buf)
+ dc_memfail();
+ p = line_buf + offset;
+ end = line_buf + buflen;
+ }
+ *p++ = c;
+ }
+ return dc_makestring(line_buf, (size_t)(p-line_buf));
+}
+
+/* return the base pointer of the dc_str value;
+ * This function is needed because no one else knows what dc_str
+ * looks like.
+ */
+const char *
+dc_str2charp DC_DECLARG((value))
+ dc_str value DC_DECLEND
+{
+ return value->s_ptr;
+}
+
+/* return the length of the dc_str value;
+ * This function is needed because no one else knows what dc_str
+ * looks like, and strlen(dc_str2charp(value)) won't work
+ * if there's an embedded '\0'.
+ */
+size_t
+dc_strlen DC_DECLARG((value))
+ dc_str value DC_DECLEND
+{
+ return value->s_len;
+}
+
+
+/* initialize the strings subsystem */
+void
+dc_string_init DC_DECLVOID()
+{
+ /* nothing to do for this implementation */
+}