From 75897234bea58127dd00d961ebfe823dd8a253fb Mon Sep 17 00:00:00 2001
From: drh
Date: Mon, 29 May 2000 14:26:00 +0000
Subject: [PATCH] initial check-in of the new version (CVS 1)
FossilOrigin-Name: 6f3655f79f9b6fc9fb7baaa10a7e0f2b6a512dfa
---
Makefile.in | 105 ++
configure | 2006 +++++++++++++++++++++
configure.in | 513 ++++++
doc/lemon.html | 861 +++++++++
manifest | 35 +-
manifest.uuid | 2 +-
src/build.c | 1435 +++++++++++++++
src/dbbe.c | 510 ++++++
src/dbbe.h | 119 ++
src/main.c | 219 +++
src/shell.c | 384 ++++
src/sqlite.h | 118 ++
src/sqliteInt.h | 235 +++
src/tclsqlite.c | 241 +++
src/tokenize.c | 375 ++++
src/util.c | 445 +++++
src/vdbe.c | 1973 +++++++++++++++++++++
src/vdbe.h | 166 ++
src/where.c | 566 ++++++
tool/gdbmdump.c | 90 +
tool/lemon.c | 4021 ++++++++++++++++++++++++++++++++++++++++++
tool/lempar.c | 606 +++++++
tool/opNames.awk | 23 +
tool/opcodeDoc.awk | 23 +
tool/renumberOps.awk | 12 +
25 files changed, 15075 insertions(+), 8 deletions(-)
create mode 100644 Makefile.in
create mode 100755 configure
create mode 100644 configure.in
create mode 100644 doc/lemon.html
create mode 100644 src/build.c
create mode 100644 src/dbbe.c
create mode 100644 src/dbbe.h
create mode 100644 src/main.c
create mode 100644 src/shell.c
create mode 100644 src/sqlite.h
create mode 100644 src/sqliteInt.h
create mode 100644 src/tclsqlite.c
create mode 100644 src/tokenize.c
create mode 100644 src/util.c
create mode 100644 src/vdbe.c
create mode 100644 src/vdbe.h
create mode 100644 src/where.c
create mode 100644 tool/gdbmdump.c
create mode 100644 tool/lemon.c
create mode 100644 tool/lempar.c
create mode 100644 tool/opNames.awk
create mode 100644 tool/opcodeDoc.awk
create mode 100644 tool/renumberOps.awk
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000000..5e9db18184
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,105 @@
+#!/usr/make
+#
+# Makefile for SQLITE
+
+# The toplevel directory of the source tree
+#
+TOP = @srcdir@
+
+# C Compiler and options for use in building executables that
+# will run on the platform that is doing the build.
+#
+BCC = @BUILD_CC@ @BUILD_CFLAGS@
+
+# C Compile and options for use in building executables that
+# will run on the target platform.
+#
+TCC = @TARGET_CC@ @TARGET_CFLAGS@ -I. -I${TOP}/src
+
+# Tools used to build a static library.
+#
+AR = @TARGET_AR@
+RANLIB = @TARGET_RANLIB@
+
+# Compiler options needed for programs that use the GDBM library.
+#
+GDBM_FLAGS = @TARGET_GDBM_INC@
+
+# The library that programs using GDBM must link against.
+#
+LIBGDBM = @TARGET_GDBM_LIBS@
+
+# Compiler options needed for programs that use the readline() library.
+#
+READLINE_FLAGS = -DHAVE_READLINE=@TARGET_HAVE_READLINE@ @TARGET_READLINE_INC@
+
+# The library that programs using readline() must link against.
+#
+LIBREADLINE = @TARGET_READLINE_LIBS@
+
+# Object files for the SQLite library.
+#
+LIBOBJ = build.o dbbe.o main.o parse.o tokenize.o util.o vdbe.o where.o
+
+# This is the default Makefile target. The objects listed here
+# are what get build when you type just "make" with no arguments.
+#
+all: libsqlite.a sqlite
+# libtclsqlite.a tclsqlite
+
+libsqlite.a: $(LIBOBJ)
+ $(AR) libsqlite.a $(LIBOBJ)
+ $(RANLIB) libsqlite.a
+
+sqlite: $(TOP)/src/shell.c libsqlite.a $(TOP)/src/sqlite.h
+ $(TCC) $(READLINE_FLAGS) -o sqlite $(TOP)/src/shell.c \
+ libsqlite.a $(LIBGDBM) $(LIBREADLINE)
+
+# Rules to build the LEMON compiler generator
+#
+lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c
+ $(BCC) -o lemon $(TOP)/tool/lemon.c
+ cp $(TOP)/tool/lempar.c .
+
+# Header files used by all library source files.
+#
+HDR = \
+ $(TOP)/src/sqlite.h \
+ $(TOP)/src/sqliteInt.h \
+ $(TOP)/src/dbbe.h \
+ $(TOP)/src/vdbe.h \
+ parse.h
+
+build.o: $(TOP)/src/build.c $(HDR)
+ $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/build.c
+
+dbbe.o: $(TOP)/src/dbbe.c $(HDR)
+ $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbe.c
+
+main.o: $(TOP)/src/main.c $(HDR)
+ $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/main.c
+
+parse.o: parse.c $(HDR)
+ $(TCC) $(GDBM_FLAGS) -c parse.c
+
+parse.h: parse.c
+
+parse.c: $(TOP)/src/parse.y lemon
+ cp $(TOP)/src/parse.y .
+ ./lemon parse.y
+
+tokenize.o: $(TOP)/src/tokenize.c $(HDR)
+ $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/tokenize.c
+
+util.o: $(TOP)/src/util.c $(HDR)
+ $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/util.c
+
+vdbe.o: $(TOP)/src/vdbe.c $(HDR)
+ $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/vdbe.c
+
+where.o: $(TOP)/src/where.c $(HDR)
+ $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/where.c
+
+clean:
+ rm -f *.o sqlite libsqlite.a
+ rm -f lemon lempar.c parse.*
diff --git a/configure b/configure
new file mode 100755
index 0000000000..dbf4986751
--- /dev/null
+++ b/configure
@@ -0,0 +1,2006 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+ --with-hints=FILE Read configuration options from FILE"
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ case "$ac_option" in
+ -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) ac_optarg= ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case "$ac_option" in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir="$ac_optarg" ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build="$ac_optarg" ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file="$ac_optarg" ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir="$ac_optarg" ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ eval "enable_${ac_feature}=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix="$ac_optarg" ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he)
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+ --cache-file=FILE cache test results in FILE
+ --help print this message
+ --no-create do not create output files
+ --quiet, --silent do not print \`checking...' messages
+ --version print the version of autoconf that created configure
+Directory and file names:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [same as prefix]
+ --bindir=DIR user executables in DIR [EPREFIX/bin]
+ --sbindir=DIR system admin executables in DIR [EPREFIX/sbin]
+ --libexecdir=DIR program executables in DIR [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data in DIR
+ [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data in DIR
+ [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var]
+ --libdir=DIR object code libraries in DIR [EPREFIX/lib]
+ --includedir=DIR C header files in DIR [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include]
+ --infodir=DIR info documentation in DIR [PREFIX/info]
+ --mandir=DIR man documentation in DIR [PREFIX/man]
+ --srcdir=DIR find the sources in DIR [configure dir or ..]
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM
+ run sed PROGRAM on installed program names
+EOF
+ cat << EOF
+Host type:
+ --build=BUILD configure for building on BUILD [BUILD=HOST]
+ --host=HOST configure for HOST [guessed]
+ --target=TARGET configure for TARGET [TARGET=HOST]
+Features and packages:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+EOF
+ if test -n "$ac_help"; then
+ echo "--enable and --with options recognized:$ac_help"
+ fi
+ exit 0 ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host="$ac_optarg" ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir="$ac_optarg" ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir="$ac_optarg" ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir="$ac_optarg" ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir="$ac_optarg" ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir="$ac_optarg" ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir="$ac_optarg" ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir="$ac_optarg" ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix="$ac_optarg" ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix="$ac_optarg" ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix="$ac_optarg" ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name="$ac_optarg" ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir="$ac_optarg" ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir="$ac_optarg" ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site="$ac_optarg" ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir="$ac_optarg" ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir="$ac_optarg" ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target="$ac_optarg" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers)
+ echo "configure generated by autoconf version 2.13"
+ exit 0 ;;
+
+ -with-* | --with-*)
+ ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_${ac_package}='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ eval "with_${ac_package}=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes="$ac_optarg" ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries="$ac_optarg" ;;
+
+ -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+ ;;
+
+ *)
+ if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+ echo "configure: warning: $ac_option: invalid host type" 1>&2
+ fi
+ if test "x$nonopt" != xNONE; then
+ { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+ fi
+ nonopt="$ac_option"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+ exec 6>/dev/null
+else
+ exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+ case "$ac_arg" in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c) ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+ esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set. These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=src/sqlite.h
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_prog=$0
+ ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+ test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+ else
+ { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+ fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ echo "loading site script $ac_site_file"
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ echo "loading cache $cache_file"
+ . $cache_file
+else
+ echo "creating cache $cache_file"
+ > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+# The following RCS revision string applies to configure.in
+# $Revision: 1.1 $
+
+#########
+# Make sure we are not building in a subdirectory of the source tree.
+#
+
+temp=`echo $srcdir | grep '[^./]'`
+
+if test "$temp" = ""; then
+ { echo "configure: error:
+**************************************************************************
+** This program may not be compiled in the same directory that contains **
+** the configure script or any subdirectory of that directory. Rerun **
+** the configure script from a directory that is separate from the **
+** source tree. **
+**************************************************************************" 1>&2; exit 1; }
+fi
+
+#########
+# Set up an appropriate program prefix
+#
+if test "$program_prefix" = "NONE"; then
+ program_prefix=""
+fi
+
+
+#########
+# Check to see if the --with-hints=FILE option is used. If there is none,
+# then check for a files named "$host.hints" and ../$hosts.hints where
+# $host is the hostname of the build system. If still no hints are
+# found, try looking in $system.hints and ../$system.hints where
+# $system is the result of uname -s.
+#
+# Check whether --with-hints or --without-hints was given.
+if test "${with_hints+set}" = set; then
+ withval="$with_hints"
+ hints=$withval
+fi
+
+if test "$hints" = ""; then
+ host=`hostname | sed 's/\..*//'`
+ if test -r $host.hints; then
+ hints=$host.hints
+ else
+ if test -r ../$host.hints; then
+ hints=../$host.hints
+ fi
+ fi
+fi
+if test "$hints" = ""; then
+ sys=`uname -s`
+ if test -r $sys.hints; then
+ hints=$sys.hints
+ else
+ if test -r ../$sys.hints; then
+ hints=../$sys.hints
+ fi
+ fi
+fi
+if test "$hints" != ""; then
+ echo "$ac_t""reading hints from $hints" 1>&6
+ . $hints
+fi
+
+#########
+# Locate a compiler for the build machine. This compiler should
+# generate command-line programs that run on the build machine.
+#
+default_build_cflags="-g"
+if test "$config_BUILD_CC" = ""; then
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:602: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:632: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_prog_rejected=no
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# -gt 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ set dummy "$ac_dir/$ac_word" "$@"
+ shift
+ ac_cv_prog_CC="$@"
+ fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test -z "$CC"; then
+ case "`uname -s`" in
+ *win32* | *WIN32*)
+ # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:683: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="cl"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+ ;;
+ esac
+ fi
+ test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:715: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 726 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:731: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ ac_cv_prog_cc_works=yes
+ # If we can't run a trivial program, we are probably using a cross compiler.
+ if (./conftest; exit) 2>/dev/null; then
+ ac_cv_prog_cc_cross=no
+ else
+ ac_cv_prog_cc_cross=yes
+ fi
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+ { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:757: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:762: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+ ac_cv_prog_gcc=yes
+else
+ ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:790: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_g=yes
+else
+ ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+
+ if test "$cross_compiling" = "yes"; then
+ { echo "configure: error: unable to find a compiler for building build tools" 1>&2; exit 1; }
+ fi
+ BUILD_CC=$CC
+ default_build_cflags=$CFLAGS
+else
+ BUILD_CC=$config_BUILD_CC
+ echo $ac_n "checking host compiler""... $ac_c" 1>&6
+echo "configure:829: checking host compiler" >&5
+ CC=$BUILD_CC
+ echo "$ac_t""$BUILD_CC" 1>&6
+fi
+echo $ac_n "checking switches for the host compiler""... $ac_c" 1>&6
+echo "configure:834: checking switches for the host compiler" >&5
+if test "$config_BUILD_CFLAGS" != ""; then
+ CFLAGS=$config_BUILD_CFLAGS
+ BUILD_CFLAGS=$config_BUILD_CFLAGS
+else
+ BUILD_CFLAGS=$default_build_cflags
+fi
+echo "$ac_t""$BUILD_CFLAGS" 1>&6
+if test "$config_BUILD_LIBS" != ""; then
+ BUILD_LIBS=$config_BUILD_LIBS
+fi
+
+
+
+
+##########
+# Locate a compiler that converts C code into *.o files that run on
+# the target machine.
+#
+echo $ac_n "checking target compiler""... $ac_c" 1>&6
+echo "configure:854: checking target compiler" >&5
+if test "$config_TARGET_CC" != ""; then
+ TARGET_CC=$config_TARGET_CC
+else
+ TARGET_CC=$BUILD_CC
+fi
+echo "$ac_t""$TARGET_CC" 1>&6
+echo $ac_n "checking switches on the target compiler""... $ac_c" 1>&6
+echo "configure:862: checking switches on the target compiler" >&5
+if test "$config_TARGET_CFLAGS" != ""; then
+ TARGET_CFLAGS=$config_TARGET_CFLAGS
+else
+ TARGET_CFLAGS=$BUILD_CFLAGS
+fi
+echo "$ac_t""$TARGET_CFLAGS" 1>&6
+echo $ac_n "checking target linker""... $ac_c" 1>&6
+echo "configure:870: checking target linker" >&5
+if test "$config_TARGET_LINK" = ""; then
+ TARGET_LINK=$TARGET_CC
+else
+ TARGET_LINK=$config_TARGET_LINK
+fi
+echo "$ac_t""$TARGET_LINK" 1>&6
+echo $ac_n "checking switches on the target compiler""... $ac_c" 1>&6
+echo "configure:878: checking switches on the target compiler" >&5
+if test "$config_TARGET_TFLAGS" != ""; then
+ TARGET_TFLAGS=$config_TARGET_TFLAGS
+else
+ TARGET_TFLAGS=$BUILD_CFLAGS
+fi
+if test "$config_TARGET_RANLIB" != ""; then
+ TARGET_RANLIB=$config_TARGET_RANLIB
+else
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:890: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_RANLIB="ranlib"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+ echo "$ac_t""$RANLIB" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ TARGET_RANLIB=$RANLIB
+fi
+if test "$config_TARGET_AR" != ""; then
+ TARGET_RANLIB=$config_TARGET_AR
+else
+ TARGET_AR='ar cr'
+fi
+echo "$ac_t""$TARGET_TFLAGS" 1>&6
+
+
+
+
+
+
+
+# Set the $cross variable if we are cross-compiling. Make
+# it 0 if we are not.
+#
+echo $ac_n "checking if host and target compilers are the same""... $ac_c" 1>&6
+echo "configure:936: checking if host and target compilers are the same" >&5
+if test "$BUILD_CC" = "$TARGET_CC"; then
+ cross=0
+ echo "$ac_t""yes" 1>&6
+else
+ cross=1
+ echo "$ac_t""no" 1>&6
+fi
+
+###########
+# Lots of things are different if we are compiling for Windows using
+# the CYGWIN environment. So check for that special case and handle
+# things accordingly.
+#
+echo $ac_n "checking if executables have the .exe suffix""... $ac_c" 1>&6
+echo "configure:951: checking if executables have the .exe suffix" >&5
+if test "$config_BUILD_EXEEXT" = ".exe"; then
+ CYGWIN=yes
+ echo "$ac_t""yes" 1>&6
+else
+ echo "$ac_t""unknown" 1>&6
+fi
+if test "$CYGWIN" != "yes"; then
+ echo $ac_n "checking for Cygwin environment""... $ac_c" 1>&6
+echo "configure:960: checking for Cygwin environment" >&5
+if eval "test \"`echo '$''{'ac_cv_cygwin'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_cygwin=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_cygwin=no
+fi
+rm -f conftest*
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_cygwin" 1>&6
+CYGWIN=
+test "$ac_cv_cygwin" = yes && CYGWIN=yes
+fi
+if test "$CYGWIN" = "yes"; then
+ BUILD_EXEEXT=.exe
+else
+ BUILD_EXEEXT=""
+fi
+if test "$cross" = "0"; then
+ TARGET_EXEEXT=$BUILD_EXEEXT
+else
+ TARGET_EXEEXT=$config_TARGET_EXEEXT
+fi
+if test "$TARGET_EXEEXT" = ".exe"; then
+ OS_UNIX=0
+ OS_WIN=1
+ tclsubdir=win
+else
+ OS_UNIX=1
+ OS_WIN=0
+ tclsubdir=unix
+fi
+TARGET_CFLAGS="$TARGET_CFLAGS -DOS_UNIX=$OS_UNIX -DOS_WIN=$OS_WIN"
+
+
+
+
+
+
+##########
+# Extract generic linker options from the environment.
+#
+if test "$config_TARGET_LIBS" != ""; then
+ TARGET_LIBS=$config_TARGET_LIBS
+else
+ TARGET_LIBS=""
+fi
+
+
+##########
+# Figure out what C libraries are required to compile Tcl programs.
+#
+if test "$config_TARGET_TCL_LIBS" != ""; then
+ TARGET_TCL_LIBS="$config_TARGET_TCL_LIBS"
+else
+ if test "$with_tcl" != ""; then
+ extra=`echo $with_tcl/$tclsubdir/libtcl8*.a`
+ fi
+ CC=$TARGET_CC
+ echo $ac_n "checking for sin""... $ac_c" 1>&6
+echo "configure:1040: checking for sin" >&5
+if eval "test \"`echo '$''{'ac_cv_func_sin'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char sin();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_sin) || defined (__stub___sin)
+choke me
+#else
+sin();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1068: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_sin=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_sin=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'sin`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ LIBS=""
+else
+ echo "$ac_t""no" 1>&6
+LIBS="-lm"
+fi
+
+ echo $ac_n "checking for dlopen in -ldl""... $ac_c" 1>&6
+echo "configure:1089: checking for dlopen in -ldl" >&5
+ac_lib_var=`echo dl'_'dlopen | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ldl $LIBS"
+cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo dl | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <&6
+fi
+
+ otherlibs=$LIBS
+ if test "$extra" != ""; then
+ LIBS=$extra
+ else
+ LIBS=""
+
+echo $ac_n "checking for library containing Tcl_Init""... $ac_c" 1>&6
+echo "configure:1142: checking for library containing Tcl_Init" >&5
+if eval "test \"`echo '$''{'ac_cv_search_Tcl_Init'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_func_search_save_LIBS="$LIBS"
+ac_cv_search_Tcl_Init="no"
+cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_cv_search_Tcl_Init="none required"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+test "$ac_cv_search_Tcl_Init" = "no" && for i in tcl8.4 tcl8.3 tcl8.2 tcl8.1 tcl8.0 tcl80 tcl; do
+LIBS="-l$i $otherlibs $ac_func_search_save_LIBS"
+cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_cv_search_Tcl_Init="-l$i"
+break
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+done
+LIBS="$ac_func_search_save_LIBS"
+fi
+
+echo "$ac_t""$ac_cv_search_Tcl_Init" 1>&6
+if test "$ac_cv_search_Tcl_Init" != "no"; then
+ test "$ac_cv_search_Tcl_Init" = "none required" || LIBS="$ac_cv_search_Tcl_Init $LIBS"
+
+else :
+
+fi
+ fi
+ TARGET_TCL_LIBS="$LIBS $otherlibs"
+fi
+
+
+##########
+# Figure out where to get the TCL header files.
+#
+echo $ac_n "checking TCL header files""... $ac_c" 1>&6
+echo "configure:1211: checking TCL header files" >&5
+found=no
+if test "$config_TARGET_TCL_INC" != ""; then
+ TARGET_TCL_INC=$config_TARGET_TCL_INC
+ found=yes
+else
+ if test "$with_tcl" != ""; then
+ TARGET_TCL_INC="-I$with_tcl/generic -I$with_tcl/$tclsubdir"
+ found=yes
+ else
+ TARGET_TCL_INC=""
+ found=no
+ fi
+fi
+if test "$found" = "yes"; then
+ echo "$ac_t""$TARGET_TCL_INC" 1>&6
+else
+ echo "$ac_t""not specified: still searching..." 1>&6
+ echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:1230: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and "${CC-cc}" will confuse make.
+ CPP="${CC-cc} -E"
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp.
+ cat > conftest.$ac_ext <
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1251: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -E -traditional-cpp"
+ cat > conftest.$ac_ext <
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1268: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -nologo -E"
+ cat > conftest.$ac_ext <
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1285: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+ ac_cv_prog_CPP="$CPP"
+fi
+ CPP="$ac_cv_prog_CPP"
+else
+ ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+ac_safe=`echo "tcl.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for tcl.h""... $ac_c" 1>&6
+echo "configure:1311: checking for tcl.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1321: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ found=yes
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+if test "$found" = "no"; then
+ for dir in /usr/local /usr/X11* /usr/pkg /usr/contrib /usr; do
+
+ac_safe=`echo "$dir/include/tcl.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $dir/include/tcl.h""... $ac_c" 1>&6
+echo "configure:1348: checking for $dir/include/tcl.h" >&5
+if eval "test \"`echo '$''{'ac_cv_file_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: Cannot check for file existence when cross compiling" 1>&2; exit 1; }
+else
+ if test -r $dir/include/tcl.h; then
+ eval "ac_cv_file_$ac_safe=yes"
+ else
+ eval "ac_cv_file_$ac_safe=no"
+ fi
+fi
+fi
+if eval "test \"`echo '$ac_cv_file_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ found=yes
+else
+ echo "$ac_t""no" 1>&6
+
+fi
+
+ if test "$found" = "yes"; then
+ TARGET_TCL_INC="-I$dir/include"
+ break
+ fi
+ done
+fi
+
+
+##########
+# Figure out what C libraries are required to compile programs
+# that use GDBM.
+#
+if test "$config_TARGET_GDBM_LIBS" != ""; then
+ TARGET_GDBM_LIBS="$config_TARGET_GDBM_LIBS"
+else
+ CC=$TARGET_CC
+ LIBS=""
+
+echo $ac_n "checking for library containing gdbm_open""... $ac_c" 1>&6
+echo "configure:1389: checking for library containing gdbm_open" >&5
+if eval "test \"`echo '$''{'ac_cv_search_gdbm_open'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_func_search_save_LIBS="$LIBS"
+ac_cv_search_gdbm_open="no"
+cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_cv_search_gdbm_open="none required"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+test "$ac_cv_search_gdbm_open" = "no" && for i in gdbm; do
+LIBS="-l$i $ac_func_search_save_LIBS"
+cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_cv_search_gdbm_open="-l$i"
+break
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+done
+LIBS="$ac_func_search_save_LIBS"
+fi
+
+echo "$ac_t""$ac_cv_search_gdbm_open" 1>&6
+if test "$ac_cv_search_gdbm_open" != "no"; then
+ test "$ac_cv_search_gdbm_open" = "none required" || LIBS="$ac_cv_search_gdbm_open $LIBS"
+
+else :
+
+fi
+ TARGET_GDBM_LIBS="$LIBS"
+fi
+
+
+##########
+# Figure out where to get the GDBM header files.
+#
+echo $ac_n "checking GDBM header files""... $ac_c" 1>&6
+echo "configure:1457: checking GDBM header files" >&5
+found=no
+if test "$config_TARGET_GDBM_INC" != ""; then
+ TARGET_GDBM_INC=$config_TARGET_GDBM_INC
+ found=yes
+fi
+if test "$found" = "yes"; then
+ echo "$ac_t""$TARGET_TCL_INC" 1>&6
+else
+ echo "$ac_t""not specified: still searching..." 1>&6
+ ac_safe=`echo "gdbm.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for gdbm.h""... $ac_c" 1>&6
+echo "configure:1469: checking for gdbm.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1479: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ found=yes
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+if test "$found" = "no"; then
+ for dir in /usr/local /usr/pkg /usr/contrib; do
+
+ac_safe=`echo "$dir/include/gdbm.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $dir/include/gdbm.h""... $ac_c" 1>&6
+echo "configure:1506: checking for $dir/include/gdbm.h" >&5
+if eval "test \"`echo '$''{'ac_cv_file_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: Cannot check for file existence when cross compiling" 1>&2; exit 1; }
+else
+ if test -r $dir/include/gdbm.h; then
+ eval "ac_cv_file_$ac_safe=yes"
+ else
+ eval "ac_cv_file_$ac_safe=no"
+ fi
+fi
+fi
+if eval "test \"`echo '$ac_cv_file_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ found=yes
+else
+ echo "$ac_t""no" 1>&6
+
+fi
+
+ if test "$found" = "yes"; then
+ TARGET_GDBM_INC="-I$dir/include"
+ break
+ fi
+ done
+fi
+
+
+##########
+# Figure out what C libraries are required to compile programs
+# that use "readline()" library.
+#
+if test "$config_TARGET_READLINE_LIBS" != ""; then
+ TARGET_READLINE_LIBS="$config_TARGET_READLINE_LIBS"
+else
+ CC=$TARGET_CC
+ LIBS=""
+
+echo $ac_n "checking for library containing readline""... $ac_c" 1>&6
+echo "configure:1547: checking for library containing readline" >&5
+if eval "test \"`echo '$''{'ac_cv_search_readline'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_func_search_save_LIBS="$LIBS"
+ac_cv_search_readline="no"
+cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_cv_search_readline="none required"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+test "$ac_cv_search_readline" = "no" && for i in readline; do
+LIBS="-l$i $ac_func_search_save_LIBS"
+cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_cv_search_readline="-l$i"
+break
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+done
+LIBS="$ac_func_search_save_LIBS"
+fi
+
+echo "$ac_t""$ac_cv_search_readline" 1>&6
+if test "$ac_cv_search_readline" != "no"; then
+ test "$ac_cv_search_readline" = "none required" || LIBS="$ac_cv_search_readline $LIBS"
+
+else :
+
+fi
+ TARGET_READLINE_LIBS="$LIBS"
+fi
+
+
+##########
+# Figure out where to get the READLINE header files.
+#
+echo $ac_n "checking readline header files""... $ac_c" 1>&6
+echo "configure:1615: checking readline header files" >&5
+found=no
+if test "$config_TARGET_READLINE_INC" != ""; then
+ TARGET_READLINE_INC=$config_TARGET_READLINE_INC
+ found=yes
+fi
+if test "$found" = "yes"; then
+ echo "$ac_t""$TARGET_READLINE_INC" 1>&6
+else
+ echo "$ac_t""not specified: still searching..." 1>&6
+ ac_safe=`echo "readline.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for readline.h""... $ac_c" 1>&6
+echo "configure:1627: checking for readline.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1637: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ found=yes
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+if test "$found" = "no"; then
+ for dir in /usr /usr/local /usr/local/readline /usr/contrib; do
+
+ac_safe=`echo "$dir/include/readline.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $dir/include/readline.h""... $ac_c" 1>&6
+echo "configure:1664: checking for $dir/include/readline.h" >&5
+if eval "test \"`echo '$''{'ac_cv_file_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: Cannot check for file existence when cross compiling" 1>&2; exit 1; }
+else
+ if test -r $dir/include/readline.h; then
+ eval "ac_cv_file_$ac_safe=yes"
+ else
+ eval "ac_cv_file_$ac_safe=no"
+ fi
+fi
+fi
+if eval "test \"`echo '$ac_cv_file_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ found=yes
+else
+ echo "$ac_t""no" 1>&6
+
+fi
+
+ if test "$found" = "yes"; then
+ TARGET_READLINE_INC="-I$dir/include"
+ break
+ fi
+
+ac_safe=`echo "$dir/include/readline/readline.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $dir/include/readline/readline.h""... $ac_c" 1>&6
+echo "configure:1693: checking for $dir/include/readline/readline.h" >&5
+if eval "test \"`echo '$''{'ac_cv_file_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: Cannot check for file existence when cross compiling" 1>&2; exit 1; }
+else
+ if test -r $dir/include/readline/readline.h; then
+ eval "ac_cv_file_$ac_safe=yes"
+ else
+ eval "ac_cv_file_$ac_safe=no"
+ fi
+fi
+fi
+if eval "test \"`echo '$ac_cv_file_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ found=yes
+else
+ echo "$ac_t""no" 1>&6
+
+fi
+
+ if test "$found" = "yes"; then
+ TARGET_READLINE_INC="-I$dir/include/readline"
+ break
+ fi
+ done
+fi
+if test "$found" = "yes"; then
+ TARGET_HAVE_READLINE=1
+else
+ TARGET_HAVE_READLINE=0
+fi
+
+
+
+#########
+# Generate the output files.
+#
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote substitution
+ # turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ -e "s/'/'\\\\''/g" \
+ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+ ;;
+ esac >> confcache
+if cmp -s $cache_file confcache; then
+ :
+else
+ if test -w $cache_file; then
+ echo "updating cache $cache_file"
+ cat confcache > $cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+cat > conftest.defs <<\EOF
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g
+s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g
+s%\[%\\&%g
+s%\]%\\&%g
+s%\$%$$%g
+EOF
+DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '`
+rm -f conftest.defs
+
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS </dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+ case "\$ac_option" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+ exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+ -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+ echo "$CONFIG_STATUS generated by autoconf version 2.13"
+ exit 0 ;;
+ -help | --help | --hel | --he | --h)
+ echo "\$ac_cs_usage"; exit 0 ;;
+ *) echo "\$ac_cs_usage"; exit 1 ;;
+ esac
+done
+
+ac_given_srcdir=$srcdir
+
+trap 'rm -fr `echo "Makefile" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@program_prefix@%$program_prefix%g
+s%@CC@%$CC%g
+s%@BUILD_CC@%$BUILD_CC%g
+s%@BUILD_CFLAGS@%$BUILD_CFLAGS%g
+s%@BUILD_LIBS@%$BUILD_LIBS%g
+s%@RANLIB@%$RANLIB%g
+s%@TARGET_CC@%$TARGET_CC%g
+s%@TARGET_CFLAGS@%$TARGET_CFLAGS%g
+s%@TARGET_LINK@%$TARGET_LINK%g
+s%@TARGET_LFLAGS@%$TARGET_LFLAGS%g
+s%@TARGET_RANLIB@%$TARGET_RANLIB%g
+s%@TARGET_AR@%$TARGET_AR%g
+s%@BUILD_EXEEXT@%$BUILD_EXEEXT%g
+s%@OS_UNIX@%$OS_UNIX%g
+s%@OS_WIN@%$OS_WIN%g
+s%@TARGET_EXEEXT@%$TARGET_EXEEXT%g
+s%@TARGET_LIBS@%$TARGET_LIBS%g
+s%@TARGET_TCL_LIBS@%$TARGET_TCL_LIBS%g
+s%@CPP@%$CPP%g
+s%@TARGET_TCL_INC@%$TARGET_TCL_INC%g
+s%@TARGET_GDBM_LIBS@%$TARGET_GDBM_LIBS%g
+s%@TARGET_GDBM_INC@%$TARGET_GDBM_INC%g
+s%@TARGET_READLINE_LIBS@%$TARGET_READLINE_LIBS%g
+s%@TARGET_READLINE_INC@%$TARGET_READLINE_INC%g
+s%@TARGET_HAVE_READLINE@%$TARGET_HAVE_READLINE%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+ else
+ sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+ fi
+ if test ! -s conftest.s$ac_file; then
+ ac_more_lines=false
+ rm -f conftest.s$ac_file
+ else
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f conftest.s$ac_file"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+ fi
+ ac_file=`expr $ac_file + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_cmds`
+ fi
+done
+if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+ else
+ ac_dir_suffix= ac_dots=
+ fi
+
+ case "$ac_given_srcdir" in
+ .) srcdir=.
+ if test -z "$ac_dots"; then top_srcdir=.
+ else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+ /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+ *) # Relative path.
+ srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+ top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+
+ echo creating "$ac_file"
+ rm -f "$ac_file"
+ configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+ case "$ac_file" in
+ *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+ *) ac_comsub= ;;
+ esac
+
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+EOF
+cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000000..a6f9a3147a
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,513 @@
+#
+# This file describes a "configure" script that is used to build
+# makefiles for a particular platform. Process this file using
+# Autoconf version 1.13 in order to generate that script. All
+# lines of this file up to the AC_INIT macro are ignored.
+#
+# The build process allows for using a cross-compiler. But the default
+# action is to target the same platform that we are running on. The
+# configure script needs to discover the following properties of the
+# build and target systems:
+#
+# srcdir
+#
+# The is the name of the directory that contains the
+# "configure" shell script. All source files are
+# located relative to this directory.
+#
+# bindir
+#
+# The name of the directory where executables should be
+# written by the "install" target of the makefile.
+#
+# program_prefix
+#
+# Add this prefix to the names of all executables that run
+# on the target machine. Default: ""
+#
+# ENABLE_SHARED
+#
+# True if shared libraries should be generated.
+#
+# BUILD_CC
+#
+# The name of a command that is used to convert C
+# source files into executables that run on the build
+# platform.
+#
+# BUILD_CFLAGS
+#
+# Switches that the build compiler needs in order to construct
+# command-line programs.
+#
+# BUILD_LIBS
+#
+# Libraries that the build compiler needs in order to construct
+# command-line programs.
+#
+# BUILD_EXEEXT
+#
+# The filename extension for executables on the build
+# platform. "" for Unix and ".exe" for Windows.
+#
+# TARGET_CC
+#
+# The name of a command that runs on the build platform
+# and converts C source files into *.o files for the
+# target platform. In other words, the cross-compiler.
+#
+# TARGET_CFLAGS
+#
+# Switches that the target compiler needs to turn C source files
+# into *.o files. Do not include TARGET_TCL_INC in this list.
+# Makefiles might add additional switches such as "-I.".
+#
+# TARGET_TCL_LIBS
+#
+# This is the library directives passed to the target linker
+# that cause the executable to link against Tcl. This might
+# be a switch like "-ltcl8.0" or pathnames of library file
+# like "../../src/libtcl8.0.a".
+#
+# TARGET_TCL_INC
+#
+# This variables define the directory that contain header
+# files for Tcl. If the compiler is able to find
+# on its own, then this can be blank.
+#
+# TARGET_GDBM_LIBS
+#
+# This is the library directives passed to the target linker
+# that cause the executable to link against GDBM. This might
+# be a switch like "-lgdbm" or pathnames of library file
+# like "../../src/libgdbm.a".
+#
+# TARGET_GDBM_INC
+#
+# This variables define the directory that contain header
+# files for GDBM. If the compiler is able to find
+# on its own, then this can be blank.
+#
+# TARGET_READLINE_LIBS
+#
+# This is the library directives passed to the target linker
+# that cause the executable to link against GDBM. This might
+# be a switch like "-lreadline" or pathnames of library file
+# like "../../src/libreadline.a".
+#
+# TARGET_READLINE_INC
+#
+# This variables define the directory that contain header
+# files for the readline library. If the compiler is able
+# to find on its own, then this can be blank.
+#
+# TARGET_LINK
+#
+# The name of the linker that combines *.o files generated
+# by TARGET_CC into executables for the target platform.
+#
+# TARGET_LIBS
+#
+# Additional libraries or other switch that the target linker needs
+# to build an executable on the target. Do not include
+# on this list any libraries in TARGET_TCL_LIBS, TARGET_GDBM_LIBS,
+# TARGET_READLINE_LIBS, etc.
+#
+# TARGET_EXEEXT
+#
+# The filename extension for executables on the
+# target platform. "" for Unix and ".exe" for windows.
+#
+# The generated configure script will make an attempt to guess
+# at all of the above parameters. You can override any of
+# the guesses by setting the environment variable named
+# "config_AAAA" where "AAAA" is the name of the parameter
+# described above. (Exception: srcdir cannot be set this way.)
+# If you have a file that sets one or more of these environment
+# variables, you can invoke configure as follows:
+#
+# configure --with-hints=FILE
+#
+# where FILE is the name of the file that sets the environment
+# variables. FILE should be an absolute pathname.
+#
+# If you have a Tcl/Tk/BLT source distribution available, then the
+# files in that distribution will be used instead of any other
+# Tcl/Tk/BLT files the script might discover if you tell the configure
+# script about the source tree. Use commandline options:
+#
+# --with-tcl=PATH --with-tk=PATH --with-blt=PATH
+#
+# Or set environment variables config_WITH_TCL, config_WITH_TK, or
+# config_WITH_BLT.
+#
+# This configure.in file is easy to reuse on other projects. Just
+# change the argument to AC_INIT(). And disable any features that
+# you don't need (for example BLT) by erasing or commenting out
+# the corresponding code.
+#
+AC_INIT(src/sqlite.h)
+
+dnl Put the RCS revision string after AC_INIT so that it will also
+dnl show in in configure.
+# The following RCS revision string applies to configure.in
+# $Revision: 1.1 $
+
+#########
+# Make sure we are not building in a subdirectory of the source tree.
+#
+changequote(<<<,>>>)
+temp=`echo $srcdir | grep '[^./]'`
+changequote([,])
+if test "$temp" = ""; then
+ AC_MSG_ERROR([
+**************************************************************************
+** This program may not be compiled in the same directory that contains **
+** the configure script or any subdirectory of that directory. Rerun **
+** the configure script from a directory that is separate from the **
+** source tree. **
+**************************************************************************])
+fi
+
+#########
+# Set up an appropriate program prefix
+#
+if test "$program_prefix" = "NONE"; then
+ program_prefix=""
+fi
+AC_SUBST(program_prefix)
+
+#########
+# Check to see if the --with-hints=FILE option is used. If there is none,
+# then check for a files named "$host.hints" and ../$hosts.hints where
+# $host is the hostname of the build system. If still no hints are
+# found, try looking in $system.hints and ../$system.hints where
+# $system is the result of uname -s.
+#
+AC_ARG_WITH(hints,
+ [ --with-hints=FILE Read configuration options from FILE],
+ hints=$withval)
+if test "$hints" = ""; then
+ host=`hostname | sed 's/\..*//'`
+ if test -r $host.hints; then
+ hints=$host.hints
+ else
+ if test -r ../$host.hints; then
+ hints=../$host.hints
+ fi
+ fi
+fi
+if test "$hints" = ""; then
+ sys=`uname -s`
+ if test -r $sys.hints; then
+ hints=$sys.hints
+ else
+ if test -r ../$sys.hints; then
+ hints=../$sys.hints
+ fi
+ fi
+fi
+if test "$hints" != ""; then
+ AC_MSG_RESULT(reading hints from $hints)
+ . $hints
+fi
+
+#########
+# Locate a compiler for the build machine. This compiler should
+# generate command-line programs that run on the build machine.
+#
+default_build_cflags="-g"
+if test "$config_BUILD_CC" = ""; then
+ AC_PROG_CC
+ if test "$cross_compiling" = "yes"; then
+ AC_MSG_ERROR([unable to find a compiler for building build tools])
+ fi
+ BUILD_CC=$CC
+ default_build_cflags=$CFLAGS
+else
+ BUILD_CC=$config_BUILD_CC
+ AC_MSG_CHECKING([host compiler])
+ CC=$BUILD_CC
+ AC_MSG_RESULT($BUILD_CC)
+fi
+AC_MSG_CHECKING([switches for the host compiler])
+if test "$config_BUILD_CFLAGS" != ""; then
+ CFLAGS=$config_BUILD_CFLAGS
+ BUILD_CFLAGS=$config_BUILD_CFLAGS
+else
+ BUILD_CFLAGS=$default_build_cflags
+fi
+AC_MSG_RESULT($BUILD_CFLAGS)
+if test "$config_BUILD_LIBS" != ""; then
+ BUILD_LIBS=$config_BUILD_LIBS
+fi
+AC_SUBST(BUILD_CC)
+AC_SUBST(BUILD_CFLAGS)
+AC_SUBST(BUILD_LIBS)
+
+##########
+# Locate a compiler that converts C code into *.o files that run on
+# the target machine.
+#
+AC_MSG_CHECKING([target compiler])
+if test "$config_TARGET_CC" != ""; then
+ TARGET_CC=$config_TARGET_CC
+else
+ TARGET_CC=$BUILD_CC
+fi
+AC_MSG_RESULT($TARGET_CC)
+AC_MSG_CHECKING([switches on the target compiler])
+if test "$config_TARGET_CFLAGS" != ""; then
+ TARGET_CFLAGS=$config_TARGET_CFLAGS
+else
+ TARGET_CFLAGS=$BUILD_CFLAGS
+fi
+AC_MSG_RESULT($TARGET_CFLAGS)
+AC_MSG_CHECKING([target linker])
+if test "$config_TARGET_LINK" = ""; then
+ TARGET_LINK=$TARGET_CC
+else
+ TARGET_LINK=$config_TARGET_LINK
+fi
+AC_MSG_RESULT($TARGET_LINK)
+AC_MSG_CHECKING([switches on the target compiler])
+if test "$config_TARGET_TFLAGS" != ""; then
+ TARGET_TFLAGS=$config_TARGET_TFLAGS
+else
+ TARGET_TFLAGS=$BUILD_CFLAGS
+fi
+if test "$config_TARGET_RANLIB" != ""; then
+ TARGET_RANLIB=$config_TARGET_RANLIB
+else
+ AC_PROG_RANLIB
+ TARGET_RANLIB=$RANLIB
+fi
+if test "$config_TARGET_AR" != ""; then
+ TARGET_RANLIB=$config_TARGET_AR
+else
+ TARGET_AR='ar cr'
+fi
+AC_MSG_RESULT($TARGET_TFLAGS)
+AC_SUBST(TARGET_CC)
+AC_SUBST(TARGET_CFLAGS)
+AC_SUBST(TARGET_LINK)
+AC_SUBST(TARGET_LFLAGS)
+AC_SUBST(TARGET_RANLIB)
+AC_SUBST(TARGET_AR)
+
+# Set the $cross variable if we are cross-compiling. Make
+# it 0 if we are not.
+#
+AC_MSG_CHECKING([if host and target compilers are the same])
+if test "$BUILD_CC" = "$TARGET_CC"; then
+ cross=0
+ AC_MSG_RESULT(yes)
+else
+ cross=1
+ AC_MSG_RESULT(no)
+fi
+
+###########
+# Lots of things are different if we are compiling for Windows using
+# the CYGWIN environment. So check for that special case and handle
+# things accordingly.
+#
+AC_MSG_CHECKING([if executables have the .exe suffix])
+if test "$config_BUILD_EXEEXT" = ".exe"; then
+ CYGWIN=yes
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(unknown)
+fi
+if test "$CYGWIN" != "yes"; then
+ AC_CYGWIN
+fi
+if test "$CYGWIN" = "yes"; then
+ BUILD_EXEEXT=.exe
+else
+ BUILD_EXEEXT=""
+fi
+if test "$cross" = "0"; then
+ TARGET_EXEEXT=$BUILD_EXEEXT
+else
+ TARGET_EXEEXT=$config_TARGET_EXEEXT
+fi
+if test "$TARGET_EXEEXT" = ".exe"; then
+ OS_UNIX=0
+ OS_WIN=1
+ tclsubdir=win
+else
+ OS_UNIX=1
+ OS_WIN=0
+ tclsubdir=unix
+fi
+TARGET_CFLAGS="$TARGET_CFLAGS -DOS_UNIX=$OS_UNIX -DOS_WIN=$OS_WIN"
+
+AC_SUBST(BUILD_EXEEXT)
+AC_SUBST(OS_UNIX)
+AC_SUBST(OS_WIN)
+AC_SUBST(TARGET_EXEEXT)
+
+##########
+# Extract generic linker options from the environment.
+#
+if test "$config_TARGET_LIBS" != ""; then
+ TARGET_LIBS=$config_TARGET_LIBS
+else
+ TARGET_LIBS=""
+fi
+AC_SUBST(TARGET_LIBS)
+
+##########
+# Figure out what C libraries are required to compile Tcl programs.
+#
+if test "$config_TARGET_TCL_LIBS" != ""; then
+ TARGET_TCL_LIBS="$config_TARGET_TCL_LIBS"
+else
+ if test "$with_tcl" != ""; then
+ extra=`echo $with_tcl/$tclsubdir/libtcl8*.a`
+ fi
+ CC=$TARGET_CC
+ AC_CHECK_FUNC(sin, LIBS="", LIBS="-lm")
+ AC_CHECK_LIB(dl, dlopen)
+ otherlibs=$LIBS
+ if test "$extra" != ""; then
+ LIBS=$extra
+ else
+ LIBS=""
+ AC_SEARCH_LIBS(Tcl_Init, dnl
+ tcl8.4 tcl8.3 tcl8.2 tcl8.1 tcl8.0 tcl80 tcl,,,$otherlibs)
+ fi
+ TARGET_TCL_LIBS="$LIBS $otherlibs"
+fi
+AC_SUBST(TARGET_TCL_LIBS)
+
+##########
+# Figure out where to get the TCL header files.
+#
+AC_MSG_CHECKING([TCL header files])
+found=no
+if test "$config_TARGET_TCL_INC" != ""; then
+ TARGET_TCL_INC=$config_TARGET_TCL_INC
+ found=yes
+else
+ if test "$with_tcl" != ""; then
+ TARGET_TCL_INC="-I$with_tcl/generic -I$with_tcl/$tclsubdir"
+ found=yes
+ else
+ TARGET_TCL_INC=""
+ found=no
+ fi
+fi
+if test "$found" = "yes"; then
+ AC_MSG_RESULT($TARGET_TCL_INC)
+else
+ AC_MSG_RESULT(not specified: still searching...)
+ AC_CHECK_HEADER(tcl.h, [found=yes])
+fi
+if test "$found" = "no"; then
+ for dir in /usr/local /usr/X11* /usr/pkg /usr/contrib /usr; do
+ AC_CHECK_FILE($dir/include/tcl.h, found=yes)
+ if test "$found" = "yes"; then
+ TARGET_TCL_INC="-I$dir/include"
+ break
+ fi
+ done
+fi
+AC_SUBST(TARGET_TCL_INC)
+
+##########
+# Figure out what C libraries are required to compile programs
+# that use GDBM.
+#
+if test "$config_TARGET_GDBM_LIBS" != ""; then
+ TARGET_GDBM_LIBS="$config_TARGET_GDBM_LIBS"
+else
+ CC=$TARGET_CC
+ LIBS=""
+ AC_SEARCH_LIBS(gdbm_open, gdbm,,,)
+ TARGET_GDBM_LIBS="$LIBS"
+fi
+AC_SUBST(TARGET_GDBM_LIBS)
+
+##########
+# Figure out where to get the GDBM header files.
+#
+AC_MSG_CHECKING([GDBM header files])
+found=no
+if test "$config_TARGET_GDBM_INC" != ""; then
+ TARGET_GDBM_INC=$config_TARGET_GDBM_INC
+ found=yes
+fi
+if test "$found" = "yes"; then
+ AC_MSG_RESULT($TARGET_TCL_INC)
+else
+ AC_MSG_RESULT(not specified: still searching...)
+ AC_CHECK_HEADER(gdbm.h, [found=yes])
+fi
+if test "$found" = "no"; then
+ for dir in /usr/local /usr/pkg /usr/contrib; do
+ AC_CHECK_FILE($dir/include/gdbm.h, found=yes)
+ if test "$found" = "yes"; then
+ TARGET_GDBM_INC="-I$dir/include"
+ break
+ fi
+ done
+fi
+AC_SUBST(TARGET_GDBM_INC)
+
+##########
+# Figure out what C libraries are required to compile programs
+# that use "readline()" library.
+#
+if test "$config_TARGET_READLINE_LIBS" != ""; then
+ TARGET_READLINE_LIBS="$config_TARGET_READLINE_LIBS"
+else
+ CC=$TARGET_CC
+ LIBS=""
+ AC_SEARCH_LIBS(readline, readline,,,)
+ TARGET_READLINE_LIBS="$LIBS"
+fi
+AC_SUBST(TARGET_READLINE_LIBS)
+
+##########
+# Figure out where to get the READLINE header files.
+#
+AC_MSG_CHECKING([readline header files])
+found=no
+if test "$config_TARGET_READLINE_INC" != ""; then
+ TARGET_READLINE_INC=$config_TARGET_READLINE_INC
+ found=yes
+fi
+if test "$found" = "yes"; then
+ AC_MSG_RESULT($TARGET_READLINE_INC)
+else
+ AC_MSG_RESULT(not specified: still searching...)
+ AC_CHECK_HEADER(readline.h, [found=yes])
+fi
+if test "$found" = "no"; then
+ for dir in /usr /usr/local /usr/local/readline /usr/contrib; do
+ AC_CHECK_FILE($dir/include/readline.h, found=yes)
+ if test "$found" = "yes"; then
+ TARGET_READLINE_INC="-I$dir/include"
+ break
+ fi
+ AC_CHECK_FILE($dir/include/readline/readline.h, found=yes)
+ if test "$found" = "yes"; then
+ TARGET_READLINE_INC="-I$dir/include/readline"
+ break
+ fi
+ done
+fi
+if test "$found" = "yes"; then
+ TARGET_HAVE_READLINE=1
+else
+ TARGET_HAVE_READLINE=0
+fi
+AC_SUBST(TARGET_READLINE_INC)
+AC_SUBST(TARGET_HAVE_READLINE)
+
+#########
+# Generate the output files.
+#
+AC_OUTPUT(Makefile)
diff --git a/doc/lemon.html b/doc/lemon.html
new file mode 100644
index 0000000000..9b4648f46c
--- /dev/null
+++ b/doc/lemon.html
@@ -0,0 +1,861 @@
+
+
+The Lemon Parser Generator
+
+
+The Lemon Parser Generator
+
+Lemon is an LALR(1) parser generator for C or C++.
+It does the same job as ``bison'' and ``yacc''.
+But lemon is not another bison or yacc clone. It
+uses a different grammar syntax which is designed to
+reduce the number of coding errors. Lemon also uses a more
+sophisticated parsing engine that is faster than yacc and
+bison and which is both reentrant and thread-safe.
+Furthermore, Lemon implements features that can be used
+to eliminate resource leaks, making is suitable for use
+in long-running programs such as graphical user interfaces
+or embedded controllers.
+
+This document is an introduction to the Lemon
+parser generator.
+
+Theory of Operation
+
+The main goal of Lemon is to translate a context free grammar (CFG)
+for a particular language into C code that implements a parser for
+that language.
+The program has two inputs:
+
+- The grammar specification.
+
- A parser template file.
+
+Typically, only the grammar specification is supplied by the programmer.
+Lemon comes with a default parser template which works fine for most
+applications. But the user is free to substitute a different parser
+template if desired.
+
+Depending on command-line options, Lemon will generate between
+one and three files of outputs.
+
+- C code to implement the parser.
+
- A header file defining an integer ID for each terminal symbol.
+
- An information file that describes the states of the generated parser
+ automaton.
+
+By default, all three of these output files are generated.
+The header file is suppressed if the ``-m'' command-line option is
+used and the report file is omitted when ``-q'' is selected.
+
+The grammar specification file uses a ``.y'' suffix, by convention.
+In the examples used in this document, we'll assume the name of the
+grammar file is ``gram.y''. A typical use of Lemon would be the
+following command:
+
+ lemon gram.y
+
+This command will generate three output files named ``gram.c'',
+``gram.h'' and ``gram.out''.
+The first is C code to implement the parser. The second
+is the header file that defines numerical values for all
+terminal symbols, and the last is the report that explains
+the states used by the parser automaton.
+
+Command Line Options
+
+The behavior of Lemon can be modified using command-line options.
+You can obtain a list of the available command-line options together
+with a brief explanation of what each does by typing
+
+ lemon -?
+
+As of this writing, the following command-line options are supported:
+
+- -b
+
- -c
+
- -g
+
- -m
+
- -q
+
- -s
+
- -x
+
+The ``-b'' option reduces the amount of text in the report file by
+printing only the basis of each parser state, rather than the full
+configuration.
+The ``-c'' option suppresses action table compression. Using -c
+will make the parser a little larger and slower but it will detect
+syntax errors sooner.
+The ``-g'' option causes no output files to be generated at all.
+Instead, the input grammar file is printed on standard output but
+with all comments, actions and other extraneous text deleted. This
+is a useful way to get a quick summary of a grammar.
+The ``-m'' option causes the output C source file to be compatible
+with the ``makeheaders'' program.
+Makeheaders is a program that automatically generates header files
+from C source code. When the ``-m'' option is used, the header
+file is not output since the makeheaders program will take care
+of generated all header files automatically.
+The ``-q'' option suppresses the report file.
+Using ``-s'' causes a brief summary of parser statistics to be
+printed. Like this:
+
+ Parser statistics: 74 terminals, 70 nonterminals, 179 rules
+ 340 states, 2026 parser table entries, 0 conflicts
+
+Finally, the ``-x'' option causes Lemon to print its version number
+and copyright information
+and then stop without attempting to read the grammar or generate a parser.
+
+The Parser Interface
+
+Lemon doesn't generate a complete, working program. It only generates
+a few subroutines that implement a parser. This section describes
+the interface to those subroutines. It is up to the programmer to
+call these subroutines in an appropriate way in order to produce a
+complete system.
+
+Before a program begins using a Lemon-generated parser, the program
+must first create the parser.
+A new parser is created as follows:
+
+ void *pParser = ParseAlloc( malloc );
+
+The ParseAlloc() routine allocates and initializes a new parser and
+returns a pointer to it.
+The actual data structure used to represent a parser is opaque --
+its internal structure is not visible or usable by the calling routine.
+For this reason, the ParseAlloc() routine returns a pointer to void
+rather than a pointer to some particular structure.
+The sole argument to the ParseAlloc() routine is a pointer to the
+subroutine used to allocate memory. Typically this means ``malloc()''.
+
+After a program is finished using a parser, it can reclaim all
+memory allocated by that parser by calling
+
+ ParseFree(pParser, free);
+
+The first argument is the same pointer returned by ParseAlloc(). The
+second argument is a pointer to the function used to release bulk
+memory back to the system.
+
+After a parser has been allocated using ParseAlloc(), the programmer
+must supply the parser with a sequence of tokens (terminal symbols) to
+be parsed. This is accomplished by calling the following function
+once for each token:
+
+ Parse(pParser, hTokenID, sTokenData, pArg);
+
+The first argument to the Parse() routine is the pointer returned by
+ParseAlloc().
+The second argument is a small positive integer that tells the parse the
+type of the next token in the data stream.
+There is one token type for each terminal symbol in the grammar.
+The gram.h file generated by Lemon contains #define statements that
+map symbolic terminal symbol names into appropriate integer values.
+(A value of 0 for the second argument is a special flag to the
+parser to indicate that the end of input has been reached.)
+The third argument is the value of the given token. By default,
+the type of the third argument is integer, but the grammar will
+usually redefine this type to be some kind of structure.
+Typically the second argument will be a broad category of tokens
+such as ``identifier'' or ``number'' and the third argument will
+be the name of the identifier or the value of the number.
+
+The Parse() function may have either three or four arguments,
+depending on the grammar. If the grammar specification file request
+it, the Parse() function will have a fourth parameter that can be
+of any type chosen by the programmer. The parser doesn't do anything
+with this argument except to pass it through to action routines.
+This is a convenient mechanism for passing state information down
+to the action routines without having to use global variables.
+
+A typical use of a Lemon parser might look something like the
+following:
+
+ 01 ParseTree *ParseFile(const char *zFilename){
+ 02 Tokenizer *pTokenizer;
+ 03 void *pParser;
+ 04 Token sToken;
+ 05 int hTokenId;
+ 06 ParserState sState;
+ 07
+ 08 pTokenizer = TokenizerCreate(zFilename);
+ 09 pParser = ParseAlloc( malloc );
+ 10 InitParserState(&sState);
+ 11 while( GetNextToken(pTokenizer, &hTokenId, &sToken) ){
+ 12 Parse(pParser, hTokenId, sToken, &sState);
+ 13 }
+ 14 Parse(pParser, 0, sToken, &sState);
+ 15 ParseFree(pParser, free );
+ 16 TokenizerFree(pTokenizer);
+ 17 return sState.treeRoot;
+ 18 }
+
+This example shows a user-written routine that parses a file of
+text and returns a pointer to the parse tree.
+(We've omitted all error-handling from this example to keep it
+simple.)
+We assume the existence of some kind of tokenizer which is created
+using TokenizerCreate() on line 8 and deleted by TokenizerFree()
+on line 16. The GetNextToken() function on line 11 retrieves the
+next token from the input file and puts its type in the
+integer variable hTokenId. The sToken variable is assumed to be
+some kind of structure that contains details about each token,
+such as its complete text, what line it occurs on, etc.
+
+This example also assumes the existence of structure of type
+ParserState that holds state information about a particular parse.
+An instance of such a structure is created on line 6 and initialized
+on line 10. A pointer to this structure is passed into the Parse()
+routine as the optional 4th argument.
+The action routine specified by the grammar for the parser can use
+the ParserState structure to hold whatever information is useful and
+appropriate. In the example, we note that the treeRoot field of
+the ParserState structure is left pointing to the root of the parse
+tree.
+
+The core of this example as it relates to Lemon is as follows:
+
+ ParseFile(){
+ pParser = ParseAlloc( malloc );
+ while( GetNextToken(pTokenizer,&hTokenId, &sToken) ){
+ Parse(pParser, hTokenId, sToken);
+ }
+ Parse(pParser, 0, sToken);
+ ParseFree(pParser, free );
+ }
+
+Basically, what a program has to do to use a Lemon-generated parser
+is first create the parser, then send it lots of tokens obtained by
+tokenizing an input source. When the end of input is reached, the
+Parse() routine should be called one last time with a token type
+of 0. This step is necessary to inform the parser that the end of
+input has been reached. Finally, we reclaim memory used by the
+parser by calling ParseFree().
+
+There is one other interface routine that should be mentioned
+before we move on.
+The ParseTrace() function can be used to generate debugging output
+from the parser. A prototype for this routine is as follows:
+
+ ParseTrace(FILE *stream, char *zPrefix);
+
+After this routine is called, a short (one-line) message is written
+to the designated output stream every time the parser changes states
+or calls an action routine. Each such message is prefaced using
+the text given by zPrefix. This debugging output can be turned off
+by calling ParseTrace() again with a first argument of NULL (0).
+
+Differences With YACC and BISON
+
+Programmers who have previously used the yacc or bison parser
+generator will notice several important differences between yacc and/or
+bison and Lemon.
+
+- In yacc and bison, the parser calls the tokenizer. In Lemon,
+ the tokenizer calls the parser.
+
- Lemon uses no global variables. Yacc and bison use global variables
+ to pass information between the tokenizer and parser.
+
- Lemon allows multiple parsers to be running simultaneously. Yacc
+ and bison do not.
+
+These differences may cause some initial confusion for programmers
+with prior yacc and bison experience.
+But after years of experience using Lemon, I firmly
+believe that the Lemon way of doing things is better.
+
+Input File Syntax
+
+The main purpose of the grammar specification file for Lemon is
+to define the grammar for the parser. But the input file also
+specifies additional information Lemon requires to do its job.
+Most of the work in using Lemon is in writing an appropriate
+grammar file.
+
+The grammar file for lemon is, for the most part, free format.
+It does not have sections or divisions like yacc or bison. Any
+declaration can occur at any point in the file.
+Lemon ignores whitespace (except where it is needed to separate
+tokens) and it honors the same commenting conventions as C and C++.
+
+Terminals and Nonterminals
+
+A terminal symbol (token) is any string of alphanumeric
+and underscore characters
+that begins with an upper case letter.
+A terminal can contain lower class letters after the first character,
+but the usual convention is to make terminals all upper case.
+A nonterminal, on the other hand, is any string of alphanumeric
+and underscore characters than begins with a lower case letter.
+Again, the usual convention is to make nonterminals use all lower
+case letters.
+
+In Lemon, terminal and nonterminal symbols do not need to
+be declared or identified in a separate section of the grammar file.
+Lemon is able to generate a list of all terminals and nonterminals
+by examining the grammar rules, and it can always distinguish a
+terminal from a nonterminal by checking the case of the first
+character of the name.
+
+Yacc and bison allow terminal symbols to have either alphanumeric
+names or to be individual characters included in single quotes, like
+this: ')' or '$'. Lemon does not allow this alternative form for
+terminal symbols. With Lemon, all symbols, terminals and nonterminals,
+must have alphanumeric names.
+
+Grammar Rules
+
+The main component of a Lemon grammar file is a sequence of grammar
+rules.
+Each grammar rule consists of a nonterminal symbol followed by
+the special symbol ``::='' and then a list of terminals and/or nonterminals.
+The rule is terminated by a period.
+The list of terminals and nonterminals on the right-hand side of the
+rule can be empty.
+Rules can occur in any order, except that the left-hand side of the
+first rule is assumed to be the start symbol for the grammar (unless
+specified otherwise using the %start directive described below.)
+A typical sequence of grammar rules might look something like this:
+
+ expr ::= expr PLUS expr.
+ expr ::= expr TIMES expr.
+ expr ::= LPAREN expr RPAREN.
+ expr ::= VALUE.
+
+
+
+There is one non-terminal in this example, ``expr'', and five
+terminal symbols or tokens: ``PLUS'', ``TIMES'', ``LPAREN'',
+``RPAREN'' and ``VALUE''.
+
+Like yacc and bison, Lemon allows the grammar to specify a block
+of C code that will be executed whenever a grammar rule is reduced
+by the parser.
+In Lemon, this action is specified by putting the C code (contained
+within curly braces {...}) immediately after the
+period that closes the rule.
+For example:
+
+ expr ::= expr PLUS expr. { printf("Doing an addition...\n"); }
+
+
+
+In order to be useful, grammar actions must normally be linked to
+their associated grammar rules.
+In yacc and bison, this is accomplished by embedding a ``$$'' in the
+action to stand for the value of the left-hand side of the rule and
+symbols ``$1'', ``$2'', and so forth to stand for the value of
+the terminal or nonterminal at position 1, 2 and so forth on the
+right-hand side of the rule.
+This idea is very powerful, but it is also very error-prone. The
+single most common source of errors in a yacc or bison grammar is
+to miscount the number of symbols on the right-hand side of a grammar
+rule and say ``$7'' when you really mean ``$8''.
+
+Lemon avoids the need to count grammar symbols by assigning symbolic
+names to each symbol in a grammar rule and then using those symbolic
+names in the action.
+In yacc or bison, one would write this:
+
+ expr -> expr PLUS expr { $$ = $1 + $3; };
+
+But in Lemon, the same rule becomes the following:
+
+ expr(A) ::= expr(B) PLUS expr(C). { A = B+C; }
+
+In the Lemon rule, any symbol in parentheses after a grammar rule
+symbol becomes a place holder for that symbol in the grammar rule.
+This place holder can then be used in the associated C action to
+stand for the value of that symbol.
+
+
The Lemon notation for linking a grammar rule with its reduce
+action is superior to yacc/bison on several counts.
+First, as mentioned above, the Lemon method avoids the need to
+count grammar symbols.
+Secondly, if a terminal or nonterminal in a Lemon grammar rule
+includes a linking symbol in parentheses but that linking symbol
+is not actually used in the reduce action, then an error message
+is generated.
+For example, the rule
+
+ expr(A) ::= expr(B) PLUS expr(C). { A = B; }
+
+will generate an error because the linking symbol ``C'' is used
+in the grammar rule but not in the reduce action.
+
+The Lemon notation for linking grammar rules to reduce actions
+also facilitates the use of destructors for reclaiming memory
+allocated by the values of terminals and nonterminals on the
+right-hand side of a rule.
+
+Precedence Rules
+
+Lemon resolves parsing ambiguities in exactly the same way as
+yacc and bison. A shift-reduce conflict is resolved in favor
+of the shift, and a reduce-reduce conflict is resolved by reducing
+whichever rule comes first in the grammar file.
+
+Just like in
+yacc and bison, Lemon allows a measure of control
+over the resolution of paring conflicts using precedence rules.
+A precedence value can be assigned to any terminal symbol
+using the %left, %right or %nonassoc directives. Terminal symbols
+mentioned in earlier directives have a lower precedence that
+terminal symbols mentioned in later directives. For example:
+
+
+ %left AND.
+ %left OR.
+ %nonassoc EQ NE GT GE LT LE.
+ %left PLUS MINUS.
+ %left TIMES DIVIDE MOD.
+ %right EXP NOT.
+
+
+In the preceding sequence of directives, the AND operator is
+defined to have the lowest precedence. The OR operator is one
+precedence level higher. And so forth. Hence, the grammar would
+attempt to group the ambiguous expression
+
+ a AND b OR c
+
+like this
+
+ a AND (b OR c).
+
+The associativity (left, right or nonassoc) is used to determine
+the grouping when the precedence is the same. AND is left-associative
+in our example, so
+
+ a AND b AND c
+
+is parsed like this
+
+ (a AND b) AND c.
+
+The EXP operator is right-associative, though, so
+
+ a EXP b EXP c
+
+is parsed like this
+
+ a EXP (b EXP c).
+
+The nonassoc precedence is used for non-associative operators.
+So
+
+ a EQ b EQ c
+
+is an error.
+
+The precedence of non-terminals is transferred to rules as follows:
+The precedence of a grammar rule is equal to the precedence of the
+left-most terminal symbol in the rule for which a precedence is
+defined. This is normally what you want, but in those cases where
+you want to precedence of a grammar rule to be something different,
+you can specify an alternative precedence symbol by putting the
+symbol in square braces after the period at the end of the rule and
+before any C-code. For example:
+
+
+ expr = MINUS expr. [NOT]
+
+
+This rule has a precedence equal to that of the NOT symbol, not the
+MINUS symbol as would have been the case by default.
+
+With the knowledge of how precedence is assigned to terminal
+symbols and individual
+grammar rules, we can now explain precisely how parsing conflicts
+are resolved in Lemon. Shift-reduce conflicts are resolved
+as follows:
+
+- If either the token to be shifted or the rule to be reduced
+ lacks precedence information, then resolve in favor of the
+ shift, but report a parsing conflict.
+
- If the precedence of the token to be shifted is greater than
+ the precedence of the rule to reduce, then resolve in favor
+ of the shift. No parsing conflict is reported.
+
- If the precedence of the token it be shifted is less than the
+ precedence of the rule to reduce, then resolve in favor of the
+ reduce action. No parsing conflict is reported.
+
- If the precedences are the same and the shift token is
+ right-associative, then resolve in favor of the shift.
+ No parsing conflict is reported.
+
- If the precedences are the same the the shift token is
+ left-associative, then resolve in favor of the reduce.
+ No parsing conflict is reported.
+
- Otherwise, resolve the conflict by doing the shift and
+ report the parsing conflict.
+
+Reduce-reduce conflicts are resolved this way:
+
+- If either reduce rule
+ lacks precedence information, then resolve in favor of the
+ rule that appears first in the grammar and report a parsing
+ conflict.
+
- If both rules have precedence and the precedence is different
+ then resolve the dispute in favor of the rule with the highest
+ precedence and do not report a conflict.
+
- Otherwise, resolve the conflict by reducing by the rule that
+ appears first in the grammar and report a parsing conflict.
+
+
+Special Directives
+
+The input grammar to Lemon consists of grammar rules and special
+directives. We've described all the grammar rules, so now we'll
+talk about the special directives.
+
+Directives in lemon can occur in any order. You can put them before
+the grammar rules, or after the grammar rules, or in the mist of the
+grammar rules. It doesn't matter. The relative order of
+directives used to assign precedence to terminals is important, but
+other than that, the order of directives in Lemon is arbitrary.
+
+Lemon supports the following special directives:
+
+- %destructor
+
- %extra_argument
+
- %include
+
- %left
+
- %name
+
- %nonassoc
+
- %parse_accept
+
- %parse_failure
+
- %right
+
- %stack_overflow
+
- %stack_size
+
- %start_symbol
+
- %syntax_error
+
- %token_destructor
+
- %token_prefix
+
- %token_type
+
- %type
+
+Each of these directives will be described separately in the
+following sections:
+
+The %destructor directive
+
+The %destructor directive is used to specify a destructor for
+a non-terminal symbol.
+(See also the %token_destructor directive which is used to
+specify a destructor for terminal symbols.)
+
+A non-terminal's destructor is called to dispose of the
+non-terminal's value whenever the non-terminal is popped from
+the stack. This includes all of the following circumstances:
+
+- When a rule reduces and the value of a non-terminal on
+ the right-hand side is not linked to C code.
+
- When the stack is popped during error processing.
+
- When the ParseFree() function runs.
+
+The destructor can do whatever it wants with the value of
+the non-terminal, but its design is to deallocate memory
+or other resources held by that non-terminal.
+
+Consider an example:
+
+ %type nt {void*}
+ %destructor nt { free($$); }
+ nt(A) ::= ID NUM. { A = malloc( 100 ); }
+
+This example is a bit contrived but it serves to illustrate how
+destructors work. The example shows a non-terminal named
+``nt'' that holds values of type ``void*''. When the rule for
+an ``nt'' reduces, it sets the value of the non-terminal to
+space obtained from malloc(). Later, when the nt non-terminal
+is popped from the stack, the destructor will fire and call
+free() on this malloced space, thus avoiding a memory leak.
+(Note that the symbol ``$$'' in the destructor code is replaced
+by the value of the non-terminal.)
+
+It is important to note that the value of a non-terminal is passed
+to the destructor whenever the non-terminal is removed from the
+stack, unless the non-terminal is used in a C-code action. If
+the non-terminal is used by C-code, then it is assumed that the
+C-code will take care of destroying it if it should really
+be destroyed. More commonly, the value is used to build some
+larger structure and we don't want to destroy it, which is why
+the destructor is not called in this circumstance.
+
+By appropriate use of destructors, it is possible to
+build a parser using Lemon that can be used within a long-running
+program, such as a GUI, that will not leak memory or other resources.
+To do the same using yacc or bison is much more difficult.
+
+The %extra_argument directive
+
+The %extra_argument directive instructs Lemon to add a 4th parameter
+to the parameter list of the Parse() function it generates. Lemon
+doesn't do anything itself with this extra argument, but it does
+make the argument available to C-code action routines, destructors,
+and so forth. For example, if the grammar file contains:
+
+
+ %extra_argument { MyStruct *pAbc }
+
+
+Then the Parse() function generated will have an 4th parameter
+of type ``MyStruct*'' and all action routines will have access to
+a variable named ``pAbc'' that is the value of the 4th parameter
+in the most recent call to Parse().
+
+The %include directive
+
+The %include directive specifies C code that is included at the
+top of the generated parser. You can include any text you want --
+the Lemon parser generator copies to blindly. If you have multiple
+%include directives in your grammar file, their values are concatenated
+before being put at the beginning of the generated parser.
+
+The %include directive is very handy for getting some extra #include
+preprocessor statements at the beginning of the generated parser.
+For example:
+
+
+ %include {#include <unistd.h>}
+
+
+This might be needed, for example, if some of the C actions in the
+grammar call functions that are prototyed in unistd.h.
+
+The %left directive
+
+The %left directive is used (along with the %right and
+%nonassoc directives) to declare precedences of terminal
+symbols. Every terminal symbol whose name appears after
+a %left directive but before the next period (``.'') is
+given the same left-associative precedence value. Subsequent
+%left directives have higher precedence. For example:
+
+
+ %left AND.
+ %left OR.
+ %nonassoc EQ NE GT GE LT LE.
+ %left PLUS MINUS.
+ %left TIMES DIVIDE MOD.
+ %right EXP NOT.
+
+
+Note the period that terminates each %left, %right or %nonassoc
+directive.
+
+LALR(1) grammars can get into a situation where they require
+a large amount of stack space if you make heavy use or right-associative
+operators. For this reason, it is recommended that you use %left
+rather than %right whenever possible.
+
+The %name directive
+
+By default, the functions generated by Lemon all begin with the
+five-character string ``Parse''. You can change this string to something
+different using the %name directive. For instance:
+
+
+ %name Abcde
+
+
+Putting this directive in the grammar file will cause Lemon to generate
+functions named
+
+- AbcdeAlloc(),
+
- AbcdeFree(),
+
- AbcdeTrace(), and
+
- Abcde().
+
+The %name directive allows you to generator two or more different
+parsers and link them all into the same executable.
+
+
+The %nonassoc directive
+
+This directive is used to assign non-associative precedence to
+one or more terminal symbols. See the section on precedence rules
+or on the %left directive for additional information.
+
+The %parse_accept directive
+
+The %parse_accept directive specifies a block of C code that is
+executed whenever the parser accepts its input string. To ``accept''
+an input string means that the parser was able to process all tokens
+without error.
+
+For example:
+
+
+ %parse_accept {
+ printf("parsing complete!\n");
+ }
+
+
+
+The %parse_failure directive
+
+The %parse_failure directive specifies a block of C code that
+is executed whenever the parser fails complete. This code is not
+executed until the parser has tried and failed to resolve an input
+error using is usual error recovery strategy. The routine is
+only invoked when parsing is unable to continue.
+
+
+ %parse_failure {
+ fprintf(stderr,"Giving up. Parser is hopelessly lost...\n");
+ }
+
+
+The %right directive
+
+This directive is used to assign right-associative precedence to
+one or more terminal symbols. See the section on precedence rules
+or on the %left directive for additional information.
+
+The %stack_overflow directive
+
+The %stack_overflow directive specifies a block of C code that
+is executed if the parser's internal stack ever overflows. Typically
+this just prints an error message. After a stack overflow, the parser
+will be unable to continue and must be reset.
+
+
+ %stack_overflow {
+ fprintf(stderr,"Giving up. Parser stack overflow\n");
+ }
+
+
+You can help prevent parser stack overflows by avoiding the use
+of right recursion and right-precedence operators in your grammar.
+Use left recursion and and left-precedence operators instead, to
+encourage rules to reduce sooner and keep the stack size down.
+For example, do rules like this:
+
+ list ::= list element. // left-recursion. Good!
+ list ::= .
+
+Not like this:
+
+ list ::= element list. // right-recursion. Bad!
+ list ::= .
+
+
+The %stack_size directive
+
+If stack overflow is a problem and you can't resolve the trouble
+by using left-recursion, then you might want to increase the size
+of the parser's stack using this directive. Put an positive integer
+after the %stack_size directive and Lemon will generate a parse
+with a stack of the requested size. The default value is 100.
+
+
+ %stack_size 2000
+
+
+The %start_symbol directive
+
+By default, the start-symbol for the grammar that Lemon generates
+is the first non-terminal that appears in the grammar file. But you
+can choose a different start-symbol using the %start_symbol directive.
+
+
+ %start_symbol prog
+
+
+The %token_destructor directive
+
+The %destructor directive assigns a destructor to a non-terminal
+symbol. (See the description of the %destructor directive above.)
+This directive does the same thing for all terminal symbols.
+
+Unlike non-terminal symbols which may each have a different data type
+for their values, terminals all use the same data type (defined by
+the %token_type directive) and so they use a common destructor. Other
+than that, the token destructor works just like the non-terminal
+destructors.
+
+The %token_prefix directive
+
+Lemon generates #defines that assign small integer constants
+to each terminal symbol in the grammar. If desired, Lemon will
+add a prefix specified by this directive
+to each of the #defines it generates.
+So if the default output of Lemon looked like this:
+
+ #define AND 1
+ #define MINUS 2
+ #define OR 3
+ #define PLUS 4
+
+You can insert a statement into the grammar like this:
+
+ %token_prefix TOKEN_
+
+to cause Lemon to produce these symbols instead:
+
+ #define TOKEN_AND 1
+ #define TOKEN_MINUS 2
+ #define TOKEN_OR 3
+ #define TOKEN_PLUS 4
+
+
+The %token_type and %type directives
+
+These directives are used to specify the data types for values
+on the parser's stack associated with terminal and non-terminal
+symbols. The values of all terminal symbols must be of the same
+type. This turns out to be the same data type as the 3rd parameter
+to the Parse() function generated by Lemon. Typically, you will
+make the value of a terminal symbol by a pointer to some kind of
+token structure. Like this:
+
+
+ %token_type {Token*}
+
+
+If the data type of terminals is not specified, the default value
+is ``int''.
+
+Non-terminal symbols can each have their own data types. Typically
+the data type of a non-terminal is a pointer to the root of a parse-tree
+structure that contains all information about that non-terminal.
+For example:
+
+
+ %type expr {Expr*}
+
+
+Each entry on the parser's stack is actually a union containing
+instances of all data types for every non-terminal and terminal symbol.
+Lemon will automatically use the correct element of this union depending
+on what the corresponding non-terminal or terminal symbol is. But
+the grammar designer should keep in mind that the size of the union
+will be the size of its largest element. So if you have a single
+non-terminal whose data type requires 1K of storage, then your 100
+entry parser stack will require 100K of heap space. If you are willing
+and able to pay that price, fine. You just need to know.
+
+Error Processing
+
+After extensive experimentation over several years, it has been
+discovered that the error recovery strategy used by yacc is about
+as good as it gets. And so that is what Lemon uses.
+
+When a Lemon-generated parser encounters a syntax error, it
+first invokes the code specified by the %syntax_error directive, if
+any. It then enters its error recovery strategy. The error recovery
+strategy is to begin popping the parsers stack until it enters a
+state where it is permitted to shift a special non-terminal symbol
+named ``error''. It then shifts this non-terminal and continues
+parsing. But the %syntax_error routine will not be called again
+until at least three new tokens have been successfully shifted.
+
+If the parser pops its stack until the stack is empty, and it still
+is unable to shift the error symbol, then the %parse_failed routine
+is invoked and the parser resets itself to its start state, ready
+to begin parsing a new file. This is what will happen at the very
+first syntax error, of course, if there are no instances of the
+``error'' non-terminal in your grammar.
+
+
+
diff --git a/manifest b/manifest
index f96d70fa5a..a2d479053a 100644
--- a/manifest
+++ b/manifest
@@ -1,8 +1,29 @@
-C initial\sempty\scheck-in
-D 2000-05-29T14:16:00
-P
-R d41d8cd98f00b204e9800998ecf8427e
-T *branch * trunk
-T *sym-trunk *
+C initial\scheck-in\sof\sthe\snew\sversion\s(CVS\s1)
+D 2000-05-29T14:26:00
+F Makefile.in 4bd5c67a3a2816e930df4b22df8c1631ee87ff0c
+F configure 8faba4d0194321e5f61a64e842c65eab0f68e6d8 x
+F configure.in 4fc2947d631037bd340b112d6193847d7ac827ae
+F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
+F src/build.c 64016990ebbbcbc848165551732a1f9f397bd150
+F src/dbbe.c ab05293e89525041eaab8b4aca10516db3648792
+F src/dbbe.h bedeb3a0985bb584458e7849fb59927e99e751e6
+F src/main.c 25cce7bce0eb3ba10bada7c05f4b38dc6dbbc86f
+F src/shell.c de770d16aae1ea219e7a9ab5147a4437c23b4a6c
+F src/sqlite.h 2397c17a8f4ca90c09acab0100dc7e2f8f441b69
+F src/sqliteInt.h 0365970442441b5e9b74e1e828afdeac7b0662be
+F src/tclsqlite.c 6eca1c1b8713048245d295ed18dca71c91d4931f
+F src/tokenize.c ab578d90ec6ab117b7ade6e6cfbcb5b0f9cad500
+F src/util.c 370c2339bb9ff82645804a4c62506149392fd032
+F src/vdbe.c 80132b6bb9a744d1990a1c16666d54baaff2dbc3
+F src/vdbe.h e721ad308f2e6ca805cebc4dd0a196ce4419d030
+F src/where.c 67ffea57920e16b33c580e9a9b9855b3ec9dea7b
+F tool/gdbmdump.c 529e67c78d920606ba196326ea55b57b75fcc82b
+F tool/lemon.c cff35578b3c4d1491021b6418016639ebe21b1a5
+F tool/lempar.c a1eec94d6eacc12332368660ec65f3b248853833
+F tool/opNames.awk 2bd9071a138e4e2be13dc98fe066398a61219e1e
+F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
+F tool/renumberOps.awk 6d067177ad5f8d711b79577b462da9b3634bd0a9
+P 704b122e5308587b60b47a5c2fff40c593d4bf8f
+R 33c985d67f2f41286bc65b8529a1ae84
U drh
-Z 8c6f780fffd15dac29a44b424067ccfc
+Z a9e2b0f2d67c72179e4ea5172821c6d6
diff --git a/manifest.uuid b/manifest.uuid
index 919e65c522..1fff0226d4 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-704b122e5308587b60b47a5c2fff40c593d4bf8f
\ No newline at end of file
+6f3655f79f9b6fc9fb7baaa10a7e0f2b6a512dfa
\ No newline at end of file
diff --git a/src/build.c b/src/build.c
new file mode 100644
index 0000000000..ba4eb170da
--- /dev/null
+++ b/src/build.c
@@ -0,0 +1,1435 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** 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 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** General Public License for more details.
+**
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This file contains C code routines that are called by the parser
+** when syntax rules are reduced.
+**
+** $Id: build.c,v 1.1 2000/05/29 14:26:01 drh Exp $
+*/
+#include "sqliteInt.h"
+
+/*
+** This routine is called after a single SQL statement has been
+** parsed and we want to execute the code to implement
+** the statement. Prior action routines should have already
+** constructed VDBE code to do the work of the SQL statement.
+** This routine just has to execute the VDBE code.
+**
+** Note that if an error occurred, it might be the case that
+** no VDBE code was generated.
+*/
+void sqliteExec(Parse *pParse){
+ if( pParse->pVdbe ){
+ if( pParse->explain ){
+ sqliteVdbeList(pParse->pVdbe, pParse->xCallback, pParse->pArg,
+ &pParse->zErrMsg);
+ }else{
+ FILE *trace = (pParse->db->flags & SQLITE_VdbeTrace)!=0 ? stderr : 0;
+ sqliteVdbeTrace(pParse->pVdbe, trace);
+ sqliteVdbeExec(pParse->pVdbe, pParse->xCallback, pParse->pArg,
+ &pParse->zErrMsg);
+ }
+ sqliteVdbeDelete(pParse->pVdbe);
+ pParse->pVdbe = 0;
+ }
+}
+
+/*
+** Construct a new expression node and return a pointer to it.
+*/
+Expr *sqliteExpr(int op, Expr *pLeft, Expr *pRight, Token *pToken){
+ Expr *pNew;
+ pNew = sqliteMalloc( sizeof(Expr) );
+ if( pNew==0 ) return 0;
+ pNew->op = op;
+ pNew->pLeft = pLeft;
+ pNew->pRight = pRight;
+ if( pToken ){
+ pNew->token = *pToken;
+ }else{
+ pNew->token.z = "";
+ pNew->token.n = 0;
+ }
+ return pNew;
+}
+
+/*
+** Construct a new expression node for a function with multiple
+** arguments.
+*/
+Expr *sqliteExprFunction(ExprList *pList, Token *pToken){
+ Expr *pNew;
+ pNew = sqliteMalloc( sizeof(Expr) );
+ if( pNew==0 ) return 0;
+ pNew->op = TK_FUNCTION;
+ pNew->pList = pList;
+ if( pToken ){
+ pNew->token = *pToken;
+ }else{
+ pNew->token.z = "";
+ pNew->token.n = 0;
+ }
+ return pNew;
+}
+
+/*
+** Recursively delete an expression tree.
+*/
+void sqliteExprDelete(Expr *p){
+ if( p==0 ) return;
+ if( p->pLeft ) sqliteExprDelete(p->pLeft);
+ if( p->pRight ) sqliteExprDelete(p->pRight);
+ sqliteFree(p);
+}
+
+/*
+** Locate the in-memory structure that describes the
+** format of a particular database table given the name
+** of that table. Return NULL if not found.
+*/
+Table *sqliteFindTable(sqlite *db, char *zName){
+ Table *pTable;
+ int h;
+
+ h = sqliteHashNoCase(zName, 0) % N_HASH;
+ for(pTable=db->apTblHash[h]; pTable; pTable=pTable->pHash){
+ if( sqliteStrICmp(pTable->zName, zName)==0 ) return pTable;
+ }
+ return 0;
+}
+
+/*
+** Locate the in-memory structure that describes the
+** format of a particular index table given the name
+** of that table. Return NULL if not found.
+*/
+Index *sqliteFindIndex(sqlite *db, char *zName){
+ Index *p;
+ int h;
+
+ h = sqliteHashNoCase(zName, 0) % N_HASH;
+ for(p=db->apIdxHash[h]; p; p=p->pHash){
+ if( sqliteStrICmp(p->zName, zName)==0 ) return p;
+ }
+ return 0;
+}
+
+/*
+** Remove the given index from the index hash table, and free
+** its memory structures.
+**
+** The index is removed from the database hash table, but it is
+** not unlinked from the table that is being indexed. Unlinking
+** from the table must be done by the calling function.
+*/
+static void sqliteDeleteIndex(sqlite *db, Index *pIndex){
+ int h;
+ if( pIndex->zName ){
+ h = sqliteHashNoCase(pIndex->zName, 0) % N_HASH;
+ if( db->apIdxHash[h]==pIndex ){
+ db->apIdxHash[h] = pIndex->pHash;
+ }else{
+ Index *p;
+ for(p=db->apIdxHash[h]; p && p->pHash!=pIndex; p=p->pHash){}
+ if( p && p->pHash==pIndex ){
+ p->pHash = pIndex->pHash;
+ }
+ }
+ }
+ sqliteFree(pIndex);
+}
+
+/*
+** Remove the memory data structures associated with the given
+** table. No changes are made to disk by this routine.
+**
+** This routine just deletes the data structure. It does not unlink
+** the table data structure from the hash table. But does it destroy
+** memory structures of the indices associated with the table.
+*/
+void sqliteDeleteTable(sqlite *db, Table *pTable){
+ int i;
+ Index *pIndex, *pNext;
+ if( pTable==0 ) return;
+ for(i=0; inCol; i++){
+ if( pTable->azCol[i] ) sqliteFree(pTable->azCol[i]);
+ }
+ for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){
+ pNext = pIndex->pNext;
+ sqliteDeleteIndex(db, pIndex);
+ }
+ sqliteFree(pTable->azCol);
+ sqliteFree(pTable);
+}
+
+/*
+** Construct the name of a user table from a token.
+**
+** Space to hold the name is obtained from sqliteMalloc() and must
+** be freed by the calling function.
+*/
+static char *sqliteTableNameFromToken(Token *pName){
+ char *zName = 0;
+ sqliteSetNString(&zName, pName->z, pName->n, 0);
+ return zName;
+}
+
+/*
+** Begin constructing a new table representation in memory. This is
+** the first of several action routines that get called in response
+** to a CREATE TABLE statement.
+*/
+void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){
+ Table *pTable;
+ char *zName;
+
+ pParse->sFirstToken = *pStart;
+ zName = sqliteTableNameFromToken(pName);
+ pTable = sqliteFindTable(pParse->db, zName);
+ if( pTable!=0 ){
+ sqliteSetNString(&pParse->zErrMsg, "table \"", 0, pName->z, pName->n,
+ "\" already exists", 0, 0);
+ sqliteFree(zName);
+ pParse->nErr++;
+ return;
+ }
+ if( sqliteFindIndex(pParse->db, zName) ){
+ sqliteSetString(&pParse->zErrMsg, "there is already an index named \"",
+ zName, "\"", 0);
+ sqliteFree(zName);
+ pParse->nErr++;
+ return;
+ }
+ pTable = sqliteMalloc( sizeof(Table) );
+ if( pTable==0 ){
+ sqliteSetString(&pParse->zErrMsg, "out of memory", 0);
+ pParse->nErr++;
+ return;
+ }
+ pTable->zName = zName;
+ pTable->pHash = 0;
+ pTable->nCol = 0;
+ pTable->azCol = 0;
+ pTable->pIndex = 0;
+ if( pParse->pNewTable ) sqliteDeleteTable(pParse->db, pParse->pNewTable);
+ pParse->pNewTable = pTable;
+}
+
+/*
+** Add a new column to the table currently being constructed.
+*/
+void sqliteAddColumn(Parse *pParse, Token *pName){
+ Table *p;
+ char **pz;
+ if( (p = pParse->pNewTable)==0 ) return;
+ if( (p->nCol & 0x7)==0 ){
+ p->azCol = sqliteRealloc( p->azCol, p->nCol+8);
+ }
+ if( p->azCol==0 ){
+ p->nCol = 0;
+ return;
+ }
+ pz = &p->azCol[p->nCol++];
+ *pz = 0;
+ sqliteSetNString(pz, pName->z, pName->n, 0);
+}
+
+/*
+** This routine is called to report the final ")" that terminates
+** a CREATE TABLE statement.
+**
+** The table structure is added to the internal hash tables.
+**
+** An entry for the table is made in the master table, unless
+** initFlag==1. When initFlag==1, it means we are reading the
+** master table because we just connected to the database, so
+** the entry for this table already exists in the master table.
+** We do not want to create it again.
+*/
+void sqliteEndTable(Parse *pParse, Token *pEnd){
+ Table *p;
+ int h;
+
+ if( pParse->nErr ) return;
+
+ /* Add the table to the in-memory representation of the database
+ */
+ if( (p = pParse->pNewTable)!=0 && pParse->explain==0 ){
+ h = sqliteHashNoCase(p->zName, 0) % N_HASH;
+ p->pHash = pParse->db->apTblHash[h];
+ pParse->db->apTblHash[h] = p;
+ pParse->pNewTable = 0;
+ }
+
+ /* If not initializing, then create the table on disk.
+ */
+ if( !pParse->initFlag ){
+ static VdbeOp addTable[] = {
+ { OP_Open, 0, 0, MASTER_NAME },
+ { OP_New, 0, 0, 0},
+ { OP_String, 0, 0, "table" },
+ { OP_String, 0, 0, 0}, /* 2 */
+ { OP_String, 0, 0, 0}, /* 3 */
+ { OP_String, 0, 0, 0}, /* 4 */
+ { OP_MakeRecord, 4, 0, 0},
+ { OP_Put, 0, 0, 0},
+ { OP_Close, 0, 0, 0},
+ };
+ int n, base;
+ Vdbe *v = pParse->pVdbe;
+
+ if( v==0 ){
+ v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+ }
+ if( v==0 ) return;
+ n = (int)pEnd->z - (int)pParse->sFirstToken.z + 1;
+ base = sqliteVdbeAddOpList(v, ArraySize(addTable), addTable);
+ sqliteVdbeChangeP3(v, base+2, p->zName, 0);
+ sqliteVdbeChangeP3(v, base+3, p->zName, 0);
+ sqliteVdbeChangeP3(v, base+4, pParse->sFirstToken.z, n);
+ }
+}
+
+/*
+** Given a token, look up a table with that name. If not found, leave
+** an error for the parser to find and return NULL.
+*/
+static Table *sqliteTableFromToken(Parse *pParse, Token *pTok){
+ char *zName = sqliteTableNameFromToken(pTok);
+ Table *pTab = sqliteFindTable(pParse->db, zName);
+ sqliteFree(zName);
+ if( pTab==0 ){
+ sqliteSetNString(&pParse->zErrMsg, "no such table: \"", 0,
+ pTok->z, pTok->n, "\"", 1, 0);
+ pParse->nErr++;
+ }
+ return pTab;
+}
+
+/*
+** This routine is called to do the work of a DROP TABLE statement.
+*/
+void sqliteDropTable(Parse *pParse, Token *pName){
+ Table *pTable;
+ int h;
+ Vdbe *v;
+ int base;
+
+ pTable = sqliteTableFromToken(pParse, pName);
+ if( pTable==0 ) return;
+ if( pTable->readOnly ){
+ sqliteSetString(&pParse->zErrMsg, "table \"", pTable->zName,
+ "\" may not be dropped", 0);
+ pParse->nErr++;
+ return;
+ }
+
+ /* Generate code to remove the table and its reference in sys_master */
+ v = pParse->pVdbe;
+ if( v==0 ){
+ v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+ }
+ if( v ){
+ static VdbeOp dropTable[] = {
+ { OP_Open, 0, 0, MASTER_NAME },
+ { OP_ListOpen, 0, 0, 0},
+ { OP_String, 0, 0, 0}, /* 2 */
+ { OP_Next, 0, ADDR(10), 0}, /* 3 */
+ { OP_Dup, 0, 0, 0},
+ { OP_Field, 0, 2, 0},
+ { OP_Ne, 0, ADDR(3), 0},
+ { OP_Key, 0, 0, 0},
+ { OP_ListWrite, 0, 0, 0},
+ { OP_Goto, 0, ADDR(3), 0},
+ { OP_ListRewind, 0, 0, 0}, /* 10 */
+ { OP_ListRead, 0, ADDR(14), 0}, /* 11 */
+ { OP_Delete, 0, 0, 0},
+ { OP_Goto, 0, ADDR(11), 0},
+ { OP_Destroy, 0, 0, 0}, /* 14 */
+ { OP_Close, 0, 0, 0},
+ };
+ Index *pIdx;
+ base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable);
+ sqliteVdbeChangeP3(v, base+2, pTable->zName, 0);
+ sqliteVdbeChangeP3(v, base+14, pTable->zName, 0);
+ for(pIdx=pTable->pIndex; pIdx; pIdx=pIdx->pNext){
+ sqliteVdbeAddOp(v, OP_Destroy, 0, 0, pIdx->zName, 0);
+ }
+ }
+
+ /* Remove the table structure and free its memory.
+ **
+ ** Exception: if the SQL statement began with the EXPLAIN keyword,
+ ** then no changes are made.
+ */
+ if( !pParse->explain ){
+ h = sqliteHashNoCase(pTable->zName, 0) % N_HASH;
+ if( pParse->db->apTblHash[h]==pTable ){
+ pParse->db->apTblHash[h] = pTable->pHash;
+ }else{
+ Table *p;
+ for(p=pParse->db->apTblHash[h]; p && p->pHash!=pTable; p=p->pHash){}
+ if( p && p->pHash==pTable ){
+ p->pHash = pTable->pHash;
+ }
+ }
+ sqliteDeleteTable(pParse->db, pTable);
+ }
+}
+
+/*
+** Create a new index for an SQL table. pIndex is the name of the index
+** and pTable is the name of the table that is to be indexed. Both will
+** be NULL for a primary key. In that case, use pParse->pNewTable as the
+** table to be indexed.
+**
+** pList is a list of fields to be indexed. pList will be NULL if the
+** most recently added field of the table is labeled as the primary key.
+*/
+void sqliteCreateIndex(
+ Parse *pParse, /* All information about this parse */
+ Token *pName, /* Name of the index. May be NULL */
+ Token *pTable, /* Name of the table to index. Use pParse->pNewTable if 0 */
+ IdList *pList, /* A list of fields to be indexed */
+ Token *pStart, /* The CREATE token that begins a CREATE TABLE statement */
+ Token *pEnd /* The ")" that closes the CREATE INDEX statement */
+){
+ Table *pTab; /* Table to be indexed */
+ Index *pIndex; /* The index to be created */
+ char *zName = 0;
+ int i, j, h;
+ Token nullId; /* Fake token for an empty ID list */
+
+ /*
+ ** Find the table that is to be indexed. Return early if not found.
+ */
+ if( pTable!=0 ){
+ pTab = sqliteTableFromToken(pParse, pTable);
+ }else{
+ pTab = pParse->pNewTable;
+ }
+ if( pTab==0 || pParse->nErr ) goto exit_create_index;
+ if( pTab->readOnly ){
+ sqliteSetString(&pParse->zErrMsg, "table \"", pTab->zName,
+ "\" may not have new indices added", 0);
+ pParse->nErr++;
+ goto exit_create_index;
+ }
+
+ /*
+ ** Find the name of the index. Make sure there is not already another
+ ** index or table with the same name.
+ */
+ if( pName ){
+ zName = sqliteTableNameFromToken(pName);
+ }else{
+ zName = 0;
+ sqliteSetString(&zName, pTab->zName, "__primary_key", 0);
+ }
+ if( sqliteFindIndex(pParse->db, zName) ){
+ sqliteSetString(&pParse->zErrMsg, "index \"", zName,
+ "\" already exists", 0);
+ pParse->nErr++;
+ goto exit_create_index;
+ }
+ if( sqliteFindTable(pParse->db, zName) ){
+ sqliteSetString(&pParse->zErrMsg, "there is already a table named \"",
+ zName, "\"", 0);
+ pParse->nErr++;
+ goto exit_create_index;
+ }
+
+ /* If pList==0, it means this routine was called to make a primary
+ ** key out of the last field added to the table under construction.
+ ** So create a fake list to simulate this.
+ */
+ if( pList==0 ){
+ nullId.z = pTab->azCol[pTab->nCol-1];
+ nullId.n = strlen(nullId.z);
+ pList = sqliteIdListAppend(0, &nullId);
+ if( pList==0 ) goto exit_create_index;
+ }
+
+ /*
+ ** Allocate the index structure.
+ */
+ pIndex = sqliteMalloc( sizeof(Index) + strlen(zName) +
+ sizeof(int)*pList->nId );
+ if( pIndex==0 ){
+ sqliteSetString(&pParse->zErrMsg, "out of memory", 0);
+ pParse->nErr++;
+ goto exit_create_index;
+ }
+ pIndex->aiField = (int*)&pIndex[1];
+ pIndex->zName = (char*)&pIndex->aiField[pList->nId];
+ strcpy(pIndex->zName, zName);
+ pIndex->pTable = pTab;
+ pIndex->nField = pList->nId;
+
+ /* Scan the names of the fields of the table to be indexed and
+ ** load the field indices into the Index structure. Report an error
+ ** if any field is not found.
+ */
+ for(i=0; inId; i++){
+ for(j=0; jnCol; j++){
+ if( sqliteStrICmp(pList->a[i].zName, pTab->azCol[j])==0 ) break;
+ }
+ if( j>=pTab->nCol ){
+ sqliteSetString(&pParse->zErrMsg, "table being indexed has no field "
+ "named \"", pList->a[i].zName, "\"", 0);
+ pParse->nErr++;
+ sqliteFree(pIndex);
+ goto exit_create_index;
+ }
+ pIndex->aiField[i] = j;
+ }
+
+ /* Link the new Index structure to its table and to the other
+ ** in-memory database structures.
+ */
+ if( pParse->explain==0 ){
+ h = sqliteHashNoCase(pIndex->zName, 0) % N_HASH;
+ pIndex->pHash = pParse->db->apIdxHash[h];
+ pParse->db->apIdxHash[h] = pIndex;
+ pIndex->pNext = pTab->pIndex;
+ pTab->pIndex = pIndex;
+ }
+
+ /* If the initFlag is 0 then create the index on disk. This
+ ** involves writing the index into the master table and filling in the
+ ** index with the current table contents.
+ **
+ ** The initFlag is 0 when the user first enters a CREATE INDEX
+ ** command. The initFlag is 1 when a database is opened and
+ ** CREATE INDEX statements are read out of the master table. In
+ ** the latter case the index already exists on disk, which is why
+ ** we don't want to recreate it.
+ */
+ if( pParse->initFlag==0 ){
+ static VdbeOp addTable[] = {
+ { OP_Open, 0, 0, MASTER_NAME},
+ { OP_New, 0, 0, 0},
+ { OP_String, 0, 0, "index"},
+ { OP_String, 0, 0, 0}, /* 2 */
+ { OP_String, 0, 0, 0}, /* 3 */
+ { OP_String, 0, 0, 0}, /* 4 */
+ { OP_MakeRecord, 4, 0, 0},
+ { OP_Put, 0, 0, 0},
+ { OP_Close, 0, 0, 0},
+ };
+ int n;
+ Vdbe *v = pParse->pVdbe;
+ int lbl1, lbl2;
+ int i;
+
+ if( v==0 ){
+ v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+ }
+ if( v==0 ) goto exit_create_index;
+ if( pStart && pEnd ){
+ int base;
+ n = (int)pEnd->z - (int)pStart->z + 1;
+ base = sqliteVdbeAddOpList(v, ArraySize(addTable), addTable);
+ sqliteVdbeChangeP3(v, base+2, pIndex->zName, 0);
+ sqliteVdbeChangeP3(v, base+3, pTab->zName, 0);
+ sqliteVdbeChangeP3(v, base+4, pStart->z, n);
+ }
+ sqliteVdbeAddOp(v, OP_Open, 0, 0, pTab->zName, 0);
+ sqliteVdbeAddOp(v, OP_Open, 1, 0, pIndex->zName, 0);
+ lbl1 = sqliteVdbeMakeLabel(v);
+ lbl2 = sqliteVdbeMakeLabel(v);
+ sqliteVdbeAddOp(v, OP_Next, 0, lbl2, 0, lbl1);
+ sqliteVdbeAddOp(v, OP_Key, 0, 0, 0, 0);
+ for(i=0; inField; i++){
+ sqliteVdbeAddOp(v, OP_Field, 0, pIndex->aiField[i], 0, 0);
+ }
+ sqliteVdbeAddOp(v, OP_MakeKey, pIndex->nField, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_PutIdx, 1, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_Goto, 0, lbl1, 0, 0);
+ sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, lbl2);
+ sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_Close, 1, 0, 0, 0);
+ }
+
+ /* Reclaim memory on an EXPLAIN call.
+ */
+ if( pParse->explain ){
+ sqliteFree(pIndex);
+ }
+
+ /* Clean up before exiting */
+exit_create_index:
+ sqliteIdListDelete(pList);
+ sqliteFree(zName);
+ return;
+}
+
+/*
+** This routine will drop an existing named index.
+*/
+void sqliteDropIndex(Parse *pParse, Token *pName){
+ Index *pIndex;
+ char *zName;
+ Vdbe *v;
+
+ zName = sqliteTableNameFromToken(pName);
+ pIndex = sqliteFindIndex(pParse->db, zName);
+ sqliteFree(zName);
+ if( pIndex==0 ){
+ sqliteSetNString(&pParse->zErrMsg, "no such index: \"", 0,
+ pName->z, pName->n, "\"", 1, 0);
+ pParse->nErr++;
+ return;
+ }
+
+ /* Generate code to remove the index and from the master table */
+ v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+ if( v ){
+ static VdbeOp dropIndex[] = {
+ { OP_Open, 0, 0, MASTER_NAME},
+ { OP_ListOpen, 0, 0, 0},
+ { OP_String, 0, 0, 0}, /* 2 */
+ { OP_Next, 0, ADDR(9), 0}, /* 3 */
+ { OP_Dup, 0, 0, 0},
+ { OP_Field, 0, 1, 0},
+ { OP_Ne, 0, ADDR(3), 0},
+ { OP_Key, 0, 0, 0},
+ { OP_Delete, 0, 0, 0},
+ { OP_Destroy, 0, 0, 0}, /* 9 */
+ { OP_Close, 0, 0, 0},
+ };
+ int base;
+
+ base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex);
+ sqliteVdbeChangeP3(v, base+2, pIndex->zName, 0);
+ sqliteVdbeChangeP3(v, base+9, pIndex->zName, 0);
+ }
+
+ /* Remove the index structure and free its memory. Except if the
+ ** EXPLAIN keyword is present, no changes are made.
+ */
+ if( !pParse->explain ){
+ if( pIndex->pTable->pIndex==pIndex ){
+ pIndex->pTable->pIndex = pIndex->pNext;
+ }else{
+ Index *p;
+ for(p=pIndex->pTable->pIndex; p && p->pNext!=pIndex; p=p->pNext){}
+ if( p && p->pNext==pIndex ){
+ p->pNext = pIndex->pNext;
+ }
+ }
+ sqliteDeleteIndex(pParse->db, pIndex);
+ }
+}
+
+/*
+** Add a new element to the end of an expression list. If pList is
+** initially NULL, then create a new expression list.
+*/
+ExprList *sqliteExprListAppend(ExprList *pList, Expr *pExpr, Token *pName){
+ int i;
+ if( pList==0 ){
+ pList = sqliteMalloc( sizeof(ExprList) );
+ }
+ if( pList==0 ) return 0;
+ if( (pList->nExpr & 7)==0 ){
+ int n = pList->nExpr + 8;
+ pList->a = sqliteRealloc(pList->a, n*sizeof(pList->a[0]));
+ if( pList->a==0 ){
+ pList->nExpr = 0;
+ return pList;
+ }
+ }
+ i = pList->nExpr++;
+ pList->a[i].pExpr = pExpr;
+ pList->a[i].zName = 0;
+ if( pName ){
+ sqliteSetNString(&pList->a[i].zName, pName->z, pName->n, 0);
+ }
+ return pList;
+}
+
+/*
+** Delete an entire expression list.
+*/
+void sqliteExprListDelete(ExprList *pList){
+ int i;
+ if( pList==0 ) return;
+ for(i=0; inExpr; i++){
+ sqliteExprDelete(pList->a[i].pExpr);
+ sqliteFree(pList->a[i].zName);
+ }
+ sqliteFree(pList->a);
+ sqliteFree(pList);
+}
+
+/*
+** Append a new element to the given IdList. Create a new IdList if
+** need be.
+*/
+IdList *sqliteIdListAppend(IdList *pList, Token *pToken){
+ if( pList==0 ){
+ pList = sqliteMalloc( sizeof(IdList) );
+ if( pList==0 ) return 0;
+ }
+ if( (pList->nId & 7)==0 ){
+ pList->a = sqliteRealloc(pList->a, (pList->nId+8)*sizeof(pList->a[0]) );
+ if( pList->a==0 ){
+ pList->nId = 0;
+ return pList;
+ }
+ }
+ memset(&pList->a[pList->nId], 0, sizeof(pList->a[0]));
+ if( pToken ){
+ sqliteSetNString(&pList->a[pList->nId].zName, pToken->z, pToken->n, 0);
+ }
+ pList->nId++;
+ return pList;
+}
+
+/*
+** Add an alias to the last identifier on the given identifier list.
+*/
+void sqliteIdListAddAlias(IdList *pList, Token *pToken){
+ if( pList && pList->nId>0 ){
+ int i = pList->nId - 1;
+ sqliteSetNString(&pList->a[i].zAlias, pToken->z, pToken->n, 0);
+ }
+}
+
+/*
+** Delete an entire IdList
+*/
+void sqliteIdListDelete(IdList *pList){
+ int i;
+ if( pList==0 ) return;
+ for(i=0; inId; i++){
+ sqliteFree(pList->a[i].zName);
+ sqliteFree(pList->a[i].zAlias);
+ }
+ sqliteFree(pList->a);
+ sqliteFree(pList);
+}
+
+/*
+** This routine is call to handle SQL of the following form:
+**
+** insert into TABLE (IDLIST) values(EXPRLIST)
+**
+** The parameters are the table name and the expression list.
+*/
+void sqliteInsert(
+ Parse *pParse, /* Parser context */
+ Token *pTableName, /* Name of table into which we are inserting */
+ ExprList *pList, /* List of values to be inserted */
+ IdList *pField /* Field name corresponding to pList. Might be NULL */
+){
+ Table *pTab;
+ char *zTab;
+ int i, j;
+ Vdbe *v;
+
+ zTab = sqliteTableNameFromToken(pTableName);
+ pTab = sqliteFindTable(pParse->db, zTab);
+ sqliteFree(zTab);
+ if( pTab==0 ){
+ sqliteSetNString(&pParse->zErrMsg, "no such table: \"", 0,
+ pTableName->z, pTableName->n, "\"", 1, 0);
+ pParse->nErr++;
+ goto insert_cleanup;
+ }
+ if( pTab->readOnly ){
+ sqliteSetString(&pParse->zErrMsg, "table \"", pTab->zName,
+ "\" may not be modified", 0);
+ pParse->nErr++;
+ goto insert_cleanup;
+ }
+ if( pField==0 && pList->nExpr!=pTab->nCol ){
+ char zNum1[30];
+ char zNum2[30];
+ sprintf(zNum1,"%d", pList->nExpr);
+ sprintf(zNum2,"%d", pTab->nCol);
+ sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
+ " has ", zNum2, " columns but only ",
+ zNum1, " values were supplied", 0);
+ pParse->nErr++;
+ goto insert_cleanup;
+ }
+ if( pField!=0 && pList->nExpr!=pField->nId ){
+ char zNum1[30];
+ char zNum2[30];
+ sprintf(zNum1,"%d", pList->nExpr);
+ sprintf(zNum2,"%d", pTab->nCol);
+ sqliteSetString(&pParse->zErrMsg, zNum1, " values for ",
+ zNum2, " columns", 0);
+ pParse->nErr++;
+ goto insert_cleanup;
+ }
+ if( pField ){
+ for(i=0; inId; i++){
+ pField->a[i].idx = -1;
+ }
+ for(i=0; inId; i++){
+ for(j=0; jnCol; j++){
+ if( sqliteStrICmp(pField->a[i].zName, pTab->azCol[j])==0 ){
+ pField->a[i].idx = j;
+ break;
+ }
+ }
+ if( j>=pTab->nCol ){
+ sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
+ " has no column named ", pField->a[i].zName, 0);
+ pParse->nErr++;
+ goto insert_cleanup;
+ }
+ }
+ }
+ v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+ if( v ){
+ Index *pIdx;
+ sqliteVdbeAddOp(v, OP_Open, 0, 0, pTab->zName, 0);
+ sqliteVdbeAddOp(v, OP_New, 0, 0, 0, 0);
+ if( pTab->pIndex ){
+ sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
+ }
+ for(i=0; inCol; i++){
+ if( pField==0 ){
+ j = i;
+ }else{
+ for(j=0; jnId; j++){
+ if( pField->a[j].idx==i ) break;
+ }
+ }
+ if( pField && j>=pField->nId ){
+ sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
+ }else{
+ sqliteExprCode(pParse, pList->a[j].pExpr);
+ }
+ }
+ sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0);
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( pIdx->pNext ){
+ sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
+ }
+ sqliteVdbeAddOp(v, OP_Open, 0, 0, pIdx->zName, 0);
+ for(i=0; inField; i++){
+ int idx = pIdx->aiField[i];
+ if( pField==0 ){
+ j = idx;
+ }else{
+ for(j=0; jnId; j++){
+ if( pField->a[j].idx==idx ) break;
+ }
+ }
+ if( pField && j>=pField->nId ){
+ sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
+ }else{
+ sqliteExprCode(pParse, pList->a[j].pExpr);
+ }
+ }
+ sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_PutIdx, 0, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0);
+ }
+ }
+
+insert_cleanup:
+ sqliteExprListDelete(pList);
+ sqliteIdListDelete(pField);
+}
+
+/*
+** This routine walks an expression tree and resolves references to
+** table fields. Nodes of the form ID.ID or ID resolve into an
+** index to the table in the table list and a field offset. The opcode
+** for such nodes is changed to TK_FIELD. The iTable value is changed
+** to the index of the referenced table in pTabList, and the iField value
+** is changed to the index of the field of the referenced table.
+**
+** Unknown fields or tables provoke an error. The function returns
+** the number of errors seen and leaves an error message on pParse->zErrMsg.
+*/
+int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
+ if( pExpr==0 ) return 0;
+ switch( pExpr->op ){
+ /* A lone identifier */
+ case TK_ID: {
+ int cnt = 0; /* Number of matches */
+ int i; /* Loop counter */
+ char *z = pExpr->token.z;
+ int n = pExpr->token.n;
+ for(i=0; inId; i++){
+ int j;
+ Table *pTab = pTabList->a[i].pTab;
+ if( pTab==0 ) continue;
+ for(j=0; jnCol; j++){
+ if( sqliteStrNICmp(pTab->azCol[j], z, n)==0 ){
+ cnt++;
+ pExpr->iTable = i;
+ pExpr->iField = j;
+ }
+ }
+ }
+ if( cnt==0 ){
+ sqliteSetNString(&pParse->zErrMsg, "unknown field name: \"", -1,
+ pExpr->token.z, pExpr->token.n, "\"", -1, 0);
+ pParse->nErr++;
+ return 1;
+ }else if( cnt>1 ){
+ sqliteSetNString(&pParse->zErrMsg, "ambiguous field name: \"", -1,
+ pExpr->token.z, pExpr->token.n, "\"", -1, 0);
+ pParse->nErr++;
+ return 1;
+ }
+ pExpr->op = TK_FIELD;
+ break;
+ }
+
+ /* A table name and field name: ID.ID */
+ case TK_DOT: {
+ int cnt = 0; /* Number of matches */
+ int i; /* Loop counter */
+ Expr *pLeft, *pRight; /* Left and right subbranches of the expr */
+ int n; /* Length of an identifier */
+ char *z; /* Text of an identifier */
+
+ pLeft = pExpr->pLeft;
+ pRight = pExpr->pRight;
+ assert( pLeft && pLeft->op==TK_ID );
+ assert( pRight && pRight->op==TK_ID );
+ n = pRight->token.n;
+ z = pRight->token.z;
+ for(i=0; inId; i++){
+ int j;
+ char *zTab;
+ Table *pTab = pTabList->a[i].pTab;
+ if( pTab==0 ) continue;
+ if( pTabList->a[i].zAlias ){
+ zTab = pTabList->a[i].zAlias;
+ }else{
+ zTab = pTab->zName;
+ }
+ if( sqliteStrNICmp(zTab, pLeft->token.z, pLeft->token.n)!=0 ) continue;
+ for(j=0; jnCol; j++){
+ if( sqliteStrNICmp(pTab->azCol[j], z, n)==0 ){
+ cnt++;
+ pExpr->iTable = i;
+ pExpr->iField = j;
+ }
+ }
+ }
+ if( cnt==0 ){
+ sqliteSetNString(&pParse->zErrMsg, "unknown field name: \"", -1,
+ pLeft->token.z, pLeft->token.n, ".", 1, z, n, "\"", 1, 0);
+ pParse->nErr++;
+ return 1;
+ }else if( cnt>1 ){
+ sqliteSetNString(&pParse->zErrMsg, "ambiguous field name: \"", -1,
+ pExpr->token.z, pExpr->token.n, ".", 1, z, n, "\"", 1, 0);
+ pParse->nErr++;
+ return 1;
+ }
+ sqliteExprDelete(pLeft);
+ pExpr->pLeft = 0;
+ sqliteExprDelete(pRight);
+ pExpr->pRight = 0;
+ pExpr->op = TK_FIELD;
+ break;
+ }
+
+ /* For all else, just recursively walk the tree */
+ default: {
+ if( pExpr->pLeft
+ && sqliteExprResolveIds(pParse, pTabList, pExpr->pLeft) ){
+ return 1;
+ }
+ if( pExpr->pRight
+ && sqliteExprResolveIds(pParse, pTabList, pExpr->pRight) ){
+ return 1;
+ }
+ if( pExpr->pList ){
+ int i;
+ ExprList *pList = pExpr->pList;
+ for(i=0; inExpr; i++){
+ if( sqliteExprResolveIds(pParse, pTabList, pList->a[i].pExpr) ){
+ return 1;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** Process a SELECT statement.
+*/
+void sqliteSelect(
+ Parse *pParse, /* The parser context */
+ ExprList *pEList, /* List of fields to extract. NULL means "*" */
+ IdList *pTabList, /* List of tables to select from */
+ Expr *pWhere, /* The WHERE clause. May be NULL */
+ ExprList *pOrderBy /* The ORDER BY clause. May be NULL */
+){
+ int i, j;
+ WhereInfo *pWInfo;
+ Vdbe *v;
+
+ if( pParse->nErr>0 ) goto select_cleanup;
+
+ /* Look up every table in the table list.
+ */
+ for(i=0; inId; i++){
+ pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName);
+ if( pTabList->a[i].pTab==0 ){
+ sqliteSetString(&pParse->zErrMsg, "unknown table \"",
+ pTabList->a[i].zName, "\"", 0);
+ pParse->nErr++;
+ goto select_cleanup;
+ }
+ }
+
+ /* If the list of fields to retrieve is "*" then replace it with
+ ** a list of all fields from all tables.
+ */
+ if( pEList==0 ){
+ for(i=0; inId; i++){
+ Table *pTab = pTabList->a[i].pTab;
+ for(j=0; jnCol; j++){
+ Expr *pExpr = sqliteExpr(TK_FIELD, 0, 0, 0);
+ pExpr->iTable = i;
+ pExpr->iField = j;
+ pEList = sqliteExprListAppend(pEList, pExpr, 0);
+ }
+ }
+ }
+
+ /* Resolve the field names in all the expressions.
+ */
+ for(i=0; inExpr; i++){
+ if( sqliteExprResolveIds(pParse, pTabList, pEList->a[i].pExpr) ){
+ goto select_cleanup;
+ }
+ }
+ if( pWhere && sqliteExprResolveIds(pParse, pTabList, pWhere) ){
+ goto select_cleanup;
+ }
+ if( pOrderBy ){
+ for(i=0; inExpr; i++){
+ if( sqliteExprResolveIds(pParse, pTabList, pOrderBy->a[i].pExpr) ){
+ goto select_cleanup;
+ }
+ }
+ }
+
+ /* Begin generating code.
+ */
+ v = pParse->pVdbe;
+ if( v==0 ){
+ v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+ }
+ if( v==0 ) goto select_cleanup;
+ if( pOrderBy ){
+ sqliteVdbeAddOp(v, OP_SortOpen, 0, 0, 0, 0);
+ }
+
+
+ /* Identify column names
+ */
+ sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0);
+ for(i=0; inExpr; i++){
+ Expr *p;
+ if( pEList->a[i].zName ){
+ char *zName = pEList->a[i].zName;
+ int addr = sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
+ if( zName[0]=='\'' || zName[0]=='"' ){
+ sqliteVdbeDequoteP3(v, addr);
+ }
+ continue;
+ }
+ p = pEList->a[i].pExpr;
+ if( p->op!=TK_FIELD ){
+ char zName[30];
+ sprintf(zName, "field%d", i+1);
+ sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
+ }else{
+ if( pTabList->nId>1 ){
+ char *zName = 0;
+ Table *pTab = pTabList->a[p->iTable].pTab;
+ sqliteSetString(&zName, pTab->zName, ".",
+ pTab->azCol[p->iField], 0);
+ sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
+ sqliteFree(zName);
+ }else{
+ Table *pTab = pTabList->a[0].pTab;
+ sqliteVdbeAddOp(v, OP_ColumnName, i, 0, pTab->azCol[p->iField], 0);
+ }
+ }
+ }
+
+ /* Begin the database scan
+ */
+ pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0);
+ if( pWInfo==0 ) goto select_cleanup;
+
+ /* Pull the requested fields.
+ */
+ for(i=0; inExpr; i++){
+ sqliteExprCode(pParse, pEList->a[i].pExpr);
+ }
+
+ /* If there is no ORDER BY clause, then we can invoke the callback
+ ** right away. If there is an ORDER BY, then we need to put the
+ ** data into an appropriate sorter record.
+ */
+ if( pOrderBy==0 ){
+ sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
+ }else{
+ char *zSortOrder;
+ sqliteVdbeAddOp(v, OP_SortMakeRec, pEList->nExpr, 0, 0, 0);
+ zSortOrder = sqliteMalloc( pOrderBy->nExpr + 1 );
+ if( zSortOrder==0 ) goto select_cleanup;
+ for(i=0; inExpr; i++){
+ zSortOrder[i] = pOrderBy->a[i].idx ? '-' : '+';
+ sqliteExprCode(pParse, pOrderBy->a[i].pExpr);
+ }
+ zSortOrder[pOrderBy->nExpr] = 0;
+ sqliteVdbeAddOp(v, OP_SortMakeKey, pOrderBy->nExpr, 0, zSortOrder, 0);
+ sqliteVdbeAddOp(v, OP_SortPut, 0, 0, 0, 0);
+ }
+
+ /* End the database scan loop.
+ */
+ sqliteWhereEnd(pWInfo);
+
+ /* If there is an ORDER BY clause, then we need to sort the results
+ ** and send them to the callback one by one.
+ */
+ if( pOrderBy ){
+ int end = sqliteVdbeMakeLabel(v);
+ int addr;
+ sqliteVdbeAddOp(v, OP_Sort, 0, 0, 0, 0);
+ addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end, 0, 0);
+ sqliteVdbeAddOp(v, OP_SortCallback, pEList->nExpr, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
+ sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, end);
+ }
+
+ /* Always execute the following code before exiting, in order to
+ ** release resources.
+ */
+select_cleanup:
+ sqliteExprListDelete(pEList);
+ sqliteIdListDelete(pTabList);
+ sqliteExprDelete(pWhere);
+ sqliteExprListDelete(pOrderBy);
+ return;
+}
+
+/*
+** Process a DELETE FROM statement.
+*/
+void sqliteDeleteFrom(
+ Parse *pParse, /* The parser context */
+ Token *pTableName, /* The table from which we should delete things */
+ Expr *pWhere /* The WHERE clause. May be null */
+){
+ Vdbe *v; /* The virtual database engine */
+ Table *pTab; /* The table from which records will be deleted */
+ IdList *pTabList; /* An ID list holding pTab and nothing else */
+ int end, addr; /* A couple addresses of generated code */
+ int i; /* Loop counter */
+ WhereInfo *pWInfo; /* Information about the WHERE clause */
+ Index *pIdx; /* For looping over indices of the table */
+
+ /* Locate the table which we want to update. This table has to be
+ ** put in an IdList structure because some of the subroutines will
+ ** will be calling are designed to work with multiple tables and expect
+ ** an IdList* parameter instead of just a Table* parameger.
+ */
+ pTabList = sqliteIdListAppend(0, pTableName);
+ for(i=0; inId; i++){
+ pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName);
+ if( pTabList->a[i].pTab==0 ){
+ sqliteSetString(&pParse->zErrMsg, "unknown table \"",
+ pTabList->a[i].zName, "\"", 0);
+ pParse->nErr++;
+ goto delete_from_cleanup;
+ }
+ if( pTabList->a[i].pTab->readOnly ){
+ sqliteSetString(&pParse->zErrMsg, "table \"", pTabList->a[i].zName,
+ "\" may not be modified", 0);
+ pParse->nErr++;
+ goto delete_from_cleanup;
+ }
+ }
+ pTab = pTabList->a[0].pTab;
+
+ /* Resolve the field names in all the expressions.
+ */
+ if( pWhere && sqliteExprResolveIds(pParse, pTabList, pWhere) ){
+ goto delete_from_cleanup;
+ }
+
+ /* Begin generating code.
+ */
+ v = pParse->pVdbe;
+ if( v==0 ){
+ v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+ }
+ if( v==0 ) goto delete_from_cleanup;
+
+ /* Begin the database scan
+ */
+ sqliteVdbeAddOp(v, OP_ListOpen, 0, 0, 0, 0);
+ pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1);
+ if( pWInfo==0 ) goto delete_from_cleanup;
+
+ /* Remember the index of every item to be deleted.
+ */
+ sqliteVdbeAddOp(v, OP_ListWrite, 0, 0, 0, 0);
+
+ /* End the database scan loop.
+ */
+ sqliteWhereEnd(pWInfo);
+
+ /* Delete every item identified in the list.
+ */
+ sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0);
+ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
+ sqliteVdbeAddOp(v, OP_Open, i, 0, pIdx->zName, 0);
+ }
+ end = sqliteVdbeMakeLabel(v);
+ addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0);
+ if( pTab->pIndex ){
+ sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_Fetch, 0, 0, 0, 0);
+ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
+ int j;
+ sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
+ for(j=0; jnField; j++){
+ sqliteVdbeAddOp(v, OP_Field, 0, pIdx->aiField[j], 0, 0);
+ }
+ sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_DeleteIdx, i, 0, 0, 0);
+ }
+ }
+ sqliteVdbeAddOp(v, OP_Delete, 0, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
+ sqliteVdbeAddOp(v, OP_ListClose, 0, 0, 0, end);
+
+delete_from_cleanup:
+ sqliteIdListDelete(pTabList);
+ sqliteExprDelete(pWhere);
+ return;
+}
+
+/*
+** Process an UPDATE statement.
+*/
+void sqliteUpdate(
+ Parse *pParse, /* The parser context */
+ Token *pTableName, /* The table in which we should change things */
+ ExprList *pChanges, /* Things to be changed */
+ Expr *pWhere /* The WHERE clause. May be null */
+){
+ int i, j; /* Loop counters */
+ Table *pTab; /* The table to be updated */
+ IdList *pTabList = 0; /* List containing only pTab */
+ int end, addr; /* A couple of addresses in the generated code */
+ WhereInfo *pWInfo; /* Information about the WHERE clause */
+ Vdbe *v; /* The virtual database engine */
+ Index *pIdx; /* For looping over indices */
+ int nIdx; /* Number of indices that need updating */
+ Index **apIdx = 0; /* An array of indices that need updating too */
+ int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the
+ ** an expression for the i-th field of the table.
+ ** aXRef[i]==-1 if the i-th field is not changed. */
+
+ /* Locate the table which we want to update. This table has to be
+ ** put in an IdList structure because some of the subroutines will
+ ** will be calling are designed to work with multiple tables and expect
+ ** an IdList* parameter instead of just a Table* parameger.
+ */
+ pTabList = sqliteIdListAppend(0, pTableName);
+ for(i=0; inId; i++){
+ pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName);
+ if( pTabList->a[i].pTab==0 ){
+ sqliteSetString(&pParse->zErrMsg, "unknown table \"",
+ pTabList->a[i].zName, "\"", 0);
+ pParse->nErr++;
+ goto update_cleanup;
+ }
+ if( pTabList->a[i].pTab->readOnly ){
+ sqliteSetString(&pParse->zErrMsg, "table \"", pTabList->a[i].zName,
+ "\" may not be modified", 0);
+ pParse->nErr++;
+ goto update_cleanup;
+ }
+ }
+ pTab = pTabList->a[0].pTab;
+ aXRef = sqliteMalloc( sizeof(int) * pTab->nCol );
+ if( aXRef==0 ) goto update_cleanup;
+ for(i=0; inCol; i++) aXRef[i] = -1;
+
+ /* Resolve the field names in all the expressions in both the
+ ** WHERE clause and in the new values. Also find the field index
+ ** for each field to be updated in the pChanges array.
+ */
+ if( pWhere && sqliteExprResolveIds(pParse, pTabList, pWhere) ){
+ goto update_cleanup;
+ }
+ for(i=0; inExpr; i++){
+ if( sqliteExprResolveIds(pParse, pTabList, pChanges->a[i].pExpr) ){
+ goto update_cleanup;
+ }
+ for(j=0; jnCol; j++){
+ if( strcmp(pTab->azCol[j], pChanges->a[i].zName)==0 ){
+ pChanges->a[i].idx = j;
+ aXRef[j] = i;
+ break;
+ }
+ }
+ if( j>=pTab->nCol ){
+ sqliteSetString(&pParse->zErrMsg, "no such field: \"",
+ pChanges->a[i].zName, "\"", 0);
+ pParse->nErr++;
+ goto update_cleanup;
+ }
+ }
+
+ /* Allocate memory for the array apIdx[] and fill it pointers to every
+ ** index that needs to be updated. Indices only need updating if their
+ ** key includes one of the fields named in pChanges.
+ */
+ for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ for(i=0; inField; i++){
+ if( aXRef[pIdx->aiField[i]]>=0 ) break;
+ }
+ if( inField ) nIdx++;
+ }
+ apIdx = sqliteMalloc( sizeof(Index*) * nIdx );
+ if( apIdx==0 ) goto update_cleanup;
+ for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ for(i=0; inField; i++){
+ if( aXRef[pIdx->aiField[i]]>=0 ) break;
+ }
+ if( inField ) apIdx[nIdx++] = pIdx;
+ }
+
+ /* Begin generating code.
+ */
+ v = pParse->pVdbe;
+ if( v==0 ){
+ v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+ }
+ if( v==0 ) goto update_cleanup;
+
+ /* Begin the database scan
+ */
+ sqliteVdbeAddOp(v, OP_ListOpen, 0, 0, 0, 0);
+ pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1);
+ if( pWInfo==0 ) goto update_cleanup;
+
+ /* Remember the index of every item to be updated.
+ */
+ sqliteVdbeAddOp(v, OP_ListWrite, 0, 0, 0, 0);
+
+ /* End the database scan loop.
+ */
+ sqliteWhereEnd(pWInfo);
+
+ /* Rewind the list of records that need to be updated and
+ ** open every index that needs updating.
+ */
+ sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0);
+ for(i=0; izName, 0);
+ }
+
+ /* Loop over every record that needs updating. We have to load
+ ** the old data for each record to be updated because some fields
+ ** might not change and we will need to copy the old value, therefore.
+ ** Also, the old data is needed to delete the old index entires.
+ */
+ end = sqliteVdbeMakeLabel(v);
+ addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0);
+ sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_Fetch, 0, 0, 0, 0);
+
+ /* Delete the old indices for the current record.
+ */
+ for(i=0; inField; j++){
+ sqliteVdbeAddOp(v, OP_Field, 0, pIdx->aiField[j], 0, 0);
+ }
+ sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_DeleteIdx, i+1, 0, 0, 0);
+ }
+
+ /* Compute a completely new data for this record.
+ */
+ for(i=0; inCol; i++){
+ j = aXRef[i];
+ if( j<0 ){
+ sqliteVdbeAddOp(v, OP_Field, 0, i, 0, 0);
+ }else{
+ sqliteExprCode(pParse, pChanges->a[j].pExpr);
+ }
+ }
+
+ /* Insert new index entries that correspond to the new data
+ */
+ for(i=0; inCol, 0, 0, 0); /* The KEY */
+ pIdx = apIdx[i];
+ for(j=0; jnField; j++){
+ sqliteVdbeAddOp(v, OP_Dup, j+pTab->nCol-pIdx->aiField[j], 0, 0, 0);
+ }
+ sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_PutIdx, i+1, 0, 0, 0);
+ }
+
+ /* Write the new data back into the database.
+ */
+ sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0);
+
+ /* Repeat the above with the next record to be updated, until
+ ** all record selected by the WHERE clause have been updated.
+ */
+ sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
+ sqliteVdbeAddOp(v, OP_ListClose, 0, 0, 0, end);
+
+update_cleanup:
+ sqliteFree(apIdx);
+ sqliteFree(aXRef);
+ sqliteIdListDelete(pTabList);
+ sqliteExprListDelete(pChanges);
+ sqliteExprDelete(pWhere);
+ return;
+}
diff --git a/src/dbbe.c b/src/dbbe.c
new file mode 100644
index 0000000000..808d3429fd
--- /dev/null
+++ b/src/dbbe.c
@@ -0,0 +1,510 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** 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 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** General Public License for more details.
+**
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This file contains code to implement the database baseend (DBBE)
+** for sqlite. The database backend is the interface between
+** sqlite and the code that does the actually reading and writing
+** of information to the disk.
+**
+** This file uses GDBM as the database backend. It should be
+** relatively simple to convert to a different database such
+** as NDBM, SDBM, or BerkeleyDB.
+**
+** $Id: dbbe.c,v 1.1 2000/05/29 14:26:01 drh Exp $
+*/
+#include "sqliteInt.h"
+#include
+#include
+#include
+#include
+#include
+
+/*
+** Each open database file is an instance of this structure.
+*/
+typedef struct BeFile BeFile;
+struct BeFile {
+ char *zName; /* Name of the file */
+ GDBM_FILE dbf; /* The file itself */
+ int nRef; /* Number of references */
+ BeFile *pNext, *pPrev; /* Next and previous on list of open files */
+};
+
+/*
+** The complete database is an instance of the following structure.
+*/
+struct Dbbe {
+ char *zDir; /* The directory containing the database */
+ int write; /* True for write permission */
+ BeFile *pOpen; /* List of open files */
+ int nTemp; /* Number of temporary files created */
+ FILE **apTemp; /* Space to hold temporary file pointers */
+};
+
+/*
+** Each file within the database is an instance of this
+** structure.
+*/
+struct DbbeTable {
+ Dbbe *pBe; /* The database of which this record is a part */
+ BeFile *pFile; /* The database file for this table */
+ datum key; /* Most recently used key */
+ datum data; /* Most recent data */
+ int needRewind; /* Next key should be the first */
+ int readPending; /* The fetch hasn't actually been done yet */
+};
+
+/*
+** This routine opens a new database. For the current driver scheme,
+** the database name is the name of the directory
+** containing all the files of the database.
+*/
+Dbbe *sqliteDbbeOpen(
+ const char *zName, /* The name of the database */
+ int write, /* True if we will be writing to the database */
+ int create, /* True to create database if it doesn't exist */
+ char **pzErrMsg /* Write error messages (if any) here */
+){
+ Dbbe *pNew;
+ struct stat statbuf;
+
+ if( stat(zName, &statbuf)!=0 ){
+ sqliteSetString(pzErrMsg, "can't find file \"", zName, "\"", 0);
+ return 0;
+ }
+ if( !S_ISDIR(statbuf.st_mode) ){
+ sqliteSetString(pzErrMsg, "not a directory: \"", zName, "\"", 0);
+ return 0;
+ }
+ pNew = sqliteMalloc(sizeof(Dbbe) + strlen(zName) + 1);
+ if( pNew==0 ){
+ sqliteSetString(pzErrMsg, "out of memory", 0);
+ return 0;
+ }
+ pNew->zDir = (char*)&pNew[1];
+ strcpy(pNew->zDir, zName);
+ pNew->write = write;
+ pNew->pOpen = 0;
+ return pNew;
+}
+
+/*
+** Completely shutdown the given database. Close all files. Free all memory.
+*/
+void sqliteDbbeClose(Dbbe *pBe){
+ BeFile *pFile, *pNext;
+ for(pFile=pBe->pOpen; pFile; pFile=pNext){
+ pNext = pFile->pNext;
+ gdbm_close(pFile->dbf);
+ memset(pFile, 0, sizeof(*pFile));
+ sqliteFree(pFile);
+ }
+ memset(pBe, 0, sizeof(*pBe));
+ sqliteFree(pBe);
+}
+
+/*
+** Translate the name of a table into the name of a file that holds
+** that table. Space to hold the filename is obtained from
+** sqliteMalloc() and must be freed by the calling function.
+*/
+static char *sqliteFileOfTable(Dbbe *pBe, const char *zTable){
+ char *zFile = 0;
+ int i;
+ sqliteSetString(&zFile, pBe->zDir, "/", zTable, ".tbl", 0);
+ if( zFile==0 ) return 0;
+ for(i=strlen(pBe->zDir)+1; zFile[i]; i++){
+ int c = zFile[i];
+ if( isupper(c) ){
+ zFile[i] = tolower(c);
+ }else if( !isalnum(c) && c!='-' && c!='_' && c!='.' ){
+ zFile[i] = '+';
+ }
+ }
+ return zFile;
+}
+
+/*
+** Open a new table cursor
+*/
+DbbeTable *sqliteDbbeOpenTable(
+ Dbbe *pBe, /* The database the table belongs to */
+ const char *zTable, /* The name of the table */
+ int writeable /* True to open for writing */
+){
+ char *zFile; /* Name of the table file */
+ DbbeTable *pTable; /* The new table cursor */
+ BeFile *pFile; /* The underlying data file for this table */
+
+ pTable = sqliteMalloc( sizeof(*pTable) );
+ if( pTable==0 ) return 0;
+ zFile = sqliteFileOfTable(pBe, zTable);
+ for(pFile=pBe->pOpen; pFile; pFile=pFile->pNext){
+ if( strcmp(pFile->zName,zFile)==0 ) break;
+ }
+ if( pFile==0 ){
+ pFile = sqliteMalloc( sizeof(*pFile) );
+ if( pFile==0 ){
+ sqliteFree(zFile);
+ return 0;
+ }
+ pFile->zName = zFile;
+ pFile->nRef = 1;
+ pFile->pPrev = 0;
+ if( pBe->pOpen ){
+ pBe->pOpen->pPrev = pFile;
+ }
+ pFile->pNext = pBe->pOpen;
+ pBe->pOpen = pFile;
+ pFile->dbf = gdbm_open(pFile->zName, 0, GDBM_WRCREAT, 0640, 0);
+ }else{
+ sqliteFree(zFile);
+ pFile->nRef++;
+ }
+ pTable->pBe = pBe;
+ pTable->pFile = pFile;
+ pTable->readPending = 0;
+ pTable->needRewind = 1;
+ return pTable;
+}
+
+/*
+** Drop a table from the database.
+*/
+void sqliteDbbeDropTable(Dbbe *pBe, const char *zTable){
+ char *zFile; /* Name of the table file */
+
+ zFile = sqliteFileOfTable(pBe, zTable);
+ unlink(zFile);
+ sqliteFree(zFile);
+}
+
+/*
+** Close a table previously opened by sqliteDbbeOpenTable().
+*/
+void sqliteDbbeCloseTable(DbbeTable *pTable){
+ BeFile *pFile;
+ Dbbe *pBe;
+ if( pTable==0 ) return;
+ pFile = pTable->pFile;
+ pBe = pTable->pBe;
+ pFile->nRef--;
+ if( pFile->nRef<=0 ){
+ if( pFile->dbf!=NULL ){
+ gdbm_close(pFile->dbf);
+ }
+ if( pFile->pPrev ){
+ pFile->pPrev->pNext = pFile->pNext;
+ }else{
+ pBe->pOpen = pFile->pNext;
+ }
+ if( pFile->pNext ){
+ pFile->pNext->pPrev = pFile->pPrev;
+ }
+ sqliteFree(pFile->zName);
+ memset(pFile, 0, sizeof(*pFile));
+ sqliteFree(pFile);
+ }
+ if( pTable->key.dptr ) free(pTable->key.dptr);
+ if( pTable->data.dptr ) free(pTable->data.dptr);
+ memset(pTable, 0, sizeof(*pTable));
+ sqliteFree(pTable);
+}
+
+/*
+** Clear the given datum
+*/
+static void datumClear(datum *p){
+ if( p->dptr ) free(p->dptr);
+ p->dptr = 0;
+ p->dsize = 0;
+}
+
+/*
+** Fetch a single record from an open table. Return 1 on success
+** and 0 on failure.
+*/
+int sqliteDbbeFetch(DbbeTable *pTable, int nKey, char *pKey){
+ datum key;
+ key.dsize = nKey;
+ key.dptr = pKey;
+ datumClear(&pTable->key);
+ datumClear(&pTable->data);
+ if( pTable->pFile && pTable->pFile->dbf ){
+ pTable->data = gdbm_fetch(pTable->pFile->dbf, key);
+ }
+ return pTable->data.dptr!=0;
+}
+
+/*
+** Copy bytes from the current key or data into a buffer supplied by
+** the calling function. Return the number of bytes copied.
+*/
+int sqliteDbbeCopyKey(DbbeTable *pTable, int offset, int size, char *zBuf){
+ int n;
+ if( offset>=pTable->key.dsize ) return 0;
+ if( offset+size>pTable->key.dsize ){
+ n = pTable->key.dsize - offset;
+ }else{
+ n = size;
+ }
+ memcpy(zBuf, &pTable->key.dptr[offset], n);
+ return n;
+}
+int sqliteDbbeCopyData(DbbeTable *pTable, int offset, int size, char *zBuf){
+ int n;
+ if( pTable->readPending && pTable->pFile && pTable->pFile->dbf ){
+ pTable->data = gdbm_fetch(pTable->pFile->dbf, pTable->key);
+ pTable->readPending = 0;
+ }
+ if( offset>=pTable->data.dsize ) return 0;
+ if( offset+size>pTable->data.dsize ){
+ n = pTable->data.dsize - offset;
+ }else{
+ n = size;
+ }
+ memcpy(zBuf, &pTable->data.dptr[offset], n);
+ return n;
+}
+
+/*
+** Return a pointer to bytes from the key or data. The data returned
+** is ephemeral.
+*/
+char *sqliteDbbeReadKey(DbbeTable *pTable, int offset){
+ if( offset<0 || offset>=pTable->key.dsize ) return "";
+ return &pTable->key.dptr[offset];
+}
+char *sqliteDbbeReadData(DbbeTable *pTable, int offset){
+ if( pTable->readPending && pTable->pFile && pTable->pFile->dbf ){
+ pTable->data = gdbm_fetch(pTable->pFile->dbf, pTable->key);
+ pTable->readPending = 0;
+ }
+ if( offset<0 || offset>=pTable->data.dsize ) return "";
+ return &pTable->data.dptr[offset];
+}
+
+/*
+** Return the total number of bytes in either data or key.
+*/
+int sqliteDbbeKeyLength(DbbeTable *pTable){
+ return pTable->key.dsize;
+}
+int sqliteDbbeDataLength(DbbeTable *pTable){
+ if( pTable->readPending && pTable->pFile && pTable->pFile->dbf ){
+ pTable->data = gdbm_fetch(pTable->pFile->dbf, pTable->key);
+ pTable->readPending = 0;
+ }
+ return pTable->data.dsize;
+}
+
+/*
+** Make is so that the next call to sqliteNextKey() finds the first
+** key of the table.
+*/
+int sqliteDbbeRewind(DbbeTable *pTable){
+ pTable->needRewind = 1;
+ return 0;
+}
+
+/*
+** Read the next key from the table. Return 1 on success. Return
+** 0 if there are no more keys.
+*/
+int sqliteDbbeNextKey(DbbeTable *pTable){
+ datum nextkey;
+ int rc;
+ if( pTable==0 || pTable->pFile==0 || pTable->pFile->dbf==0 ){
+ pTable->readPending = 0;
+ return 0;
+ }
+ if( pTable->needRewind ){
+ nextkey = gdbm_firstkey(pTable->pFile->dbf);
+ pTable->needRewind = 0;
+ }else{
+ nextkey = gdbm_nextkey(pTable->pFile->dbf, pTable->key);
+ }
+ datumClear(&pTable->key);
+ datumClear(&pTable->data);
+ pTable->key = nextkey;
+ if( pTable->key.dptr ){
+ pTable->readPending = 1;
+ rc = 1;
+ }else{
+ pTable->needRewind = 1;
+ pTable->readPending = 0;
+ rc = 0;
+ }
+ return rc;
+}
+
+/*
+** The following are state variables for the RC4 algorithm. We
+** use RC4 as a random number generator. Each call to RC4 gives
+** a random 8-bit number.
+*/
+static struct {
+ int i, j;
+ int s[256];
+} rc4;
+
+/*
+** Initialize the RC4 algorithm.
+*/
+static void rc4init(char *key, int keylen){
+ int i;
+ char k[256];
+ rc4.j = 0;
+ rc4.i = 0;
+ for(i=0; i<256; i++){
+ rc4.s[i] = i;
+ k[i] = key[i%keylen];
+ }
+ for(i=0; i<256; i++){
+ int t;
+ rc4.j = (rc4.j + rc4.s[i] + k[i]) & 0xff;
+ t = rc4.s[rc4.j];
+ rc4.s[rc4.j] = rc4.s[i];
+ rc4.s[i] = t;
+ }
+}
+
+/*
+** Get a single 8-bit random value from the RC4 algorithm.
+*/
+static int rc4byte(void){
+ int t;
+ rc4.i = (rc4.i + 1) & 0xff;
+ rc4.j = (rc4.j + rc4.s[rc4.i]) & 0xff;
+ t = rc4.s[rc4.i];
+ rc4.s[rc4.i] = rc4.s[rc4.j];
+ rc4.s[rc4.j] = t;
+ t = rc4.s[rc4.i] + rc4.s[rc4.j];
+ return t & 0xff;
+}
+
+/*
+** Get a new integer key.
+*/
+int sqliteDbbeNew(DbbeTable *pTable){
+ static int isInit = 0;
+ int iKey;
+ datum key;
+ int go = 1;
+ int i;
+
+ if( !isInit ){
+ struct stat statbuf;
+ stat(pTable->pFile->zName, &statbuf);
+ time(&statbuf.st_ctime);
+ rc4init((char*)&statbuf, sizeof(statbuf));
+ isInit = 1;
+ }
+ if( pTable->pFile==0 || pTable->pFile->dbf==0 ) return 1;
+ while( go ){
+ iKey = 0;
+ for(i=0; i<4; i++){
+ iKey = (iKey<<8) + rc4byte();
+ }
+ key.dptr = (char*)&iKey;
+ key.dsize = 4;
+ go = gdbm_exists(pTable->pFile->dbf, key);
+ }
+ return iKey;
+}
+
+/*
+** Write an entry into the table. Overwrite any prior entry with the
+** same key.
+*/
+int sqliteDbbePut(DbbeTable *pTable, int nKey,char *pKey,int nData,char *pData){
+ datum data, key;
+ if( pTable->pFile==0 || pTable->pFile->dbf==0 ) return 0;
+ data.dsize = nData;
+ data.dptr = pData;
+ key.dsize = nKey;
+ key.dptr = pKey;
+ gdbm_store(pTable->pFile->dbf, key, data, GDBM_REPLACE);
+ datumClear(&pTable->key);
+ datumClear(&pTable->data);
+ return 1;
+}
+
+/*
+** Remove an entry from a table, if the entry exists.
+*/
+int sqliteDbbeDelete(DbbeTable *pTable, int nKey, char *pKey){
+ datum key;
+ datumClear(&pTable->key);
+ datumClear(&pTable->data);
+ if( pTable->pFile==0 || pTable->pFile->dbf==0 ) return 0;
+ key.dsize = nKey;
+ key.dptr = pKey;
+ gdbm_delete(pTable->pFile->dbf, key);
+ return 1;
+}
+
+/*
+** Open a temporary file.
+*/
+FILE *sqliteDbbeOpenTempFile(Dbbe *pBe){
+ char *zFile;
+ char zBuf[30];
+ int i;
+
+ for(i=0; inTemp; i++){
+ if( pBe->apTemp[i]==0 ) break;
+ }
+ if( i>=pBe->nTemp ){
+ pBe->nTemp++;
+ pBe->apTemp = sqliteRealloc(pBe->apTemp, pBe->nTemp*sizeof(FILE*) );
+ }
+ if( pBe->apTemp==0 ) return 0;
+ sprintf(zBuf, "/_temp_%d~", i);
+ zFile = 0;
+ sqliteSetString(&zFile, pBe->zDir, zBuf, 0);
+ pBe->apTemp[i] = fopen(zFile, "w+");
+ sqliteFree(zFile);
+ return pBe->apTemp[i];
+}
+
+/*
+** Close a temporary file opened using sqliteDbbeOpenTempFile()
+*/
+void sqliteDbbeCloseTempFile(Dbbe *pBe, FILE *f){
+ int i;
+ for(i=0; inTemp; i++){
+ if( pBe->apTemp[i]==f ){
+ char *zFile;
+ char zBuf[30];
+ sprintf(zBuf, "/_temp_%d~", i);
+ zFile = 0;
+ sqliteSetString(&zFile, pBe->zDir, zBuf, 0);
+ unlink(zFile);
+ sqliteFree(zFile);
+ pBe->apTemp[i] = 0;
+ break;
+ }
+ }
+ fclose(f);
+}
diff --git a/src/dbbe.h b/src/dbbe.h
new file mode 100644
index 0000000000..26e83a6485
--- /dev/null
+++ b/src/dbbe.h
@@ -0,0 +1,119 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** 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 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** General Public License for more details.
+**
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This file defines the interface to the database backend (Dbbe).
+**
+** The database backend is designed to be as general as possible
+** so that it can easily be replaced by a different backend.
+** This library was originally designed to support the following
+** backends: GDBM, NDBM, SDBM, Berkeley DB.
+**
+** $Id: dbbe.h,v 1.1 2000/05/29 14:26:01 drh Exp $
+*/
+#ifndef _SQLITE_DBBE_H_
+#define _SQLITE_DBBE_H_
+#include
+
+/*
+** The database backend supports two opaque structures. A Dbbe is
+** a context for the entire set of tables forming a complete
+** database. A DbbeTable is a single table.
+**
+** The DbbeTable structure holds some state information, such as
+** the key and data from the last retrieval. For this reason,
+** the backend must allow the creation of multiple independent
+** DbbeTable structures for each table in the database.
+*/
+typedef struct Dbbe Dbbe;
+typedef struct DbbeTable DbbeTable;
+
+/*
+** The 18 interface routines.
+*/
+
+/* Open a complete database */
+Dbbe *sqliteDbbeOpen(const char *zName, int write, int create, char **pzErr);
+
+/* Close the whole database. */
+void sqliteDbbeClose(Dbbe*);
+
+/* Open a particular table of a previously opened database.
+** Create the table if it doesn't already exist and writeable!=0.
+*/
+DbbeTable *sqliteDbbeOpenTable(Dbbe*, const char *zTableName, int writeable);
+
+/* Delete a table from the database */
+void sqliteDbbeDropTable(Dbbe*, const char *zTableName);
+
+/* Close a table */
+void sqliteDbbeCloseTable(DbbeTable*);
+
+/* Fetch an entry from a table with the given key. Return 1 if
+** successful and 0 if no such entry exists.
+*/
+int sqliteDbbeFetch(DbbeTable*, int nKey, char *pKey);
+
+/* Retrieve the key or data used for the last fetch. Only size
+** bytes are read beginning with the offset-th byte. The return
+** value is the actual number of bytes read.
+*/
+int sqliteDbbeCopyKey(DbbeTable*, int offset, int size, char *zBuf);
+int sqliteDbbeCopyData(DbbeTable*, int offset, int size, char *zBuf);
+
+/* Retrieve the key or data. The result is ephemeral.
+*/
+char *sqliteDbbeReadKey(DbbeTable*, int offset);
+char *sqliteDbbeReadData(DbbeTable*, int offset);
+
+/* Return the length of the most recently fetched key or data. */
+int sqliteDbbeKeyLength(DbbeTable*);
+int sqliteDbbeDataLength(DbbeTable*);
+
+/* Retrieve the next entry in the table. The first key is retrieved
+** the first time this routine is called, or after a call to
+** sqliteDbbeRewind(). The return value is 1 if there is another
+** entry, or 0 if there are no more entries. */
+int sqliteDbbeNextKey(DbbeTable*);
+
+/* Make it so that the next call to sqliteDbbeNextKey() returns
+** the first entry of the table. */
+int sqliteDbbeRewind(DbbeTable*);
+
+/* Get a new integer key for this table. */
+int sqliteDbbeNew(DbbeTable*);
+
+/* Write an entry into a table. If another entry already exists with
+** the same key, the old entry is discarded first.
+*/
+int sqliteDbbePut(DbbeTable*, int nKey, char *pKey, int nData, char *pData);
+
+/* Remove an entry from the table */
+int sqliteDbbeDelete(DbbeTable*, int nKey, char *pKey);
+
+/* Open a file suitable for temporary storage */
+FILE *sqliteDbbeOpenTempFile(Dbbe*);
+
+/* Close a temporary file */
+void sqliteDbbeCloseTempFile(Dbbe *, FILE *);
+
+#endif /* defined(_SQLITE_DBBE_H_) */
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000000..4bbc75cedd
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,219 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** 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 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** General Public License for more details.
+**
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*************************************************************************
+** Main file for the SQLite library. The routines in this file
+** implement the programmer interface to the library. Routines in
+** other files are for internal use by SQLite and should not be
+** accessed by users of the library.
+**
+** $Id: main.c,v 1.1 2000/05/29 14:26:01 drh Exp $
+*/
+#include "sqliteInt.h"
+
+/*
+** This is the callback routine for the code that initializes the
+** database. Each callback contains text of a CREATE TABLE or
+** CREATE INDEX statement that must be parsed to yield the internal
+** structures that describe the tables.
+*/
+static int sqliteOpenCb(void *pDb, int argc, char **argv, char **azColName){
+ sqlite *db = (sqlite*)pDb;
+ Parse sParse;
+ int nErr;
+ char *zErrMsg = 0;
+
+ if( argc!=1 ) return 0;
+ memset(&sParse, 0, sizeof(sParse));
+ sParse.db = db;
+ sParse.initFlag = 1;
+ nErr = sqliteRunParser(&sParse, argv[0], &zErrMsg);
+ return nErr;
+}
+
+/*
+** Open a new SQLite database. Construct an "sqlite" structure to define
+** the state of this database and return a pointer to that structure.
+*/
+sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
+ sqlite *db;
+ Vdbe *vdbe;
+ Table *pTab;
+ char *azArg[2];
+ static char master_schema[] =
+ "CREATE TABLE " MASTER_NAME " (\n"
+ " type text,\n"
+ " name text,\n"
+ " tbl_name text,\n"
+ " sql text\n"
+ ")"
+ ;
+
+ /* The following program is used to initialize the internal
+ ** structure holding the tables and indexes of the database.
+ ** The database contains a special table named "sqlite_master"
+ ** defined as follows:
+ **
+ ** CREATE TABLE sqlite_master (
+ ** type text, -- Either "table" or "index"
+ ** name text, -- Name of table or index
+ ** tbl_name text, -- Associated table
+ ** sql text -- The CREATE statement for this object
+ ** );
+ **
+ ** The sqlite_master table contains a single entry for each table
+ ** and each index. The "type" field tells whether the entry is
+ ** a table or index. The "name" field is the name of the object.
+ ** The "tbl_name" is the name of the associated table. For tables,
+ ** the tbl_name field is always the same as name. For indices, the
+ ** tbl_name field contains the name of the table that the index
+ ** indexes. Finally, the sql field contains the complete text of
+ ** the CREATE TABLE or CREATE INDEX statement that originally created
+ ** the table or index.
+ **
+ ** The following program invokes its callback on the SQL for each
+ ** table then goes back and invokes the callback on the
+ ** SQL for each index. The callback will invoke the
+ ** parser to build the internal representation of the
+ ** database scheme.
+ */
+ static VdbeOp initProg[] = {
+ { OP_Open, 0, 0, MASTER_NAME},
+ { OP_Next, 0, 8, 0}, /* 1 */
+ { OP_Field, 0, 0, 0},
+ { OP_String, 0, 0, "table"},
+ { OP_Ne, 0, 1, 0},
+ { OP_Field, 0, 3, 0},
+ { OP_Callback, 1, 0, 0},
+ { OP_Goto, 0, 1, 0},
+ { OP_Rewind, 0, 0, 0}, /* 8 */
+ { OP_Next, 0, 16, 0}, /* 9 */
+ { OP_Field, 0, 0, 0},
+ { OP_String, 0, 0, "index"},
+ { OP_Ne, 0, 9, 0},
+ { OP_Field, 0, 3, 0},
+ { OP_Callback, 1, 0, 0},
+ { OP_Goto, 0, 9, 0},
+ { OP_Halt, 0, 0, 0}, /* 16 */
+ };
+
+ /* Allocate space to hold the main database structure */
+ db = sqliteMalloc( sizeof(sqlite) );
+ if( pzErrMsg ) *pzErrMsg = 0;
+ if( db==0 ){
+ sqliteSetString(pzErrMsg, "out of memory", 0);
+ return 0;
+ }
+
+ /* Open the backend database driver */
+ db->pBe = sqliteDbbeOpen(zFilename, (mode&0222)!=0, mode!=0, pzErrMsg);
+ if( db->pBe==0 ){
+ sqliteFree(db);
+ return 0;
+ }
+
+ /* Create a virtual machine to run the initialization program. Run
+ ** the program. The delete the virtual machine.
+ */
+ azArg[0] = master_schema;
+ azArg[1] = 0;
+ sqliteOpenCb(db, 1, azArg, 0);
+ pTab = sqliteFindTable(db, MASTER_NAME);
+ if( pTab ){
+ pTab->readOnly = 1;
+ }
+ vdbe = sqliteVdbeCreate(db->pBe);
+ sqliteVdbeAddOpList(vdbe, sizeof(initProg)/sizeof(initProg[0]), initProg);
+ sqliteVdbeExec(vdbe, sqliteOpenCb, db, pzErrMsg);
+ sqliteVdbeDelete(vdbe);
+ return db;
+}
+
+/*
+** Close an existing SQLite database
+*/
+void sqlite_close(sqlite *db){
+ int i;
+ sqliteDbbeClose(db->pBe);
+ for(i=0; iapTblHash[i];
+ db->apTblHash[i] = 0;
+ while( pList ){
+ pNext = pList->pHash;
+ pList->pHash = 0;
+ sqliteDeleteTable(db, pList);
+ pList = pNext;
+ }
+ }
+ sqliteFree(db);
+}
+
+/*
+** Return TRUE if the given SQL string ends in a semicolon.
+*/
+int sqlite_complete(const char *zSql){
+ int i;
+ int lastWasSemi = 0;
+
+ i = 0;
+ while( i>=0 && zSql[i]!=0 ){
+ int tokenType;
+ int n;
+
+ n = sqliteGetToken(&zSql[i], &tokenType);
+ switch( tokenType ){
+ case TK_SPACE:
+ case TK_COMMENT:
+ break;
+ case TK_SEMI:
+ lastWasSemi = 1;
+ break;
+ default:
+ lastWasSemi = 0;
+ break;
+ }
+ i += n;
+ }
+ return lastWasSemi;
+}
+
+/*
+** Execute SQL code
+*/
+int sqlite_exec(
+ sqlite *db, /* The database on which the SQL executes */
+ char *zSql, /* The SQL to be executed */
+ sqlite_callback xCallback, /* Invoke this callback routine */
+ void *pArg, /* First argument to xCallback() */
+ char **pzErrMsg /* Write error messages here */
+){
+ Parse sParse;
+ int nErr;
+
+ if( pzErrMsg ) *pzErrMsg = 0;
+ memset(&sParse, 0, sizeof(sParse));
+ sParse.db = db;
+ sParse.xCallback = xCallback;
+ sParse.pArg = pArg;
+ nErr = sqliteRunParser(&sParse, zSql, pzErrMsg);
+ return nErr;
+}
diff --git a/src/shell.c b/src/shell.c
new file mode 100644
index 0000000000..0812747457
--- /dev/null
+++ b/src/shell.c
@@ -0,0 +1,384 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** 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 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** General Public License for more details.
+**
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This file contains code to implement the "sqlite" command line
+** utility for accessing SQLite databases.
+**
+** $Id: shell.c,v 1.1 2000/05/29 14:26:01 drh Exp $
+*/
+#include
+#include
+#include
+#include "sqlite.h"
+#include
+#include
+
+#if !defined(NO_READLINE)
+#include
+#include
+#endif
+
+/*
+** An pointer to an instance of this structure is passed from
+** the main program to the callback. This is used to communicate
+** state and mode information.
+*/
+struct callback_data {
+ int cnt; /* Number of records displayed so far */
+ FILE *out; /* Write results here */
+ int mode; /* An output mode setting */
+ int showHeader; /* True to show column names in List or Column mode */
+ char separator[20];/* Separator character for MODE_List */
+ int colWidth[30]; /* Width of each column when in column mode */
+};
+
+/*
+** These are the allowed modes.
+*/
+#define MODE_Line 0 /* One field per line. Blank line between records */
+#define MODE_Column 1 /* One record per line in neat columns */
+#define MODE_List 2 /* One record per line with a separator */
+
+/*
+** Number of elements in an array
+*/
+#define ArraySize(X) (sizeof(X)/sizeof(X[0]))
+
+/*
+** This is the callback routine that the SQLite library
+** invokes for each row of a query result.
+*/
+static int callback(void *pArg, int nArg, char **azArg, char **azCol){
+ int i;
+ struct callback_data *p = (struct callback_data*)pArg;
+ switch( p->mode ){
+ case MODE_Line: {
+ if( p->cnt++>0 ) fprintf(p->out,"\n");
+ for(i=0; iout,"%s = %s\n", azCol[i], azArg[i]);
+ }
+ break;
+ }
+ case MODE_Column: {
+ if( p->cnt++==0 && p->showHeader ){
+ for(i=0; icolWidth) && p->colWidth[i]>0 ){
+ w = p->colWidth[i];
+ }else{
+ w = 10;
+ }
+ fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " ");
+ }
+ for(i=0; icolWidth) && p->colWidth[i]>0 ){
+ w = p->colWidth[i];
+ }else{
+ w = 10;
+ }
+ fprintf(p->out,"%-*.*s%s",w,w,"-------------------------------------",
+ i==nArg-1 ? "\n": " ");
+ }
+ }
+ for(i=0; icolWidth) && p->colWidth[i]>0 ){
+ w = p->colWidth[i];
+ }else{
+ w = 10;
+ }
+ fprintf(p->out,"%-*.*s%s",w,w,azArg[i], i==nArg-1 ? "\n": " ");
+ }
+ break;
+ }
+ case MODE_List: {
+ if( p->cnt++==0 && p->showHeader ){
+ for(i=0; iout,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator);
+ }
+ }
+ for(i=0; iout,"%s%s",azArg[i], i==nArg-1 ? "\n" : p->separator);
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
+** Text of a help message
+*/
+static char zHelp[] =
+ ".exit Exit this program\n"
+ ".explain Set output mode suitable for EXPLAIN\n"
+ ".header ON|OFF Turn display of headers on or off\n"
+ ".help Show this message\n"
+ ".indices TABLE Show names of all indices on TABLE\n"
+ ".mode MODE Set mode to one of \"line\", \"column\", or"
+ " \"list\"\n"
+ ".output FILENAME Send output to FILENAME\n"
+ ".output stdout Send output to the screen\n"
+ ".schema ?TABLE? Show the CREATE statements\n"
+ ".separator STRING Change separator string for \"list\" mode\n"
+ ".tables List names all tables in the database\n"
+ ".width NUM NUM ... Set column widths for \"column\" mode\n"
+;
+
+/*
+** If an input line begins with "." then invoke this routine to
+** process that line.
+*/
+static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
+ int i = 1;
+ int nArg = 0;
+ int n, c;
+ char *azArg[50];
+
+ /* Parse the input line into tokens.
+ */
+ while( zLine[i] && nArgmode = MODE_Column;
+ p->showHeader = 1;
+ p->colWidth[0] = 4;
+ p->colWidth[1] = 12;
+ p->colWidth[2] = 5;
+ p->colWidth[3] = 5;
+ p->colWidth[4] = 40;
+ }else
+
+ if( c=='h' && strncmp(azArg[0], "header", n)==0 && nArg>1 ){
+ int j;
+ char *z = azArg[1];
+ int val = atoi(azArg[1]);
+ for(j=0; z[j]; j++){
+ if( isupper(z[j]) ) z[j] = tolower(z[j]);
+ }
+ if( strcmp(z,"on")==0 ){
+ val = 1;
+ }else if( strcmp(z,"yes")==0 ){
+ val = 1;
+ }
+ p->showHeader = val;
+ }else
+
+ if( c=='h' && strncmp(azArg[0], "help", n)==0 ){
+ fprintf(stderr,zHelp);
+ }else
+
+ if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg>1 ){
+ struct callback_data data;
+ char *zErrMsg = 0;
+ char zSql[1000];
+ memcpy(&data, p, sizeof(data));
+ data.showHeader = 0;
+ data.mode = MODE_List;
+ sprintf(zSql, "SELECT name FROM sqlite_master "
+ "WHERE type='index' AND tbl_name='%.900s'", azArg[1]);
+ sqlite_exec(db, zSql, callback, &data, &zErrMsg);
+ if( zErrMsg ){
+ fprintf(stderr,"Error: %s\n", zErrMsg);
+ free(zErrMsg);
+ }
+ }else
+
+ if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==2 ){
+ int n2 = strlen(azArg[1]);
+ if( strncmp(azArg[1],"line",n2)==0 ){
+ p->mode = MODE_Line;
+ }else if( strncmp(azArg[1],"column",n2)==0 ){
+ p->mode = MODE_Column;
+ }else if( strncmp(azArg[1],"list",n2)==0 ){
+ p->mode = MODE_List;
+ }
+ }else
+
+ if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){
+ if( p->out!=stdout ){
+ fclose(p->out);
+ }
+ if( strcmp(azArg[1],"stdout")==0 ){
+ p->out = stdout;
+ }else{
+ p->out = fopen(azArg[1], "w");
+ if( p->out==0 ){
+ fprintf(stderr,"can't write to \"%s\"\n", azArg[1]);
+ p->out = stdout;
+ }
+ }
+ }else
+
+ if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){
+ struct callback_data data;
+ char *zErrMsg = 0;
+ char zSql[1000];
+ memcpy(&data, p, sizeof(data));
+ data.showHeader = 0;
+ data.mode = MODE_List;
+ if( nArg>1 ){
+ sprintf(zSql, "SELECT sql FROM sqlite_master WHERE name='%.900s'",
+ azArg[1]);
+ }else{
+ sprintf(zSql, "SELECT sql FROM sqlite_master "
+ "ORDER BY tbl_name, type DESC, name");
+ }
+ sqlite_exec(db, zSql, callback, &data, &zErrMsg);
+ if( zErrMsg ){
+ fprintf(stderr,"Error: %s\n", zErrMsg);
+ free(zErrMsg);
+ }
+ }else
+
+ if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){
+ sprintf(p->separator, "%.*s", (int)ArraySize(p->separator)-1, azArg[1]);
+ }else
+
+ if( c=='t' && strncmp(azArg[0], "tables", n)==0 ){
+ struct callback_data data;
+ char *zErrMsg = 0;
+ static char zSql[] = "SELECT name FROM sqlite_master WHERE type='table'";
+ memcpy(&data, p, sizeof(data));
+ data.showHeader = 0;
+ data.mode = MODE_List;
+ sqlite_exec(db, zSql, callback, &data, &zErrMsg);
+ if( zErrMsg ){
+ fprintf(stderr,"Error: %s\n", zErrMsg);
+ free(zErrMsg);
+ }
+ }else
+
+ if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
+ int j;
+ for(j=1; jcolWidth); j++){
+ p->colWidth[j-1] = atoi(azArg[j]);
+ }
+ }else
+
+ {
+ fprintf(stderr, "unknown command: \"%s\". Enter \".help\" for help\n",
+ azArg[0]);
+ }
+}
+
+int main(int argc, char **argv){
+ sqlite *db;
+ char *zErrMsg = 0;
+ struct callback_data data;
+
+ if( argc!=2 && argc!=3 ){
+ fprintf(stderr,"Usage: %s FILENAME ?SQL?\n", *argv);
+ exit(1);
+ }
+ db = sqlite_open(argv[1], 0666, &zErrMsg);
+ if( db==0 ){
+ fprintf(stderr,"Unable to open database \"%s\": %s\n", argv[1], zErrMsg);
+ exit(1);
+ }
+ memset(&data, 0, sizeof(data));
+ data.out = stdout;
+ if( argc==3 ){
+ data.mode = MODE_List;
+ strcpy(data.separator,"|");
+ if( sqlite_exec(db, argv[2], callback, &data, &zErrMsg)!=0 && zErrMsg!=0 ){
+ fprintf(stderr,"SQL error: %s\n", zErrMsg);
+ exit(1);
+ }
+ }else{
+ char *zLine;
+ char *zSql = 0;
+ int nSql = 0;
+ int istty = isatty(0);
+ data.mode = MODE_Line;
+ strcpy(data.separator,"|");
+ data.showHeader = 0;
+ if( istty ){
+ printf(
+ "Enter \".help\" for instructions\n"
+ );
+ }
+ while( (zLine = readline(istty ? (zSql==0 ? "sql> " : ".... ") : 0))!=0 ){
+ if( zLine && zLine[0]=='.' ){
+ do_meta_command(zLine, db, &data);
+ free(zLine);
+ continue;
+ }
+ if( zSql==0 ){
+ nSql = strlen(zLine);
+ zSql = malloc( nSql+1 );
+ strcpy(zSql, zLine);
+ }else{
+ int len = strlen(zLine);
+ zSql = realloc( zSql, nSql + len + 2 );
+ if( zSql==0 ){
+ fprintf(stderr,"%s: out of memory!\n", *argv);
+ exit(1);
+ }
+ strcpy(&zSql[nSql++], "\n");
+ strcpy(&zSql[nSql], zLine);
+ nSql += len;
+ }
+ free(zLine);
+ if( sqlite_complete(zSql) ){
+ data.cnt = 0;
+ if( sqlite_exec(db, zSql, callback, &data, &zErrMsg)!=0
+ && zErrMsg!=0 ){
+ printf("SQL error: %s\n", zErrMsg);
+ free(zErrMsg);
+ zErrMsg = 0;
+ }
+ free(zSql);
+ zSql = 0;
+ nSql = 0;
+ }
+ }
+ }
+ sqlite_close(db);
+ return 0;
+}
diff --git a/src/sqlite.h b/src/sqlite.h
new file mode 100644
index 0000000000..26b48746ad
--- /dev/null
+++ b/src/sqlite.h
@@ -0,0 +1,118 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** 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 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** General Public License for more details.
+**
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This header file defines the interface that the sqlite library
+** presents to client programs.
+**
+** @(#) $Id: sqlite.h,v 1.1 2000/05/29 14:26:01 drh Exp $
+*/
+#ifndef _SQLITE_H_
+#define _SQLITE_H_
+
+/*
+** Each open sqlite database is represented by an instance of the
+** following opaque structure.
+*/
+typedef struct sqlite sqlite;
+
+/*
+** A function to open a new sqlite database.
+**
+** If the database does not exist and mode indicates write
+** permission, then a new database is created. If the database
+** does not exist and mode does not indicate write permission,
+** then the open fails, an error message generated (if errmsg!=0)
+** and the function returns 0.
+**
+** If mode does not indicates user write permission, then the
+** database is opened read-only.
+**
+** The Truth: As currently implemented, all databases are opened
+** for writing all the time. Maybe someday we will provide the
+** ability to open a database readonly. The mode parameters is
+** provide in anticipation of that enhancement.
+*/
+sqlite *sqlite_open(const char *filename, int mode, char **errmsg);
+
+/*
+** A function to close the database.
+**
+** Call this function with a pointer to a structure that was previously
+** returned from sqlite_open() and the corresponding database will by closed.
+*/
+void sqlite_close(sqlite *);
+
+/*
+** The type for a callback function.
+*/
+typedef int (*sqlite_callback)(void*,int,char**, char**);
+
+/*
+** A function to executes one or more statements of SQL.
+**
+** If one or more of the SQL statements are queries, then
+** the callback function specified by the 3rd parameter is
+** invoked once for each row of the query result. This callback
+** should normally return 0. If the callback returns a non-zero
+** value then the query is aborted, all subsequent SQL statements
+** are skipped and the sqlite_exec() function returns the same
+** value that the callback returned.
+**
+** The 4th parameter is an arbitrary pointer that is passed
+** to the callback function as its first parameter.
+**
+** The 2nd parameter to the callback function is the number of
+** columns in the query result. The 3rd parameter is an array
+** of string holding the values for each column. The 4th parameter
+** is an array of strings holding the names of each column.
+**
+** The callback function may be NULL, even for queries. A NULL
+** callback is not an error. It just means that no callback
+** will be invoked.
+**
+** If an error occurs while parsing or evaluating the SQL (but
+** not while executing the callback) then an appropriate error
+** message is written into memory obtained from malloc() and
+** *errmsg is made to point to that message. If errmsg==NULL,
+** then no error message is ever written. The return value is
+** non-zero if an error occurs.
+*/
+int sqlite_exec(
+ sqlite*, /* An open database */
+ char *sql, /* SQL to be executed */
+ sqlite_callback, /* Callback function */
+ void *, /* 1st argument to callback function */
+ char **errmsg /* Error msg written here */
+);
+
+
+/* This function returns true if the given input string comprises
+** one or more complete SQL statements.
+**
+** The algorithm is simple. If the last token other than spaces
+** and comments is a semicolon, then return true. otherwise return
+** false.
+*/
+int sqlite_complete(const char *sql);
+
+#endif /* _SQLITE_H_ */
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
new file mode 100644
index 0000000000..b2bf129dee
--- /dev/null
+++ b/src/sqliteInt.h
@@ -0,0 +1,235 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** 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 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** General Public License for more details.
+**
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*************************************************************************
+** Internal interface definitions for SQLite.
+**
+** @(#) $Id: sqliteInt.h,v 1.1 2000/05/29 14:26:01 drh Exp $
+*/
+#include "sqlite.h"
+#include "dbbe.h"
+#include "vdbe.h"
+#include "parse.h"
+#include
+#include
+#include
+#include
+#include
+
+/*
+** The number of entries in the in-memory hash table holding the
+** schema.
+*/
+#define N_HASH 51
+
+/*
+** Name of the master database table. The master database table
+** is a special table that holds the names and attributes of all
+** user tables and indices.
+*/
+#define MASTER_NAME "sqlite_master"
+
+/*
+** A convenience macro that returns the number of elements in
+** an array.
+*/
+#define ArraySize(X) (sizeof(X)/sizeof(X[0]))
+
+/*
+** Forward references to structures
+*/
+typedef struct Table Table;
+typedef struct Index Index;
+typedef struct Instruction Instruction;
+typedef struct Expr Expr;
+typedef struct ExprList ExprList;
+typedef struct Parse Parse;
+typedef struct Token Token;
+typedef struct IdList IdList;
+typedef struct WhereInfo WhereInfo;
+
+/*
+** Each database is an instance of the following structure
+*/
+struct sqlite {
+ Dbbe *pBe; /* The backend driver */
+ int flags; /* Miscellanous flags */
+ Table *apTblHash[N_HASH]; /* All tables of the database */
+ Index *apIdxHash[N_HASH]; /* All indices of the database */
+};
+
+/*
+** Possible values for the flags field of sqlite
+*/
+#define SQLITE_VdbeTrace 0x00000001
+
+/*
+** Each table is represented in memory by
+** an instance of the following structure
+*/
+struct Table {
+ char *zName; /* Name of the table */
+ Table *pHash; /* Next table with same hash on zName */
+ int nCol; /* Number of columns in this table */
+ int readOnly; /* True if this table should not be written by the user */
+ char **azCol; /* Name of each column */
+ Index *pIndex; /* List of indices on this table. */
+};
+
+/*
+** Each index is represented in memory by and
+** instance of the following structure.
+*/
+struct Index {
+ char *zName; /* Name of this index */
+ Index *pHash; /* Next index with the same hash on zName */
+ int nField; /* Number of fields in the table indexed by this index */
+ int *aiField; /* Indices of fields used by this index. 1st is 0 */
+ Table *pTable; /* The table being indexed */
+ Index *pNext; /* The next index associated with the same table */
+};
+
+/*
+** Each token coming out of the lexer is an instance of
+** this structure.
+*/
+struct Token {
+ char *z; /* Text of the token */
+ int n; /* Number of characters in this token */
+};
+
+/*
+** Each node of an expression in the parse tree is an instance
+** of this structure
+*/
+struct Expr {
+ int op; /* Operation performed by this node */
+ Expr *pLeft, *pRight; /* Left and right subnodes */
+ ExprList *pList; /* A list of expressions used as a function argument */
+ Token token; /* An operand token */
+ int iTable, iField; /* When op==TK_FIELD, then this node means the
+ ** iField-th field of the iTable-th table */
+};
+
+/*
+** A list of expressions. Each expression may optionally have a
+** name. An expr/name combination can be used in several ways, such
+** as the list of "expr AS ID" fields following a "SELECT" or in the
+** list of "ID = expr" items in an UPDATE. A list of expressions can
+** also be used as the argument to a function, in which case the azName
+** field is not used.
+*/
+struct ExprList {
+ int nExpr; /* Number of expressions on the list */
+ struct {
+ Expr *pExpr; /* The list of expressions */
+ char *zName; /* Token associated with this expression */
+ int idx; /* ... */
+ } *a; /* One entry for each expression */
+};
+
+/*
+** A list of identifiers.
+*/
+struct IdList {
+ int nId; /* Number of identifiers on the list */
+ struct {
+ char *zName; /* Text of the identifier. */
+ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
+ Table *pTab; /* Table corresponding to zName */
+ int idx; /* Index of a field name in the table */
+ } *a; /* One entry for each identifier on the list */
+};
+
+/*
+** The WHERE clause processing routine has two halves. The
+** first part does the start of the WHERE loop and the second
+** half does the tail of the WHERE loop. An instance of
+** this structure is returned by the first half and passed
+** into the second half to give some continuity.
+*/
+struct WhereInfo {
+ Parse *pParse;
+ IdList *pTabList;
+ int iContinue;
+ int iBreak;
+};
+
+/*
+** An SQL parser context
+*/
+struct Parse {
+ sqlite *db; /* The main database structure */
+ sqlite_callback xCallback; /* The callback function */
+ void *pArg; /* First argument to the callback function */
+ char *zErrMsg; /* An error message */
+ Token sErrToken; /* The token at which the error occurred */
+ Token sFirstToken; /* The first token parsed */
+ Token sLastToken; /* The last token parsed */
+ Table *pNewTable; /* A table being constructed by CREATE TABLE */
+ Vdbe *pVdbe; /* An engine for executing database bytecode */
+ int explain; /* True if the EXPLAIN flag is found on the query */
+ int initFlag; /* True if reparsing CREATE TABLEs */
+ int nErr; /* Number of errors seen */
+};
+
+/*
+** Internal function prototypes
+*/
+int sqliteStrICmp(const char *, const char *);
+int sqliteStrNICmp(const char *, const char *, int);
+int sqliteHashNoCase(const char *, int);
+int sqliteCompare(const char *, const char *);
+int sqliteSortCompare(const char *, const char *);
+void *sqliteMalloc(int);
+void sqliteFree(void*);
+void *sqliteRealloc(void*,int);
+int sqliteGetToken(const char*, int *);
+void sqliteSetString(char **, const char *, ...);
+void sqliteSetNString(char **, ...);
+int sqliteRunParser(Parse*, char*, char **);
+void sqliteExec(Parse*);
+Expr *sqliteExpr(int, Expr*, Expr*, Token*);
+Expr *sqliteExprFunction(ExprList*, Token*);
+void sqliteExprDelete(Expr*);
+ExprList *sqliteExprListAppend(ExprList*,Expr*,Token*);
+void sqliteExprListDelete(ExprList*);
+void sqliteStartTable(Parse*,Token*,Token*);
+void sqliteAddColumn(Parse*,Token*);
+void sqliteEndTable(Parse*,Token*);
+void sqliteDropTable(Parse*, Token*);
+void sqliteDeleteTable(sqlite*, Table*);
+void sqliteInsert(Parse*, Token*, ExprList*, IdList*);
+IdList *sqliteIdListAppend(IdList*, Token*);
+void sqliteIdListAddAlias(IdList*, Token*);
+void sqliteIdListDelete(IdList*);
+void sqliteCreateIndex(Parse*, Token*, Token*, IdList*, Token*, Token*);
+void sqliteDropIndex(Parse*, Token*);
+void sqliteSelect(Parse*, ExprList*, IdList*, Expr*, ExprList*);
+void sqliteDeleteFrom(Parse*, Token*, Expr*);
+void sqliteUpdate(Parse*, Token*, ExprList*, Expr*);
+WhereInfo *sqliteWhereBegin(Parse*, IdList*, Expr*, int);
+void sqliteWhereEnd(WhereInfo*);
+void sqliteExprCode(Parse*, Expr*);
+void sqliteExprIfTrue(Parse*, Expr*, int);
+void sqliteExprIfFalse(Parse*, Expr*, int);
+Table *sqliteFindTable(sqlite*,char*);
diff --git a/src/tclsqlite.c b/src/tclsqlite.c
new file mode 100644
index 0000000000..55e9a85570
--- /dev/null
+++ b/src/tclsqlite.c
@@ -0,0 +1,241 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** 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 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** General Public License for more details.
+**
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*************************************************************************
+** A TCL Interface to SQLite
+**
+** $Id: tclsqlite.c,v 1.1 2000/05/29 14:26:01 drh Exp $
+*/
+#include "sqlite.h"
+#include
+#include
+#include
+
+/*
+** An instance of this structure passes information thru the sqlite
+** logic from the original TCL command into the callback routine.
+*/
+typedef struct CallbackData CallbackData;
+struct CallbackData {
+ Tcl_Interp *interp; /* The TCL interpreter */
+ char *zArray; /* The array into which data is written */
+ char *zCode; /* The code to execute for each row */
+ int once; /* Set only for the first invocation of callback */
+};
+
+/*
+** Called for each row of the result.
+*/
+static int DbEvalCallback(
+ void *clientData, /* An instance of CallbackData */
+ int nCol, /* Number of columns in the result */
+ char ** azCol, /* Data for each column */
+ char ** azN /* Name for each column */
+){
+ CallbackData *cbData = (CallbackData*)clientData;
+ int i, rc;
+ if( cbData->zArray[0] ){
+ if( cbData->once ){
+ for(i=0; iinterp, cbData->zArray, "*", azN[i],
+ TCL_LIST_ELEMENT|TCL_APPEND_VALUE);
+ }
+ }
+ for(i=0; iinterp, cbData->zArray, azN[i], azCol[i], 0);
+ }
+ }else{
+ for(i=0; iinterp, azN[i], azCol[i], 0);
+ }
+ }
+ cbData->once = 0;
+ rc = Tcl_Eval(cbData->interp, cbData->zCode);
+ return rc;
+}
+
+/*
+** Called when the command is deleted.
+*/
+static void DbDeleteCmd(void *db){
+ sqlite_close((sqlite*)db);
+}
+
+/*
+** The "sqlite" command below creates a new Tcl command for each
+** connection it opens to an SQLite database. This routine is invoked
+** whenever one of those connection-specific commands is executed
+** in Tcl. For example, if you run Tcl code like this:
+**
+** sqlite db1 "my_database"
+** db1 close
+**
+** The first command opens a connection to the "my_database" database
+** and calls that connection "db1". The second command causes this
+** subroutine to be invoked.
+*/
+static int DbCmd(void *cd, Tcl_Interp *interp, int argc, char **argv){
+ char *z;
+ int n, c;
+ sqlite *db = cd;
+ if( argc<2 ){
+ Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
+ " SUBCOMMAND ...\"", 0);
+ return TCL_ERROR;
+ }
+ z = argv[1];
+ n = strlen(z);
+ c = z[0];
+
+ /* $db close
+ **
+ ** Shutdown the database
+ */
+ if( c=='c' && n>=2 && strncmp(z,"close",n)==0 ){
+ Tcl_DeleteCommand(interp, argv[0]);
+ }else
+
+ /* $db complete SQL
+ **
+ ** Return TRUE if SQL is a complete SQL statement. Return FALSE if
+ ** additional lines of input are needed. This is similar to the
+ ** built-in "info complete" command of Tcl.
+ */
+ if( c=='c' && n>=2 && strncmp(z,"complete",n)==0 ){
+ char *zRes;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
+ " complete SQL\"", 0);
+ return TCL_ERROR;
+ }
+ zRes = sqlite_complete(argv[2]) ? "1" : "0";
+ Tcl_SetResult(interp, zRes, TCL_VOLATILE);
+ }else
+
+ /*
+ ** $db eval $sql ?array { ...code... }?
+ **
+ ** The SQL statement in $sql is evaluated. For each row, the values are
+ ** placed in elements of the array named "array" and ...code.. is executed.
+ ** If "array" and "code" are omitted, then no callback is every invoked.
+ ** If "array" is an empty string, then the values are placed in variables
+ ** that have the same name as the fields extracted by the query.
+ */
+ if( c=='e' && strncmp(z,"eval",n)==0 ){
+ CallbackData cbData;
+ char *zErrMsg;
+ int rc;
+
+ if( argc!=5 && argc!=3 ){
+ Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
+ " eval SQL ?ARRAY-NAME CODE?", 0);
+ return TCL_ERROR;
+ }
+ if( argc==5 ){
+ cbData.interp = interp;
+ cbData.zArray = argv[3];
+ cbData.zCode = argv[4];
+ zErrMsg = 0;
+ rc = sqlite_exec(db, argv[2], DbEvalCallback, &cbData, &zErrMsg);
+ }else{
+ rc = sqlite_exec(db, argv[2], 0, 0, &zErrMsg);
+ }
+ if( zErrMsg ){
+ Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE);
+ free(zErrMsg);
+ }
+ return rc;
+ }
+
+ /* The default
+ */
+ else{
+ Tcl_AppendResult(interp,"unknown subcommand \"", z,
+ "\" - should be one of: close complete eval", 0);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+** sqlite DBNAME FILENAME ?MODE?
+**
+** This is the main Tcl command. When the "sqlite" Tcl command is
+** invoked, this routine runs to process that command.
+**
+** The first argument, DBNAME, is an arbitrary name for a new
+** database connection. This command creates a new command named
+** DBNAME that is used to control that connection. The database
+** connection is deleted when the DBNAME command is deleted.
+**
+** The second argument is the name of the directory that contains
+** the sqlite database that is to be accessed.
+*/
+static int DbMain(void *cd, Tcl_Interp *interp, int argc, char **argv){
+ int mode;
+ sqlite *p;
+ char *zErrMsg;
+ if( argc!=3 && argc!=4 ){
+ Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
+ " HANDLE FILENAME ?MODE?\"", 0);
+ return TCL_ERROR;
+ }
+ if( argc==3 ){
+ mode = 0;
+ }else if( Tcl_GetInt(interp, argv[3], &mode)!=TCL_OK ){
+ return TCL_ERROR;
+ }
+ zErrMsg = 0;
+ p = sqlite_open(argv[2], mode, &zErrMsg);
+ if( p==0 ){
+ Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE);
+ free(zErrMsg);
+ return TCL_ERROR;
+ }
+ Tcl_CreateCommand(interp, argv[1], DbCmd, p, DbDeleteCmd);
+ return TCL_OK;
+}
+
+/*
+** Initialize this module.
+**
+** This Tcl module contains only a single new Tcl command named "sqlite".
+** (Hence there is no namespace. There is no point in using a namespace
+** if the extension only supplies one new name!) The "sqlite" command is
+** used to open a new SQLite database. See the DbMain() routine above
+** for additional information.
+*/
+int Sqlite_Init(Tcl_Interp *interp){
+ Tcl_CreateCommand(interp, "sqlite", DbMain, 0, 0);
+ return TCL_OK;
+}
+int Sqlite_SafeInit(Tcl_Interp *interp){
+ return TCL_OK;
+}
+
+/*
+** If compiled using mktclapp, this routine runs to initialize
+** everything.
+*/
+int Et_AppInit(Tcl_Interp *interp){
+ return Sqlite_Init(interp);
+}
diff --git a/src/tokenize.c b/src/tokenize.c
new file mode 100644
index 0000000000..f762b679d0
--- /dev/null
+++ b/src/tokenize.c
@@ -0,0 +1,375 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** 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 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** General Public License for more details.
+**
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*************************************************************************
+** An tokenizer for SQL
+**
+** This file contains C code that splits an SQL input string up into
+** individual tokens and sends those tokens one-by-one over to the
+** parser for analysis.
+**
+** $Id: tokenize.c,v 1.1 2000/05/29 14:26:02 drh Exp $
+*/
+#include "sqliteInt.h"
+#include
+
+/*
+** All the keywords of the SQL language are stored as in a hash
+** table composed of instances of the following structure.
+*/
+typedef struct Keyword Keyword;
+struct Keyword {
+ char *zName; /* The keyword name */
+ int len; /* Number of characters in the keyword */
+ int tokenType; /* The token value for this keyword */
+ Keyword *pNext; /* Next keyword with the same hash */
+};
+
+/*
+** These are the keywords
+*/
+static Keyword aKeywordTable[] = {
+ { "AND", 0, TK_AND, 0 },
+ { "AS", 0, TK_AS, 0 },
+ { "ASC", 0, TK_ASC, 0 },
+ { "BY", 0, TK_BY, 0 },
+ { "CHECK", 0, TK_CHECK, 0 },
+ { "CONSTRAINT", 0, TK_CONSTRAINT, 0 },
+ { "CREATE", 0, TK_CREATE, 0 },
+ { "DEFAULT", 0, TK_DEFAULT, 0 },
+ { "DELETE", 0, TK_DELETE, 0 },
+ { "DESC", 0, TK_DESC, 0 },
+ { "DROP", 0, TK_DROP, 0 },
+ { "EXPLAIN", 0, TK_EXPLAIN, 0 },
+ { "FROM", 0, TK_FROM, 0 },
+ { "INDEX", 0, TK_INDEX, 0 },
+ { "INSERT", 0, TK_INSERT, 0 },
+ { "INTO", 0, TK_INTO, 0 },
+ { "IS", 0, TK_IS, 0 },
+ { "ISNULL", 0, TK_ISNULL, 0 },
+ { "KEY", 0, TK_KEY, 0 },
+ { "NOT", 0, TK_NOT, 0 },
+ { "NOTNULL", 0, TK_NOTNULL, 0 },
+ { "NULL", 0, TK_NULL, 0 },
+ { "ON", 0, TK_ON, 0 },
+ { "OR", 0, TK_OR, 0 },
+ { "ORDER", 0, TK_ORDER, 0 },
+ { "PRIMARY", 0, TK_PRIMARY, 0 },
+ { "SELECT", 0, TK_SELECT, 0 },
+ { "SET", 0, TK_SET, 0 },
+ { "TABLE", 0, TK_TABLE, 0 },
+ { "UNIQUE", 0, TK_UNIQUE, 0 },
+ { "UPDATE", 0, TK_UPDATE, 0 },
+ { "VALUES", 0, TK_VALUES, 0 },
+ { "WHERE", 0, TK_WHERE, 0 },
+};
+
+/*
+** This is the hash table
+*/
+#define KEY_HASH_SIZE 37
+static Keyword *apHashTable[KEY_HASH_SIZE];
+
+
+/*
+** This function looks up an identifier to determine if it is a
+** keyword. If it is a keyword, the token code of that keyword is
+** returned. If the input is not a keyword, TK_ID is returned.
+*/
+static int sqliteKeywordCode(const char *z, int n){
+ int h;
+ Keyword *p;
+ if( aKeywordTable[0].len==0 ){
+ /* Initialize the keyword hash table */
+ int i;
+ int n;
+ n = sizeof(aKeywordTable)/sizeof(aKeywordTable[0]);
+ for(i=0; ipNext){
+ if( p->len==n && sqliteStrNICmp(p->zName, z, n)==0 ){
+ return p->tokenType;
+ }
+ }
+ return TK_ID;
+}
+
+/*
+** Return the length of the token that begins at z[0]. Return
+** -1 if the token is (or might be) incomplete. Store the token
+** type in *tokenType before returning.
+*/
+int sqliteGetToken(const char *z, int *tokenType){
+ int i;
+ switch( *z ){
+ case ' ': case '\t': case '\n': case '\f': {
+ for(i=1; z[i] && isspace(z[i]); i++){}
+ *tokenType = TK_SPACE;
+ return i;
+ }
+ case '-': {
+ if( z[1]==0 ) return -1;
+ if( z[1]=='-' ){
+ for(i=2; z[i] && z[i]!='\n'; i++){}
+ *tokenType = TK_COMMENT;
+ return i;
+ }
+ *tokenType = TK_MINUS;
+ return 1;
+ }
+ case '(': {
+ *tokenType = TK_LP;
+ return 1;
+ }
+ case ')': {
+ *tokenType = TK_RP;
+ return 1;
+ }
+ case ';': {
+ *tokenType = TK_SEMI;
+ return 1;
+ }
+ case '+': {
+ *tokenType = TK_PLUS;
+ return 1;
+ }
+ case '*': {
+ *tokenType = TK_STAR;
+ return 1;
+ }
+ case '/': {
+ *tokenType = TK_SLASH;
+ return 1;
+ }
+ case '=': {
+ *tokenType = TK_EQ;
+ return 1 + (z[1]=='=');
+ }
+ case '<': {
+ if( z[1]=='=' ){
+ *tokenType = TK_LE;
+ return 2;
+ }else if( z[1]=='>' ){
+ *tokenType = TK_NE;
+ return 2;
+ }else{
+ *tokenType = TK_LT;
+ return 1;
+ }
+ }
+ case '>': {
+ if( z[1]=='=' ){
+ *tokenType = TK_GE;
+ return 2;
+ }else{
+ *tokenType = TK_GT;
+ return 1;
+ }
+ }
+ case '!': {
+ if( z[1]!='=' ){
+ *tokenType = TK_ILLEGAL;
+ return 1;
+ }else{
+ *tokenType = TK_NE;
+ return 2;
+ }
+ }
+ case ',': {
+ *tokenType = TK_COMMA;
+ return 1;
+ }
+ case '\'': case '"': {
+ int delim = z[0];
+ for(i=1; z[i]; i++){
+ if( z[i]==delim ){
+ if( z[i+1]==delim ){
+ i++;
+ }else{
+ break;
+ }
+ }
+ }
+ if( z[i] ) i++;
+ *tokenType = TK_STRING;
+ return i;
+ }
+ case '.': {
+ if( !isdigit(z[1]) ){
+ *tokenType = TK_DOT;
+ return 1;
+ }
+ /* Fall thru into the next case */
+ }
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9': {
+ for(i=1; z[i] && isdigit(z[i]); i++){}
+ if( z[i]=='.' ){
+ i++;
+ while( z[i] && isdigit(z[i]) ){ i++; }
+ if( (z[i]=='e' || z[i]=='E') &&
+ ( isdigit(z[i+1])
+ || ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2]))
+ )
+ ){
+ i += 2;
+ while( z[i] && isdigit(z[i]) ){ i++; }
+ }
+ *tokenType = TK_FLOAT;
+ }else if( z[0]=='.' ){
+ *tokenType = TK_FLOAT;
+ }else{
+ *tokenType = TK_INTEGER;
+ }
+ return i;
+ }
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z': case '_':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z': {
+ for(i=1; z[i] && (isalnum(z[i]) || z[i]=='_'); i++){}
+ *tokenType = sqliteKeywordCode(z, i);
+ return i;
+ }
+ default: {
+ break;
+ }
+ }
+ *tokenType = TK_ILLEGAL;
+ return 1;
+}
+
+/*
+** Run the parser on the given SQL string. The parser structure is
+** passed in. Return the number of errors.
+*/
+int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){
+ int nErr = 0;
+ int i;
+ void *pEngine;
+ int once = 1;
+ static FILE *trace = 0;
+ extern void *sqliteParserAlloc(void*(*)(int));
+ extern void sqliteParserFree(void*, void(*)(void*));
+ extern int sqliteParser(void*, int, ...);
+ extern void sqliteParserTrace(FILE*, char *);
+
+ i = 0;
+ pEngine = sqliteParserAlloc(sqliteMalloc);
+ if( pEngine==0 ){
+ sqliteSetString(pzErrMsg, "out of memory", 0);
+ return 1;
+ }
+ sqliteParserTrace(trace, "parser: ");
+ while( nErr==0 && i>=0 && zSql[i]!=0 ){
+ int tokenType;
+
+ pParse->sLastToken.z = &zSql[i];
+ pParse->sLastToken.n = sqliteGetToken(&zSql[i], &tokenType);
+ i += pParse->sLastToken.n;
+ if( once ){
+ pParse->sFirstToken = pParse->sLastToken;
+ once = 0;
+ }
+ switch( tokenType ){
+ case TK_SPACE:
+ break;
+ case TK_COMMENT: {
+ /* Various debugging modes can be turned on and off using
+ ** special SQL comments. Check for the special comments
+ ** here and take approriate action if found.
+ */
+ char *z = pParse->sLastToken.z;
+ if( sqliteStrNICmp(z,"--parser-trace-on--",19)==0 ){
+ trace = stderr;
+ sqliteParserTrace(trace, "parser: ");
+ }else if( sqliteStrNICmp(z,"--parser-trace-off--", 20)==0 ){
+ trace = 0;
+ sqliteParserTrace(trace, "parser: ");
+ }else if( sqliteStrNICmp(z,"--vdbe-trace-on--",17)==0 ){
+ pParse->db->flags |= SQLITE_VdbeTrace;
+ }else if( sqliteStrNICmp(z,"--vdbe-trace-off--", 19)==0 ){
+ pParse->db->flags &= ~SQLITE_VdbeTrace;
+ }
+ break;
+ }
+ case TK_ILLEGAL:
+ sqliteSetNString(pzErrMsg, "illegal token: \"", -1,
+ pParse->sLastToken.z, pParse->sLastToken.n, 0);
+ nErr++;
+ break;
+ default:
+ sqliteParser(pEngine, tokenType, pParse->sLastToken, pParse);
+ if( pParse->zErrMsg ){
+ sqliteSetNString(pzErrMsg, "near \"", -1,
+ pParse->sErrToken.z, pParse->sErrToken.n,
+ "\": ", -1,
+ pParse->zErrMsg, -1,
+ 0);
+ nErr++;
+ }
+ break;
+ }
+ }
+ if( nErr==0 ){
+ sqliteParser(pEngine, 0, pParse->sLastToken, pParse);
+ if( pParse->zErrMsg ){
+ sqliteSetNString(pzErrMsg, "near \"", -1,
+ pParse->sErrToken.z, pParse->sErrToken.n,
+ "\": ", -1,
+ pParse->zErrMsg, -1,
+ 0);
+ nErr++;
+ }
+ }
+ sqliteParserFree(pEngine, sqliteFree);
+ if( pParse->zErrMsg ){
+ if( pzErrMsg ){
+ *pzErrMsg = pParse->zErrMsg;
+ }else{
+ sqliteFree(pParse->zErrMsg);
+ }
+ if( !nErr ) nErr++;
+ }
+ if( pParse->pVdbe ){
+ sqliteVdbeDelete(pParse->pVdbe);
+ pParse->pVdbe = 0;
+ }
+ if( pParse->pNewTable ){
+ sqliteDeleteTable(pParse->db, pParse->pNewTable);
+ pParse->pNewTable = 0;
+ }
+ return nErr;
+}
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000000..130634547e
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,445 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** 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 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** General Public License for more details.
+**
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*************************************************************************
+** Utility functions used throughout sqlite.
+**
+** This file contains functions for allocating memory, comparing
+** strings, and stuff like that.
+**
+** $Id: util.c,v 1.1 2000/05/29 14:26:02 drh Exp $
+*/
+#include "sqliteInt.h"
+#include
+#include
+
+/*
+** Allocate new memory and set it to zero. Return NULL if
+** no memory is available.
+*/
+void *sqliteMalloc(int n){
+ void *p = malloc(n);
+ if( p==0 ) return 0;
+ memset(p, 0, n);
+ return p;
+}
+
+/*
+** Free memory previously obtained from sqliteMalloc()
+*/
+void sqliteFree(void *p){
+ if( p ) free(p);
+}
+
+/*
+** Resize a prior allocation. If p==0, then this routine
+** works just like sqliteMalloc(). If n==0, then this routine
+** works just like sqliteFree().
+*/
+void *sqliteRealloc(void *p, int n){
+ if( p==0 ){
+ return sqliteMalloc(n);
+ }
+ if( n==0 ){
+ sqliteFree(p);
+ return 0;
+ }
+ return realloc(p, n);
+}
+
+/*
+** Create a string from the 2nd and subsequent arguments (up to the
+** first NULL argument), store the string in memory obtained from
+** sqliteMalloc() and make the pointer indicated by the 1st argument
+** point to that string.
+*/
+void sqliteSetString(char **pz, const char *zFirst, ...){
+ va_list ap;
+ int nByte;
+ const char *z;
+ char *zResult;
+
+ if( pz==0 ) return;
+ nByte = strlen(zFirst) + 1;
+ va_start(ap, zFirst);
+ while( (z = va_arg(ap, const char*))!=0 ){
+ nByte += strlen(z);
+ }
+ va_end(ap);
+ sqliteFree(*pz);
+ *pz = zResult = sqliteMalloc( nByte );
+ if( zResult==0 ) return;
+ strcpy(zResult, zFirst);
+ zResult += strlen(zResult);
+ va_start(ap, zFirst);
+ while( (z = va_arg(ap, const char*))!=0 ){
+ strcpy(zResult, z);
+ zResult += strlen(zResult);
+ }
+ va_end(ap);
+}
+
+/*
+** Works like sqliteSetString, but each string is now followed by
+** a length integer. -1 means use the whole string.
+*/
+void sqliteSetNString(char **pz, ...){
+ va_list ap;
+ int nByte;
+ const char *z;
+ char *zResult;
+ int n;
+
+ if( pz==0 ) return;
+ nByte = 0;
+ va_start(ap, pz);
+ while( (z = va_arg(ap, const char*))!=0 ){
+ n = va_arg(ap, int);
+ if( n<=0 ) n = strlen(z);
+ nByte += n;
+ }
+ va_end(ap);
+ sqliteFree(*pz);
+ *pz = zResult = sqliteMalloc( nByte + 1 );
+ if( zResult==0 ) return;
+ va_start(ap, pz);
+ while( (z = va_arg(ap, const char*))!=0 ){
+ n = va_arg(ap, int);
+ if( n<=0 ) n = strlen(z);
+ strncpy(zResult, z, n);
+ zResult += n;
+ }
+ *zResult = 0;
+ va_end(ap);
+}
+
+/* An array to map all upper-case characters into their corresponding
+** lower-case character.
+*/
+static unsigned char UpperToLower[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103,
+ 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,
+ 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107,
+ 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,
+ 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,
+ 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,
+ 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,
+ 198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,
+ 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,
+ 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,
+ 252,253,254,255
+};
+
+/*
+** This function computes a hash on the name of a keyword.
+** Case is not significant.
+*/
+int sqliteHashNoCase(const char *z, int n){
+ int h = 0;
+ int c;
+ if( n<=0 ) n = strlen(z);
+ while( n-- > 0 && (c = *z++)!=0 ){
+ h = h<<3 ^ h ^ UpperToLower[c];
+ }
+ if( h<0 ) h = -h;
+ return h;
+}
+
+/*
+** Some system shave stricmp(). Others have strcasecmp(). Because
+** there is no consistency, we will define our own.
+*/
+int sqliteStrICmp(const char *zLeft, const char *zRight){
+ register unsigned char *a, *b;
+ a = (unsigned char *)zLeft;
+ b = (unsigned char *)zRight;
+ while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
+ return *a - *b;
+}
+int sqliteStrNICmp(const char *zLeft, const char *zRight, int N){
+ register unsigned char *a, *b;
+ a = (unsigned char *)zLeft;
+ b = (unsigned char *)zRight;
+ while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
+ return N<=0 ? 0 : *a - *b;
+}
+
+/* Notes on string comparisions.
+**
+** We want the main string comparision function used for sorting to
+** sort both numbers and alphanumeric words into the correct sequence.
+** The same routine should do both without prior knowledge of which
+** type of text the input represents. It should even work for strings
+** which are a mixture of text and numbers.
+**
+** To accomplish this, we keep track of a state number while scanning
+** the two strings. The states are as follows:
+**
+** 1 Beginning of word
+** 2 Arbitrary text
+** 3 Integer
+** 4 Negative integer
+** 5 Real number
+** 6 Negative real
+**
+** The scan begins in state 1, beginning of word. Transitions to other
+** states are determined by characters seen, as shown in the following
+** chart:
+**
+** Current State Character Seen New State
+** -------------------- -------------- -------------------
+** 0 Beginning of word "-" 3 Negative integer
+** digit 2 Integer
+** space 0 Beginning of word
+** otherwise 1 Arbitrary text
+**
+** 1 Arbitrary text space 0 Beginning of word
+** digit 2 Integer
+** otherwise 1 Arbitrary text
+**
+** 2 Integer space 0 Beginning of word
+** "." 4 Real number
+** digit 2 Integer
+** otherwise 1 Arbitrary text
+**
+** 3 Negative integer space 0 Beginning of word
+** "." 5 Negative Real num
+** digit 3 Negative integer
+** otherwise 1 Arbitrary text
+**
+** 4 Real number space 0 Beginning of word
+** digit 4 Real number
+** otherwise 1 Arbitrary text
+**
+** 5 Negative real num space 0 Beginning of word
+** digit 5 Negative real num
+** otherwise 1 Arbitrary text
+**
+** To implement this state machine, we first classify each character
+** into on of the following categories:
+**
+** 0 Text
+** 1 Space
+** 2 Digit
+** 3 "-"
+** 4 "."
+**
+** Given an arbitrary character, the array charClass[] maps that character
+** into one of the atove categories.
+*/
+static const unsigned char charClass[] = {
+ /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
+/* 0x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0,
+/* 1x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* 2x */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0,
+/* 3x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0,
+/* 4x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* 5x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* 6x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* 7x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* 8x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* 9x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* Ax */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* Bx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* Cx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* Dx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* Ex */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* Fx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+#define N_CHAR_CLASS 5
+
+/*
+** Given the current state number (0 thru 5), this array figures
+** the new state number given the character class.
+*/
+static const unsigned char stateMachine[] = {
+ /* Text, Space, Digit, "-", "." */
+ 1, 0, 2, 3, 1, /* State 0: Beginning of word */
+ 1, 0, 2, 1, 1, /* State 1: Arbitrary text */
+ 1, 0, 2, 1, 4, /* State 2: Integer */
+ 1, 0, 3, 1, 5, /* State 3: Negative integer */
+ 1, 0, 4, 1, 1, /* State 4: Real number */
+ 1, 0, 5, 1, 1, /* State 5: Negative real num */
+};
+
+/* This routine does a comparison of two strings. Case is used only
+** if useCase!=0. Numbers compare in numerical order.
+*/
+static int privateStrCmp(const char *atext, const char *btext, int useCase){
+ register unsigned char *a, *b, *map, ca, cb;
+ int result;
+ register int cclass = 0;
+
+ a = (unsigned char *)atext;
+ b = (unsigned char *)btext;
+ if( useCase ){
+ do{
+ if( (ca= *a++)!=(cb= *b++) ) break;
+ cclass = stateMachine[cclass*N_CHAR_CLASS + charClass[ca]];
+ }while( ca!=0 );
+ }else{
+ map = UpperToLower;
+ do{
+ if( (ca=map[*a++])!=(cb=map[*b++]) ) break;
+ cclass = stateMachine[cclass*N_CHAR_CLASS + charClass[ca]];
+ }while( ca!=0 );
+ }
+ switch( cclass ){
+ case 0:
+ case 1: {
+ if( isdigit(ca) && isdigit(cb) ){
+ cclass = 2;
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ switch( cclass ){
+ case 2:
+ case 3: {
+ if( isdigit(ca) ){
+ if( isdigit(cb) ){
+ int acnt, bcnt;
+ acnt = bcnt = 0;
+ while( isdigit(*a++) ) acnt++;
+ while( isdigit(*b++) ) bcnt++;
+ result = acnt - bcnt;
+ if( result==0 ) result = ca-cb;
+ }else{
+ result = 1;
+ }
+ }else if( isdigit(cb) ){
+ result = -1;
+ }else if( ca=='.' ){
+ result = 1;
+ }else if( cb=='.' ){
+ result = -1;
+ }else{
+ result = ca - cb;
+ cclass = 2;
+ }
+ if( cclass==3 ) result = -result;
+ break;
+ }
+ case 0:
+ case 1:
+ case 4: {
+ result = ca - cb;
+ break;
+ }
+ case 5: {
+ result = cb - ca;
+ };
+ }
+ return result;
+}
+
+/* This comparison routine is what we use for comparison operations
+** in an SQL expression. (Ex: name<'Hello' or value<5). Compare two
+** strings. Use case only as a tie-breaker. Numbers compare in
+** numerical order.
+*/
+int sqliteCompare(const char *atext, const char *btext){
+ int result;
+ result = privateStrCmp(atext, btext, 0);
+ if( result==0 ) result = privateStrCmp(atext, btext, 1);
+ return result;
+}
+
+/*
+** If you compile just this one file with the -DTEST_COMPARE=1 option,
+** it generates a program to test the comparisons routines.
+*/
+#ifdef TEST_COMPARE
+#include
+#include
+int sortCmp(const char **a, const char **b){
+ return sqliteCompare(*a, *b);
+}
+int main(int argc, char **argv){
+ int i, j, k, n;
+ static char *azStr[] = {
+ "abc", "aBc", "abcd", "aBcd",
+ "123", "124", "1234", "-123", "-124", "-1234",
+ "123.45", "123.456", "123.46", "-123.45", "-123.46", "-123.456",
+ "x9", "x10", "x-9", "x-10", "X9", "X10",
+ };
+ n = sizeof(azStr)/sizeof(azStr[0]);
+ qsort(azStr, n, sizeof(azStr[0]), sortCmp);
+ for(i=0; ipBe = pBe;
+ return p;
+}
+
+/*
+** Turn tracing on or off
+*/
+void sqliteVdbeTrace(Vdbe *p, FILE *trace){
+ p->trace = trace;
+}
+
+/*
+** Add a new instruction to the list of instructions current in the
+** VDBE. Return the address of the new instruction.
+**
+** Parameters:
+**
+** p Pointer to the VDBE
+**
+** op The opcode for this instruction
+**
+** p1, p2, p3 Three operands.
+**
+** lbl A symbolic label for this instruction.
+**
+** Symbolic labels are negative numbers that stand for the address
+** of instructions that have yet to be coded. When the instruction
+** is coded, its real address is substituted in the p2 field of
+** prior and subsequent instructions that have the lbl value in
+** their p2 fields.
+*/
+int sqliteVdbeAddOp(Vdbe *p, int op, int p1, int p2, const char *p3, int lbl){
+ int i, j;
+
+ i = p->nOp;
+ p->nOp++;
+ if( i>=p->nOpAlloc ){
+ int oldSize = p->nOpAlloc;
+ p->nOpAlloc = p->nOpAlloc*2 + 10;
+ p->aOp = sqliteRealloc(p->aOp, p->nOpAlloc*sizeof(Op));
+ if( p->aOp==0 ){
+ p->nOp = 0;
+ p->nOpAlloc = 0;
+ return 0;
+ }
+ memset(&p->aOp[oldSize], 0, (p->nOpAlloc-oldSize)*sizeof(Op));
+ }
+ p->aOp[i].opcode = op;
+ p->aOp[i].p1 = p1;
+ if( p2<0 && (-1-p2)nLabel && p->aLabel[-1-p2]>=0 ){
+ p2 = p->aLabel[-1-p2];
+ }
+ p->aOp[i].p2 = p2;
+ if( p3 && p3[0] ){
+ sqliteSetString(&p->aOp[i].p3, p3, 0);
+ }else{
+ p->aOp[i].p3 = 0;
+ }
+ if( lbl<0 && (-lbl)<=p->nLabel ){
+ p->aLabel[-1-lbl] = i;
+ for(j=0; jaOp[j].p2==lbl ) p->aOp[j].p2 = i;
+ }
+ }
+ return i;
+}
+
+/*
+** Resolve label "x" to be the address of the next instruction to
+** be inserted.
+*/
+void sqliteVdbeResolveLabel(Vdbe *p, int x){
+ int j;
+ if( x<0 && (-x)<=p->nLabel ){
+ p->aLabel[-1-x] = p->nOp;
+ for(j=0; jnOp; j++){
+ if( p->aOp[j].p2==x ) p->aOp[j].p2 = p->nOp;
+ }
+ }
+}
+
+/*
+** Return the address of the next instruction to be inserted.
+*/
+int sqliteVdbeCurrentAddr(Vdbe *p){
+ return p->nOp;
+}
+
+/*
+** Add a whole list of operations to the operation stack. Return the
+** address of the first operation added.
+*/
+int sqliteVdbeAddOpList(Vdbe *p, int nOp, VdbeOp const *aOp){
+ int addr;
+ if( p->nOp + nOp >= p->nOpAlloc ){
+ int oldSize = p->nOpAlloc;
+ p->nOpAlloc = p->nOpAlloc*2 + nOp + 10;
+ p->aOp = sqliteRealloc(p->aOp, p->nOpAlloc*sizeof(Op));
+ if( p->aOp==0 ){
+ p->nOp = 0;
+ p->nOpAlloc = 0;
+ return 0;
+ }
+ memset(&p->aOp[oldSize], 0, (p->nOpAlloc-oldSize)*sizeof(Op));
+ }
+ addr = p->nOp;
+ if( nOp>0 ){
+ int i;
+ for(i=0; i=0 && p->nOp>addr && zP3 ){
+ sqliteSetNString(&p->aOp[addr].p3, zP3, n, 0);
+ }
+}
+
+/*
+** If the P3 operand to the specified instruction appears
+** to be a quoted string token, then this procedure removes
+** the quotes.
+**
+** The quoting operator can be either a grave ascent (ASCII 0x27)
+** or a double quote character (ASCII 0x22). Two quotes in a row
+** resolve to be a single actual quote character within the string.
+*/
+void sqliteVdbeDequoteP3(Vdbe *p, int addr){
+ int quote;
+ int i, j;
+ char *z;
+ if( addr<0 || addr>=p->nOp ) return;
+ z = p->aOp[addr].p3;
+ quote = z[0];
+ if( quote!='\'' && quote!='"' ) return;
+ for(i=1, j=0; z[i]; i++){
+ if( z[i]==quote ){
+ if( z[i+1]==quote ){
+ z[j++] = quote;
+ i++;
+ }else{
+ z[j++] = 0;
+ break;
+ }
+ }else{
+ z[j++] = z[i];
+ }
+ }
+}
+
+/*
+** Create a new symbolic label for an instruction that has yet to be
+** coded. The symbolic label is really just a negative number. The
+** label can be used as the P2 value of an operation. Later, when
+** the label is resolved to a specific address, the VDBE will scan
+** through its operation list and change all values of P2 which match
+** the label into the resolved address.
+**
+** The VDBE knows that a P2 value is a label because labels are
+** always negative and P2 values are suppose to be non-negative.
+** Hence, a negative P2 value is a label that has yet to be resolved.
+*/
+int sqliteVdbeMakeLabel(Vdbe *p){
+ int i;
+ i = p->nLabel++;
+ if( i>=p->nLabelAlloc ){
+ p->nLabelAlloc = p->nLabelAlloc*2 + 10;
+ p->aLabel = sqliteRealloc( p->aLabel, p->nLabelAlloc*sizeof(int));
+ }
+ if( p->aLabel==0 ){
+ p->nLabel = 0;
+ p->nLabelAlloc = 0;
+ return 0;
+ }
+ p->aLabel[i] = -1;
+ return -1-i;
+}
+
+/*
+** Pop the stack N times. Free any memory associated with the
+** popped stack elements.
+*/
+static void PopStack(Vdbe *p, int N){
+ if( p->zStack==0 ) return;
+ while( p->tos>=0 && N-->0 ){
+ int i = p->tos--;
+ sqliteFree(p->zStack[i]);
+ p->zStack[i] = 0;
+ }
+}
+
+/*
+** Clean up the VM after execution.
+**
+** This routine will automatically close any tables, list, and/or
+** sorters that were left open.
+*/
+static void Cleanup(Vdbe *p){
+ int i;
+ PopStack(p, p->tos+1);
+ sqliteFree(p->azColName);
+ p->azColName = 0;
+ for(i=0; inTable; i++){
+ if( p->aTab[i].pTable ){
+ sqliteDbbeCloseTable(p->aTab[i].pTable);
+ p->aTab[i].pTable = 0;
+ }
+ }
+ sqliteFree(p->aTab);
+ p->aTab = 0;
+ p->nTable = 0;
+ for(i=0; inList; i++){
+ if( p->apList[i] ){
+ sqliteDbbeCloseTempFile(p->pBe, p->apList[i]);
+ p->apList[i] = 0;
+ }
+ }
+ sqliteFree(p->apList);
+ p->apList = 0;
+ p->nList = 0;
+ for(i=0; inSort; i++){
+ Sorter *pSorter;
+ while( (pSorter = p->apSort[i])!=0 ){
+ p->apSort[i] = pSorter->pNext;
+ sqliteFree(pSorter->zKey);
+ sqliteFree(pSorter->pData);
+ sqliteFree(pSorter);
+ }
+ }
+ sqliteFree(p->apSort);
+ p->apSort = 0;
+ p->nSort = 0;
+}
+
+/*
+** Delete an entire VDBE.
+*/
+void sqliteVdbeDelete(Vdbe *p){
+ int i;
+ if( p==0 ) return;
+ Cleanup(p);
+ if( p->nOpAlloc==0 ){
+ p->aOp = 0;
+ p->nOp = 0;
+ }
+ for(i=0; inOp; i++){
+ sqliteFree(p->aOp[i].p3);
+ }
+ sqliteFree(p->aOp);
+ sqliteFree(p->aLabel);
+ sqliteFree(p->iStack);
+ sqliteFree(p->zStack);
+ sqliteFree(p);
+}
+
+/*
+** A translation from opcode numbers to opcode names. Used for testing
+** and debugging only.
+**
+** If any of the numeric OP_ values for opcodes defined in sqliteVdbe.h
+** change, be sure to change this array to match. You can use the
+** "opNames.awk" awk script which is part of the source tree to regenerate
+** this array, then copy and paste it into this file, if you want.
+*/
+static char *zOpName[] = { 0,
+ "Open", "Close", "Destroy", "Fetch",
+ "New", "Put", "Delete", "Field",
+ "Key", "Rewind", "Next", "ResetIdx",
+ "NextIdx", "PutIdx", "DeleteIdx", "ListOpen",
+ "ListWrite", "ListRewind", "ListRead", "ListClose",
+ "SortOpen", "SortPut", "SortMakeRec", "SortMakeKey",
+ "Sort", "SortNext", "SortKey", "SortCallback",
+ "SortClose", "MakeRecord", "MakeKey", "Goto",
+ "If", "Halt", "ColumnCount", "ColumnName",
+ "Callback", "Integer", "String", "Pop",
+ "Dup", "Pull", "Add", "AddImm",
+ "Subtract", "Multiply", "Divide", "Min",
+ "Max", "Eq", "Ne", "Lt",
+ "Le", "Gt", "Ge", "IsNull",
+ "NotNull", "Negative", "And", "Or",
+ "Not", "Concat", "Noop",
+};
+
+/*
+** Given the name of an opcode, return its number. Return 0 if
+** there is no match.
+**
+** This routine is used for testing and debugging.
+*/
+int sqliteVdbeOpcode(const char *zName){
+ int i;
+ for(i=1; i<=OP_MAX; i++){
+ if( sqliteStrICmp(zName, zOpName[i])==0 ) return i;
+ }
+ return 0;
+}
+
+/*
+** Give a listing of the program in the virtual machine.
+**
+** The interface is the same as sqliteVdbeExec(). But instead of
+** running the code, it invokes the callback once for each instruction.
+** This feature is used to implement "EXPLAIN".
+*/
+int sqliteVdbeList(
+ Vdbe *p, /* The VDBE */
+ sqlite_callback xCallback, /* The callback */
+ void *pArg, /* 1st argument to callback */
+ char **pzErrMsg /* Error msg written here */
+){
+ int i, rc;
+ char *azField[6];
+ char zAddr[20];
+ char zP1[20];
+ char zP2[20];
+ static char *azColumnNames[] = {
+ "addr", "opcode", "p1", "p2", "p3", 0
+ };
+
+ if( xCallback==0 ) return 0;
+ azField[0] = zAddr;
+ azField[2] = zP1;
+ azField[3] = zP2;
+ azField[5] = 0;
+ rc = 0;
+ if( pzErrMsg ){ *pzErrMsg = 0; }
+ for(i=0; rc==0 && inOp; i++){
+ sprintf(zAddr,"%d",i);
+ sprintf(zP1,"%d", p->aOp[i].p1);
+ sprintf(zP2,"%d", p->aOp[i].p2);
+ azField[4] = p->aOp[i].p3;
+ if( azField[4]==0 ) azField[4] = "";
+ azField[1] = zOpName[p->aOp[i].opcode];
+ rc = xCallback(pArg, 5, azField, azColumnNames);
+ }
+ return rc;
+}
+
+/*
+** Make sure space has been allocated to hold at least N
+** stack elements. Allocate additional stack space if
+** necessary.
+**
+** Return 0 on success and non-zero if there are memory
+** allocation errors.
+*/
+static int NeedStack(Vdbe *p, int N){
+ int oldAlloc;
+ int i;
+ if( N>=p->nStackAlloc ){
+ oldAlloc = p->nStackAlloc;
+ p->nStackAlloc = N + 20;
+ p->iStack = sqliteRealloc(p->iStack, p->nStackAlloc*sizeof(int));
+ p->zStack = sqliteRealloc(p->zStack, p->nStackAlloc*sizeof(char*));
+ if( p->iStack==0 || p->zStack==0 ){
+ sqliteFree(p->iStack);
+ sqliteFree(p->zStack);
+ p->iStack = 0;
+ p->zStack = 0;
+ p->nStackAlloc = 0;
+ return 1;
+ }
+ for(i=oldAlloc; inStackAlloc; i++){
+ p->zStack[i] = 0;
+ }
+ }
+ return 0;
+}
+
+/*
+** Convert the given stack entity into a string if it isn't one
+** already. Return non-zero if we run out of memory.
+*/
+static int Stringify(Vdbe *p, int i){
+ if( p->zStack[i]==0 ){
+ char zBuf[30];
+ sprintf(zBuf,"%d",p->iStack[i]);
+ sqliteSetString(&p->zStack[i], zBuf, 0);
+ if( p->zStack[i]==0 ) return 1;
+ p->iStack[i] = strlen(p->zStack[i])+1;
+ }
+ return 0;
+}
+
+/*
+** Convert the given stack entity into a integer if it isn't one
+** already.
+*/
+static int Integerify(Vdbe *p, int i){
+ if( p->zStack[i]!=0 ){
+ p->iStack[i] = atoi(p->zStack[i]);
+ sqliteFree(p->zStack[i]);
+ p->zStack[i] = 0;
+ }
+ return p->iStack[i];
+}
+
+/*
+** The parameters are pointers to the head of two sorted lists
+** of Sorter structures. Merge these two lists together and return
+** a single sorted list. This routine forms the core of the merge-sort
+** algorithm.
+**
+** In the case of a tie, left sorts in front of right.
+*/
+static Sorter *Merge(Sorter *pLeft, Sorter *pRight){
+ Sorter sHead;
+ Sorter *pTail;
+ pTail = &sHead;
+ pTail->pNext = 0;
+ while( pLeft && pRight ){
+ int c = sqliteSortCompare(pLeft->zKey, pRight->zKey);
+ if( c<=0 ){
+ pTail->pNext = pLeft;
+ pLeft = pLeft->pNext;
+ }else{
+ pTail->pNext = pRight;
+ pRight = pRight->pNext;
+ }
+ pTail = pTail->pNext;
+ }
+ if( pLeft ){
+ pTail->pNext = pLeft;
+ }else if( pRight ){
+ pTail->pNext = pRight;
+ }
+ return sHead.pNext;
+}
+
+
+/*
+** Execute the program in the VDBE.
+**
+** If an error occurs, an error message is written to memory obtained
+** from sqliteMalloc() and *pzErrMsg is made to point to that memory.
+** The return parameter is the number of errors.
+**
+** If the callback every returns non-zero, then the program exits
+** immediately. No error message is written but the return value
+** from the callback because the return value of this routine.
+*/
+int sqliteVdbeExec(
+ Vdbe *p, /* The VDBE */
+ sqlite_callback xCallback, /* The callback */
+ void *pArg, /* 1st argument to callback */
+ char **pzErrMsg /* Error msg written here */
+){
+ int pc; /* The program counter */
+ Op *pOp; /* Current operation */
+ int rc; /* Value to return */
+ char zBuf[100]; /* Space to sprintf() and integer */
+
+ p->tos = -1;
+ rc = 0;
+ if( pzErrMsg ){ *pzErrMsg = 0; }
+ for(pc=0; rc==0 && pcnOp && pc>=0; pc++){
+ pOp = &p->aOp[pc];
+ if( p->trace ){
+ fprintf(p->trace,"%4d %-12s %4d %4d %s\n",
+ pc, zOpName[pOp->opcode], pOp->p1, pOp->p2,
+ pOp->p3 ? pOp->p3 : "");
+ }
+ switch( pOp->opcode ){
+ /* Opcode: Goto P2 * *
+ **
+ ** An unconditional jump to address P2.
+ ** The next instruction executed will be
+ ** the one at index P2 from the beginning of
+ ** the program.
+ */
+ case OP_Goto: {
+ pc = pOp->p2;
+ if( pc<0 || pc>p->nOp ){
+ sqliteSetString(pzErrMsg, "jump destination out of range", 0);
+ rc = 1;
+ }
+ pc--;
+ break;
+ }
+
+ /* Opcode: Halt * * *
+ **
+ ** Exit immediately. All open DBs, Lists, Sorts, etc are closed
+ ** automatically.
+ */
+ case OP_Halt: {
+ pc = p->nOp-1;
+ break;
+ }
+
+ /* Opcode: Integer P1 * *
+ **
+ ** The integer value P1 is pushed onto the stack.
+ */
+ case OP_Integer: {
+ int i = ++p->tos;
+ if( NeedStack(p, p->tos) ) goto no_mem;
+ p->iStack[i] = pOp->p1;
+ p->zStack[i] = 0;
+ break;
+ }
+
+ /* Opcode: String * * P3
+ **
+ ** The string value P3 is pushed onto the stack.
+ */
+ case OP_String: {
+ int i = ++p->tos;
+ char *z;
+ if( NeedStack(p, p->tos) ) goto no_mem;
+ z = pOp->p3;
+ if( z==0 ) z = "";
+ p->iStack[i] = strlen(z) + 1;
+ sqliteSetString(&p->zStack[i], z, 0);
+ break;
+ }
+
+ /* Opcode: Pop P1 * *
+ **
+ ** P1 elements are popped off of the top of stack and discarded.
+ */
+ case OP_Pop: {
+ PopStack(p, pOp->p1);
+ break;
+ }
+
+ /* Opcode: Dup P1 * *
+ **
+ ** A copy of the P1-th element of the stack
+ ** is made and pushed onto the top of the stack.
+ ** The top of the stack is element 0. So the
+ ** instruction "Dup 0 0 0" will make a copy of the
+ ** top of the stack.
+ */
+ case OP_Dup: {
+ int i = p->tos - pOp->p1;
+ int j = ++p->tos;
+ if( i<0 ) goto not_enough_stack;
+ if( NeedStack(p, p->tos) ) goto no_mem;
+ p->iStack[j] = p->iStack[i];
+ if( p->zStack[i] ){
+ p->zStack[j] = sqliteMalloc( p->iStack[j] );
+ if( p->zStack[j] ) memcpy(p->zStack[j], p->zStack[i], p->iStack[j]);
+ }else{
+ p->zStack[j] = 0;
+ }
+ break;
+ }
+
+ /* Opcode: Pull P1 * *
+ **
+ ** The P1-th element is removed its current location on
+ ** the stack and pushed back on top of the stack. The
+ ** top of the stack is element 0, so "Pull 0 0 0" is
+ ** a no-op.
+ */
+ case OP_Pull: {
+ int from = p->tos - pOp->p1;
+ int to = p->tos;
+ int i;
+ int ti;
+ char *tz;
+ if( from<0 ) goto not_enough_stack;
+ ti = p->iStack[from];
+ tz = p->zStack[from];
+ for(i=from; iiStack[i] = p->iStack[i+1];
+ p->zStack[i] = p->zStack[i+1];
+ }
+ p->iStack[to] = ti;
+ p->zStack[to] = tz;
+ break;
+ }
+
+ /* Opcode: ColumnCount P1 * *
+ **
+ ** Specify the number of column values that will appear in the
+ ** array passed as the 4th parameter to the callback. No checking
+ ** is done. If this value is wrong, a coredump can result.
+ */
+ case OP_ColumnCount: {
+ p->azColName = sqliteRealloc(p->azColName, (pOp->p1+1)*sizeof(char*));
+ if( p->azColName==0 ) goto no_mem;
+ p->azColName[pOp->p1] = 0;
+ break;
+ }
+
+ /* Opcode: ColumnName P1 * P3
+ **
+ ** P3 becomes the P1-th column name (first is 0). An array of pointers
+ ** to all column names is passed as the 4th parameter to the callback.
+ ** The ColumnCount opcode must be executed first to allocate space to
+ ** hold the column names. Failure to do this will likely result in
+ ** a coredump.
+ */
+ case OP_ColumnName: {
+ p->azColName[pOp->p1] = pOp->p3 ? pOp->p3 : "";
+ break;
+ }
+
+ /* Opcode: Callback P1 * *
+ **
+ ** Pop P1 values off the stack and form them into an array. Then
+ ** invoke the callback function using the newly formed array as the
+ ** 3rd parameter.
+ */
+ case OP_Callback: {
+ int i = p->tos - pOp->p1 + 1;
+ int j;
+ if( i<0 ) goto not_enough_stack;
+ if( NeedStack(p, p->tos+2) ) goto no_mem;
+ for(j=i; j<=p->tos; j++){
+ if( Stringify(p, j) ) goto no_mem;
+ }
+ p->zStack[p->tos+1] = 0;
+ rc = xCallback(pArg, pOp->p1, &p->zStack[i], p->azColName);
+ PopStack(p, pOp->p1);
+ break;
+ }
+
+ /* Opcode: Concat * * *
+ **
+ ** Pop two elements from the stack. Append the first (what used
+ ** to be the top of stack) to the second (the next on stack) to
+ ** form a new string. Push the new string back onto the stack.
+ */
+ case OP_Concat: {
+ int tos = p->tos;
+ int nos = tos - 1;
+ char *z;
+ if( nos<0 ) goto not_enough_stack;
+ Stringify(p, tos);
+ Stringify(p, nos);
+ z = 0;
+ sqliteSetString(&z, p->zStack[nos], p->zStack[tos], 0);
+ PopStack(p, 1);
+ sqliteFree(p->zStack[nos]);
+ p->zStack[nos] = z;
+ p->iStack[nos] = strlen(p->zStack[nos])+1;
+ break;
+ }
+
+ /* Opcode: Add * * *
+ **
+ ** Pop the top two elements from the stack, add them together,
+ ** and push the result back onto the stack. If either element
+ ** is a string then it is converted to a double using the atof()
+ ** function before the addition.
+ */
+ /* Opcode: Multiply * * *
+ **
+ ** Pop the top two elements from the stack, multiply them together,
+ ** and push the result back onto the stack. If either element
+ ** is a string then it is converted to a double using the atof()
+ ** function before the multiplication.
+ */
+ /* Opcode: Subtract * * *
+ **
+ ** Pop the top two elements from the stack, subtract the
+ ** first (what was on top of the stack) from the second (the
+ ** next on stack)
+ ** and push the result back onto the stack. If either element
+ ** is a string then it is converted to a double using the atof()
+ ** function before the subtraction.
+ */
+ /* Opcode: Divide * * *
+ **
+ ** Pop the top two elements from the stack, divide the
+ ** first (what was on top of the stack) from the second (the
+ ** next on stack)
+ ** and push the result back onto the stack. If either element
+ ** is a string then it is converted to a double using the atof()
+ ** function before the division. Division by zero causes the
+ ** program to abort with an error.
+ */
+ case OP_Add:
+ case OP_Subtract:
+ case OP_Multiply:
+ case OP_Divide: {
+ int tos = p->tos;
+ int nos = tos - 1;
+ if( nos<0 ) goto not_enough_stack;
+ if( p->zStack[tos]==0 && p->zStack[nos]==0 ){
+ int a, b;
+ a = p->iStack[tos];
+ b = p->iStack[nos];
+ switch( pOp->opcode ){
+ case OP_Add: b += a; break;
+ case OP_Subtract: b -= a; break;
+ case OP_Multiply: b *= a; break;
+ default: {
+ if( a==0 ){
+ sqliteSetString(pzErrMsg, "division by zero", 0);
+ rc = 1;
+ goto cleanup;
+ }
+ b /= a;
+ break;
+ }
+ }
+ PopStack(p, 1);
+ p->iStack[nos] = b;
+ }else{
+ double a, b;
+ Stringify(p, tos);
+ Stringify(p, nos);
+ a = atof(p->zStack[tos]);
+ b = atof(p->zStack[nos]);
+ switch( pOp->opcode ){
+ case OP_Add: b += a; break;
+ case OP_Subtract: b -= a; break;
+ case OP_Multiply: b *= a; break;
+ default: {
+ if( a==0.0 ){
+ sqliteSetString(pzErrMsg, "division by zero", 0);
+ rc = 1;
+ goto cleanup;
+ }
+ b /= a;
+ break;
+ }
+ }
+ sprintf(zBuf,"%g",b);
+ PopStack(p, 1);
+ sqliteSetString(&p->zStack[nos], zBuf, 0);
+ if( p->zStack[nos]==0 ) goto no_mem;
+ p->iStack[nos] = strlen(p->zStack[nos]) + 1;
+ }
+ break;
+ }
+
+ /* Opcode: Max * * *
+ **
+ ** Pop the top two elements from the stack then push back the
+ ** largest of the two.
+ */
+ case OP_Max: {
+ int tos = p->tos;
+ int nos = tos - 1;
+ if( nos<0 ) goto not_enough_stack;
+ if( p->zStack[tos]==0 && p->zStack[nos]==0 ){
+ if( p->iStack[nos]iStack[tos] ){
+ p->iStack[nos] = p->iStack[tos];
+ }
+ }else{
+ Stringify(p, tos);
+ Stringify(p, nos);
+ if( sqliteCompare(p->zStack[nos], p->zStack[tos])<0 ){
+ sqliteFree(p->zStack[nos]);
+ p->zStack[nos] = p->zStack[tos];
+ p->iStack[nos] = p->iStack[tos];
+ }
+ }
+ p->tos--;
+ break;
+ }
+
+ /* Opcode: Min * * *
+ **
+ ** Pop the top two elements from the stack then push back the
+ ** smaller of the two.
+ */
+ case OP_Min: {
+ int tos = p->tos;
+ int nos = tos - 1;
+ if( nos<0 ) goto not_enough_stack;
+ if( p->zStack[tos]==0 && p->zStack[nos]==0 ){
+ if( p->iStack[nos]>p->iStack[tos] ){
+ p->iStack[nos] = p->iStack[tos];
+ }
+ }else{
+ Stringify(p, tos);
+ Stringify(p, nos);
+ if( sqliteCompare(p->zStack[nos], p->zStack[tos])>0 ){
+ sqliteFree(p->zStack[nos]);
+ p->zStack[nos] = p->zStack[tos];
+ p->iStack[nos] = p->iStack[tos];
+ }
+ }
+ p->tos--;
+ break;
+ }
+
+ /* Opcode: AddImm P1 * *
+ **
+ ** Add the value P1 to whatever is on top of the stack.
+ */
+ case OP_AddImm: {
+ int tos = p->tos;
+ if( tos<0 ) goto not_enough_stack;
+ Integerify(p, tos);
+ p->iStack[tos] += pOp->p1;
+ break;
+ }
+
+ /* Opcode: Eq * P2 *
+ **
+ ** Pop the top two elements from the stack. If they are equal, then
+ ** jump to instruction P2. Otherwise, continue to the next instruction.
+ */
+ /* Opcode: Ne * P2 *
+ **
+ ** Pop the top two elements from the stack. If they are not equal, then
+ ** jump to instruction P2. Otherwise, continue to the next instruction.
+ */
+ /* Opcode: Lt * P2 *
+ **
+ ** Pop the top two elements from the stack. If second element (the
+ ** next on stack) is less than the first (the top of stack), then
+ ** jump to instruction P2. Otherwise, continue to the next instruction.
+ ** In other words, jump if NOSTOS.
+ */
+ /* Opcode: Ge * P2 *
+ **
+ ** Pop the top two elements from the stack. If second element (the next
+ ** on stack) is greater than or equal to the first (the top of stack),
+ ** then jump to instruction P2. In other words, jump if NOS>=TOS.
+ */
+ case OP_Eq:
+ case OP_Ne:
+ case OP_Lt:
+ case OP_Le:
+ case OP_Gt:
+ case OP_Ge: {
+ int tos = p->tos;
+ int nos = tos - 1;
+ int c;
+ if( nos<0 ) goto not_enough_stack;
+ if( p->zStack[tos]==0 && p->zStack[nos]==0 ){
+ int a, b;
+ a = p->iStack[tos];
+ b = p->iStack[nos];
+ switch( pOp->opcode ){
+ case OP_Eq: c = b==a; break;
+ case OP_Ne: c = b!=a; break;
+ case OP_Lt: c = ba; break;
+ default: c = b>=a; break;
+ }
+ }else{
+ Stringify(p, tos);
+ Stringify(p, nos);
+ c = sqliteCompare(p->zStack[nos], p->zStack[tos]);
+ switch( pOp->opcode ){
+ case OP_Eq: c = c==0; break;
+ case OP_Ne: c = c!=0; break;
+ case OP_Lt: c = c<0; break;
+ case OP_Le: c = c<=0; break;
+ case OP_Gt: c = c>0; break;
+ default: c = c>=0; break;
+ }
+ }
+ PopStack(p, 2);
+ if( c ) pc = pOp->p2-1;
+ break;
+ }
+
+ /* Opcode: And * * *
+ **
+ ** Pop two values off the stack. Take the logical AND of the
+ ** two values and push the resulting boolean value back onto the
+ ** stack. Integers are considered false if zero and true otherwise.
+ ** Strings are considered false if their length is zero and true
+ ** otherwise.
+ */
+ /* Opcode: Or * * *
+ **
+ ** Pop two values off the stack. Take the logical OR of the
+ ** two values and push the resulting boolean value back onto the
+ ** stack. Integers are considered false if zero and true otherwise.
+ ** Strings are considered false if their length is zero and true
+ ** otherwise.
+ */
+ case OP_And:
+ case OP_Or: {
+ int tos = p->tos;
+ int nos = tos - 1;
+ int x, y, c;
+ if( nos<0 ) goto not_enough_stack;
+ x = p->zStack[nos] ? p->zStack[nos][0] : p->iStack[nos];
+ y = p->zStack[tos] ? p->zStack[tos][0] : p->iStack[tos];
+ if( pOp->opcode==OP_And ){
+ c = x && y;
+ }else{
+ c = x || y;
+ }
+ PopStack(p, 2);
+ p->tos++;
+ p->iStack[nos] = c;
+ break;
+ }
+
+ /* Opcode: Negative * * *
+ **
+ ** Treat the top of the stack as a numeric quantity. Replace it
+ ** with its additive inverse. If the top of stack is a string,
+ ** then it is converted into a number using atof().
+ */
+ case OP_Negative: {
+ int tos;
+ if( (tos = p->tos)<0 ) goto not_enough_stack;
+ if( p->zStack[tos] ){
+ double r = atof(p->zStack[tos]);
+ sprintf(zBuf, "%g", -r);
+ sqliteSetString(&p->zStack[tos], zBuf, 0);
+ p->iStack[tos] = strlen(zBuf) + 1;
+ }else{
+ p->iStack[tos] = -p->iStack[tos];
+ }
+ break;
+ }
+
+ /* Opcode: Not * * *
+ **
+ ** Treat the top of the stack as a boolean value. Replace it
+ ** with its complement. Integers are false if zero and true
+ ** otherwise. Strings are false if zero-length and true otherwise.
+ */
+ case OP_Not: {
+ int c;
+ if( p->tos<0 ) goto not_enough_stack;
+ c = p->zStack[p->tos] ? p->zStack[p->tos][0] : p->iStack[p->tos];
+ PopStack(p, 1);
+ p->tos++;
+ p->iStack[p->tos] = !c;
+ break;
+ }
+
+ /* Opcode: Noop * * *
+ **
+ ** Do nothing. This instruction is often useful as a jump
+ ** destination.
+ */
+ case OP_Noop: {
+ break;
+ }
+
+ /* Opcode: If * P2 *
+ **
+ ** Pop a single boolean from the stack. If the boolean popped is
+ ** true, then jump to p2. Otherwise continue to the next instruction.
+ ** An integer is false if zero and true otherwise. A string is
+ ** false if it has zero length and true otherwise.
+ */
+ case OP_If: {
+ int c;
+ if( p->tos<0 ) goto not_enough_stack;
+ c = p->zStack[p->tos] ? p->zStack[p->tos][0] : p->iStack[p->tos];
+ PopStack(p, 1);
+ if( c ) pc = pOp->p2-1;
+ break;
+ }
+
+ /* Opcode: IsNull * P2 *
+ **
+ ** Pop a single value from the stack. If the value popped is the
+ ** empty string, then jump to p2. Otherwise continue to the next
+ ** instruction.
+ */
+ case OP_IsNull: {
+ int c;
+ if( p->tos<0 ) goto not_enough_stack;
+ c = p->zStack[p->tos]!=0 && p->zStack[p->tos][0]==0;
+ PopStack(p, 1);
+ if( c ) pc = pOp->p2-1;
+ break;
+ }
+
+ /* Opcode: NotNull * P2 *
+ **
+ ** Pop a single value from the stack. If the value popped is not an
+ ** empty string, then jump to p2. Otherwise continue to the next
+ ** instruction.
+ */
+ case OP_NotNull: {
+ int c;
+ if( p->tos<0 ) goto not_enough_stack;
+ c = p->zStack[p->tos]==0 || p->zStack[p->tos][0]!=0;
+ PopStack(p, 1);
+ if( c ) pc = pOp->p2-1;
+ break;
+ }
+
+ /* Opcode: MakeRecord P1 * *
+ **
+ ** Convert the top P1 entries of the stack into a single entry
+ ** suitable for use as a data record in the database. To do this
+ ** each entry is converted to a string and all the strings are
+ ** concatenated. The null-terminators are preserved by the concatation
+ ** and serve as a boundry marker between fields. The lowest entry
+ ** on the stack is the first in the concatenation and the top of
+ ** the stack is the last. After all fields are concatenated, an
+ ** index header is added. The index header consists of P1 integers
+ ** which hold the offset of the beginning of each field from the
+ ** beginning of the completed record including the header.
+ */
+ case OP_MakeRecord: {
+ char *zNewRecord;
+ int nByte;
+ int nField;
+ int i, j;
+ int addr;
+
+ nField = pOp->p1;
+ if( p->tos+1tos-nField+1; i<=p->tos; i++){
+ if( Stringify(p, i) ) goto no_mem;
+ nByte += p->iStack[i];
+ }
+ nByte += sizeof(int)*nField;
+ zNewRecord = sqliteMalloc( nByte );
+ if( zNewRecord==0 ) goto no_mem;
+ j = 0;
+ addr = sizeof(int)*nField;
+ for(i=p->tos-nField+1; itos; i++){
+ memcpy(&zNewRecord[j], (char*)&addr, sizeof(int));
+ addr += p->iStack[i];
+ j += sizeof(int);
+ }
+ memcpy(&zNewRecord[j], (char*)&addr, sizeof(int));
+ j += sizeof(int);
+ for(i=p->tos-nField+1; i<=p->tos; i++){
+ memcpy(&zNewRecord[j], p->zStack[i], p->iStack[i]);
+ j += p->iStack[i];
+ }
+ PopStack(p, nField);
+ NeedStack(p, p->tos+1);
+ p->tos++;
+ p->iStack[p->tos] = nByte;
+ p->zStack[p->tos] = zNewRecord;
+ break;
+ }
+
+ /* Opcode: MakeKey P1 * *
+ **
+ ** Convert the top P1 entries of the stack into a single entry suitable
+ ** for use as the key in an index or a sort. The top P1 records are
+ ** concatenated with a tab character (ASCII 0x09) used as a record
+ ** separator. The entire concatenation is null-terminated. The
+ ** lowest entry in the stack is the first field and the top of the
+ ** stack becomes the last.
+ **
+ ** See also the SortMakeKey opcode.
+ */
+ case OP_MakeKey: {
+ char *zNewKey;
+ int nByte;
+ int nField;
+ int i, j;
+
+ nField = pOp->p1;
+ if( p->tos+1tos-nField+1; i<=p->tos; i++){
+ if( Stringify(p, i) ) goto no_mem;
+ nByte += p->iStack[i]+1;
+ }
+ zNewKey = sqliteMalloc( nByte );
+ if( zNewKey==0 ) goto no_mem;
+ j = 0;
+ for(i=p->tos-nField+1; i<=p->tos; i++){
+ memcpy(&zNewKey[j], p->zStack[i], p->iStack[i]-1);
+ j += p->iStack[i]-1;
+ if( itos ) zNewKey[j++] = '\t';
+ }
+ zNewKey[j] = 0;
+ PopStack(p, nField);
+ NeedStack(p, p->tos+1);
+ p->tos++;
+ p->iStack[p->tos] = nByte;
+ p->zStack[p->tos] = zNewKey;
+ break;
+ }
+
+ /* Open P1 P3 P2
+ **
+ ** Open a new database table named P3. Give it an identifier P1.
+ ** Open readonly if P2==0 and for reading and writing if P2!=0.
+ ** The table is created if it does not already exist and P2!=0.
+ ** If there is already another table opened on P1, then the old
+ ** table is closed first. All tables are automatically closed when
+ ** the VDBE finishes execution. The P1 values need not be
+ ** contiguous but all P1 values should be small integers. It is
+ ** an error for P1 to be negative.
+ */
+ case OP_Open: {
+ int i = pOp->p1;
+ if( i<0 ) goto bad_instruction;
+ if( i>=p->nTable ){
+ int j;
+ p->aTab = sqliteRealloc( p->aTab, (i+1)*sizeof(VdbeTable) );
+ if( p->aTab==0 ){ p->nTable = 0; goto no_mem; }
+ for(j=p->nTable; j<=i; j++) p->aTab[j].pTable = 0;
+ p->nTable = i+1;
+ }else if( p->aTab[i].pTable ){
+ sqliteDbbeCloseTable(p->aTab[i].pTable);
+ }
+ p->aTab[i].pTable = sqliteDbbeOpenTable(p->pBe, pOp->p3, pOp->p2);
+ p->aTab[i].index = 0;
+ break;
+ }
+
+ /* Opcode: Close P1 * *
+ **
+ ** Close a database table previously opened as P1. If P1 is not
+ ** currently open, this instruction is a no-op.
+ */
+ case OP_Close: {
+ int i = pOp->p1;
+ if( i>=0 && inTable && p->aTab[i].pTable ){
+ sqliteDbbeCloseTable(p->aTab[i].pTable);
+ p->aTab[i].pTable = 0;
+ }
+ break;
+ }
+
+ /* Opcode: Fetch P1 * *
+ **
+ ** Pop the top of the stack and use its value as a key to fetch
+ ** a record from database table or index P1. The data is held
+ ** in the P1 cursor until needed. The data is not pushed onto the
+ ** stack or anything like that.
+ */
+ case OP_Fetch: {
+ int i = pOp->p1;
+ int tos = p->tos;
+ if( tos<0 ) goto not_enough_stack;
+ if( i>=0 && inTable && p->aTab[i].pTable ){
+ if( p->zStack[tos]==0 ){
+ sqliteDbbeFetch(p->aTab[i].pTable, sizeof(int),
+ (char*)&p->iStack[tos]);
+ }else{
+ sqliteDbbeFetch(p->aTab[i].pTable, p->iStack[tos], p->zStack[tos]);
+ }
+ }
+ PopStack(p, 1);
+ break;
+ }
+
+ /* Opcode: New P1 * *
+ **
+ ** Get a new integer key not previous used by table P1 and
+ ** push it onto the stack.
+ */
+ case OP_New: {
+ int i = pOp->p1;
+ int v;
+ if( i<0 || i>=p->nTable || p->aTab[i].pTable==0 ){
+ v = 0;
+ }else{
+ v = sqliteDbbeNew(p->aTab[i].pTable);
+ }
+ NeedStack(p, p->tos+1);
+ p->tos++;
+ p->iStack[p->tos] = v;
+ break;
+ }
+
+ /* Opcode: Put P1 * *
+ **
+ ** Write an entry into the database table P1. A new entry is
+ ** created if it doesn't already exist, or the data for an existing
+ ** entry is overwritten. The data is the value on the top of the
+ ** stack. The key is the next value down on the stack. The stack
+ ** is popped twice by this instruction.
+ */
+ case OP_Put: {
+ int tos = p->tos;
+ int nos = p->tos-1;
+ int i = pOp->p1;
+ if( nos<0 ) goto not_enough_stack;
+ if( i>=0 && inTable && p->aTab[i].pTable!=0 ){
+ char *zKey;
+ int nKey;
+ Stringify(p, tos);
+ if( p->zStack[nos]!=0 ){
+ nKey = p->iStack[nos];
+ zKey = p->zStack[nos];
+ }else{
+ nKey = sizeof(int);
+ zKey = (char*)&p->iStack[nos];
+ }
+ sqliteDbbePut(p->aTab[i].pTable, nKey, zKey,
+ p->iStack[tos], p->zStack[tos]);
+ }
+ PopStack(p, 2);
+ break;
+ }
+
+ /* Opcode: Delete P1 * *
+ **
+ ** The top of the stack is a key. Remove this key and its data
+ ** from database table P1. Then pop the stack to discard the key.
+ */
+ case OP_Delete: {
+ int tos = p->tos;
+ int i = pOp->p1;
+ if( tos<0 ) goto not_enough_stack;
+ if( i>=0 && inTable && p->aTab[i].pTable!=0 ){
+ char *zKey;
+ int nKey;
+ if( p->zStack[tos]!=0 ){
+ nKey = p->iStack[tos];
+ zKey = p->zStack[tos];
+ }else{
+ nKey = sizeof(int);
+ zKey = (char*)&p->iStack[tos];
+ }
+ sqliteDbbeDelete(p->aTab[i].pTable, nKey, zKey);
+ }
+ PopStack(p, 1);
+ break;
+ }
+
+ /* Opcode: Field P1 P2 *
+ **
+ ** Push onto the stack the value of the P2-th field from the
+ ** most recent Fetch from table P1.
+ */
+ case OP_Field: {
+ int *pAddr;
+ int amt;
+ int i = pOp->p1;
+ int p2 = pOp->p2;
+ int tos = ++p->tos;
+ DbbeTable *pTab;
+ char *z;
+
+ if( NeedStack(p, p->tos) ) goto no_mem;
+ if( i>=0 && inTable && (pTab = p->aTab[i].pTable)!=0 ){
+ amt = sqliteDbbeDataLength(pTab);
+ if( amt<=sizeof(int)*(p2+1) ){
+ sqliteSetString(&p->zStack[tos], "", 0);
+ break;
+ }
+ pAddr = (int*)sqliteDbbeReadData(pTab, sizeof(int)*p2);
+ z = sqliteDbbeReadData(pTab, *pAddr);
+ sqliteSetString(&p->zStack[tos], z, 0);
+ p->iStack[tos] = strlen(z)+1;
+ }
+ break;
+ }
+
+ /* Opcode: Key P1 * *
+ **
+ ** Push onto the stack an integer which is the first 4 bytes of the
+ ** the key to the current entry in a sequential scan of the table P1.
+ ** A sequential scan is started using the Next opcode.
+ */
+ case OP_Key: {
+ int i = pOp->p1;
+ int tos = ++p->tos;
+ DbbeTable *pTab;
+
+ if( NeedStack(p, p->tos) ) goto no_mem;
+ if( i>=0 && inTable && (pTab = p->aTab[i].pTable)!=0 ){
+ char *z = sqliteDbbeReadKey(pTab, 0);
+ memcpy(&p->iStack[tos], z, sizeof(int));
+ p->zStack[tos] = 0;
+ }
+ break;
+ }
+
+ /* Opcode: Rewind P1 * *
+ **
+ ** The next use of the Key or Field or Next instruction for P1
+ ** will refer to the first entry in the table.
+ */
+ case OP_Rewind: {
+ int i = pOp->p1;
+ if( i>=0 && inTable && p->aTab[i].pTable!=0 ){
+ sqliteDbbeRewind(p->aTab[i].pTable);
+ }
+ break;
+ }
+
+ /* Opcode: Next P1 P2 *
+ **
+ ** Advance P1 to the next entry in the table. Or, if there are no
+ ** more entries, rewind P1 and jump to location P2.
+ */
+ case OP_Next: {
+ int i = pOp->p1;
+ if( i>=0 && inTable && p->aTab[i].pTable!=0 ){
+ if( sqliteDbbeNextKey(p->aTab[i].pTable)==0 ){
+ pc = pOp->p2;
+ if( pc<0 || pc>p->nOp ){
+ sqliteSetString(pzErrMsg, "jump destination out of range", 0);
+ rc = 1;
+ }
+ pc--;
+ }
+ }
+ break;
+ }
+
+ /* Opcode: ResetIdx P1 * *
+ **
+ ** Begin treating the current row of table P1 as an index. The next
+ ** NextIdx instruction will refer to the first index in the table.
+ */
+ case OP_ResetIdx: {
+ int i = pOp->p1;
+ if( i>=0 && inTable ){
+ p->aTab[i].index = 0;
+ }
+ break;
+ }
+
+ /* Opcode: NextIdx P1 P2 *
+ **
+ ** Push the next index from the current entry of table P1 onto the
+ ** stack and advance the pointer. If there are no more indices, then
+ ** reset the table entry and jump to P2
+ */
+ case OP_NextIdx: {
+ int i = pOp->p1;
+ int tos = ++p->tos;
+ DbbeTable *pTab;
+
+ if( NeedStack(p, p->tos) ) goto no_mem;
+ p->zStack[tos] = 0;
+ if( i>=0 && inTable && (pTab = p->aTab[i].pTable)!=0 ){
+ int *aIdx;
+ int nIdx;
+ int j;
+ nIdx = sqliteDbbeDataLength(pTab)/sizeof(int);
+ aIdx = (int*)sqliteDbbeReadData(pTab, 0);
+ for(j=p->aTab[i].index; jiStack[tos] = aIdx[j];
+ break;
+ }
+ }
+ if( j>=nIdx ){
+ j = -1;
+ pc = pOp->p2;
+ if( pc<0 || pc>p->nOp ){
+ sqliteSetString(pzErrMsg, "jump destination out of range", 0);
+ rc = 1;
+ }
+ pc--;
+ }
+ p->aTab[i].index = j+1;
+ }
+ break;
+ }
+
+ /* Opcode: PutIdx P1 * *
+ **
+ ** The top of the stack hold an index key (proably made using the
+ ** MakeKey instruction) and next on stack holds an index value for
+ ** a table. Locate the record in the index P1 that has the key
+ ** and insert the index value into its
+ ** data. Write the results back to the index.
+ ** If the key doesn't exist it is created.
+ */
+ case OP_PutIdx: {
+ int i = pOp->p1;
+ int tos = p->tos;
+ int nos = tos - 1;
+ DbbeTable *pTab;
+ if( nos<0 ) goto not_enough_stack;
+ if( i>=0 && inTable && (pTab = p->aTab[i].pTable)!=0 ){
+ int r;
+ int newVal = Integerify(p, nos);
+ Stringify(p, tos);
+ r = sqliteDbbeFetch(pTab, p->iStack[tos], p->zStack[tos]);
+ if( r==0 ){
+ /* Create a new record for this index */
+ sqliteDbbePut(pTab, p->iStack[tos], p->zStack[tos],
+ sizeof(int), (char*)&newVal);
+ }else{
+ /* Extend the existing record */
+ int nIdx;
+ int *aIdx;
+ nIdx = sqliteDbbeDataLength(pTab)/sizeof(int);
+ aIdx = sqliteMalloc( sizeof(int)*(nIdx+1) );
+ if( aIdx==0 ) goto no_mem;
+ sqliteDbbeCopyData(pTab, 0, nIdx*sizeof(int), (char*)aIdx);
+ aIdx[nIdx] = newVal;
+ sqliteDbbePut(pTab, p->iStack[tos], p->zStack[tos],
+ sizeof(int)*(nIdx+1), (char*)aIdx);
+ sqliteFree(aIdx);
+ }
+ }
+ PopStack(p, 2);
+ break;
+ }
+
+ /* Opcode: DeleteIdx P1 * *
+ **
+ ** The top of the stack is a key and next on stack is an index value.
+ ** Locate the record
+ ** in index P1 that has the key and remove the index value from its
+ ** data. Write the results back to the table. If after removing
+ ** the index value no more indices remain in the record, then the
+ ** record is removed from the table.
+ */
+ case OP_DeleteIdx: {
+ int i = pOp->p1;
+ int tos = p->tos;
+ int nos = tos - 1;
+ DbbeTable *pTab;
+ if( nos<0 ) goto not_enough_stack;
+ if( i>=0 && inTable && (pTab = p->aTab[i].pTable)!=0 ){
+ int *aIdx;
+ int nIdx;
+ int j;
+ int r;
+ int oldVal = Integerify(p, nos);
+ Stringify(p, tos);
+ r = sqliteDbbeFetch(pTab, p->iStack[tos], p->zStack[tos]);
+ if( r==0 ) break;
+ nIdx = sqliteDbbeDataLength(pTab)/sizeof(int);
+ aIdx = (int*)sqliteDbbeReadData(pTab, 0);
+ for(j=0; j=nIdx ) break;
+ aIdx[j] = aIdx[nIdx-1];
+ if( nIdx==1 ){
+ sqliteDbbeDelete(pTab, p->iStack[tos], p->zStack[tos]);
+ }else{
+ sqliteDbbePut(pTab, p->iStack[tos], p->zStack[tos],
+ sizeof(int)*(nIdx-1), (char*)aIdx);
+ }
+ }
+ PopStack(p, 2);
+ break;
+ }
+
+ /* Opcode: Destroy * * P3
+ **
+ ** Drop the table whose name is P3. The file that holds this table
+ ** is removed from the disk drive.
+ */
+ case OP_Destroy: {
+ sqliteDbbeDropTable(p->pBe, pOp->p3);
+ break;
+ }
+
+ /* Opcode: ListOpen P1 * *
+ **
+ ** Open a file used for temporary storage of index numbers. P1
+ ** will server as a handle to this temporary file for future
+ ** interactions. If another temporary file with the P1 handle is
+ ** already opened, the prior file is closed and a new one opened
+ ** in its place.
+ */
+ case OP_ListOpen: {
+ int i = pOp->p1;
+ if( i<0 ) goto bad_instruction;
+ if( i>=p->nList ){
+ int j;
+ p->apList = sqliteRealloc( p->apList, (i+1)*sizeof(FILE*) );
+ if( p->apList==0 ){ p->nList = 0; goto no_mem; }
+ for(j=p->nList; j<=i; j++) p->apList[j] = 0;
+ p->nList = i+1;
+ }else if( p->apList[i] ){
+ sqliteDbbeCloseTempFile(p->pBe, p->apList[i]);
+ }
+ p->apList[i] = sqliteDbbeOpenTempFile(p->pBe);
+ break;
+ }
+
+ /* Opcode: ListWrite P1 * *
+ **
+ ** Write the integer on the top of the stack
+ ** into the temporary storage file P1.
+ */
+ case OP_ListWrite: {
+ int i = pOp->p1;
+ if( i<0 ) goto bad_instruction;
+ if( p->tos<0 ) goto not_enough_stack;
+ if( inList && p->apList[i]!=0 ){
+ int val = Integerify(p, p->tos);
+ PopStack(p, 1);
+ fwrite(&val, sizeof(int), 1, p->apList[i]);
+ }
+ break;
+ }
+
+ /* Opcode: ListRewind P1 * *
+ **
+ ** Rewind the temporary buffer P1 back to the beginning.
+ */
+ case OP_ListRewind: {
+ int i = pOp->p1;
+ if( i<0 ) goto bad_instruction;
+ if( inList && p->apList[i]!=0 ){
+ rewind(p->apList[i]);
+ }
+ break;
+ }
+
+ /* Opcode: ListRead P1 P2 *
+ **
+ ** Attempt to read an integer from temporary storage buffer P1
+ ** and push it onto the stack. If the storage buffer is empty
+ ** push nothing but instead jump to P2.
+ */
+ case OP_ListRead: {
+ int i = pOp->p1;
+ int val, amt;
+ if( i<0 || i>=p->nList || p->apList[i]==0 ) goto bad_instruction;
+ amt = fread(&val, sizeof(int), 1, p->apList[i]);
+ if( amt==1 ){
+ p->tos++;
+ if( NeedStack(p, p->tos) ) goto no_mem;
+ p->iStack[p->tos] = val;
+ p->zStack[p->tos] = 0;
+ }else{
+ pc = pOp->p2;
+ if( pc<0 || pc>p->nOp ){
+ sqliteSetString(pzErrMsg, "jump destination out of range", 0);
+ rc = 1;
+ }
+ pc--;
+ }
+ break;
+ }
+
+ /* Opcode: ListClose P1 * *
+ **
+ ** Close the temporary storage buffer and discard its contents.
+ */
+ case OP_ListClose: {
+ int i = pOp->p1;
+ if( i<0 ) goto bad_instruction;
+ if( inList && p->apList[i]!=0 ){
+ sqliteDbbeCloseTempFile(p->pBe, p->apList[i]);
+ p->apList[i] = 0;
+ }
+ break;
+ }
+
+ /* Opcode: SortOpen P1 * *
+ **
+ ** Create a new sorter with index P1
+ */
+ case OP_SortOpen: {
+ int i = pOp->p1;
+ if( i<0 ) goto bad_instruction;
+ if( i>=p->nSort ){
+ int j;
+ p->apSort = sqliteRealloc( p->apSort, (i+1)*sizeof(Sorter*) );
+ if( p->apSort==0 ){ p->nSort = 0; goto no_mem; }
+ for(j=p->nSort; j<=i; j++) p->apSort[j] = 0;
+ p->nSort = i+1;
+ }
+ break;
+ }
+
+ /* Opcode: SortPut P1 * *
+ **
+ ** The TOS is the key and the NOS is the data. Pop both from the stack
+ ** and put them on the sorter.
+ */
+ case OP_SortPut: {
+ int i = pOp->p1;
+ Sorter *pSorter;
+ if( i<0 || i>=p->nSort ) goto bad_instruction;
+ if( p->tos<1 ) goto not_enough_stack;
+ Stringify(p, p->tos);
+ Stringify(p, p->tos-1);
+ pSorter = sqliteMalloc( sizeof(Sorter) );
+ if( pSorter==0 ) goto no_mem;
+ pSorter->pNext = p->apSort[i];
+ p->apSort[i] = pSorter;
+ pSorter->nKey = p->iStack[p->tos];
+ pSorter->zKey = p->zStack[p->tos];
+ pSorter->nData = p->iStack[p->tos-1];
+ pSorter->pData = p->zStack[p->tos-1];
+ p->zStack[p->tos] = p->zStack[p->tos-1] = 0;
+ PopStack(p, 2);
+ break;
+ }
+
+ /* Opcode: SortMakeRec P1 * *
+ **
+ ** The top P1 elements are the arguments to a callback. Form these
+ ** elements into a single data entry that can be stored on a sorter
+ ** using SortPut and later fed to a callback using SortCallback.
+ */
+ case OP_SortMakeRec: {
+ char *z;
+ char **azArg;
+ int nByte;
+ int nField;
+ int i, j;
+
+ nField = pOp->p1;
+ if( p->tos+1tos-nField+1; i<=p->tos; i++){
+ if( Stringify(p, i) ) goto no_mem;
+ nByte += p->iStack[i];
+ }
+ nByte += sizeof(char*)*(nField+1);
+ azArg = sqliteMalloc( nByte );
+ if( azArg==0 ) goto no_mem;
+ z = (char*)&azArg[nField+1];
+ for(j=0, i=p->tos-nField+1; i<=p->tos; i++, j++){
+ azArg[j] = z;
+ strcpy(z, p->zStack[i]);
+ z += p->iStack[i];
+ }
+ PopStack(p, nField);
+ NeedStack(p, p->tos+1);
+ p->tos++;
+ p->iStack[p->tos] = nByte;
+ p->zStack[p->tos] = (char*)azArg;
+ break;
+ }
+
+ /* Opcode: SortMakeKey P1 * P3
+ **
+ ** Convert the top few entries of the stack into a sort key. The
+ ** number of stack entries consumed is the number of characters in
+ ** the string P3. One character from P3 is prepended to each entry.
+ ** The first character of P3 is prepended to the element lowest in
+ ** the stack and the last character of P3 is appended to the top of
+ ** the stack. All stack entries are separated by a \000 character
+ ** in the result. The whole key is terminated by two \000 characters
+ ** in a row.
+ **
+ ** See also the MakeKey opcode.
+ */
+ case OP_SortMakeKey: {
+ char *zNewKey;
+ int nByte;
+ int nField;
+ int i, j, k;
+
+ nField = strlen(pOp->p3);
+ if( p->tos+1tos-nField+1; i<=p->tos; i++){
+ if( Stringify(p, i) ) goto no_mem;
+ nByte += p->iStack[i]+2;
+ }
+ zNewKey = sqliteMalloc( nByte );
+ if( zNewKey==0 ) goto no_mem;
+ j = 0;
+ k = nField-1;
+ for(i=p->tos-nField+1; i<=p->tos; i++){
+ zNewKey[j++] = pOp->p3[k--];
+ memcpy(&zNewKey[j], p->zStack[i], p->iStack[i]-1);
+ j += p->iStack[i]-1;
+ zNewKey[j++] = 0;
+ }
+ zNewKey[j] = 0;
+ PopStack(p, nField);
+ NeedStack(p, p->tos+1);
+ p->tos++;
+ p->iStack[p->tos] = nByte;
+ p->zStack[p->tos] = zNewKey;
+ break;
+ }
+
+ /* Opcode: Sort P1 * *
+ **
+ ** Sort all elements on the given sorter. The algorithm is a
+ ** mergesort.
+ */
+ case OP_Sort: {
+ int j;
+ j = pOp->p1;
+ if( j<0 ) goto bad_instruction;
+ if( jnSort ){
+ int i;
+ Sorter *pElem;
+ Sorter *apSorter[NSORT];
+ for(i=0; iapSort[j] ){
+ pElem = p->apSort[j];
+ p->apSort[j] = pElem->pNext;
+ pElem->pNext = 0;
+ for(i=0; i=NSORT-1 ){
+ apSorter[NSORT-1] = Merge(apSorter[NSORT-1],pElem);
+ }
+ }
+ pElem = 0;
+ for(i=0; iapSort[j] = pElem;
+ }
+ break;
+ }
+
+ /* Opcode: SortNext P1 P2 *
+ **
+ ** Push the data for the topmost element in the given sorter onto the
+ ** stack, then remove the element from the sorter.
+ */
+ case OP_SortNext: {
+ int i = pOp->p1;
+ if( i<0 ) goto bad_instruction;
+ if( inSort && p->apSort[i]!=0 ){
+ Sorter *pSorter = p->apSort[i];
+ p->apSort[i] = pSorter->pNext;
+ p->tos++;
+ NeedStack(p, p->tos);
+ p->zStack[p->tos] = pSorter->pData;
+ p->iStack[p->tos] = pSorter->nData;
+ sqliteFree(pSorter->zKey);
+ sqliteFree(pSorter);
+ }else{
+ pc = pOp->p2;
+ if( pc<0 || pc>p->nOp ){
+ sqliteSetString(pzErrMsg, "jump destination out of range", 0);
+ rc = 1;
+ }
+ pc--;
+ }
+ break;
+ }
+
+ /* Opcode: SortKey P1 * *
+ **
+ ** Push the key for the topmost element of the sorter onto the stack.
+ ** But don't change the sorter an any other way.
+ */
+ case OP_SortKey: {
+ int i = pOp->p1;
+ if( i<0 ) goto bad_instruction;
+ if( inSort && p->apSort[i]!=0 ){
+ Sorter *pSorter = p->apSort[i];
+ p->tos++;
+ NeedStack(p, p->tos);
+ sqliteSetString(&p->zStack[p->tos], pSorter->zKey, 0);
+ p->iStack[p->tos] = pSorter->nKey;
+ }
+ break;
+ }
+
+ /* Opcode: SortCallback P1 P2 *
+ **
+ ** The top of the stack contains a callback record built using
+ ** the SortMakeRec operation with the same P1 value as this
+ ** instruction. Pop this record from the stack and invoke the
+ ** callback on it.
+ */
+ case OP_SortCallback: {
+ int i = p->tos;
+ if( i<0 ) goto not_enough_stack;
+ rc = xCallback(pArg, pOp->p1, (char**)p->zStack[i], p->azColName);
+ PopStack(p, 1);
+ break;
+ }
+
+ /* Opcode: SortClose P1 * *
+ **
+ ** Close the given sorter and remove all its elements.
+ */
+ case OP_SortClose: {
+ Sorter *pSorter;
+ int i = pOp->p1;
+ if( i<0 ) goto bad_instruction;
+ if( inSort ){
+ while( (pSorter = p->apSort[i])!=0 ){
+ p->apSort[i] = pSorter->pNext;
+ sqliteFree(pSorter->zKey);
+ sqliteFree(pSorter->pData);
+ sqliteFree(pSorter);
+ }
+ }
+ break;
+ }
+
+ /* An other opcode is illegal...
+ */
+ default: {
+ sprintf(zBuf,"%d",pOp->opcode);
+ sqliteSetString(pzErrMsg, "unknown opcode ", zBuf, 0);
+ rc = 1;
+ break;
+ }
+ }
+ if( p->trace && p->tos>=0 ){
+ int i;
+ fprintf(p->trace, "Stack:");
+ for(i=p->tos; i>=0 && i>p->tos-5; i--){
+ if( p->zStack[i] ){
+ fprintf(p->trace, " [%.11s]", p->zStack[i]);
+ }else{
+ fprintf(p->trace, " [%d]", p->iStack[i]);
+ }
+ }
+ fprintf(p->trace,"\n");
+ }
+ }
+
+cleanup:
+ Cleanup(p);
+ return rc;
+
+ /* Jump to here if a malloc() fails. It's hard to get a malloc()
+ ** to fail on a modern VM computer, so this code is untested.
+ */
+no_mem:
+ Cleanup(p);
+ sqliteSetString(pzErrMsg, "out or memory", 0);
+ return 1;
+
+ /* Jump to here if a operator is encountered that requires more stack
+ ** operands than are currently available on the stack.
+ */
+not_enough_stack:
+ sprintf(zBuf,"%d",pc);
+ sqliteSetString(pzErrMsg, "too few operands on stack at ", zBuf, 0);
+ rc = 1;
+ goto cleanup;
+
+ /* Jump here if an illegal or illformed instruction is executed.
+ */
+bad_instruction:
+ sprintf(zBuf,"%d",pc);
+ sqliteSetString(pzErrMsg, "illegal operation at ", zBuf, 0);
+ rc = 1;
+ goto cleanup;
+
+}
diff --git a/src/vdbe.h b/src/vdbe.h
new file mode 100644
index 0000000000..5bafa9943b
--- /dev/null
+++ b/src/vdbe.h
@@ -0,0 +1,166 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** 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 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** General Public License for more details.
+**
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*************************************************************************
+** Header file for the Virtual DataBase Engine (VDBE)
+**
+** This header defines the interface to the virtual database engine
+** or VDBE. The VDBE implements an abstract machine that runs a
+** simple program to access and modify the underlying database.
+**
+** $Id: vdbe.h,v 1.1 2000/05/29 14:26:02 drh Exp $
+*/
+#ifndef _SQLITE_VDBE_H_
+#define _SQLITE_VDBE_H_
+#include
+
+/*
+** A single VDBE is an opaque structure named "Vdbe". Only routines
+** in the source file sqliteVdbe.c are allowed to see the insides
+** of this structure.
+*/
+typedef struct Vdbe Vdbe;
+
+/*
+** A single instruction of the virtual machine has an opcode
+** and as many as three operands. The instruction is recorded
+** as an instance of the following structure:
+*/
+struct VdbeOp {
+ int opcode; /* What operation to perform */
+ int p1; /* First operand */
+ int p2; /* Second parameter (often the jump destination) */
+ char *p3; /* Third parameter */
+};
+typedef struct VdbeOp VdbeOp;
+
+/*
+** The following macro converts a relative address in the p2 field
+** of a VdbeOp structure into a negative number so that
+** sqliteVdbeAddOpList() knows that the address is relative. Calling
+** the macro again restores the address.
+*/
+#define ADDR(X) (-1-(X))
+
+/*
+** These are the available opcodes.
+**
+** If any of the values changes or if opcodes are added or removed,
+** be sure to also update the zOpName[] array in sqliteVdbe.c to
+** mirror the change.
+**
+** The source tree contains an AWK script named renumberOps.awk that
+** can be used to renumber these opcodes when new opcodes are inserted.
+*/
+#define OP_Open 1
+#define OP_Close 2
+#define OP_Destroy 3
+#define OP_Fetch 4
+#define OP_New 5
+#define OP_Put 6
+#define OP_Delete 7
+#define OP_Field 8
+#define OP_Key 9
+#define OP_Rewind 10
+#define OP_Next 11
+#define OP_ResetIdx 12
+#define OP_NextIdx 13
+#define OP_PutIdx 14
+#define OP_DeleteIdx 15
+
+#define OP_ListOpen 16
+#define OP_ListWrite 17
+#define OP_ListRewind 18
+#define OP_ListRead 19
+#define OP_ListClose 20
+
+#define OP_SortOpen 21
+#define OP_SortPut 22
+#define OP_SortMakeRec 23
+#define OP_SortMakeKey 24
+#define OP_Sort 25
+#define OP_SortNext 26
+#define OP_SortKey 27
+#define OP_SortCallback 28
+#define OP_SortClose 29
+
+#define OP_MakeRecord 30
+#define OP_MakeKey 31
+
+#define OP_Goto 32
+#define OP_If 33
+#define OP_Halt 34
+
+#define OP_ColumnCount 35
+#define OP_ColumnName 36
+#define OP_Callback 37
+
+#define OP_Integer 38
+#define OP_String 39
+#define OP_Pop 40
+#define OP_Dup 41
+#define OP_Pull 42
+
+#define OP_Add 43
+#define OP_AddImm 44
+#define OP_Subtract 45
+#define OP_Multiply 46
+#define OP_Divide 47
+#define OP_Min 48
+#define OP_Max 49
+#define OP_Eq 50
+#define OP_Ne 51
+#define OP_Lt 52
+#define OP_Le 53
+#define OP_Gt 54
+#define OP_Ge 55
+#define OP_IsNull 56
+#define OP_NotNull 57
+#define OP_Negative 58
+#define OP_And 59
+#define OP_Or 60
+#define OP_Not 61
+#define OP_Concat 62
+#define OP_Noop 63
+
+#define OP_MAX 63
+
+/*
+** Prototypes for the VDBE interface. See comments on the implementation
+** for a description of what each of these routines does.
+*/
+Vdbe *sqliteVdbeCreate(Dbbe*);
+int sqliteVdbeAddOp(Vdbe*,int,int,int,const char*,int);
+int sqliteVdbeAddOpList(Vdbe*, int nOp, VdbeOp const *aOp);
+void sqliteVdbeChangeP3(Vdbe*, int addr, const char *zP1, int N);
+void sqliteVdbeDequoteP3(Vdbe*, int addr);
+int sqliteVdbeMakeLabel(Vdbe*);
+void sqliteVdbeDelete(Vdbe*);
+int sqliteVdbeOpcode(const char *zName);
+int sqliteVdbeExec(Vdbe*,sqlite_callback,void*,char**);
+int sqliteVdbeList(Vdbe*,sqlite_callback,void*,char**);
+void sqliteVdbeResolveLabel(Vdbe*, int);
+int sqliteVdbeCurrentAddr(Vdbe*);
+void sqliteVdbeTrace(Vdbe*,FILE*);
+
+
+#endif
diff --git a/src/where.c b/src/where.c
new file mode 100644
index 0000000000..ea15a5eeb3
--- /dev/null
+++ b/src/where.c
@@ -0,0 +1,566 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** 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 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** General Public License for more details.
+**
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This module contains C code that generates VDBE code used to process
+** the WHERE clause of SQL statements. Also found here are subroutines
+** to generate VDBE code to evaluate expressions.
+**
+** $Id: where.c,v 1.1 2000/05/29 14:26:02 drh Exp $
+*/
+#include "sqliteInt.h"
+
+/*
+** The query generator uses an array of instances of this structure to
+** help it analyze the subexpressions of the WHERE clause. Each WHERE
+** clause subexpression is separated from the others by an AND operator.
+*/
+typedef struct ExprInfo ExprInfo;
+struct ExprInfo {
+ Expr *p; /* Pointer to the subexpression */
+ int indexable; /* True if this subexprssion is usable by an index */
+ int idxLeft; /* p->pLeft is a field in this table number. -1 if
+ ** p->pLeft is not the field of any table */
+ int idxRight; /* p->pRight is a field in this table number. -1 if
+ ** p->pRight is not the field of any table */
+ unsigned prereqLeft; /* Tables referenced by p->pLeft */
+ unsigned prereqRight; /* Tables referenced by p->pRight */
+};
+
+/*
+** Determine the number of elements in an array.
+*/
+#define ARRAYSIZE(X) (sizeof(X)/sizeof(X[0]))
+
+/*
+** This routine is used to divide the WHERE expression into subexpressions
+** separated by the AND operator.
+**
+** aSlot[] is an array of subexpressions structures.
+** There are nSlot spaces left in this array. This routine attempts to
+** split pExpr into subexpressions and fills aSlot[] with those subexpressions.
+** The return value is the number of slots filled.
+*/
+static int exprSplit(int nSlot, ExprInfo *aSlot, Expr *pExpr){
+ int cnt = 0;
+ if( pExpr==0 || nSlot<1 ) return 0;
+ if( nSlot==1 || pExpr->op!=TK_AND ){
+ aSlot[0].p = pExpr;
+ return 1;
+ }
+ if( pExpr->pLeft->op!=TK_AND ){
+ aSlot[0].p = pExpr->pLeft;
+ cnt = 1 + exprSplit(nSlot-1, &aSlot[1], pExpr->pRight);
+ }else{
+ cnt = exprSplit(nSlot, aSlot, pExpr->pRight);
+ cnt += exprSplit(nSlot-cnt, &aSlot[cnt], pExpr->pLeft);
+ }
+ return cnt;
+}
+
+/*
+** This routine walks (recursively) an expression tree and generates
+** a bitmask indicating which tables are used in that expression
+** tree. Bit 0 of the mask is set if table 0 is used. But 1 is set
+** if table 1 is used. And so forth.
+**
+** In order for this routine to work, the calling function must have
+** previously invoked sqliteExprResolveIds() on the expression. See
+** the header comment on that routine for additional information.
+*/
+static int exprTableUsage(Expr *p){
+ unsigned int mask = 0;
+ if( p==0 ) return 0;
+ if( p->op==TK_FIELD ){
+ return 1<iTable;
+ }
+ if( p->pRight ){
+ mask = exprTableUsage(p->pRight);
+ }
+ if( p->pLeft ){
+ mask |= exprTableUsage(p->pLeft);
+ }
+ return mask;
+}
+
+/*
+** The input to this routine is an ExprInfo structure with only the
+** "p" field filled in. The job of this routine is to analyze the
+** subexpression and populate all the other fields of the ExprInfo
+** structure.
+*/
+static void exprAnalyze(ExprInfo *pInfo){
+ Expr *pExpr = pInfo->p;
+ pInfo->prereqLeft = exprTableUsage(pExpr->pLeft);
+ pInfo->prereqRight = exprTableUsage(pExpr->pRight);
+ pInfo->indexable = 0;
+ pInfo->idxLeft = -1;
+ pInfo->idxRight = -1;
+ if( pExpr->op==TK_EQ && (pInfo->prereqRight & pInfo->prereqLeft)==0 ){
+ if( pExpr->pRight->op==TK_FIELD ){
+ pInfo->idxRight = pExpr->pRight->iTable;
+ pInfo->indexable = 1;
+ }
+ if( pExpr->pLeft->op==TK_FIELD ){
+ pInfo->idxLeft = pExpr->pLeft->iTable;
+ pInfo->indexable = 1;
+ }
+ }
+}
+
+/*
+** Generating the beginning of the loop used for WHERE clause processing.
+** The return value is a pointer to an (opaque) structure that contains
+** information needed to terminate the loop. Later, the calling routine
+** should invoke sqliteWhereEnd() with the return value of this function
+** in order to complete the WHERE clause processing.
+**
+** If an error occurs, this routine returns NULL.
+*/
+WhereInfo *sqliteWhereBegin(
+ Parse *pParse, /* The parser context */
+ IdList *pTabList, /* A list of all tables */
+ Expr *pWhere, /* The WHERE clause */
+ int pushKey /* If TRUE, leave the table key on the stack */
+){
+ int i; /* Loop counter */
+ WhereInfo *pWInfo; /* Will become the return value of this function */
+ Vdbe *v = pParse->pVdbe; /* The virtual database engine */
+ int brk, cont; /* Addresses used during code generation */
+ int *aOrder; /* Order in which pTabList entries are searched */
+ int nExpr; /* Number of subexpressions in the WHERE clause */
+ int loopMask; /* One bit set for each outer loop */
+ int haveKey; /* True if KEY is on the stack */
+ Index *aIdx[32]; /* Index to use on each nested loop. */
+ ExprInfo aExpr[50]; /* The WHERE clause is divided into these expressions */
+
+ /* Allocate space for aOrder[]. */
+ aOrder = sqliteMalloc( sizeof(int) * pTabList->nId );
+
+ /* Allocate and initialize the WhereInfo structure that will become the
+ ** return value.
+ */
+ pWInfo = sqliteMalloc( sizeof(WhereInfo) );
+ if( pWInfo==0 ){
+ sqliteFree(aOrder);
+ return 0;
+ }
+ pWInfo->pParse = pParse;
+ pWInfo->pTabList = pTabList;
+
+ /* Split the WHERE clause into as many as 32 separate subexpressions
+ ** where each subexpression is separated by an AND operator. Any additional
+ ** subexpressions are attached in the aExpr[32] and will not enter
+ ** into the query optimizer computations. 32 is chosen as the cutoff
+ ** since that is the number of bits in an integer that we use for an
+ ** expression-used mask.
+ */
+ memset(aExpr, 0, sizeof(aExpr));
+ nExpr = exprSplit(ARRAYSIZE(aExpr), aExpr, pWhere);
+
+ /* Analyze all of the subexpressions.
+ */
+ for(i=0; inId-1] will
+ ** be the innermost loop.
+ **
+ ** Someday will put in a good algorithm here to reorder to the loops
+ ** for an effiecient query. But for now, just use whatever order the
+ ** tables appear in in the pTabList.
+ */
+ for(i=0; inId; i++){
+ aOrder[i] = i;
+ }
+
+ /* Figure out what index to use (if any) for each nested loop.
+ ** Make aIdx[i] point to the index to use for the i-th nested loop
+ ** where i==0 is the outer loop and i==pTabList->nId-1 is the inner
+ ** loop.
+ **
+ ** Actually, if there are more than 32 tables in the join, only the
+ ** first 32 tables are candidates for indices.
+ */
+ loopMask = 0;
+ for(i=0; inId && ia[idx].pTab;
+ Index *pIdx;
+ Index *pBestIdx = 0;
+
+ /* Do a search for usable indices. Leave pBestIdx pointing to
+ ** most specific usable index.
+ **
+ ** "Most specific" means that pBestIdx is the usable index that
+ ** has the largest value for nField. A usable index is one for
+ ** which there are subexpressions to compute every field of the
+ ** index.
+ */
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ int j;
+ int fieldMask = 0;
+
+ if( pIdx->nField>32 ) continue;
+ for(j=0; jpLeft->iField;
+ int k;
+ for(k=0; knField; k++){
+ if( pIdx->aiField[k]==iField ){
+ fieldMask |= 1<pRight->iField;
+ int k;
+ for(k=0; knField; k++){
+ if( pIdx->aiField[k]==iField ){
+ fieldMask |= 1<nField) ){
+ if( pBestIdx==0 || pBestIdx->nFieldnField ){
+ pBestIdx = pIdx;
+ }
+ }
+ }
+ aIdx[i] = pBestIdx;
+ }
+
+ /* Open all tables in the pTabList and all indices in aIdx[].
+ */
+ for(i=0; inId; i++){
+ sqliteVdbeAddOp(v, OP_Open, i, 0, pTabList->a[i].pTab->zName, 0);
+ if( inId+i, 0, aIdx[i]->zName, 0);
+ }
+ }
+
+ /* Generate the code to do the search
+ */
+ pWInfo->iBreak = brk = sqliteVdbeMakeLabel(v);
+ loopMask = 0;
+ for(i=0; inId; i++){
+ int j, k;
+ int idx = aOrder[i];
+ Index *pIdx = inField; j++){
+ for(k=0; kpLeft->iField==pIdx->aiField[j]
+ ){
+ sqliteExprCode(pParse, aExpr[k].p->pRight);
+ aExpr[k].p = 0;
+ break;
+ }
+ if( aExpr[k].idxRight==idx
+ && (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft
+ && aExpr[k].p->pRight->iField==pIdx->aiField[j]
+ ){
+ sqliteExprCode(pParse, aExpr[k].p->pLeft);
+ aExpr[k].p = 0;
+ break;
+ }
+ }
+ }
+ sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_Fetch, pTabList->nId+i, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_NextIdx, pTabList->nId+i, brk, 0, cont);
+ if( i==pTabList->nId-1 && pushKey ){
+ haveKey = 1;
+ }else{
+ sqliteVdbeAddOp(v, OP_Fetch, idx, 0, 0, 0);
+ haveKey = 0;
+ }
+ }
+ loopMask |= 1<iContinue = cont;
+ if( pushKey && !haveKey ){
+ sqliteVdbeAddOp(v, OP_Key, 0, 0, 0, 0);
+ }
+ sqliteFree(aOrder);
+ return pWInfo;
+}
+
+/*
+** Generate the end of the WHERE loop.
+*/
+void sqliteWhereEnd(WhereInfo *pWInfo){
+ Vdbe *v = pWInfo->pParse->pVdbe;
+ sqliteVdbeAddOp(v, OP_Goto, 0, pWInfo->iContinue, 0, 0);
+ sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, pWInfo->iBreak);
+ sqliteFree(pWInfo);
+ return;
+}
+
+/*
+** Generate code into the current Vdbe to evaluate the given
+** expression and leave the result on the stack.
+*/
+void sqliteExprCode(Parse *pParse, Expr *pExpr){
+ Vdbe *v = pParse->pVdbe;
+ int op;
+ switch( pExpr->op ){
+ case TK_PLUS: op = OP_Add; break;
+ case TK_MINUS: op = OP_Subtract; break;
+ case TK_STAR: op = OP_Multiply; break;
+ case TK_SLASH: op = OP_Divide; break;
+ case TK_AND: op = OP_And; break;
+ case TK_OR: op = OP_Or; break;
+ case TK_LT: op = OP_Lt; break;
+ case TK_LE: op = OP_Le; break;
+ case TK_GT: op = OP_Gt; break;
+ case TK_GE: op = OP_Ge; break;
+ case TK_NE: op = OP_Ne; break;
+ case TK_EQ: op = OP_Eq; break;
+ case TK_ISNULL: op = OP_IsNull; break;
+ case TK_NOTNULL: op = OP_NotNull; break;
+ case TK_NOT: op = OP_Not; break;
+ case TK_UMINUS: op = OP_Negative; break;
+ default: break;
+ }
+ switch( pExpr->op ){
+ case TK_FIELD: {
+ sqliteVdbeAddOp(v, OP_Field, pExpr->iTable, pExpr->iField, 0, 0);
+ break;
+ }
+ case TK_INTEGER: {
+ int i = atoi(pExpr->token.z);
+ sqliteVdbeAddOp(v, OP_Integer, i, 0, 0, 0);
+ break;
+ }
+ case TK_FLOAT: {
+ int addr = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0);
+ sqliteVdbeChangeP3(v, addr, pExpr->token.z, pExpr->token.n);
+ break;
+ }
+ case TK_STRING: {
+ int addr = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0);
+ sqliteVdbeChangeP3(v, addr, pExpr->token.z, pExpr->token.n);
+ sqliteVdbeDequoteP3(v, addr);
+ break;
+ }
+ case TK_NULL: {
+ sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
+ break;
+ }
+ case TK_AND:
+ case TK_OR:
+ case TK_PLUS:
+ case TK_STAR:
+ case TK_MINUS:
+ case TK_SLASH: {
+ sqliteExprCode(pParse, pExpr->pLeft);
+ sqliteExprCode(pParse, pExpr->pRight);
+ sqliteVdbeAddOp(v, op, 0, 0, 0, 0);
+ break;
+ }
+ case TK_LT:
+ case TK_LE:
+ case TK_GT:
+ case TK_GE:
+ case TK_NE:
+ case TK_EQ: {
+ int dest;
+ sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0);
+ sqliteExprCode(pParse, pExpr->pLeft);
+ sqliteExprCode(pParse, pExpr->pRight);
+ dest = sqliteVdbeCurrentAddr(v) + 2;
+ sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
+ sqliteVdbeAddOp(v, OP_AddImm, 1, 0, 0, 0);
+ break;
+ }
+ case TK_NOT:
+ case TK_UMINUS: {
+ sqliteExprCode(pParse, pExpr->pLeft);
+ sqliteVdbeAddOp(v, op, 0, 0, 0, 0);
+ break;
+ }
+ case TK_ISNULL:
+ case TK_NOTNULL: {
+ int dest;
+ sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0);
+ sqliteExprCode(pParse, pExpr->pLeft);
+ dest = sqliteVdbeCurrentAddr(v) + 2;
+ sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
+ sqliteVdbeAddOp(v, OP_AddImm, 1, 0, 0, 0);
+ break;
+ }
+ }
+ return;
+}
+
+/*
+** Generate code for a boolean expression such that a jump is made
+** to the label "dest" if the expression is true but execution
+** continues straight thru if the expression is false.
+*/
+void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
+ Vdbe *v = pParse->pVdbe;
+ int op = 0;
+ switch( pExpr->op ){
+ case TK_LT: op = OP_Lt; break;
+ case TK_LE: op = OP_Le; break;
+ case TK_GT: op = OP_Gt; break;
+ case TK_GE: op = OP_Ge; break;
+ case TK_NE: op = OP_Ne; break;
+ case TK_EQ: op = OP_Eq; break;
+ case TK_ISNULL: op = OP_IsNull; break;
+ case TK_NOTNULL: op = OP_NotNull; break;
+ default: break;
+ }
+ switch( pExpr->op ){
+ case TK_AND: {
+ int d2 = sqliteVdbeMakeLabel(v);
+ sqliteExprIfFalse(pParse, pExpr->pLeft, d2);
+ sqliteExprIfTrue(pParse, pExpr->pRight, dest);
+ sqliteVdbeResolveLabel(v, d2);
+ break;
+ }
+ case TK_OR: {
+ sqliteExprIfTrue(pParse, pExpr->pLeft, dest);
+ sqliteExprIfTrue(pParse, pExpr->pRight, dest);
+ break;
+ }
+ case TK_LT:
+ case TK_LE:
+ case TK_GT:
+ case TK_GE:
+ case TK_NE:
+ case TK_EQ: {
+ sqliteExprCode(pParse, pExpr->pLeft);
+ sqliteExprCode(pParse, pExpr->pRight);
+ sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
+ break;
+ }
+ case TK_ISNULL:
+ case TK_NOTNULL: {
+ sqliteExprCode(pParse, pExpr->pLeft);
+ sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
+ break;
+ }
+ default: {
+ sqliteExprCode(pParse, pExpr);
+ sqliteVdbeAddOp(v, OP_If, 0, dest, 0, 0);
+ break;
+ }
+ }
+}
+
+/*
+** Generate code for boolean expression such that a jump is made
+** to the label "dest" if the expression is false but execution
+** continues straight thru if the expression is true.
+*/
+void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
+ Vdbe *v = pParse->pVdbe;
+ int op = 0;
+ switch( pExpr->op ){
+ case TK_LT: op = OP_Ge; break;
+ case TK_LE: op = OP_Gt; break;
+ case TK_GT: op = OP_Le; break;
+ case TK_GE: op = OP_Lt; break;
+ case TK_NE: op = OP_Eq; break;
+ case TK_EQ: op = OP_Ne; break;
+ case TK_ISNULL: op = OP_NotNull; break;
+ case TK_NOTNULL: op = OP_IsNull; break;
+ default: break;
+ }
+ switch( pExpr->op ){
+ case TK_AND: {
+ sqliteExprIfFalse(pParse, pExpr->pLeft, dest);
+ sqliteExprIfFalse(pParse, pExpr->pRight, dest);
+ break;
+ }
+ case TK_OR: {
+ int d2 = sqliteVdbeMakeLabel(v);
+ sqliteExprIfTrue(pParse, pExpr->pLeft, d2);
+ sqliteExprIfFalse(pParse, pExpr->pRight, dest);
+ sqliteVdbeResolveLabel(v, d2);
+ break;
+ }
+ case TK_LT:
+ case TK_LE:
+ case TK_GT:
+ case TK_GE:
+ case TK_NE:
+ case TK_EQ: {
+ sqliteExprCode(pParse, pExpr->pLeft);
+ sqliteExprCode(pParse, pExpr->pRight);
+ sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
+ break;
+ }
+ case TK_ISNULL:
+ case TK_NOTNULL: {
+ sqliteExprCode(pParse, pExpr->pLeft);
+ sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
+ break;
+ }
+ default: {
+ sqliteExprCode(pParse, pExpr);
+ sqliteVdbeAddOp(v, OP_Not, 0, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_If, 0, dest, 0, 0);
+ break;
+ }
+ }
+}
diff --git a/tool/gdbmdump.c b/tool/gdbmdump.c
new file mode 100644
index 0000000000..4385355ee5
--- /dev/null
+++ b/tool/gdbmdump.c
@@ -0,0 +1,90 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** 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 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** General Public License for more details.
+**
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*************************************************************************
+** A utility to dump the entire contents of a GDBM table in a
+** readable format.
+*/
+#include
+#include
+#include
+#include
+
+static void print_data(char *zPrefix, datum p){
+ int i, j;
+
+ printf("%-5s: ", zPrefix);
+ for(i=0; i
+#include
+#include
+#include
+
+extern void qsort();
+extern double strtod();
+extern long strtol();
+extern void free();
+extern int access();
+extern int atoi();
+
+#ifndef __WIN32__
+# if defined(_WIN32) || defined(WIN32)
+# define __WIN32__
+# endif
+#endif
+
+/* #define PRIVATE static */
+#define PRIVATE
+
+#ifdef TEST
+#define MAXRHS 5 /* Set low to exercise exception code */
+#else
+#define MAXRHS 1000
+#endif
+
+char *msort();
+extern void *malloc();
+
+/******** From the file "action.h" *************************************/
+struct action *Action_new();
+struct action *Action_sort();
+void Action_add();
+
+/********* From the file "assert.h" ************************************/
+void myassert();
+#ifndef NDEBUG
+# define assert(X) if(!(X))myassert(__FILE__,__LINE__)
+#else
+# define assert(X)
+#endif
+
+/********** From the file "build.h" ************************************/
+void FindRulePrecedences();
+void FindFirstSets();
+void FindStates();
+void FindLinks();
+void FindFollowSets();
+void FindActions();
+
+/********* From the file "configlist.h" *********************************/
+void Configlist_init(/* void */);
+struct config *Configlist_add(/* struct rule *, int */);
+struct config *Configlist_addbasis(/* struct rule *, int */);
+void Configlist_closure(/* void */);
+void Configlist_sort(/* void */);
+void Configlist_sortbasis(/* void */);
+struct config *Configlist_return(/* void */);
+struct config *Configlist_basis(/* void */);
+void Configlist_eat(/* struct config * */);
+void Configlist_reset(/* void */);
+
+/********* From the file "error.h" ***************************************/
+void ErrorMsg( /* char *, int, char *, ... */ );
+
+/****** From the file "option.h" ******************************************/
+struct s_options {
+ enum { OPT_FLAG=1, OPT_INT, OPT_DBL, OPT_STR,
+ OPT_FFLAG, OPT_FINT, OPT_FDBL, OPT_FSTR} type;
+ char *label;
+ char *arg;
+ char *message;
+};
+int optinit(/* char**,struct s_options*,FILE* */);
+int optnargs(/* void */);
+char *optarg(/* int */);
+void opterr(/* int */);
+void optprint(/* void */);
+
+/******** From the file "parse.h" *****************************************/
+void Parse(/* struct lemon *lemp */);
+
+/********* From the file "plink.h" ***************************************/
+struct plink *Plink_new(/* void */);
+void Plink_add(/* struct plink **, struct config * */);
+void Plink_copy(/* struct plink **, struct plink * */);
+void Plink_delete(/* struct plink * */);
+
+/********** From the file "report.h" *************************************/
+void Reprint(/* struct lemon * */);
+void ReportOutput(/* struct lemon * */);
+void ReportTable(/* struct lemon * */);
+void ReportHeader(/* struct lemon * */);
+void CompressTables(/* struct lemon * */);
+
+/********** From the file "set.h" ****************************************/
+void SetSize(/* int N */); /* All sets will be of size N */
+char *SetNew(/* void */); /* A new set for element 0..N */
+void SetFree(/* char* */); /* Deallocate a set */
+
+int SetAdd(/* char*,int */); /* Add element to a set */
+int SetUnion(/* char *A,char *B */); /* A <- A U B, thru element N */
+
+#define SetFind(X,Y) (X[Y]) /* True if Y is in set X */
+
+/********** From the file "struct.h" *************************************/
+/*
+** Principal data structures for the LEMON parser generator.
+*/
+
+typedef enum {FALSE=0, TRUE} Boolean;
+
+/* Symbols (terminals and nonterminals) of the grammar are stored
+** in the following: */
+struct symbol {
+ char *name; /* Name of the symbol */
+ int index; /* Index number for this symbol */
+ enum {
+ TERMINAL,
+ NONTERMINAL
+ } type; /* Symbols are all either TERMINALS or NTs */
+ struct rule *rule; /* Linked list of rules of this (if an NT) */
+ int prec; /* Precedence if defined (-1 otherwise) */
+ enum e_assoc {
+ LEFT,
+ RIGHT,
+ NONE,
+ UNK
+ } assoc; /* Associativity if predecence is defined */
+ char *firstset; /* First-set for all rules of this symbol */
+ Boolean lambda; /* True if NT and can generate an empty string */
+ char *destructor; /* Code which executes whenever this symbol is
+ ** popped from the stack during error processing */
+ int destructorln; /* Line number of destructor code */
+ char *datatype; /* The data type of information held by this
+ ** object. Only used if type==NONTERMINAL */
+ int dtnum; /* The data type number. In the parser, the value
+ ** stack is a union. The .yy%d element of this
+ ** union is the correct data type for this object */
+};
+
+/* Each production rule in the grammar is stored in the following
+** structure. */
+struct rule {
+ struct symbol *lhs; /* Left-hand side of the rule */
+ char *lhsalias; /* Alias for the LHS (NULL if none) */
+ int ruleline; /* Line number for the rule */
+ int nrhs; /* Number of RHS symbols */
+ struct symbol **rhs; /* The RHS symbols */
+ char **rhsalias; /* An alias for each RHS symbol (NULL if none) */
+ int line; /* Line number at which code begins */
+ char *code; /* The code executed when this rule is reduced */
+ struct symbol *precsym; /* Precedence symbol for this rule */
+ int index; /* An index number for this rule */
+ Boolean canReduce; /* True if this rule is ever reduced */
+ struct rule *nextlhs; /* Next rule with the same LHS */
+ struct rule *next; /* Next rule in the global list */
+};
+
+/* A configuration is a production rule of the grammar together with
+** a mark (dot) showing how much of that rule has been processed so far.
+** Configurations also contain a follow-set which is a list of terminal
+** symbols which are allowed to immediately follow the end of the rule.
+** Every configuration is recorded as an instance of the following: */
+struct config {
+ struct rule *rp; /* The rule upon which the configuration is based */
+ int dot; /* The parse point */
+ char *fws; /* Follow-set for this configuration only */
+ struct plink *fplp; /* Follow-set forward propagation links */
+ struct plink *bplp; /* Follow-set backwards propagation links */
+ struct state *stp; /* Pointer to state which contains this */
+ enum {
+ COMPLETE, /* The status is used during followset and */
+ INCOMPLETE /* shift computations */
+ } status;
+ struct config *next; /* Next configuration in the state */
+ struct config *bp; /* The next basis configuration */
+};
+
+/* Every shift or reduce operation is stored as one of the following */
+struct action {
+ struct symbol *sp; /* The look-ahead symbol */
+ enum e_action {
+ SHIFT,
+ ACCEPT,
+ REDUCE,
+ ERROR,
+ CONFLICT, /* Was a reduce, but part of a conflict */
+ SH_RESOLVED, /* Was a shift. Precedence resolved conflict */
+ RD_RESOLVED, /* Was reduce. Precedence resolved conflict */
+ NOT_USED /* Deleted by compression */
+ } type;
+ union {
+ struct state *stp; /* The new state, if a shift */
+ struct rule *rp; /* The rule, if a reduce */
+ } x;
+ struct action *next; /* Next action for this state */
+ struct action *collide; /* Next action with the same hash */
+};
+
+/* Each state of the generated parser's finite state machine
+** is encoded as an instance of the following structure. */
+struct state {
+ struct config *bp; /* The basis configurations for this state */
+ struct config *cfp; /* All configurations in this set */
+ int index; /* Sequencial number for this state */
+ struct action *ap; /* Array of actions for this state */
+ int naction; /* Number of actions for this state */
+ int tabstart; /* First index of the action table */
+ int tabdfltact; /* Default action */
+};
+
+/* A followset propagation link indicates that the contents of one
+** configuration followset should be propagated to another whenever
+** the first changes. */
+struct plink {
+ struct config *cfp; /* The configuration to which linked */
+ struct plink *next; /* The next propagate link */
+};
+
+/* The state vector for the entire parser generator is recorded as
+** follows. (LEMON uses no global variables and makes little use of
+** static variables. Fields in the following structure can be thought
+** of as begin global variables in the program.) */
+struct lemon {
+ struct state **sorted; /* Table of states sorted by state number */
+ struct rule *rule; /* List of all rules */
+ int nstate; /* Number of states */
+ int nrule; /* Number of rules */
+ int nsymbol; /* Number of terminal and nonterminal symbols */
+ int nterminal; /* Number of terminal symbols */
+ struct symbol **symbols; /* Sorted array of pointers to symbols */
+ int errorcnt; /* Number of errors */
+ struct symbol *errsym; /* The error symbol */
+ char *name; /* Name of the generated parser */
+ char *arg; /* Declaration of the 3th argument to parser */
+ char *tokentype; /* Type of terminal symbols in the parser stack */
+ char *start; /* Name of the start symbol for the grammar */
+ char *stacksize; /* Size of the parser stack */
+ char *include; /* Code to put at the start of the C file */
+ int includeln; /* Line number for start of include code */
+ char *error; /* Code to execute when an error is seen */
+ int errorln; /* Line number for start of error code */
+ char *overflow; /* Code to execute on a stack overflow */
+ int overflowln; /* Line number for start of overflow code */
+ char *failure; /* Code to execute on parser failure */
+ int failureln; /* Line number for start of failure code */
+ char *accept; /* Code to execute when the parser excepts */
+ int acceptln; /* Line number for the start of accept code */
+ char *extracode; /* Code appended to the generated file */
+ int extracodeln; /* Line number for the start of the extra code */
+ char *tokendest; /* Code to execute to destroy token data */
+ int tokendestln; /* Line number for token destroyer code */
+ char *filename; /* Name of the input file */
+ char *outname; /* Name of the current output file */
+ char *tokenprefix; /* A prefix added to token names in the .h file */
+ int nconflict; /* Number of parsing conflicts */
+ int tablesize; /* Size of the parse tables */
+ int basisflag; /* Print only basis configurations */
+ char *argv0; /* Name of the program */
+};
+
+#define MemoryCheck(X) if((X)==0){ \
+ extern void memory_error(); \
+ memory_error(); \
+}
+
+/**************** From the file "table.h" *********************************/
+/*
+** All code in this file has been automatically generated
+** from a specification in the file
+** "table.q"
+** by the associative array code building program "aagen".
+** Do not edit this file! Instead, edit the specification
+** file, then rerun aagen.
+*/
+/*
+** Code for processing tables in the LEMON parser generator.
+*/
+
+/* Routines for handling a strings */
+
+char *Strsafe();
+
+void Strsafe_init(/* void */);
+int Strsafe_insert(/* char * */);
+char *Strsafe_find(/* char * */);
+
+/* Routines for handling symbols of the grammar */
+
+struct symbol *Symbol_new();
+int Symbolcmpp(/* struct symbol **, struct symbol ** */);
+void Symbol_init(/* void */);
+int Symbol_insert(/* struct symbol *, char * */);
+struct symbol *Symbol_find(/* char * */);
+struct symbol *Symbol_Nth(/* int */);
+int Symbol_count(/* */);
+struct symbol **Symbol_arrayof(/* */);
+
+/* Routines to manage the state table */
+
+int Configcmp(/* struct config *, struct config * */);
+struct state *State_new();
+void State_init(/* void */);
+int State_insert(/* struct state *, struct config * */);
+struct state *State_find(/* struct config * */);
+struct state **State_arrayof(/* */);
+
+/* Routines used for efficiency in Configlist_add */
+
+void Configtable_init(/* void */);
+int Configtable_insert(/* struct config * */);
+struct config *Configtable_find(/* struct config * */);
+void Configtable_clear(/* int(*)(struct config *) */);
+/****************** From the file "action.c" *******************************/
+/*
+** Routines processing parser actions in the LEMON parser generator.
+*/
+
+/* Allocate a new parser action */
+struct action *Action_new(){
+ static struct action *freelist = 0;
+ struct action *new;
+
+ if( freelist==0 ){
+ int i;
+ int amt = 100;
+ freelist = (struct action *)malloc( sizeof(struct action)*amt );
+ if( freelist==0 ){
+ fprintf(stderr,"Unable to allocate memory for a new parser action.");
+ exit(1);
+ }
+ for(i=0; inext;
+ return new;
+}
+
+/* Compare two actions */
+static int actioncmp(ap1,ap2)
+struct action *ap1;
+struct action *ap2;
+{
+ int rc;
+ rc = ap1->sp->index - ap2->sp->index;
+ if( rc==0 ) rc = (int)ap1->type - (int)ap2->type;
+ if( rc==0 ){
+ assert( ap1->type==REDUCE && ap2->type==REDUCE );
+ rc = ap1->x.rp->index - ap2->x.rp->index;
+ }
+ return rc;
+}
+
+/* Sort parser actions */
+struct action *Action_sort(ap)
+struct action *ap;
+{
+ ap = (struct action *)msort(ap,&ap->next,actioncmp);
+ return ap;
+}
+
+void Action_add(app,type,sp,arg)
+struct action **app;
+enum e_action type;
+struct symbol *sp;
+char *arg;
+{
+ struct action *new;
+ new = Action_new();
+ new->next = *app;
+ *app = new;
+ new->type = type;
+ new->sp = sp;
+ if( type==SHIFT ){
+ new->x.stp = (struct state *)arg;
+ }else{
+ new->x.rp = (struct rule *)arg;
+ }
+}
+/********************** From the file "assert.c" ****************************/
+/*
+** A more efficient way of handling assertions.
+*/
+void myassert(file,line)
+char *file;
+int line;
+{
+ fprintf(stderr,"Assertion failed on line %d of file \"%s\"\n",line,file);
+ exit(1);
+}
+/********************** From the file "build.c" *****************************/
+/*
+** Routines to construction the finite state machine for the LEMON
+** parser generator.
+*/
+
+/* Find a precedence symbol of every rule in the grammar.
+**
+** Those rules which have a precedence symbol coded in the input
+** grammar using the "[symbol]" construct will already have the
+** rp->precsym field filled. Other rules take as their precedence
+** symbol the first RHS symbol with a defined precedence. If there
+** are not RHS symbols with a defined precedence, the precedence
+** symbol field is left blank.
+*/
+void FindRulePrecedences(xp)
+struct lemon *xp;
+{
+ struct rule *rp;
+ for(rp=xp->rule; rp; rp=rp->next){
+ if( rp->precsym==0 ){
+ int i;
+ for(i=0; inrhs; i++){
+ if( rp->rhs[i]->prec>=0 ){
+ rp->precsym = rp->rhs[i];
+ break;
+ }
+ }
+ }
+ }
+ return;
+}
+
+/* Find all nonterminals which will generate the empty string.
+** Then go back and compute the first sets of every nonterminal.
+** The first set is the set of all terminal symbols which can begin
+** a string generated by that nonterminal.
+*/
+void FindFirstSets(lemp)
+struct lemon *lemp;
+{
+ int i;
+ struct rule *rp;
+ int progress;
+
+ for(i=0; insymbol; i++){
+ lemp->symbols[i]->lambda = FALSE;
+ }
+ for(i=lemp->nterminal; insymbol; i++){
+ lemp->symbols[i]->firstset = SetNew();
+ }
+
+ /* First compute all lambdas */
+ do{
+ progress = 0;
+ for(rp=lemp->rule; rp; rp=rp->next){
+ if( rp->lhs->lambda ) continue;
+ for(i=0; inrhs; i++){
+ if( rp->rhs[i]->lambda==FALSE ) break;
+ }
+ if( i==rp->nrhs ){
+ rp->lhs->lambda = TRUE;
+ progress = 1;
+ }
+ }
+ }while( progress );
+
+ /* Now compute all first sets */
+ do{
+ struct symbol *s1, *s2;
+ progress = 0;
+ for(rp=lemp->rule; rp; rp=rp->next){
+ s1 = rp->lhs;
+ for(i=0; inrhs; i++){
+ s2 = rp->rhs[i];
+ if( s2->type==TERMINAL ){
+ progress += SetAdd(s1->firstset,s2->index);
+ break;
+ }else if( s1==s2 ){
+ if( s1->lambda==FALSE ) break;
+ }else{
+ progress += SetUnion(s1->firstset,s2->firstset);
+ if( s2->lambda==FALSE ) break;
+ }
+ }
+ }
+ }while( progress );
+ return;
+}
+
+/* Compute all LR(0) states for the grammar. Links
+** are added to between some states so that the LR(1) follow sets
+** can be computed later.
+*/
+PRIVATE struct state *getstate(/* struct lemon * */); /* forward reference */
+void FindStates(lemp)
+struct lemon *lemp;
+{
+ struct symbol *sp;
+ struct rule *rp;
+
+ Configlist_init();
+
+ /* Find the start symbol */
+ if( lemp->start ){
+ sp = Symbol_find(lemp->start);
+ if( sp==0 ){
+ ErrorMsg(lemp->filename,0,
+"The specified start symbol \"%s\" is not \
+in a nonterminal of the grammar. \"%s\" will be used as the start \
+symbol instead.",lemp->start,lemp->rule->lhs->name);
+ lemp->errorcnt++;
+ sp = lemp->rule->lhs;
+ }
+ }else{
+ sp = lemp->rule->lhs;
+ }
+
+ /* Make sure the start symbol doesn't occur on the right-hand side of
+ ** any rule. Report an error if it does. (YACC would generate a new
+ ** start symbol in this case.) */
+ for(rp=lemp->rule; rp; rp=rp->next){
+ int i;
+ for(i=0; inrhs; i++){
+ if( rp->rhs[i]==sp ){
+ ErrorMsg(lemp->filename,0,
+"The start symbol \"%s\" occurs on the \
+right-hand side of a rule. This will result in a parser which \
+does not work properly.",sp->name);
+ lemp->errorcnt++;
+ }
+ }
+ }
+
+ /* The basis configuration set for the first state
+ ** is all rules which have the start symbol as their
+ ** left-hand side */
+ for(rp=sp->rule; rp; rp=rp->nextlhs){
+ struct config *newcfp;
+ newcfp = Configlist_addbasis(rp,0);
+ SetAdd(newcfp->fws,0);
+ }
+
+ /* Compute the first state. All other states will be
+ ** computed automatically during the computation of the first one.
+ ** The returned pointer to the first state is not used. */
+ (void)getstate(lemp);
+ return;
+}
+
+/* Return a pointer to a state which is described by the configuration
+** list which has been built from calls to Configlist_add.
+*/
+PRIVATE void buildshifts(/* struct lemon *, struct state * */); /* Forwd ref */
+PRIVATE struct state *getstate(lemp)
+struct lemon *lemp;
+{
+ struct config *cfp, *bp;
+ struct state *stp;
+
+ /* Extract the sorted basis of the new state. The basis was constructed
+ ** by prior calls to "Configlist_addbasis()". */
+ Configlist_sortbasis();
+ bp = Configlist_basis();
+
+ /* Get a state with the same basis */
+ stp = State_find(bp);
+ if( stp ){
+ /* A state with the same basis already exists! Copy all the follow-set
+ ** propagation links from the state under construction into the
+ ** preexisting state, then return a pointer to the preexisting state */
+ struct config *x, *y;
+ for(x=bp, y=stp->bp; x && y; x=x->bp, y=y->bp){
+ Plink_copy(&y->bplp,x->bplp);
+ Plink_delete(x->fplp);
+ x->fplp = x->bplp = 0;
+ }
+ cfp = Configlist_return();
+ Configlist_eat(cfp);
+ }else{
+ /* This really is a new state. Construct all the details */
+ Configlist_closure(lemp); /* Compute the configuration closure */
+ Configlist_sort(); /* Sort the configuration closure */
+ cfp = Configlist_return(); /* Get a pointer to the config list */
+ stp = State_new(); /* A new state structure */
+ MemoryCheck(stp);
+ stp->bp = bp; /* Remember the configuration basis */
+ stp->cfp = cfp; /* Remember the configuration closure */
+ stp->index = lemp->nstate++; /* Every state gets a sequence number */
+ stp->ap = 0; /* No actions, yet. */
+ State_insert(stp,stp->bp); /* Add to the state table */
+ buildshifts(lemp,stp); /* Recursively compute successor states */
+ }
+ return stp;
+}
+
+/* Construct all successor states to the given state. A "successor"
+** state is any state which can be reached by a shift action.
+*/
+PRIVATE void buildshifts(lemp,stp)
+struct lemon *lemp;
+struct state *stp; /* The state from which successors are computed */
+{
+ struct config *cfp; /* For looping thru the config closure of "stp" */
+ struct config *bcfp; /* For the inner loop on config closure of "stp" */
+ struct config *new; /* */
+ struct symbol *sp; /* Symbol following the dot in configuration "cfp" */
+ struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */
+ struct state *newstp; /* A pointer to a successor state */
+
+ /* Each configuration becomes complete after it contibutes to a successor
+ ** state. Initially, all configurations are incomplete */
+ for(cfp=stp->cfp; cfp; cfp=cfp->next) cfp->status = INCOMPLETE;
+
+ /* Loop through all configurations of the state "stp" */
+ for(cfp=stp->cfp; cfp; cfp=cfp->next){
+ if( cfp->status==COMPLETE ) continue; /* Already used by inner loop */
+ if( cfp->dot>=cfp->rp->nrhs ) continue; /* Can't shift this config */
+ Configlist_reset(); /* Reset the new config set */
+ sp = cfp->rp->rhs[cfp->dot]; /* Symbol after the dot */
+
+ /* For every configuration in the state "stp" which has the symbol "sp"
+ ** following its dot, add the same configuration to the basis set under
+ ** construction but with the dot shifted one symbol to the right. */
+ for(bcfp=cfp; bcfp; bcfp=bcfp->next){
+ if( bcfp->status==COMPLETE ) continue; /* Already used */
+ if( bcfp->dot>=bcfp->rp->nrhs ) continue; /* Can't shift this one */
+ bsp = bcfp->rp->rhs[bcfp->dot]; /* Get symbol after dot */
+ if( bsp!=sp ) continue; /* Must be same as for "cfp" */
+ bcfp->status = COMPLETE; /* Mark this config as used */
+ new = Configlist_addbasis(bcfp->rp,bcfp->dot+1);
+ Plink_add(&new->bplp,bcfp);
+ }
+
+ /* Get a pointer to the state described by the basis configuration set
+ ** constructed in the preceding loop */
+ newstp = getstate(lemp);
+
+ /* The state "newstp" is reached from the state "stp" by a shift action
+ ** on the symbol "sp" */
+ Action_add(&stp->ap,SHIFT,sp,newstp);
+ }
+}
+
+/*
+** Construct the propagation links
+*/
+void FindLinks(lemp)
+struct lemon *lemp;
+{
+ int i;
+ struct config *cfp, *other;
+ struct state *stp;
+ struct plink *plp;
+
+ /* Housekeeping detail:
+ ** Add to every propagate link a pointer back to the state to
+ ** which the link is attached. */
+ for(i=0; instate; i++){
+ stp = lemp->sorted[i];
+ for(cfp=stp->cfp; cfp; cfp=cfp->next){
+ cfp->stp = stp;
+ }
+ }
+
+ /* Convert all backlinks into forward links. Only the forward
+ ** links are used in the follow-set computation. */
+ for(i=0; instate; i++){
+ stp = lemp->sorted[i];
+ for(cfp=stp->cfp; cfp; cfp=cfp->next){
+ for(plp=cfp->bplp; plp; plp=plp->next){
+ other = plp->cfp;
+ Plink_add(&other->fplp,cfp);
+ }
+ }
+ }
+}
+
+/* Compute all followsets.
+**
+** A followset is the set of all symbols which can come immediately
+** after a configuration.
+*/
+void FindFollowSets(lemp)
+struct lemon *lemp;
+{
+ int i;
+ struct config *cfp;
+ struct plink *plp;
+ int progress;
+ int change;
+
+ for(i=0; instate; i++){
+ for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){
+ cfp->status = INCOMPLETE;
+ }
+ }
+
+ do{
+ progress = 0;
+ for(i=0; instate; i++){
+ for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){
+ if( cfp->status==COMPLETE ) continue;
+ for(plp=cfp->fplp; plp; plp=plp->next){
+ change = SetUnion(plp->cfp->fws,cfp->fws);
+ if( change ){
+ plp->cfp->status = INCOMPLETE;
+ progress = 1;
+ }
+ }
+ cfp->status = COMPLETE;
+ }
+ }
+ }while( progress );
+}
+
+static int resolve_conflict();
+
+/* Compute the reduce actions, and resolve conflicts.
+*/
+void FindActions(lemp)
+struct lemon *lemp;
+{
+ int i,j;
+ struct config *cfp;
+ struct state *stp;
+ struct symbol *sp;
+ struct rule *rp;
+
+ /* Add all of the reduce actions
+ ** A reduce action is added for each element of the followset of
+ ** a configuration which has its dot at the extreme right.
+ */
+ for(i=0; instate; i++){ /* Loop over all states */
+ stp = lemp->sorted[i];
+ for(cfp=stp->cfp; cfp; cfp=cfp->next){ /* Loop over all configurations */
+ if( cfp->rp->nrhs==cfp->dot ){ /* Is dot at extreme right? */
+ for(j=0; jnterminal; j++){
+ if( SetFind(cfp->fws,j) ){
+ /* Add a reduce action to the state "stp" which will reduce by the
+ ** rule "cfp->rp" if the lookahead symbol is "lemp->symbols[j]" */
+ Action_add(&stp->ap,REDUCE,lemp->symbols[j],cfp->rp);
+ }
+ }
+ }
+ }
+ }
+
+ /* Add the accepting token */
+ if( lemp->start ){
+ sp = Symbol_find(lemp->start);
+ if( sp==0 ) sp = lemp->rule->lhs;
+ }else{
+ sp = lemp->rule->lhs;
+ }
+ /* Add to the first state (which is always the starting state of the
+ ** finite state machine) an action to ACCEPT if the lookahead is the
+ ** start nonterminal. */
+ Action_add(&lemp->sorted[0]->ap,ACCEPT,sp,0);
+
+ /* Resolve conflicts */
+ for(i=0; instate; i++){
+ struct action *ap, *nap;
+ struct state *stp;
+ stp = lemp->sorted[i];
+ assert( stp->ap );
+ stp->ap = Action_sort(stp->ap);
+ for(ap=stp->ap; ap && ap->next; ap=nap){
+ for(nap=ap->next; nap && nap->sp==ap->sp; nap=nap->next){
+ /* The two actions "ap" and "nap" have the same lookahead.
+ ** Figure out which one should be used */
+ lemp->nconflict += resolve_conflict(ap,nap,lemp->errsym);
+ }
+ }
+ }
+
+ /* Report an error for each rule that can never be reduced. */
+ for(rp=lemp->rule; rp; rp=rp->next) rp->canReduce = FALSE;
+ for(i=0; instate; i++){
+ struct action *ap;
+ for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){
+ if( ap->type==REDUCE ) ap->x.rp->canReduce = TRUE;
+ }
+ }
+ for(rp=lemp->rule; rp; rp=rp->next){
+ if( rp->canReduce ) continue;
+ ErrorMsg(lemp->filename,rp->ruleline,"This rule can not be reduced.\n");
+ lemp->errorcnt++;
+ }
+}
+
+/* Resolve a conflict between the two given actions. If the
+** conflict can't be resolve, return non-zero.
+**
+** NO LONGER TRUE:
+** To resolve a conflict, first look to see if either action
+** is on an error rule. In that case, take the action which
+** is not associated with the error rule. If neither or both
+** actions are associated with an error rule, then try to
+** use precedence to resolve the conflict.
+**
+** If either action is a SHIFT, then it must be apx. This
+** function won't work if apx->type==REDUCE and apy->type==SHIFT.
+*/
+static int resolve_conflict(apx,apy,errsym)
+struct action *apx;
+struct action *apy;
+struct symbol *errsym; /* The error symbol (if defined. NULL otherwise) */
+{
+ struct symbol *spx, *spy;
+ int errcnt = 0;
+ assert( apx->sp==apy->sp ); /* Otherwise there would be no conflict */
+ if( apx->type==SHIFT && apy->type==REDUCE ){
+ spx = apx->sp;
+ spy = apy->x.rp->precsym;
+ if( spy==0 || spx->prec<0 || spy->prec<0 ){
+ /* Not enough precedence information. */
+ apy->type = CONFLICT;
+ errcnt++;
+ }else if( spx->prec>spy->prec ){ /* Lower precedence wins */
+ apy->type = RD_RESOLVED;
+ }else if( spx->precprec ){
+ apx->type = SH_RESOLVED;
+ }else if( spx->prec==spy->prec && spx->assoc==RIGHT ){ /* Use operator */
+ apy->type = RD_RESOLVED; /* associativity */
+ }else if( spx->prec==spy->prec && spx->assoc==LEFT ){ /* to break tie */
+ apx->type = SH_RESOLVED;
+ }else{
+ assert( spx->prec==spy->prec && spx->assoc==NONE );
+ apy->type = CONFLICT;
+ errcnt++;
+ }
+ }else if( apx->type==REDUCE && apy->type==REDUCE ){
+ spx = apx->x.rp->precsym;
+ spy = apy->x.rp->precsym;
+ if( spx==0 || spy==0 || spx->prec<0 ||
+ spy->prec<0 || spx->prec==spy->prec ){
+ apy->type = CONFLICT;
+ errcnt++;
+ }else if( spx->prec>spy->prec ){
+ apy->type = RD_RESOLVED;
+ }else if( spx->precprec ){
+ apx->type = RD_RESOLVED;
+ }
+ }else{
+ /* Can't happen. Shifts have to come before Reduces on the
+ ** list because the reduces were added last. Hence, if apx->type==REDUCE
+ ** then it is impossible for apy->type==SHIFT */
+ }
+ return errcnt;
+}
+/********************* From the file "configlist.c" *************************/
+/*
+** Routines to processing a configuration list and building a state
+** in the LEMON parser generator.
+*/
+
+static struct config *freelist = 0; /* List of free configurations */
+static struct config *current = 0; /* Top of list of configurations */
+static struct config **currentend = 0; /* Last on list of configs */
+static struct config *basis = 0; /* Top of list of basis configs */
+static struct config **basisend = 0; /* End of list of basis configs */
+
+/* Return a pointer to a new configuration */
+PRIVATE struct config *newconfig(){
+ struct config *new;
+ if( freelist==0 ){
+ int i;
+ int amt = 3;
+ freelist = (struct config *)malloc( sizeof(struct config)*amt );
+ if( freelist==0 ){
+ fprintf(stderr,"Unable to allocate memory for a new configuration.");
+ exit(1);
+ }
+ for(i=0; inext;
+ return new;
+}
+
+/* The configuration "old" is no longer used */
+PRIVATE void deleteconfig(old)
+struct config *old;
+{
+ old->next = freelist;
+ freelist = old;
+}
+
+/* Initialized the configuration list builder */
+void Configlist_init(){
+ current = 0;
+ currentend = ¤t;
+ basis = 0;
+ basisend = &basis;
+ Configtable_init();
+ return;
+}
+
+/* Initialized the configuration list builder */
+void Configlist_reset(){
+ current = 0;
+ currentend = ¤t;
+ basis = 0;
+ basisend = &basis;
+ Configtable_clear(0);
+ return;
+}
+
+/* Add another configuration to the configuration list */
+struct config *Configlist_add(rp,dot)
+struct rule *rp; /* The rule */
+int dot; /* Index into the RHS of the rule where the dot goes */
+{
+ struct config *cfp, model;
+
+ assert( currentend!=0 );
+ model.rp = rp;
+ model.dot = dot;
+ cfp = Configtable_find(&model);
+ if( cfp==0 ){
+ cfp = newconfig();
+ cfp->rp = rp;
+ cfp->dot = dot;
+ cfp->fws = SetNew();
+ cfp->stp = 0;
+ cfp->fplp = cfp->bplp = 0;
+ cfp->next = 0;
+ cfp->bp = 0;
+ *currentend = cfp;
+ currentend = &cfp->next;
+ Configtable_insert(cfp);
+ }
+ return cfp;
+}
+
+/* Add a basis configuration to the configuration list */
+struct config *Configlist_addbasis(rp,dot)
+struct rule *rp;
+int dot;
+{
+ struct config *cfp, model;
+
+ assert( basisend!=0 );
+ assert( currentend!=0 );
+ model.rp = rp;
+ model.dot = dot;
+ cfp = Configtable_find(&model);
+ if( cfp==0 ){
+ cfp = newconfig();
+ cfp->rp = rp;
+ cfp->dot = dot;
+ cfp->fws = SetNew();
+ cfp->stp = 0;
+ cfp->fplp = cfp->bplp = 0;
+ cfp->next = 0;
+ cfp->bp = 0;
+ *currentend = cfp;
+ currentend = &cfp->next;
+ *basisend = cfp;
+ basisend = &cfp->bp;
+ Configtable_insert(cfp);
+ }
+ return cfp;
+}
+
+/* Compute the closure of the configuration list */
+void Configlist_closure(lemp)
+struct lemon *lemp;
+{
+ struct config *cfp, *newcfp;
+ struct rule *rp, *newrp;
+ struct symbol *sp, *xsp;
+ int i, dot;
+
+ assert( currentend!=0 );
+ for(cfp=current; cfp; cfp=cfp->next){
+ rp = cfp->rp;
+ dot = cfp->dot;
+ if( dot>=rp->nrhs ) continue;
+ sp = rp->rhs[dot];
+ if( sp->type==NONTERMINAL ){
+ if( sp->rule==0 && sp!=lemp->errsym ){
+ ErrorMsg(lemp->filename,rp->line,"Nonterminal \"%s\" has no rules.",
+ sp->name);
+ lemp->errorcnt++;
+ }
+ for(newrp=sp->rule; newrp; newrp=newrp->nextlhs){
+ newcfp = Configlist_add(newrp,0);
+ for(i=dot+1; inrhs; i++){
+ xsp = rp->rhs[i];
+ if( xsp->type==TERMINAL ){
+ SetAdd(newcfp->fws,xsp->index);
+ break;
+ }else{
+ SetUnion(newcfp->fws,xsp->firstset);
+ if( xsp->lambda==FALSE ) break;
+ }
+ }
+ if( i==rp->nrhs ) Plink_add(&cfp->fplp,newcfp);
+ }
+ }
+ }
+ return;
+}
+
+/* Sort the configuration list */
+void Configlist_sort(){
+ current = (struct config *)msort(current,&(current->next),Configcmp);
+ currentend = 0;
+ return;
+}
+
+/* Sort the basis configuration list */
+void Configlist_sortbasis(){
+ basis = (struct config *)msort(current,&(current->bp),Configcmp);
+ basisend = 0;
+ return;
+}
+
+/* Return a pointer to the head of the configuration list and
+** reset the list */
+struct config *Configlist_return(){
+ struct config *old;
+ old = current;
+ current = 0;
+ currentend = 0;
+ return old;
+}
+
+/* Return a pointer to the head of the configuration list and
+** reset the list */
+struct config *Configlist_basis(){
+ struct config *old;
+ old = basis;
+ basis = 0;
+ basisend = 0;
+ return old;
+}
+
+/* Free all elements of the given configuration list */
+void Configlist_eat(cfp)
+struct config *cfp;
+{
+ struct config *nextcfp;
+ for(; cfp; cfp=nextcfp){
+ nextcfp = cfp->next;
+ assert( cfp->fplp==0 );
+ assert( cfp->bplp==0 );
+ if( cfp->fws ) SetFree(cfp->fws);
+ deleteconfig(cfp);
+ }
+ return;
+}
+/***************** From the file "error.c" *********************************/
+/*
+** Code for printing error message.
+*/
+
+/* Find a good place to break "msg" so that its length is at least "min"
+** but no more than "max". Make the point as close to max as possible.
+*/
+static int findbreak(msg,min,max)
+char *msg;
+int min;
+int max;
+{
+ int i,spot;
+ char c;
+ for(i=spot=min; i<=max; i++){
+ c = msg[i];
+ if( c=='\t' ) msg[i] = ' ';
+ if( c=='\n' ){ msg[i] = ' '; spot = i; break; }
+ if( c==0 ){ spot = i; break; }
+ if( c=='-' && i0 ){
+ sprintf(prefix,"%.*s:%d: ",PREFIXLIMIT-10,filename,lineno);
+ }else{
+ sprintf(prefix,"%.*s: ",PREFIXLIMIT-10,filename);
+ }
+ prefixsize = strlen(prefix);
+ availablewidth = LINEWIDTH - prefixsize;
+
+ /* Generate the error message */
+ vsprintf(errmsg,format,ap);
+ va_end(ap);
+ errmsgsize = strlen(errmsg);
+ /* Remove trailing '\n's from the error message. */
+ while( errmsgsize>0 && errmsg[errmsgsize-1]=='\n' ){
+ errmsg[--errmsgsize] = 0;
+ }
+
+ /* Print the error message */
+ base = 0;
+ while( errmsg[base]!=0 ){
+ end = restart = findbreak(&errmsg[base],0,availablewidth);
+ restart += base;
+ while( errmsg[restart]==' ' ) restart++;
+ fprintf(stdout,"%s%.*s\n",prefix,end,&errmsg[base]);
+ base = restart;
+ }
+}
+/**************** From the file "main.c" ************************************/
+/*
+** Main program file for the LEMON parser generator.
+*/
+
+/* Report an out-of-memory condition and abort. This function
+** is used mostly by the "MemoryCheck" macro in struct.h
+*/
+void memory_error(){
+ fprintf(stderr,"Out of memory. Aborting...\n");
+ exit(1);
+}
+
+
+/* The main program. Parse the command line and do it... */
+int main(argc,argv)
+int argc;
+char **argv;
+{
+ static int version = 0;
+ static int rpflag = 0;
+ static int basisflag = 0;
+ static int compress = 0;
+ static int quiet = 0;
+ static int statistics = 0;
+ static int mhflag = 0;
+ static struct s_options options[] = {
+ {OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."},
+ {OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."},
+ {OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."},
+ {OPT_FLAG, "m", (char*)&mhflag, "Output a makeheaders compatible file"},
+ {OPT_FLAG, "q", (char*)&quiet, "(Quiet) Don't print the report file."},
+ {OPT_FLAG, "s", (char*)&statistics, "Print parser stats to standard output."},
+ {OPT_FLAG, "x", (char*)&version, "Print the version number."},
+ {OPT_FLAG,0,0,0}
+ };
+ int i;
+ struct lemon lem;
+
+ optinit(argv,options,stderr);
+ if( version ){
+ printf("Lemon version 1.0\n"
+ "Copyright 1991-1997 by D. Richard Hipp\n"
+ "Freely distributable under the GNU Public License.\n"
+ );
+ exit(0);
+ }
+ if( optnargs()!=1 ){
+ fprintf(stderr,"Exactly one filename argument is required.\n");
+ exit(1);
+ }
+ lem.errorcnt = 0;
+
+ /* Initialize the machine */
+ Strsafe_init();
+ Symbol_init();
+ State_init();
+ lem.argv0 = argv[0];
+ lem.filename = optarg(0);
+ lem.basisflag = basisflag;
+ lem.nconflict = 0;
+ lem.name = lem.include = lem.arg = lem.tokentype = lem.start = 0;
+ lem.stacksize = 0;
+ lem.error = lem.overflow = lem.failure = lem.accept = lem.tokendest =
+ lem.tokenprefix = lem.outname = lem.extracode = 0;
+ lem.tablesize = 0;
+ Symbol_new("$");
+ lem.errsym = Symbol_new("error");
+
+ /* Parse the input file */
+ Parse(&lem);
+ if( lem.errorcnt ) exit(lem.errorcnt);
+ if( lem.rule==0 ){
+ fprintf(stderr,"Empty grammar.\n");
+ exit(1);
+ }
+
+ /* Count and index the symbols of the grammar */
+ lem.nsymbol = Symbol_count();
+ Symbol_new("{default}");
+ lem.symbols = Symbol_arrayof();
+ qsort(lem.symbols,lem.nsymbol+1,sizeof(struct symbol*),
+ (int(*)())Symbolcmpp);
+ for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i;
+ for(i=1; isupper(lem.symbols[i]->name[0]); i++);
+ lem.nterminal = i;
+
+ /* Generate a reprint of the grammar, if requested on the command line */
+ if( rpflag ){
+ Reprint(&lem);
+ }else{
+ /* Initialize the size for all follow and first sets */
+ SetSize(lem.nterminal);
+
+ /* Find the precedence for every production rule (that has one) */
+ FindRulePrecedences(&lem);
+
+ /* Compute the lambda-nonterminals and the first-sets for every
+ ** nonterminal */
+ FindFirstSets(&lem);
+
+ /* Compute all LR(0) states. Also record follow-set propagation
+ ** links so that the follow-set can be computed later */
+ lem.nstate = 0;
+ FindStates(&lem);
+ lem.sorted = State_arrayof();
+
+ /* Tie up loose ends on the propagation links */
+ FindLinks(&lem);
+
+ /* Compute the follow set of every reducible configuration */
+ FindFollowSets(&lem);
+
+ /* Compute the action tables */
+ FindActions(&lem);
+
+ /* Compress the action tables */
+ if( compress==0 ) CompressTables(&lem);
+
+ /* Generate a report of the parser generated. (the "y.output" file) */
+ if( !quiet ) ReportOutput(&lem);
+
+ /* Generate the source code for the parser */
+ ReportTable(&lem, mhflag);
+
+ /* Produce a header file for use by the scanner. (This step is
+ ** omitted if the "-m" option is used because makeheaders will
+ ** generate the file for us.) */
+ if( !mhflag ) ReportHeader(&lem);
+ }
+ if( statistics ){
+ printf("Parser statistics: %d terminals, %d nonterminals, %d rules\n",
+ lem.nterminal, lem.nsymbol - lem.nterminal, lem.nrule);
+ printf(" %d states, %d parser table entries, %d conflicts\n",
+ lem.nstate, lem.tablesize, lem.nconflict);
+ }
+ if( lem.nconflict ){
+ fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict);
+ }
+ exit(lem.errorcnt + lem.nconflict);
+}
+/******************** From the file "msort.c" *******************************/
+/*
+** A generic merge-sort program.
+**
+** USAGE:
+** Let "ptr" be a pointer to some structure which is at the head of
+** a null-terminated list. Then to sort the list call:
+**
+** ptr = msort(ptr,&(ptr->next),cmpfnc);
+**
+** In the above, "cmpfnc" is a pointer to a function which compares
+** two instances of the structure and returns an integer, as in
+** strcmp. The second argument is a pointer to the pointer to the
+** second element of the linked list. This address is used to compute
+** the offset to the "next" field within the structure. The offset to
+** the "next" field must be constant for all structures in the list.
+**
+** The function returns a new pointer which is the head of the list
+** after sorting.
+**
+** ALGORITHM:
+** Merge-sort.
+*/
+
+/*
+** Return a pointer to the next structure in the linked list.
+*/
+#define NEXT(A) (*(char**)(((int)A)+offset))
+
+/*
+** Inputs:
+** a: A sorted, null-terminated linked list. (May be null).
+** b: A sorted, null-terminated linked list. (May be null).
+** cmp: A pointer to the comparison function.
+** offset: Offset in the structure to the "next" field.
+**
+** Return Value:
+** A pointer to the head of a sorted list containing the elements
+** of both a and b.
+**
+** Side effects:
+** The "next" pointers for elements in the lists a and b are
+** changed.
+*/
+static char *merge(a,b,cmp,offset)
+char *a;
+char *b;
+int (*cmp)();
+int offset;
+{
+ char *ptr, *head;
+
+ if( a==0 ){
+ head = b;
+ }else if( b==0 ){
+ head = a;
+ }else{
+ if( (*cmp)(a,b)<0 ){
+ ptr = a;
+ a = NEXT(a);
+ }else{
+ ptr = b;
+ b = NEXT(b);
+ }
+ head = ptr;
+ while( a && b ){
+ if( (*cmp)(a,b)<0 ){
+ NEXT(ptr) = a;
+ ptr = a;
+ a = NEXT(a);
+ }else{
+ NEXT(ptr) = b;
+ ptr = b;
+ b = NEXT(b);
+ }
+ }
+ if( a ) NEXT(ptr) = a;
+ else NEXT(ptr) = b;
+ }
+ return head;
+}
+
+/*
+** Inputs:
+** list: Pointer to a singly-linked list of structures.
+** next: Pointer to pointer to the second element of the list.
+** cmp: A comparison function.
+**
+** Return Value:
+** A pointer to the head of a sorted list containing the elements
+** orginally in list.
+**
+** Side effects:
+** The "next" pointers for elements in list are changed.
+*/
+#define LISTSIZE 30
+char *msort(list,next,cmp)
+char *list;
+char **next;
+int (*cmp)();
+{
+ int offset;
+ char *ep;
+ char *set[LISTSIZE];
+ int i;
+ offset = (int)next - (int)list;
+ for(i=0; i0 ){
+ fprintf(err,"Valid command line options for \"%s\" are:\n",*a);
+ optprint();
+ exit(1);
+ }
+ return 0;
+}
+
+int optnargs(){
+ int cnt = 0;
+ int dashdash = 0;
+ int i;
+ if( argv!=0 && argv[0]!=0 ){
+ for(i=1; argv[i]; i++){
+ if( dashdash || !ISOPT(argv[i]) ) cnt++;
+ if( strcmp(argv[i],"--")==0 ) dashdash = 1;
+ }
+ }
+ return cnt;
+}
+
+char *optarg(n)
+int n;
+{
+ int i;
+ i = argindex(n);
+ return i>=0 ? argv[i] : 0;
+}
+
+void opterr(n)
+int n;
+{
+ int i;
+ i = argindex(n);
+ if( i>=0 ) errline(i,0,errstream);
+}
+
+void optprint(){
+ int i;
+ int max, len;
+ max = 0;
+ for(i=0; op[i].label; i++){
+ len = strlen(op[i].label) + 1;
+ switch( op[i].type ){
+ case OPT_FLAG:
+ case OPT_FFLAG:
+ break;
+ case OPT_INT:
+ case OPT_FINT:
+ len += 9; /* length of "" */
+ break;
+ case OPT_DBL:
+ case OPT_FDBL:
+ len += 6; /* length of "" */
+ break;
+ case OPT_STR:
+ case OPT_FSTR:
+ len += 8; /* length of "" */
+ break;
+ }
+ if( len>max ) max = len;
+ }
+ for(i=0; op[i].label; i++){
+ switch( op[i].type ){
+ case OPT_FLAG:
+ case OPT_FFLAG:
+ fprintf(errstream," -%-*s %s\n",max,op[i].label,op[i].message);
+ break;
+ case OPT_INT:
+ case OPT_FINT:
+ fprintf(errstream," %s=%*s %s\n",op[i].label,
+ max-strlen(op[i].label)-9,"",op[i].message);
+ break;
+ case OPT_DBL:
+ case OPT_FDBL:
+ fprintf(errstream," %s=%*s %s\n",op[i].label,
+ max-strlen(op[i].label)-6,"",op[i].message);
+ break;
+ case OPT_STR:
+ case OPT_FSTR:
+ fprintf(errstream," %s=%*s %s\n",op[i].label,
+ max-strlen(op[i].label)-8,"",op[i].message);
+ break;
+ }
+ }
+}
+/*********************** From the file "parse.c" ****************************/
+/*
+** Input file parser for the LEMON parser generator.
+*/
+
+/* The state of the parser */
+struct pstate {
+ char *filename; /* Name of the input file */
+ int tokenlineno; /* Linenumber at which current token starts */
+ int errorcnt; /* Number of errors so far */
+ char *tokenstart; /* Text of current token */
+ struct lemon *gp; /* Global state vector */
+ enum e_state {
+ INITIALIZE,
+ WAITING_FOR_DECL_OR_RULE,
+ WAITING_FOR_DECL_KEYWORD,
+ WAITING_FOR_DECL_ARG,
+ WAITING_FOR_PRECEDENCE_SYMBOL,
+ WAITING_FOR_ARROW,
+ IN_RHS,
+ LHS_ALIAS_1,
+ LHS_ALIAS_2,
+ LHS_ALIAS_3,
+ RHS_ALIAS_1,
+ RHS_ALIAS_2,
+ PRECEDENCE_MARK_1,
+ PRECEDENCE_MARK_2,
+ RESYNC_AFTER_RULE_ERROR,
+ RESYNC_AFTER_DECL_ERROR,
+ WAITING_FOR_DESTRUCTOR_SYMBOL,
+ WAITING_FOR_DATATYPE_SYMBOL
+ } state; /* The state of the parser */
+ struct symbol *lhs; /* Left-hand side of current rule */
+ char *lhsalias; /* Alias for the LHS */
+ int nrhs; /* Number of right-hand side symbols seen */
+ struct symbol *rhs[MAXRHS]; /* RHS symbols */
+ char *alias[MAXRHS]; /* Aliases for each RHS symbol (or NULL) */
+ struct rule *prevrule; /* Previous rule parsed */
+ char *declkeyword; /* Keyword of a declaration */
+ char **declargslot; /* Where the declaration argument should be put */
+ int *decllnslot; /* Where the declaration linenumber is put */
+ enum e_assoc declassoc; /* Assign this association to decl arguments */
+ int preccounter; /* Assign this precedence to decl arguments */
+ struct rule *firstrule; /* Pointer to first rule in the grammar */
+ struct rule *lastrule; /* Pointer to the most recently parsed rule */
+};
+
+/* Parse a single token */
+static void parseonetoken(psp)
+struct pstate *psp;
+{
+ char *x;
+ x = Strsafe(psp->tokenstart); /* Save the token permanently */
+#if 0
+ printf("%s:%d: Token=[%s] state=%d\n",psp->filename,psp->tokenlineno,
+ x,psp->state);
+#endif
+ switch( psp->state ){
+ case INITIALIZE:
+ psp->prevrule = 0;
+ psp->preccounter = 0;
+ psp->firstrule = psp->lastrule = 0;
+ psp->gp->nrule = 0;
+ /* Fall thru to next case */
+ case WAITING_FOR_DECL_OR_RULE:
+ if( x[0]=='%' ){
+ psp->state = WAITING_FOR_DECL_KEYWORD;
+ }else if( islower(x[0]) ){
+ psp->lhs = Symbol_new(x);
+ psp->nrhs = 0;
+ psp->lhsalias = 0;
+ psp->state = WAITING_FOR_ARROW;
+ }else if( x[0]=='{' ){
+ if( psp->prevrule==0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+"There is not prior rule opon which to attach the code \
+fragment which begins on this line.");
+ psp->errorcnt++;
+ }else if( psp->prevrule->code!=0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+"Code fragment beginning on this line is not the first \
+to follow the previous rule.");
+ psp->errorcnt++;
+ }else{
+ psp->prevrule->line = psp->tokenlineno;
+ psp->prevrule->code = &x[1];
+ }
+ }else if( x[0]=='[' ){
+ psp->state = PRECEDENCE_MARK_1;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Token \"%s\" should be either \"%%\" or a nonterminal name.",
+ x);
+ psp->errorcnt++;
+ }
+ break;
+ case PRECEDENCE_MARK_1:
+ if( !isupper(x[0]) ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "The precedence symbol must be a terminal.");
+ psp->errorcnt++;
+ }else if( psp->prevrule==0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "There is no prior rule to assign precedence \"[%s]\".",x);
+ psp->errorcnt++;
+ }else if( psp->prevrule->precsym!=0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+"Precedence mark on this line is not the first \
+to follow the previous rule.");
+ psp->errorcnt++;
+ }else{
+ psp->prevrule->precsym = Symbol_new(x);
+ }
+ psp->state = PRECEDENCE_MARK_2;
+ break;
+ case PRECEDENCE_MARK_2:
+ if( x[0]!=']' ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Missing \"]\" on precedence mark.");
+ psp->errorcnt++;
+ }
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ break;
+ case WAITING_FOR_ARROW:
+ if( x[0]==':' && x[1]==':' && x[2]=='=' ){
+ psp->state = IN_RHS;
+ }else if( x[0]=='(' ){
+ psp->state = LHS_ALIAS_1;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Expected to see a \":\" following the LHS symbol \"%s\".",
+ psp->lhs->name);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case LHS_ALIAS_1:
+ if( isalpha(x[0]) ){
+ psp->lhsalias = x;
+ psp->state = LHS_ALIAS_2;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "\"%s\" is not a valid alias for the LHS \"%s\"\n",
+ x,psp->lhs->name);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case LHS_ALIAS_2:
+ if( x[0]==')' ){
+ psp->state = LHS_ALIAS_3;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case LHS_ALIAS_3:
+ if( x[0]==':' && x[1]==':' && x[2]=='=' ){
+ psp->state = IN_RHS;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Missing \"->\" following: \"%s(%s)\".",
+ psp->lhs->name,psp->lhsalias);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case IN_RHS:
+ if( x[0]=='.' ){
+ struct rule *rp;
+ rp = (struct rule *)malloc( sizeof(struct rule) +
+ sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs );
+ if( rp==0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Can't allocate enough memory for this rule.");
+ psp->errorcnt++;
+ psp->prevrule = 0;
+ }else{
+ int i;
+ rp->ruleline = psp->tokenlineno;
+ rp->rhs = (struct symbol**)&rp[1];
+ rp->rhsalias = (char**)&(rp->rhs[psp->nrhs]);
+ for(i=0; inrhs; i++){
+ rp->rhs[i] = psp->rhs[i];
+ rp->rhsalias[i] = psp->alias[i];
+ }
+ rp->lhs = psp->lhs;
+ rp->lhsalias = psp->lhsalias;
+ rp->nrhs = psp->nrhs;
+ rp->code = 0;
+ rp->precsym = 0;
+ rp->index = psp->gp->nrule++;
+ rp->nextlhs = rp->lhs->rule;
+ rp->lhs->rule = rp;
+ rp->next = 0;
+ if( psp->firstrule==0 ){
+ psp->firstrule = psp->lastrule = rp;
+ }else{
+ psp->lastrule->next = rp;
+ psp->lastrule = rp;
+ }
+ psp->prevrule = rp;
+ }
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }else if( isalpha(x[0]) ){
+ if( psp->nrhs>=MAXRHS ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Too many symbol on RHS or rule beginning at \"%s\".",
+ x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }else{
+ psp->rhs[psp->nrhs] = Symbol_new(x);
+ psp->alias[psp->nrhs] = 0;
+ psp->nrhs++;
+ }
+ }else if( x[0]=='(' && psp->nrhs>0 ){
+ psp->state = RHS_ALIAS_1;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Illegal character on RHS of rule: \"%s\".",x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case RHS_ALIAS_1:
+ if( isalpha(x[0]) ){
+ psp->alias[psp->nrhs-1] = x;
+ psp->state = RHS_ALIAS_2;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "\"%s\" is not a valid alias for the RHS symbol \"%s\"\n",
+ x,psp->rhs[psp->nrhs-1]->name);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case RHS_ALIAS_2:
+ if( x[0]==')' ){
+ psp->state = IN_RHS;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_RULE_ERROR;
+ }
+ break;
+ case WAITING_FOR_DECL_KEYWORD:
+ if( isalpha(x[0]) ){
+ psp->declkeyword = x;
+ psp->declargslot = 0;
+ psp->decllnslot = 0;
+ psp->state = WAITING_FOR_DECL_ARG;
+ if( strcmp(x,"name")==0 ){
+ psp->declargslot = &(psp->gp->name);
+ }else if( strcmp(x,"include")==0 ){
+ psp->declargslot = &(psp->gp->include);
+ psp->decllnslot = &psp->gp->includeln;
+ }else if( strcmp(x,"code")==0 ){
+ psp->declargslot = &(psp->gp->extracode);
+ psp->decllnslot = &psp->gp->extracodeln;
+ }else if( strcmp(x,"token_destructor")==0 ){
+ psp->declargslot = &psp->gp->tokendest;
+ psp->decllnslot = &psp->gp->tokendestln;
+ }else if( strcmp(x,"token_prefix")==0 ){
+ psp->declargslot = &psp->gp->tokenprefix;
+ }else if( strcmp(x,"syntax_error")==0 ){
+ psp->declargslot = &(psp->gp->error);
+ psp->decllnslot = &psp->gp->errorln;
+ }else if( strcmp(x,"parse_accept")==0 ){
+ psp->declargslot = &(psp->gp->accept);
+ psp->decllnslot = &psp->gp->acceptln;
+ }else if( strcmp(x,"parse_failure")==0 ){
+ psp->declargslot = &(psp->gp->failure);
+ psp->decllnslot = &psp->gp->failureln;
+ }else if( strcmp(x,"stack_overflow")==0 ){
+ psp->declargslot = &(psp->gp->overflow);
+ psp->decllnslot = &psp->gp->overflowln;
+ }else if( strcmp(x,"extra_argument")==0 ){
+ psp->declargslot = &(psp->gp->arg);
+ }else if( strcmp(x,"token_type")==0 ){
+ psp->declargslot = &(psp->gp->tokentype);
+ }else if( strcmp(x,"stack_size")==0 ){
+ psp->declargslot = &(psp->gp->stacksize);
+ }else if( strcmp(x,"start_symbol")==0 ){
+ psp->declargslot = &(psp->gp->start);
+ }else if( strcmp(x,"left")==0 ){
+ psp->preccounter++;
+ psp->declassoc = LEFT;
+ psp->state = WAITING_FOR_PRECEDENCE_SYMBOL;
+ }else if( strcmp(x,"right")==0 ){
+ psp->preccounter++;
+ psp->declassoc = RIGHT;
+ psp->state = WAITING_FOR_PRECEDENCE_SYMBOL;
+ }else if( strcmp(x,"nonassoc")==0 ){
+ psp->preccounter++;
+ psp->declassoc = NONE;
+ psp->state = WAITING_FOR_PRECEDENCE_SYMBOL;
+ }else if( strcmp(x,"destructor")==0 ){
+ psp->state = WAITING_FOR_DESTRUCTOR_SYMBOL;
+ }else if( strcmp(x,"type")==0 ){
+ psp->state = WAITING_FOR_DATATYPE_SYMBOL;
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Unknown declaration keyword: \"%%%s\".",x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Illegal declaration keyword: \"%s\".",x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }
+ break;
+ case WAITING_FOR_DESTRUCTOR_SYMBOL:
+ if( !isalpha(x[0]) ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Symbol name missing after %destructor keyword");
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }else{
+ struct symbol *sp = Symbol_new(x);
+ psp->declargslot = &sp->destructor;
+ psp->decllnslot = &sp->destructorln;
+ psp->state = WAITING_FOR_DECL_ARG;
+ }
+ break;
+ case WAITING_FOR_DATATYPE_SYMBOL:
+ if( !isalpha(x[0]) ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Symbol name missing after %destructor keyword");
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }else{
+ struct symbol *sp = Symbol_new(x);
+ psp->declargslot = &sp->datatype;
+ psp->decllnslot = 0;
+ psp->state = WAITING_FOR_DECL_ARG;
+ }
+ break;
+ case WAITING_FOR_PRECEDENCE_SYMBOL:
+ if( x[0]=='.' ){
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }else if( isupper(x[0]) ){
+ struct symbol *sp;
+ sp = Symbol_new(x);
+ if( sp->prec>=0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Symbol \"%s\" has already be given a precedence.",x);
+ psp->errorcnt++;
+ }else{
+ sp->prec = psp->preccounter;
+ sp->assoc = psp->declassoc;
+ }
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Can't assign a precedence to \"%s\".",x);
+ psp->errorcnt++;
+ }
+ break;
+ case WAITING_FOR_DECL_ARG:
+ if( (x[0]=='{' || x[0]=='\"' || isalnum(x[0])) ){
+ if( *(psp->declargslot)!=0 ){
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "The argument \"%s\" to declaration \"%%%s\" is not the first.",
+ x[0]=='\"' ? &x[1] : x,psp->declkeyword);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }else{
+ *(psp->declargslot) = (x[0]=='\"' || x[0]=='{') ? &x[1] : x;
+ if( psp->decllnslot ) *psp->decllnslot = psp->tokenlineno;
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }
+ }else{
+ ErrorMsg(psp->filename,psp->tokenlineno,
+ "Illegal argument to %%%s: %s",psp->declkeyword,x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }
+ break;
+ case RESYNC_AFTER_RULE_ERROR:
+/* if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE;
+** break; */
+ case RESYNC_AFTER_DECL_ERROR:
+ if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE;
+ if( x[0]=='%' ) psp->state = WAITING_FOR_DECL_KEYWORD;
+ break;
+ }
+}
+
+/* In spite of its name, this function is really a scanner. It read
+** in the entire input file (all at once) then tokenizes it. Each
+** token is passed to the function "parseonetoken" which builds all
+** the appropriate data structures in the global state vector "gp".
+*/
+void Parse(gp)
+struct lemon *gp;
+{
+ struct pstate ps;
+ FILE *fp;
+ char *filebuf;
+ int filesize;
+ int lineno;
+ int c;
+ char *cp, *nextcp;
+ int startline = 0;
+
+ ps.gp = gp;
+ ps.filename = gp->filename;
+ ps.errorcnt = 0;
+ ps.state = INITIALIZE;
+
+ /* Begin by reading the input file */
+ fp = fopen(ps.filename,"rb");
+ if( fp==0 ){
+ ErrorMsg(ps.filename,0,"Can't open this file for reading.");
+ gp->errorcnt++;
+ return;
+ }
+ fseek(fp,0,2);
+ filesize = ftell(fp);
+ rewind(fp);
+ filebuf = (char *)malloc( filesize+1 );
+ if( filebuf==0 ){
+ ErrorMsg(ps.filename,0,"Can't allocate %d of memory to hold this file.",
+ filesize+1);
+ gp->errorcnt++;
+ return;
+ }
+ if( fread(filebuf,1,filesize,fp)!=filesize ){
+ ErrorMsg(ps.filename,0,"Can't read in all %d bytes of this file.",
+ filesize);
+ free(filebuf);
+ gp->errorcnt++;
+ return;
+ }
+ fclose(fp);
+ filebuf[filesize] = 0;
+
+ /* Now scan the text of the input file */
+ lineno = 1;
+ for(cp=filebuf; (c= *cp)!=0; ){
+ if( c=='\n' ) lineno++; /* Keep track of the line number */
+ if( isspace(c) ){ cp++; continue; } /* Skip all white space */
+ if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments */
+ cp+=2;
+ while( (c= *cp)!=0 && c!='\n' ) cp++;
+ continue;
+ }
+ if( c=='/' && cp[1]=='*' ){ /* Skip C style comments */
+ cp+=2;
+ while( (c= *cp)!=0 && (c!='/' || cp[-1]!='*') ){
+ if( c=='\n' ) lineno++;
+ cp++;
+ }
+ if( c ) cp++;
+ continue;
+ }
+ ps.tokenstart = cp; /* Mark the beginning of the token */
+ ps.tokenlineno = lineno; /* Linenumber on which token begins */
+ if( c=='\"' ){ /* String literals */
+ cp++;
+ while( (c= *cp)!=0 && c!='\"' ){
+ if( c=='\n' ) lineno++;
+ cp++;
+ }
+ if( c==0 ){
+ ErrorMsg(ps.filename,startline,
+"String starting on this line is not terminated before the end of the file.");
+ ps.errorcnt++;
+ nextcp = cp;
+ }else{
+ nextcp = cp+1;
+ }
+ }else if( c=='{' ){ /* A block of C code */
+ int level;
+ cp++;
+ for(level=1; (c= *cp)!=0 && (level>1 || c!='}'); cp++){
+ if( c=='\n' ) lineno++;
+ else if( c=='{' ) level++;
+ else if( c=='}' ) level--;
+ else if( c=='/' && cp[1]=='*' ){ /* Skip comments */
+ int prevc;
+ cp = &cp[2];
+ prevc = 0;
+ while( (c= *cp)!=0 && (c!='/' || prevc!='*') ){
+ if( c=='\n' ) lineno++;
+ prevc = c;
+ cp++;
+ }
+ }else if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments too */
+ cp = &cp[2];
+ while( (c= *cp)!=0 && c!='\n' ) cp++;
+ if( c ) lineno++;
+ }else if( c=='\'' || c=='\"' ){ /* String a character literals */
+ int startchar, prevc;
+ startchar = c;
+ prevc = 0;
+ for(cp++; (c= *cp)!=0 && (c!=startchar || prevc=='\\'); cp++){
+ if( c=='\n' ) lineno++;
+ if( prevc=='\\' ) prevc = 0;
+ else prevc = c;
+ }
+ }
+ }
+ if( c==0 ){
+ ErrorMsg(ps.filename,startline,
+"C code starting on this line is not terminated before the end of the file.");
+ ps.errorcnt++;
+ nextcp = cp;
+ }else{
+ nextcp = cp+1;
+ }
+ }else if( isalnum(c) ){ /* Identifiers */
+ while( (c= *cp)!=0 && (isalnum(c) || c=='_') ) cp++;
+ nextcp = cp;
+ }else if( c==':' && cp[1]==':' && cp[2]=='=' ){ /* The operator "::=" */
+ cp += 3;
+ nextcp = cp;
+ }else{ /* All other (one character) operators */
+ cp++;
+ nextcp = cp;
+ }
+ c = *cp;
+ *cp = 0; /* Null terminate the token */
+ parseonetoken(&ps); /* Parse the token */
+ *cp = c; /* Restore the buffer */
+ cp = nextcp;
+ }
+ free(filebuf); /* Release the buffer after parsing */
+ gp->rule = ps.firstrule;
+ gp->errorcnt = ps.errorcnt;
+}
+/*************************** From the file "plink.c" *********************/
+/*
+** Routines processing configuration follow-set propagation links
+** in the LEMON parser generator.
+*/
+static struct plink *plink_freelist = 0;
+
+/* Allocate a new plink */
+struct plink *Plink_new(){
+ struct plink *new;
+
+ if( plink_freelist==0 ){
+ int i;
+ int amt = 100;
+ plink_freelist = (struct plink *)malloc( sizeof(struct plink)*amt );
+ if( plink_freelist==0 ){
+ fprintf(stderr,
+ "Unable to allocate memory for a new follow-set propagation link.\n");
+ exit(1);
+ }
+ for(i=0; inext;
+ return new;
+}
+
+/* Add a plink to a plink list */
+void Plink_add(plpp,cfp)
+struct plink **plpp;
+struct config *cfp;
+{
+ struct plink *new;
+ new = Plink_new();
+ new->next = *plpp;
+ *plpp = new;
+ new->cfp = cfp;
+}
+
+/* Transfer every plink on the list "from" to the list "to" */
+void Plink_copy(to,from)
+struct plink **to;
+struct plink *from;
+{
+ struct plink *nextpl;
+ while( from ){
+ nextpl = from->next;
+ from->next = *to;
+ *to = from;
+ from = nextpl;
+ }
+}
+
+/* Delete every plink on the list */
+void Plink_delete(plp)
+struct plink *plp;
+{
+ struct plink *nextpl;
+
+ while( plp ){
+ nextpl = plp->next;
+ plp->next = plink_freelist;
+ plink_freelist = plp;
+ plp = nextpl;
+ }
+}
+/*********************** From the file "report.c" **************************/
+/*
+** Procedures for generating reports and tables in the LEMON parser generator.
+*/
+
+/* Generate a filename with the given suffix. Space to hold the
+** name comes from malloc() and must be freed by the calling
+** function.
+*/
+PRIVATE char *file_makename(lemp,suffix)
+struct lemon *lemp;
+char *suffix;
+{
+ char *name;
+ char *cp;
+
+ name = malloc( strlen(lemp->filename) + strlen(suffix) + 5 );
+ if( name==0 ){
+ fprintf(stderr,"Can't allocate space for a filename.\n");
+ exit(1);
+ }
+ strcpy(name,lemp->filename);
+ cp = strrchr(name,'.');
+ if( cp ) *cp = 0;
+ strcat(name,suffix);
+ return name;
+}
+
+/* Open a file with a name based on the name of the input file,
+** but with a different (specified) suffix, and return a pointer
+** to the stream */
+PRIVATE FILE *file_open(lemp,suffix,mode)
+struct lemon *lemp;
+char *suffix;
+char *mode;
+{
+ FILE *fp;
+
+ if( lemp->outname ) free(lemp->outname);
+ lemp->outname = file_makename(lemp, suffix);
+ fp = fopen(lemp->outname,mode);
+ if( fp==0 && *mode=='w' ){
+ fprintf(stderr,"Can't open file \"%s\".\n",lemp->outname);
+ lemp->errorcnt++;
+ return 0;
+ }
+ return fp;
+}
+
+/* Duplicate the input file without comments and without actions
+** on rules */
+void Reprint(lemp)
+struct lemon *lemp;
+{
+ struct rule *rp;
+ struct symbol *sp;
+ int i, j, maxlen, len, ncolumns, skip;
+ printf("// Reprint of input file \"%s\".\n// Symbols:\n",lemp->filename);
+ maxlen = 10;
+ for(i=0; insymbol; i++){
+ sp = lemp->symbols[i];
+ len = strlen(sp->name);
+ if( len>maxlen ) maxlen = len;
+ }
+ ncolumns = 76/(maxlen+5);
+ if( ncolumns<1 ) ncolumns = 1;
+ skip = (lemp->nsymbol + ncolumns - 1)/ncolumns;
+ for(i=0; insymbol; j+=skip){
+ sp = lemp->symbols[j];
+ assert( sp->index==j );
+ printf(" %3d %-*.*s",j,maxlen,maxlen,sp->name);
+ }
+ printf("\n");
+ }
+ for(rp=lemp->rule; rp; rp=rp->next){
+ printf("%s",rp->lhs->name);
+/* if( rp->lhsalias ) printf("(%s)",rp->lhsalias); */
+ printf(" ::=");
+ for(i=0; inrhs; i++){
+ printf(" %s",rp->rhs[i]->name);
+/* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */
+ }
+ printf(".");
+ if( rp->precsym ) printf(" [%s]",rp->precsym->name);
+/* if( rp->code ) printf("\n %s",rp->code); */
+ printf("\n");
+ }
+}
+
+void ConfigPrint(fp,cfp)
+FILE *fp;
+struct config *cfp;
+{
+ struct rule *rp;
+ int i;
+ rp = cfp->rp;
+ fprintf(fp,"%s ::=",rp->lhs->name);
+ for(i=0; i<=rp->nrhs; i++){
+ if( i==cfp->dot ) fprintf(fp," *");
+ if( i==rp->nrhs ) break;
+ fprintf(fp," %s",rp->rhs[i]->name);
+ }
+}
+
+/* #define TEST */
+#ifdef TEST
+/* Print a set */
+PRIVATE void SetPrint(out,set,lemp)
+FILE *out;
+char *set;
+struct lemon *lemp;
+{
+ int i;
+ char *spacer;
+ spacer = "";
+ fprintf(out,"%12s[","");
+ for(i=0; interminal; i++){
+ if( SetFind(set,i) ){
+ fprintf(out,"%s%s",spacer,lemp->symbols[i]->name);
+ spacer = " ";
+ }
+ }
+ fprintf(out,"]\n");
+}
+
+/* Print a plink chain */
+PRIVATE void PlinkPrint(out,plp,tag)
+FILE *out;
+struct plink *plp;
+char *tag;
+{
+ while( plp ){
+ fprintf(out,"%12s%s (state %2d) ","",tag,plp->cfp->stp->index);
+ ConfigPrint(out,plp->cfp);
+ fprintf(out,"\n");
+ plp = plp->next;
+ }
+}
+#endif
+
+/* Print an action to the given file descriptor. Return FALSE if
+** nothing was actually printed.
+*/
+int PrintAction(struct action *ap, FILE *fp, int indent){
+ int result = 1;
+ switch( ap->type ){
+ case SHIFT:
+ fprintf(fp,"%*s shift %d",indent,ap->sp->name,ap->x.stp->index);
+ break;
+ case REDUCE:
+ fprintf(fp,"%*s reduce %d",indent,ap->sp->name,ap->x.rp->index);
+ break;
+ case ACCEPT:
+ fprintf(fp,"%*s accept",indent,ap->sp->name);
+ break;
+ case ERROR:
+ fprintf(fp,"%*s error",indent,ap->sp->name);
+ break;
+ case CONFLICT:
+ fprintf(fp,"%*s reduce %-3d ** Parsing conflict **",
+ indent,ap->sp->name,ap->x.rp->index);
+ break;
+ case SH_RESOLVED:
+ case RD_RESOLVED:
+ case NOT_USED:
+ result = 0;
+ break;
+ }
+ return result;
+}
+
+/* Generate the "y.output" log file */
+void ReportOutput(lemp)
+struct lemon *lemp;
+{
+ int i;
+ struct state *stp;
+ struct config *cfp;
+ struct action *ap;
+ FILE *fp;
+
+ fp = file_open(lemp,".out","w");
+ if( fp==0 ) return;
+ fprintf(fp," \b");
+ for(i=0; instate; i++){
+ stp = lemp->sorted[i];
+ fprintf(fp,"State %d:\n",stp->index);
+ if( lemp->basisflag ) cfp=stp->bp;
+ else cfp=stp->cfp;
+ while( cfp ){
+ char buf[20];
+ if( cfp->dot==cfp->rp->nrhs ){
+ sprintf(buf,"(%d)",cfp->rp->index);
+ fprintf(fp," %5s ",buf);
+ }else{
+ fprintf(fp," ");
+ }
+ ConfigPrint(fp,cfp);
+ fprintf(fp,"\n");
+#ifdef TEST
+ SetPrint(fp,cfp->fws,lemp);
+ PlinkPrint(fp,cfp->fplp,"To ");
+ PlinkPrint(fp,cfp->bplp,"From");
+#endif
+ if( lemp->basisflag ) cfp=cfp->bp;
+ else cfp=cfp->next;
+ }
+ fprintf(fp,"\n");
+ for(ap=stp->ap; ap; ap=ap->next){
+ if( PrintAction(ap,fp,30) ) fprintf(fp,"\n");
+ }
+ fprintf(fp,"\n");
+ }
+ fclose(fp);
+ return;
+}
+
+/* Search for the file "name" which is in the same directory as
+** the exacutable */
+PRIVATE char *pathsearch(argv0,name,modemask)
+char *argv0;
+char *name;
+int modemask;
+{
+ char *pathlist;
+ char *path,*cp;
+ char c;
+ extern int access();
+
+#ifdef __WIN32__
+ cp = strrchr(argv0,'\\');
+#else
+ cp = strrchr(argv0,'/');
+#endif
+ if( cp ){
+ c = *cp;
+ *cp = 0;
+ path = (char *)malloc( strlen(argv0) + strlen(name) + 2 );
+ if( path ) sprintf(path,"%s/%s",argv0,name);
+ *cp = c;
+ }else{
+ extern char *getenv();
+ pathlist = getenv("PATH");
+ if( pathlist==0 ) pathlist = ".:/bin:/usr/bin";
+ path = (char *)malloc( strlen(pathlist)+strlen(name)+2 );
+ if( path!=0 ){
+ while( *pathlist ){
+ cp = strchr(pathlist,':');
+ if( cp==0 ) cp = &pathlist[strlen(pathlist)];
+ c = *cp;
+ *cp = 0;
+ sprintf(path,"%s/%s",pathlist,name);
+ *cp = c;
+ if( c==0 ) pathlist = "";
+ else pathlist = &cp[1];
+ if( access(path,modemask)==0 ) break;
+ }
+ }
+ }
+ return path;
+}
+
+/* Given an action, compute the integer value for that action
+** which is to be put in the action table of the generated machine.
+** Return negative if no action should be generated.
+*/
+PRIVATE int compute_action(lemp,ap)
+struct lemon *lemp;
+struct action *ap;
+{
+ int act;
+ switch( ap->type ){
+ case SHIFT: act = ap->x.stp->index; break;
+ case REDUCE: act = ap->x.rp->index + lemp->nstate; break;
+ case ERROR: act = lemp->nstate + lemp->nrule; break;
+ case ACCEPT: act = lemp->nstate + lemp->nrule + 1; break;
+ default: act = -1; break;
+ }
+ return act;
+}
+
+#define LINESIZE 1000
+/* The next cluster of routines are for reading the template file
+** and writing the results to the generated parser */
+/* The first function transfers data from "in" to "out" until
+** a line is seen which begins with "%%". The line number is
+** tracked.
+**
+** if name!=0, then any word that begin with "Parse" is changed to
+** begin with *name instead.
+*/
+PRIVATE void tplt_xfer(name,in,out,lineno)
+char *name;
+FILE *in;
+FILE *out;
+int *lineno;
+{
+ int i, iStart;
+ char line[LINESIZE];
+ while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){
+ (*lineno)++;
+ iStart = 0;
+ if( name ){
+ for(i=0; line[i]; i++){
+ if( line[i]=='P' && strncmp(&line[i],"Parse",5)==0
+ && (i==0 || !isalpha(line[i-1]))
+ ){
+ if( i>iStart ) fprintf(out,"%.*s",i-iStart,&line[iStart]);
+ fprintf(out,"%s",name);
+ i += 4;
+ iStart = i+1;
+ }
+ }
+ }
+ fprintf(out,"%s",&line[iStart]);
+ }
+}
+
+/* The next function finds the template file and opens it, returning
+** a pointer to the opened file. */
+PRIVATE FILE *tplt_open(lemp)
+struct lemon *lemp;
+{
+ static char templatename[] = "lempar.c";
+ char buf[1000];
+ FILE *in;
+ char *tpltname;
+ char *cp;
+
+ cp = strrchr(lemp->filename,'.');
+ if( cp ){
+ sprintf(buf,"%.*s.lt",(int)cp-(int)lemp->filename,lemp->filename);
+ }else{
+ sprintf(buf,"%s.lt",lemp->filename);
+ }
+ if( access(buf,004)==0 ){
+ tpltname = buf;
+ }else{
+ tpltname = pathsearch(lemp->argv0,templatename,0);
+ }
+ if( tpltname==0 ){
+ fprintf(stderr,"Can't find the parser driver template file \"%s\".\n",
+ templatename);
+ lemp->errorcnt++;
+ return 0;
+ }
+ in = fopen(tpltname,"r");
+ if( in==0 ){
+ fprintf(stderr,"Can't open the template file \"%s\".\n",templatename);
+ lemp->errorcnt++;
+ return 0;
+ }
+ return in;
+}
+
+/* Print a string to the file and keep the linenumber up to date */
+PRIVATE void tplt_print(out,lemp,str,strln,lineno)
+FILE *out;
+struct lemon *lemp;
+char *str;
+int strln;
+int *lineno;
+{
+ if( str==0 ) return;
+ fprintf(out,"#line %d \"%s\"\n",strln,lemp->filename); (*lineno)++;
+ while( *str ){
+ if( *str=='\n' ) (*lineno)++;
+ putc(*str,out);
+ str++;
+ }
+ fprintf(out,"\n#line %d \"%s\"\n",*lineno+2,lemp->outname); (*lineno)+=2;
+ return;
+}
+
+/*
+** The following routine emits code for the destructor for the
+** symbol sp
+*/
+void emit_destructor_code(out,sp,lemp,lineno)
+FILE *out;
+struct symbol *sp;
+struct lemon *lemp;
+int *lineno;
+{
+ char *cp;
+
+ int linecnt = 0;
+ if( sp->type==TERMINAL ){
+ cp = lemp->tokendest;
+ if( cp==0 ) return;
+ fprintf(out,"#line %d \"%s\"\n{",lemp->tokendestln,lemp->filename);
+ }else{
+ cp = sp->destructor;
+ if( cp==0 ) return;
+ fprintf(out,"#line %d \"%s\"\n{",sp->destructorln,lemp->filename);
+ }
+ for(; *cp; cp++){
+ if( *cp=='$' && cp[1]=='$' ){
+ fprintf(out,"(yypminor->yy%d)",sp->dtnum);
+ cp++;
+ continue;
+ }
+ if( *cp=='\n' ) linecnt++;
+ fputc(*cp,out);
+ }
+ (*lineno) += 3 + linecnt;
+ fprintf(out,"}\n#line %d \"%s\"\n",*lineno,lemp->outname);
+ return;
+}
+
+/*
+** Return TRUE (non-zero) if the given symbol has a distructor.
+*/
+int has_destructor(sp, lemp)
+struct symbol *sp;
+struct lemon *lemp;
+{
+ int ret;
+ if( sp->type==TERMINAL ){
+ ret = lemp->tokendest!=0;
+ }else{
+ ret = sp->destructor!=0;
+ }
+ return ret;
+}
+
+/*
+** Generate code which executes when the rule "rp" is reduced. Write
+** the code to "out". Make sure lineno stays up-to-date.
+*/
+PRIVATE void emit_code(out,rp,lemp,lineno)
+FILE *out;
+struct rule *rp;
+struct lemon *lemp;
+int *lineno;
+{
+ char *cp, *xp;
+ int linecnt = 0;
+ int i;
+ char lhsused = 0; /* True if the LHS element has been used */
+ char used[MAXRHS]; /* True for each RHS element which is used */
+
+ for(i=0; inrhs; i++) used[i] = 0;
+ lhsused = 0;
+
+ /* Generate code to do the reduce action */
+ if( rp->code ){
+ fprintf(out,"#line %d \"%s\"\n{",rp->line,lemp->filename);
+ for(cp=rp->code; *cp; cp++){
+ if( isalpha(*cp) && (cp==rp->code || !isalnum(cp[-1])) ){
+ char saved;
+ for(xp= &cp[1]; isalnum(*xp); xp++);
+ saved = *xp;
+ *xp = 0;
+ if( rp->lhsalias && strcmp(cp,rp->lhsalias)==0 ){
+ fprintf(out,"yygotominor.yy%d",rp->lhs->dtnum);
+ cp = xp;
+ lhsused = 1;
+ }else{
+ for(i=0; inrhs; i++){
+ if( rp->rhsalias[i] && strcmp(cp,rp->rhsalias[i])==0 ){
+ fprintf(out,"yymsp[%d].minor.yy%d",i-rp->nrhs+1,rp->rhs[i]->dtnum);
+ cp = xp;
+ used[i] = 1;
+ break;
+ }
+ }
+ }
+ *xp = saved;
+ }
+ if( *cp=='\n' ) linecnt++;
+ fputc(*cp,out);
+ } /* End loop */
+ (*lineno) += 3 + linecnt;
+ fprintf(out,"}\n#line %d \"%s\"\n",*lineno,lemp->outname);
+ } /* End if( rp->code ) */
+
+ /* Check to make sure the LHS has been used */
+ if( rp->lhsalias && !lhsused ){
+ ErrorMsg(lemp->filename,rp->ruleline,
+ "Label \"%s\" for \"%s(%s)\" is never used.",
+ rp->lhsalias,rp->lhs->name,rp->lhsalias);
+ lemp->errorcnt++;
+ }
+
+ /* Generate destructor code for RHS symbols which are not used in the
+ ** reduce code */
+ for(i=0; inrhs; i++){
+ if( rp->rhsalias[i] && !used[i] ){
+ ErrorMsg(lemp->filename,rp->ruleline,
+ "Label $%s$ for \"%s(%s)\" is never used.",
+ rp->rhsalias[i],rp->rhs[i]->name,rp->rhsalias[i]);
+ lemp->errorcnt++;
+ }else if( rp->rhsalias[i]==0 ){
+ if( has_destructor(rp->rhs[i],lemp) ){
+ fprintf(out," yy_destructor(%d,&yymsp[%d].minor);\n",
+ rp->rhs[i]->index,i-rp->nrhs+1); (*lineno)++;
+ }else{
+ fprintf(out," /* No destructor defined for %s */\n",
+ rp->rhs[i]->name);
+ (*lineno)++;
+ }
+ }
+ }
+ return;
+}
+
+/*
+** Print the definition of the union used for the parser's data stack.
+** This union contains fields for every possible data type for tokens
+** and nonterminals. In the process of computing and printing this
+** union, also set the ".dtnum" field of every terminal and nonterminal
+** symbol.
+*/
+void print_stack_union(out,lemp,plineno,mhflag)
+FILE *out; /* The output stream */
+struct lemon *lemp; /* The main info structure for this parser */
+int *plineno; /* Pointer to the line number */
+int mhflag; /* True if generating makeheaders output */
+{
+ int lineno = *plineno; /* The line number of the output */
+ char **types; /* A hash table of datatypes */
+ int arraysize; /* Size of the "types" array */
+ int maxdtlength; /* Maximum length of any ".datatype" field. */
+ char *stddt; /* Standardized name for a datatype */
+ int i,j; /* Loop counters */
+ int hash; /* For hashing the name of a type */
+ char *name; /* Name of the parser */
+
+ /* Allocate and initialize types[] and allocate stddt[] */
+ arraysize = lemp->nsymbol * 2;
+ types = (char**)malloc( arraysize * sizeof(char*) );
+ for(i=0; insymbol; i++){
+ int len;
+ struct symbol *sp = lemp->symbols[i];
+ if( sp->datatype==0 ) continue;
+ len = strlen(sp->datatype);
+ if( len>maxdtlength ) maxdtlength = len;
+ }
+ stddt = (char*)malloc( maxdtlength*2 + 1 );
+ if( types==0 || stddt==0 ){
+ fprintf(stderr,"Out of memory.\n");
+ exit(1);
+ }
+
+ /* Build a hash table of datatypes. The ".dtnum" field of each symbol
+ ** is filled in with the hash index plus 1. A ".dtnum" value of 0 is
+ ** used for terminal symbols and for nonterminals which don't specify
+ ** a datatype using the %type directive. */
+ for(i=0; insymbol; i++){
+ struct symbol *sp = lemp->symbols[i];
+ char *cp;
+ if( sp==lemp->errsym ){
+ sp->dtnum = arraysize+1;
+ continue;
+ }
+ if( sp->type!=NONTERMINAL || sp->datatype==0 ){
+ sp->dtnum = 0;
+ continue;
+ }
+ cp = sp->datatype;
+ j = 0;
+ while( isspace(*cp) ) cp++;
+ while( *cp ) stddt[j++] = *cp++;
+ while( j>0 && isspace(stddt[j-1]) ) j--;
+ stddt[j] = 0;
+ hash = 0;
+ for(j=0; stddt[j]; j++){
+ hash = hash*53 + stddt[j];
+ }
+ if( hash<0 ) hash = -hash;
+ hash = hash%arraysize;
+ while( types[hash] ){
+ if( strcmp(types[hash],stddt)==0 ){
+ sp->dtnum = hash + 1;
+ break;
+ }
+ hash++;
+ if( hash>=arraysize ) hash = 0;
+ }
+ if( types[hash]==0 ){
+ sp->dtnum = hash + 1;
+ types[hash] = (char*)malloc( strlen(stddt)+1 );
+ if( types[hash]==0 ){
+ fprintf(stderr,"Out of memory.\n");
+ exit(1);
+ }
+ strcpy(types[hash],stddt);
+ }
+ }
+
+ /* Print out the definition of YYTOKENTYPE and YYMINORTYPE */
+ name = lemp->name ? lemp->name : "Parse";
+ lineno = *plineno;
+ if( mhflag ){ fprintf(out,"#if INTERFACE\n"); lineno++; }
+ fprintf(out,"#define %sTOKENTYPE %s\n",name,
+ lemp->tokentype?lemp->tokentype:"void*"); lineno++;
+ if( mhflag ){ fprintf(out,"#endif\n"); lineno++; }
+ fprintf(out,"typedef union {\n"); lineno++;
+ fprintf(out," %sTOKENTYPE yy0;\n",name); lineno++;
+ for(i=0; ierrsym->dtnum); lineno++;
+ free(stddt);
+ free(types);
+ fprintf(out,"} YYMINORTYPE;\n"); lineno++;
+ *plineno = lineno;
+}
+
+/* Generate C source code for the parser */
+void ReportTable(lemp, mhflag)
+struct lemon *lemp;
+int mhflag; /* Output in makeheaders format if true */
+{
+ FILE *out, *in;
+ char line[LINESIZE];
+ int lineno;
+ struct state *stp;
+ struct action *ap;
+ struct rule *rp;
+ int i;
+ int tablecnt;
+ char *name;
+
+ in = tplt_open(lemp);
+ if( in==0 ) return;
+ out = file_open(lemp,".c","w");
+ if( out==0 ){
+ fclose(in);
+ return;
+ }
+ lineno = 1;
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the include code, if any */
+ tplt_print(out,lemp,lemp->include,lemp->includeln,&lineno);
+ if( mhflag ){
+ char *name = file_makename(lemp, ".h");
+ fprintf(out,"#include \"%s\"\n", name); lineno++;
+ free(name);
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate #defines for all tokens */
+ if( mhflag ){
+ char *prefix;
+ fprintf(out,"#if INTERFACE\n"); lineno++;
+ if( lemp->tokenprefix ) prefix = lemp->tokenprefix;
+ else prefix = "";
+ for(i=1; interminal; i++){
+ fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i);
+ lineno++;
+ }
+ fprintf(out,"#endif\n"); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the defines */
+ fprintf(out,"/* \001 */\n");
+ fprintf(out,"#define YYCODETYPE %s\n",
+ lemp->nsymbol>250?"int":"unsigned char"); lineno++;
+ fprintf(out,"#define YYNOCODE %d\n",lemp->nsymbol+1); lineno++;
+ fprintf(out,"#define YYACTIONTYPE %s\n",
+ lemp->nstate+lemp->nrule>250?"int":"unsigned char"); lineno++;
+ print_stack_union(out,lemp,&lineno,mhflag);
+ if( lemp->stacksize ){
+ if( atoi(lemp->stacksize)<=0 ){
+ ErrorMsg(lemp->filename,0,
+"Illegal stack size: [%s]. The stack size should be an integer constant.",
+ lemp->stacksize);
+ lemp->errorcnt++;
+ lemp->stacksize = "100";
+ }
+ fprintf(out,"#define YYSTACKDEPTH %s\n",lemp->stacksize); lineno++;
+ }else{
+ fprintf(out,"#define YYSTACKDEPTH 100\n"); lineno++;
+ }
+ if( mhflag ){
+ fprintf(out,"#if INTERFACE\n"); lineno++;
+ }
+ name = lemp->name ? lemp->name : "Parse";
+ if( lemp->arg && lemp->arg[0] ){
+ int i;
+ i = strlen(lemp->arg);
+ while( i>=1 && isalnum(lemp->arg[i-1]) ) i--;
+ fprintf(out,"#define %sARGDECL ,%s\n",name,&lemp->arg[i]); lineno++;
+ fprintf(out,"#define %sXARGDECL %s;\n",name,lemp->arg); lineno++;
+ fprintf(out,"#define %sANSIARGDECL ,%s\n",name,lemp->arg); lineno++;
+ }else{
+ fprintf(out,"#define %sARGDECL\n",name); lineno++;
+ fprintf(out,"#define %sXARGDECL\n",name); lineno++;
+ fprintf(out,"#define %sANSIARGDECL\n",name); lineno++;
+ }
+ if( mhflag ){
+ fprintf(out,"#endif\n"); lineno++;
+ }
+ fprintf(out,"#define YYNSTATE %d\n",lemp->nstate); lineno++;
+ fprintf(out,"#define YYNRULE %d\n",lemp->nrule); lineno++;
+ fprintf(out,"#define YYERRORSYMBOL %d\n",lemp->errsym->index); lineno++;
+ fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); lineno++;
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the action table.
+ **
+ ** Each entry in the action table is an element of the following
+ ** structure:
+ ** struct yyActionEntry {
+ ** YYCODETYPE lookahead;
+ ** YYACTIONTYPE action;
+ ** struct yyActionEntry *next;
+ ** }
+ **
+ ** The entries are grouped into hash tables, one hash table for each
+ ** parser state. The hash table has a size which is the smallest
+ ** power of two needed to hold all entries.
+ */
+ tablecnt = 0;
+
+ /* Loop over parser states */
+ for(i=0; instate; i++){
+ int tablesize; /* size of the hash table */
+ int j,k; /* Loop counter */
+ int collide[2048]; /* The collision chain for the table */
+ struct action *table[2048]; /* Build the hash table here */
+
+ /* Find the number of actions and initialize the hash table */
+ stp = lemp->sorted[i];
+ stp->tabstart = tablecnt;
+ stp->naction = 0;
+ for(ap=stp->ap; ap; ap=ap->next){
+ if( ap->sp->index!=lemp->nsymbol && compute_action(lemp,ap)>=0 ){
+ stp->naction++;
+ }
+ }
+ tablesize = 1;
+ while( tablesizenaction ) tablesize += tablesize;
+ assert( tablesize<= sizeof(table)/sizeof(table[0]) );
+ for(j=0; jtabdfltact = lemp->nstate + lemp->nrule;
+ for(ap=stp->ap; ap; ap=ap->next){
+ int action = compute_action(lemp,ap);
+ int h;
+ if( ap->sp->index==lemp->nsymbol ){
+ stp->tabdfltact = action;
+ }else if( action>=0 ){
+ h = ap->sp->index & (tablesize-1);
+ ap->collide = table[h];
+ table[h] = ap;
+ }
+ }
+
+ /* Resolve collisions */
+ for(j=k=0; jcollide ){
+ while( table[k] ) k++;
+ table[k] = table[j]->collide;
+ collide[j] = k;
+ table[j]->collide = 0;
+ if( kindex); lineno++;
+ for(j=0; jsp->index,
+ compute_action(lemp,table[j]));
+ if( collide[j]>=0 ){
+ fprintf(out,"&yyActionTable[%4d] }, /* ",
+ collide[j] + tablecnt);
+ }else{
+ fprintf(out,"0 }, /* ");
+ }
+ PrintAction(table[j],out,22);
+ fprintf(out," */\n");
+ }
+ lineno++;
+ }
+
+ /* Update the table count */
+ tablecnt += tablesize;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+ lemp->tablesize = tablecnt;
+
+ /* Generate the state table
+ **
+ ** Each entry is an element of the following structure:
+ ** struct yyStateEntry {
+ ** struct yyActionEntry *hashtbl;
+ ** int mask;
+ ** YYACTIONTYPE actionDefault;
+ ** }
+ */
+ for(i=0; instate; i++){
+ int tablesize;
+ stp = lemp->sorted[i];
+ tablesize = 1;
+ while( tablesizenaction ) tablesize += tablesize;
+ fprintf(out," { &yyActionTable[%d], %d, %d},\n",
+ stp->tabstart,
+ tablesize - 1,
+ stp->tabdfltact); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate a table containing the symbolic name of every symbol */
+ for(i=0; insymbol; i++){
+ sprintf(line,"\"%s\",",lemp->symbols[i]->name);
+ fprintf(out," %-15s",line);
+ if( (i&3)==3 ){ fprintf(out,"\n"); lineno++; }
+ }
+ if( (i&3)!=0 ){ fprintf(out,"\n"); lineno++; }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes every time a symbol is popped from
+ ** the stack while processing errors or while destroying the parser.
+ ** (In other words, generate the %destructor actions) */
+ if( lemp->tokendest ){
+ for(i=0; insymbol; i++){
+ struct symbol *sp = lemp->symbols[i];
+ if( sp==0 || sp->type!=TERMINAL ) continue;
+ fprintf(out," case %d:\n",sp->index); lineno++;
+ }
+ for(i=0; insymbol && lemp->symbols[i]->type!=TERMINAL; i++);
+ if( insymbol ){
+ emit_destructor_code(out,lemp->symbols[i],lemp,&lineno);
+ fprintf(out," break;\n"); lineno++;
+ }
+ }
+ for(i=0; insymbol; i++){
+ struct symbol *sp = lemp->symbols[i];
+ if( sp==0 || sp->type==TERMINAL || sp->destructor==0 ) continue;
+ fprintf(out," case %d:\n",sp->index); lineno++;
+ emit_destructor_code(out,lemp->symbols[i],lemp,&lineno);
+ fprintf(out," break;\n"); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes whenever the parser stack overflows */
+ tplt_print(out,lemp,lemp->overflow,lemp->overflowln,&lineno);
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate the table of rule information
+ **
+ ** Note: This code depends on the fact that rules are number
+ ** sequentually beginning with 0.
+ */
+ for(rp=lemp->rule; rp; rp=rp->next){
+ fprintf(out," { %d, %d },\n",rp->lhs->index,rp->nrhs); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which execution during each REDUCE action */
+ for(rp=lemp->rule; rp; rp=rp->next){
+ fprintf(out," case %d:\n",rp->index); lineno++;
+ fprintf(out," YYTRACE(\"%s ::=",rp->lhs->name);
+ for(i=0; inrhs; i++) fprintf(out," %s",rp->rhs[i]->name);
+ fprintf(out,"\")\n"); lineno++;
+ emit_code(out,rp,lemp,&lineno);
+ fprintf(out," break;\n"); lineno++;
+ }
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes if a parse fails */
+ tplt_print(out,lemp,lemp->failure,lemp->failureln,&lineno);
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes when a syntax error occurs */
+ tplt_print(out,lemp,lemp->error,lemp->errorln,&lineno);
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Generate code which executes when the parser accepts its input */
+ tplt_print(out,lemp,lemp->accept,lemp->acceptln,&lineno);
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Append any addition code the user desires */
+ tplt_print(out,lemp,lemp->extracode,lemp->extracodeln,&lineno);
+
+ fclose(in);
+ fclose(out);
+ return;
+}
+
+/* Generate a header file for the parser */
+void ReportHeader(lemp)
+struct lemon *lemp;
+{
+ FILE *out, *in;
+ char *prefix;
+ char line[LINESIZE];
+ char pattern[LINESIZE];
+ int i;
+
+ if( lemp->tokenprefix ) prefix = lemp->tokenprefix;
+ else prefix = "";
+ in = file_open(lemp,".h","r");
+ if( in ){
+ for(i=1; interminal && fgets(line,LINESIZE,in); i++){
+ sprintf(pattern,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i);
+ if( strcmp(line,pattern) ) break;
+ }
+ fclose(in);
+ if( i==lemp->nterminal ){
+ /* No change in the file. Don't rewrite it. */
+ return;
+ }
+ }
+ out = file_open(lemp,".h","w");
+ if( out ){
+ for(i=1; interminal; i++){
+ fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i);
+ }
+ fclose(out);
+ }
+ return;
+}
+
+/* Reduce the size of the action tables, if possible, by making use
+** of defaults.
+**
+** In this version, if all REDUCE actions use the same rule, make
+** them the default. Only default them if there are more than one.
+*/
+void CompressTables(lemp)
+struct lemon *lemp;
+{
+ struct state *stp;
+ struct action *ap;
+ struct rule *rp;
+ int i;
+ int cnt;
+
+ for(i=0; instate; i++){
+ stp = lemp->sorted[i];
+
+ /* Find the first REDUCE action */
+ for(ap=stp->ap; ap && ap->type!=REDUCE; ap=ap->next);
+ if( ap==0 ) continue;
+
+ /* Remember the rule used */
+ rp = ap->x.rp;
+
+ /* See if all other REDUCE acitons use the same rule */
+ cnt = 1;
+ for(ap=ap->next; ap; ap=ap->next){
+ if( ap->type==REDUCE ){
+ if( ap->x.rp!=rp ) break;
+ cnt++;
+ }
+ }
+ if( ap || cnt==1 ) continue;
+
+ /* Combine all REDUCE actions into a single default */
+ for(ap=stp->ap; ap && ap->type!=REDUCE; ap=ap->next);
+ assert( ap );
+ ap->sp = Symbol_new("{default}");
+ for(ap=ap->next; ap; ap=ap->next){
+ if( ap->type==REDUCE ) ap->type = NOT_USED;
+ }
+ stp->ap = Action_sort(stp->ap);
+ }
+}
+/***************** From the file "set.c" ************************************/
+/*
+** Set manipulation routines for the LEMON parser generator.
+*/
+
+static int size = 0;
+
+/* Set the set size */
+void SetSize(n)
+int n;
+{
+ size = n+1;
+}
+
+/* Allocate a new set */
+char *SetNew(){
+ char *s;
+ int i;
+ s = (char*)malloc( size );
+ if( s==0 ){
+ extern void memory_error();
+ memory_error();
+ }
+ for(i=0; isize = 1024;
+ x1a->count = 0;
+ x1a->tbl = (x1node*)malloc(
+ (sizeof(x1node) + sizeof(x1node*))*1024 );
+ if( x1a->tbl==0 ){
+ free(x1a);
+ x1a = 0;
+ }else{
+ int i;
+ x1a->ht = (x1node**)&(x1a->tbl[1024]);
+ for(i=0; i<1024; i++) x1a->ht[i] = 0;
+ }
+ }
+}
+/* Insert a new record into the array. Return TRUE if successful.
+** Prior data with the same key is NOT overwritten */
+int Strsafe_insert(data)
+char *data;
+{
+ x1node *np;
+ int h;
+ int ph;
+
+ if( x1a==0 ) return 0;
+ ph = strhash(data);
+ h = ph & (x1a->size-1);
+ np = x1a->ht[h];
+ while( np ){
+ if( strcmp(np->data,data)==0 ){
+ /* An existing entry with the same key is found. */
+ /* Fail because overwrite is not allows. */
+ return 0;
+ }
+ np = np->next;
+ }
+ if( x1a->count>=x1a->size ){
+ /* Need to make the hash table bigger */
+ int i,size;
+ struct s_x1 array;
+ array.size = size = x1a->size*2;
+ array.count = x1a->count;
+ array.tbl = (x1node*)malloc(
+ (sizeof(x1node) + sizeof(x1node*))*size );
+ if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
+ array.ht = (x1node**)&(array.tbl[size]);
+ for(i=0; icount; i++){
+ x1node *oldnp, *newnp;
+ oldnp = &(x1a->tbl[i]);
+ h = strhash(oldnp->data) & (size-1);
+ newnp = &(array.tbl[i]);
+ if( array.ht[h] ) array.ht[h]->from = &(newnp->next);
+ newnp->next = array.ht[h];
+ newnp->data = oldnp->data;
+ newnp->from = &(array.ht[h]);
+ array.ht[h] = newnp;
+ }
+ free(x1a->tbl);
+ *x1a = array;
+ }
+ /* Insert the new data */
+ h = ph & (x1a->size-1);
+ np = &(x1a->tbl[x1a->count++]);
+ np->data = data;
+ if( x1a->ht[h] ) x1a->ht[h]->from = &(np->next);
+ np->next = x1a->ht[h];
+ x1a->ht[h] = np;
+ np->from = &(x1a->ht[h]);
+ return 1;
+}
+
+/* Return a pointer to data assigned to the given key. Return NULL
+** if no such key. */
+char *Strsafe_find(key)
+char *key;
+{
+ int h;
+ x1node *np;
+
+ if( x1a==0 ) return 0;
+ h = strhash(key) & (x1a->size-1);
+ np = x1a->ht[h];
+ while( np ){
+ if( strcmp(np->data,key)==0 ) break;
+ np = np->next;
+ }
+ return np ? np->data : 0;
+}
+
+/* Return a pointer to the (terminal or nonterminal) symbol "x".
+** Create a new symbol if this is the first time "x" has been seen.
+*/
+struct symbol *Symbol_new(x)
+char *x;
+{
+ struct symbol *sp;
+
+ sp = Symbol_find(x);
+ if( sp==0 ){
+ sp = (struct symbol *)malloc( sizeof(struct symbol) );
+ MemoryCheck(sp);
+ sp->name = Strsafe(x);
+ sp->type = isupper(*x) ? TERMINAL : NONTERMINAL;
+ sp->rule = 0;
+ sp->prec = -1;
+ sp->assoc = UNK;
+ sp->firstset = 0;
+ sp->lambda = FALSE;
+ sp->destructor = 0;
+ sp->datatype = 0;
+ Symbol_insert(sp,sp->name);
+ }
+ return sp;
+}
+
+/* Compare two symbols */
+int Symbolcmpp(a,b)
+struct symbol **a;
+struct symbol **b;
+{
+ return strcmp((**a).name,(**b).name);
+}
+
+/* There is one instance of the following structure for each
+** associative array of type "x2".
+*/
+struct s_x2 {
+ int size; /* The number of available slots. */
+ /* Must be a power of 2 greater than or */
+ /* equal to 1 */
+ int count; /* Number of currently slots filled */
+ struct s_x2node *tbl; /* The data stored here */
+ struct s_x2node **ht; /* Hash table for lookups */
+};
+
+/* There is one instance of this structure for every data element
+** in an associative array of type "x2".
+*/
+typedef struct s_x2node {
+ struct symbol *data; /* The data */
+ char *key; /* The key */
+ struct s_x2node *next; /* Next entry with the same hash */
+ struct s_x2node **from; /* Previous link */
+} x2node;
+
+/* There is only one instance of the array, which is the following */
+static struct s_x2 *x2a;
+
+/* Allocate a new associative array */
+void Symbol_init(){
+ if( x2a ) return;
+ x2a = (struct s_x2*)malloc( sizeof(struct s_x2) );
+ if( x2a ){
+ x2a->size = 128;
+ x2a->count = 0;
+ x2a->tbl = (x2node*)malloc(
+ (sizeof(x2node) + sizeof(x2node*))*128 );
+ if( x2a->tbl==0 ){
+ free(x2a);
+ x2a = 0;
+ }else{
+ int i;
+ x2a->ht = (x2node**)&(x2a->tbl[128]);
+ for(i=0; i<128; i++) x2a->ht[i] = 0;
+ }
+ }
+}
+/* Insert a new record into the array. Return TRUE if successful.
+** Prior data with the same key is NOT overwritten */
+int Symbol_insert(data,key)
+struct symbol *data;
+char *key;
+{
+ x2node *np;
+ int h;
+ int ph;
+
+ if( x2a==0 ) return 0;
+ ph = strhash(key);
+ h = ph & (x2a->size-1);
+ np = x2a->ht[h];
+ while( np ){
+ if( strcmp(np->key,key)==0 ){
+ /* An existing entry with the same key is found. */
+ /* Fail because overwrite is not allows. */
+ return 0;
+ }
+ np = np->next;
+ }
+ if( x2a->count>=x2a->size ){
+ /* Need to make the hash table bigger */
+ int i,size;
+ struct s_x2 array;
+ array.size = size = x2a->size*2;
+ array.count = x2a->count;
+ array.tbl = (x2node*)malloc(
+ (sizeof(x2node) + sizeof(x2node*))*size );
+ if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
+ array.ht = (x2node**)&(array.tbl[size]);
+ for(i=0; icount; i++){
+ x2node *oldnp, *newnp;
+ oldnp = &(x2a->tbl[i]);
+ h = strhash(oldnp->key) & (size-1);
+ newnp = &(array.tbl[i]);
+ if( array.ht[h] ) array.ht[h]->from = &(newnp->next);
+ newnp->next = array.ht[h];
+ newnp->key = oldnp->key;
+ newnp->data = oldnp->data;
+ newnp->from = &(array.ht[h]);
+ array.ht[h] = newnp;
+ }
+ free(x2a->tbl);
+ *x2a = array;
+ }
+ /* Insert the new data */
+ h = ph & (x2a->size-1);
+ np = &(x2a->tbl[x2a->count++]);
+ np->key = key;
+ np->data = data;
+ if( x2a->ht[h] ) x2a->ht[h]->from = &(np->next);
+ np->next = x2a->ht[h];
+ x2a->ht[h] = np;
+ np->from = &(x2a->ht[h]);
+ return 1;
+}
+
+/* Return a pointer to data assigned to the given key. Return NULL
+** if no such key. */
+struct symbol *Symbol_find(key)
+char *key;
+{
+ int h;
+ x2node *np;
+
+ if( x2a==0 ) return 0;
+ h = strhash(key) & (x2a->size-1);
+ np = x2a->ht[h];
+ while( np ){
+ if( strcmp(np->key,key)==0 ) break;
+ np = np->next;
+ }
+ return np ? np->data : 0;
+}
+
+/* Return the n-th data. Return NULL if n is out of range. */
+struct symbol *Symbol_Nth(n)
+int n;
+{
+ struct symbol *data;
+ if( x2a && n>0 && n<=x2a->count ){
+ data = x2a->tbl[n-1].data;
+ }else{
+ data = 0;
+ }
+ return data;
+}
+
+/* Return the size of the array */
+int Symbol_count()
+{
+ return x2a ? x2a->count : 0;
+}
+
+/* Return an array of pointers to all data in the table.
+** The array is obtained from malloc. Return NULL if memory allocation
+** problems, or if the array is empty. */
+struct symbol **Symbol_arrayof()
+{
+ struct symbol **array;
+ int i,size;
+ if( x2a==0 ) return 0;
+ size = x2a->count;
+ array = (struct symbol **)malloc( sizeof(struct symbol *)*size );
+ if( array ){
+ for(i=0; itbl[i].data;
+ }
+ return array;
+}
+
+/* Compare two configurations */
+int Configcmp(a,b)
+struct config *a;
+struct config *b;
+{
+ int x;
+ x = a->rp->index - b->rp->index;
+ if( x==0 ) x = a->dot - b->dot;
+ return x;
+}
+
+/* Compare two states */
+PRIVATE int statecmp(a,b)
+struct config *a;
+struct config *b;
+{
+ int rc;
+ for(rc=0; rc==0 && a && b; a=a->bp, b=b->bp){
+ rc = a->rp->index - b->rp->index;
+ if( rc==0 ) rc = a->dot - b->dot;
+ }
+ if( rc==0 ){
+ if( a ) rc = 1;
+ if( b ) rc = -1;
+ }
+ return rc;
+}
+
+/* Hash a state */
+PRIVATE int statehash(a)
+struct config *a;
+{
+ int h=0;
+ while( a ){
+ h = h*571 + a->rp->index*37 + a->dot;
+ a = a->bp;
+ }
+ return h;
+}
+
+/* Allocate a new state structure */
+struct state *State_new()
+{
+ struct state *new;
+ new = (struct state *)malloc( sizeof(struct state) );
+ MemoryCheck(new);
+ return new;
+}
+
+/* There is one instance of the following structure for each
+** associative array of type "x3".
+*/
+struct s_x3 {
+ int size; /* The number of available slots. */
+ /* Must be a power of 2 greater than or */
+ /* equal to 1 */
+ int count; /* Number of currently slots filled */
+ struct s_x3node *tbl; /* The data stored here */
+ struct s_x3node **ht; /* Hash table for lookups */
+};
+
+/* There is one instance of this structure for every data element
+** in an associative array of type "x3".
+*/
+typedef struct s_x3node {
+ struct state *data; /* The data */
+ struct config *key; /* The key */
+ struct s_x3node *next; /* Next entry with the same hash */
+ struct s_x3node **from; /* Previous link */
+} x3node;
+
+/* There is only one instance of the array, which is the following */
+static struct s_x3 *x3a;
+
+/* Allocate a new associative array */
+void State_init(){
+ if( x3a ) return;
+ x3a = (struct s_x3*)malloc( sizeof(struct s_x3) );
+ if( x3a ){
+ x3a->size = 128;
+ x3a->count = 0;
+ x3a->tbl = (x3node*)malloc(
+ (sizeof(x3node) + sizeof(x3node*))*128 );
+ if( x3a->tbl==0 ){
+ free(x3a);
+ x3a = 0;
+ }else{
+ int i;
+ x3a->ht = (x3node**)&(x3a->tbl[128]);
+ for(i=0; i<128; i++) x3a->ht[i] = 0;
+ }
+ }
+}
+/* Insert a new record into the array. Return TRUE if successful.
+** Prior data with the same key is NOT overwritten */
+int State_insert(data,key)
+struct state *data;
+struct config *key;
+{
+ x3node *np;
+ int h;
+ int ph;
+
+ if( x3a==0 ) return 0;
+ ph = statehash(key);
+ h = ph & (x3a->size-1);
+ np = x3a->ht[h];
+ while( np ){
+ if( statecmp(np->key,key)==0 ){
+ /* An existing entry with the same key is found. */
+ /* Fail because overwrite is not allows. */
+ return 0;
+ }
+ np = np->next;
+ }
+ if( x3a->count>=x3a->size ){
+ /* Need to make the hash table bigger */
+ int i,size;
+ struct s_x3 array;
+ array.size = size = x3a->size*2;
+ array.count = x3a->count;
+ array.tbl = (x3node*)malloc(
+ (sizeof(x3node) + sizeof(x3node*))*size );
+ if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
+ array.ht = (x3node**)&(array.tbl[size]);
+ for(i=0; icount; i++){
+ x3node *oldnp, *newnp;
+ oldnp = &(x3a->tbl[i]);
+ h = statehash(oldnp->key) & (size-1);
+ newnp = &(array.tbl[i]);
+ if( array.ht[h] ) array.ht[h]->from = &(newnp->next);
+ newnp->next = array.ht[h];
+ newnp->key = oldnp->key;
+ newnp->data = oldnp->data;
+ newnp->from = &(array.ht[h]);
+ array.ht[h] = newnp;
+ }
+ free(x3a->tbl);
+ *x3a = array;
+ }
+ /* Insert the new data */
+ h = ph & (x3a->size-1);
+ np = &(x3a->tbl[x3a->count++]);
+ np->key = key;
+ np->data = data;
+ if( x3a->ht[h] ) x3a->ht[h]->from = &(np->next);
+ np->next = x3a->ht[h];
+ x3a->ht[h] = np;
+ np->from = &(x3a->ht[h]);
+ return 1;
+}
+
+/* Return a pointer to data assigned to the given key. Return NULL
+** if no such key. */
+struct state *State_find(key)
+struct config *key;
+{
+ int h;
+ x3node *np;
+
+ if( x3a==0 ) return 0;
+ h = statehash(key) & (x3a->size-1);
+ np = x3a->ht[h];
+ while( np ){
+ if( statecmp(np->key,key)==0 ) break;
+ np = np->next;
+ }
+ return np ? np->data : 0;
+}
+
+/* Return an array of pointers to all data in the table.
+** The array is obtained from malloc. Return NULL if memory allocation
+** problems, or if the array is empty. */
+struct state **State_arrayof()
+{
+ struct state **array;
+ int i,size;
+ if( x3a==0 ) return 0;
+ size = x3a->count;
+ array = (struct state **)malloc( sizeof(struct state *)*size );
+ if( array ){
+ for(i=0; itbl[i].data;
+ }
+ return array;
+}
+
+/* Hash a configuration */
+PRIVATE int confighash(a)
+struct config *a;
+{
+ int h=0;
+ h = h*571 + a->rp->index*37 + a->dot;
+ return h;
+}
+
+/* There is one instance of the following structure for each
+** associative array of type "x4".
+*/
+struct s_x4 {
+ int size; /* The number of available slots. */
+ /* Must be a power of 2 greater than or */
+ /* equal to 1 */
+ int count; /* Number of currently slots filled */
+ struct s_x4node *tbl; /* The data stored here */
+ struct s_x4node **ht; /* Hash table for lookups */
+};
+
+/* There is one instance of this structure for every data element
+** in an associative array of type "x4".
+*/
+typedef struct s_x4node {
+ struct config *data; /* The data */
+ struct s_x4node *next; /* Next entry with the same hash */
+ struct s_x4node **from; /* Previous link */
+} x4node;
+
+/* There is only one instance of the array, which is the following */
+static struct s_x4 *x4a;
+
+/* Allocate a new associative array */
+void Configtable_init(){
+ if( x4a ) return;
+ x4a = (struct s_x4*)malloc( sizeof(struct s_x4) );
+ if( x4a ){
+ x4a->size = 64;
+ x4a->count = 0;
+ x4a->tbl = (x4node*)malloc(
+ (sizeof(x4node) + sizeof(x4node*))*64 );
+ if( x4a->tbl==0 ){
+ free(x4a);
+ x4a = 0;
+ }else{
+ int i;
+ x4a->ht = (x4node**)&(x4a->tbl[64]);
+ for(i=0; i<64; i++) x4a->ht[i] = 0;
+ }
+ }
+}
+/* Insert a new record into the array. Return TRUE if successful.
+** Prior data with the same key is NOT overwritten */
+int Configtable_insert(data)
+struct config *data;
+{
+ x4node *np;
+ int h;
+ int ph;
+
+ if( x4a==0 ) return 0;
+ ph = confighash(data);
+ h = ph & (x4a->size-1);
+ np = x4a->ht[h];
+ while( np ){
+ if( Configcmp(np->data,data)==0 ){
+ /* An existing entry with the same key is found. */
+ /* Fail because overwrite is not allows. */
+ return 0;
+ }
+ np = np->next;
+ }
+ if( x4a->count>=x4a->size ){
+ /* Need to make the hash table bigger */
+ int i,size;
+ struct s_x4 array;
+ array.size = size = x4a->size*2;
+ array.count = x4a->count;
+ array.tbl = (x4node*)malloc(
+ (sizeof(x4node) + sizeof(x4node*))*size );
+ if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
+ array.ht = (x4node**)&(array.tbl[size]);
+ for(i=0; icount; i++){
+ x4node *oldnp, *newnp;
+ oldnp = &(x4a->tbl[i]);
+ h = confighash(oldnp->data) & (size-1);
+ newnp = &(array.tbl[i]);
+ if( array.ht[h] ) array.ht[h]->from = &(newnp->next);
+ newnp->next = array.ht[h];
+ newnp->data = oldnp->data;
+ newnp->from = &(array.ht[h]);
+ array.ht[h] = newnp;
+ }
+ free(x4a->tbl);
+ *x4a = array;
+ }
+ /* Insert the new data */
+ h = ph & (x4a->size-1);
+ np = &(x4a->tbl[x4a->count++]);
+ np->data = data;
+ if( x4a->ht[h] ) x4a->ht[h]->from = &(np->next);
+ np->next = x4a->ht[h];
+ x4a->ht[h] = np;
+ np->from = &(x4a->ht[h]);
+ return 1;
+}
+
+/* Return a pointer to data assigned to the given key. Return NULL
+** if no such key. */
+struct config *Configtable_find(key)
+struct config *key;
+{
+ int h;
+ x4node *np;
+
+ if( x4a==0 ) return 0;
+ h = confighash(key) & (x4a->size-1);
+ np = x4a->ht[h];
+ while( np ){
+ if( Configcmp(np->data,key)==0 ) break;
+ np = np->next;
+ }
+ return np ? np->data : 0;
+}
+
+/* Remove all data from the table. Pass each data to the function "f"
+** as it is removed. ("f" may be null to avoid this step.) */
+void Configtable_clear(f)
+int(*f)(/* struct config * */);
+{
+ int i;
+ if( x4a==0 || x4a->count==0 ) return;
+ if( f ) for(i=0; icount; i++) (*f)(x4a->tbl[i].data);
+ for(i=0; isize; i++) x4a->ht[i] = 0;
+ x4a->count = 0;
+ return;
+}
diff --git a/tool/lempar.c b/tool/lempar.c
new file mode 100644
index 0000000000..e5293b1de2
--- /dev/null
+++ b/tool/lempar.c
@@ -0,0 +1,606 @@
+/* Driver template for the LEMON parser generator.
+** Copyright 1991-1995 by D. Richard Hipp.
+*
+* This version is specially modified for use with sqlite.
+* @(#) $Id: lempar.c,v 1.1 2000/05/29 14:26:02 drh Exp $
+*
+**
+** This library is free software; you can redistribute it and/or
+** modify it under the terms of the GNU Library General Public
+** License as published by the Free Software Foundation; either
+** version 2 of the License, or (at your option) any later version.
+**
+** This library 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
+** Library General Public License for more details.
+**
+** You should have received a copy of the GNU Library General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Modified 1997 to make it suitable for use with makeheaders.
+*/
+/* First off, code is include which follows the "include" declaration
+** in the input file. */
+#include
+%%
+/* Next is all token values, in a form suitable for use by makeheaders.
+** This section will be null unless lemon is run with the -m switch.
+*/
+/*
+** These constants (all generated automatically by the parser generator)
+** specify the various kinds of tokens (terminals) that the parser
+** understands.
+**
+** Each symbol here is a terminal symbol in the grammar.
+*/
+%%
+/* Make sure the INTERFACE macro is defined.
+*/
+#ifndef INTERFACE
+# define INTERFACE 1
+#endif
+/* The next thing included is series of defines which control
+** various aspects of the generated parser.
+** YYCODETYPE is the data type used for storing terminal
+** and nonterminal numbers. "unsigned char" is
+** used if there are fewer than 250 terminals
+** and nonterminals. "int" is used otherwise.
+** YYNOCODE is a number of type YYCODETYPE which corresponds
+** to no legal terminal or nonterminal number. This
+** number is used to fill in empty slots of the hash
+** table.
+** YYACTIONTYPE is the data type used for storing terminal
+** and nonterminal numbers. "unsigned char" is
+** used if there are fewer than 250 rules and
+** states combined. "int" is used otherwise.
+** ParseTOKENTYPE is the data type used for minor tokens given
+** directly to the parser from the tokenizer.
+** YYMINORTYPE is the data type used for all minor tokens.
+** This is typically a union of many types, one of
+** which is ParseTOKENTYPE. The entry in the union
+** for base tokens is called "yy0".
+** YYSTACKDEPTH is the maximum depth of the parser's stack.
+** ParseARGDECL is a declaration of a 3rd argument to the
+** parser, or null if there is no extra argument.
+** ParseKRARGDECL A version of ParseARGDECL for K&R C.
+** ParseANSIARGDECL A version of ParseARGDECL for ANSI C.
+** YYNSTATE the combined number of states.
+** YYNRULE the number of rules in the grammar
+** YYERRORSYMBOL is the code number of the error symbol. If not
+** defined, then do no error processing.
+*/
+%%
+#define YY_NO_ACTION (YYNSTATE+YYNRULE+2)
+#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1)
+#define YY_ERROR_ACTION (YYNSTATE+YYNRULE)
+/* Next is the action table. Each entry in this table contains
+**
+** + An integer which is the number representing the look-ahead
+** token
+**
+** + An integer indicating what action to take. Number (N) between
+** 0 and YYNSTATE-1 mean shift the look-ahead and go to state N.
+** Numbers between YYNSTATE and YYNSTATE+YYNRULE-1 mean reduce by
+** rule N-YYNSTATE. Number YYNSTATE+YYNRULE means that a syntax
+** error has occurred. Number YYNSTATE+YYNRULE+1 means the parser
+** accepts its input.
+**
+** + A pointer to the next entry with the same hash value.
+**
+** The action table is really a series of hash tables. Each hash
+** table contains a number of entries which is a power of two. The
+** "state" table (which follows) contains information about the starting
+** point and size of each hash table.
+*/
+struct yyActionEntry {
+ YYCODETYPE lookahead; /* The value of the look-ahead token */
+ YYACTIONTYPE action; /* Action to take for this look-ahead */
+ struct yyActionEntry *next; /* Next look-ahead with the same hash, or NULL */
+};
+static struct yyActionEntry yyActionTable[] = {
+%%
+};
+
+/* The state table contains information needed to look up the correct
+** action in the action table, given the current state of the parser.
+** Information needed includes:
+**
+** + A pointer to the start of the action hash table in yyActionTable.
+**
+** + A mask used to hash the look-ahead token. The mask is an integer
+** which is one less than the size of the hash table.
+**
+** + The default action. This is the action to take if no entry for
+** the given look-ahead is found in the action hash table.
+*/
+struct yyStateEntry {
+ struct yyActionEntry *hashtbl; /* Start of the hash table in yyActionTable */
+ int mask; /* Mask used for hashing the look-ahead */
+ YYACTIONTYPE actionDefault; /* Default action if look-ahead not found */
+};
+static struct yyStateEntry yyStateTable[] = {
+%%
+};
+
+/* The following structure represents a single element of the
+** parser's stack. Information stored includes:
+**
+** + The state number for the parser at this level of the stack.
+**
+** + The value of the token stored at this level of the stack.
+** (In other words, the "major" token.)
+**
+** + The semantic value stored at this level of the stack. This is
+** the information used by the action routines in the grammar.
+** It is sometimes called the "minor" token.
+*/
+struct yyStackEntry {
+ int stateno; /* The state-number */
+ int major; /* The major token value. This is the code
+ ** number for the token at this stack level */
+ YYMINORTYPE minor; /* The user-supplied minor token value. This
+ ** is the value of the token */
+};
+
+/* The state of the parser is completely contained in an instance of
+** the following structure */
+struct yyParser {
+ int idx; /* Index of top element in stack */
+ int errcnt; /* Shifts left before out of the error */
+ struct yyStackEntry *top; /* Pointer to the top stack element */
+ struct yyStackEntry stack[YYSTACKDEPTH]; /* The parser's stack */
+};
+typedef struct yyParser yyParser;
+
+#ifndef NDEBUG
+#include
+static FILE *yyTraceFILE = 0;
+static char *yyTracePrompt = 0;
+
+/*
+** Turn parser tracing on by giving a stream to which to write the trace
+** and a prompt to preface each trace message. Tracing is turned off
+** by making either argument NULL
+**
+** Inputs:
+**
+** - A FILE* to which trace output should be written.
+** If NULL, then tracing is turned off.
+**
- A prefix string written at the beginning of every
+** line of trace output. If NULL, then tracing is
+** turned off.
+**
+**
+** Outputs:
+** None.
+*/
+/* SQLITE MODIFICATION: Give the function file scope */
+void ParseTrace(FILE *TraceFILE, char *zTracePrompt){
+ yyTraceFILE = TraceFILE;
+ yyTracePrompt = zTracePrompt;
+ if( yyTraceFILE==0 ) yyTracePrompt = 0;
+ else if( yyTracePrompt==0 ) yyTraceFILE = 0;
+}
+
+/* For tracing shifts, the names of all terminals and nonterminals
+** are required. The following table supplies these names */
+static char *yyTokenName[] = {
+%%
+};
+#define YYTRACE(X) if( yyTraceFILE ) fprintf(yyTraceFILE,"%sReduce [%s].\n",yyTracePrompt,X);
+#else
+#define YYTRACE(X)
+#endif
+
+/*
+** This function allocates a new parser.
+** The only argument is a pointer to a function which works like
+** malloc.
+**
+** Inputs:
+** A pointer to the function used to allocate memory.
+**
+** Outputs:
+** A pointer to a parser. This pointer is used in subsequent calls
+** to Parse and ParseFree.
+*/
+/* SQLITE MODIFICATION: Give the function file scope */
+void *ParseAlloc(void *(*mallocProc)()){
+ yyParser *pParser;
+ pParser = (yyParser*)(*mallocProc)( sizeof(yyParser), __FILE__, __LINE__ );
+ if( pParser ){
+ pParser->idx = -1;
+ }
+ return pParser;
+}
+
+/* The following function deletes the value associated with a
+** symbol. The symbol can be either a terminal or nonterminal.
+** "yymajor" is the symbol code, and "yypminor" is a pointer to
+** the value.
+*/
+static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){
+ switch( yymajor ){
+ /* Here is inserted the actions which take place when a
+ ** terminal or non-terminal is destroyed. This can happen
+ ** when the symbol is popped from the stack during a
+ ** reduce or during error processing or when a parser is
+ ** being destroyed before it is finished parsing.
+ **
+ ** Note: during a reduce, the only symbols destroyed are those
+ ** which appear on the RHS of the rule, but which are not used
+ ** inside the C code.
+ */
+%%
+ default: break; /* If no destructor action specified: do nothing */
+ }
+}
+
+/*
+** Pop the parser's stack once.
+**
+** If there is a destructor routine associated with the token which
+** is popped from the stack, then call it.
+**
+** Return the major token number for the symbol popped.
+*/
+static int yy_pop_parser_stack(yyParser *pParser){
+ YYCODETYPE yymajor;
+
+ if( pParser->idx<0 ) return 0;
+#ifndef NDEBUG
+ if( yyTraceFILE && pParser->idx>=0 ){
+ fprintf(yyTraceFILE,"%sPopping %s\n",
+ yyTracePrompt,
+ yyTokenName[pParser->top->major]);
+ }
+#endif
+ yymajor = pParser->top->major;
+ yy_destructor( yymajor, &pParser->top->minor);
+ pParser->idx--;
+ pParser->top--;
+ return yymajor;
+}
+
+/*
+** Deallocate and destroy a parser. Destructors are all called for
+** all stack elements before shutting the parser down.
+**
+** Inputs:
+**
+** - A pointer to the parser. This should be a pointer
+** obtained from ParseAlloc.
+**
- A pointer to a function used to reclaim memory obtained
+** from malloc.
+**
+*/
+/* SQLITE MODIFICATION: Give the function file scope */
+void ParseFree(
+ void *p, /* The parser to be deleted */
+ void (*freeProc)() /* Function used to reclaim memory */
+){
+ yyParser *pParser = (yyParser*)p;
+ if( pParser==0 ) return;
+ while( pParser->idx>=0 ) yy_pop_parser_stack(pParser);
+ (*freeProc)(pParser, __FILE__, __LINE__);
+}
+
+/*
+** Find the appropriate action for a parser given the look-ahead token.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead. If it is, return the action, otherwise
+** return YY_NO_ACTION.
+*/
+static int yy_find_parser_action(
+ yyParser *pParser, /* The parser */
+ int iLookAhead /* The look-ahead token */
+){
+ struct yyStateEntry *pState; /* Appropriate entry in the state table */
+ struct yyActionEntry *pAction; /* Action appropriate for the look-ahead */
+
+ /* if( pParser->idx<0 ) return YY_NO_ACTION; */
+ pState = &yyStateTable[pParser->top->stateno];
+ if( iLookAhead!=YYNOCODE ){
+ pAction = &pState->hashtbl[iLookAhead & pState->mask];
+ while( pAction ){
+ if( pAction->lookahead==iLookAhead ) return pAction->action;
+ pAction = pAction->next;
+ }
+ }else if( pState->mask!=0 || pState->hashtbl->lookahead!=YYNOCODE ){
+ return YY_NO_ACTION;
+ }
+ return pState->actionDefault;
+}
+
+/*
+** Perform a shift action.
+*/
+static void yy_shift(
+ yyParser *yypParser, /* The parser to be shifted */
+ int yyNewState, /* The new state to shift in */
+ int yyMajor, /* The major token to shift in */
+ YYMINORTYPE *yypMinor /* Pointer ot the minor token to shift in */
+){
+ yypParser->idx++;
+ yypParser->top++;
+ if( yypParser->idx>=YYSTACKDEPTH ){
+ yypParser->idx--;
+ yypParser->top--;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->idx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will execute if the parser
+ ** stack every overflows */
+%%
+ return;
+ }
+ yypParser->top->stateno = yyNewState;
+ yypParser->top->major = yyMajor;
+ yypParser->top->minor = *yypMinor;
+#ifndef NDEBUG
+ if( yyTraceFILE && yypParser->idx>0 ){
+ int i;
+ fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
+ fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
+ for(i=1; i<=yypParser->idx; i++)
+ fprintf(yyTraceFILE," %s",yyTokenName[yypParser->stack[i].major]);
+ fprintf(yyTraceFILE,"\n");
+ }
+#endif
+}
+
+/* The following table contains information about every rule that
+** is used during the reduce.
+*/
+static struct {
+ YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
+ unsigned char nrhs; /* Number of right-hand side symbols in the rule */
+} yyRuleInfo[] = {
+%%
+};
+
+static void yy_accept(); /* Forward declaration */
+
+/*
+** Perform a reduce action and the shift that must immediately
+** follow the reduce.
+*/
+static void yy_reduce(
+ yyParser *yypParser, /* The parser */
+ int yyruleno /* Number of the rule by which to reduce */
+ ParseANSIARGDECL
+){
+ int yygoto; /* The next state */
+ int yyact; /* The next action */
+ YYMINORTYPE yygotominor; /* The LHS of the rule reduced */
+ struct yyStackEntry *yymsp; /* The top of the parser's stack */
+ int yysize; /* Amount to pop the stack */
+ yymsp = yypParser->top;
+ switch( yyruleno ){
+ /* Beginning here are the reduction cases. A typical example
+ ** follows:
+ ** case 0:
+ ** YYTRACE("");
+ ** #line
+ ** { ... } // User supplied code
+ ** #line
+ ** break;
+ */
+%%
+ };
+ yygoto = yyRuleInfo[yyruleno].lhs;
+ yysize = yyRuleInfo[yyruleno].nrhs;
+ yypParser->idx -= yysize;
+ yypParser->top -= yysize;
+ yyact = yy_find_parser_action(yypParser,yygoto);
+ if( yyact < YYNSTATE ){
+ yy_shift(yypParser,yyact,yygoto,&yygotominor);
+ }else if( yyact == YYNSTATE + YYNRULE + 1 ){
+ yy_accept(yypParser ParseARGDECL);
+ }
+}
+
+/*
+** The following code executes when the parse fails
+*/
+static void yy_parse_failed(
+ yyParser *yypParser /* The parser */
+ ParseANSIARGDECL /* Extra arguments (if any) */
+){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->idx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will be executed whenever the
+ ** parser fails */
+%%
+}
+
+/*
+** The following code executes when a syntax error first occurs.
+*/
+static void yy_syntax_error(
+ yyParser *yypParser, /* The parser */
+ int yymajor, /* The major type of the error token */
+ YYMINORTYPE yyminor /* The minor type of the error token */
+ ParseANSIARGDECL /* Extra arguments (if any) */
+){
+#define TOKEN (yyminor.yy0)
+%%
+}
+
+/*
+** The following is executed when the parser accepts
+*/
+static void yy_accept(
+ yyParser *yypParser /* The parser */
+ ParseANSIARGDECL /* Extra arguments (if any) */
+){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->idx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will be executed whenever the
+ ** parser accepts */
+%%
+}
+
+/* The main parser program.
+** The first argument is a pointer to a structure obtained from
+** "ParseAlloc" which describes the current state of the parser.
+** The second argument is the major token number. The third is
+** the minor token. The fourth optional argument is whatever the
+** user wants (and specified in the grammar) and is available for
+** use by the action routines.
+**
+** Inputs:
+**
+** - A pointer to the parser (an opaque structure.)
+**
- The major token number.
+**
- The minor token number.
+**
- An option argument of a grammar-specified type.
+**
+**
+** Outputs:
+** None.
+*/
+/* SQLITE MODIFICATION: Give the function file scope */
+void Parse(
+ void *yyp, /* The parser */
+ int yymajor, /* The major token code number */
+ ParseTOKENTYPE yyminor /* The value for the token */
+ ParseANSIARGDECL
+){
+ YYMINORTYPE yyminorunion;
+ int yyact; /* The parser action. */
+ int yyendofinput; /* True if we are at the end of input */
+ int yyerrorhit = 0; /* True if yymajor has invoked an error */
+ yyParser *yypParser; /* The parser */
+
+ /* (re)initialize the parser, if necessary */
+ yypParser = (yyParser*)yyp;
+ if( yypParser->idx<0 ){
+ if( yymajor==0 ) return;
+ yypParser->idx = 0;
+ yypParser->errcnt = -1;
+ yypParser->top = &yypParser->stack[0];
+ yypParser->top->stateno = 0;
+ yypParser->top->major = 0;
+ }
+ yyminorunion.yy0 = yyminor;
+ yyendofinput = (yymajor==0);
+
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]);
+ }
+#endif
+
+ do{
+ yyact = yy_find_parser_action(yypParser,yymajor);
+ if( yyacterrcnt--;
+ if( yyendofinput && yypParser->idx>=0 ){
+ yymajor = 0;
+ }else{
+ yymajor = YYNOCODE;
+ }
+ }else if( yyact < YYNSTATE + YYNRULE ){
+ yy_reduce(yypParser,yyact-YYNSTATE ParseARGDECL);
+ }else if( yyact == YY_ERROR_ACTION ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
+ }
+#endif
+#ifdef YYERRORSYMBOL
+ /* A syntax error has occurred.
+ ** The response to an error depends upon whether or not the
+ ** grammar defines an error token "ERROR".
+ **
+ ** This is what we do if the grammar does define ERROR:
+ **
+ ** * Call the %syntax_error function.
+ **
+ ** * Begin popping the stack until we enter a state where
+ ** it is legal to shift the error symbol, then shift
+ ** the error symbol.
+ **
+ ** * Set the error count to three.
+ **
+ ** * Begin accepting and shifting new tokens. No new error
+ ** processing will occur until three tokens have been
+ ** shifted successfully.
+ **
+ */
+ if( yypParser->errcnt<0 ){
+ yy_syntax_error(yypParser,yymajor,yyminorunion ParseARGDECL);
+ }
+ if( yypParser->top->major==YYERRORSYMBOL || yyerrorhit ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sDiscard input token %s\n",
+ yyTracePrompt,yyTokenName[yymajor]);
+ }
+#endif
+ yy_destructor(yymajor,&yyminorunion);
+ yymajor = YYNOCODE;
+ }else{
+ while(
+ yypParser->idx >= 0 &&
+ yypParser->top->major != YYERRORSYMBOL &&
+ (yyact = yy_find_parser_action(yypParser,YYERRORSYMBOL)) >= YYNSTATE
+ ){
+ yy_pop_parser_stack(yypParser);
+ }
+ if( yypParser->idx < 0 || yymajor==0 ){
+ yy_destructor(yymajor,&yyminorunion);
+ yy_parse_failed(yypParser ParseARGDECL);
+ yymajor = YYNOCODE;
+ }else if( yypParser->top->major!=YYERRORSYMBOL ){
+ YYMINORTYPE u2;
+ u2.YYERRSYMDT = 0;
+ yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2);
+ }
+ }
+ yypParser->errcnt = 3;
+ yyerrorhit = 1;
+#else /* YYERRORSYMBOL is not defined */
+ /* This is what we do if the grammar does not define ERROR:
+ **
+ ** * Report an error message, and throw away the input token.
+ **
+ ** * If the input token is $, then fail the parse.
+ **
+ ** As before, subsequent error messages are suppressed until
+ ** three input tokens have been successfully shifted.
+ */
+ if( yypParser->errcnt<=0 ){
+ yy_syntax_error(yypParser,yymajor,yyminorunion ParseARGDECL);
+ }
+ yypParser->errcnt = 3;
+ yy_destructor(yymajor,&yyminorunion);
+ if( yyendofinput ){
+ yy_parse_failed(yypParser ParseARGDECL);
+ }
+ yymajor = YYNOCODE;
+#endif
+ }else{
+ yy_accept(yypParser ParseARGDECL);
+ yymajor = YYNOCODE;
+ }
+ }while( yymajor!=YYNOCODE && yypParser->idx>=0 );
+ return;
+}
diff --git a/tool/opNames.awk b/tool/opNames.awk
new file mode 100644
index 0000000000..1150c5aaee
--- /dev/null
+++ b/tool/opNames.awk
@@ -0,0 +1,23 @@
+# Read the sqliteVdbe.h file and generate a table of opcode names.
+#
+BEGIN {
+ printf "static char *zOpName[] = { 0,\n"
+ n = 0
+}
+/^#define OP_MAX/ {
+ next
+}
+/^#define OP_/ {
+ name = "\"" substr($2,4) "\","
+ if( n<3 ){
+ printf " %-16s", name
+ n++
+ } else {
+ printf " %s\n", name
+ n = 0
+ }
+}
+END {
+ if( n ){ printf "\n" }
+ printf "};\n"
+}
diff --git a/tool/opcodeDoc.awk b/tool/opcodeDoc.awk
new file mode 100644
index 0000000000..492010624f
--- /dev/null
+++ b/tool/opcodeDoc.awk
@@ -0,0 +1,23 @@
+#
+# Extract opcode documentation for sqliteVdbe.c and generate HTML
+#
+BEGIN {
+ print ""
+ print "SQLite Virtual Database Engine Opcodes
"
+ print ""
+}
+/ Opcode: /,/\*\// {
+ if( $2=="Opcode:" ){
+ printf "%s %s %s %s | \n\n", $3, $4, $5, $6
+ }else if( $1=="*/" ){
+ printf " |
\n"
+ }else if( NF>1 ){
+ sub(/^ *\*\* /,"")
+ gsub(/,"<")
+ gsub(/&/,"&")
+ print
+ }
+}
+END {
+ print "
"
+}
diff --git a/tool/renumberOps.awk b/tool/renumberOps.awk
new file mode 100644
index 0000000000..098c4d86d7
--- /dev/null
+++ b/tool/renumberOps.awk
@@ -0,0 +1,12 @@
+# Run this script on sqliteVdbe.h to renumber the opcodes sequentially.
+#
+BEGIN { cnt = 1 }
+/^#define OP_MAX/ {
+ printf "#define %-20s %3d\n",$2, cnt-1
+ next
+}
+/^#define OP_/ {
+ printf "#define %-20s %3d\n",$2, cnt++
+ next
+}
+{ print }