diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2013-12-25 15:59:16 +0000 |
---|---|---|
committer | <> | 2015-02-03 11:29:43 +0000 |
commit | 5919c67c0cc46fea1ad0f884c04d7ea8a463fce7 (patch) | |
tree | 860f08eda66df9272df23fe4ba0f79e26560ea88 /src | |
download | gdbm-tarball-e5faeaaf75ecfb705a9b643b3e4cb881ebb69f48.tar.gz |
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 96 | ||||
-rw-r--r-- | src/Makefile.in | 965 | ||||
-rw-r--r-- | src/base64.c | 127 | ||||
-rw-r--r-- | src/bucket.c | 354 | ||||
-rw-r--r-- | src/datconv.c | 442 | ||||
-rw-r--r-- | src/err.c | 66 | ||||
-rw-r--r-- | src/falloc.c | 470 | ||||
-rw-r--r-- | src/findkey.c | 145 | ||||
-rw-r--r-- | src/fullio.c | 70 | ||||
-rw-r--r-- | src/gdbm.h | 189 | ||||
-rw-r--r-- | src/gdbm.h.in | 189 | ||||
-rw-r--r-- | src/gdbm_dump.c | 132 | ||||
-rw-r--r-- | src/gdbm_load.c | 317 | ||||
-rw-r--r-- | src/gdbmapp.h | 63 | ||||
-rw-r--r-- | src/gdbmclose.c | 61 | ||||
-rw-r--r-- | src/gdbmconst.h | 52 | ||||
-rw-r--r-- | src/gdbmcount.c | 65 | ||||
-rw-r--r-- | src/gdbmdefs.h | 225 | ||||
-rw-r--r-- | src/gdbmdelete.c | 106 | ||||
-rw-r--r-- | src/gdbmdump.c | 195 | ||||
-rw-r--r-- | src/gdbmerrno.c | 73 | ||||
-rw-r--r-- | src/gdbmexists.c | 34 | ||||
-rw-r--r-- | src/gdbmexp.c | 125 | ||||
-rw-r--r-- | src/gdbmfdesc.c | 30 | ||||
-rw-r--r-- | src/gdbmfetch.c | 63 | ||||
-rw-r--r-- | src/gdbmimp.c | 157 | ||||
-rw-r--r-- | src/gdbmload.c | 635 | ||||
-rw-r--r-- | src/gdbmopen.c | 475 | ||||
-rw-r--r-- | src/gdbmreorg.c | 218 | ||||
-rw-r--r-- | src/gdbmseq.c | 129 | ||||
-rw-r--r-- | src/gdbmsetopt.c | 258 | ||||
-rw-r--r-- | src/gdbmstore.c | 155 | ||||
-rw-r--r-- | src/gdbmsync.c | 37 | ||||
-rw-r--r-- | src/gdbmtool.c | 1638 | ||||
-rw-r--r-- | src/gdbmtool.h | 263 | ||||
-rw-r--r-- | src/gettext.h | 281 | ||||
-rw-r--r-- | src/gram.c | 2025 | ||||
-rw-r--r-- | src/gram.h | 110 | ||||
-rw-r--r-- | src/gram.y | 368 | ||||
-rw-r--r-- | src/hash.c | 47 | ||||
-rw-r--r-- | src/lex.c | 2463 | ||||
-rw-r--r-- | src/lex.l | 568 | ||||
-rw-r--r-- | src/lock.c | 154 | ||||
-rw-r--r-- | src/mem.c | 74 | ||||
-rw-r--r-- | src/mmap.c | 366 | ||||
-rw-r--r-- | src/parseopt.c | 585 | ||||
-rw-r--r-- | src/progname.c | 35 | ||||
-rw-r--r-- | src/proto.h | 77 | ||||
-rw-r--r-- | src/systems.h | 81 | ||||
-rw-r--r-- | src/update.c | 114 | ||||
-rw-r--r-- | src/util.c | 127 | ||||
-rw-r--r-- | src/var.c | 386 | ||||
-rw-r--r-- | src/version.c | 57 |
53 files changed, 16537 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..6a05634 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,96 @@ +# This file is part of GDBM. -*- Makefile -*- +# Copyright (C) 2007, 2011 Free Software Foundation, Inc. +# +# GDBM is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. + +# Flags +AM_CPPFLAGS=-DIN_GDBM -DLOCALEDIR=\"$(localedir)\" + +# Headers +include_HEADERS = gdbm.h +noinst_HEADERS = \ + gdbmconst.h\ + gdbmdefs.h\ + gettext.h\ + proto.h\ + systems.h + +EXTRA_DIST = gdbm.h.in gram.h + +# The libraries +VI_CURRENT = 4 +VI_REVISION = 0 +VI_AGE = 0 + +lib_LTLIBRARIES = libgdbm.la +libgdbm_la_LIBADD = @LTLIBINTL@ + +libgdbm_la_SOURCES = \ + gdbmclose.c\ + gdbmcount.c\ + gdbmdelete.c\ + gdbmdump.c\ + gdbmerrno.c\ + gdbmexists.c\ + gdbmexp.c\ + gdbmfdesc.c\ + gdbmfetch.c\ + gdbmload.c\ + gdbmopen.c\ + gdbmimp.c\ + gdbmreorg.c\ + gdbmseq.c\ + gdbmsetopt.c\ + gdbmstore.c\ + gdbmsync.c\ + base64.c\ + bucket.c\ + falloc.c\ + findkey.c\ + fullio.c\ + hash.c\ + lock.c\ + mmap.c\ + update.c\ + version.c + +libgdbm_la_LDFLAGS = -version-info $(VI_CURRENT):$(VI_REVISION):$(VI_AGE) + +noinst_LIBRARIES = libgdbmapp.a + +libgdbmapp_a_SOURCES =\ + err.c\ + mem.c\ + gdbmapp.h\ + parseopt.c\ + progname.c + +# Programs +bin_PROGRAMS = gdbmtool gdbm_load gdbm_dump + +gdbmtool_LDADD = ./libgdbmapp.a ./libgdbm.la +gdbmtool_SOURCES = \ + datconv.c\ + gram.y\ + lex.l\ + gdbmtool.h\ + gdbmtool.c\ + var.c\ + util.c + +AM_YFLAGS = -dtv +#AM_LFLAGS = -d + +gdbm_load_LDADD = ./libgdbmapp.a ./libgdbm.la +gdbm_dump_LDADD = ./libgdbmapp.a ./libgdbm.la diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..dd43081 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,965 @@ +# Makefile.in generated by automake 1.14 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# This file is part of GDBM. -*- Makefile -*- +# Copyright (C) 2007, 2011 Free Software Foundation, Inc. +# +# GDBM is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. + + + + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = gdbmtool$(EXEEXT) gdbm_load$(EXEEXT) gdbm_dump$(EXEEXT) +subdir = src +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/gdbm.h.in gram.c lex.c \ + $(top_srcdir)/build-aux/depcomp $(top_srcdir)/build-aux/ylwrap \ + $(include_HEADERS) $(noinst_HEADERS) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/longlong.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/autoconf.h +CONFIG_CLEAN_FILES = gdbm.h +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libgdbmapp_a_AR = $(AR) $(ARFLAGS) +libgdbmapp_a_LIBADD = +am_libgdbmapp_a_OBJECTS = err.$(OBJEXT) mem.$(OBJEXT) \ + parseopt.$(OBJEXT) progname.$(OBJEXT) +libgdbmapp_a_OBJECTS = $(am_libgdbmapp_a_OBJECTS) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ + "$(DESTDIR)$(includedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +libgdbm_la_DEPENDENCIES = +am_libgdbm_la_OBJECTS = gdbmclose.lo gdbmcount.lo gdbmdelete.lo \ + gdbmdump.lo gdbmerrno.lo gdbmexists.lo gdbmexp.lo gdbmfdesc.lo \ + gdbmfetch.lo gdbmload.lo gdbmopen.lo gdbmimp.lo gdbmreorg.lo \ + gdbmseq.lo gdbmsetopt.lo gdbmstore.lo gdbmsync.lo base64.lo \ + bucket.lo falloc.lo findkey.lo fullio.lo hash.lo lock.lo \ + mmap.lo update.lo version.lo +libgdbm_la_OBJECTS = $(am_libgdbm_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libgdbm_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libgdbm_la_LDFLAGS) $(LDFLAGS) -o $@ +PROGRAMS = $(bin_PROGRAMS) +gdbm_dump_SOURCES = gdbm_dump.c +gdbm_dump_OBJECTS = gdbm_dump.$(OBJEXT) +gdbm_dump_DEPENDENCIES = ./libgdbmapp.a ./libgdbm.la +gdbm_load_SOURCES = gdbm_load.c +gdbm_load_OBJECTS = gdbm_load.$(OBJEXT) +gdbm_load_DEPENDENCIES = ./libgdbmapp.a ./libgdbm.la +am_gdbmtool_OBJECTS = datconv.$(OBJEXT) gram.$(OBJEXT) lex.$(OBJEXT) \ + gdbmtool.$(OBJEXT) var.$(OBJEXT) util.$(OBJEXT) +gdbmtool_OBJECTS = $(am_gdbmtool_OBJECTS) +gdbmtool_DEPENDENCIES = ./libgdbmapp.a ./libgdbm.la +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS) +LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS) +AM_V_LEX = $(am__v_LEX_@AM_V@) +am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@) +am__v_LEX_0 = @echo " LEX " $@; +am__v_LEX_1 = +YLWRAP = $(top_srcdir)/build-aux/ylwrap +am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \ + -e s/c++$$/h++/ -e s/c$$/h/ +YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS) +LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS) +AM_V_YACC = $(am__v_YACC_@AM_V@) +am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@) +am__v_YACC_0 = @echo " YACC " $@; +am__v_YACC_1 = +SOURCES = $(libgdbmapp_a_SOURCES) $(libgdbm_la_SOURCES) gdbm_dump.c \ + gdbm_load.c $(gdbmtool_SOURCES) +DIST_SOURCES = $(libgdbmapp_a_SOURCES) $(libgdbm_la_SOURCES) \ + gdbm_dump.c gdbm_load.c $(gdbmtool_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(include_HEADERS) $(noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOM4TE = @AUTOM4TE@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GDBM183_INCLUDEDIR = @GDBM183_INCLUDEDIR@ +GDBM183_LIBDIR = @GDBM183_LIBDIR@ +GDBM183_LIBRARY = @GDBM183_LIBRARY@ +GDBM_COUNT_T = @GDBM_COUNT_T@ +GDBM_VERSION_MAJOR = @GDBM_VERSION_MAJOR@ +GDBM_VERSION_MINOR = @GDBM_VERSION_MINOR@ +GDBM_VERSION_PATCH = @GDBM_VERSION_PATCH@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +POSUB = @POSUB@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# Flags +AM_CPPFLAGS = -DIN_GDBM -DLOCALEDIR=\"$(localedir)\" + +# Headers +include_HEADERS = gdbm.h +noinst_HEADERS = \ + gdbmconst.h\ + gdbmdefs.h\ + gettext.h\ + proto.h\ + systems.h + +EXTRA_DIST = gdbm.h.in gram.h + +# The libraries +VI_CURRENT = 4 +VI_REVISION = 0 +VI_AGE = 0 +lib_LTLIBRARIES = libgdbm.la +libgdbm_la_LIBADD = @LTLIBINTL@ +libgdbm_la_SOURCES = \ + gdbmclose.c\ + gdbmcount.c\ + gdbmdelete.c\ + gdbmdump.c\ + gdbmerrno.c\ + gdbmexists.c\ + gdbmexp.c\ + gdbmfdesc.c\ + gdbmfetch.c\ + gdbmload.c\ + gdbmopen.c\ + gdbmimp.c\ + gdbmreorg.c\ + gdbmseq.c\ + gdbmsetopt.c\ + gdbmstore.c\ + gdbmsync.c\ + base64.c\ + bucket.c\ + falloc.c\ + findkey.c\ + fullio.c\ + hash.c\ + lock.c\ + mmap.c\ + update.c\ + version.c + +libgdbm_la_LDFLAGS = -version-info $(VI_CURRENT):$(VI_REVISION):$(VI_AGE) +noinst_LIBRARIES = libgdbmapp.a +libgdbmapp_a_SOURCES = \ + err.c\ + mem.c\ + gdbmapp.h\ + parseopt.c\ + progname.c + +gdbmtool_LDADD = ./libgdbmapp.a ./libgdbm.la +gdbmtool_SOURCES = \ + datconv.c\ + gram.y\ + lex.l\ + gdbmtool.h\ + gdbmtool.c\ + var.c\ + util.c + +AM_YFLAGS = -dtv +#AM_LFLAGS = -d +gdbm_load_LDADD = ./libgdbmapp.a ./libgdbm.la +gdbm_dump_LDADD = ./libgdbmapp.a ./libgdbm.la +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .l .lo .o .obj .y +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnits src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnits src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +gdbm.h: $(top_builddir)/config.status $(srcdir)/gdbm.h.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +libgdbmapp.a: $(libgdbmapp_a_OBJECTS) $(libgdbmapp_a_DEPENDENCIES) $(EXTRA_libgdbmapp_a_DEPENDENCIES) + $(AM_V_at)-rm -f libgdbmapp.a + $(AM_V_AR)$(libgdbmapp_a_AR) libgdbmapp.a $(libgdbmapp_a_OBJECTS) $(libgdbmapp_a_LIBADD) + $(AM_V_at)$(RANLIB) libgdbmapp.a + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libgdbm.la: $(libgdbm_la_OBJECTS) $(libgdbm_la_DEPENDENCIES) $(EXTRA_libgdbm_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgdbm_la_LINK) -rpath $(libdir) $(libgdbm_la_OBJECTS) $(libgdbm_la_LIBADD) $(LIBS) +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +installcheck-binPROGRAMS: $(bin_PROGRAMS) + bad=0; pid=$$$$; list="$(bin_PROGRAMS)"; for p in $$list; do \ + case ' $(AM_INSTALLCHECK_STD_OPTIONS_EXEMPT) ' in \ + *" $$p "* | *" $(srcdir)/$$p "*) continue;; \ + esac; \ + f=`echo "$$p" | \ + sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + for opt in --help --version; do \ + if "$(DESTDIR)$(bindir)/$$f" $$opt >c$${pid}_.out \ + 2>c$${pid}_.err </dev/null \ + && test -n "`cat c$${pid}_.out`" \ + && test -z "`cat c$${pid}_.err`"; then :; \ + else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \ + done; \ + done; rm -f c$${pid}_.???; exit $$bad + +gdbm_dump$(EXEEXT): $(gdbm_dump_OBJECTS) $(gdbm_dump_DEPENDENCIES) $(EXTRA_gdbm_dump_DEPENDENCIES) + @rm -f gdbm_dump$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gdbm_dump_OBJECTS) $(gdbm_dump_LDADD) $(LIBS) + +gdbm_load$(EXEEXT): $(gdbm_load_OBJECTS) $(gdbm_load_DEPENDENCIES) $(EXTRA_gdbm_load_DEPENDENCIES) + @rm -f gdbm_load$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gdbm_load_OBJECTS) $(gdbm_load_LDADD) $(LIBS) + +gdbmtool$(EXEEXT): $(gdbmtool_OBJECTS) $(gdbmtool_DEPENDENCIES) $(EXTRA_gdbmtool_DEPENDENCIES) + @rm -f gdbmtool$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gdbmtool_OBJECTS) $(gdbmtool_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bucket.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/datconv.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/err.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/falloc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/findkey.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fullio.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbm_dump.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbm_load.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmclose.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmcount.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmdelete.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmdump.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmerrno.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmexists.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmexp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmfdesc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmfetch.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmimp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmload.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmopen.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmreorg.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmseq.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmsetopt.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmstore.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmsync.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbmtool.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gram.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lex.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lock.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mem.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmap.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parseopt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/progname.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/update.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/var.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +.l.c: + $(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE) + +.y.c: + $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h `echo $@ | $(am__yacc_c2h)` y.output $*.output -- $(YACCCOMPILE) + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-includeHEADERS: $(include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \ + done + +uninstall-includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) +install-binPROGRAMS: install-libLTLIBRARIES + +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -rm -f gram.c + -rm -f lex.c +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-includeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: installcheck-binPROGRAMS + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-includeHEADERS \ + uninstall-libLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ + clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool clean-noinstLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-includeHEADERS install-info install-info-am \ + install-libLTLIBRARIES install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installcheck-binPROGRAMS installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-includeHEADERS \ + uninstall-libLTLIBRARIES + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/base64.c b/src/base64.c new file mode 100644 index 0000000..a4edf49 --- /dev/null +++ b/src/base64.c @@ -0,0 +1,127 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +# include "autoconf.h" +# include "gdbmdefs.h" +# include "gdbm.h" + +static char b64tab[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static int b64val[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 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, -1, -1, -1, -1, -1, + -1, 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, -1, -1, -1, -1, -1 +}; + +int +_gdbm_base64_encode (const unsigned char *input, size_t input_len, + unsigned char **output, size_t *output_size, + size_t *nbytes) +{ + size_t olen = 4 * (input_len + 2) / 3 + 1; + unsigned char *out; + + if (olen > *output_size) + { + out = realloc (*output, olen); + if (!out) + return GDBM_MALLOC_ERROR; + *output = out; + *output_size = olen; + } + else + out = *output; + + while (input_len >= 3) + { + *out++ = b64tab[input[0] >> 2]; + *out++ = b64tab[((input[0] << 4) & 0x30) | (input[1] >> 4)]; + *out++ = b64tab[((input[1] << 2) & 0x3c) | (input[2] >> 6)]; + *out++ = b64tab[input[2] & 0x3f]; + input_len -= 3; + input += 3; + } + + if (input_len > 0) + { + unsigned char c = (input[0] << 4) & 0x30; + *out++ = b64tab[input[0] >> 2]; + if (input_len > 1) + c |= input[1] >> 4; + *out++ = b64tab[c]; + *out++ = (input_len < 2) ? '=' : b64tab[(input[1] << 2) & 0x3c]; + *out++ = '='; + } + *out = 0; + *nbytes = out - *output; + return 0; +} + +int +_gdbm_base64_decode (const unsigned char *input, size_t input_len, + unsigned char **output, size_t *output_size, + size_t *inbytes, size_t *outbytes) +{ + int rc = 0; + int olen = input_len; + unsigned char *out; + size_t ins = 0; + + if (olen > *output_size) + { + out = realloc (*output, olen); + if (!out) + return GDBM_MALLOC_ERROR; + *output = out; + *output_size = olen; + } + else + out = *output; + + do + { + if (input_len < 4) + break; + if (input[0] > 127 || b64val[input[0]] == -1 + || input[1] > 127 || b64val[input[1]] == -1 + || input[2] > 127 || ((input[2] != '=') && (b64val[input[2]] == -1)) + || input[3] > 127 || ((input[3] != '=') + && (b64val[input[3]] == -1))) + { + rc = GDBM_ILLEGAL_DATA; + break; + } + *out++ = (b64val[input[0]] << 2) | (b64val[input[1]] >> 4); + if (input[2] != '=') + { + *out++ = ((b64val[input[1]] << 4) & 0xf0) | (b64val[input[2]] >> 2); + if (input[3] != '=') + *out++ = ((b64val[input[2]] << 6) & 0xc0) | b64val[input[3]]; + } + input += 4; + input_len -= 4; + ins += 4; + } + while (input_len > 0); + *inbytes = ins; + *outbytes = out - *output; + return rc; +} diff --git a/src/bucket.c b/src/bucket.c new file mode 100644 index 0000000..8bc33f1 --- /dev/null +++ b/src/bucket.c @@ -0,0 +1,354 @@ +/* bucket.c - The routines for playing with hash buckets. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + + +/* Initializing a new hash buckets sets all bucket entries to -1 hash value. */ +void +_gdbm_new_bucket (GDBM_FILE dbf, hash_bucket *bucket, int bits) +{ + int index; + + /* Initialize the avail block. */ + bucket->av_count = 0; + + /* Set the information fields first. */ + bucket->bucket_bits = bits; + bucket->count = 0; + + /* Initialize all bucket elements. */ + for (index = 0; index < dbf->header->bucket_elems; index++) + bucket->h_table[index].hash_value = -1; +} + + + +/* Find a bucket for DBF that is pointed to by the bucket directory from + location DIR_INDEX. The bucket cache is first checked to see if it + is already in memory. If not, a bucket may be tossed to read the new + bucket. In any case, the requested bucket is make the "current" bucket + and dbf->bucket points to the correct bucket. */ + +void +_gdbm_get_bucket (GDBM_FILE dbf, int dir_index) +{ + int rc; + off_t bucket_adr; /* The address of the correct hash bucket. */ + off_t file_pos; /* The return address for lseek. */ + int index; /* Loop index. */ + + /* Initial set up. */ + dbf->bucket_dir = dir_index; + bucket_adr = dbf->dir [dir_index]; + + if (dbf->bucket_cache == NULL) + { + if(_gdbm_init_cache(dbf, DEFAULT_CACHESIZE) == -1) + _gdbm_fatal(dbf, _("couldn't init cache")); + } + + /* Is that one is not already current, we must find it. */ + if (dbf->cache_entry->ca_adr != bucket_adr) + { + /* Look in the cache. */ + for (index = 0; index < dbf->cache_size; index++) + { + if (dbf->bucket_cache[index].ca_adr == bucket_adr) + { + dbf->bucket = dbf->bucket_cache[index].ca_bucket; + dbf->cache_entry = &dbf->bucket_cache[index]; + return; + } + } + + /* It is not in the cache, read it from the disk. */ + dbf->last_read = (dbf->last_read + 1) % dbf->cache_size; + if (dbf->bucket_cache[dbf->last_read].ca_changed) + _gdbm_write_bucket (dbf, &dbf->bucket_cache[dbf->last_read]); + dbf->bucket_cache[dbf->last_read].ca_adr = bucket_adr; + dbf->bucket = dbf->bucket_cache[dbf->last_read].ca_bucket; + dbf->cache_entry = &dbf->bucket_cache[dbf->last_read]; + dbf->cache_entry->ca_data.elem_loc = -1; + dbf->cache_entry->ca_changed = FALSE; + + /* Read the bucket. */ + file_pos = __lseek (dbf, bucket_adr, SEEK_SET); + if (file_pos != bucket_adr) + _gdbm_fatal (dbf, _("lseek error")); + + rc = _gdbm_full_read (dbf, dbf->bucket, dbf->header->bucket_size); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); + } + + return; +} + +int +_gdbm_read_bucket_at (GDBM_FILE dbf, off_t off, hash_bucket *bucket, + size_t size) +{ + off_t file_pos; + int i, rc; + + if (dbf->cache_entry && dbf->cache_entry->ca_adr == off) + { + memcpy (bucket, dbf->bucket, size); + return 0; + } + + /* Look in the cache. */ + for (i = 0; i < dbf->cache_size; i++) + { + if (dbf->bucket_cache[i].ca_adr == off) + { + memcpy (bucket, dbf->bucket_cache[i].ca_bucket, size); + return 0; + } + } + + /* Read the bucket. */ + file_pos = __lseek (dbf, off, SEEK_SET); + if (file_pos != off) + { + gdbm_errno = GDBM_FILE_SEEK_ERROR; + return -1; + } + rc = _gdbm_full_read (dbf, bucket, size); + if (rc) + { + gdbm_errno = rc; + return -1; + } + return 0; +} + +/* Split the current bucket. This includes moving all items in the bucket to + a new bucket. This doesn't require any disk reads because all hash values + are stored in the buckets. Splitting the current bucket may require + doubling the size of the hash directory. */ +void +_gdbm_split_bucket (GDBM_FILE dbf, int next_insert) +{ + hash_bucket *bucket[2]; /* Pointers to the new buckets. */ + + int new_bits; /* The number of bits for the new buckets. */ + int cache_0; /* Location in the cache for the buckets. */ + int cache_1; + off_t adr_0; /* File address of the new bucket 0. */ + off_t adr_1; /* File address of the new bucket 1. */ + avail_elem old_bucket; /* Avail Struct for the old bucket. */ + + off_t dir_start0; /* Used in updating the directory. */ + off_t dir_start1; + off_t dir_end; + + off_t *new_dir; /* Pointer to the new directory. */ + off_t dir_adr; /* Address of the new directory. */ + int dir_size; /* Size of the new directory. */ + off_t old_adr[31]; /* Address of the old directories. */ + int old_size[31]; /* Size of the old directories. */ + int old_count; /* Number of old directories. */ + + int index; /* Used in array indexing. */ + int index1; /* Used in array indexing. */ + int elem_loc; /* Location in new bucket to put element. */ + bucket_element *old_el; /* Pointer into the old bucket. */ + int select; /* Used to index bucket during movement. */ + + + /* No directories are yet old. */ + old_count = 0; + + if (dbf->bucket_cache == NULL) + { + if(_gdbm_init_cache(dbf, DEFAULT_CACHESIZE) == -1) + _gdbm_fatal(dbf, _("couldn't init cache")); + } + + while (dbf->bucket->count == dbf->header->bucket_elems) + { + /* Initialize the "new" buckets in the cache. */ + do + { + dbf->last_read = (dbf->last_read + 1) % dbf->cache_size; + cache_0 = dbf->last_read; + } + while (dbf->bucket_cache[cache_0].ca_bucket == dbf->bucket); + bucket[0] = dbf->bucket_cache[cache_0].ca_bucket; + if (dbf->bucket_cache[cache_0].ca_changed) + _gdbm_write_bucket (dbf, &dbf->bucket_cache[cache_0]); + do + { + dbf->last_read = (dbf->last_read + 1) % dbf->cache_size; + cache_1 = dbf->last_read; + } + while (dbf->bucket_cache[cache_1].ca_bucket == dbf->bucket); + bucket[1] = dbf->bucket_cache[cache_1].ca_bucket; + if (dbf->bucket_cache[cache_1].ca_changed) + _gdbm_write_bucket (dbf, &dbf->bucket_cache[cache_1]); + new_bits = dbf->bucket->bucket_bits+1; + _gdbm_new_bucket (dbf, bucket[0], new_bits); + _gdbm_new_bucket (dbf, bucket[1], new_bits); + adr_0 = _gdbm_alloc (dbf, dbf->header->bucket_size); + dbf->bucket_cache[cache_0].ca_adr = adr_0; + adr_1 = _gdbm_alloc (dbf, dbf->header->bucket_size); + dbf->bucket_cache[cache_1].ca_adr = adr_1; + + /* Double the directory size if necessary. */ + if (dbf->header->dir_bits == dbf->bucket->bucket_bits) + { + dir_size = dbf->header->dir_size * 2; + dir_adr = _gdbm_alloc (dbf, dir_size); + new_dir = (off_t *) malloc (dir_size); + if (new_dir == NULL) _gdbm_fatal (dbf, _("malloc error")); + for (index = 0; index < GDBM_DIR_COUNT (dbf); index++) + { + new_dir[2*index] = dbf->dir[index]; + new_dir[2*index+1] = dbf->dir[index]; + } + + /* Update header. */ + old_adr[old_count] = dbf->header->dir; + dbf->header->dir = dir_adr; + old_size[old_count] = dbf->header->dir_size; + dbf->header->dir_size = dir_size; + dbf->header->dir_bits = new_bits; + old_count++; + + /* Now update dbf. */ + dbf->header_changed = TRUE; + dbf->bucket_dir *= 2; + free (dbf->dir); + dbf->dir = new_dir; + } + + /* Copy all elements in dbf->bucket into the new buckets. */ + for (index = 0; index < dbf->header->bucket_elems; index++) + { + old_el = & (dbf->bucket->h_table[index]); + select = (old_el->hash_value >> (31-new_bits)) & 1; + elem_loc = old_el->hash_value % dbf->header->bucket_elems; + while (bucket[select]->h_table[elem_loc].hash_value != -1) + elem_loc = (elem_loc + 1) % dbf->header->bucket_elems; + bucket[select]->h_table[elem_loc] = *old_el; + bucket[select]->count += 1; + } + + /* Allocate avail space for the bucket[1]. */ + bucket[1]->bucket_avail[0].av_adr + = _gdbm_alloc (dbf, dbf->header->block_size); + bucket[1]->bucket_avail[0].av_size = dbf->header->block_size; + bucket[1]->av_count = 1; + + /* Copy the avail elements in dbf->bucket to bucket[0]. */ + bucket[0]->av_count = dbf->bucket->av_count; + index = 0; + index1 = 0; + if (bucket[0]->av_count == BUCKET_AVAIL) + { + /* The avail is full, move the first one to bucket[1]. */ + _gdbm_put_av_elem (dbf->bucket->bucket_avail[0], + bucket[1]->bucket_avail, + &bucket[1]->av_count, FALSE); + index = 1; + bucket[0]->av_count --; + } + for (; index < dbf->bucket->av_count; index++) + { + bucket[0]->bucket_avail[index1++] = dbf->bucket->bucket_avail[index]; + } + + /* Update the directory. We have new file addresses for both buckets. */ + dir_start1 = (dbf->bucket_dir >> (dbf->header->dir_bits - new_bits)) | 1; + dir_end = (dir_start1 + 1) << (dbf->header->dir_bits - new_bits); + dir_start1 = dir_start1 << (dbf->header->dir_bits - new_bits); + dir_start0 = dir_start1 - (dir_end - dir_start1); + for (index = dir_start0; index < dir_start1; index++) + dbf->dir[index] = adr_0; + for (index = dir_start1; index < dir_end; index++) + dbf->dir[index] = adr_1; + + + /* Set changed flags. */ + dbf->bucket_cache[cache_0].ca_changed = TRUE; + dbf->bucket_cache[cache_1].ca_changed = TRUE; + dbf->bucket_changed = TRUE; + dbf->directory_changed = TRUE; + dbf->second_changed = TRUE; + + /* Update the cache! */ + dbf->bucket_dir = next_insert >> (31-dbf->header->dir_bits); + + /* Invalidate old cache entry. */ + old_bucket.av_adr = dbf->cache_entry->ca_adr; + old_bucket.av_size = dbf->header->bucket_size; + dbf->cache_entry->ca_adr = 0; + dbf->cache_entry->ca_changed = FALSE; + + /* Set dbf->bucket to the proper bucket. */ + if (dbf->dir[dbf->bucket_dir] == adr_0) + { + dbf->bucket = bucket[0]; + dbf->cache_entry = &dbf->bucket_cache[cache_0]; + _gdbm_put_av_elem (old_bucket, + bucket[1]->bucket_avail, + &bucket[1]->av_count, FALSE); + } + else + { + dbf->bucket = bucket[1]; + dbf->cache_entry = &dbf->bucket_cache[cache_1]; + _gdbm_put_av_elem (old_bucket, + bucket[0]->bucket_avail, + &bucket[0]->av_count, FALSE); + } + + } + + /* Get rid of old directories. */ + for (index = 0; index < old_count; index++) + _gdbm_free (dbf, old_adr[index], old_size[index]); +} + + +/* The only place where a bucket is written. CA_ENTRY is the + cache entry containing the bucket to be written. */ + +void +_gdbm_write_bucket (GDBM_FILE dbf, cache_elem *ca_entry) +{ + int rc; + off_t file_pos; /* The return value for lseek. */ + + file_pos = __lseek (dbf, ca_entry->ca_adr, SEEK_SET); + if (file_pos != ca_entry->ca_adr) + _gdbm_fatal (dbf, _("lseek error")); + rc = _gdbm_full_write (dbf, ca_entry->ca_bucket, dbf->header->bucket_size); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); + + ca_entry->ca_changed = FALSE; + ca_entry->ca_data.hash_val = -1; + ca_entry->ca_data.elem_loc = -1; +} diff --git a/src/datconv.c b/src/datconv.c new file mode 100644 index 0000000..c4adfa4 --- /dev/null +++ b/src/datconv.c @@ -0,0 +1,442 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +#include "gdbmtool.h" + +#define DEFFMT(name, type, fmt) \ +static int \ +name (FILE *fp, void *ptr, int size) \ +{ \ + fprintf (fp, fmt, *(type*) ptr); \ + return size; \ +} + +DEFFMT (f_char, char, "%c") +DEFFMT (f_short, short, "%hd") +DEFFMT (f_ushort, unsigned short, "%hu") +DEFFMT (f_int, int, "%d") +DEFFMT (f_uint, unsigned, "%u") +DEFFMT (f_long, long, "%ld") +DEFFMT (f_ulong, unsigned long, "%lu") +DEFFMT (f_llong, long long, "%lld") +DEFFMT (f_ullong, unsigned long long, "%llu") +DEFFMT (f_float, float, "%f") +DEFFMT (f_double, double, "%e") + +static int +f_stringz (FILE *fp, void *ptr, int size) +{ + int sz; + char *s; + + for (sz = 1, s = ptr; *s; s++, sz++) + { + int c; + + if (isprint (*s)) + fputc (*s, fp); + else if ((c = escape (*s))) + fprintf (fp, "\\%c", c); + else + fprintf (fp, "\\%03o", *s); + } + return sz; +} + +static int +f_string (FILE *fp, void *ptr, int size) +{ + int sz; + char *s; + + for (sz = 0, s = ptr; sz < size; s++, sz++) + { + int c; + + if (isprint (*s)) + fputc (*s, fp); + else if ((c = escape (*s))) + fprintf (fp, "\\%c", c); + else + fprintf (fp, "\\%03o", *s); + } + return sz; +} + +int +s_char (struct xdatum *xd, char *str) +{ + xd_store (xd, str, 1); + return 0; +} + +#define DEFNSCAN(name, type, temptype, strto) \ +int \ +name (struct xdatum *xd, char *str) \ +{ \ + temptype n; \ + type t; \ + char *p; \ + \ + errno = 0; \ + n = strto (str, &p, 0); \ + if (*p) \ + return 1; \ + if (errno == ERANGE || (t = n) != n) \ + return 1; \ + xd_store (xd, &n, sizeof (n)); \ + return 0; \ +} + +DEFNSCAN(s_short, short, long, strtol); +DEFNSCAN(s_ushort, unsigned short, unsigned long, strtoul); +DEFNSCAN(s_int, int, long, strtol) +DEFNSCAN(s_uint, unsigned, unsigned long, strtol) +DEFNSCAN(s_long, long, long, strtoul) +DEFNSCAN(s_ulong, unsigned long, unsigned long, strtoul) +DEFNSCAN(s_llong, long long, long long, strtoll) +DEFNSCAN(s_ullong, unsigned long long, unsigned long long, strtoull) + +int +s_double (struct xdatum *xd, char *str) +{ + double d; + char *p; + + errno = 0; + d = strtod (str, &p); + if (errno || *p) + return 1; + xd_store (xd, &d, sizeof (d)); + return 0; +} + +int +s_float (struct xdatum *xd, char *str) +{ + float d; + char *p; + + errno = 0; + d = strtod (str, &p); + if (errno || *p) + return 1; + xd_store (xd, &d, sizeof (d)); + return 0; +} + +int +s_stringz (struct xdatum *xd, char *str) +{ + xd_store (xd, str, strlen (str) + 1); + return 0; +} + +int +s_string (struct xdatum *xd, char *str) +{ + xd_store (xd, str, strlen (str)); + return 0; +} + +static struct datadef datatab[] = { + { "char", sizeof(char), f_char, s_char }, + { "short", sizeof(short), f_short, s_short }, + { "ushort", sizeof(unsigned short), f_ushort, s_ushort }, + { "int", sizeof(int), f_int, s_int }, + { "unsigned", sizeof(unsigned), f_uint, s_uint }, + { "uint", sizeof(unsigned), f_uint, s_uint }, + { "long", sizeof(long), f_long, s_long }, + { "ulong", sizeof(unsigned long), f_ulong, s_ulong }, + { "llong", sizeof(long long), f_llong, s_llong }, + { "ullong", sizeof(unsigned long long), f_ullong, s_ullong }, + { "float", sizeof(float), f_float, s_float }, + { "double", sizeof(double), f_double, s_double }, + { "stringz", 0, f_stringz, s_stringz }, + { "string", 0, f_string, s_string }, + { NULL } +}; + +struct datadef * +datadef_lookup (const char *name) +{ + struct datadef *p; + + for (p = datatab; p->name; p++) + if (strcmp (p->name, name) == 0) + return p; + return NULL; +} + +struct dsegm * +dsegm_new (int type) +{ + struct dsegm *p = emalloc (sizeof (*p)); + p->next = NULL; + p->type = type; + return p; +} + +struct dsegm * +dsegm_new_field (struct datadef *type, char *id, int dim) +{ + struct dsegm *p = dsegm_new (FDEF_FLD); + p->v.field.type = type; + p->v.field.name = id; + p->v.field.dim = dim; + return p; +} + +void +dsegm_free_list (struct dsegm *dp) +{ + while (dp) + { + struct dsegm *next = dp->next; + free (dp); + dp = next; + } +} + +void +datum_format (FILE *fp, datum const *dat, struct dsegm *ds) +{ + int off = 0; + char *delim[2]; + int first_field = 1; + + if (!ds) + { + fprintf (fp, "%.*s\n", dat->dsize, dat->dptr); + return; + } + + if (variable_get ("delim1", VART_STRING, (void*) &delim[0])) + abort (); + if (variable_get ("delim2", VART_STRING, (void*) &delim[1])) + abort (); + + for (; ds && off <= dat->dsize; ds = ds->next) + { + switch (ds->type) + { + case FDEF_FLD: + if (!first_field) + fwrite (delim[1], strlen (delim[1]), 1, fp); + if (ds->v.field.name) + fprintf (fp, "%s=", ds->v.field.name); + if (ds->v.field.dim > 1) + fprintf (fp, "{ "); + if (ds->v.field.type->format) + { + int i, n; + + for (i = 0; i < ds->v.field.dim; i++) + { + if (i) + fwrite (delim[0], strlen (delim[0]), 1, fp); + if (off + ds->v.field.type->size > dat->dsize) + { + fprintf (fp, _("(not enough data)")); + off += dat->dsize; + break; + } + else + { + n = ds->v.field.type->format (fp, + (char*) dat->dptr + off, + ds->v.field.type->size ? + ds->v.field.type->size : + dat->dsize - off); + off += n; + } + } + } + if (ds->v.field.dim > 1) + fprintf (fp, " }"); + first_field = 0; + break; + + case FDEF_OFF: + off = ds->v.n; + break; + + case FDEF_PAD: + off += ds->v.n; + break; + } + } +} + +struct xdatum +{ + char *dptr; + size_t dsize; + size_t dmax; + int off; +}; + +void +xd_expand (struct xdatum *xd, size_t size) +{ + if (xd->dmax < size) + { + xd->dptr = erealloc (xd->dptr, size); + memset (xd->dptr + xd->dmax, 0, size - xd->dmax); + xd->dmax = size; + } +} + +void +xd_store (struct xdatum *xd, void *val, size_t size) +{ + xd_expand (xd, xd->off + size); + memcpy (xd->dptr + xd->off, val, size); + xd->off += size; + if (xd->off > xd->dsize) + xd->dsize = xd->off; +} + +static int +datum_scan_notag (datum *dat, struct dsegm *ds, struct kvpair *kv) +{ + struct xdatum xd; + int i; + struct slist *s; + int err = 0; + + memset (&xd, 0, sizeof (xd)); + + for (; err == 0 && ds && kv; ds = ds->next, kv = kv->next) + { + if (kv->key) + { + lerror (&kv->loc, + _("mixing tagged and untagged values is not allowed")); + err = 1; + break; + } + + switch (ds->type) + { + case FDEF_FLD: + if (!ds->v.field.type->scan) + abort (); + + switch (kv->type) + { + case KV_STRING: + err = ds->v.field.type->scan (&xd, kv->val.s); + if (err) + lerror (&kv->loc, _("cannot convert")); + break; + + case KV_LIST: + for (i = 0, s = kv->val.l; i < ds->v.field.dim && s; + i++, s = s->next) + { + err = ds->v.field.type->scan (&xd, s->str); + if (err) + { + lerror (&kv->loc, + _("cannot convert value #%d: %s"), + i, s->str); + break; + } + } + /* FIXME: Warn if (s) -> "extra data" */ + } + break; + + case FDEF_OFF: + xd_expand (&xd, ds->v.n); + xd.off = ds->v.n; + break; + + case FDEF_PAD: + xd_expand (&xd, xd.off + ds->v.n); + xd.off += ds->v.n; + break; + } + } + + if (err) + { + free (xd.dptr); + return 1; + } + + dat->dptr = xd.dptr; + dat->dsize = xd.dsize; + + return 0; +} + +static int +datum_scan_tag (datum *dat, struct dsegm *ds, struct kvpair *kv) +{ + lerror (&kv->loc, "tagged values are not yet supported"); + return 1; +} + +int +datum_scan (datum *dat, struct dsegm *ds, struct kvpair *kv) +{ + return (kv->key ? datum_scan_tag : datum_scan_notag) (dat, ds, kv); +} + +void +dsprint (FILE *fp, int what, struct dsegm *ds) +{ + static char *dsstr[] = { "key", "content" }; + int delim; + + fprintf (fp, "define %s", dsstr[what]); + if (ds->next) + { + fprintf (fp, " {\n"); + delim = '\t'; + } + else + delim = ' '; + for (; ds; ds = ds->next) + { + switch (ds->type) + { + case FDEF_FLD: + fprintf (fp, "%c%s", delim, ds->v.field.type->name); + if (ds->v.field.name) + fprintf (fp, " %s", ds->v.field.name); + if (ds->v.field.dim > 1) + fprintf (fp, "[%d]", ds->v.field.dim); + break; + + case FDEF_OFF: + fprintf (fp, "%coffset %d", delim, ds->v.n); + break; + + case FDEF_PAD: + fprintf (fp, "%cpad %d", delim, ds->v.n); + break; + } + if (ds->next) + fputc (',', fp); + fputc ('\n', fp); + } + if (delim == '\t') + fputs ("}\n", fp); +} diff --git a/src/err.c b/src/err.c new file mode 100644 index 0000000..db0b87a --- /dev/null +++ b/src/err.c @@ -0,0 +1,66 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +# include "autoconf.h" +# include "gdbm.h" +# include "gdbmapp.h" +# include <stdio.h> +# include <errno.h> +# include <string.h> + +static void +prerror (const char *fmt, va_list ap, const char *diag) +{ + fprintf (stderr, "%s: ", progname); + vfprintf (stderr, fmt, ap); + if (diag) + fprintf (stderr, ": %s", diag); + fputc ('\n', stderr); +} + +void +verror (const char *fmt, va_list ap) +{ + prerror (fmt, ap, NULL); +} + +void +error (const char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + verror (fmt, ap); + va_end (ap); +} + +void +sys_perror (int code, const char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + prerror (fmt, ap, strerror (code)); + va_end (ap); +} + +void +gdbm_perror (const char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + prerror (fmt, ap, gdbm_strerror (gdbm_errno)); + va_end (ap); +} + diff --git a/src/falloc.c b/src/falloc.c new file mode 100644 index 0000000..32aad51 --- /dev/null +++ b/src/falloc.c @@ -0,0 +1,470 @@ +/* falloc.c - The file space management routines for dbm. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 1994, 2007, 2011, 2013 Free Software + Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + + +/* The forward definitions for this file. See the functions for + the definition of the function. */ + +static avail_elem get_elem (int, avail_elem [], int *); +static avail_elem get_block (int, GDBM_FILE); +static void push_avail_block (GDBM_FILE); +static void pop_avail_block (GDBM_FILE); +static void adjust_bucket_avail (GDBM_FILE); + +/* Allocate space in the file DBF for a block NUM_BYTES in length. Return + the file address of the start of the block. + + Each hash bucket has a fixed size avail table. We first check this + avail table to satisfy the request for space. In most cases we can + and this causes changes to be only in the current hash bucket. + Allocation is done on a first fit basis from the entries. If a + request can not be satisfied from the current hash bucket, then it is + satisfied from the file header avail block. If nothing is there that + has enough space, another block at the end of the file is allocated + and the unused portion is returned to the avail block. This routine + "guarantees" that an allocation does not cross a block boundary unless + the size is larger than a single block. The avail structure is + changed by this routine if a change is needed. If an error occurs, + the value of 0 will be returned. */ + +off_t +_gdbm_alloc (GDBM_FILE dbf, int num_bytes) +{ + off_t file_adr; /* The address of the block. */ + avail_elem av_el; /* For temporary use. */ + + /* The current bucket is the first place to look for space. */ + av_el = get_elem (num_bytes, dbf->bucket->bucket_avail, + &dbf->bucket->av_count); + + /* If we did not find some space, we have more work to do. */ + if (av_el.av_size == 0) + { + /* If the header avail table is less than half full, and there's + something on the stack. */ + if ((dbf->header->avail.count <= (dbf->header->avail.size >> 1)) + && (dbf->header->avail.next_block != 0)) + pop_avail_block (dbf); + + /* check the header avail table next */ + av_el = get_elem (num_bytes, dbf->header->avail.av_table, + &dbf->header->avail.count); + if (av_el.av_size == 0) + /* Get another full block from end of file. */ + av_el = get_block (num_bytes, dbf); + + dbf->header_changed = TRUE; + } + + /* We now have the place from which we will allocate the new space. */ + file_adr = av_el.av_adr; + + /* Put the unused space back in the avail block. */ + av_el.av_adr += num_bytes; + av_el.av_size -= num_bytes; + _gdbm_free (dbf, av_el.av_adr, av_el.av_size); + + /* Return the address. */ + return file_adr; + +} + + + +/* Free space of size NUM_BYTES in the file DBF at file address FILE_ADR. Make + it avaliable for reuse through _gdbm_alloc. This routine changes the + avail structure. */ + +void +_gdbm_free (GDBM_FILE dbf, off_t file_adr, int num_bytes) +{ + avail_elem temp; + + /* Is it too small to worry about? */ + if (num_bytes <= IGNORE_SIZE) + return; + + /* Initialize the avail element. */ + temp.av_size = num_bytes; + temp.av_adr = file_adr; + + /* Is the freed space large or small? */ + if ((num_bytes >= dbf->header->block_size) || dbf->central_free) + { + if (dbf->header->avail.count == dbf->header->avail.size) + { + push_avail_block (dbf); + } + _gdbm_put_av_elem (temp, dbf->header->avail.av_table, + &dbf->header->avail.count, dbf->coalesce_blocks); + dbf->header_changed = TRUE; + } + else + { + /* Try to put into the current bucket. */ + if (dbf->bucket->av_count < BUCKET_AVAIL) + _gdbm_put_av_elem (temp, dbf->bucket->bucket_avail, + &dbf->bucket->av_count, dbf->coalesce_blocks); + else + { + if (dbf->header->avail.count == dbf->header->avail.size) + { + push_avail_block (dbf); + } + _gdbm_put_av_elem (temp, dbf->header->avail.av_table, + &dbf->header->avail.count, dbf->coalesce_blocks); + dbf->header_changed = TRUE; + } + } + + if (dbf->header_changed) + adjust_bucket_avail (dbf); + + /* All work is done. */ + return; +} + + + +/* The following are all utility routines needed by the previous two. */ + + +/* Gets the avail block at the top of the stack and loads it into the + active avail block. It does a "free" for itself! This can (and is) + now called even when the avail block is not empty, so we must be + smart about things. */ + +static void +pop_avail_block (GDBM_FILE dbf) +{ + int rc; + off_t file_pos; /* For use with the lseek system call. */ + avail_elem new_el; + avail_block *new_blk; + int index; + + if (dbf->header->avail.count == dbf->header->avail.size) + { + /* We're kind of stuck here, so we re-split the header in order to + avoid crashing. Sigh. */ + push_avail_block(dbf); + } + + /* Set up variables. */ + new_el.av_adr = dbf->header->avail.next_block; + new_el.av_size = ( ( (dbf->header->avail.size * sizeof (avail_elem)) >> 1) + + sizeof (avail_block)); + + /* Allocate space for the block. */ + new_blk = (avail_block *) malloc (new_el.av_size); + if (new_blk == NULL) _gdbm_fatal(dbf, _("malloc failed")); + + /* Read the block. */ + file_pos = __lseek (dbf, new_el.av_adr, SEEK_SET); + if (file_pos != new_el.av_adr) _gdbm_fatal (dbf, _("lseek error")); + rc = _gdbm_full_read (dbf, new_blk, new_el.av_size); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); + + /* Add the elements from the new block to the header. */ + index = 0; + while (index < new_blk->count) + { + while(index < new_blk->count + && dbf->header->avail.count < dbf->header->avail.size) + { + /* With luck, this will merge a lot of blocks! */ + _gdbm_put_av_elem(new_blk->av_table[index], + dbf->header->avail.av_table, + &dbf->header->avail.count, TRUE); + index++; + } + if (dbf->header->avail.count == dbf->header->avail.size) + { + /* We're kind of stuck here, so we re-split the header in order to + avoid crashing. Sigh. */ + push_avail_block(dbf); + } + } + + /* Fix next_block, as well. */ + dbf->header->avail.next_block = new_blk->next_block; + + /* We changed the header. */ + dbf->header_changed = TRUE; + + /* Free the previous avail block. It is possible that the header table + is now FULL, which will cause us to overflow it! */ + if (dbf->header->avail.count == dbf->header->avail.size) + { + /* We're kind of stuck here, so we re-split the header in order to + avoid crashing. Sigh. */ + push_avail_block(dbf); + } + + _gdbm_put_av_elem (new_el, dbf->header->avail.av_table, + &dbf->header->avail.count, TRUE); + free (new_blk); +} + + +/* Splits the header avail block and pushes half onto the avail stack. */ + +static void +push_avail_block (GDBM_FILE dbf) +{ + int av_size; + off_t av_adr; + int index; + off_t file_pos; + avail_block *temp; + avail_elem new_loc; + int rc; + + /* Caclulate the size of the split block. */ + av_size = ( (dbf->header->avail.size * sizeof (avail_elem)) >> 1) + + sizeof (avail_block); + + /* Get address in file for new av_size bytes. */ + new_loc = get_elem (av_size, dbf->header->avail.av_table, + &dbf->header->avail.count); + if (new_loc.av_size == 0) + new_loc = get_block (av_size, dbf); + av_adr = new_loc.av_adr; + + + /* Split the header block. */ + temp = (avail_block *) malloc (av_size); + if (temp == NULL) _gdbm_fatal (dbf, _("malloc error")); + /* Set the size to be correct AFTER the pop_avail_block. */ + temp->size = dbf->header->avail.size; + temp->count = 0; + temp->next_block = dbf->header->avail.next_block; + dbf->header->avail.next_block = av_adr; + for (index = 1; index < dbf->header->avail.count; index++) + if ( (index & 0x1) == 1) /* Index is odd. */ + temp->av_table[temp->count++] = dbf->header->avail.av_table[index]; + else + dbf->header->avail.av_table[index>>1] + = dbf->header->avail.av_table[index]; + + /* Update the header avail count to previous size divided by 2. */ + dbf->header->avail.count >>= 1; + + /* Free the unneeded space. */ + new_loc.av_adr += av_size; + new_loc.av_size -= av_size; + _gdbm_free (dbf, new_loc.av_adr, new_loc.av_size); + + /* Update the disk. */ + file_pos = __lseek (dbf, av_adr, SEEK_SET); + if (file_pos != av_adr) _gdbm_fatal (dbf, _("lseek error")); + rc = _gdbm_full_write (dbf, temp, av_size); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); + free (temp); +} + + + +/* Get_elem returns an element in the AV_TABLE block which is + larger than SIZE. AV_COUNT is the number of elements in the + AV_TABLE. If an item is found, it extracts it from the AV_TABLE + and moves the other elements up to fill the space. If no block is + found larger than SIZE, get_elem returns a size of zero. This + routine does no I/O. */ + +static avail_elem +get_elem (int size, avail_elem av_table[], int *av_count) +{ + int index; /* For searching through the avail block. */ + avail_elem val; /* The default return value. */ + + /* Initialize default return value. */ + val.av_adr = 0; + val.av_size = 0; + + /* Search for element. List is sorted by size. */ + index = 0; + while (index < *av_count && av_table[index].av_size < size) + { + index++; + } + + /* Did we find one of the right size? */ + if (index >= *av_count) + return val; + + /* Ok, save that element and move all others up one. */ + val = av_table[index]; + *av_count -= 1; + while (index < *av_count) + { + av_table[index] = av_table[index+1]; + index++; + } + + return val; +} + + +/* This routine inserts a single NEW_EL into the AV_TABLE block. + This routine does no I/O. */ + +int +_gdbm_put_av_elem (avail_elem new_el, avail_elem av_table[], int *av_count, + int can_merge) +{ + int index; /* For searching through the avail block. */ + int index1; + + /* Is it too small to deal with? */ + if (new_el.av_size <= IGNORE_SIZE) + return FALSE; + + if (can_merge == TRUE) + { + /* Search for blocks to coalesce with this one. */ + index = 0; + + while (index < *av_count) + { + /* Can we merge with the previous block? */ + if ((av_table[index].av_adr + + av_table[index].av_size) == new_el.av_adr) + { + /* Simply expand the endtry. */ + av_table[index].av_size += new_el.av_size; + } + /* Can we merge with the next block? */ + else if ((new_el.av_adr + + new_el.av_size) == av_table[index].av_adr) + { + /* Update this entry. */ + av_table[index].av_adr = new_el.av_adr; + av_table[index].av_size += new_el.av_size; + } + /* Not contiguous */ + else + { + index++; + continue; + } + + /* If we got here, we're done. */ + return TRUE; + } + } + + /* Search for place to put element. List is sorted by size. */ + index = 0; + while (index < *av_count && av_table[index].av_size < new_el.av_size) + { + index++; + } + + /* Move all others up one. */ + index1 = *av_count-1; + while (index1 >= index) + { + av_table[index1+1] = av_table[index1]; + index1--; + } + + /* Add the new element. */ + av_table[index] = new_el; + + /* Increment the number of elements. */ + *av_count += 1; + + return TRUE; +} + + + + + +/* Get_block "allocates" new file space and the end of the file. This is + done in integral block sizes. (This helps insure that data smaller than + one block size is in a single block.) Enough blocks are allocated to + make sure the number of bytes allocated in the blocks is larger than SIZE. + DBF contains the file header that needs updating. This routine does + no I/O. */ + +static avail_elem +get_block (int size, GDBM_FILE dbf) +{ + avail_elem val; + + /* Need at least one block. */ + val.av_adr = dbf->header->next_block; + val.av_size = dbf->header->block_size; + + /* Get enough blocks to fit the need. */ + while (val.av_size < size) + val.av_size += dbf->header->block_size; + + /* Update the header and return. */ + dbf->header->next_block += val.av_size; + + /* We changed the header. */ + dbf->header_changed = TRUE; + + return val; + +} + + +/* When the header already needs writing, we can make sure the current + bucket has its avail block as close to 1/3 full as possible. */ +static void +adjust_bucket_avail (GDBM_FILE dbf) +{ + int third = BUCKET_AVAIL / 3; + avail_elem av_el; + + /* Can we add more entries to the bucket? */ + if (dbf->bucket->av_count < third) + { + if (dbf->header->avail.count > 0) + { + dbf->header->avail.count -= 1; + av_el = dbf->header->avail.av_table[dbf->header->avail.count]; + _gdbm_put_av_elem (av_el, dbf->bucket->bucket_avail, + &dbf->bucket->av_count, dbf->coalesce_blocks); + dbf->bucket_changed = TRUE; + } + return; + } + + /* Is there too much in the bucket? */ + while (dbf->bucket->av_count > BUCKET_AVAIL-third + && dbf->header->avail.count < dbf->header->avail.size) + { + av_el = get_elem (0, dbf->bucket->bucket_avail, &dbf->bucket->av_count); + _gdbm_put_av_elem (av_el, dbf->header->avail.av_table, + &dbf->header->avail.count, dbf->coalesce_blocks); + dbf->bucket_changed = TRUE; + } +} diff --git a/src/findkey.c b/src/findkey.c new file mode 100644 index 0000000..c2a0653 --- /dev/null +++ b/src/findkey.c @@ -0,0 +1,145 @@ +/* findkey.c - The routine that finds a key entry in the file. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + + +/* Read the data found in bucket entry ELEM_LOC in file DBF and + return a pointer to it. Also, cache the read value. */ + +char * +_gdbm_read_entry (GDBM_FILE dbf, int elem_loc) +{ + int rc; + int key_size; + int data_size; + off_t file_pos; + data_cache_elem *data_ca; + + /* Is it already in the cache? */ + if (dbf->cache_entry->ca_data.elem_loc == elem_loc) + return dbf->cache_entry->ca_data.dptr; + + /* Set sizes and pointers. */ + key_size = dbf->bucket->h_table[elem_loc].key_size; + data_size = dbf->bucket->h_table[elem_loc].data_size; + data_ca = &dbf->cache_entry->ca_data; + + /* Set up the cache. */ + if (data_ca->dptr != NULL) free (data_ca->dptr); + data_ca->key_size = key_size; + data_ca->data_size = data_size; + data_ca->elem_loc = elem_loc; + data_ca->hash_val = dbf->bucket->h_table[elem_loc].hash_value; + if (key_size+data_size == 0) + data_ca->dptr = (char *) malloc (1); + else + data_ca->dptr = (char *) malloc (key_size+data_size); + if (data_ca->dptr == NULL) _gdbm_fatal (dbf, _("malloc error")); + + + /* Read into the cache. */ + file_pos = __lseek (dbf, dbf->bucket->h_table[elem_loc].data_pointer, + SEEK_SET); + if (file_pos != dbf->bucket->h_table[elem_loc].data_pointer) + _gdbm_fatal (dbf, _("lseek error")); + rc = _gdbm_full_read (dbf, data_ca->dptr, key_size+data_size); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); + + return data_ca->dptr; +} + + + +/* Find the KEY in the file and get ready to read the associated data. The + return value is the location in the current hash bucket of the KEY's + entry. If it is found, a pointer to the data and the key are returned + in DPTR. If it is not found, the value -1 is returned. Since find + key computes the hash value of key, that value */ +int +_gdbm_findkey (GDBM_FILE dbf, datum key, char **dptr, int *new_hash_val) +{ + int bucket_hash_val; /* The hash value from the bucket. */ + char *file_key; /* The complete key as stored in the file. */ + int elem_loc; /* The location in the bucket. */ + int home_loc; /* The home location in the bucket. */ + int key_size; /* Size of the key on the file. */ + + /* Compute hash value and load proper bucket. */ + *new_hash_val = _gdbm_hash (key); + _gdbm_get_bucket (dbf, *new_hash_val>> (31-dbf->header->dir_bits)); + + /* Is the element the last one found for this bucket? */ + if (dbf->cache_entry->ca_data.elem_loc != -1 + && *new_hash_val == dbf->cache_entry->ca_data.hash_val + && dbf->cache_entry->ca_data.key_size == key.dsize + && dbf->cache_entry->ca_data.dptr != NULL + && memcmp (dbf->cache_entry->ca_data.dptr, key.dptr, key.dsize) == 0) + { + /* This is it. Return the cache pointer. */ + *dptr = dbf->cache_entry->ca_data.dptr+key.dsize; + return dbf->cache_entry->ca_data.elem_loc; + } + + /* It is not the cached value, search for element in the bucket. */ + elem_loc = *new_hash_val % dbf->header->bucket_elems; + home_loc = elem_loc; + bucket_hash_val = dbf->bucket->h_table[elem_loc].hash_value; + while (bucket_hash_val != -1) + { + key_size = dbf->bucket->h_table[elem_loc].key_size; + if (bucket_hash_val != *new_hash_val + || key_size != key.dsize + || memcmp (dbf->bucket->h_table[elem_loc].key_start, key.dptr, + (SMALL < key_size ? SMALL : key_size)) != 0) + { + /* Current elem_loc is not the item, go to next item. */ + elem_loc = (elem_loc + 1) % dbf->header->bucket_elems; + if (elem_loc == home_loc) return -1; + bucket_hash_val = dbf->bucket->h_table[elem_loc].hash_value; + } + else + { + /* This may be the one we want. + The only way to tell is to read it. */ + file_key = _gdbm_read_entry (dbf, elem_loc); + if (memcmp (file_key, key.dptr, key_size) == 0) + { + /* This is the item. */ + *dptr = file_key+key.dsize; + return elem_loc; + } + else + { + /* Not the item, try the next one. Return if not found. */ + elem_loc = (elem_loc + 1) % dbf->header->bucket_elems; + if (elem_loc == home_loc) return -1; + bucket_hash_val = dbf->bucket->h_table[elem_loc].hash_value; + } + } + } + + /* If we get here, we never found the key. */ + return -1; + +} diff --git a/src/fullio.c b/src/fullio.c new file mode 100644 index 0000000..adfff2c --- /dev/null +++ b/src/fullio.c @@ -0,0 +1,70 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +#include "autoconf.h" +#include "gdbmdefs.h" + +/* Read exactly SIZE bytes of data into BUFFER. Return value is 0 on + success, GDBM_FILE_EOF, if not enough data is available, and + GDBM_FILE_READ_ERROR, if a read error occurs. In the latter case + errno keeps actual system error code. */ +int +_gdbm_full_read (GDBM_FILE dbf, void *buffer, size_t size) +{ + char *ptr = buffer; + while (size) + { + ssize_t rdbytes = __read (dbf, ptr, size); + if (rdbytes == -1) + { + if (errno == EINTR) + continue; + return GDBM_FILE_READ_ERROR; + } + if (rdbytes == 0) + return GDBM_FILE_EOF; + ptr += rdbytes; + size -= rdbytes; + } + return 0; +} + +/* Write exactly SIZE bytes of data from BUFFER tp DBF. Return 0 on + success, and GDBM_FILE_READ_ERROR on error. In the latter case errno + will keep actual system error code. */ +int +_gdbm_full_write (GDBM_FILE dbf, void *buffer, size_t size) +{ + char *ptr = buffer; + while (size) + { + ssize_t wrbytes = __write (dbf, ptr, size); + if (wrbytes == -1) + { + if (errno == EINTR) + continue; + return GDBM_FILE_WRITE_ERROR; + } + if (wrbytes == 0) + { + errno = ENOSPC; + return GDBM_FILE_WRITE_ERROR; + } + ptr += wrbytes; + size -= wrbytes; + } + return 0; +} diff --git a/src/gdbm.h b/src/gdbm.h new file mode 100644 index 0000000..8787f02 --- /dev/null +++ b/src/gdbm.h @@ -0,0 +1,189 @@ +/* gdbm.h - The include file for dbm users. -*- c -*- */ + +/* This file is part of GDBM, the GNU data base manager, by Philip A. Nelson. + Copyright (C) 1990, 1991, 1993, 2011 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department + Western Washington University + Bellingham, WA 98226 + +*************************************************************************/ + +/* Protection for multiple includes. */ +#ifndef _GDBM_H_ +# define _GDBM_H_ + +# include <stdio.h> + +/* GDBM C++ support */ +# if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +# endif + +/* Parameters to gdbm_open for READERS, WRITERS, and WRITERS who + can create the database. */ +# define GDBM_READER 0 /* A reader. */ +# define GDBM_WRITER 1 /* A writer. */ +# define GDBM_WRCREAT 2 /* A writer. Create the db if needed. */ +# define GDBM_NEWDB 3 /* A writer. Always create a new db. */ +# define GDBM_OPENMASK 7 /* Mask for the above. */ + +# define GDBM_FAST 0x010 /* Write fast! => No fsyncs. OBSOLETE. */ +# define GDBM_SYNC 0x020 /* Sync operations to the disk. */ +# define GDBM_NOLOCK 0x040 /* Don't do file locking operations. */ +# define GDBM_NOMMAP 0x080 /* Don't use mmap(). */ +# define GDBM_CLOEXEC 0x100 /* Close the underlying fd on exec(3) */ + +/* Parameters to gdbm_store for simple insertion or replacement in the + case that the key is already in the database. */ +# define GDBM_INSERT 0 /* Never replace old data with new. */ +# define GDBM_REPLACE 1 /* Always replace old data with new. */ + +/* Parameters to gdbm_setopt, specifing the type of operation to perform. */ +# define GDBM_SETCACHESIZE 1 /* Set the cache size. */ +# define GDBM_FASTMODE 2 /* Toggle fast mode. OBSOLETE. */ +# define GDBM_SETSYNCMODE 3 /* Turn on or off sync operations. */ +# define GDBM_SETCENTFREE 4 /* Keep all free blocks in the header. */ +# define GDBM_SETCOALESCEBLKS 5 /* Attempt to coalesce free blocks. */ +# define GDBM_SETMAXMAPSIZE 6 /* Set maximum mapped memory size */ +# define GDBM_SETMMAP 7 /* Toggle mmap mode */ + +/* Compatibility defines: */ +# define GDBM_CACHESIZE GDBM_SETCACHESIZE +# define GDBM_SYNCMODE GDBM_SETSYNCMODE +# define GDBM_CENTFREE GDBM_SETCENTFREE +# define GDBM_COALESCEBLKS GDBM_SETCOALESCEBLKS + +# define GDBM_GETFLAGS 8 /* Get gdbm_open flags */ +# define GDBM_GETMMAP 9 /* Get mmap status */ +# define GDBM_GETCACHESIZE 10 /* Get current cache side */ +# define GDBM_GETSYNCMODE 11 /* Get synch mode */ +# define GDBM_GETCENTFREE 12 /* Get "centfree" status */ +# define GDBM_GETCOALESCEBLKS 13 /* Get free block coalesce status */ +# define GDBM_GETMAXMAPSIZE 14 /* Get maximum mapped memory size */ +# define GDBM_GETDBNAME 15 /* Return database file name */ + +typedef unsigned long long int gdbm_count_t; + +/* The data and key structure. */ +typedef struct { + char *dptr; + int dsize; + } datum; + + +/* A pointer to the GDBM file. */ +typedef struct gdbm_file_info *GDBM_FILE; + +/* External variable, the gdbm build release string. */ +extern const char *gdbm_version; + +# define GDBM_VERSION_MAJOR 1 +# define GDBM_VERSION_MINOR 11 +# define GDBM_VERSION_PATCH 0 + +extern int const gdbm_version_number[3]; + +/* GDBM external functions. */ + +extern GDBM_FILE gdbm_open (const char *, int, int, int, + void (*)(const char *)); +extern void gdbm_close (GDBM_FILE); +extern int gdbm_store (GDBM_FILE, datum, datum, int); +extern datum gdbm_fetch (GDBM_FILE, datum); +extern int gdbm_delete (GDBM_FILE, datum); +extern datum gdbm_firstkey (GDBM_FILE); +extern datum gdbm_nextkey (GDBM_FILE, datum); +extern int gdbm_reorganize (GDBM_FILE); +extern void gdbm_sync (GDBM_FILE); +extern int gdbm_exists (GDBM_FILE, datum); +extern int gdbm_setopt (GDBM_FILE, int, void *, int); +extern int gdbm_fdesc (GDBM_FILE); + +extern int gdbm_export (GDBM_FILE, const char *, int, int); +extern int gdbm_export_to_file (GDBM_FILE dbf, FILE *fp); + +extern int gdbm_import (GDBM_FILE, const char *, int); +extern int gdbm_import_from_file (GDBM_FILE dbf, FILE *fp, int flag); + +extern int gdbm_count (GDBM_FILE dbf, gdbm_count_t *pcount); + +#define GDBM_DUMP_FMT_BINARY 0 +#define GDBM_DUMP_FMT_ASCII 1 + +#define GDBM_META_MASK_MODE 0x01 +#define GDBM_META_MASK_OWNER 0x02 + +extern int gdbm_dump (GDBM_FILE, const char *, int fmt, int open_flags, + int mode); +extern int gdbm_dump_to_file (GDBM_FILE, FILE *, int fmt); + +extern int gdbm_load (GDBM_FILE *, const char *, int replace, + int meta_flags, + unsigned long *line); +extern int gdbm_load_from_file (GDBM_FILE *, FILE *, int replace, + int meta_flags, + unsigned long *line); + +# define GDBM_NO_ERROR 0 +# define GDBM_MALLOC_ERROR 1 +# define GDBM_BLOCK_SIZE_ERROR 2 +# define GDBM_FILE_OPEN_ERROR 3 +# define GDBM_FILE_WRITE_ERROR 4 +# define GDBM_FILE_SEEK_ERROR 5 +# define GDBM_FILE_READ_ERROR 6 +# define GDBM_BAD_MAGIC_NUMBER 7 +# define GDBM_EMPTY_DATABASE 8 +# define GDBM_CANT_BE_READER 9 +# define GDBM_CANT_BE_WRITER 10 +# define GDBM_READER_CANT_DELETE 11 +# define GDBM_READER_CANT_STORE 12 +# define GDBM_READER_CANT_REORGANIZE 13 +# define GDBM_UNKNOWN_UPDATE 14 +# define GDBM_ITEM_NOT_FOUND 15 +# define GDBM_REORGANIZE_FAILED 16 +# define GDBM_CANNOT_REPLACE 17 +# define GDBM_ILLEGAL_DATA 18 +# define GDBM_OPT_ALREADY_SET 19 +# define GDBM_OPT_ILLEGAL 20 +# define GDBM_BYTE_SWAPPED 21 +# define GDBM_BAD_FILE_OFFSET 22 +# define GDBM_BAD_OPEN_FLAGS 23 +# define GDBM_FILE_STAT_ERROR 24 +# define GDBM_FILE_EOF 25 +# define GDBM_NO_DBNAME 26 +# define GDBM_ERR_FILE_OWNER 27 +# define GDBM_ERR_FILE_MODE 28 + +# define _GDBM_MIN_ERRNO 0 +# define _GDBM_MAX_ERRNO GDBM_ERR_FILE_MODE +typedef int gdbm_error; /* For compatibilities sake. */ +extern gdbm_error gdbm_errno; +extern const char * const gdbm_errlist[]; + +/* extra prototypes */ + +extern const char *gdbm_strerror (gdbm_error); +extern int gdbm_version_cmp (int const a[], int const b[]); + +# if defined(__cplusplus) || defined(c_plusplus) +} +# endif + +#endif diff --git a/src/gdbm.h.in b/src/gdbm.h.in new file mode 100644 index 0000000..f8238b9 --- /dev/null +++ b/src/gdbm.h.in @@ -0,0 +1,189 @@ +/* gdbm.h - The include file for dbm users. -*- c -*- */ + +/* This file is part of GDBM, the GNU data base manager, by Philip A. Nelson. + Copyright (C) 1990, 1991, 1993, 2011 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department + Western Washington University + Bellingham, WA 98226 + +*************************************************************************/ + +/* Protection for multiple includes. */ +#ifndef _GDBM_H_ +# define _GDBM_H_ + +# include <stdio.h> + +/* GDBM C++ support */ +# if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +# endif + +/* Parameters to gdbm_open for READERS, WRITERS, and WRITERS who + can create the database. */ +# define GDBM_READER 0 /* A reader. */ +# define GDBM_WRITER 1 /* A writer. */ +# define GDBM_WRCREAT 2 /* A writer. Create the db if needed. */ +# define GDBM_NEWDB 3 /* A writer. Always create a new db. */ +# define GDBM_OPENMASK 7 /* Mask for the above. */ + +# define GDBM_FAST 0x010 /* Write fast! => No fsyncs. OBSOLETE. */ +# define GDBM_SYNC 0x020 /* Sync operations to the disk. */ +# define GDBM_NOLOCK 0x040 /* Don't do file locking operations. */ +# define GDBM_NOMMAP 0x080 /* Don't use mmap(). */ +# define GDBM_CLOEXEC 0x100 /* Close the underlying fd on exec(3) */ + +/* Parameters to gdbm_store for simple insertion or replacement in the + case that the key is already in the database. */ +# define GDBM_INSERT 0 /* Never replace old data with new. */ +# define GDBM_REPLACE 1 /* Always replace old data with new. */ + +/* Parameters to gdbm_setopt, specifing the type of operation to perform. */ +# define GDBM_SETCACHESIZE 1 /* Set the cache size. */ +# define GDBM_FASTMODE 2 /* Toggle fast mode. OBSOLETE. */ +# define GDBM_SETSYNCMODE 3 /* Turn on or off sync operations. */ +# define GDBM_SETCENTFREE 4 /* Keep all free blocks in the header. */ +# define GDBM_SETCOALESCEBLKS 5 /* Attempt to coalesce free blocks. */ +# define GDBM_SETMAXMAPSIZE 6 /* Set maximum mapped memory size */ +# define GDBM_SETMMAP 7 /* Toggle mmap mode */ + +/* Compatibility defines: */ +# define GDBM_CACHESIZE GDBM_SETCACHESIZE +# define GDBM_SYNCMODE GDBM_SETSYNCMODE +# define GDBM_CENTFREE GDBM_SETCENTFREE +# define GDBM_COALESCEBLKS GDBM_SETCOALESCEBLKS + +# define GDBM_GETFLAGS 8 /* Get gdbm_open flags */ +# define GDBM_GETMMAP 9 /* Get mmap status */ +# define GDBM_GETCACHESIZE 10 /* Get current cache side */ +# define GDBM_GETSYNCMODE 11 /* Get synch mode */ +# define GDBM_GETCENTFREE 12 /* Get "centfree" status */ +# define GDBM_GETCOALESCEBLKS 13 /* Get free block coalesce status */ +# define GDBM_GETMAXMAPSIZE 14 /* Get maximum mapped memory size */ +# define GDBM_GETDBNAME 15 /* Return database file name */ + +typedef @GDBM_COUNT_T@ gdbm_count_t; + +/* The data and key structure. */ +typedef struct { + char *dptr; + int dsize; + } datum; + + +/* A pointer to the GDBM file. */ +typedef struct gdbm_file_info *GDBM_FILE; + +/* External variable, the gdbm build release string. */ +extern const char *gdbm_version; + +# define GDBM_VERSION_MAJOR @GDBM_VERSION_MAJOR@ +# define GDBM_VERSION_MINOR @GDBM_VERSION_MINOR@ +# define GDBM_VERSION_PATCH @GDBM_VERSION_PATCH@ + +extern int const gdbm_version_number[3]; + +/* GDBM external functions. */ + +extern GDBM_FILE gdbm_open (const char *, int, int, int, + void (*)(const char *)); +extern void gdbm_close (GDBM_FILE); +extern int gdbm_store (GDBM_FILE, datum, datum, int); +extern datum gdbm_fetch (GDBM_FILE, datum); +extern int gdbm_delete (GDBM_FILE, datum); +extern datum gdbm_firstkey (GDBM_FILE); +extern datum gdbm_nextkey (GDBM_FILE, datum); +extern int gdbm_reorganize (GDBM_FILE); +extern void gdbm_sync (GDBM_FILE); +extern int gdbm_exists (GDBM_FILE, datum); +extern int gdbm_setopt (GDBM_FILE, int, void *, int); +extern int gdbm_fdesc (GDBM_FILE); + +extern int gdbm_export (GDBM_FILE, const char *, int, int); +extern int gdbm_export_to_file (GDBM_FILE dbf, FILE *fp); + +extern int gdbm_import (GDBM_FILE, const char *, int); +extern int gdbm_import_from_file (GDBM_FILE dbf, FILE *fp, int flag); + +extern int gdbm_count (GDBM_FILE dbf, gdbm_count_t *pcount); + +#define GDBM_DUMP_FMT_BINARY 0 +#define GDBM_DUMP_FMT_ASCII 1 + +#define GDBM_META_MASK_MODE 0x01 +#define GDBM_META_MASK_OWNER 0x02 + +extern int gdbm_dump (GDBM_FILE, const char *, int fmt, int open_flags, + int mode); +extern int gdbm_dump_to_file (GDBM_FILE, FILE *, int fmt); + +extern int gdbm_load (GDBM_FILE *, const char *, int replace, + int meta_flags, + unsigned long *line); +extern int gdbm_load_from_file (GDBM_FILE *, FILE *, int replace, + int meta_flags, + unsigned long *line); + +# define GDBM_NO_ERROR 0 +# define GDBM_MALLOC_ERROR 1 +# define GDBM_BLOCK_SIZE_ERROR 2 +# define GDBM_FILE_OPEN_ERROR 3 +# define GDBM_FILE_WRITE_ERROR 4 +# define GDBM_FILE_SEEK_ERROR 5 +# define GDBM_FILE_READ_ERROR 6 +# define GDBM_BAD_MAGIC_NUMBER 7 +# define GDBM_EMPTY_DATABASE 8 +# define GDBM_CANT_BE_READER 9 +# define GDBM_CANT_BE_WRITER 10 +# define GDBM_READER_CANT_DELETE 11 +# define GDBM_READER_CANT_STORE 12 +# define GDBM_READER_CANT_REORGANIZE 13 +# define GDBM_UNKNOWN_UPDATE 14 +# define GDBM_ITEM_NOT_FOUND 15 +# define GDBM_REORGANIZE_FAILED 16 +# define GDBM_CANNOT_REPLACE 17 +# define GDBM_ILLEGAL_DATA 18 +# define GDBM_OPT_ALREADY_SET 19 +# define GDBM_OPT_ILLEGAL 20 +# define GDBM_BYTE_SWAPPED 21 +# define GDBM_BAD_FILE_OFFSET 22 +# define GDBM_BAD_OPEN_FLAGS 23 +# define GDBM_FILE_STAT_ERROR 24 +# define GDBM_FILE_EOF 25 +# define GDBM_NO_DBNAME 26 +# define GDBM_ERR_FILE_OWNER 27 +# define GDBM_ERR_FILE_MODE 28 + +# define _GDBM_MIN_ERRNO 0 +# define _GDBM_MAX_ERRNO GDBM_ERR_FILE_MODE +typedef int gdbm_error; /* For compatibilities sake. */ +extern gdbm_error gdbm_errno; +extern const char * const gdbm_errlist[]; + +/* extra prototypes */ + +extern const char *gdbm_strerror (gdbm_error); +extern int gdbm_version_cmp (int const a[], int const b[]); + +# if defined(__cplusplus) || defined(c_plusplus) +} +# endif + +#endif diff --git a/src/gdbm_dump.c b/src/gdbm_dump.c new file mode 100644 index 0000000..ad251d5 --- /dev/null +++ b/src/gdbm_dump.c @@ -0,0 +1,132 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +# include "autoconf.h" +# include "gdbm.h" +# include "gdbmapp.h" +# include "gdbmdefs.h" + +char *parseopt_program_doc = "dump a GDBM database to a file"; +char *parseopt_program_args = "DB_FILE [FILE]"; +struct gdbm_option optab[] = { + { 'H', "format", "binary|ascii|0|1", N_("select dump format") }, + { 0 } +}; + +int format = GDBM_DUMP_FMT_ASCII; + +int +main (int argc, char **argv) +{ + GDBM_FILE dbf; + int rc, opt; + char *dbname, *filename; + FILE *fp; + +#ifdef HAVE_SETLOCALE + setlocale (LC_ALL, ""); +#endif + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + set_progname (argv[0]); + + for (opt = parseopt_first (argc, argv, optab); + opt != EOF; + opt = parseopt_next ()) + { + switch (opt) + { + case 'H': + if (strcmp (optarg, "binary") == 0) + format = GDBM_DUMP_FMT_BINARY; + else if (strcmp (optarg, "ascii") == 0) + format = GDBM_DUMP_FMT_ASCII; + else + { + format = atoi (optarg); + switch (format) + { + case GDBM_DUMP_FMT_BINARY: + case GDBM_DUMP_FMT_ASCII: + break; + default: + error (_("unknown dump format")); + exit (EXIT_USAGE); + } + } + break; + + default: + error (_("unknown option")); + exit (EXIT_USAGE); + } + } + + argc -= optind; + argv += optind; + + if (argc == 0) + { + parseopt_print_help (); + exit (EXIT_OK); + } + + if (argc > 2) + { + error (_("too many arguments; try `%s -h' for more info"), progname); + exit (EXIT_USAGE); + } + + dbname = argv[0]; + if (argc == 2) + filename = argv[1]; + else + filename = NULL; + + if (!filename || strcmp (filename, "-") == 0) + { + filename = "<stdout>"; + fp = stdout; + } + else + { + fp = fopen (filename, "w"); + if (!fp) + { + sys_perror (errno, _("cannot open %s"), filename); + exit (EXIT_FATAL); + } + } + + dbf = gdbm_open (dbname, 0, GDBM_READER, 0600, NULL); + if (!dbf) + { + gdbm_perror (_("gdbm_open failed")); + exit (EXIT_FATAL); + } + + rc = gdbm_dump_to_file (dbf, fp, format); + if (rc) + { + gdbm_perror (_("dump error"), filename); + } + + gdbm_close (dbf); + + exit (rc ? EXIT_OK : EXIT_FATAL); +} + diff --git a/src/gdbm_load.c b/src/gdbm_load.c new file mode 100644 index 0000000..260a601 --- /dev/null +++ b/src/gdbm_load.c @@ -0,0 +1,317 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +# include "autoconf.h" +# include "gdbm.h" +# include "gdbmapp.h" +# include "gdbmdefs.h" +# include <pwd.h> +# include <grp.h> + +int replace = 0; +int meta_mask = 0; +int no_meta_option; + +int mode; +uid_t owner_uid; +gid_t owner_gid; + +char *parseopt_program_doc = "load a GDBM database from a file"; +char *parseopt_program_args = "FILE [DB_FILE]"; +struct gdbm_option optab[] = { + { 'r', "replace", NULL, N_("replace records in the existing database") }, + { 'm', "mode", N_("MODE"), N_("set file mode") }, + { 'u', "user", N_("NAME|UID[:NAME|GID]"), N_("set file owner") }, + { 'n', "no-meta", NULL, N_("do not attempt to set file meta-data") }, + { 'M', "mmap", NULL, N_("use memory mapping") }, + { 'c', "cache-size", N_("NUM"), N_("set the cache size") }, + { 'b', "block-size", N_("NUM"), N_("set the block size") }, + { 0 } +}; + +static int +set_meta_info (GDBM_FILE dbf) +{ + if (meta_mask) + { + int fd = gdbm_fdesc (dbf); + + if (meta_mask & GDBM_META_MASK_OWNER) + { + if (fchown (fd, owner_uid, owner_gid)) + { + gdbm_errno = GDBM_ERR_FILE_OWNER; + return 1; + } + } + if ((meta_mask & GDBM_META_MASK_MODE) && fchmod (fd, mode)) + { + gdbm_errno = GDBM_ERR_FILE_OWNER; + return 1; + } + } + return 0; +} + +static int +get_int (const char *arg) +{ + char *p; + long n; + + errno = 0; + n = strtol (arg, &p, 0); + if (*p) + { + error (_("invalid number: %s"), arg); + exit (EXIT_USAGE); + } + if (errno) + { + error (_("invalid number: %s: %s"), arg, strerror (errno)); + exit (EXIT_USAGE); + } + return n; +} + +int +main (int argc, char **argv) +{ + GDBM_FILE dbf = NULL; + int rc, opt; + char *dbname, *filename; + FILE *fp; + unsigned long err_line, n; + char *end; + int oflags = GDBM_NEWDB|GDBM_NOMMAP; + int cache_size = 0; + int block_size = 0; + +#ifdef HAVE_SETLOCALE + setlocale (LC_ALL, ""); +#endif + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + set_progname (argv[0]); + + for (opt = parseopt_first (argc, argv, optab); + opt != EOF; + opt = parseopt_next ()) + { + switch (opt) + { + case 'b': + block_size = get_int (optarg); + break; + + case 'c': + cache_size = get_int (optarg); + break; + + case 'm': + { + errno = 0; + n = strtoul (optarg, &end, 8); + if (*end == 0 && errno == 0) + { + mode = n & 0777; + meta_mask |= GDBM_META_MASK_MODE; + } + else + { + error ("%s", _("invalid octal number")); + exit (EXIT_USAGE); + } + } + break; + + case 'u': + { + size_t len; + struct passwd *pw; + + len = strcspn (optarg, ".:"); + if (optarg[len]) + optarg[len++] = 0; + pw = getpwnam (optarg); + if (pw) + owner_uid = pw->pw_uid; + else + { + errno = 0; + n = strtoul (optarg, &end, 10); + if (*end == 0 && errno == 0) + owner_uid = n; + else + { + error (_("invalid user name: %s"), optarg); + exit (EXIT_USAGE); + } + } + + if (optarg[len]) + { + char *grname = optarg + len; + struct group *gr = getgrnam (grname); + if (gr) + owner_gid = gr->gr_gid; + else + { + errno = 0; + n = strtoul (grname, &end, 10); + if (*end == 0 && errno == 0) + owner_gid = n; + else + { + error (_("invalid group name: %s"), grname); + exit (EXIT_USAGE); + } + } + } + else + { + if (!pw) + { + pw = getpwuid (owner_uid); + if (!pw) + { + error (_("no such UID: %lu"), (unsigned long)owner_uid); + exit (EXIT_USAGE); + } + } + owner_gid = pw->pw_gid; + } + meta_mask |= GDBM_META_MASK_OWNER; + } + break; + + case 'r': + replace = 1; + break; + + case 'n': + no_meta_option = 1; + break; + + case 'M': + oflags &= ~GDBM_NOMMAP; + break; + + default: + error (_("unknown option")); + exit (EXIT_USAGE); + } + } + + argc -= optind; + argv += optind; + + if (argc == 0) + { + parseopt_print_help (); + exit (EXIT_OK); + } + + if (argc > 2) + { + error (_("too many arguments; try `%s -h' for more info"), progname); + exit (EXIT_USAGE); + } + + filename = argv[0]; + if (argc == 2) + dbname = argv[1]; + else + dbname = NULL; + + if (strcmp (filename, "-") == 0) + { + filename = "<stdin>"; + fp = stdin; + } + else + { + fp = fopen (filename, "r"); + if (!fp) + { + sys_perror (errno, _("cannot open %s"), filename); + exit (EXIT_FATAL); + } + } + + if (dbname) + { + dbf = gdbm_open (dbname, block_size, oflags, 0600, NULL); + if (!dbf) + { + gdbm_perror (_("gdbm_open failed")); + exit (EXIT_FATAL); + } + + if (cache_size && + gdbm_setopt (dbf, GDBM_SETCACHESIZE, &cache_size, sizeof (int)) == -1) + error (_("gdbm_setopt failed: %s"), gdbm_strerror (gdbm_errno)); + } + + rc = gdbm_load_from_file (&dbf, fp, replace, + no_meta_option ? + (GDBM_META_MASK_MODE | GDBM_META_MASK_OWNER) : + meta_mask, + &err_line); + if (rc) + { + switch (gdbm_errno) + { + case GDBM_ERR_FILE_OWNER: + case GDBM_ERR_FILE_MODE: + error (_("error restoring metadata: %s (%s)"), + gdbm_strerror (gdbm_errno), strerror (errno)); + rc = EXIT_MILD; + break; + + default: + if (err_line) + gdbm_perror ("%s:%lu", filename, err_line); + else + gdbm_perror (_("cannot load from %s"), filename); + rc = EXIT_FATAL; + } + } + + if (dbf) + { + if (!no_meta_option && set_meta_info (dbf)) + { + error (_("error restoring metadata: %s (%s)"), + gdbm_strerror (gdbm_errno), strerror (errno)); + rc = EXIT_MILD; + } + + if (!dbname) + { + if (gdbm_setopt (dbf, GDBM_GETDBNAME, &dbname, sizeof (dbname))) + gdbm_perror (_("gdbm_setopt failed")); + else + { + printf ("%s: created %s\n", progname, dbname); + free (dbname); + } + } + gdbm_close (dbf); + } + exit (rc); +} diff --git a/src/gdbmapp.h b/src/gdbmapp.h new file mode 100644 index 0000000..715f948 --- /dev/null +++ b/src/gdbmapp.h @@ -0,0 +1,63 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +#include <stdlib.h> +#include <stdarg.h> +#include "gettext.h" +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif + +extern const char *progname; + +void set_progname (const char *arg); +void gdbm_perror (const char *fmt, ...); +void sys_perror (int code, const char *fmt, ...); +void error (const char *fmt, ...); +void verror (const char *fmt, va_list ap); + +void *emalloc (size_t size); +void *erealloc (void *ptr, size_t size); +void *ecalloc (size_t nmemb, size_t size); +void *ezalloc (size_t size); +char *estrdup (const char *str); + +#define PARSEOPT_HIDDEN 0x01 +#define PARSEOPT_ALIAS 0x02 + +struct gdbm_option +{ + int opt_short; + char *opt_long; + char *opt_arg; + char *opt_descr; + int opt_flags; +}; + +int parseopt_first (int pc, char **pv, struct gdbm_option *options); +int parseopt_next (void); +void parseopt_print_help (void); + +extern char *parseopt_program_name; +extern char *parseopt_program_doc; +extern char *parseopt_program_args; + +/* Application exit codes */ +#define EXIT_OK 0 +#define EXIT_FATAL 1 +#define EXIT_MILD 2 +#define EXIT_USAGE 3 + diff --git a/src/gdbmclose.c b/src/gdbmclose.c new file mode 100644 index 0000000..f711a8f --- /dev/null +++ b/src/gdbmclose.c @@ -0,0 +1,61 @@ +/* gdbmclose.c - Close a previously opened dbm file. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + +/* Close the dbm file and free all memory associated with the file DBF. + Before freeing members of DBF, check and make sure that they were + allocated. */ + +void +gdbm_close (GDBM_FILE dbf) +{ + int index; /* For freeing the bucket cache. */ + + /* Make sure the database is all on disk. */ + if (dbf->read_write != GDBM_READER) + __fsync (dbf); + + /* Close the file and free all malloced memory. */ +#if HAVE_MMAP + _gdbm_mapped_unmap(dbf); +#endif + if (dbf->file_locking) + { + _gdbm_unlock_file (dbf); + } + close (dbf->desc); + free (dbf->name); + if (dbf->dir != NULL) free (dbf->dir); + + if (dbf->bucket_cache != NULL) { + for (index = 0; index < dbf->cache_size; index++) { + if (dbf->bucket_cache[index].ca_bucket != NULL) + free (dbf->bucket_cache[index].ca_bucket); + if (dbf->bucket_cache[index].ca_data.dptr != NULL) + free (dbf->bucket_cache[index].ca_data.dptr); + } + free (dbf->bucket_cache); + } + if ( dbf->header != NULL ) free (dbf->header); + free (dbf); +} diff --git a/src/gdbmconst.h b/src/gdbmconst.h new file mode 100644 index 0000000..0002213 --- /dev/null +++ b/src/gdbmconst.h @@ -0,0 +1,52 @@ +/* gdbmconst.h - The constants defined for use in gdbm. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Start with the constant definitions. */ +#define TRUE 1 +#define FALSE 0 + +/* Header magic numbers. Since we don't have space for flags or versions, we + use different static numbers to determine what kind of file it is. + + This should've been done back when off_t was added to the library, but + alas... We just have to assume that an OMAGIC file is readable. */ + +#define GDBM_OMAGIC 0x13579ace /* Original magic number. */ +#define GDBM_MAGIC32 0x13579acd /* New 32bit magic number. */ +#define GDBM_MAGIC64 0x13579acf /* New 64bit magic number. */ + +#define GDBM_OMAGIC_SWAP 0xce9a5713 /* OMAGIC swapped. */ +#define GDBM_MAGIC32_SWAP 0xcd9a5713 /* MAGIC32 swapped. */ +#define GDBM_MAGIC64_SWAP 0xcf9a5713 /* MAGIC64 swapped. */ + +/* In freeing blocks, we will ignore any blocks smaller (and equal) to + IGNORE_SIZE number of bytes. */ +#define IGNORE_SIZE 4 + +/* The number of key bytes kept in a hash bucket. */ +#define SMALL 4 + +/* The number of bucket_avail entries in a hash bucket. */ +#define BUCKET_AVAIL 6 + +/* The size of the bucket cache. */ +#define DEFAULT_CACHESIZE 100 + +/* Maximum size representable by a size_t variable */ +#define SIZE_T_MAX ((size_t)-1) diff --git a/src/gdbmcount.c b/src/gdbmcount.c new file mode 100644 index 0000000..8e22213 --- /dev/null +++ b/src/gdbmcount.c @@ -0,0 +1,65 @@ +/* gdbmcount.c - get number of items in a gdbm file. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1993, 1994, 2007, 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" +#include "gdbmdefs.h" + +static int +compoff (const void *a, const void *b) +{ + if (*(off_t*)a < *(off_t*)b) + return -1; + if (*(off_t*)a > *(off_t*)b) + return 1; + return 0; +} + +int +gdbm_count (GDBM_FILE dbf, gdbm_count_t *pcount) +{ + hash_bucket bucket; + int nbuckets = GDBM_DIR_COUNT (dbf); + off_t *sdir; + gdbm_count_t count = 0; + int i, last; + + sdir = malloc (dbf->header->dir_size); + if (!sdir) + { + gdbm_errno = GDBM_MALLOC_ERROR; + return -1; + } + + memcpy (sdir, dbf->dir, dbf->header->dir_size); + qsort (sdir, nbuckets, sizeof (off_t), compoff); + + for (i = last = 0; i < nbuckets; i++) + { + if (i == 0 || sdir[i] != sdir[last]) + { + if (_gdbm_read_bucket_at (dbf, sdir[i], &bucket, sizeof bucket)) + return -1; + count += bucket.count; + last = i; + } + } + free (sdir); + *pcount = count; + return 0; +} diff --git a/src/gdbmdefs.h b/src/gdbmdefs.h new file mode 100644 index 0000000..c62413b --- /dev/null +++ b/src/gdbmdefs.h @@ -0,0 +1,225 @@ +/* gdbmdefs.h - The include file for dbm. Defines structure and constants. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +#include "systems.h" +#include "gdbmconst.h" +#include "gdbm.h" +#define DEFAULT_TEXT_DOMAIN PACKAGE +#include "gettext.h" + +#define _(s) gettext (s) +#define N_(s) s + +/* The type definitions are next. */ + +/* The available file space is stored in an "avail" table. The one with + most activity is contained in the file header. (See below.) When that + one filles up, it is split in half and half is pushed on an "avail + stack." When the active avail table is empty and the "avail stack" is + not empty, the top of the stack is popped into the active avail table. */ + +/* The following structure is the element of the avaliable table. */ +typedef struct { + int av_size; /* The size of the available block. */ + off_t av_adr; /* The file address of the available block. */ + } avail_elem; + +/* This is the actual table. The in-memory images of the avail blocks are + allocated by malloc using a calculated size. */ +typedef struct { + int size; /* The number of avail elements in the table.*/ + int count; /* The number of entries in the table. */ + off_t next_block; /* The file address of the next avail block. */ + avail_elem av_table[1]; /* The table. Make it look like an array. */ + } avail_block; + +/* The dbm file header keeps track of the current location of the hash + directory and the free space in the file. */ + +typedef struct { + int header_magic; /* Version of file. */ + int block_size; /* The optimal i/o blocksize from stat. */ + off_t dir; /* File address of hash directory table. */ + int dir_size; /* Size in bytes of the table. */ + int dir_bits; /* The number of address bits used in the table.*/ + int bucket_size; /* Size in bytes of a hash bucket struct. */ + int bucket_elems; /* Number of elements in a hash bucket. */ + off_t next_block; /* The next unallocated block address. */ + avail_block avail; /* This must be last because of the pseudo + array in avail. This avail grows to fill + the entire block. */ + } gdbm_file_header; + + +/* The dbm hash bucket element contains the full 31 bit hash value, the + "pointer" to the key and data (stored together) with their sizes. It also + has a small part of the actual key value. It is used to verify the first + part of the key has the correct value without having to read the actual + key. */ + +typedef struct { + int hash_value; /* The complete 31 bit value. */ + char key_start[SMALL]; /* Up to the first SMALL bytes of the key. */ + off_t data_pointer; /* The file address of the key record. The + data record directly follows the key. */ + int key_size; /* Size of key data in the file. */ + int data_size; /* Size of associated data in the file. */ + } bucket_element; + + +/* A bucket is a small hash table. This one consists of a number of + bucket elements plus some bookkeeping fields. The number of elements + depends on the optimum blocksize for the storage device and on a + parameter given at file creation time. This bucket takes one block. + When one of these tables gets full, it is split into two hash buckets. + The contents are split between them by the use of the first few bits + of the 31 bit hash function. The location in a bucket is the hash + value modulo the size of the bucket. The in-memory images of the + buckets are allocated by malloc using a calculated size depending of + the file system buffer size. To speed up write, each bucket will have + BUCKET_AVAIL avail elements with the bucket. */ + +typedef struct { + int av_count; /* The number of bucket_avail entries. */ + avail_elem bucket_avail[BUCKET_AVAIL]; /* Distributed avail. */ + int bucket_bits; /* The number of bits used to get here. */ + int count; /* The number of element buckets full. */ + bucket_element h_table[1]; /* The table. Make it look like an array.*/ + } hash_bucket; + +/* We want to keep from reading buckets as much as possible. The following is + to implement a bucket cache. When full, buckets will be dropped in a + least recently read from disk order. */ + +/* To speed up fetching and "sequential" access, we need to implement a + data cache for key/data pairs read from the file. To find a key, we + must exactly match the key from the file. To reduce overhead, the + data will be read at the same time. Both key and data will be stored + in a data cache. Each bucket cached will have a one element data + cache. */ + +typedef struct { + int hash_val; + int data_size; + int key_size; + char *dptr; + int elem_loc; + } data_cache_elem; + +typedef struct { + hash_bucket * ca_bucket; + off_t ca_adr; + char ca_changed; /* Data in the bucket changed. */ + data_cache_elem ca_data; + } cache_elem; + +/* This final structure contains all main memory based information for + a gdbm file. This allows multiple gdbm files to be opened at the same + time by one program. */ + +struct gdbm_file_info { + /* Global variables and pointers to dynamic variables used by gdbm. */ + + /* The file name. */ + char *name; + + /* The reader/writer status. */ + unsigned read_write :2; + + /* Fast_write is set to 1 if no fsyncs are to be done. */ + unsigned fast_write :1; + + /* Central_free is set if all free blocks are kept in the header. */ + unsigned central_free :1; + + /* Coalesce_blocks is set if we should try to merge free blocks. */ + unsigned coalesce_blocks :1; + + /* Whether or not we should do file locking ourselves. */ + unsigned file_locking :1; + + /* Whether or not we're allowing mmap() use. */ + unsigned memory_mapping :1; + + /* Whether the database was open with GDBM_CLOEXEC flag */ + unsigned cloexec :1; + + /* Type of file locking in use. */ + enum { LOCKING_NONE = 0, LOCKING_FLOCK, LOCKING_LOCKF, + LOCKING_FCNTL } lock_type; + + /* The fatal error handling routine. */ + void (*fatal_err) (const char *); + + /* The gdbm file descriptor which is set in gdbm_open. */ + int desc; + + /* The file header holds information about the database. */ + gdbm_file_header *header; + + /* The hash table directory from extendable hashing. See Fagin et al, + ACM Trans on Database Systems, Vol 4, No 3. Sept 1979, 315-344 */ + off_t *dir; + + /* The bucket cache. */ + cache_elem *bucket_cache; + size_t cache_size; + int last_read; + + /* Points to the current hash bucket in the cache. */ + hash_bucket *bucket; + + /* The directory entry used to get the current hash bucket. */ + int bucket_dir; + + /* Pointer to the current bucket's cache entry. */ + cache_elem *cache_entry; + + /* Bookkeeping of things that need to be written back at the + end of an update. */ + unsigned header_changed :1; + unsigned directory_changed :1; + unsigned bucket_changed :1; + unsigned second_changed :1; + + /* Mmap info */ + size_t mapped_size_max;/* Max. allowed value for mapped_size */ + void *mapped_region; /* Mapped region */ + size_t mapped_size; /* Size of the region */ + off_t mapped_pos; /* Current offset in the region */ + off_t mapped_off; /* Position in the file where the region + begins */ + }; + +#define GDBM_DIR_COUNT(db) ((db)->header->dir_size / sizeof (off_t)) + +/* Execute CODE without clobbering errno */ +#define SAVE_ERRNO(code) \ + do \ + { \ + int __ec = errno; \ + code; \ + errno = __ec; \ + } \ + while (0) \ + +#define _GDBM_MAX_DUMP_LINE_LEN 76 + +/* Now define all the routines in use. */ +#include "proto.h" diff --git a/src/gdbmdelete.c b/src/gdbmdelete.c new file mode 100644 index 0000000..1970422 --- /dev/null +++ b/src/gdbmdelete.c @@ -0,0 +1,106 @@ +/* gdbmdelete.c - Remove the key and its associated data from the database. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + +/* Remove the KEYed item and the KEY from the database DBF. The file on disk + is updated to reflect the structure of the new database before returning + from this procedure. */ + +int +gdbm_delete (GDBM_FILE dbf, datum key) +{ + int elem_loc; /* The location in the current hash bucket. */ + int last_loc; /* Last location emptied by the delete. */ + int home; /* Home position of an item. */ + bucket_element elem; /* The element to be deleted. */ + char *find_data; /* Return pointer from findkey. */ + int hash_val; /* Returned by findkey. */ + off_t free_adr; /* Temporary stroage for address and size. */ + int free_size; + + /* First check to make sure this guy is a writer. */ + if (dbf->read_write == GDBM_READER) + { + gdbm_errno = GDBM_READER_CANT_DELETE; + return -1; + } + + /* Initialize the gdbm_errno variable. */ + gdbm_errno = GDBM_NO_ERROR; + + /* Find the item. */ + elem_loc = _gdbm_findkey (dbf, key, &find_data, &hash_val); + if (elem_loc == -1) + { + gdbm_errno = GDBM_ITEM_NOT_FOUND; + return -1; + } + + /* Save the element. */ + elem = dbf->bucket->h_table[elem_loc]; + + /* Delete the element. */ + dbf->bucket->h_table[elem_loc].hash_value = -1; + dbf->bucket->count--; + + /* Move other elements to guarantee that they can be found. */ + last_loc = elem_loc; + elem_loc = (elem_loc + 1) % dbf->header->bucket_elems; + while (elem_loc != last_loc + && dbf->bucket->h_table[elem_loc].hash_value != -1) + { + home = dbf->bucket->h_table[elem_loc].hash_value + % dbf->header->bucket_elems; + if ( (last_loc < elem_loc && (home <= last_loc || home > elem_loc)) + || (last_loc > elem_loc && home <= last_loc && home > elem_loc)) + + { + dbf->bucket->h_table[last_loc] = dbf->bucket->h_table[elem_loc]; + dbf->bucket->h_table[elem_loc].hash_value = -1; + last_loc = elem_loc; + } + elem_loc = (elem_loc + 1) % dbf->header->bucket_elems; + } + + /* Free the file space. */ + free_adr = elem.data_pointer; + free_size = elem.key_size + elem.data_size; + _gdbm_free (dbf, free_adr, free_size); + + /* Set the flags. */ + dbf->bucket_changed = TRUE; + + /* Clear out the data cache for the current bucket. */ + if (dbf->cache_entry->ca_data.dptr != NULL) + { + free (dbf->cache_entry->ca_data.dptr); + dbf->cache_entry->ca_data.dptr = NULL; + } + dbf->cache_entry->ca_data.hash_val = -1; + dbf->cache_entry->ca_data.key_size = 0; + dbf->cache_entry->ca_data.elem_loc = -1; + + /* Do the writes. */ + _gdbm_end_update (dbf); + return 0; +} diff --git a/src/gdbmdump.c b/src/gdbmdump.c new file mode 100644 index 0000000..1b374f3 --- /dev/null +++ b/src/gdbmdump.c @@ -0,0 +1,195 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +# include "autoconf.h" +# include "gdbmdefs.h" +# include "gdbm.h" +# include <pwd.h> +# include <grp.h> +# include <time.h> + +static int +print_datum (datum const *dat, unsigned char **bufptr, + size_t *bufsize, FILE *fp) +{ + int rc; + size_t len; + unsigned char *p; + + fprintf (fp, "#:len=%lu\n", (unsigned long) dat->dsize); + rc = _gdbm_base64_encode ((unsigned char*) dat->dptr, dat->dsize, + bufptr, bufsize, &len); + if (rc) + return rc; + + p = *bufptr; + while (len) + { + size_t n = len; + if (n > _GDBM_MAX_DUMP_LINE_LEN) + n = _GDBM_MAX_DUMP_LINE_LEN; + if (fwrite (p, n, 1, fp) != 1) + return GDBM_FILE_WRITE_ERROR; + fputc ('\n', fp); + len -= n; + p += n; + } + return 0; +} + +int +_gdbm_dump_ascii (GDBM_FILE dbf, FILE *fp) +{ + time_t t; + int fd; + struct stat st; + struct passwd *pw; + struct group *gr; + datum key; + size_t count = 0; + unsigned char *buffer = NULL; + size_t bufsize = 0; + int rc; + + fd = gdbm_fdesc (dbf); + if (fstat (fd, &st)) + return GDBM_FILE_STAT_ERROR; + + /* Print header */ + time (&t); + fprintf (fp, "# GDBM dump file created by %s on %s", + gdbm_version, ctime (&t)); + fprintf (fp, "#:version=1.0\n"); + + fprintf (fp, "#:file=%s\n", dbf->name); + fprintf (fp, "#:uid=%lu,", (unsigned long) st.st_uid); + pw = getpwuid (st.st_uid); + if (pw) + fprintf (fp, "user=%s,", pw->pw_name); + fprintf (fp, "gid=%lu,", (unsigned long) st.st_gid); + gr = getgrgid (st.st_gid); + if (gr) + fprintf (fp, "group=%s,", gr->gr_name); + fprintf (fp, "mode=%03o\n", st.st_mode & 0777); + fprintf (fp, "# End of header\n"); + + key = gdbm_firstkey (dbf); + + while (key.dptr) + { + datum nextkey; + datum data = gdbm_fetch (dbf, key); + if (data.dptr) + { + if ((rc = print_datum (&key, &buffer, &bufsize, fp)) || + (rc = print_datum (&data, &buffer, &bufsize, fp))) + { + free (key.dptr); + free (data.dptr); + gdbm_errno = rc; + break; + } + } + nextkey = gdbm_nextkey (dbf, key); + free (key.dptr); + free (data.dptr); + key = nextkey; + count++; + } + + if (rc == 0) + { + /* FIXME: Something like that won't hurt, although load does not + use it currently. */ + fprintf (fp, "#:count=%lu\n", (unsigned long) count); + fprintf (fp, "# End of data\n"); + } + free (buffer); + + + return rc ? -1 : 0; +} + +int +gdbm_dump_to_file (GDBM_FILE dbf, FILE *fp, int format) +{ + int rc; + + switch (format) + { + case GDBM_DUMP_FMT_BINARY: + rc = gdbm_export_to_file (dbf, fp) == -1; + break; + + case GDBM_DUMP_FMT_ASCII: + rc = _gdbm_dump_ascii (dbf, fp); + break; + + default: + return EINVAL; + } + + if (rc == 0 && ferror (fp)) + rc = gdbm_errno = GDBM_FILE_WRITE_ERROR; + + return rc; +} + +int +gdbm_dump (GDBM_FILE dbf, const char *filename, int fmt, int open_flags, + int mode) +{ + int nfd, rc; + FILE *fp; + + /* Only support GDBM_WCREAT or GDBM_NEWDB */ + switch (open_flags) + { + case GDBM_WRCREAT: + nfd = open (filename, O_WRONLY | O_CREAT | O_EXCL, mode); + if (nfd == -1) + { + gdbm_errno = GDBM_FILE_OPEN_ERROR; + return -1; + } + break; + case GDBM_NEWDB: + nfd = open (filename, O_WRONLY | O_CREAT | O_TRUNC, mode); + if (nfd == -1) + { + gdbm_errno = GDBM_FILE_OPEN_ERROR; + return -1; + } + break; + default: + gdbm_errno = GDBM_BAD_OPEN_FLAGS; + return -1; + } + + fp = fdopen (nfd, "w"); + if (!fp) + { + close (nfd); + gdbm_errno = GDBM_FILE_OPEN_ERROR; + return -1; + } + rc = gdbm_dump_to_file (dbf, fp, fmt); + fclose (fp); + return rc; +} + + + diff --git a/src/gdbmerrno.c b/src/gdbmerrno.c new file mode 100644 index 0000000..b84503b --- /dev/null +++ b/src/gdbmerrno.c @@ -0,0 +1,73 @@ +/* gdbmerrno.c - convert gdbm errors into english. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1993, 2007, 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + +/* The dbm error number is placed in the variable GDBM_ERRNO. */ +gdbm_error gdbm_errno = GDBM_NO_ERROR; + +/* this is not static so that applications may access the array if they + like. it must be in the same order as the error codes! */ + +const char * const gdbm_errlist[_GDBM_MAX_ERRNO+1] = { + N_("No error"), /* GDBM_NO_ERROR */ + N_("Malloc error"), /* GDBM_MALLOC_ERROR */ + N_("Block size error"), /* GDBM_BLOCK_SIZE_ERROR */ + N_("File open error"), /* GDBM_FILE_OPEN_ERROR */ + N_("File write error"), /* GDBM_FILE_WRITE_ERROR */ + N_("File seek error"), /* GDBM_FILE_SEEK_ERROR */ + N_("File read error"), /* GDBM_FILE_READ_ERROR */ + N_("Bad magic number"), /* GDBM_BAD_MAGIC_NUMBER */ + N_("Empty database"), /* GDBM_EMPTY_DATABASE */ + N_("Can't be reader"), /* GDBM_CANT_BE_READER */ + N_("Can't be writer"), /* GDBM_CANT_BE_WRITER */ + N_("Reader can't delete"), /* GDBM_READER_CANT_DELETE */ + N_("Reader can't store"), /* GDBM_READER_CANT_STORE */ + N_("Reader can't reorganize"), /* GDBM_READER_CANT_REORGANIZE */ + N_("Unknown update"), /* GDBM_UNKNOWN_UPDATE */ + N_("Item not found"), /* GDBM_ITEM_NOT_FOUND */ + N_("Reorganize failed"), /* GDBM_REORGANIZE_FAILED */ + N_("Cannot replace"), /* GDBM_CANNOT_REPLACE */ + N_("Illegal data"), /* GDBM_ILLEGAL_DATA */ + N_("Option already set"), /* GDBM_OPT_ALREADY_SET */ + N_("Illegal option"), /* GDBM_OPT_ILLEGAL */ + N_("Byte-swapped file"), /* GDBM_BYTE_SWAPPED */ + N_("Wrong file offset"), /* GDBM_BAD_FILE_OFFSET */ + N_("Bad file flags"), /* GDBM_BAD_OPEN_FLAGS */ + N_("Cannot stat file"), /* GDBM_FILE_STAT_ERROR */ + N_("Unexpected end of file"), /* GDBM_FILE_EOF */ + N_("Database name not given"), /* GDBM_NO_DBNAME */ + N_("Failed to restore file owner"), /* GDBM_ERR_FILE_OWNER */ + N_("Failed to restore file mode"), /* GDBM_ERR_FILE_MODE */ +}; + +const char * +gdbm_strerror (gdbm_error error) +{ + if (((int)error < _GDBM_MIN_ERRNO) || ((int)error > _GDBM_MAX_ERRNO)) + { + return _("Unknown error"); + } + else + { + return gettext (gdbm_errlist[(int)error]); + } +} diff --git a/src/gdbmexists.c b/src/gdbmexists.c new file mode 100644 index 0000000..c162037 --- /dev/null +++ b/src/gdbmexists.c @@ -0,0 +1,34 @@ +/* gdbmexists.c - Check to see if a key exists */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1993, 2007, 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + +/* This is nothing more than a wrapper around _gdbm_findkey(). The + point? It doesn't alloate any memory. */ + +int +gdbm_exists (GDBM_FILE dbf, datum key) +{ + char *find_data; /* Dummy */ + int hash_val; /* Dummy */ + + return (_gdbm_findkey (dbf, key, &find_data, &hash_val) >= 0); +} diff --git a/src/gdbmexp.c b/src/gdbmexp.c new file mode 100644 index 0000000..e3babc0 --- /dev/null +++ b/src/gdbmexp.c @@ -0,0 +1,125 @@ +/* gdbmexp.c - Export a GDBM database. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 2007, 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +# include "autoconf.h" +# include <arpa/inet.h> + +# include "gdbmdefs.h" +# include "gdbm.h" + +int +gdbm_export_to_file (GDBM_FILE dbf, FILE *fp) +{ + unsigned long size; + datum key, nextkey, data; + const char *header1 = "!\r\n! GDBM FLAT FILE DUMP -- THIS IS NOT A TEXT FILE\r\n! "; + const char *header2 = "\r\n!\r\n"; + int count = 0; + + /* Write out the text header. */ + if (fwrite (header1, strlen (header1), 1, fp) != 1) + goto write_fail; + if (fwrite (gdbm_version, strlen (gdbm_version), 1, fp) != 1) + goto write_fail; + if (fwrite (header2, strlen (header2), 1, fp) != 1) + goto write_fail; + + /* For each item in the database, write out a record to the file. */ + key = gdbm_firstkey (dbf); + + while (key.dptr != NULL) + { + data = gdbm_fetch (dbf, key); + if (data.dptr != NULL) + { + /* Add the data to the new file. */ + size = htonl (key.dsize); + if (fwrite (&size, sizeof (size), 1, fp) != 1) + goto write_fail; + if (fwrite (key.dptr, key.dsize, 1, fp) != 1) + goto write_fail; + + size = htonl (data.dsize); + if (fwrite (&size, sizeof (size), 1, fp) != 1) + goto write_fail; + if (fwrite (data.dptr, data.dsize, 1, fp) != 1) + goto write_fail; + } + nextkey = gdbm_nextkey (dbf, key); + free (key.dptr); + free (data.dptr); + key = nextkey; + + count++; + } + + return count; + + write_fail: + + gdbm_errno = GDBM_FILE_WRITE_ERROR; + return -1; +} + +int +gdbm_export (GDBM_FILE dbf, const char *exportfile, int flags, int mode) +{ + int nfd, rc; + FILE *fp; + + /* Only support GDBM_WCREAT or GDBM_NEWDB */ + switch (flags) + { + case GDBM_WRCREAT: + nfd = open (exportfile, O_WRONLY | O_CREAT | O_EXCL, mode); + if (nfd == -1) + { + gdbm_errno = GDBM_FILE_OPEN_ERROR; + return -1; + } + break; + case GDBM_NEWDB: + nfd = open (exportfile, O_WRONLY | O_CREAT | O_TRUNC, mode); + if (nfd == -1) + { + gdbm_errno = GDBM_FILE_OPEN_ERROR; + return -1; + } + break; + default: +#ifdef GDBM_BAD_OPEN_FLAGS + gdbm_errno = GDBM_BAD_OPEN_FLAGS; +#else + gdbm_errno = GDBM_FILE_OPEN_ERROR; +#endif + return -1; + } + + fp = fdopen (nfd, "w"); + if (!fp) + { + close (nfd); + gdbm_errno = GDBM_FILE_OPEN_ERROR; + return -1; + } + + rc = gdbm_export_to_file (dbf, fp); + fclose (fp); + return rc; +} diff --git a/src/gdbmfdesc.c b/src/gdbmfdesc.c new file mode 100644 index 0000000..c6db1ef --- /dev/null +++ b/src/gdbmfdesc.c @@ -0,0 +1,30 @@ +/* gdbfdesc.c - return the file descriptor associated with the database. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1999, 2007, 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + +/* Return the file number of the DBF file. */ + +int +gdbm_fdesc(GDBM_FILE dbf) +{ + return (dbf->desc); +} diff --git a/src/gdbmfetch.c b/src/gdbmfetch.c new file mode 100644 index 0000000..83d4204 --- /dev/null +++ b/src/gdbmfetch.c @@ -0,0 +1,63 @@ +/* gdbmfetch.c - Find a key and return the associated data. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + +/* Look up a given KEY and return the information associated with that KEY. + The pointer in the structure that is returned is a pointer to dynamically + allocated memory block. */ + +datum +gdbm_fetch (GDBM_FILE dbf, datum key) +{ + datum return_val; /* The return value. */ + int elem_loc; /* The location in the bucket. */ + char *find_data; /* Returned from find_key. */ + int hash_val; /* Returned from find_key. */ + + /* Set the default return value. */ + return_val.dptr = NULL; + return_val.dsize = 0; + + /* Initialize the gdbm_errno variable. */ + gdbm_errno = GDBM_NO_ERROR; + + /* Find the key and return a pointer to the data. */ + elem_loc = _gdbm_findkey (dbf, key, &find_data, &hash_val); + + /* Copy the data if the key was found. */ + if (elem_loc >= 0) + { + /* This is the item. Return the associated data. */ + return_val.dsize = dbf->bucket->h_table[elem_loc].data_size; + if (return_val.dsize == 0) + return_val.dptr = (char *) malloc (1); + else + return_val.dptr = (char *) malloc (return_val.dsize); + if (return_val.dptr == NULL) _gdbm_fatal (dbf, _("malloc error")); + memcpy (return_val.dptr, find_data, return_val.dsize); + } + + /* Check for an error and return. */ + if (return_val.dptr == NULL) gdbm_errno = GDBM_ITEM_NOT_FOUND; + return return_val; +} diff --git a/src/gdbmimp.c b/src/gdbmimp.c new file mode 100644 index 0000000..9f2c9a6 --- /dev/null +++ b/src/gdbmimp.c @@ -0,0 +1,157 @@ +/* gdbmimp.c - Import a GDBM database. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 2007, 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +# include "autoconf.h" +# include <arpa/inet.h> + +# include "gdbmdefs.h" +# include "gdbm.h" + +int +gdbm_import_from_file (GDBM_FILE dbf, FILE *fp, int flag) +{ + int seenbang, seennewline, rsize, size, kbufsize, dbufsize, rret; + int c; + char *kbuffer, *dbuffer; + datum key, data; + int count = 0; + + seenbang = 0; + seennewline = 0; + kbuffer = NULL; + dbuffer = NULL; + + /* Read (and discard) four lines begining with ! and ending with \n. */ + while (1) + { + if ((c = fgetc (fp)) == -1) + goto read_fail; + + if (c == '!') + seenbang++; + if (c == '\n') + { + if (seenbang > 3 && seennewline > 2) + { + /* End of last line. */ + break; + } + seennewline++; + } + } + + /* Allocate buffers. */ + kbufsize = 512; + kbuffer = malloc (kbufsize); + if (kbuffer == NULL) + { + gdbm_errno = GDBM_MALLOC_ERROR; + return -1; + } + dbufsize = 512; + dbuffer = malloc (dbufsize); + if (dbuffer == NULL) + { + free (kbuffer); + gdbm_errno = GDBM_MALLOC_ERROR; + return -1; + } + + /* Insert/replace records in the database until we run out of file. */ + while ((rret = fread (&rsize, sizeof (rsize), 1, fp)) == 1) + { + /* Read the key. */ + size = ntohl (rsize); + if (size > kbufsize) + { + kbufsize = (size + 512); + kbuffer = realloc (kbuffer, kbufsize); + if (kbuffer == NULL) + { + free (dbuffer); + gdbm_errno = GDBM_MALLOC_ERROR; + return -1; + } + } + if (fread (kbuffer, size, 1, fp) != 1) + goto read_fail; + + key.dptr = kbuffer; + key.dsize = size; + + /* Read the data. */ + if (fread (&rsize, sizeof (rsize), 1, fp) != 1) + goto read_fail; + + size = ntohl (rsize); + if (size > dbufsize) + { + dbufsize = (size + 512); + dbuffer = realloc (dbuffer, dbufsize); + if (dbuffer == NULL) + { + free (kbuffer); + gdbm_errno = GDBM_MALLOC_ERROR; + return -1; + } + } + if (fread (dbuffer, size, 1, fp) != 1) + goto read_fail; + + data.dptr = dbuffer; + data.dsize = size; + + if (gdbm_store (dbf, key, data, flag) != 0) + { + /* Keep the existing errno. */ + free (kbuffer); + free (dbuffer); + return -1; + } + + count++; + } + + if (rret == 0) + return count; + +read_fail: + + free (kbuffer); + free (dbuffer); + gdbm_errno = GDBM_FILE_READ_ERROR; + return -1; +} + +int +gdbm_import (GDBM_FILE dbf, const char *importfile, int flag) +{ + FILE *fp; + int rc; + + fp = fopen (importfile, "r"); + if (!fp) + { + gdbm_errno = GDBM_FILE_OPEN_ERROR; + return -1; + } + rc = gdbm_import_from_file (dbf, fp, flag); + fclose (fp); + return rc; +} + diff --git a/src/gdbmload.c b/src/gdbmload.c new file mode 100644 index 0000000..19ee0fb --- /dev/null +++ b/src/gdbmload.c @@ -0,0 +1,635 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +# include "autoconf.h" +# include "gdbmdefs.h" +# include "gdbm.h" +# include <sys/types.h> +# include <pwd.h> +# include <grp.h> + +struct datbuf +{ + unsigned char *buffer; + size_t size; +}; + +struct dump_file +{ + FILE *fp; + size_t line; + + char *linebuf; + size_t lbsize; + size_t lblevel; + + char *buffer; + size_t bufsize; + size_t buflevel; + + size_t parmc; + + struct datbuf data[2]; + char *header; +}; + +static void +dump_file_free (struct dump_file *file) +{ + free (file->linebuf); + free (file->buffer); + free (file->data[0].buffer); + free (file->data[1].buffer); + free (file->header); +} + +static const char * +getparm (const char *buf, const char *parm) +{ + if (!buf) + return NULL; + while (*buf) + { + const char *p; + for (p = parm; *p == *buf; p++, buf++) + ; + if (*p == 0 && *buf == '=') + return buf + 1; + buf += strlen (buf) + 1; + } + return NULL; +} + +static size_t +get_dump_line (struct dump_file *file) +{ + char buf[80]; + + if (file->lblevel == 0) + { + while (fgets (buf, sizeof buf, file->fp)) + { + size_t n = strlen (buf); + + if (buf[n-1] == '\n') + { + file->line++; + --n; + } + + if (n + 1 + file->lblevel > file->lbsize) + { + size_t s = ((file->lblevel + n + _GDBM_MAX_DUMP_LINE_LEN) + / _GDBM_MAX_DUMP_LINE_LEN) + * _GDBM_MAX_DUMP_LINE_LEN; + char *newp = realloc (file->linebuf, s); + if (!newp) + return GDBM_MALLOC_ERROR; + file->linebuf = newp; + file->lbsize = s; + } + + memcpy (file->linebuf + file->lblevel, buf, n); + file->lblevel += n; + if (buf[n]) + { + file->linebuf[file->lblevel] = 0; + break; + } + } + } + return file->lblevel; +} + +static int +get_data (struct dump_file *file) +{ + size_t n; + + file->buflevel = 0; + file->parmc = 0; + + while ((n = get_dump_line (file))) + { + if (file->linebuf[0] == '#') + return 0; + if (n + file->buflevel > file->bufsize) + { + size_t s = ((file->buflevel + n + _GDBM_MAX_DUMP_LINE_LEN - 1) + / _GDBM_MAX_DUMP_LINE_LEN) + * _GDBM_MAX_DUMP_LINE_LEN; + char *newp = realloc (file->buffer, s); + if (!newp) + return GDBM_MALLOC_ERROR; + file->buffer = newp; + file->bufsize = s; + } + memcpy (file->buffer + file->buflevel, file->linebuf, n); + file->buflevel += n; + file->lblevel = 0; + } + return ferror (file->fp) ? GDBM_FILE_READ_ERROR : 0; +} + +static int +get_parms (struct dump_file *file) +{ + size_t n; + + file->buflevel = 0; + file->parmc = 0; + while ((n = get_dump_line (file))) + { + char *p; + + p = file->linebuf; + if (*p != '#') + return 0; + if (n == 0 || *++p != ':') + { + file->lblevel = 0; + continue; + } + if (--n == 0) + { + file->lblevel = 0; + continue; + } + + if (n + 1 + file->buflevel > file->bufsize) + { + size_t s = ((file->buflevel + n + _GDBM_MAX_DUMP_LINE_LEN) + / _GDBM_MAX_DUMP_LINE_LEN) + * _GDBM_MAX_DUMP_LINE_LEN; + char *newp = realloc (file->buffer, s); + if (!newp) + return GDBM_MALLOC_ERROR; + file->buffer = newp; + file->bufsize = s; + } + + while (*p) + { + p++; + while (*p == ' ' || *p == '\t') + p++; + if (*p) + { + while (*p && *p != '=') + file->buffer[file->buflevel++] = *p++; + + if (*p == '=') + { + file->buffer[file->buflevel++] = *p++; + if (*p == '"') + { + p++; + while (*p && *p != '"') + file->buffer[file->buflevel++] = *p++; + + if (*p == '"') + p++; + } + else + { + while (!(*p == 0 || *p == ',')) + file->buffer[file->buflevel++] = *p++; + } + file->parmc++; + file->buffer[file->buflevel++] = 0; + } + else + return GDBM_ILLEGAL_DATA; + } + else + break; + } + file->lblevel = 0; + } + + file->buffer[file->buflevel] = 0; + + return ferror (file->fp) ? GDBM_FILE_READ_ERROR : 0; +} + +int +get_len (const char *param, size_t *plen) +{ + unsigned long n; + const char *p = getparm (param, "len"); + char *end; + + if (!p) + return GDBM_ITEM_NOT_FOUND; + + errno = 0; + n = strtoul (p, &end, 10); + if (*end == 0 && errno == 0) + { + *plen = n; + return 0; + } + + return GDBM_ILLEGAL_DATA; +} + +int +read_record (struct dump_file *file, char *param, int n, datum *dat) +{ + int rc; + size_t len, consumed_size, decoded_size; + + if (!param) + { + rc = get_parms (file); + if (rc) + return rc; + if (file->parmc == 0) + return GDBM_ITEM_NOT_FOUND; + param = file->buffer; + } + rc = get_len (param, &len); + if (rc) + return rc; + dat->dsize = len; /* FIXME: data type mismatch */ + rc = get_data (file); + if (rc) + return rc; + + rc = _gdbm_base64_decode ((unsigned char *)file->buffer, file->buflevel, + &file->data[n].buffer, &file->data[n].size, + &consumed_size, &decoded_size); + if (rc) + return rc; + if (consumed_size != file->buflevel || decoded_size != len) + return GDBM_ILLEGAL_DATA; + dat->dptr = (void*) file->data[n].buffer; + return 0; +} + +#define META_UID 0x01 +#define META_GID 0x02 +#define META_MODE 0x04 + +static int +_set_gdbm_meta_info (GDBM_FILE dbf, char *param, int meta_mask) +{ + unsigned long n; + uid_t owner_uid; + uid_t owner_gid; + mode_t mode; + int meta_flags = 0; + const char *p; + char *end; + int rc = 0; + + if (!(meta_mask & GDBM_META_MASK_OWNER)) + { + p = getparm (param, "user"); + if (p) + { + struct passwd *pw = getpwnam (p); + if (pw) + { + owner_uid = pw->pw_uid; + meta_flags |= META_UID; + } + } + + if (!(meta_flags & META_UID) && (p = getparm (param, "uid"))) + { + errno = 0; + n = strtoul (p, &end, 10); + if (*end == 0 && errno == 0) + { + owner_uid = n; + meta_flags |= META_UID; + } + } + + p = getparm (param, "group"); + if (p) + { + struct group *gr = getgrnam (p); + if (gr) + { + owner_gid = gr->gr_gid; + meta_flags |= META_GID; + } + } + if (!(meta_flags & META_GID) && (p = getparm (param, "gid"))) + { + errno = 0; + n = strtoul (p, &end, 10); + if (*end == 0 && errno == 0) + { + owner_gid = n; + meta_flags |= META_GID; + } + } + } + + if (!(meta_mask & GDBM_META_MASK_MODE)) + { + p = getparm (param, "mode"); + if (p) + { + errno = 0; + n = strtoul (p, &end, 8); + if (*end == 0 && errno == 0) + { + mode = n & 0777; + meta_flags |= META_MODE; + } + } + } + + if (meta_flags) + { + int fd = gdbm_fdesc (dbf); + if (getuid () == 0 && (meta_flags & (META_UID|META_GID))) + { + if ((meta_flags & (META_UID|META_GID)) != (META_UID|META_GID)) + { + struct stat st; + fstat (fd, &st); + if (!(meta_flags & META_UID)) + owner_uid = st.st_uid; + if (!(meta_flags & META_GID)) + owner_gid = st.st_gid; + } + if (fchown (fd, owner_uid, owner_gid)) + { + gdbm_errno = GDBM_ERR_FILE_OWNER; + rc = 1; + } + } + if ((meta_flags & META_MODE) && fchmod (fd, mode)) + { + gdbm_errno = GDBM_ERR_FILE_OWNER; + rc = 1; + } + } + return rc; +} + +int +_gdbm_load_file (struct dump_file *file, GDBM_FILE dbf, GDBM_FILE *ofp, + int replace, int meta_mask) +{ + char *param = NULL; + int rc; + GDBM_FILE tmp = NULL; + + rc = get_parms (file); + if (rc) + return rc; + + if (file->parmc) + { + file->header = file->buffer; + file->buffer = NULL; + file->bufsize = file->buflevel = 0; + } + else + return GDBM_ILLEGAL_DATA; + + if (!dbf) + { + const char *filename = getparm (file->header, "file"); + if (!filename) + return GDBM_NO_DBNAME; + tmp = gdbm_open (filename, 0, + replace ? GDBM_WRCREAT : GDBM_NEWDB, 0600, NULL); + if (!tmp) + return gdbm_errno; + dbf = tmp; + } + + param = file->header; + while (1) + { + datum key, content; + rc = read_record (file, param, 0, &key); + if (rc) + { + if (rc == GDBM_ITEM_NOT_FOUND && feof (file->fp)) + rc = 0; + break; + } + param = NULL; + + rc = read_record (file, NULL, 1, &content); + if (rc) + break; + + if (gdbm_store (dbf, key, content, replace)) + { + rc = gdbm_errno; + break; + } + } + + if (rc == 0) + { + rc = _set_gdbm_meta_info (dbf, file->header, meta_mask); + *ofp = dbf; + } + else if (tmp) + gdbm_close (tmp); + + return rc; +} + +static int +read_bdb_header (struct dump_file *file) +{ + char buf[256]; + + file->line = 1; + if (!fgets (buf, sizeof (buf), file->fp)) + return -1; + if (strcmp (buf, "VERSION=3\n")) + return -1; + while (fgets (buf, sizeof (buf), file->fp)) + { + ++file->line; + if (strcmp (buf, "HEADER=END\n") == 0) + return 0; + } + return -1; +} + +static int +c2x (int c) +{ + static char xdig[] = "0123456789abcdef"; + char *p = strchr (xdig, c); + if (!p) + return -1; + return p - xdig; +} + +#define DINCR 128 + +static int +xdatum_read (FILE *fp, datum *d, size_t *pdmax) +{ + int c; + size_t dmax = *pdmax; + + d->dsize = 0; + while ((c = fgetc (fp)) != EOF && c != '\n') + { + int t, n; + + t = c2x (c); + if (t == -1) + return EOF; + t <<= 4; + + if ((c = fgetc (fp)) == EOF) + break; + + n = c2x (c); + if (n == -1) + return EOF; + t += n; + + if (d->dsize == dmax) + { + char *np = realloc (d->dptr, dmax + DINCR); + if (!np) + return GDBM_MALLOC_ERROR; + d->dptr = np; + dmax += DINCR; + } + d->dptr[d->dsize++] = t; + } + *pdmax = dmax; + if (c == '\n') + return 0; + return c; +} + +int +gdbm_load_bdb_dump (struct dump_file *file, GDBM_FILE dbf, int replace) +{ + datum xd[2]; + size_t xs[2]; + int rc, c; + int i; + + if (read_bdb_header (file)) + return -1; + memset (&xd, 0, sizeof (xd)); + xs[0] = xs[1] = 0; + i = 0; + while ((c = fgetc (file->fp)) == ' ') + { + rc = xdatum_read (file->fp, &xd[i], &xs[i]); + if (rc) + break; + ++file->line; + + if (i == 1) + { + if (gdbm_store (dbf, xd[0], xd[1], replace)) + return gdbm_errno; + } + i = !i; + } + //FIXME: Read "DATA=END" + free (xd[0].dptr); + free (xd[1].dptr); + if (rc == 0 && i) + rc = EOF; + + return rc; +} + +int +gdbm_load_from_file (GDBM_FILE *pdbf, FILE *fp, int replace, + int meta_mask, + unsigned long *line) +{ + struct dump_file df; + int rc; + + if (!pdbf || !fp) + return EINVAL; + + /* Guess input file format */ + rc = fgetc (fp); + ungetc (rc, fp); + if (rc == '!') + { + if (line) + *line = 0; + if (!*pdbf) + { + gdbm_errno = GDBM_NO_DBNAME; + return -1; + } + if (gdbm_import_from_file (*pdbf, fp, replace) == -1) + return -1; + return 0; + } + + memset (&df, 0, sizeof df); + df.fp = fp; + + if (rc == 'V') + { + if (!*pdbf) + { + gdbm_errno = GDBM_NO_DBNAME; + return -1; + } + rc = gdbm_load_bdb_dump (&df, *pdbf, replace); + } + else + rc = _gdbm_load_file (&df, *pdbf, pdbf, replace, meta_mask); + dump_file_free (&df); + if (rc) + { + if (line) + *line = df.line; + gdbm_errno = rc; + return -1; + } + return 0; +} + +int +gdbm_load (GDBM_FILE *pdbf, const char *filename, int replace, + int meta_mask, + unsigned long *line) +{ + FILE *fp; + int rc; + + fp = fopen (filename, "r"); + if (!fp) + { + gdbm_errno = GDBM_FILE_OPEN_ERROR; + return -1; + } + rc = gdbm_load_from_file (pdbf, fp, replace, meta_mask, line); + fclose (fp); + return rc; +} + diff --git a/src/gdbmopen.c b/src/gdbmopen.c new file mode 100644 index 0000000..14c687d --- /dev/null +++ b/src/gdbmopen.c @@ -0,0 +1,475 @@ +/* gdbmopen.c - Open the dbm file and initialize data structures for use. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + +/* Determine our native magic number and bail if we can't. */ +#if SIZEOF_OFF_T == 4 +# define GDBM_MAGIC GDBM_MAGIC32 +#elif SIZEOF_OFF_T == 8 +# define GDBM_MAGIC GDBM_MAGIC64 +#else +# error "Unsupported off_t size, contact GDBM maintainer. What crazy system is this?!?" +#endif + +/* Initialize dbm system. FILE is a pointer to the file name. If the file + has a size of zero bytes, a file initialization procedure is performed, + setting up the initial structure in the file. BLOCK_SIZE is used during + initialization to determine the size of various constructs. If the value + is less than 512, the file system blocksize is used, otherwise the value + of BLOCK_SIZE is used. BLOCK_SIZE is ignored if the file has previously + initialized. If FLAGS is set to GDBM_READ the user wants to just + read the database and any call to dbm_store or dbm_delete will fail. Many + readers can access the database at the same time. If FLAGS is set to + GDBM_WRITE, the user wants both read and write access to the database and + requires exclusive access. If FLAGS is GDBM_WRCREAT, the user wants + both read and write access to the database and if the database does not + exist, create a new one. If FLAGS is GDBM_NEWDB, the user want a + new database created, regardless of whether one existed, and wants read + and write access to the new database. Any error detected will cause a + return value of null and an approprate value will be in gdbm_errno. If + no errors occur, a pointer to the "gdbm file descriptor" will be + returned. */ + + +GDBM_FILE +gdbm_open (const char *file, int block_size, int flags, int mode, + void (*fatal_func) (const char *)) +{ + GDBM_FILE dbf; /* The record to return. */ + struct stat file_stat; /* Space for the stat information. */ + int len; /* Length of the file name. */ + off_t file_pos; /* Used with seeks. */ + int file_block_size; /* Block size to use for a new file. */ + int index; /* Used as a loop index. */ + char need_trunc; /* Used with GDBM_NEWDB and locking to avoid + truncating a file from under a reader. */ + int rc; /* temporary error code */ + int fbits = 0; /* additional bits for open(2) flags */ + + /* Initialize the gdbm_errno variable. */ + gdbm_errno = GDBM_NO_ERROR; + + /* Allocate new info structure. */ + dbf = (GDBM_FILE) malloc (sizeof (*dbf)); + if (dbf == NULL) + { + gdbm_errno = GDBM_MALLOC_ERROR; + return NULL; + } + + /* Initialize some fields for known values. This is done so gdbm_close + will work if called before allocating some structures. */ + dbf->dir = NULL; + dbf->bucket = NULL; + dbf->header = NULL; + dbf->bucket_cache = NULL; + dbf->cache_size = 0; + + dbf->memory_mapping = FALSE; + dbf->mapped_size_max = SIZE_T_MAX; + dbf->mapped_region = NULL; + dbf->mapped_size = 0; + dbf->mapped_pos = 0; + dbf->mapped_off = 0; + + /* Save name of file. */ + len = strlen (file); + dbf->name = (char *) malloc (len + 1); + if (dbf->name == NULL) + { + free (dbf); + gdbm_errno = GDBM_MALLOC_ERROR; + return NULL; + } + strcpy (dbf->name, file); + + /* Initialize the fatal error routine. */ + dbf->fatal_err = fatal_func; + + dbf->fast_write = TRUE; /* Default to setting fast_write. */ + dbf->file_locking = TRUE; /* Default to doing file locking. */ + dbf->central_free = FALSE; /* Default to not using central_free. */ + dbf->coalesce_blocks = FALSE; /* Default to not coalescing blocks. */ + + /* GDBM_FAST used to determine whether or not we set fast_write. */ + if (flags & GDBM_SYNC) + { + /* If GDBM_SYNC has been requested, don't do fast_write. */ + dbf->fast_write = FALSE; + } + if (flags & GDBM_NOLOCK) + { + dbf->file_locking = FALSE; + } + if (flags & GDBM_CLOEXEC) + { + fbits = O_CLOEXEC; + dbf->cloexec = TRUE; + } + else + dbf->cloexec = FALSE; + + /* Open the file. */ + need_trunc = FALSE; + switch (flags & GDBM_OPENMASK) + { + case GDBM_READER: + dbf->desc = open (dbf->name, O_RDONLY|fbits, 0); + break; + + case GDBM_WRITER: + dbf->desc = open (dbf->name, O_RDWR|fbits, 0); + break; + + case GDBM_NEWDB: + dbf->desc = open (dbf->name, O_RDWR|O_CREAT|fbits, mode); + need_trunc = TRUE; + break; + + default: + dbf->desc = open (dbf->name, O_RDWR|O_CREAT|fbits, mode); + break; + + } + if (dbf->desc < 0) + { + SAVE_ERRNO (free (dbf->name); + free (dbf)); + gdbm_errno = GDBM_FILE_OPEN_ERROR; + return NULL; + } + + /* Get the status of the file. */ + if (fstat (dbf->desc, &file_stat)) + { + SAVE_ERRNO (close (dbf->desc); + free (dbf->name); + free (dbf)); + gdbm_errno = GDBM_FILE_STAT_ERROR; + return NULL; + } + + /* Zero-length file can't be a reader... */ + if (((flags & GDBM_OPENMASK) == GDBM_READER) && (file_stat.st_size == 0)) + { + close (dbf->desc); + free (dbf->name); + free (dbf); + gdbm_errno = GDBM_EMPTY_DATABASE; + return NULL; + } + + /* Record the kind of user. */ + dbf->read_write = (flags & GDBM_OPENMASK); + + /* Lock the file in the appropriate way. */ + if (dbf->file_locking) + { + if (_gdbm_lock_file (dbf) == -1) + { + close (dbf->desc); + free (dbf->name); + free (dbf); + if ((flags & GDBM_OPENMASK) == GDBM_READER) + gdbm_errno = GDBM_CANT_BE_READER; + else + gdbm_errno = GDBM_CANT_BE_WRITER; + return NULL; + } + } + + /* If we do have a write lock and it was a GDBM_NEWDB, it is + now time to truncate the file. */ + if (need_trunc && file_stat.st_size != 0) + { + TRUNCATE (dbf); + fstat (dbf->desc, &file_stat); + } + + /* Decide if this is a new file or an old file. */ + if (file_stat.st_size == 0) + { + + /* This is a new file. Create an empty database. */ + + /* Start with the blocksize. */ + if (block_size < 512) + file_block_size = STATBLKSIZE; + else + file_block_size = block_size; + + /* Get space for the file header. It will be written to disk, so + make sure there's no garbage in it. */ + dbf->header = (gdbm_file_header *) calloc (1, file_block_size); + if (dbf->header == NULL) + { + gdbm_close (dbf); + gdbm_errno = GDBM_MALLOC_ERROR; + return NULL; + } + + /* Set the magic number and the block_size. */ + dbf->header->header_magic = GDBM_MAGIC; + dbf->header->block_size = file_block_size; + + /* Create the initial hash table directory. */ + dbf->header->dir_size = 8 * sizeof (off_t); + dbf->header->dir_bits = 3; + while (dbf->header->dir_size < dbf->header->block_size) + { + dbf->header->dir_size <<= 1; + dbf->header->dir_bits += 1; + } + + /* Check for correct block_size. */ + if (dbf->header->dir_size != dbf->header->block_size) + { + gdbm_close (dbf); + gdbm_errno = GDBM_BLOCK_SIZE_ERROR; + return NULL; + } + + /* Allocate the space for the directory. */ + dbf->dir = (off_t *) malloc (dbf->header->dir_size); + if (dbf->dir == NULL) + { + gdbm_close (dbf); + gdbm_errno = GDBM_MALLOC_ERROR; + return NULL; + } + dbf->header->dir = dbf->header->block_size; + + /* Create the first and only hash bucket. */ + dbf->header->bucket_elems = + (dbf->header->block_size - sizeof (hash_bucket)) + / sizeof (bucket_element) + 1; + dbf->header->bucket_size = dbf->header->block_size; + dbf->bucket = (hash_bucket *) malloc (dbf->header->bucket_size); + if (dbf->bucket == NULL) + { + gdbm_close (dbf); + gdbm_errno = GDBM_MALLOC_ERROR; + return NULL; + } + _gdbm_new_bucket (dbf, dbf->bucket, 0); + dbf->bucket->av_count = 1; + dbf->bucket->bucket_avail[0].av_adr = 3*dbf->header->block_size; + dbf->bucket->bucket_avail[0].av_size = dbf->header->block_size; + + /* Set table entries to point to hash buckets. */ + for (index = 0; index < GDBM_DIR_COUNT (dbf); index++) + dbf->dir[index] = 2*dbf->header->block_size; + + /* Initialize the active avail block. */ + dbf->header->avail.size + = ( (dbf->header->block_size - sizeof (gdbm_file_header)) + / sizeof (avail_elem)) + 1; + dbf->header->avail.count = 0; + dbf->header->avail.next_block = 0; + dbf->header->next_block = 4*dbf->header->block_size; + + /* Write initial configuration to the file. */ + /* Block 0 is the file header and active avail block. */ + rc = _gdbm_full_write (dbf, dbf->header, dbf->header->block_size); + if (rc) + { + SAVE_ERRNO (gdbm_close (dbf)); + gdbm_errno = rc; + return NULL; + } + + /* Block 1 is the initial bucket directory. */ + rc = _gdbm_full_write (dbf, dbf->dir, dbf->header->dir_size); + if (rc) + { + SAVE_ERRNO (gdbm_close (dbf)); + gdbm_errno = rc; + return NULL; + } + + /* Block 2 is the only bucket. */ + rc = _gdbm_full_write (dbf, dbf->bucket, dbf->header->bucket_size); + if (rc) + { + SAVE_ERRNO (gdbm_close (dbf)); + gdbm_errno = rc; + return NULL; + } + + /* Wait for initial configuration to be written to disk. */ + __fsync (dbf); + + free (dbf->bucket); + } + else + { + /* This is an old database. Read in the information from the file + header and initialize the hash directory. */ + + gdbm_file_header partial_header; /* For the first part of it. */ + + /* Read the partial file header. */ + rc = _gdbm_full_read (dbf, &partial_header, sizeof (gdbm_file_header)); + if (rc) + { + SAVE_ERRNO (gdbm_close (dbf)); + gdbm_errno = rc; + return NULL; + } + + /* Is the magic number good? */ + if (partial_header.header_magic != GDBM_MAGIC + && partial_header.header_magic != GDBM_OMAGIC) + { + gdbm_close (dbf); + switch (partial_header.header_magic) + { + case GDBM_OMAGIC_SWAP: + case GDBM_MAGIC32_SWAP: + case GDBM_MAGIC64_SWAP: + gdbm_errno = GDBM_BYTE_SWAPPED; + break; + case GDBM_MAGIC32: + case GDBM_MAGIC64: + gdbm_errno = GDBM_BAD_FILE_OFFSET; + break; + default: + gdbm_errno = GDBM_BAD_MAGIC_NUMBER; + } + return NULL; + } + + /* It is a good database, read the entire header. */ + dbf->header = (gdbm_file_header *) malloc (partial_header.block_size); + if (dbf->header == NULL) + { + gdbm_close (dbf); + gdbm_errno = GDBM_MALLOC_ERROR; + return NULL; + } + memcpy (dbf->header, &partial_header, sizeof (gdbm_file_header)); + rc = _gdbm_full_read (dbf, &dbf->header->avail.av_table[1], + dbf->header->block_size-sizeof (gdbm_file_header)); + if (rc) + { + SAVE_ERRNO (gdbm_close (dbf)); + gdbm_errno = rc; + return NULL; + } + + /* Allocate space for the hash table directory. */ + dbf->dir = (off_t *) malloc (dbf->header->dir_size); + if (dbf->dir == NULL) + { + gdbm_close (dbf); + gdbm_errno = GDBM_MALLOC_ERROR; + return NULL; + } + + /* Read the hash table directory. */ + file_pos = __lseek (dbf, dbf->header->dir, SEEK_SET); + if (file_pos != dbf->header->dir) + { + SAVE_ERRNO (gdbm_close (dbf)); + gdbm_errno = GDBM_FILE_SEEK_ERROR; + return NULL; + } + + rc = _gdbm_full_read (dbf, dbf->dir, dbf->header->dir_size); + if (rc) + { + SAVE_ERRNO (gdbm_close (dbf)); + gdbm_errno = rc; + return NULL; + } + + } + +#if HAVE_MMAP + if (!(flags & GDBM_NOMMAP)) + { + if (_gdbm_mapped_init (dbf) == 0) + dbf->memory_mapping = TRUE; + else + { + /* gdbm_errno should already be set. */ + close (dbf->desc); + free (dbf->name); + free (dbf); + return NULL; + } + } +#endif + + /* Finish initializing dbf. */ + dbf->last_read = -1; + dbf->bucket = NULL; + dbf->bucket_dir = 0; + dbf->cache_entry = NULL; + dbf->header_changed = FALSE; + dbf->directory_changed = FALSE; + dbf->bucket_changed = FALSE; + dbf->second_changed = FALSE; + + /* Everything is fine, return the pointer to the file + information structure. */ + return dbf; + +} + +/* Initialize the bucket cache. */ +int +_gdbm_init_cache(GDBM_FILE dbf, size_t size) +{ + int index; + + if (dbf->bucket_cache == NULL) + { + dbf->bucket_cache = (cache_elem *) malloc(sizeof(cache_elem) * size); + if(dbf->bucket_cache == NULL) + { + gdbm_errno = GDBM_MALLOC_ERROR; + return -1; + } + dbf->cache_size = size; + + for(index = 0; index < size; index++) + { + (dbf->bucket_cache[index]).ca_bucket + = (hash_bucket *) malloc (dbf->header->bucket_size); + if ((dbf->bucket_cache[index]).ca_bucket == NULL) + { + gdbm_errno = GDBM_MALLOC_ERROR; + return -1; + } + (dbf->bucket_cache[index]).ca_adr = 0; + (dbf->bucket_cache[index]).ca_changed = FALSE; + (dbf->bucket_cache[index]).ca_data.hash_val = -1; + (dbf->bucket_cache[index]).ca_data.elem_loc = -1; + (dbf->bucket_cache[index]).ca_data.dptr = NULL; + } + dbf->bucket = dbf->bucket_cache[0].ca_bucket; + dbf->cache_entry = &dbf->bucket_cache[0]; + } + return 0; +} diff --git a/src/gdbmreorg.c b/src/gdbmreorg.c new file mode 100644 index 0000000..7e554cb --- /dev/null +++ b/src/gdbmreorg.c @@ -0,0 +1,218 @@ +/* gdbmreorg.c - Reorganize the database file. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + +#if !HAVE_RENAME + +/* Rename takes OLD_NAME and renames it as NEW_NAME. If it can not rename + the file a non-zero value is returned. OLD_NAME is guaranteed to + remain if it can't be renamed. It assumes NEW_NAME always exists (due + to being used in gdbm). */ + +static int +_gdbm_rename (char *old_name, char *new_name) +{ + if (unlink (new_name) != 0) + return -1; + + if (link (old_name, new_name) != 0) + return -1; + + unlink (old_name); + return 0; + +} + +# define rename _gdbm_rename +#endif + + + +/* Reorganize the database. This requires creating a new file and inserting + all the elements in the old file DBF into the new file. The new file + is then renamed to the same name as the old file and DBF is updated to + contain all the correct information about the new file. If an error + is detected, the return value is negative. The value zero is returned + after a successful reorganization. */ + +int +gdbm_reorganize (GDBM_FILE dbf) +{ + GDBM_FILE new_dbf; /* The new file. */ + char *new_name; /* A temporary name. */ + int len; /* Used in new_name construction. */ + datum key, nextkey, data; /* For the sequential sweep. */ + struct stat fileinfo; /* Information about the file. */ + int index; /* Use in moving the bucket cache. */ + + + /* Readers can not reorganize! */ + if (dbf->read_write == GDBM_READER) + { + gdbm_errno = GDBM_READER_CANT_REORGANIZE; + return -1; + } + + /* Get the mode for the old file */ + if (fstat (dbf->desc, &fileinfo)) + { + gdbm_errno = GDBM_FILE_STAT_ERROR; + return -1; + } + + /* Initialize the gdbm_errno variable. */ + gdbm_errno = GDBM_NO_ERROR; + + /* Construct new name for temporary file. */ + len = strlen (dbf->name); + new_name = (char *) malloc (len + 3); + if (new_name == NULL) + { + gdbm_errno = GDBM_MALLOC_ERROR; + return -1; + } + strcpy (&new_name[0], dbf->name); + new_name[len+2] = 0; + new_name[len+1] = '#'; + while ( (len > 0) && new_name[len-1] != '/') + { + new_name[len] = new_name[len-1]; + len -= 1; + } + new_name[len] = '#'; + + /* Open the new database. */ + new_dbf = gdbm_open (new_name, dbf->header->block_size, + GDBM_WRCREAT | (dbf->cloexec ? GDBM_CLOEXEC : 0), + fileinfo.st_mode, dbf->fatal_err); + + if (new_dbf == NULL) + { + free (new_name); + gdbm_errno = GDBM_REORGANIZE_FAILED; + return -1; + } + + + /* For each item in the old database, add an entry in the new. */ + key = gdbm_firstkey (dbf); + + while (key.dptr != NULL) + { + data = gdbm_fetch (dbf, key); + if (data.dptr != NULL) + { + /* Add the data to the new file. */ + if (gdbm_store (new_dbf, key, data, GDBM_INSERT) != 0) + { + gdbm_close (new_dbf); + gdbm_errno = GDBM_REORGANIZE_FAILED; + unlink (new_name); + free (new_name); + return -1; + } + } + else + { + /* ERROR! Abort and don't finish reorganize. */ + gdbm_close (new_dbf); + gdbm_errno = GDBM_REORGANIZE_FAILED; + unlink (new_name); + free (new_name); + return -1; + } + nextkey = gdbm_nextkey (dbf, key); + free (key.dptr); + free (data.dptr); + key = nextkey; + } + + /* Write everything. */ + _gdbm_end_update (new_dbf); + gdbm_sync (new_dbf); + +#if HAVE_MMAP + _gdbm_mapped_unmap (dbf); +#endif + + /* Move the new file to old name. */ + + if (rename (new_name, dbf->name) != 0) + { + gdbm_errno = GDBM_REORGANIZE_FAILED; + gdbm_close (new_dbf); + free (new_name); + return -1; + } + + /* Fix up DBF to have the correct information for the new file. */ + if (dbf->file_locking) + { + _gdbm_unlock_file (dbf); + } + close (dbf->desc); + free (dbf->header); + free (dbf->dir); + + if (dbf->bucket_cache != NULL) { + for (index = 0; index < dbf->cache_size; index++) { + if (dbf->bucket_cache[index].ca_bucket != NULL) + free (dbf->bucket_cache[index].ca_bucket); + if (dbf->bucket_cache[index].ca_data.dptr != NULL) + free (dbf->bucket_cache[index].ca_data.dptr); + } + free (dbf->bucket_cache); + } + + dbf->desc = new_dbf->desc; + dbf->header = new_dbf->header; + dbf->dir = new_dbf->dir; + dbf->bucket = new_dbf->bucket; + dbf->bucket_dir = new_dbf->bucket_dir; + dbf->last_read = new_dbf->last_read; + dbf->bucket_cache = new_dbf->bucket_cache; + dbf->cache_size = new_dbf->cache_size; + dbf->header_changed = new_dbf->header_changed; + dbf->directory_changed = new_dbf->directory_changed; + dbf->bucket_changed = new_dbf->bucket_changed; + dbf->second_changed = new_dbf->second_changed; + +#if HAVE_MMAP + /* Re-initialize mapping if required */ + if (dbf->memory_mapping) + _gdbm_mapped_init (dbf); +#endif + + free (new_dbf->name); + free (new_dbf); + free (new_name); + + /* Make sure the new database is all on disk. */ + __fsync (dbf); + + /* Force the right stuff for a correct bucket cache. */ + dbf->cache_entry = &dbf->bucket_cache[0]; + _gdbm_get_bucket (dbf, 0); + + return 0; +} diff --git a/src/gdbmseq.c b/src/gdbmseq.c new file mode 100644 index 0000000..4167697 --- /dev/null +++ b/src/gdbmseq.c @@ -0,0 +1,129 @@ +/* gdbmseq.c - Routines to visit all keys. Not in sorted order. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + +/* Special extern for this file. */ +extern char *_gdbm_read_entry (GDBM_FILE , int); + + +/* Find and read the next entry in the hash structure for DBF starting + at ELEM_LOC of the current bucket and using RETURN_VAL as the place to + put the data that is found. */ + +static void +get_next_key (GDBM_FILE dbf, int elem_loc, datum *return_val) +{ + int found; /* Have we found the next key. */ + char *find_data; /* Data pointer returned by find_key. */ + + /* Find the next key. */ + found = FALSE; + while (!found) + { + /* Advance to the next location in the bucket. */ + elem_loc++; + if (elem_loc == dbf->header->bucket_elems) + { + /* We have finished the current bucket, get the next bucket. */ + elem_loc = 0; + + /* Find the next bucket. It is possible several entries in + the bucket directory point to the same bucket. */ + while (dbf->bucket_dir < GDBM_DIR_COUNT (dbf) + && dbf->cache_entry->ca_adr == dbf->dir[dbf->bucket_dir]) + dbf->bucket_dir++; + + /* Check to see if there was a next bucket. */ + if (dbf->bucket_dir < GDBM_DIR_COUNT (dbf)) + _gdbm_get_bucket (dbf, dbf->bucket_dir); + else + /* No next key, just return. */ + return ; + } + found = dbf->bucket->h_table[elem_loc].hash_value != -1; + } + + /* Found the next key, read it into return_val. */ + find_data = _gdbm_read_entry (dbf, elem_loc); + return_val->dsize = dbf->bucket->h_table[elem_loc].key_size; + if (return_val->dsize == 0) + return_val->dptr = (char *) malloc (1); + else + return_val->dptr = (char *) malloc (return_val->dsize); + if (return_val->dptr == NULL) _gdbm_fatal (dbf, _("malloc error")); + memcpy (return_val->dptr, find_data, return_val->dsize); +} + + +/* Start the visit of all keys in the database. This produces something in + hash order, not in any sorted order. */ + +datum +gdbm_firstkey (GDBM_FILE dbf) +{ + datum return_val; /* To return the first key. */ + + /* Set the default return value for not finding a first entry. */ + return_val.dptr = NULL; + + /* Initialize the gdbm_errno variable. */ + gdbm_errno = GDBM_NO_ERROR; + + /* Get the first bucket. */ + _gdbm_get_bucket (dbf, 0); + + /* Look for first entry. */ + get_next_key (dbf, -1, &return_val); + + return return_val; +} + + +/* Continue visiting all keys. The next key following KEY is returned. */ + +datum +gdbm_nextkey (GDBM_FILE dbf, datum key) +{ + datum return_val; /* The return value. */ + int elem_loc; /* The location in the bucket. */ + char *find_data; /* Data pointer returned by _gdbm_findkey. */ + int hash_val; /* Returned by _gdbm_findkey. */ + + /* Initialize the gdbm_errno variable. */ + gdbm_errno = GDBM_NO_ERROR; + + /* Set the default return value for no next entry. */ + return_val.dptr = NULL; + + /* Do we have a valid key? */ + if (key.dptr == NULL) return return_val; + + /* Find the key. */ + elem_loc = _gdbm_findkey (dbf, key, &find_data, &hash_val); + if (elem_loc == -1) return return_val; + + /* Find the next key. */ + get_next_key (dbf, elem_loc, &return_val); + + return return_val; +} diff --git a/src/gdbmsetopt.c b/src/gdbmsetopt.c new file mode 100644 index 0000000..23cdd9a --- /dev/null +++ b/src/gdbmsetopt.c @@ -0,0 +1,258 @@ +/* gdbmsetopt.c - set options pertaining to a GDBM descriptor. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1993, 1994, 2007, 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + +/* operate on an already open descriptor. */ + +static int +getbool (void *optval, int optlen) +{ + int n; + + if (!optval || optlen != sizeof (int) || + (((n = *(int*)optval) != TRUE) && n != FALSE)) + { + gdbm_errno = GDBM_OPT_ILLEGAL; + return -1; + } + return n; +} + +static int +get_size (void *optval, int optlen, size_t *ret) +{ + if (!optval) + { + gdbm_errno = GDBM_OPT_ILLEGAL; + return -1; + } + if (optlen == sizeof (unsigned)) + *ret = *(unsigned*) optval; + else if (optlen == sizeof (unsigned long)) + *ret = *(unsigned long*) optval; + else if (optlen == sizeof (size_t)) + *ret = *(size_t*) optval; + else + { + gdbm_errno = GDBM_OPT_ILLEGAL; + return -1; + } + return 0; +} + +int +gdbm_setopt (GDBM_FILE dbf, int optflag, void *optval, int optlen) +{ + int n; + size_t sz; + + switch (optflag) + { + /* Cache size: */ + + case GDBM_SETCACHESIZE: + /* Optval will point to the new size of the cache. */ + if (dbf->bucket_cache != NULL) + { + gdbm_errno = GDBM_OPT_ALREADY_SET; + return -1; + } + + if (get_size (optval, optlen, &sz)) + return -1; + return _gdbm_init_cache (dbf, (sz > 9) ? sz : 10); + + case GDBM_GETCACHESIZE: + if (!optval || optlen != sizeof (size_t)) + { + gdbm_errno = GDBM_OPT_ILLEGAL; + return -1; + } + *(size_t*) optval = dbf->cache_size; + break; + + /* Obsolete form of GDBM_SETSYNCMODE. */ + case GDBM_FASTMODE: + if ((n = getbool (optval, optlen)) == -1) + return -1; + dbf->fast_write = n; + break; + + /* SYNC mode: */ + + case GDBM_SETSYNCMODE: + /* Optval will point to either true or false. */ + if ((n = getbool (optval, optlen)) == -1) + return -1; + dbf->fast_write = !n; + break; + + case GDBM_GETSYNCMODE: + if (!optval || optlen != sizeof (int)) + { + gdbm_errno = GDBM_OPT_ILLEGAL; + return -1; + } + *(int*) optval = !dbf->fast_write; + break; + + /* CENTFREE - set or get the stat of the central block repository */ + case GDBM_SETCENTFREE: + /* Optval will point to either true or false. */ + if ((n = getbool (optval, optlen)) == -1) + return -1; + dbf->central_free = n; + break; + + case GDBM_GETCENTFREE: + if (!optval || optlen != sizeof (int)) + { + gdbm_errno = GDBM_OPT_ILLEGAL; + return -1; + } + *(int*) optval = !dbf->central_free; + break; + + /* Coalesce state: */ + case GDBM_SETCOALESCEBLKS: + /* Optval will point to either true or false. */ + if ((n = getbool (optval, optlen)) == -1) + return -1; + dbf->coalesce_blocks = n; + break; + + case GDBM_GETCOALESCEBLKS: + if (!optval || optlen != sizeof (int)) + { + gdbm_errno = GDBM_OPT_ILLEGAL; + return -1; + } + *(int*) optval = dbf->coalesce_blocks; + break; + + /* Mmap mode */ + case GDBM_SETMMAP: +#if HAVE_MMAP + if ((n = getbool (optval, optlen)) == -1) + return -1; + __fsync (dbf); + if (n == dbf->memory_mapping) + return 0; + if (n) + { + if (_gdbm_mapped_init (dbf) == 0) + dbf->memory_mapping = TRUE; + else + return -1; + } + else + { + _gdbm_mapped_unmap (dbf); + dbf->memory_mapping = FALSE; + } +#else + gdbm_errno = GDBM_OPT_ILLEGAL; + return -1; +#endif + break; + + case GDBM_GETMMAP: + if (!optval || optlen != sizeof (int)) + { + gdbm_errno = GDBM_OPT_ILLEGAL; + return -1; + } + *(int*) optval = dbf->memory_mapping; + break; + + /* Maximum size of a memory mapped region */ + case GDBM_SETMAXMAPSIZE: +#if HAVE_MMAP + { + size_t page_size = sysconf (_SC_PAGESIZE); + + if (get_size (optval, optlen, &sz)) + return -1; + dbf->mapped_size_max = ((sz + page_size - 1) / page_size) * + page_size; + _gdbm_mapped_init (dbf); + break; + } +#else + gdbm_errno = GDBM_OPT_ILLEGAL; + return -1; +#endif + + case GDBM_GETMAXMAPSIZE: + if (!optval || optlen != sizeof (size_t)) + { + gdbm_errno = GDBM_OPT_ILLEGAL; + return -1; + } + *(size_t*) optval = dbf->mapped_size_max; + break; + + /* Flags */ + case GDBM_GETFLAGS: + if (!optval || optlen != sizeof (int)) + { + gdbm_errno = GDBM_OPT_ILLEGAL; + return -1; + } + else + { + int flags = dbf->read_write; + if (!dbf->fast_write) + flags |= GDBM_SYNC; + if (!dbf->file_locking) + flags |= GDBM_NOLOCK; + if (!dbf->memory_mapping) + flags |= GDBM_NOMMAP; + *(int*) optval = flags; + } + break; + + case GDBM_GETDBNAME: + if (!optval || optlen != sizeof (char*)) + { + gdbm_errno = GDBM_OPT_ILLEGAL; + return -1; + } + else + { + char *p = strdup (dbf->name); + if (!p) + { + gdbm_errno = GDBM_MALLOC_ERROR; + return -1; + } + *(char**) optval = p; + } + break; + + default: + gdbm_errno = GDBM_OPT_ILLEGAL; + return -1; + } + + return 0; +} diff --git a/src/gdbmstore.c b/src/gdbmstore.c new file mode 100644 index 0000000..8c6064d --- /dev/null +++ b/src/gdbmstore.c @@ -0,0 +1,155 @@ +/* gdbmstore.c - Add a new key/data pair to the database. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + + +/* Add a new element to the database. CONTENT is keyed by KEY. The + file on disk is updated to reflect the structure of the new database + before returning from this procedure. The FLAGS define the action to + take when the KEY is already in the database. The value GDBM_REPLACE + asks that the old data be replaced by the new CONTENT. The value + GDBM_INSERT asks that an error be returned and no action taken. A + return value of 0 means no errors. A return value of -1 means that + the item was not stored in the data base because the caller was not an + official writer. A return value of 0 means that the item was not stored + because the argument FLAGS was GDBM_INSERT and the KEY was already in + the database. */ + +int +gdbm_store (GDBM_FILE dbf, datum key, datum content, int flags) +{ + int new_hash_val; /* The new hash value. */ + int elem_loc; /* The location in hash bucket. */ + off_t file_adr; /* The address of new space in the file. */ + off_t file_pos; /* The position after a lseek. */ + off_t free_adr; /* For keeping track of a freed section. */ + int free_size; + int new_size; /* Used in allocating space. */ + char *temp; /* Used in _gdbm_findkey call. */ + int rc; + + /* First check to make sure this guy is a writer. */ + if (dbf->read_write == GDBM_READER) + { + gdbm_errno = GDBM_READER_CANT_STORE; + return -1; + } + + /* Check for illegal data values. A NULL dptr field is illegal because + NULL dptr returned by a lookup procedure indicates an error. */ + if ((key.dptr == NULL) || (content.dptr == NULL)) + { + gdbm_errno = GDBM_ILLEGAL_DATA; + return -1; + } + + /* Initialize the gdbm_errno variable. */ + gdbm_errno = GDBM_NO_ERROR; + + /* Look for the key in the file. + A side effect loads the correct bucket and calculates the hash value. */ + elem_loc = _gdbm_findkey (dbf, key, &temp, &new_hash_val); + + /* Initialize these. */ + file_adr = 0; + new_size = key.dsize + content.dsize; + + /* Did we find the item? */ + if (elem_loc != -1) + { + if (flags == GDBM_REPLACE) + { + /* Just replace the data. */ + free_adr = dbf->bucket->h_table[elem_loc].data_pointer; + free_size = dbf->bucket->h_table[elem_loc].key_size + + dbf->bucket->h_table[elem_loc].data_size; + if (free_size != new_size) + { + _gdbm_free (dbf, free_adr, free_size); + } + else + { + /* Just reuse the same address! */ + file_adr = free_adr; + } + } + else + { + gdbm_errno = GDBM_CANNOT_REPLACE; + return 1; + } + } + + + /* Get the file address for the new space. + (Current bucket's free space is first place to look.) */ + if (file_adr == 0) + file_adr = _gdbm_alloc (dbf, new_size); + + /* If this is a new entry in the bucket, we need to do special things. */ + if (elem_loc == -1) + { + if (dbf->bucket->count == dbf->header->bucket_elems) + { + /* Split the current bucket. */ + _gdbm_split_bucket (dbf, new_hash_val); + } + + /* Find space to insert into bucket and set elem_loc to that place. */ + elem_loc = new_hash_val % dbf->header->bucket_elems; + while (dbf->bucket->h_table[elem_loc].hash_value != -1) + elem_loc = (elem_loc + 1) % dbf->header->bucket_elems; + + /* We now have another element in the bucket. Add the new information.*/ + dbf->bucket->count++; + dbf->bucket->h_table[elem_loc].hash_value = new_hash_val; + memcpy (dbf->bucket->h_table[elem_loc].key_start, key.dptr, + (SMALL < key.dsize ? SMALL : key.dsize)); + } + + + /* Update current bucket data pointer and sizes. */ + dbf->bucket->h_table[elem_loc].data_pointer = file_adr; + dbf->bucket->h_table[elem_loc].key_size = key.dsize; + dbf->bucket->h_table[elem_loc].data_size = content.dsize; + + /* Write the data to the file. */ + file_pos = __lseek (dbf, file_adr, SEEK_SET); + if (file_pos != file_adr) + _gdbm_fatal (dbf, _("lseek error")); + rc = _gdbm_full_write (dbf, key.dptr, key.dsize); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); + rc = _gdbm_full_write (dbf, content.dptr, content.dsize); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); + + /* Current bucket has changed. */ + dbf->cache_entry->ca_changed = TRUE; + dbf->bucket_changed = TRUE; + + /* Write everything that is needed to the disk. */ + _gdbm_end_update (dbf); + return 0; + +} diff --git a/src/gdbmsync.c b/src/gdbmsync.c new file mode 100644 index 0000000..7635a75 --- /dev/null +++ b/src/gdbmsync.c @@ -0,0 +1,37 @@ +/* gdbmsync.c - Sync the disk with the in memory state. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + +/* Make sure the database is all on disk. */ + +void +gdbm_sync (GDBM_FILE dbf) +{ + + /* Initialize the gdbm_errno variable. */ + gdbm_errno = GDBM_NO_ERROR; + + /* Do the sync on the file. */ + __fsync (dbf); + +} diff --git a/src/gdbmtool.c b/src/gdbmtool.c new file mode 100644 index 0000000..68a7e1d --- /dev/null +++ b/src/gdbmtool.c @@ -0,0 +1,1638 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +#include "gdbmtool.h" +#include "gdbm.h" +#include "gram.h" + +#include <errno.h> +#include <ctype.h> +#include <signal.h> +#include <pwd.h> +#include <sys/ioctl.h> +#ifdef HAVE_SYS_TERMIOS_H +# include <sys/termios.h> +#endif +#include <stdarg.h> +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif + +char *file_name = NULL; /* Database file name */ +GDBM_FILE gdbm_file = NULL; /* Database to operate upon */ +datum key_data; /* Current key */ +datum return_data; /* Current data */ +int open_mode; /* Default open mode */ + +#define SIZE_T_MAX ((size_t)-1) + +unsigned input_line; + + +static int +opendb (char *dbname) +{ + int cache_size = 0; + int block_size = 0; + int flags = 0; + int filemode; + GDBM_FILE db; + + switch (variable_get ("cachesize", VART_INT, (void**) &cache_size)) + { + case VAR_OK: + case VAR_ERR_NOTSET: + break; + default: + abort (); + } + switch (variable_get ("blocksize", VART_INT, (void**) &block_size)) + { + case VAR_OK: + case VAR_ERR_NOTSET: + break; + default: + abort (); + } + + if (!variable_is_true ("lock")) + flags |= GDBM_NOLOCK; + if (!variable_is_true ("mmap")) + flags |= GDBM_NOMMAP; + if (variable_is_true ("sync")) + flags |= GDBM_SYNC; + + if (open_mode == GDBM_NEWDB) + { + if (interactive && variable_is_true ("confirm") && + access (dbname, F_OK) == 0) + { + if (!getyn (_("database %s already exists; overwrite"), dbname)) + return 1; + } + } + + if (variable_get ("filemode", VART_INT, (void**) &filemode)) + abort (); + + db = gdbm_open (dbname, block_size, open_mode | flags, filemode, NULL); + + if (db == NULL) + { + terror (_("cannot open database %s: %s"), dbname, + gdbm_strerror (gdbm_errno)); + return 1; + } + + if (cache_size && + gdbm_setopt (db, GDBM_CACHESIZE, &cache_size, sizeof (int)) == -1) + terror (_("gdbm_setopt failed: %s"), gdbm_strerror (gdbm_errno)); + + if (gdbm_file) + gdbm_close (gdbm_file); + + gdbm_file = db; + return 0; +} + +static int +checkdb () +{ + if (!gdbm_file) + { + if (!file_name) + { + file_name = estrdup (GDBMTOOL_DEFFILE); + terror (_("warning: using default database file %s"), + file_name); + } + return opendb (file_name); + } + return 0; +} + +size_t +bucket_print_lines (hash_bucket *bucket) +{ + return 6 + gdbm_file->header->bucket_elems + 3 + bucket->av_count; +} + +/* Debug procedure to print the contents of the current hash bucket. */ +void +print_bucket (FILE *fp, hash_bucket *bucket, const char *mesg) +{ + int index; + + fprintf (fp, + _("******* %s **********\n\nbits = %d\ncount= %d\nHash Table:\n"), + mesg, bucket->bucket_bits, bucket->count); + fprintf (fp, + _(" # hash value key size data size data adr home\n")); + for (index = 0; index < gdbm_file->header->bucket_elems; index++) + fprintf (fp, " %4d %12x %11d %11d %11lu %5d\n", index, + bucket->h_table[index].hash_value, + bucket->h_table[index].key_size, + bucket->h_table[index].data_size, + (unsigned long) bucket->h_table[index].data_pointer, + bucket->h_table[index].hash_value % + gdbm_file->header->bucket_elems); + + fprintf (fp, _("\nAvail count = %1d\n"), bucket->av_count); + fprintf (fp, _("Avail adr size\n")); + for (index = 0; index < bucket->av_count; index++) + fprintf (fp, "%9lu%9d\n", + (unsigned long) bucket->bucket_avail[index].av_adr, + bucket->bucket_avail[index].av_size); +} + +size_t +_gdbm_avail_list_size (GDBM_FILE dbf, size_t min_size) +{ + int temp; + int size; + avail_block *av_stk; + size_t lines; + int rc; + + lines = 4 + dbf->header->avail.count; + if (lines > min_size) + return lines; + /* Initialize the variables for a pass throught the avail stack. */ + temp = dbf->header->avail.next_block; + size = (((dbf->header->avail.size * sizeof (avail_elem)) >> 1) + + sizeof (avail_block)); + av_stk = emalloc (size); + + /* Traverse the stack. */ + while (temp) + { + if (__lseek (dbf, temp, SEEK_SET) != temp) + { + terror ("lseek: %s", strerror (errno)); + break; + } + + if ((rc = _gdbm_full_read (dbf, av_stk, size))) + { + if (rc == GDBM_FILE_EOF) + terror ("read: %s", gdbm_strerror (rc)); + else + terror ("read: %s (%s)", + gdbm_strerror (rc), strerror (errno)); + break; + } + + lines += av_stk->count; + if (lines > min_size) + break; + temp = av_stk->next_block; + } + free (av_stk); + + return lines; +} + +void +_gdbm_print_avail_list (FILE *fp, GDBM_FILE dbf) +{ + int temp; + int size; + avail_block *av_stk; + int rc; + + /* Print the the header avail block. */ + fprintf (fp, _("\nheader block\nsize = %d\ncount = %d\n"), + dbf->header->avail.size, dbf->header->avail.count); + for (temp = 0; temp < dbf->header->avail.count; temp++) + { + fprintf (fp, " %15d %10lu \n", + dbf->header->avail.av_table[temp].av_size, + (unsigned long) dbf->header->avail.av_table[temp].av_adr); + } + + /* Initialize the variables for a pass throught the avail stack. */ + temp = dbf->header->avail.next_block; + size = (((dbf->header->avail.size * sizeof (avail_elem)) >> 1) + + sizeof (avail_block)); + av_stk = emalloc (size); + + /* Print the stack. */ + while (temp) + { + if (__lseek (dbf, temp, SEEK_SET) != temp) + { + terror ("lseek: %s", strerror (errno)); + break; + } + + if ((rc = _gdbm_full_read (dbf, av_stk, size))) + { + if (rc == GDBM_FILE_EOF) + terror ("read: %s", gdbm_strerror (rc)); + else + terror ("read: %s (%s)", gdbm_strerror (rc), strerror (errno)); + break; + } + + /* Print the block! */ + fprintf (fp, _("\nblock = %d\nsize = %d\ncount = %d\n"), temp, + av_stk->size, av_stk->count); + for (temp = 0; temp < av_stk->count; temp++) + { + fprintf (fp, " %15d %10lu \n", av_stk->av_table[temp].av_size, + (unsigned long) av_stk->av_table[temp].av_adr); + } + temp = av_stk->next_block; + } + free (av_stk); +} + +void +_gdbm_print_bucket_cache (FILE *fp, GDBM_FILE dbf) +{ + int index; + char changed; + + if (dbf->bucket_cache != NULL) + { + fprintf (fp, + _("Bucket Cache (size %d):\n Index: Address Changed Data_Hash \n"), + dbf->cache_size); + for (index = 0; index < dbf->cache_size; index++) + { + changed = dbf->bucket_cache[index].ca_changed; + fprintf (fp, " %5d: %7lu %7s %x\n", + index, + (unsigned long) dbf->bucket_cache[index].ca_adr, + (changed ? _("True") : _("False")), + dbf->bucket_cache[index].ca_data.hash_val); + } + } + else + fprintf (fp, _("Bucket cache has not been initialized.\n")); +} + +int +trimnl (char *str) +{ + int len = strlen (str); + + if (str[len - 1] == '\n') + { + str[--len] = 0; + return 1; + } + return 0; +} + +int +get_screen_lines () +{ +#ifdef TIOCGWINSZ + if (isatty (1)) + { + struct winsize ws; + + ws.ws_col = ws.ws_row = 0; + if ((ioctl(1, TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_row == 0) + { + const char *lines = getenv ("LINES"); + if (lines) + ws.ws_row = strtol (lines, NULL, 10); + } + return ws.ws_row; + } +#else + const char *lines = getenv ("LINES"); + if (lines) + return strtol (lines, NULL, 10); +#endif + return -1; +} + + +#define ARG_UNUSED __attribute__ ((__unused__)) + +#define NARGS 5 + +struct handler_param +{ + int argc; + struct gdbmarg **argv; + FILE *fp; + void *data; +}; + + +/* Open database */ +void +open_handler (struct handler_param *param) +{ + if (opendb (param->argv[0]->v.string) == 0) + { + free (file_name); + file_name = estrdup (param->argv[0]->v.string); + } +} + +/* Close database */ +void +close_handler (struct handler_param *param) +{ + if (!gdbm_file) + terror (_("nothing to close")); + else + { + gdbm_close (gdbm_file); + gdbm_file = NULL; + } +} + + +static char * +count_to_str (gdbm_count_t count, char *buf, size_t bufsize) +{ + char *p = buf + bufsize; + + *--p = 0; + if (count == 0) + *--p = '0'; + else + while (count) + { + if (p == buf) + return NULL; + *--p = '0' + count % 10; + count /= 10; + } + return p; +} + +/* count - count items in the database */ +void +count_handler (struct handler_param *param) +{ + gdbm_count_t count; + + if (gdbm_count (gdbm_file, &count)) + terror ("gdbm_count: %s", gdbm_strerror (gdbm_errno)); + else + { + char buf[128]; + char *p = count_to_str (count, buf, sizeof buf); + + if (!p) + terror (_("count buffer overflow")); + else + fprintf (param->fp, + ngettext ("There is %s item in the database.\n", + "There are %s items in the database.\n", + count), + p); + } +} + +/* delete KEY - delete a key*/ +void +delete_handler (struct handler_param *param) +{ + if (gdbm_delete (gdbm_file, param->argv[0]->v.dat) != 0) + { + if (gdbm_errno == GDBM_ITEM_NOT_FOUND) + terror (_("Item not found")); + else + terror (_("Can't delete: %s"), gdbm_strerror (gdbm_errno)); + } +} + +/* fetch KEY - fetch a record by its key */ +void +fetch_handler (struct handler_param *param) +{ + return_data = gdbm_fetch (gdbm_file, param->argv[0]->v.dat); + if (return_data.dptr != NULL) + { + datum_format (param->fp, &return_data, dsdef[DS_CONTENT]); + fputc ('\n', param->fp); + free (return_data.dptr); + } + else + fprintf (stderr, _("No such item found.\n")); +} + +/* store KEY DATA - store data */ +void +store_handler (struct handler_param *param) +{ + if (gdbm_store (gdbm_file, + param->argv[0]->v.dat, param->argv[1]->v.dat, + GDBM_REPLACE) != 0) + fprintf (stderr, _("Item not inserted.\n")); +} + +/* first - begin iteration */ + +void +firstkey_handler (struct handler_param *param) +{ + if (key_data.dptr != NULL) + free (key_data.dptr); + key_data = gdbm_firstkey (gdbm_file); + if (key_data.dptr != NULL) + { + datum_format (param->fp, &key_data, dsdef[DS_KEY]); + fputc ('\n', param->fp); + + return_data = gdbm_fetch (gdbm_file, key_data); + datum_format (param->fp, &return_data, dsdef[DS_CONTENT]); + fputc ('\n', param->fp); + + free (return_data.dptr); + } + else + fprintf (param->fp, _("No such item found.\n")); +} + +/* next [KEY] - next key */ +void +nextkey_handler (struct handler_param *param) +{ + if (param->argc == 1) + { + if (key_data.dptr != NULL) + free (key_data.dptr); + key_data.dptr = emalloc (param->argv[0]->v.dat.dsize); + key_data.dsize = param->argv[0]->v.dat.dsize; + memcpy (key_data.dptr, param->argv[0]->v.dat.dptr, key_data.dsize); + } + return_data = gdbm_nextkey (gdbm_file, key_data); + if (return_data.dptr != NULL) + { + key_data = return_data; + datum_format (param->fp, &key_data, dsdef[DS_KEY]); + fputc ('\n', param->fp); + + return_data = gdbm_fetch (gdbm_file, key_data); + datum_format (param->fp, &return_data, dsdef[DS_CONTENT]); + fputc ('\n', param->fp); + + free (return_data.dptr); + } + else + { + fprintf (stderr, _("No such item found.\n")); + free (key_data.dptr); + key_data.dptr = NULL; + } +} + +/* reorganize */ +void +reorganize_handler (struct handler_param *param ARG_UNUSED) +{ + if (gdbm_reorganize (gdbm_file)) + fprintf (stderr, _("Reorganization failed.\n")); + else + fprintf (stderr, _("Reorganization succeeded.\n")); +} + +/* avail - print available list */ +int +avail_begin (struct handler_param *param ARG_UNUSED, size_t *exp_count) +{ + if (checkdb ()) + return 1; + if (exp_count) + *exp_count = _gdbm_avail_list_size (gdbm_file, SIZE_T_MAX); + return 0; +} + +void +avail_handler (struct handler_param *param) +{ + _gdbm_print_avail_list (param->fp, gdbm_file); +} + +/* C - print current bucket */ +int +print_current_bucket_begin (struct handler_param *param ARG_UNUSED, + size_t *exp_count) +{ + if (checkdb ()) + return 1; + + if (exp_count) + *exp_count = bucket_print_lines (gdbm_file->bucket) + 3; + return 0; +} + +void +print_current_bucket_handler (struct handler_param *param) +{ + print_bucket (param->fp, gdbm_file->bucket, _("Current bucket")); + fprintf (param->fp, _("\n current directory entry = %d.\n"), + gdbm_file->bucket_dir); + fprintf (param->fp, _(" current bucket address = %lu.\n"), + (unsigned long) gdbm_file->cache_entry->ca_adr); +} + +int +getnum (int *pnum, char *arg, char **endp) +{ + char *p; + unsigned long x = strtoul (arg, &p, 10); + if (*p && !isspace (*p)) + { + printf (_("not a number (stopped near %s)\n"), p); + return 1; + } + while (*p && isspace (*p)) + p++; + if (endp) + *endp = p; + else if (*p) + { + printf (_("not a number (stopped near %s)\n"), p); + return 1; + } + *pnum = x; + return 0; +} + +/* bucket NUM - print a bucket and set it as a current one. + Uses print_current_bucket_handler */ +int +print_bucket_begin (struct handler_param *param, size_t *exp_count) +{ + int temp; + + if (checkdb ()) + return 1; + + if (getnum (&temp, param->argv[0]->v.string, NULL)) + return 1; + + if (temp >= GDBM_DIR_COUNT (gdbm_file)) + { + fprintf (stderr, _("Not a bucket.\n")); + return 1; + } + _gdbm_get_bucket (gdbm_file, temp); + if (exp_count) + *exp_count = bucket_print_lines (gdbm_file->bucket) + 3; + return 0; +} + + +/* dir - print hash directory */ +int +print_dir_begin (struct handler_param *param ARG_UNUSED, size_t *exp_count) +{ + if (checkdb ()) + return 1; + if (exp_count) + *exp_count = GDBM_DIR_COUNT (gdbm_file) + 3; + return 0; +} + +void +print_dir_handler (struct handler_param *param) +{ + int i; + + fprintf (param->fp, _("Hash table directory.\n")); + fprintf (param->fp, _(" Size = %d. Bits = %d. \n\n"), + gdbm_file->header->dir_size, gdbm_file->header->dir_bits); + + for (i = 0; i < GDBM_DIR_COUNT (gdbm_file); i++) + fprintf (param->fp, " %10d: %12lu\n", + i, (unsigned long) gdbm_file->dir[i]); +} + +/* header - print file handler */ +int +print_header_begin (struct handler_param *param ARG_UNUSED, size_t *exp_count) +{ + if (checkdb ()) + return 1; + if (exp_count) + *exp_count = 14; + return 0; +} + +void +print_header_handler (struct handler_param *param) +{ + FILE *fp = param->fp; + + fprintf (fp, _("\nFile Header: \n\n")); + fprintf (fp, _(" table = %lu\n"), + (unsigned long) gdbm_file->header->dir); + fprintf (fp, _(" table size = %d\n"), gdbm_file->header->dir_size); + fprintf (fp, _(" table bits = %d\n"), gdbm_file->header->dir_bits); + fprintf (fp, _(" block size = %d\n"), gdbm_file->header->block_size); + fprintf (fp, _(" bucket elems = %d\n"), gdbm_file->header->bucket_elems); + fprintf (fp, _(" bucket size = %d\n"), gdbm_file->header->bucket_size); + fprintf (fp, _(" header magic = %x\n"), gdbm_file->header->header_magic); + fprintf (fp, _(" next block = %lu\n"), + (unsigned long) gdbm_file->header->next_block); + fprintf (fp, _(" avail size = %d\n"), gdbm_file->header->avail.size); + fprintf (fp, _(" avail count = %d\n"), gdbm_file->header->avail.count); + fprintf (fp, _(" avail nx blk = %lu\n"), + (unsigned long) gdbm_file->header->avail.next_block); +} + +/* hash KEY - hash the key */ +void +hash_handler (struct handler_param *param) +{ + fprintf (param->fp, _("hash value = %x. \n"), + _gdbm_hash (param->argv[0]->v.dat)); +} + +/* cache - print the bucket cache */ +int +print_cache_begin (struct handler_param *param ARG_UNUSED, size_t *exp_count) +{ + if (checkdb ()) + return 1; + if (exp_count) + *exp_count = gdbm_file->bucket_cache ? gdbm_file->cache_size + 1 : 1; + return 0; +} + +void +print_cache_handler (struct handler_param *param) +{ + _gdbm_print_bucket_cache (param->fp, gdbm_file); +} + +/* version - print GDBM version */ +void +print_version_handler (struct handler_param *param) +{ + fprintf (param->fp, "%s\n", gdbm_version); +} + +/* list - List all entries */ +int +list_begin (struct handler_param *param ARG_UNUSED, size_t *exp_count) +{ + if (checkdb ()) + return 1; + if (exp_count) + { + gdbm_count_t count; + + if (gdbm_count (gdbm_file, &count)) + *exp_count = 0; + else if (count > SIZE_T_MAX) + *exp_count = SIZE_T_MAX; + else + *exp_count = count; + } + + return 0; +} + +void +list_handler (struct handler_param *param) +{ + datum key; + datum data; + + key = gdbm_firstkey (gdbm_file); + while (key.dptr) + { + datum nextkey = gdbm_nextkey (gdbm_file, key); + + data = gdbm_fetch (gdbm_file, key); + if (!data.dptr) + { + terror (_("cannot fetch data; the key was:")); + datum_format (stderr, &key, dsdef[DS_KEY]); + } + else + { + datum_format (param->fp, &key, dsdef[DS_KEY]); + fputc (' ', param->fp); + datum_format (param->fp, &data, dsdef[DS_CONTENT]); + fputc ('\n', param->fp); + free (data.dptr); + } + free (key.dptr); + key = nextkey; + } +} + +/* quit - quit the program */ +void +quit_handler (struct handler_param *param ARG_UNUSED) +{ + if (gdbm_file != NULL) + gdbm_close (gdbm_file); + + exit (EXIT_OK); +} + +/* export FILE [truncate] - export to a flat file format */ +void +export_handler (struct handler_param *param) +{ + int format = GDBM_DUMP_FMT_ASCII; + int flags = GDBM_WRCREAT; + int i; + int filemode; + + for (i = 1; i < param->argc; i++) + { + if (strcmp (param->argv[i]->v.string, "truncate") == 0) + flags = GDBM_NEWDB; + else if (strcmp (param->argv[i]->v.string, "binary") == 0) + format = GDBM_DUMP_FMT_BINARY; + else if (strcmp (param->argv[i]->v.string, "ascii") == 0) + format = GDBM_DUMP_FMT_ASCII; + else + { + terror (_("unrecognized argument: %s"), + param->argv[i]->v.string); + return; + } + } + + if (variable_get ("filemode", VART_INT, (void**) &filemode)) + abort (); + if (gdbm_dump (gdbm_file, param->argv[0]->v.string, format, flags, filemode)) + { + terror (_("error dumping database: %s"), + gdbm_strerror (gdbm_errno)); + } +} + +/* import FILE [replace] [nometa] - import from a flat file */ +void +import_handler (struct handler_param *param) +{ + int flag = GDBM_INSERT; + unsigned long err_line; + int meta_mask = 0; + int i; + int rc; + + for (i = 1; i < param->argc; i++) + { + if (strcmp (param->argv[i]->v.string, "replace") == 0) + flag = GDBM_REPLACE; + else if (strcmp (param->argv[i]->v.string, "nometa") == 0) + meta_mask = GDBM_META_MASK_MODE | GDBM_META_MASK_OWNER; + else + { + terror (_("unrecognized argument: %s"), + param->argv[i]->v.string); + return; + } + } + + rc = gdbm_load (&gdbm_file, param->argv[0]->v.string, flag, + meta_mask, &err_line); + if (rc && gdbm_errno == GDBM_NO_DBNAME) + { + int t = open_mode; + + open_mode = GDBM_NEWDB; + rc = checkdb (); + open_mode = t; + + if (rc) + return; + + rc = gdbm_load (&gdbm_file, param->argv[0]->v.string, flag, + meta_mask, &err_line); + } + if (rc) + { + switch (gdbm_errno) + { + case GDBM_ERR_FILE_OWNER: + case GDBM_ERR_FILE_MODE: + terror (_("error restoring metadata: %s (%s)"), + gdbm_strerror (gdbm_errno), strerror (errno)); + break; + + default: + if (err_line) + terror ("%s:%lu: %s", param->argv[0], err_line, + gdbm_strerror (gdbm_errno)); + else + terror (_("cannot load from %s: %s"), param->argv[0], + gdbm_strerror (gdbm_errno)); + } + return; + } + + free (file_name); + if (gdbm_setopt (gdbm_file, GDBM_GETDBNAME, &file_name, sizeof (file_name))) + terror (_("gdbm_setopt failed: %s"), gdbm_strerror (gdbm_errno)); +} + +/* status - print current program status */ +void +status_handler (struct handler_param *param) +{ + if (file_name) + fprintf (param->fp, _("Database file: %s\n"), file_name); + else + fprintf (param->fp, "%s\n", _("No database name")); + if (gdbm_file) + fprintf (param->fp, "%s\n", _("Database is open")); + else + fprintf (param->fp, "%s\n", _("Database is not open")); + dsprint (param->fp, DS_KEY, dsdef[DS_KEY]); + dsprint (param->fp, DS_CONTENT, dsdef[DS_CONTENT]); +} + +void +source_handler (struct handler_param *param) +{ + char *fname = tildexpand (param->argv[0]->v.string); + if (setsource (fname, 0) == 0) + yyparse (); + free (fname); +} + + +void help_handler (struct handler_param *param); +int help_begin (struct handler_param *param, size_t *exp_count); + +struct argdef +{ + char *name; + int type; + int ds; +}; + +struct command +{ + char *name; /* Command name */ + size_t len; /* Name length */ + int tok; + int (*begin) (struct handler_param *param, size_t *); + void (*handler) (struct handler_param *param); + void (*end) (void *data); + struct argdef args[NARGS]; + char *doc; +}; + + +struct command command_tab[] = { +#define S(s) #s, sizeof (#s) - 1 + { S(count), T_CMD, + checkdb, count_handler, NULL, + { { NULL } }, N_("count (number of entries)") }, + { S(delete), T_CMD, + checkdb, delete_handler, NULL, + { { N_("KEY"), ARG_DATUM, DS_KEY }, { NULL } }, N_("delete a record") }, + { S(export), T_CMD, + checkdb, export_handler, NULL, + { { N_("FILE"), ARG_STRING }, + { "[truncate]", ARG_STRING }, + { "[binary|ascii]", ARG_STRING }, + { NULL } }, + N_("export") }, + { S(fetch), T_CMD, + checkdb, fetch_handler, NULL, + { { N_("KEY"), ARG_DATUM, DS_KEY }, { NULL } }, N_("fetch record") }, + { S(import), T_CMD, + NULL, import_handler, NULL, + { { N_("FILE"), ARG_STRING }, + { "[replace]", ARG_STRING }, + { "[nometa]" , ARG_STRING }, + { NULL } }, + N_("import") }, + { S(list), T_CMD, + list_begin, list_handler, NULL, + { { NULL } }, N_("list") }, + { S(next), T_CMD, + checkdb, nextkey_handler, NULL, + { { N_("[KEY]"), ARG_STRING }, + { NULL } }, + N_("nextkey") }, + { S(store), T_CMD, + checkdb, store_handler, NULL, + { { N_("KEY"), ARG_DATUM, DS_KEY }, + { N_("DATA"), ARG_DATUM, DS_CONTENT }, + { NULL } }, + N_("store") }, + { S(first), T_CMD, + checkdb, firstkey_handler, NULL, + { { NULL } }, N_("firstkey") }, + { S(reorganize), T_CMD, + checkdb, reorganize_handler, NULL, + { { NULL } }, N_("reorganize") }, + { S(avail), T_CMD, + avail_begin, avail_handler, NULL, + { { NULL } }, N_("print avail list") }, + { S(bucket), T_CMD, + print_bucket_begin, print_current_bucket_handler, NULL, + { { N_("NUMBER"), ARG_STRING }, + { NULL } }, N_("print a bucket") }, + { S(current), T_CMD, + print_current_bucket_begin, print_current_bucket_handler, NULL, + { { NULL } }, + N_("print current bucket") }, + { S(dir), T_CMD, + print_dir_begin, print_dir_handler, NULL, + { { NULL } }, N_("print hash directory") }, + { S(header), T_CMD, + print_header_begin , print_header_handler, NULL, + { { NULL } }, N_("print database file header") }, + { S(hash), T_CMD, + NULL, hash_handler, NULL, + { { N_("KEY"), ARG_DATUM, DS_KEY }, + { NULL } }, N_("hash value of key") }, + { S(cache), T_CMD, + print_cache_begin, print_cache_handler, NULL, + { { NULL } }, N_("print the bucket cache") }, + { S(status), T_CMD, + NULL, status_handler, NULL, + { { NULL } }, N_("print current program status") }, + { S(version), T_CMD, + NULL, print_version_handler, NULL, + { { NULL } }, N_("print version of gdbm") }, + { S(help), T_CMD, + help_begin, help_handler, NULL, + { { NULL } }, N_("print this help list") }, + { S(quit), T_CMD, + NULL, quit_handler, NULL, + { { NULL } }, N_("quit the program") }, + { S(set), T_SET, + NULL, NULL, NULL, + { { "[VAR=VALUE...]" }, { NULL } }, N_("set or list variables") }, + { S(unset), T_UNSET, + NULL, NULL, NULL, + { { "VAR..." }, { NULL } }, N_("unset variables") }, + { S(define), T_DEF, + NULL, NULL, NULL, + { { "key|content", ARG_STRING }, + { "{ FIELD-LIST }", ARG_STRING }, + { NULL } }, N_("define datum structure") }, + { S(source), T_CMD, + NULL, source_handler, NULL, + { { "FILE", ARG_STRING }, + { NULL } }, N_("source command script") }, + { S(close), T_CMD, + NULL, close_handler, NULL, + { { NULL } }, N_("close the database") }, + { S(open), T_CMD, + NULL, open_handler, NULL, + { { "FILE", ARG_STRING }, { NULL } }, + N_("open new database") }, +#undef S + { 0 } +}; + +static int +cmdcmp (const void *a, const void *b) +{ + struct command const *ac = a; + struct command const *bc = b; + return strcmp (ac->name, bc->name); +} + +void +sort_commands () +{ + qsort (command_tab, sizeof (command_tab) / sizeof (command_tab[0]) - 1, + sizeof (command_tab[0]), cmdcmp); +} + + +/* ? - help handler */ +#define CMDCOLS 30 + +int +help_begin (struct handler_param *param ARG_UNUSED, size_t *exp_count) +{ + if (exp_count) + *exp_count = sizeof (command_tab) / sizeof (command_tab[0]) + 1; + return 0; +} + +void +help_handler (struct handler_param *param) +{ + struct command *cmd; + FILE *fp = param->fp; + + for (cmd = command_tab; cmd->name; cmd++) + { + int i; + int n; + + n = fprintf (fp, " %s", cmd->name); + + for (i = 0; i < NARGS && cmd->args[i].name; i++) + n += fprintf (fp, " %s", gettext (cmd->args[i].name)); + + if (n < CMDCOLS) + fprintf (fp, "%*.s", CMDCOLS-n, ""); + fprintf (fp, " %s", gettext (cmd->doc)); + fputc ('\n', fp); + } +} + +int +command_lookup (const char *str, struct locus *loc, struct command **pcmd) +{ + enum { fcom_init, fcom_found, fcom_ambig, fcom_abort } state = fcom_init; + struct command *cmd, *found = NULL; + size_t len = strlen (str); + + for (cmd = command_tab; state != fcom_abort && cmd->name; cmd++) + { + if (memcmp (cmd->name, str, len < cmd->len ? len : cmd->len) == 0) + { + switch (state) + { + case fcom_init: + found = cmd; + state = fcom_found; + break; + + case fcom_found: + if (!interactive) + { + state = fcom_abort; + found = NULL; + continue; + } + fprintf (stderr, "ambiguous command: %s\n", str); + fprintf (stderr, " %s\n", found->name); + found = NULL; + state = fcom_ambig; + /* fall through */ + case fcom_ambig: + fprintf (stderr, " %s\n", cmd->name); + break; + + case fcom_abort: + /* should not happen */ + abort (); + } + } + } + + if (state == fcom_init) + lerror (loc, + interactive ? _("Invalid command. Try ? for help.") : + _("Unknown command")); + if (!found) + return T_BOGUS; + + *pcmd = found; + return found->tok; +} + +char *parseopt_program_doc = N_("examine and/or modify a GDBM database"); +char *parseopt_program_args = N_("DBFILE"); + +struct gdbm_option optab[] = { + { 'b', "block-size", N_("SIZE"), N_("set block size") }, + { 'c', "cache-size", N_("SIZE"), N_("set cache size") }, + { 'f', "file", N_("FILE"), N_("read commands from FILE") }, + { 'g', NULL, "FILE", NULL, PARSEOPT_HIDDEN }, + { 'l', "no-lock", NULL, N_("disable file locking") }, + { 'm', "no-mmap", NULL, N_("do not use mmap") }, + { 'n', "newdb", NULL, N_("create database") }, + { 'N', "norc", NULL, N_("do not read .gdbmtoolrc file") }, + { 'r', "read-only", NULL, N_("open database in read-only mode") }, + { 's', "synchronize", NULL, N_("synchronize to disk after each write") }, + { 'q', "quiet", NULL, N_("don't print initial banner") }, + { 0 } +}; + +#define ARGINC 16 + + +struct gdbmarg * +gdbmarg_string (char *string, struct locus *loc) +{ + struct gdbmarg *arg = ecalloc (1, sizeof (*arg)); + arg->next = NULL; + arg->type = ARG_STRING; + arg->ref = 1; + if (loc) + arg->loc = *loc; + arg->v.string = string; + return arg; +} + +struct gdbmarg * +gdbmarg_datum (datum *dat, struct locus *loc) +{ + struct gdbmarg *arg = ecalloc (1, sizeof (*arg)); + arg->next = NULL; + arg->type = ARG_DATUM; + arg->ref = 1; + if (loc) + arg->loc = *loc; + arg->v.dat = *dat; + return arg; +} + +struct gdbmarg * +gdbmarg_kvpair (struct kvpair *kvp, struct locus *loc) +{ + struct gdbmarg *arg = ecalloc (1, sizeof (*arg)); + arg->next = NULL; + arg->type = ARG_KVPAIR; + arg->ref = 1; + if (loc) + arg->loc = *loc; + arg->v.kvpair = kvp; + return arg; +} + +struct slist * +slist_new (char *s) +{ + struct slist *lp = emalloc (sizeof (*lp)); + lp->next = NULL; + lp->str = s; + return lp; +} + +void +slist_free (struct slist *lp) +{ + while (lp) + { + struct slist *next = lp->next; + free (lp->str); + free (lp); + lp = next; + } +} + +struct kvpair * +kvpair_string (struct locus *loc, char *val) +{ + struct kvpair *p = ecalloc (1, sizeof (*p)); + p->type = KV_STRING; + if (loc) + p->loc = *loc; + p->val.s = val; + return p; +} + +struct kvpair * +kvpair_list (struct locus *loc, struct slist *s) +{ + struct kvpair *p = ecalloc (1, sizeof (*p)); + p->type = KV_LIST; + if (loc) + p->loc = *loc; + p->val.l = s; + return p; +} + + +static void +kvlist_free (struct kvpair *kvp) +{ + while (kvp) + { + struct kvpair *next = kvp->next; + free (kvp->key); + switch (kvp->type) + { + case KV_STRING: + free (kvp->val.s); + break; + + case KV_LIST: + slist_free (kvp->val.l); + break; + } + free (kvp); + kvp = next; + } +} + +int +gdbmarg_free (struct gdbmarg *arg) +{ + if (arg && --arg->ref == 0) + { + switch (arg->type) + { + case ARG_STRING: + free (arg->v.string); + break; + + case ARG_KVPAIR: + kvlist_free (arg->v.kvpair); + break; + + case ARG_DATUM: + free (arg->v.dat.dptr); + break; + } + free (arg); + return 0; + } + return 1; +} + +void +gdbmarg_destroy (struct gdbmarg **parg) +{ + if (parg && gdbmarg_free (*parg)) + *parg = NULL; +} + +void +gdbmarglist_init (struct gdbmarglist *lst, struct gdbmarg *arg) +{ + if (arg) + arg->next = NULL; + lst->head = lst->tail = arg; +} + +void +gdbmarglist_add (struct gdbmarglist *lst, struct gdbmarg *arg) +{ + arg->next = NULL; + if (lst->tail) + lst->tail->next = arg; + else + lst->head = arg; + lst->tail = arg; +} + +void +gdbmarglist_free (struct gdbmarglist *lst) +{ + struct gdbmarg *arg; + + for (arg = lst->head; arg; ) + { + struct gdbmarg *next = arg->next; + gdbmarg_free (arg); + arg = next; + } +} + +struct handler_param param; +size_t argmax; + +void +param_free_argv (struct handler_param *param, int n) +{ + int i; + + for (i = 0; i < n; i++) + gdbmarg_destroy (¶m->argv[i]); + param->argc = 0; +} + +typedef struct gdbmarg *(*coerce_type_t) (struct gdbmarg *arg, + struct argdef *def); + +struct gdbmarg * +coerce_ref (struct gdbmarg *arg, struct argdef *def) +{ + ++arg->ref; + return arg; +} + +struct gdbmarg * +coerce_k2d (struct gdbmarg *arg, struct argdef *def) +{ + datum d; + + if (datum_scan (&d, dsdef[def->ds], arg->v.kvpair)) + return NULL; + return gdbmarg_datum (&d, &arg->loc); +} + +struct gdbmarg * +coerce_s2d (struct gdbmarg *arg, struct argdef *def) +{ + datum d; + struct kvpair kvp; + + memset (&kvp, 0, sizeof (kvp)); + kvp.type = KV_STRING; + kvp.val.s = arg->v.string; + + if (datum_scan (&d, dsdef[def->ds], &kvp)) + return NULL; + return gdbmarg_datum (&d, &arg->loc); +} + +#define coerce_fail NULL + +coerce_type_t coerce_tab[ARG_MAX][ARG_MAX] = { + /* s d k */ + /* s */ { coerce_ref, coerce_fail, coerce_fail }, + /* d */ { coerce_s2d, coerce_ref, coerce_k2d }, + /* k */ { coerce_fail, coerce_fail, coerce_ref } +}; + +char *argtypestr[] = { "string", "datum", "k/v pair" }; + +struct gdbmarg * +coerce (struct gdbmarg *arg, struct argdef *def) +{ + if (!coerce_tab[def->type][arg->type]) + { + lerror (&arg->loc, _("cannot coerce %s to %s"), + argtypestr[arg->type], argtypestr[def->type]); + return NULL; + } + return coerce_tab[def->type][arg->type] (arg, def); +} + +int +run_command (struct command *cmd, struct gdbmarglist *arglist) +{ + int i; + struct gdbmarg *arg; + char *pager = NULL; + char argbuf[128]; + size_t expected_lines, *expected_lines_ptr; + FILE *pagfp = NULL; + + variable_get ("pager", VART_STRING, (void**) &pager); + + arg = arglist ? arglist->head : NULL; + + for (i = 0; cmd->args[i].name && arg; i++, arg = arg->next) + { + if (i >= argmax) + { + argmax += ARGINC; + param.argv = erealloc (param.argv, + sizeof (param.argv[0]) * argmax); + } + if ((param.argv[i] = coerce (arg, &cmd->args[i])) == NULL) + { + param_free_argv (¶m, i); + return 1; + } + } + + for (; cmd->args[i].name; i++) + { + char *argname = cmd->args[i].name; + struct gdbmarg *t; + + if (*argname == '[') + /* Optional argument */ + break; + + if (!interactive) + { + terror (_("%s: not enough arguments"), cmd->name); + return 1; + } + printf ("%s? ", argname); + fflush (stdout); + if (fgets (argbuf, sizeof argbuf, stdin) == NULL) + { + terror (_("unexpected eof")); + exit (EXIT_USAGE); + } + + trimnl (argbuf); + if (i >= argmax) + { + argmax += ARGINC; + param.argv = erealloc (param.argv, + sizeof (param.argv[0]) * argmax); + } + + t = gdbmarg_string (estrdup (argbuf), &yylloc); + if ((param.argv[i] = coerce (t, &cmd->args[i])) == NULL) + { + gdbmarg_free (t); + param_free_argv (¶m, i); + return 1; + } + } + + if (arg) + { + terror (_("%s: too many arguments"), cmd->name); + return 1; + } + + /* Prepare for calling the handler */ + param.argc = i; + if (!param.argv) + { + argmax = ARGINC; + param.argv = ecalloc (argmax, sizeof (param.argv[0])); + } + param.argv[i] = NULL; + param.fp = NULL; + param.data = NULL; + pagfp = NULL; + + expected_lines = 0; + expected_lines_ptr = (interactive && pager) ? &expected_lines : NULL; + if (!(cmd->begin && cmd->begin (¶m, expected_lines_ptr))) + { + if (pager && expected_lines > get_screen_lines ()) + { + pagfp = popen (pager, "w"); + if (pagfp) + param.fp = pagfp; + else + { + terror (_("cannot run pager `%s': %s"), pager, + strerror (errno)); + pager = NULL; + param.fp = stdout; + } + } + else + param.fp = stdout; + + cmd->handler (¶m); + if (cmd->end) + cmd->end (param.data); + else if (param.data) + free (param.data); + + if (pagfp) + pclose (pagfp); + } + + param_free_argv (¶m, param.argc); + + return 0; +} + +static void +source_rcfile () +{ + if (access (GDBMTOOLRC, R_OK) == 0) + { + if (setsource (GDBMTOOLRC, 0) == 0) + yyparse (); + } + else + { + char *fname; + char *p = getenv ("HOME"); + if (!p) + { + struct passwd *pw = getpwuid (getuid ()); + if (!pw) + { + terror (_("cannot find home directory")); + return; + } + p = pw->pw_dir; + } + fname = mkfilename (p, GDBMTOOLRC, NULL); + if (access (fname, R_OK) == 0) + { + if (setsource (fname, 0) == 0) + yyparse (); + } + free (fname); + } +} + +int +main (int argc, char *argv[]) +{ + int intr; + int opt; + int bv; + int norc = 0; + char *source = "-"; + + set_progname (argv[0]); + +#ifdef HAVE_SETLOCALE + setlocale (LC_ALL, ""); +#endif + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + sort_commands (); + + /* Initialize variables. */ + intr = isatty (0); + dsdef[DS_KEY] = dsegm_new_field (datadef_lookup ("string"), NULL, 1); + dsdef[DS_CONTENT] = dsegm_new_field (datadef_lookup ("string"), NULL, 1); + + variable_set ("open", VART_STRING, "wrcreat"); + variable_set ("pager", VART_STRING, getenv ("PAGER")); + + for (opt = parseopt_first (argc, argv, optab); + opt != EOF; + opt = parseopt_next ()) + switch (opt) + { + case 'f': + source = optarg; + intr = 0; + break; + + case 'l': + bv = 0; + variable_set ("lock", VART_BOOL, &bv); + break; + + case 'm': + bv = 0; + variable_set ("mmap", VART_BOOL, &bv); + break; + + case 's': + bv = 1; + variable_set ("sync", VART_BOOL, &bv); + break; + + case 'r': + variable_set ("open", VART_STRING, "readonly"); + break; + + case 'n': + variable_set ("open", VART_STRING, "newdb"); + break; + + case 'N': + norc = 1; + break; + + case 'c': + variable_set ("cachesize", VART_STRING, optarg); + break; + + case 'b': + variable_set ("blocksize", VART_STRING, optarg); + break; + + case 'g': + file_name = optarg; + break; + + case 'q': + bv = 1; + variable_set ("quiet", VART_BOOL, &bv); + break; + + default: + terror (_("unknown option; try `%s -h' for more info"), + progname); + exit (EXIT_USAGE); + } + + argc -= optind; + argv += optind; + + if (argc > 1) + { + terror (_("too many arguments")); + exit (EXIT_USAGE); + } + + if (argc == 1) + file_name = argv[0]; + + signal (SIGPIPE, SIG_IGN); + + memset (¶m, 0, sizeof (param)); + argmax = 0; + + if (!norc) + source_rcfile (); + + /* Welcome message. */ + if (intr && !variable_is_true ("quiet")) + printf (_("\nWelcome to the gdbm tool. Type ? for help.\n\n")); + + if (setsource (source, intr)) + exit (EXIT_FATAL); + return yyparse (); +} diff --git a/src/gdbmtool.h b/src/gdbmtool.h new file mode 100644 index 0000000..c850a9a --- /dev/null +++ b/src/gdbmtool.h @@ -0,0 +1,263 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +#include "autoconf.h" +#include "gdbmdefs.h" +#include "gdbm.h" +#include "gdbmapp.h" +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <ctype.h> + +/* Position in input file */ +struct point +{ + char *file; /* file name */ + unsigned line; /* line number */ + unsigned col; /* column number */ +}; + +/* Location in input file */ +struct locus +{ + struct point beg, end; +}; + +typedef struct locus gdbm_yyltype_t; + +#define YYLTYPE gdbm_yyltype_t + +#define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + { \ + if (N) \ + { \ + (Current).beg = YYRHSLOC(Rhs, 1).beg; \ + (Current).end = YYRHSLOC(Rhs, N).end; \ + } \ + else \ + { \ + (Current).beg = YYRHSLOC(Rhs, 0).end; \ + (Current).end = (Current).beg; \ + } \ + } \ + while (0) + +#define YY_LOCATION_PRINT(File, Loc) \ + do \ + { \ + if ((Loc).beg.col == 0) \ + fprintf (File, "%s:%u", \ + (Loc).beg.file, \ + (Loc).beg.line); \ + else if (strcmp ((Loc).beg.file, (Loc).end.file)) \ + fprintf (File, "%s:%u.%u-%s:%u.%u", \ + (Loc).beg.file, \ + (Loc).beg.line, (Loc).beg.col, \ + (Loc).end.file, \ + (Loc).end.line, (Loc).end.col); \ + else if ((Loc).beg.line != (Loc).end.line) \ + fprintf (File, "%s:%u.%u-%u.%u", \ + (Loc).beg.file, \ + (Loc).beg.line, (Loc).beg.col, \ + (Loc).end.line, (Loc).end.col); \ + else if ((Loc).beg.col != (Loc).end.col) \ + fprintf (File, "%s:%u.%u-%u", \ + (Loc).beg.file, \ + (Loc).beg.line, (Loc).beg.col, \ + (Loc).end.col); \ + else \ + fprintf (File, "%s:%u.%u", \ + (Loc).beg.file, \ + (Loc).beg.line, \ + (Loc).beg.col); \ + } \ + while (0) + +void vlerror (struct locus *loc, const char *fmt, va_list ap); +void lerror (struct locus *loc, const char *fmt, ...); + +void terror (const char *fmt, ...); + +void print_prompt (void); + +int setsource (const char *filename, int intr); + +extern char *file_name; +extern int interactive; +extern int open_mode; + +#define GDBMTOOLRC ".gdbmtoolrc" +#define GDBMTOOL_DEFFILE "junk.gdbm" + + +struct slist +{ + struct slist *next; + char *str; +}; + +struct slist *slist_new (char *s); +void slist_free (struct slist *); + +#define KV_STRING 0 +#define KV_LIST 1 + +struct kvpair +{ + struct kvpair *next; + int type; + struct locus loc; + char *key; + union + { + char *s; + struct slist *l; + } val; +}; + +struct kvpair *kvpair_string (struct locus *loc, char *val); +struct kvpair *kvpair_list (struct locus *loc, struct slist *s); + + +#define ARG_STRING 0 +#define ARG_DATUM 1 +#define ARG_KVPAIR 2 +#define ARG_MAX 3 + +/* Argument to a command handler */ +struct gdbmarg +{ + struct gdbmarg *next; + int type; + int ref; + struct locus loc; + union + { + char *string; + datum dat; + struct kvpair *kvpair; + } v; +}; + +/* List of arguments */ +struct gdbmarglist +{ + struct gdbmarg *head, *tail; +}; + +void gdbmarglist_init (struct gdbmarglist *, struct gdbmarg *); +void gdbmarglist_add (struct gdbmarglist *, struct gdbmarg *); +void gdbmarglist_free (struct gdbmarglist *lst); + +struct gdbmarg *gdbmarg_string (char *, struct locus *); +struct gdbmarg *gdbmarg_datum (datum *, struct locus *); +struct gdbmarg *gdbmarg_kvpair (struct kvpair *kvl, struct locus *); + +int gdbmarg_free (struct gdbmarg *arg); +void gdbmarg_destroy (struct gdbmarg **parg); + +struct command; +int command_lookup (const char *str, struct locus *loc, struct command **pcmd); + +int run_command (struct command *cmd, struct gdbmarglist *arglist); + +struct xdatum; +void xd_expand (struct xdatum *xd, size_t size); +void xd_store (struct xdatum *xd, void *val, size_t size); + + +struct datadef +{ + char *name; + int size; + int (*format) (FILE *, void *ptr, int size); + int (*scan) (struct xdatum *xd, char *str); +}; + +struct datadef *datadef_lookup (const char *name); + +struct field +{ + struct datadef *type; + int dim; + char *name; +}; + +#define FDEF_FLD 0 +#define FDEF_OFF 1 +#define FDEF_PAD 2 + +struct dsegm +{ + struct dsegm *next; + int type; + union + { + int n; + struct field field; + } v; +}; + +struct dsegm *dsegm_new (int type); +struct dsegm *dsegm_new_field (struct datadef *type, char *id, int dim); +void dsegm_free_list (struct dsegm *dp); + +#define DS_KEY 0 +#define DS_CONTENT 1 +#define DS_MAX 2 + +extern struct dsegm *dsdef[]; + +#define VART_STRING 0 +#define VART_BOOL 1 +#define VART_INT 2 + +#define VAR_OK 0 /* operation succeeded */ +#define VAR_ERR_NOTSET 1 /* Only for variable_get: + variable is not set */ +#define VAR_ERR_NOTDEF 2 /* no such variable */ +#define VAR_ERR_BADTYPE 3 /* variable cannot be coerced to the + requested type (software error) */ +#define VAR_ERR_BADVALUE 4 /* Only for variable_set: the value is + not valid for this variable. */ + +int variable_set (const char *name, int type, void *val); +int variable_get (const char *name, int type, void **val); +int variable_is_set (const char *name); +int variable_is_true (const char *name); +void variable_print_all (FILE *fp); + + +int unescape (int c); +int escape (int c); +void begin_def (void); +void end_def (void); + +int yylex (void); +int yyerror (char *s); +int yyparse (void); + +void datum_format (FILE *fp, datum const *dat, struct dsegm *ds); +int datum_scan (datum *dat, struct dsegm *ds, struct kvpair *kv); +void dsprint (FILE *fp, int what, struct dsegm *ds); + +char *mkfilename (const char *dir, const char *file, const char *suf); +char *tildexpand (char *s); +int vgetyn (const char *prompt, va_list ap); +int getyn (const char *prompt, ...); diff --git a/src/gettext.h b/src/gettext.h new file mode 100644 index 0000000..35a269d --- /dev/null +++ b/src/gettext.h @@ -0,0 +1,281 @@ +/* Convenience header for conditional use of GNU <libintl.h>. + Copyright (C) 1995-1998, 2000-2002, 2004-2006, 2009, + 2013 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, 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 + Library General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. */ + +#ifndef _LIBGETTEXT_H +#define _LIBGETTEXT_H 1 + +/* NLS can be disabled through the configure --disable-nls option. */ +#if ENABLE_NLS + +/* Get declarations of GNU message catalog functions. */ +# include <libintl.h> + +/* You can set the DEFAULT_TEXT_DOMAIN macro to specify the domain used by + the gettext() and ngettext() macros. This is an alternative to calling + textdomain(), and is useful for libraries. */ +# ifdef DEFAULT_TEXT_DOMAIN +# undef gettext +# define gettext(Msgid) \ + dgettext (DEFAULT_TEXT_DOMAIN, Msgid) +# undef ngettext +# define ngettext(Msgid1, Msgid2, N) \ + dngettext (DEFAULT_TEXT_DOMAIN, Msgid1, Msgid2, N) +# endif + +#else + +/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which + chokes if dcgettext is defined as a macro. So include it now, to make + later inclusions of <locale.h> a NOP. We don't include <libintl.h> + as well because people using "gettext.h" will not include <libintl.h>, + and also including <libintl.h> would fail on SunOS 4, whereas <locale.h> + is OK. */ +#if defined(__sun) +# include <locale.h> +#endif + +/* Many header files from the libstdc++ coming with g++ 3.3 or newer include + <libintl.h>, which chokes if dcgettext is defined as a macro. So include + it now, to make later inclusions of <libintl.h> a NOP. */ +#if defined(__cplusplus) && defined(__GNUG__) && (__GNUC__ >= 3) +# include <cstdlib> +# if (__GLIBC__ >= 2) || _GLIBCXX_HAVE_LIBINTL_H +# include <libintl.h> +# endif +#endif + +/* Disabled NLS. + The casts to 'const char *' serve the purpose of producing warnings + for invalid uses of the value returned from these functions. + On pre-ANSI systems without 'const', the config.h file is supposed to + contain "#define const". */ +# undef gettext +# define gettext(Msgid) ((const char *) (Msgid)) +# undef dgettext +# define dgettext(Domainname, Msgid) ((void) (Domainname), gettext (Msgid)) +# undef dcgettext +# define dcgettext(Domainname, Msgid, Category) \ + ((void) (Category), dgettext (Domainname, Msgid)) +# undef ngettext +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 \ + ? ((void) (Msgid2), (const char *) (Msgid1)) \ + : ((void) (Msgid1), (const char *) (Msgid2))) +# undef dngettext +# define dngettext(Domainname, Msgid1, Msgid2, N) \ + ((void) (Domainname), ngettext (Msgid1, Msgid2, N)) +# undef dcngettext +# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ + ((void) (Category), dngettext(Domainname, Msgid1, Msgid2, N)) +# undef textdomain +# define textdomain(Domainname) ((const char *) (Domainname)) +# undef bindtextdomain +# define bindtextdomain(Domainname, Dirname) \ + ((void) (Domainname), (const char *) (Dirname)) +# undef bind_textdomain_codeset +# define bind_textdomain_codeset(Domainname, Codeset) \ + ((void) (Domainname), (const char *) (Codeset)) + +#endif + +/* A pseudo function call that serves as a marker for the automated + extraction of messages, but does not call gettext(). The run-time + translation is done at a different place in the code. + The argument, String, should be a literal string. Concatenated strings + and other string expressions won't work. + The macro's expansion is not parenthesized, so that it is suitable as + initializer for static 'char[]' or 'const char[]' variables. */ +#define gettext_noop(String) String + +/* The separator between msgctxt and msgid in a .mo file. */ +#define GETTEXT_CONTEXT_GLUE "\004" + +/* Pseudo function calls, taking a MSGCTXT and a MSGID instead of just a + MSGID. MSGCTXT and MSGID must be string literals. MSGCTXT should be + short and rarely need to change. + The letter 'p' stands for 'particular' or 'special'. */ +#ifdef DEFAULT_TEXT_DOMAIN +# define pgettext(Msgctxt, Msgid) \ + pgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#else +# define pgettext(Msgctxt, Msgid) \ + pgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#endif +#define dpgettext(Domainname, Msgctxt, Msgid) \ + pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#define dcpgettext(Domainname, Msgctxt, Msgid, Category) \ + pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, Category) +#ifdef DEFAULT_TEXT_DOMAIN +# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#else +# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#endif +#define dnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#define dcnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N, Category) \ + npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, Category) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +pgettext_aux (const char *domain, + const char *msg_ctxt_id, const char *msgid, + int category) +{ + const char *translation = dcgettext (domain, msg_ctxt_id, category); + if (translation == msg_ctxt_id) + return msgid; + else + return translation; +} + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +npgettext_aux (const char *domain, + const char *msg_ctxt_id, const char *msgid, + const char *msgid_plural, unsigned long int n, + int category) +{ + const char *translation = + dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); + if (translation == msg_ctxt_id || translation == msgid_plural) + return (n == 1 ? msgid : msgid_plural); + else + return translation; +} + +/* The same thing extended for non-constant arguments. Here MSGCTXT and MSGID + can be arbitrary expressions. But for string literals these macros are + less efficient than those above. */ + +#include <string.h> + +#define _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS \ + (((__GNUC__ >= 3 || __GNUG__ >= 2) && !__STRICT_ANSI__) \ + /* || __STDC_VERSION__ >= 199901L */ ) + +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS +#include <stdlib.h> +#endif + +#define pgettext_expr(Msgctxt, Msgid) \ + dcpgettext_expr (NULL, Msgctxt, Msgid, LC_MESSAGES) +#define dpgettext_expr(Domainname, Msgctxt, Msgid) \ + dcpgettext_expr (Domainname, Msgctxt, Msgid, LC_MESSAGES) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +dcpgettext_expr (const char *domain, + const char *msgctxt, const char *msgid, + int category) +{ + size_t msgctxt_len = strlen (msgctxt) + 1; + size_t msgid_len = strlen (msgid) + 1; + const char *translation; +#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + char msg_ctxt_id[msgctxt_len + msgid_len]; +#else + char buf[1024]; + char *msg_ctxt_id = + (msgctxt_len + msgid_len <= sizeof (buf) + ? buf + : (char *) malloc (msgctxt_len + msgid_len)); + if (msg_ctxt_id != NULL) +#endif + { + memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); + msg_ctxt_id[msgctxt_len - 1] = '\004'; + memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); + translation = dcgettext (domain, msg_ctxt_id, category); +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + if (msg_ctxt_id != buf) + free (msg_ctxt_id); +#endif + if (translation != msg_ctxt_id) + return translation; + } + return msgid; +} + +#define npgettext_expr(Msgctxt, Msgid, MsgidPlural, N) \ + dcnpgettext_expr (NULL, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) +#define dnpgettext_expr(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + dcnpgettext_expr (Domainname, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +dcnpgettext_expr (const char *domain, + const char *msgctxt, const char *msgid, + const char *msgid_plural, unsigned long int n, + int category) +{ + size_t msgctxt_len = strlen (msgctxt) + 1; + size_t msgid_len = strlen (msgid) + 1; + const char *translation; +#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + char msg_ctxt_id[msgctxt_len + msgid_len]; +#else + char buf[1024]; + char *msg_ctxt_id = + (msgctxt_len + msgid_len <= sizeof (buf) + ? buf + : (char *) malloc (msgctxt_len + msgid_len)); + if (msg_ctxt_id != NULL) +#endif + { + memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); + msg_ctxt_id[msgctxt_len - 1] = '\004'; + memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); + translation = dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + if (msg_ctxt_id != buf) + free (msg_ctxt_id); +#endif + if (!(translation == msg_ctxt_id || translation == msgid_plural)) + return translation; + } + return (n == 1 ? msgid : msgid_plural); +} + +#endif /* _LIBGETTEXT_H */ diff --git a/src/gram.c b/src/gram.c new file mode 100644 index 0000000..f4f462c --- /dev/null +++ b/src/gram.c @@ -0,0 +1,2025 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006, + 2013 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 1 + + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + T_TYPE = 258, + T_OFF = 259, + T_PAD = 260, + T_DEF = 261, + T_SET = 262, + T_UNSET = 263, + T_BOGUS = 264, + T_CMD = 265, + T_NUM = 266, + T_IDENT = 267, + T_WORD = 268 + }; +#endif +/* Tokens. */ +#define T_TYPE 258 +#define T_OFF 259 +#define T_PAD 260 +#define T_DEF 261 +#define T_SET 262 +#define T_UNSET 263 +#define T_BOGUS 264 +#define T_CMD 265 +#define T_NUM 266 +#define T_IDENT 267 +#define T_WORD 268 + + + + +/* Copy the first part of user declarations. */ +#line 1 "gram.y" + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +#include <autoconf.h> +#include "gdbmtool.h" + +struct dsegm *dsdef[DS_MAX]; + + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 1 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 50 "gram.y" +{ + char *string; + struct kvpair *kvpair; + struct { struct kvpair *head, *tail; } kvlist; + struct { struct slist *head, *tail; } slist; + struct gdbmarg *arg; + struct gdbmarglist arglist; + int num; + struct datadef *type; + struct dsegm *dsegm; + struct { struct dsegm *head, *tail; } dsegmlist; + struct command *cmd; +} +/* Line 187 of yacc.c. */ +#line 161 "gram.c" + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + +/* Copy the second part of user declarations. */ + + +/* Line 216 of yacc.c. */ +#line 186 "gram.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if YYENABLE_NLS +# if ENABLE_NLS +# include <libintl.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include <alloca.h> /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include <malloc.h> /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss; + YYSTYPE yyvs; + YYLTYPE yyls; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 31 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 68 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 21 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 26 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 52 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 77 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 268 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 14, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 17, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 18, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 19, 2, 20, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 15, 2, 16, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 4, 6, 8, 11, 13, 17, 20, + 23, 26, 27, 31, 32, 34, 36, 39, 41, 43, + 47, 49, 53, 55, 59, 61, 65, 67, 71, 73, + 75, 76, 81, 86, 88, 89, 91, 93, 95, 99, + 102, 108, 111, 114, 116, 119, 122, 124, 127, 129, + 133, 135, 138 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 22, 0, -1, -1, 23, -1, 24, -1, 23, 24, + -1, 14, -1, 10, 26, 14, -1, 42, 14, -1, + 35, 14, -1, 9, 14, -1, -1, 1, 25, 14, + -1, -1, 27, -1, 28, -1, 27, 28, -1, 34, + -1, 29, -1, 15, 30, 16, -1, 31, -1, 30, + 17, 31, -1, 32, -1, 12, 18, 32, -1, 34, + -1, 15, 33, 16, -1, 34, -1, 33, 17, 34, + -1, 12, -1, 13, -1, -1, 6, 39, 36, 37, + -1, 15, 40, 38, 16, -1, 3, -1, -1, 17, + -1, 12, -1, 41, -1, 40, 17, 41, -1, 3, + 12, -1, 3, 12, 19, 11, 20, -1, 4, 11, + -1, 5, 11, -1, 7, -1, 7, 43, -1, 8, + 45, -1, 44, -1, 43, 44, -1, 12, -1, 12, + 18, 34, -1, 46, -1, 45, 46, -1, 12, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 66, 66, 67, 70, 71, 74, 75, 81, 82, + 83, 93, 93, 106, 109, 112, 116, 123, 127, 133, + 139, 143, 151, 152, 159, 163, 169, 173, 182, 183, + 186, 186, 194, 198, 204, 205, 208, 223, 227, 235, + 239, 243, 248, 255, 259, 260, 263, 264, 267, 299, + 327, 328, 331 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "T_TYPE", "\"off\"", "\"pad\"", + "\"define\"", "\"set\"", "\"unset\"", "T_BOGUS", "\"command verb\"", + "\"number\"", "\"identifier\"", "\"word\"", "'\\n'", "'{'", "'}'", "','", + "'='", "'['", "']'", "$accept", "input", "stmtlist", "stmt", "@1", + "arglist", "arg1list", "arg", "compound", "kvlist", "kvpair", "value", + "slist", "string", "defn", "@2", "defbody", "optcomma", "defid", + "deflist", "def", "set", "asgnlist", "asgn", "varlist", "var", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 10, 123, 125, 44, 61, 91, + 93 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 21, 22, 22, 23, 23, 24, 24, 24, 24, + 24, 25, 24, 26, 26, 27, 27, 28, 28, 29, + 30, 30, 31, 31, 32, 32, 33, 33, 34, 34, + 36, 35, 37, 37, 38, 38, 39, 40, 40, 41, + 41, 41, 41, 42, 42, 42, 43, 43, 44, 44, + 45, 45, 46 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 0, 1, 1, 2, 1, 3, 2, 2, + 2, 0, 3, 0, 1, 1, 2, 1, 1, 3, + 1, 3, 1, 3, 1, 3, 1, 3, 1, 1, + 0, 4, 4, 1, 0, 1, 1, 1, 3, 2, + 5, 2, 2, 1, 2, 2, 1, 2, 1, 3, + 1, 2, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 11, 0, 43, 0, 0, 13, 6, 0, 0, + 4, 0, 0, 0, 36, 30, 48, 44, 46, 52, + 45, 50, 10, 28, 29, 0, 0, 14, 15, 18, + 17, 1, 5, 9, 8, 12, 0, 0, 47, 51, + 28, 0, 0, 20, 22, 24, 7, 16, 33, 0, + 31, 49, 0, 0, 26, 19, 0, 0, 0, 0, + 34, 37, 23, 25, 0, 21, 39, 41, 42, 35, + 0, 27, 0, 38, 32, 0, 40 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 8, 9, 10, 13, 26, 27, 28, 29, 42, + 43, 44, 53, 45, 11, 36, 50, 70, 15, 60, + 61, 12, 17, 18, 20, 21 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -11 +static const yytype_int8 yypact[] = +{ + 1, -11, 2, 17, 20, 31, 21, -11, 39, 16, + -11, 32, 33, 34, -11, -11, 35, 17, -11, -11, + 20, -11, -11, -11, -11, 25, 36, 21, -11, -11, + -11, -11, -11, -11, -11, -11, 3, 7, -11, -11, + 37, 7, -4, -11, -11, -11, -11, -11, -11, 0, + -11, -11, 29, 11, -11, -11, 25, 40, 38, 43, + 26, -11, -11, -11, 7, -11, 41, -11, -11, 0, + 45, -11, 46, -11, -11, 42, -11 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -11, -11, -11, 47, -11, -11, -11, 24, -11, -11, + 8, 13, -11, -6, -11, -11, -11, -11, -11, -11, + -10, -11, -11, 49, -11, 48 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -4 +static const yytype_int8 yytable[] = +{ + 30, -2, 1, 57, 58, 59, 48, 2, 3, 4, + 5, 6, 55, 56, 14, 7, -3, 1, 49, 23, + 24, 30, 2, 3, 4, 5, 6, 63, 64, 16, + 7, 51, 19, 23, 24, 54, 25, 40, 24, 31, + 41, 23, 24, 69, 41, 22, 33, 34, 35, 67, + 46, 47, 66, 37, 68, 52, 32, 75, 71, 73, + 72, 74, 76, 0, 65, 62, 38, 0, 39 +}; + +static const yytype_int8 yycheck[] = +{ + 6, 0, 1, 3, 4, 5, 3, 6, 7, 8, + 9, 10, 16, 17, 12, 14, 0, 1, 15, 12, + 13, 27, 6, 7, 8, 9, 10, 16, 17, 12, + 14, 37, 12, 12, 13, 41, 15, 12, 13, 0, + 15, 12, 13, 17, 15, 14, 14, 14, 14, 11, + 14, 27, 12, 18, 11, 18, 9, 11, 64, 69, + 19, 16, 20, -1, 56, 52, 17, -1, 20 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 1, 6, 7, 8, 9, 10, 14, 22, 23, + 24, 35, 42, 25, 12, 39, 12, 43, 44, 12, + 45, 46, 14, 12, 13, 15, 26, 27, 28, 29, + 34, 0, 24, 14, 14, 14, 36, 18, 44, 46, + 12, 15, 30, 31, 32, 34, 14, 28, 3, 15, + 37, 34, 18, 33, 34, 16, 17, 3, 4, 5, + 40, 41, 32, 16, 17, 31, 12, 11, 11, 17, + 38, 34, 19, 41, 16, 11, 20 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, Location); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; +#endif +{ + if (!yyvaluep) + return; + YYUSE (yylocationp); +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + YY_LOCATION_PRINT (yyoutput, *yylocationp); + YYFPRINTF (yyoutput, ": "); + yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yylsp, yyrule) + YYSTYPE *yyvsp; + YYLTYPE *yylsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + , &(yylsp[(yyi + 1) - (yynrhs)]) ); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, yylsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp) +#else +static void +yydestruct (yymsg, yytype, yyvaluep, yylocationp) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; + YYLTYPE *yylocationp; +#endif +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + +/* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; +/* Location data for the look-ahead symbol. */ +YYLTYPE yylloc; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp; + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[2]; + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + yylsp = yyls; +#if YYLTYPE_IS_TRIVIAL + /* Initialize the default location before parsing starts. */ + yylloc.first_line = yylloc.last_line = 1; + yylloc.first_column = yylloc.last_column = 0; +#endif + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yyls1, yysize * sizeof (*yylsp), + &yystacksize); + yyls = yyls1; + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + YYSTACK_RELOCATE (yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + *++yylsp = yylloc; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 7: +#line 76 "gram.y" + { + if (run_command ((yyvsp[(1) - (3)].cmd), &(yyvsp[(2) - (3)].arglist)) && !interactive) + exit (EXIT_USAGE); + gdbmarglist_free (&(yyvsp[(2) - (3)].arglist)); + } + break; + + case 10: +#line 84 "gram.y" + { + if (interactive) + { + yyclearin; + yyerrok; + } + else + YYERROR; + } + break; + + case 11: +#line 93 "gram.y" + { end_def(); } + break; + + case 12: +#line 94 "gram.y" + { + if (interactive) + { + yyclearin; + yyerrok; + } + else + YYERROR; + } + break; + + case 13: +#line 106 "gram.y" + { + gdbmarglist_init (&(yyval.arglist), NULL); + } + break; + + case 15: +#line 113 "gram.y" + { + gdbmarglist_init (&(yyval.arglist), (yyvsp[(1) - (1)].arg)); + } + break; + + case 16: +#line 117 "gram.y" + { + gdbmarglist_add (&(yyvsp[(1) - (2)].arglist), (yyvsp[(2) - (2)].arg)); + (yyval.arglist) = (yyvsp[(1) - (2)].arglist); + } + break; + + case 17: +#line 124 "gram.y" + { + (yyval.arg) = gdbmarg_string ((yyvsp[(1) - (1)].string), &(yylsp[(1) - (1)])); + } + break; + + case 18: +#line 128 "gram.y" + { + (yyval.arg) = gdbmarg_kvpair ((yyvsp[(1) - (1)].kvpair), &(yylsp[(1) - (1)])); + } + break; + + case 19: +#line 134 "gram.y" + { + (yyval.kvpair) = (yyvsp[(2) - (3)].kvlist).head; + } + break; + + case 20: +#line 140 "gram.y" + { + (yyval.kvlist).head = (yyval.kvlist).tail = (yyvsp[(1) - (1)].kvpair); + } + break; + + case 21: +#line 144 "gram.y" + { + (yyvsp[(1) - (3)].kvlist).tail->next = (yyvsp[(3) - (3)].kvpair); + (yyvsp[(1) - (3)].kvlist).tail = (yyvsp[(3) - (3)].kvpair); + (yyval.kvlist) = (yyvsp[(1) - (3)].kvlist); + } + break; + + case 23: +#line 153 "gram.y" + { + (yyvsp[(3) - (3)].kvpair)->key = (yyvsp[(1) - (3)].string); + (yyval.kvpair) = (yyvsp[(3) - (3)].kvpair); + } + break; + + case 24: +#line 160 "gram.y" + { + (yyval.kvpair) = kvpair_string (&(yylsp[(1) - (1)]), (yyvsp[(1) - (1)].string)); + } + break; + + case 25: +#line 164 "gram.y" + { + (yyval.kvpair) = kvpair_list (&(yylsp[(1) - (3)]), (yyvsp[(2) - (3)].slist).head); + } + break; + + case 26: +#line 170 "gram.y" + { + (yyval.slist).head = (yyval.slist).tail = slist_new ((yyvsp[(1) - (1)].string)); + } + break; + + case 27: +#line 174 "gram.y" + { + struct slist *s = slist_new ((yyvsp[(3) - (3)].string)); + (yyvsp[(1) - (3)].slist).tail->next = s; + (yyvsp[(1) - (3)].slist).tail = s; + (yyval.slist) = (yyvsp[(1) - (3)].slist); + } + break; + + case 30: +#line 186 "gram.y" + { begin_def (); } + break; + + case 31: +#line 187 "gram.y" + { + end_def (); + dsegm_free_list (dsdef[(yyvsp[(2) - (4)].num)]); + dsdef[(yyvsp[(2) - (4)].num)] = (yyvsp[(4) - (4)].dsegm); + } + break; + + case 32: +#line 195 "gram.y" + { + (yyval.dsegm) = (yyvsp[(2) - (4)].dsegmlist).head; + } + break; + + case 33: +#line 199 "gram.y" + { + (yyval.dsegm) = dsegm_new_field ((yyvsp[(1) - (1)].type), NULL, 1); + } + break; + + case 36: +#line 209 "gram.y" + { + if (strcmp ((yyvsp[(1) - (1)].string), "key") == 0) + (yyval.num) = DS_KEY; + else if (strcmp ((yyvsp[(1) - (1)].string), "content") == 0) + (yyval.num) = DS_CONTENT; + else + { + terror (_("expected \"key\" or \"content\", " + "but found \"%s\""), (yyvsp[(1) - (1)].string)); + YYERROR; + } + } + break; + + case 37: +#line 224 "gram.y" + { + (yyval.dsegmlist).head = (yyval.dsegmlist).tail = (yyvsp[(1) - (1)].dsegm); + } + break; + + case 38: +#line 228 "gram.y" + { + (yyvsp[(1) - (3)].dsegmlist).tail->next = (yyvsp[(3) - (3)].dsegm); + (yyvsp[(1) - (3)].dsegmlist).tail = (yyvsp[(3) - (3)].dsegm); + (yyval.dsegmlist) = (yyvsp[(1) - (3)].dsegmlist); + } + break; + + case 39: +#line 236 "gram.y" + { + (yyval.dsegm) = dsegm_new_field ((yyvsp[(1) - (2)].type), (yyvsp[(2) - (2)].string), 1); + } + break; + + case 40: +#line 240 "gram.y" + { + (yyval.dsegm) = dsegm_new_field ((yyvsp[(1) - (5)].type), (yyvsp[(2) - (5)].string), (yyvsp[(4) - (5)].num)); + } + break; + + case 41: +#line 244 "gram.y" + { + (yyval.dsegm) = dsegm_new (FDEF_OFF); + (yyval.dsegm)->v.n = (yyvsp[(2) - (2)].num); + } + break; + + case 42: +#line 249 "gram.y" + { + (yyval.dsegm) = dsegm_new (FDEF_PAD); + (yyval.dsegm)->v.n = (yyvsp[(2) - (2)].num); + } + break; + + case 43: +#line 256 "gram.y" + { + variable_print_all (stdout); + } + break; + + case 48: +#line 268 "gram.y" + { + int t = 1; + int rc; + char *varname = (yyvsp[(1) - (1)].string); + + rc = variable_set (varname, VART_BOOL, &t); + if (rc == VAR_ERR_NOTDEF && strncmp (varname, "no", 2) == 0) + { + t = 0; + varname += 2; + rc = variable_set (varname, VART_BOOL, &t); + } + + switch (rc) + { + case VAR_OK: + break; + + case VAR_ERR_NOTDEF: + lerror (&(yylsp[(1) - (1)]), _("no such variable: %s"), varname); + break; + + case VAR_ERR_BADTYPE: + lerror (&(yylsp[(1) - (1)]), _("%s is not a boolean variable"), varname); + break; + + default: + lerror (&(yylsp[(1) - (1)]), _("unexpected error setting %s: %d"), (yyvsp[(1) - (1)].string), rc); + } + free((yyvsp[(1) - (1)].string)); + } + break; + + case 49: +#line 300 "gram.y" + { + int rc = variable_set ((yyvsp[(1) - (3)].string), VART_STRING, (yyvsp[(3) - (3)].string)); + switch (rc) + { + case VAR_OK: + break; + + case VAR_ERR_NOTDEF: + lerror (&(yylsp[(1) - (3)]), _("no such variable: %s"), (yyvsp[(1) - (3)].string)); + break; + + case VAR_ERR_BADTYPE: + lerror (&(yylsp[(1) - (3)]), _("%s: bad variable type"), (yyvsp[(1) - (3)].string)); + break; + + case VAR_ERR_BADVALUE: + lerror (&(yylsp[(1) - (3)]), _("%s: value %s is not allowed"), (yyvsp[(1) - (3)].string), (yyvsp[(3) - (3)].string)); + break; + + default: + lerror (&(yylsp[(1) - (3)]), _("unexpected error setting %s: %d"), (yyvsp[(1) - (3)].string), rc); + } + free ((yyvsp[(1) - (3)].string)); + free ((yyvsp[(3) - (3)].string)); + } + break; + + case 52: +#line 332 "gram.y" + { + int rc = variable_unset ((yyvsp[(1) - (1)].string)); + switch (rc) + { + case VAR_OK: + break; + + case VAR_ERR_NOTDEF: + lerror (&(yylsp[(1) - (1)]), _("no such variable: %s"), (yyvsp[(1) - (1)].string)); + break; + + case VAR_ERR_BADVALUE: + lerror (&(yylsp[(1) - (1)]), _("%s: variable cannot be unset"), (yyvsp[(1) - (1)].string)); + break; + } + free((yyvsp[(1) - (1)].string)); + } + break; + + +/* Line 1267 of yacc.c. */ +#line 1787 "gram.c" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (yymsg); + } + else + { + yyerror (YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + yyerror_range[0] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + yyerror_range[0] = yylsp[1-yylen]; + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[0] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + yyerror_range[1] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the look-ahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2); + *++yylsp = yyloc; + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, yylsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + +#line 351 "gram.y" + + +void +terror (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + vlerror (&yylloc, fmt, ap); + va_end (ap); +} + +int +yyerror (char *s) +{ + terror ("%s", s); + return 0; +} + diff --git a/src/gram.h b/src/gram.h new file mode 100644 index 0000000..39c77d5 --- /dev/null +++ b/src/gram.h @@ -0,0 +1,110 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + T_TYPE = 258, + T_OFF = 259, + T_PAD = 260, + T_DEF = 261, + T_SET = 262, + T_UNSET = 263, + T_BOGUS = 264, + T_CMD = 265, + T_NUM = 266, + T_IDENT = 267, + T_WORD = 268 + }; +#endif +/* Tokens. */ +#define T_TYPE 258 +#define T_OFF 259 +#define T_PAD 260 +#define T_DEF 261 +#define T_SET 262 +#define T_UNSET 263 +#define T_BOGUS 264 +#define T_CMD 265 +#define T_NUM 266 +#define T_IDENT 267 +#define T_WORD 268 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 50 "gram.y" +{ + char *string; + struct kvpair *kvpair; + struct { struct kvpair *head, *tail; } kvlist; + struct { struct slist *head, *tail; } slist; + struct gdbmarg *arg; + struct gdbmarglist arglist; + int num; + struct datadef *type; + struct dsegm *dsegm; + struct { struct dsegm *head, *tail; } dsegmlist; + struct command *cmd; +} +/* Line 1489 of yacc.c. */ +#line 89 "gram.h" + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + +extern YYSTYPE yylval; + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + +extern YYLTYPE yylloc; diff --git a/src/gram.y b/src/gram.y new file mode 100644 index 0000000..f654edb --- /dev/null +++ b/src/gram.y @@ -0,0 +1,368 @@ +%{ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +#include <autoconf.h> +#include "gdbmtool.h" + +struct dsegm *dsdef[DS_MAX]; + +%} + +%error-verbose +%locations + +%token <type> T_TYPE +%token T_OFF "off" + T_PAD "pad" + T_DEF "define" + T_SET "set" + T_UNSET "unset" + T_BOGUS + +%token <cmd> T_CMD "command verb" +%token <num> T_NUM "number" +%token <string> T_IDENT "identifier" T_WORD "word" +%type <string> string +%type <arg> arg +%type <arglist> arglist arg1list +%type <dsegm> def defbody +%type <dsegmlist> deflist +%type <num> defid +%type <kvpair> kvpair compound value +%type <kvlist> kvlist +%type <slist> slist + +%union { + char *string; + struct kvpair *kvpair; + struct { struct kvpair *head, *tail; } kvlist; + struct { struct slist *head, *tail; } slist; + struct gdbmarg *arg; + struct gdbmarglist arglist; + int num; + struct datadef *type; + struct dsegm *dsegm; + struct { struct dsegm *head, *tail; } dsegmlist; + struct command *cmd; +} + +%% + +input : /* empty */ + | stmtlist + ; + +stmtlist : stmt + | stmtlist stmt + ; + +stmt : /* empty */ '\n' + | T_CMD arglist '\n' + { + if (run_command ($1, &$2) && !interactive) + exit (EXIT_USAGE); + gdbmarglist_free (&$2); + } + | set '\n' + | defn '\n' + | T_BOGUS '\n' + { + if (interactive) + { + yyclearin; + yyerrok; + } + else + YYERROR; + } + | error { end_def(); } '\n' + { + if (interactive) + { + yyclearin; + yyerrok; + } + else + YYERROR; + } + ; + +arglist : /* empty */ + { + gdbmarglist_init (&$$, NULL); + } + | arg1list + ; + +arg1list : arg + { + gdbmarglist_init (&$$, $1); + } + | arg1list arg + { + gdbmarglist_add (&$1, $2); + $$ = $1; + } + ; + +arg : string + { + $$ = gdbmarg_string ($1, &@1); + } + | compound + { + $$ = gdbmarg_kvpair ($1, &@1); + } + ; + +compound : '{' kvlist '}' + { + $$ = $2.head; + } + ; + +kvlist : kvpair + { + $$.head = $$.tail = $1; + } + | kvlist ',' kvpair + { + $1.tail->next = $3; + $1.tail = $3; + $$ = $1; + } + ; + +kvpair : value + | T_IDENT '=' value + { + $3->key = $1; + $$ = $3; + } + ; + +value : string + { + $$ = kvpair_string (&@1, $1); + } + | '{' slist '}' + { + $$ = kvpair_list (&@1, $2.head); + } + ; + +slist : string + { + $$.head = $$.tail = slist_new ($1); + } + | slist ',' string + { + struct slist *s = slist_new ($3); + $1.tail->next = s; + $1.tail = s; + $$ = $1; + } + ; + +string : T_IDENT + | T_WORD + ; + +defn : T_DEF defid { begin_def (); } defbody + { + end_def (); + dsegm_free_list (dsdef[$2]); + dsdef[$2] = $4; + } + ; + +defbody : '{' deflist optcomma '}' + { + $$ = $2.head; + } + | T_TYPE + { + $$ = dsegm_new_field ($1, NULL, 1); + } + ; + +optcomma : /* empty */ + | ',' + ; + +defid : T_IDENT + { + if (strcmp ($1, "key") == 0) + $$ = DS_KEY; + else if (strcmp ($1, "content") == 0) + $$ = DS_CONTENT; + else + { + terror (_("expected \"key\" or \"content\", " + "but found \"%s\""), $1); + YYERROR; + } + } + ; + +deflist : def + { + $$.head = $$.tail = $1; + } + | deflist ',' def + { + $1.tail->next = $3; + $1.tail = $3; + $$ = $1; + } + ; + +def : T_TYPE T_IDENT + { + $$ = dsegm_new_field ($1, $2, 1); + } + | T_TYPE T_IDENT '[' T_NUM ']' + { + $$ = dsegm_new_field ($1, $2, $4); + } + | T_OFF T_NUM + { + $$ = dsegm_new (FDEF_OFF); + $$->v.n = $2; + } + | T_PAD T_NUM + { + $$ = dsegm_new (FDEF_PAD); + $$->v.n = $2; + } + ; + +set : T_SET + { + variable_print_all (stdout); + } + | T_SET asgnlist + | T_UNSET varlist + ; + +asgnlist : asgn + | asgnlist asgn + ; + +asgn : T_IDENT + { + int t = 1; + int rc; + char *varname = $1; + + rc = variable_set (varname, VART_BOOL, &t); + if (rc == VAR_ERR_NOTDEF && strncmp (varname, "no", 2) == 0) + { + t = 0; + varname += 2; + rc = variable_set (varname, VART_BOOL, &t); + } + + switch (rc) + { + case VAR_OK: + break; + + case VAR_ERR_NOTDEF: + lerror (&@1, _("no such variable: %s"), varname); + break; + + case VAR_ERR_BADTYPE: + lerror (&@1, _("%s is not a boolean variable"), varname); + break; + + default: + lerror (&@1, _("unexpected error setting %s: %d"), $1, rc); + } + free($1); + } + | T_IDENT '=' string + { + int rc = variable_set ($1, VART_STRING, $3); + switch (rc) + { + case VAR_OK: + break; + + case VAR_ERR_NOTDEF: + lerror (&@1, _("no such variable: %s"), $1); + break; + + case VAR_ERR_BADTYPE: + lerror (&@1, _("%s: bad variable type"), $1); + break; + + case VAR_ERR_BADVALUE: + lerror (&@1, _("%s: value %s is not allowed"), $1, $3); + break; + + default: + lerror (&@1, _("unexpected error setting %s: %d"), $1, rc); + } + free ($1); + free ($3); + } + ; + +varlist : var + | varlist var + ; + +var : T_IDENT + { + int rc = variable_unset ($1); + switch (rc) + { + case VAR_OK: + break; + + case VAR_ERR_NOTDEF: + lerror (&@1, _("no such variable: %s"), $1); + break; + + case VAR_ERR_BADVALUE: + lerror (&@1, _("%s: variable cannot be unset"), $1); + break; + } + free($1); + } + ; + +%% + +void +terror (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + vlerror (&yylloc, fmt, ap); + va_end (ap); +} + +int +yyerror (char *s) +{ + terror ("%s", s); + return 0; +} diff --git a/src/hash.c b/src/hash.c new file mode 100644 index 0000000..316785d --- /dev/null +++ b/src/hash.c @@ -0,0 +1,47 @@ +/* hash.c - The gdbm hash function. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + + +/* This hash function computes a 31 bit value. The value is used to index + the hash directory using the top n bits. It is also used in a hash bucket + to find the home position of the element by taking the value modulo the + bucket hash table size. */ + +int +_gdbm_hash (datum key) +{ + unsigned int value; /* Used to compute the hash value. */ + int index; /* Used to cycle through random values. */ + + + /* Set the initial value from key. */ + value = 0x238F13AF * key.dsize; + for (index = 0; index < key.dsize; index++) + value = (value + (key.dptr[index] << (index*5 % 24))) & 0x7FFFFFFF; + + value = (1103515243 * value + 12345) & 0x7FFFFFFF; + + /* Return the value. */ + return((int) value); +} diff --git a/src/lex.c b/src/lex.c new file mode 100644 index 0000000..a134ca1 --- /dev/null +++ b/src/lex.c @@ -0,0 +1,2463 @@ + +#line 3 "lex.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 35 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart(yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int yyleng; + +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart (FILE *input_file ); +void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ); +void yy_delete_buffer (YY_BUFFER_STATE b ); +void yy_flush_buffer (YY_BUFFER_STATE b ); +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state (void ); + +static void yyensure_buffer_stack (void ); +static void yy_load_buffer_state (void ); +static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); + +void *yyalloc (yy_size_t ); +void *yyrealloc (void *,yy_size_t ); +void yyfree (void * ); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +typedef unsigned char YY_CHAR; + +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; + +typedef int yy_state_type; + +extern int yylineno; + +int yylineno = 1; + +extern char *yytext; +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + yyleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 24 +#define YY_END_OF_BUFFER 25 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_acclist[127] = + { 0, + 25, 13, 23, 24, 20, 23, 24, 22, 24, 23, + 24, 3, 13, 23, 24, 23, 24, 12, 13, 23, + 24, 20, 23, 24, 3, 13, 23, 24, 9, 13, + 23, 24, 10, 12, 13, 23, 24, 24, 24, 17, + 24, 24, 21, 24, 8, 13, 23, 24, 8, 13, + 23, 24, 11, 13, 23, 24, 11, 13, 23, 24, + 11, 13, 23, 24, 13, 20, 14, 3, 13, 3, + 2, 12, 13, 20, 9, 10, 3, 3, 13, 10, + 12, 13, 17, 19, 18, 7, 13, 13, 8, 13, + 11, 13, 11, 13, 11, 13, 16, 15, 10, 3, + + 3, 13, 7, 13, 6, 13, 4, 11, 13, 5, + 11, 13, 3, 3, 13, 6, 13, 3, 3, 13, + 3, 3, 3, 1, 2, 1 + } ; + +static yyconst flex_int16_t yy_accept[86] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 5, 8, 10, 12, 16, 18, 22, 25, 29, 33, + 38, 39, 40, 42, 43, 45, 49, 53, 57, 61, + 65, 66, 67, 67, 68, 68, 70, 71, 72, 74, + 75, 75, 76, 77, 78, 80, 83, 83, 84, 84, + 85, 86, 88, 89, 91, 93, 95, 97, 98, 99, + 99, 99, 100, 101, 103, 105, 107, 110, 113, 113, + 114, 116, 118, 118, 119, 121, 121, 122, 123, 123, + 124, 126, 126, 127, 127 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 4, 5, 1, 1, 1, 1, 1, + 1, 1, 1, 6, 7, 1, 1, 8, 9, 9, + 9, 9, 9, 9, 9, 10, 10, 1, 1, 1, + 6, 1, 11, 1, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 14, 13, 13, + 6, 15, 6, 1, 13, 1, 16, 12, 12, 17, + + 18, 19, 13, 13, 20, 13, 13, 21, 13, 22, + 23, 24, 13, 13, 13, 13, 13, 13, 13, 14, + 13, 13, 6, 1, 6, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst flex_int32_t yy_meta[25] = + { 0, + 1, 2, 3, 4, 5, 4, 6, 6, 6, 6, + 5, 7, 7, 7, 1, 7, 7, 7, 7, 7, + 7, 7, 7, 7 + } ; + +static yyconst flex_int16_t yy_base[100] = + { 0, + 0, 24, 46, 48, 50, 52, 67, 0, 186, 0, + 183, 346, 88, 102, 346, 108, 55, 122, 0, 143, + 92, 346, 346, 180, 346, 50, 85, 158, 173, 81, + 0, 180, 95, 346, 178, 96, 177, 346, 192, 109, + 100, 346, 0, 113, 110, 207, 114, 346, 176, 346, + 346, 124, 215, 127, 158, 157, 121, 346, 346, 120, + 155, 0, 136, 125, 141, 227, 153, 149, 144, 139, + 149, 0, 144, 142, 150, 155, 162, 152, 150, 98, + 346, 59, 346, 346, 246, 253, 260, 267, 274, 280, + 287, 294, 301, 308, 315, 322, 324, 331, 338 + + } ; + +static yyconst flex_int16_t yy_def[100] = + { 0, + 84, 84, 85, 85, 85, 85, 84, 7, 84, 86, + 84, 84, 87, 88, 84, 89, 90, 88, 86, 91, + 92, 84, 84, 93, 84, 86, 86, 94, 94, 29, + 86, 84, 87, 84, 95, 18, 96, 84, 89, 90, + 84, 84, 97, 96, 18, 91, 92, 84, 93, 84, + 84, 86, 86, 86, 29, 29, 29, 84, 84, 84, + 84, 97, 96, 18, 86, 86, 29, 29, 84, 96, + 18, 66, 84, 96, 18, 84, 96, 98, 99, 98, + 84, 99, 84, 0, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84 + + } ; + +static yyconst flex_int16_t yy_nxt[371] = + { 0, + 10, 11, 12, 13, 14, 15, 10, 10, 10, 10, + 10, 16, 16, 16, 10, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 10, 17, 12, 13, 18, 15, + 10, 10, 10, 10, 19, 20, 20, 20, 10, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 22, 23, + 22, 23, 22, 23, 22, 23, 40, 52, 52, 41, + 24, 83, 24, 53, 24, 42, 24, 10, 11, 25, + 15, 10, 15, 10, 26, 27, 27, 10, 28, 28, + 28, 10, 28, 28, 28, 28, 28, 28, 28, 29, + 30, 34, 54, 54, 54, 48, 57, 37, 34, 55, + + 81, 60, 35, 37, 38, 37, 49, 37, 31, 35, + 40, 37, 31, 41, 44, 38, 36, 48, 31, 42, + 61, 60, 31, 44, 38, 37, 37, 37, 49, 64, + 36, 65, 65, 63, 54, 54, 54, 68, 38, 55, + 61, 38, 45, 31, 38, 36, 71, 31, 65, 65, + 37, 78, 83, 31, 81, 70, 79, 31, 31, 77, + 74, 76, 31, 78, 38, 73, 75, 55, 31, 36, + 36, 55, 31, 31, 69, 67, 55, 31, 51, 38, + 59, 32, 51, 31, 32, 84, 84, 31, 84, 84, + 84, 56, 31, 84, 84, 84, 31, 84, 84, 84, + + 84, 84, 31, 84, 84, 84, 31, 31, 84, 84, + 84, 31, 84, 84, 84, 84, 84, 31, 84, 84, + 84, 31, 66, 66, 66, 84, 66, 84, 84, 84, + 66, 66, 66, 66, 72, 72, 72, 84, 72, 84, + 84, 84, 72, 72, 72, 72, 21, 21, 21, 21, + 21, 21, 21, 31, 84, 84, 84, 31, 31, 31, + 33, 33, 84, 33, 33, 33, 33, 36, 36, 36, + 36, 36, 36, 36, 39, 84, 84, 84, 39, 39, + 39, 43, 84, 84, 43, 84, 43, 46, 84, 84, + 84, 46, 46, 46, 47, 47, 84, 47, 47, 47, + + 47, 50, 50, 50, 50, 50, 50, 50, 55, 84, + 84, 84, 55, 55, 55, 58, 58, 58, 58, 58, + 58, 58, 37, 37, 37, 37, 37, 37, 37, 62, + 62, 80, 80, 80, 80, 80, 80, 80, 82, 82, + 82, 82, 82, 82, 82, 9, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84 + } ; + +static yyconst flex_int16_t yy_chk[371] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, + 4, 4, 5, 5, 6, 6, 17, 26, 26, 17, + 3, 82, 4, 26, 5, 17, 6, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 13, 27, 27, 27, 21, 30, 36, 33, 30, + + 80, 41, 13, 14, 14, 14, 21, 14, 16, 33, + 40, 45, 16, 40, 44, 44, 36, 47, 16, 40, + 41, 60, 16, 18, 18, 18, 64, 18, 47, 45, + 45, 52, 52, 44, 54, 54, 54, 57, 63, 57, + 60, 70, 18, 20, 74, 64, 64, 20, 65, 65, + 71, 75, 79, 20, 78, 63, 76, 20, 28, 74, + 70, 73, 28, 77, 77, 69, 71, 68, 28, 71, + 75, 67, 28, 29, 61, 56, 55, 29, 49, 37, + 35, 32, 24, 29, 11, 9, 0, 29, 0, 0, + 0, 29, 39, 0, 0, 0, 39, 0, 0, 0, + + 0, 0, 39, 0, 0, 0, 39, 46, 0, 0, + 0, 46, 0, 0, 0, 0, 0, 46, 0, 0, + 0, 46, 53, 53, 53, 0, 53, 0, 0, 0, + 53, 53, 53, 53, 66, 66, 66, 0, 66, 0, + 0, 0, 66, 66, 66, 66, 85, 85, 85, 85, + 85, 85, 85, 86, 0, 0, 0, 86, 86, 86, + 87, 87, 0, 87, 87, 87, 87, 88, 88, 88, + 88, 88, 88, 88, 89, 0, 0, 0, 89, 89, + 89, 90, 0, 0, 90, 0, 90, 91, 0, 0, + 0, 91, 91, 91, 92, 92, 0, 92, 92, 92, + + 92, 93, 93, 93, 93, 93, 93, 93, 94, 0, + 0, 0, 94, 94, 94, 95, 95, 95, 95, 95, + 95, 95, 96, 96, 96, 96, 96, 96, 96, 97, + 97, 98, 98, 98, 98, 98, 98, 98, 99, 99, + 99, 99, 99, 99, 99, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84 + } ; + +extern int yy_flex_debug; +int yy_flex_debug = 0; + +static yy_state_type *yy_state_buf=0, *yy_state_ptr=0; +static char *yy_full_match; +static int yy_lp; +#define REJECT \ +{ \ +*yy_cp = (yy_hold_char); /* undo effects of setting up yytext */ \ +yy_cp = (yy_full_match); /* restore poss. backed-over text */ \ +++(yy_lp); \ +goto find_rule; \ +} + +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "lex.l" +#line 2 "lex.l" +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +#include "gdbmtool.h" +#include "gram.h" + +struct point point; + +/* Advance locus to the next line */ +void +advance_line () +{ + ++point.line; + point.col = 0; +} + +#define YY_USER_ACTION \ + do \ + { \ + if (YYSTATE == 0) \ + { \ + yylloc.beg = point; \ + yylloc.beg.col++; \ + } \ + point.col += yyleng; \ + yylloc.end = point; \ + } \ + while (0); + +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + do \ + { \ + result = read_input (buf, max_size); \ + } \ + while (0); + +void string_begin (void); +void string_add (const char *s, int l); +void string_addc (int c); +char *string_end (void); +int unescape (int c); + +static ssize_t read_input (char *buf, size_t size); + +struct context /* Input context */ +{ + struct context *parent; /* Pointer to the parent context */ + struct locus locus; /* Locus */ + struct point point; + int interactive; + ino_t ino; /* Inode number */ + dev_t dev; /* Device number */ + FILE *file; /* Input file */ + YY_BUFFER_STATE buf; /* Buffer */ +}; + +static struct context *context_tos; +static ino_t ino; +static dev_t dev; +int interactive; /* Are we running in interactive mode? */ +static int initialized; + +static void +context_push () +{ + struct context *cp = ecalloc (1, sizeof (*cp)); + + cp->locus = yylloc; + cp->point = point; + cp->interactive = interactive; + cp->ino = ino; + cp->dev = dev; + cp->file = yyin; + cp->buf = YY_CURRENT_BUFFER; + cp->parent = context_tos; + context_tos = cp; +} + +int +context_pop () +{ + struct context *cp = context_tos; + + fclose (yyin); + yyin = NULL; + free (point.file); + point.file = NULL; + memset (&yylloc, 0, sizeof (yylloc)); + + if (!cp) + return 1; + + context_tos = cp->parent; + + yylloc = cp->locus; + point = cp->point; + interactive = cp->interactive; + ino = cp->ino; + dev = cp->dev; + yyin = cp->file; + yy_delete_buffer (YY_CURRENT_BUFFER); + yy_switch_to_buffer (cp->buf); + + return 0; +} + +static struct context * +findctx (struct stat *st) +{ + struct context *cp; + + for (cp = context_tos; cp; cp = cp->parent) + if (cp->dev == st->st_dev && cp->ino == st->st_ino) + break; + return cp; +} + +int +setsource (const char *name, int intr) +{ + struct stat st; + struct context *cp; + FILE *fp; + + if (strcmp (name, "-") == 0) + { + fp = stdin; + name = "stdin"; + } + else + { + if (stat (name, &st)) + { + terror (_("cannot open `%s': %s"), name, strerror (errno)); + return -1; + } + else if (!S_ISREG (st.st_mode)) + { + terror (_("%s is not a regular file"), name); + return -1; + } + + cp = findctx (&st); + if (cp) + { + terror (_("recursive sourcing")); + if (cp->parent) + lerror (&cp->locus, _("%s already sourced here"), name); + return 1; + } + + fp = fopen (name, "r"); + if (!fp) + { + terror (_("cannot open %s for reading: %s"), name, + strerror (errno)); + return 1; + } + } + + if (yyin) + context_push (); + + yyin = fp; + yy_switch_to_buffer (yy_create_buffer (yyin, YY_BUF_SIZE)); + + interactive = intr; + dev = st.st_dev; + ino = st.st_ino; + + point.file = estrdup (name); + point.line = 1; + point.col = 0; + + initialized = 1; + + return 0; +} + +#line 774 "lex.c" + +#define INITIAL 0 +#define STR 1 +#define MLSTR 2 +#define DEF 3 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals (void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy (void ); + +int yyget_debug (void ); + +void yyset_debug (int debug_flag ); + +YY_EXTRA_TYPE yyget_extra (void ); + +void yyset_extra (YY_EXTRA_TYPE user_defined ); + +FILE *yyget_in (void ); + +void yyset_in (FILE * in_str ); + +FILE *yyget_out (void ); + +void yyset_out (FILE * out_str ); + +int yyget_leng (void ); + +char *yyget_text (void ); + +int yyget_lineno (void ); + +void yyset_lineno (int line_number ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap (void ); +#else +extern int yywrap (void ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex (void); + +#define YY_DECL int yylex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + if ( yyleng > 0 ) \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \ + (yytext[yyleng - 1] == '\n'); \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 208 "lex.l" + +#line 962 "lex.c" + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + /* Create the reject buffer large enough to save one state per allowed character. */ + if ( ! (yy_state_buf) ) + (yy_state_buf) = (yy_state_type *)yyalloc(YY_STATE_BUF_SIZE ); + if ( ! (yy_state_buf) ) + YY_FATAL_ERROR( "out of dynamic memory in yylex()" ); + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ); + } + + yy_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); + yy_current_state += YY_AT_BOL(); + + (yy_state_ptr) = (yy_state_buf); + *(yy_state_ptr)++ = yy_current_state; + +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 85 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + *(yy_state_ptr)++ = yy_current_state; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 346 ); + +yy_find_action: + yy_current_state = *--(yy_state_ptr); + (yy_lp) = yy_accept[yy_current_state]; +find_rule: /* we branch to this label when backing up */ + for ( ; ; ) /* until we find what rule we matched */ + { + if ( (yy_lp) && (yy_lp) < yy_accept[yy_current_state + 1] ) + { + yy_act = yy_acclist[(yy_lp)]; + { + (yy_full_match) = yy_cp; + break; + } + } + --yy_cp; + yy_current_state = *--(yy_state_ptr); + (yy_lp) = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ +case 1: +/* rule 1 can match eol */ +YY_RULE_SETUP +#line 209 "lex.l" +{ + char *p; + char *file = NULL; + int line, len; + + for (p = strchr (yytext, '#') + 1; *p == ' ' || *p == '\t'; p++); + p += 4; + for (; *p == ' ' || *p == '\t'; p++); + + line = strtol (p, &p, 10); + for (; *p == ' ' || *p == '\t'; p++); + + if (*p == '"') + { + p++; + len = strcspn (p, "\""); + if (p[len] == 0) + { + yyerror (_("invalid #line statement")); + REJECT; + } + file = emalloc (len + 1); + memcpy (file, p, len); + file[len] = 0; + for (p += len + 1; *p == ' ' || *p == '\t'; p++); + } + if (*p != '\n' ) + { + yyerror (_("invalid #line statement")); + free (file); + REJECT; + } + if (file) + point.file = file; + point.line = line; + point.col = 0; +} + YY_BREAK +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +#line 246 "lex.l" +advance_line (); + YY_BREAK +case 3: +YY_RULE_SETUP +#line 247 "lex.l" +/* end-of-file comment */; + YY_BREAK +case 4: +YY_RULE_SETUP +#line 249 "lex.l" +{ return T_OFF; } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 250 "lex.l" +{ return T_PAD; } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 251 "lex.l" +{ yylval.num = strtoul (yytext, NULL, 16); + return T_NUM; }; + YY_BREAK +case 7: +YY_RULE_SETUP +#line 253 "lex.l" +{ yylval.num = strtoul (yytext, NULL, 8); + return T_NUM; }; + YY_BREAK +case 8: +YY_RULE_SETUP +#line 255 "lex.l" +{ yylval.num = strtoul (yytext, NULL, 10); + return T_NUM; }; + YY_BREAK +case 9: +YY_RULE_SETUP +#line 257 "lex.l" +{ return command_lookup ("help", &yylloc, &yylval.cmd); } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 258 "lex.l" +{ char *p = yytext + strspn (yytext, " \t"); + return command_lookup (p, &yylloc, &yylval.cmd); + } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 261 "lex.l" +{ if ((yylval.type = datadef_lookup (yytext))) + return T_TYPE; + else + { + yylval.string = estrdup (yytext); + return T_IDENT; + } + } + YY_BREAK +case 12: +YY_RULE_SETUP +#line 269 "lex.l" +{ yylval.string = estrdup (yytext); + return T_IDENT; + } + YY_BREAK +case 13: +YY_RULE_SETUP +#line 272 "lex.l" +{ yylval.string = estrdup (yytext); + return T_WORD; } + YY_BREAK +case 14: +YY_RULE_SETUP +#line 274 "lex.l" +{ yylval.string = emalloc (yyleng - 1); + memcpy (yylval.string, yytext+1, yyleng-2); + yylval.string[yyleng-2] = 0; + return T_WORD; } + YY_BREAK +case 15: +*yy_cp = (yy_hold_char); /* undo effects of setting up yytext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 278 "lex.l" +{ string_begin (); + string_add (yytext + 1, yyleng - 2); + BEGIN (MLSTR); } + YY_BREAK +case 16: +YY_RULE_SETUP +#line 281 "lex.l" +{ string_begin (); + string_add (yytext + 1, yyleng - 3); + string_addc (unescape (yytext[yyleng-1])); + BEGIN (STR); } + YY_BREAK +case 17: +YY_RULE_SETUP +#line 285 "lex.l" +{ if (yyleng > 1) + string_add (yytext, yyleng - 1); + yylval.string = string_end (); + BEGIN (INITIAL); + return T_WORD; } + YY_BREAK +case 18: +*yy_cp = (yy_hold_char); /* undo effects of setting up yytext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 290 "lex.l" +{ string_add (yytext, yyleng - 1); } + YY_BREAK +case 19: +YY_RULE_SETUP +#line 291 "lex.l" +{ string_add (yytext, yyleng - 2); + string_addc (unescape (yytext[yyleng-1])); } + YY_BREAK +case 20: +YY_RULE_SETUP +#line 293 "lex.l" +; + YY_BREAK +case 21: +/* rule 21 can match eol */ +YY_RULE_SETUP +#line 294 "lex.l" +{ advance_line (); } + YY_BREAK +case 22: +/* rule 22 can match eol */ +YY_RULE_SETUP +#line 295 "lex.l" +{ advance_line (); return '\n'; } + YY_BREAK +case 23: +YY_RULE_SETUP +#line 296 "lex.l" +return yytext[0]; + YY_BREAK +case 24: +YY_RULE_SETUP +#line 297 "lex.l" +ECHO; + YY_BREAK +#line 1249 "lex.c" + case YY_STATE_EOF(INITIAL): + case YY_STATE_EOF(STR): + case YY_STATE_EOF(MLSTR): + case YY_STATE_EOF(DEF): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), (size_t) num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart(yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + yy_current_state += YY_AT_BOL(); + + (yy_state_ptr) = (yy_state_buf); + *(yy_state_ptr)++ = yy_current_state; + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 85 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + *(yy_state_ptr)++ = yy_current_state; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + + register YY_CHAR yy_c = 1; + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 85 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 84); + if ( ! yy_is_jam ) + *(yy_state_ptr)++ = yy_current_state; + + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart(yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n'); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ); + } + + yy_init_buffer(YY_CURRENT_BUFFER,input_file ); + yy_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void yy_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * + */ + void yy_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree((void *) b->yy_ch_buf ); + + yyfree((void *) b ); +} + +#ifndef __cplusplus +extern int isatty (int ); +#endif /* __cplusplus */ + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + yy_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void yy_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void yypop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (yyconst char * yystr ) +{ + + return yy_scan_bytes(yystr,strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) yyalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int yyget_lineno (void) +{ + + return yylineno; +} + +/** Get the input stream. + * + */ +FILE *yyget_in (void) +{ + return yyin; +} + +/** Get the output stream. + * + */ +FILE *yyget_out (void) +{ + return yyout; +} + +/** Get the length of the current token. + * + */ +int yyget_leng (void) +{ + return yyleng; +} + +/** Get the current token. + * + */ + +char *yyget_text (void) +{ + return yytext; +} + +/** Set the current line number. + * @param line_number + * + */ +void yyset_lineno (int line_number ) +{ + + yylineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * in_str ) +{ + yyin = in_str ; +} + +void yyset_out (FILE * out_str ) +{ + yyout = out_str ; +} + +int yyget_debug (void) +{ + return yy_flex_debug; +} + +void yyset_debug (int bdebug ) +{ + yy_flex_debug = bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = 0; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = (char *) 0; + (yy_init) = 0; + (yy_start) = 0; + + (yy_state_buf) = 0; + (yy_state_ptr) = 0; + (yy_full_match) = 0; + (yy_lp) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = (FILE *) 0; + yyout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + yyfree ( (yy_state_buf) ); + (yy_state_buf) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *yyrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 297 "lex.l" + + + +int +yywrap () +{ + return context_pop (); +} + +void +begin_def (void) +{ + BEGIN (DEF); +} + +void +end_def (void) +{ + BEGIN (INITIAL); +} + +static ssize_t +read_input (char *buf, size_t size) +{ + if (interactive) + { + if (YY_AT_BOL ()) + print_prompt (); + if (fgets (buf, size, yyin) == NULL) + return 0; + return strlen (buf); + } + return fread (buf, 1, size, yyin); +} + + +struct strseg +{ + struct strseg *next; + int len; + char ptr[1]; +}; + +static struct strseg *strseg_head, *strseg_tail; + +void +string_begin (void) +{ + strseg_head = strseg_tail = NULL; +} + +void +strseg_attach (struct strseg *seg) +{ + seg->next = NULL; + if (strseg_tail) + strseg_tail->next = seg; + else + strseg_head = seg; + strseg_tail = seg; +} + +void +string_add (const char *s, int l) +{ + struct strseg *seg = emalloc (sizeof (*seg) + l); + memcpy (seg->ptr, s, l); + seg->len = l; + strseg_attach (seg); +} + +void +string_addc (int c) +{ + struct strseg *seg = emalloc (sizeof (*seg)); + seg->ptr[0] = c; + seg->len = 1; + strseg_attach (seg); +} + +char * +string_end (void) +{ + int len = 1; + struct strseg *seg; + char *ret, *p; + + for (seg = strseg_head; seg; seg = seg->next) + len += seg->len; + + ret = emalloc (len); + p = ret; + for (seg = strseg_head; seg; ) + { + struct strseg *next = seg->next; + memcpy (p, seg->ptr, seg->len); + p += seg->len; + free (seg); + seg = next; + } + *p = 0; + + strseg_head = strseg_tail = NULL; + + return ret; +} + +static char transtab[] = "\\\\\"\"a\ab\bf\fn\nr\rt\tv\v"; + +int +unescape (int c) +{ + char *p; + + for (p = transtab; *p; p += 2) + { + if (*p == c) + return p[1]; + } + return c; +} + +int +escape (int c) +{ + char *p; + for (p = transtab + sizeof (transtab) - 2; p > transtab; p -= 2) + { + if (*p == c) + return p[-1]; + } + return 0; +} + +void +vlerror (struct locus *loc, const char *fmt, va_list ap) +{ + if (!interactive) + fprintf (stderr, "%s: ", progname); + if (initialized && loc && loc->beg.file) + { + YY_LOCATION_PRINT (stderr, *loc); + fprintf (stderr, ": "); + } + vfprintf (stderr, fmt, ap); + fputc ('\n', stderr); +} + +void +lerror (struct locus *loc, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + vlerror (loc, fmt, ap); + va_end (ap); +} + + +struct prompt_exp; + +void +pe_file_name (struct prompt_exp *p) +{ + if (file_name) + fwrite (file_name, strlen (file_name), 1, stdout); +} + +void +pe_program_name (struct prompt_exp *p) +{ + fwrite (progname, strlen (progname), 1, stdout); +} + +void +pe_package_name (struct prompt_exp *p) +{ + fwrite (PACKAGE_NAME, sizeof (PACKAGE_NAME) - 1, 1, stdout); +} + +void +pe_program_version (struct prompt_exp *p) +{ + fwrite (PACKAGE_VERSION, sizeof (PACKAGE_VERSION) - 1, 1, stdout); +} + +void +pe_space (struct prompt_exp *p) +{ + fwrite (" ", 1, 1, stdout); +} + +struct prompt_exp +{ + int ch; + void (*fun) (struct prompt_exp *); + char *cache; +}; + +struct prompt_exp prompt_exp[] = { + { 'f', pe_file_name }, + { 'p', pe_program_name }, + { 'P', pe_package_name }, + { 'v', pe_program_version }, + { '_', pe_space }, + { 0 } +}; + +static void +expand_char (int c) +{ + struct prompt_exp *p; + + if (c && c != '%') + { + for (p = prompt_exp; p->ch; p++) + { + if (c == p->ch) + { + if (p->cache) + free (p->cache); + return p->fun (p); + } + } + } + putchar ('%'); + putchar (c); +} + +char const * +psname () +{ + if (YYSTATE == DEF || YYSTATE == MLSTR) + return "ps2"; + return "ps1"; +} + +void +print_prompt () +{ + const char *s; + const char *prompt; + + switch (variable_get (psname (), VART_STRING, (void *) &prompt)) + { + case VAR_OK: + break; + + case VAR_ERR_NOTSET: + return; + + default: + abort (); + } + + for (s = prompt; *s; s++) + { + if (*s == '%') + { + if (!*++s) + { + putchar ('%'); + break; + } + expand_char (*s); + } + else + putchar (*s); + } + + fflush (stdout); +} + + diff --git a/src/lex.l b/src/lex.l new file mode 100644 index 0000000..abb9047 --- /dev/null +++ b/src/lex.l @@ -0,0 +1,568 @@ +%{ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +#include "gdbmtool.h" +#include "gram.h" + +struct point point; + +/* Advance locus to the next line */ +void +advance_line () +{ + ++point.line; + point.col = 0; +} + +#define YY_USER_ACTION \ + do \ + { \ + if (YYSTATE == 0) \ + { \ + yylloc.beg = point; \ + yylloc.beg.col++; \ + } \ + point.col += yyleng; \ + yylloc.end = point; \ + } \ + while (0); + +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + do \ + { \ + result = read_input (buf, max_size); \ + } \ + while (0); + +void string_begin (void); +void string_add (const char *s, int l); +void string_addc (int c); +char *string_end (void); +int unescape (int c); + +static ssize_t read_input (char *buf, size_t size); + +struct context /* Input context */ +{ + struct context *parent; /* Pointer to the parent context */ + struct locus locus; /* Locus */ + struct point point; + int interactive; + ino_t ino; /* Inode number */ + dev_t dev; /* Device number */ + FILE *file; /* Input file */ + YY_BUFFER_STATE buf; /* Buffer */ +}; + +static struct context *context_tos; +static ino_t ino; +static dev_t dev; +int interactive; /* Are we running in interactive mode? */ +static int initialized; + +static void +context_push () +{ + struct context *cp = ecalloc (1, sizeof (*cp)); + + cp->locus = yylloc; + cp->point = point; + cp->interactive = interactive; + cp->ino = ino; + cp->dev = dev; + cp->file = yyin; + cp->buf = YY_CURRENT_BUFFER; + cp->parent = context_tos; + context_tos = cp; +} + +int +context_pop () +{ + struct context *cp = context_tos; + + fclose (yyin); + yyin = NULL; + free (point.file); + point.file = NULL; + memset (&yylloc, 0, sizeof (yylloc)); + + if (!cp) + return 1; + + context_tos = cp->parent; + + yylloc = cp->locus; + point = cp->point; + interactive = cp->interactive; + ino = cp->ino; + dev = cp->dev; + yyin = cp->file; + yy_delete_buffer (YY_CURRENT_BUFFER); + yy_switch_to_buffer (cp->buf); + + return 0; +} + +static struct context * +findctx (struct stat *st) +{ + struct context *cp; + + for (cp = context_tos; cp; cp = cp->parent) + if (cp->dev == st->st_dev && cp->ino == st->st_ino) + break; + return cp; +} + +int +setsource (const char *name, int intr) +{ + struct stat st; + struct context *cp; + FILE *fp; + + if (strcmp (name, "-") == 0) + { + fp = stdin; + name = "stdin"; + } + else + { + if (stat (name, &st)) + { + terror (_("cannot open `%s': %s"), name, strerror (errno)); + return -1; + } + else if (!S_ISREG (st.st_mode)) + { + terror (_("%s is not a regular file"), name); + return -1; + } + + cp = findctx (&st); + if (cp) + { + terror (_("recursive sourcing")); + if (cp->parent) + lerror (&cp->locus, _("%s already sourced here"), name); + return 1; + } + + fp = fopen (name, "r"); + if (!fp) + { + terror (_("cannot open %s for reading: %s"), name, + strerror (errno)); + return 1; + } + } + + if (yyin) + context_push (); + + yyin = fp; + yy_switch_to_buffer (yy_create_buffer (yyin, YY_BUF_SIZE)); + + interactive = intr; + dev = st.st_dev; + ino = st.st_ino; + + point.file = estrdup (name); + point.line = 1; + point.col = 0; + + initialized = 1; + + return 0; +} +%} + +%option nounput + +%x STR MLSTR DEF + +WS [ \t][ \t]* +IDENT [a-zA-Z_][a-zA-Z_0-9-]* +N [0-9][0-9]* +P [1-9][0-9]* +X [0-9a-fA-F] +O [0-7] + +%% +^[ \t]*#[ \t]*line[ \t].*\n { + char *p; + char *file = NULL; + int line, len; + + for (p = strchr (yytext, '#') + 1; *p == ' ' || *p == '\t'; p++); + p += 4; + for (; *p == ' ' || *p == '\t'; p++); + + line = strtol (p, &p, 10); + for (; *p == ' ' || *p == '\t'; p++); + + if (*p == '"') + { + p++; + len = strcspn (p, "\""); + if (p[len] == 0) + { + yyerror (_("invalid #line statement")); + REJECT; + } + file = emalloc (len + 1); + memcpy (file, p, len); + file[len] = 0; + for (p += len + 1; *p == ' ' || *p == '\t'; p++); + } + if (*p != '\n' ) + { + yyerror (_("invalid #line statement")); + free (file); + REJECT; + } + if (file) + point.file = file; + point.line = line; + point.col = 0; +} +#.*\n advance_line (); +#.* /* end-of-file comment */; + +<DEF>off { return T_OFF; } +<DEF>pad { return T_PAD; } +<DEF>0[xX]{X}{X}* { yylval.num = strtoul (yytext, NULL, 16); + return T_NUM; }; +<DEF>0{O}{O}* { yylval.num = strtoul (yytext, NULL, 8); + return T_NUM; }; +<DEF>0|{P} { yylval.num = strtoul (yytext, NULL, 10); + return T_NUM; }; +^[ \t]*\? { return command_lookup ("help", &yylloc, &yylval.cmd); } +^[ \t]*{IDENT} { char *p = yytext + strspn (yytext, " \t"); + return command_lookup (p, &yylloc, &yylval.cmd); + } +<DEF>{IDENT} { if ((yylval.type = datadef_lookup (yytext))) + return T_TYPE; + else + { + yylval.string = estrdup (yytext); + return T_IDENT; + } + } +{IDENT} { yylval.string = estrdup (yytext); + return T_IDENT; + } +<INITIAL,DEF>[^ \"\t\n\[\]{},=]+ { yylval.string = estrdup (yytext); + return T_WORD; } +\"[^\\\"\n]*\" { yylval.string = emalloc (yyleng - 1); + memcpy (yylval.string, yytext+1, yyleng-2); + yylval.string[yyleng-2] = 0; + return T_WORD; } +\"[^\\\"\n]*\\$ { string_begin (); + string_add (yytext + 1, yyleng - 2); + BEGIN (MLSTR); } +\"[^\\\"\n]*\\. { string_begin (); + string_add (yytext + 1, yyleng - 3); + string_addc (unescape (yytext[yyleng-1])); + BEGIN (STR); } +<STR,MLSTR>[^\\\"\n]*\" { if (yyleng > 1) + string_add (yytext, yyleng - 1); + yylval.string = string_end (); + BEGIN (INITIAL); + return T_WORD; } +<STR,MLSTR>[^\\\"\n]*\\$ { string_add (yytext, yyleng - 1); } +<STR,MLSTR>[^\\\"\n]*\\. { string_add (yytext, yyleng - 2); + string_addc (unescape (yytext[yyleng-1])); } +<INITIAL,DEF>{WS} ; +<DEF>\n { advance_line (); } +\n { advance_line (); return '\n'; } +<INITIAL,DEF>. return yytext[0]; +%% + +int +yywrap () +{ + return context_pop (); +} + +void +begin_def (void) +{ + BEGIN (DEF); +} + +void +end_def (void) +{ + BEGIN (INITIAL); +} + +static ssize_t +read_input (char *buf, size_t size) +{ + if (interactive) + { + if (YY_AT_BOL ()) + print_prompt (); + if (fgets (buf, size, yyin) == NULL) + return 0; + return strlen (buf); + } + return fread (buf, 1, size, yyin); +} + + +struct strseg +{ + struct strseg *next; + int len; + char ptr[1]; +}; + +static struct strseg *strseg_head, *strseg_tail; + +void +string_begin (void) +{ + strseg_head = strseg_tail = NULL; +} + +void +strseg_attach (struct strseg *seg) +{ + seg->next = NULL; + if (strseg_tail) + strseg_tail->next = seg; + else + strseg_head = seg; + strseg_tail = seg; +} + +void +string_add (const char *s, int l) +{ + struct strseg *seg = emalloc (sizeof (*seg) + l); + memcpy (seg->ptr, s, l); + seg->len = l; + strseg_attach (seg); +} + +void +string_addc (int c) +{ + struct strseg *seg = emalloc (sizeof (*seg)); + seg->ptr[0] = c; + seg->len = 1; + strseg_attach (seg); +} + +char * +string_end (void) +{ + int len = 1; + struct strseg *seg; + char *ret, *p; + + for (seg = strseg_head; seg; seg = seg->next) + len += seg->len; + + ret = emalloc (len); + p = ret; + for (seg = strseg_head; seg; ) + { + struct strseg *next = seg->next; + memcpy (p, seg->ptr, seg->len); + p += seg->len; + free (seg); + seg = next; + } + *p = 0; + + strseg_head = strseg_tail = NULL; + + return ret; +} + +static char transtab[] = "\\\\\"\"a\ab\bf\fn\nr\rt\tv\v"; + +int +unescape (int c) +{ + char *p; + + for (p = transtab; *p; p += 2) + { + if (*p == c) + return p[1]; + } + return c; +} + +int +escape (int c) +{ + char *p; + for (p = transtab + sizeof (transtab) - 2; p > transtab; p -= 2) + { + if (*p == c) + return p[-1]; + } + return 0; +} + +void +vlerror (struct locus *loc, const char *fmt, va_list ap) +{ + if (!interactive) + fprintf (stderr, "%s: ", progname); + if (initialized && loc && loc->beg.file) + { + YY_LOCATION_PRINT (stderr, *loc); + fprintf (stderr, ": "); + } + vfprintf (stderr, fmt, ap); + fputc ('\n', stderr); +} + +void +lerror (struct locus *loc, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + vlerror (loc, fmt, ap); + va_end (ap); +} + + +struct prompt_exp; + +void +pe_file_name (struct prompt_exp *p) +{ + if (file_name) + fwrite (file_name, strlen (file_name), 1, stdout); +} + +void +pe_program_name (struct prompt_exp *p) +{ + fwrite (progname, strlen (progname), 1, stdout); +} + +void +pe_package_name (struct prompt_exp *p) +{ + fwrite (PACKAGE_NAME, sizeof (PACKAGE_NAME) - 1, 1, stdout); +} + +void +pe_program_version (struct prompt_exp *p) +{ + fwrite (PACKAGE_VERSION, sizeof (PACKAGE_VERSION) - 1, 1, stdout); +} + +void +pe_space (struct prompt_exp *p) +{ + fwrite (" ", 1, 1, stdout); +} + +struct prompt_exp +{ + int ch; + void (*fun) (struct prompt_exp *); + char *cache; +}; + +struct prompt_exp prompt_exp[] = { + { 'f', pe_file_name }, + { 'p', pe_program_name }, + { 'P', pe_package_name }, + { 'v', pe_program_version }, + { '_', pe_space }, + { 0 } +}; + +static void +expand_char (int c) +{ + struct prompt_exp *p; + + if (c && c != '%') + { + for (p = prompt_exp; p->ch; p++) + { + if (c == p->ch) + { + if (p->cache) + free (p->cache); + return p->fun (p); + } + } + } + putchar ('%'); + putchar (c); +} + +char const * +psname () +{ + if (YYSTATE == DEF || YYSTATE == MLSTR) + return "ps2"; + return "ps1"; +} + +void +print_prompt () +{ + const char *s; + const char *prompt; + + switch (variable_get (psname (), VART_STRING, (void *) &prompt)) + { + case VAR_OK: + break; + + case VAR_ERR_NOTSET: + return; + + default: + abort (); + } + + for (s = prompt; *s; s++) + { + if (*s == '%') + { + if (!*++s) + { + putchar ('%'); + break; + } + expand_char (*s); + } + else + putchar (*s); + } + + fflush (stdout); +} + diff --git a/src/lock.c b/src/lock.c new file mode 100644 index 0000000..0168ff9 --- /dev/null +++ b/src/lock.c @@ -0,0 +1,154 @@ +/* lock.c - Implement basic file locking for GDBM. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 2008, 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + +#include <errno.h> + +#if HAVE_FLOCK +# ifndef LOCK_SH +# define LOCK_SH 1 +# endif + +# ifndef LOCK_EX +# define LOCK_EX 2 +# endif + +# ifndef LOCK_NB +# define LOCK_NB 4 +# endif + +# ifndef LOCK_UN +# define LOCK_UN 8 +# endif +#endif + +#if defined(F_SETLK) && defined(F_RDLCK) && defined(F_WRLCK) +# define HAVE_FCNTL_LOCK 1 +#else +# define HAVE_FCNTL_LOCK 0 +#endif + +#if 0 +int +gdbm_locked (GDBM_FILE dbf) +{ + return (dbf->lock_type != LOCKING_NONE); +} +#endif + +void +_gdbm_unlock_file (GDBM_FILE dbf) +{ +#if HAVE_FCNTL_LOCK + struct flock fl; +#endif + + switch (dbf->lock_type) + { + case LOCKING_FLOCK: +#if HAVE_FLOCK + flock (dbf->desc, LOCK_UN); +#endif + break; + + case LOCKING_LOCKF: +#if HAVE_LOCKF + lockf (dbf->desc, F_ULOCK, (off_t)0L); +#endif + break; + + case LOCKING_FCNTL: +#if HAVE_FCNTL_LOCK + fl.l_type = F_UNLCK; + fl.l_whence = SEEK_SET; + fl.l_start = fl.l_len = (off_t)0L; + fcntl (dbf->desc, F_SETLK, &fl); +#endif + break; + + case LOCKING_NONE: + break; + } + + dbf->lock_type = LOCKING_NONE; +} + +/* Try each supported locking mechanism. */ +int +_gdbm_lock_file (GDBM_FILE dbf) +{ +#if HAVE_FCNTL_LOCK + struct flock fl; +#endif + int lock_val = -1; + +#if HAVE_FLOCK + if (dbf->read_write == GDBM_READER) + lock_val = flock (dbf->desc, LOCK_SH + LOCK_NB); + else + lock_val = flock (dbf->desc, LOCK_EX + LOCK_NB); + + if ((lock_val == -1) && (errno == EWOULDBLOCK)) + { + dbf->lock_type = LOCKING_NONE; + return lock_val; + } + else if (lock_val != -1) + { + dbf->lock_type = LOCKING_FLOCK; + return lock_val; + } +#endif + +#if HAVE_LOCKF + /* Mask doesn't matter for lockf. */ + lock_val = lockf (dbf->desc, F_LOCK, (off_t)0L); + if ((lock_val == -1) && (errno == EDEADLK)) + { + dbf->lock_type = LOCKING_NONE; + return lock_val; + } + else if (lock_val != -1) + { + dbf->lock_type = LOCKING_LOCKF; + return lock_val; + } +#endif + +#if HAVE_FCNTL_LOCK + /* If we're still here, try fcntl. */ + if (dbf->read_write == GDBM_READER) + fl.l_type = F_RDLCK; + else + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = fl.l_len = (off_t)0L; + lock_val = fcntl (dbf->desc, F_SETLK, &fl); + + if (lock_val != -1) + dbf->lock_type = LOCKING_FCNTL; +#endif + + if (lock_val == -1) + dbf->lock_type = LOCKING_NONE; + return lock_val; +} diff --git a/src/mem.c b/src/mem.c new file mode 100644 index 0000000..2d3b755 --- /dev/null +++ b/src/mem.c @@ -0,0 +1,74 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +# include "autoconf.h" +# include "gdbm.h" +# include "gdbmapp.h" +# include "gdbmdefs.h" + +void +ealloc_die () +{ + error ("%s", strerror (ENOMEM)); + exit (EXIT_FATAL); +} + +void * +emalloc (size_t size) +{ + void *p = malloc (size); + if (!p) + ealloc_die (); + return p; +} + +void * +erealloc (void *ptr, size_t size) +{ + void *newptr = realloc (ptr, size); + if (!newptr) + ealloc_die (); + return newptr; +} + +void * +ecalloc (size_t nmemb, size_t size) +{ + void *p = calloc (nmemb, size); + if (!p) + ealloc_die (); + return p; +} + +void * +ezalloc (size_t size) +{ + return ecalloc (1, size); +} + +char * +estrdup (const char *str) +{ + char *p; + + if (!str) + return NULL; + p = emalloc (strlen (str) + 1); + strcpy (p, str); + return p; +} + + diff --git a/src/mmap.c b/src/mmap.c new file mode 100644 index 0000000..b2f3a85 --- /dev/null +++ b/src/mmap.c @@ -0,0 +1,366 @@ +/* This file is part of GDBM. + Copyright (C) 2007, 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +#include "autoconf.h" + +#if HAVE_MMAP + +# include "gdbmdefs.h" + +# include <sys/types.h> +# include <sys/time.h> +# include <sys/file.h> +# include <sys/stat.h> +# include <sys/mman.h> +# include <stdio.h> + +/* Some systems fail to define this */ +# ifndef MAP_FAILED +# define MAP_FAILED ((void*)-1) +# endif + +/* Translate current offset in the mapped region into the absolute position */ +# define _GDBM_MMAPPED_POS(dbf) ((dbf)->mapped_off + (dbf)->mapped_pos) +/* Return true if the absolute offset OFF lies within the currentlty mmapped + region */ +# define _GDBM_IN_MAPPED_REGION_P(dbf, off) \ + ((off) >= (dbf)->mapped_off \ + && ((off) - (dbf)->mapped_off) < (dbf)->mapped_size) +/* Return true if the current region needs to be remapped */ +# define _GDBM_NEED_REMAP(dbf) \ + (!(dbf)->mapped_region || (dbf)->mapped_pos == (dbf)->mapped_size) +/* Return the sum of the currently mapped size and DELTA */ +# define SUM_FILE_SIZE(dbf, delta) \ + ((dbf)->mapped_off + (dbf)->mapped_size + (delta)) + +/* Store the size of the GDBM file DBF in *PSIZE. + Return 0 on success and -1 on failure. */ +int +_gdbm_file_size (GDBM_FILE dbf, off_t *psize) +{ + struct stat sb; + if (fstat (dbf->desc, &sb)) + return -1; + *psize = sb.st_size; + return 0; +} + +/* Unmap the region. Reset all mapped fields to initial values. */ +void +_gdbm_mapped_unmap (GDBM_FILE dbf) +{ + if (dbf->mapped_region) + { + munmap (dbf->mapped_region, dbf->mapped_size); + dbf->mapped_region = NULL; + dbf->mapped_size = 0; + dbf->mapped_pos = 0; + dbf->mapped_off = 0; + } +} + +/* Remap the DBF file according to dbf->{mapped_off,mapped_pos,mapped_size}. + Take care to recompute {mapped_off,mapped_pos} so that the former lies + on a page size boundary. */ +int +_gdbm_internal_remap (GDBM_FILE dbf, size_t size) +{ + void *p; + int flags = PROT_READ; + size_t page_size = sysconf (_SC_PAGESIZE); + + munmap (dbf->mapped_region, dbf->mapped_size); + dbf->mapped_size = size; + + if (size == 0) + return 0; + + dbf->mapped_pos += dbf->mapped_off % page_size; + dbf->mapped_off = (dbf->mapped_off / page_size) * page_size; + + if (dbf->read_write) + flags |= PROT_WRITE; + + p = mmap (NULL, dbf->mapped_size, flags, MAP_SHARED, + dbf->desc, dbf->mapped_off); + if (p == MAP_FAILED) + { + dbf->mapped_region = NULL; + gdbm_errno = GDBM_MALLOC_ERROR; + return -1; + } + + dbf->mapped_region = p; + return 0; +} + +# define _REMAP_DEFAULT 0 +# define _REMAP_EXTEND 1 +# define _REMAP_END 2 + +/* Remap the GDBM file so that its mapped region ends on SIZEth byte. + If the file is opened with write permissions, FLAG controls how + it is expanded. The value _REMAP_DEFAULT truncates SIZE to the + actual file size. The value _REMAP_EXTEND extends the file, if + necessary, to accomodate max(SIZE,dbf->header->next_block) bytes. + Finally, the value _REMAP_END instructs the function to use + max(SIZE, file_size) as the upper bound of the mapped region. + + If the file is opened read-only, FLAG is ignored and SIZE is + truncated to the actual file size. + + The upper bound obtained that way is used as a *hint* to select + the actual size of the mapped region. which can never exceed + dbf->mapped_size_max. + + The function returns 0 on success, -1 on failure. */ +int +_gdbm_mapped_remap (GDBM_FILE dbf, off_t size, int flag) +{ + off_t file_size, pos; + + if (_gdbm_file_size (dbf, &file_size)) + { + SAVE_ERRNO (_gdbm_mapped_unmap (dbf)); + gdbm_errno = GDBM_FILE_STAT_ERROR; + return -1; + } + + if (flag == _REMAP_END && size < file_size) + size = file_size; + + if (dbf->read_write) + { + if (size > file_size) + { + if (flag != _REMAP_DEFAULT) + { + char c = 0; + + if (size < dbf->header->next_block) + size = dbf->header->next_block; + lseek (dbf->desc, size - 1, SEEK_SET); + write (dbf->desc, &c, 1); + file_size = size; + } + else + { + size = file_size; + return 0; + } + } + } + else + { + if (size > file_size) + size = file_size; + + if (size == SUM_FILE_SIZE (dbf, 0)) + return 0; + } + + pos = _GDBM_MMAPPED_POS (dbf); + if (size > dbf->mapped_size_max) + { + dbf->mapped_off = pos; + dbf->mapped_pos = 0; + size = dbf->mapped_size_max; + if (dbf->mapped_off + size > file_size) + size = file_size - dbf->mapped_off; + } + else + { + dbf->mapped_pos += dbf->mapped_off; + dbf->mapped_off = 0; + } + + return _gdbm_internal_remap (dbf, size); +} + +/* Initialize mapping system. If the file size is less than MAPPED_SIZE_MAX, + map the entire file into the memory. Otherwise, map first MAPPED_SIZE_MAX + bytes. */ +int +_gdbm_mapped_init (GDBM_FILE dbf) +{ + if (dbf->mapped_size_max == 0) + dbf->mapped_size_max = SIZE_T_MAX; + return _gdbm_mapped_remap (dbf, 0, _REMAP_END); +} + +/* Read LEN bytes from the GDBM file DBF into BUFFER. If mmapping is + not initialized or if it fails, fall back to the classical read(1). + Return number of bytes read or -1 on failure. */ +ssize_t +_gdbm_mapped_read (GDBM_FILE dbf, void *buffer, size_t len) +{ + if (dbf->memory_mapping) + { + ssize_t total = 0; + char *cbuf = buffer; + + while (len) + { + size_t nbytes; + + if (_GDBM_NEED_REMAP (dbf)) + { + off_t pos = _GDBM_MMAPPED_POS (dbf); + if (_gdbm_mapped_remap (dbf, SUM_FILE_SIZE (dbf, len), + _REMAP_DEFAULT)) + { + int rc; + + dbf->memory_mapping = FALSE; + if (lseek (dbf->desc, pos, SEEK_SET) != pos) + return total > 0 ? total : -1; + rc = read (dbf->desc, cbuf, len); + if (rc == -1) + return total > 0 ? total : -1; + return total + rc; + } + } + + nbytes = dbf->mapped_size - dbf->mapped_pos; + if (nbytes == 0) + break; + if (nbytes > len) + nbytes = len; + + memcpy (cbuf, (char*) dbf->mapped_region + dbf->mapped_pos, nbytes); + cbuf += nbytes; + dbf->mapped_pos += nbytes; + total += nbytes; + len -= nbytes; + } + return total; + } + return read (dbf->desc, buffer, len); +} + +/* Write LEN bytes from BUFFER to the GDBM file DBF. If mmapping is + not initialized or if it fails, fall back to the classical write(1). + Return number of bytes written or -1 on failure. */ +ssize_t +_gdbm_mapped_write (GDBM_FILE dbf, void *buffer, size_t len) +{ + if (dbf->memory_mapping) + { + ssize_t total = 0; + char *cbuf = buffer; + + while (len) + { + size_t nbytes; + + if (_GDBM_NEED_REMAP (dbf)) + { + off_t pos = _GDBM_MMAPPED_POS (dbf); + if (_gdbm_mapped_remap (dbf, SUM_FILE_SIZE (dbf, len), + _REMAP_EXTEND)) + { + int rc; + + dbf->memory_mapping = FALSE; + if (lseek (dbf->desc, pos, SEEK_SET) != pos) + return total > 0 ? total : -1; + rc = write (dbf->desc, cbuf, len); + if (rc == -1) + return total > 0 ? total : -1; + return total + rc; + } + } + + nbytes = dbf->mapped_size - dbf->mapped_pos; + if (nbytes == 0) + break; + if (nbytes > len) + nbytes = len; + + memcpy ((char*) dbf->mapped_region + dbf->mapped_pos, cbuf, nbytes); + cbuf += nbytes; + dbf->mapped_pos += nbytes; + total += nbytes; + len -= nbytes; + } + return total; + } + return write (dbf->desc, buffer, len); +} + +/* Seek to the offset OFFSET in the GDBM file DBF, according to the + lseek(1)-style directive WHENCE. Return the resulting absolute + offset or -1 in case of failure. If mmapping is not initialized or + if it fails, fall back to the classical lseek(1). + + Return number of bytes written or -1 on failure. */ + +off_t +_gdbm_mapped_lseek (GDBM_FILE dbf, off_t offset, int whence) +{ + if (dbf->memory_mapping) + { + off_t needle; + + switch (whence) + { + case SEEK_SET: + needle = offset; + break; + + case SEEK_CUR: + needle = offset + _GDBM_MMAPPED_POS (dbf); + break; + + case SEEK_END: + { + off_t file_size; + if (_gdbm_file_size (dbf, &file_size)) + { + gdbm_errno = GDBM_FILE_STAT_ERROR; + return -1; + } + needle = file_size - offset; + break; + } + } + + if (!_GDBM_IN_MAPPED_REGION_P (dbf, needle)) + { + _gdbm_mapped_unmap (dbf); + dbf->mapped_off = needle; + dbf->mapped_pos = 0; + } + else + dbf->mapped_pos = needle - dbf->mapped_off; + return needle; + } + return lseek (dbf->desc, offset, whence); +} + +/* Sync the mapped region to disk. */ +int +_gdbm_mapped_sync (GDBM_FILE dbf) +{ + if (dbf->mapped_region) + { + return msync (dbf->mapped_region, dbf->mapped_size, + MS_SYNC | MS_INVALIDATE); + } + return fsync (dbf->desc); +} + +#endif diff --git a/src/parseopt.c b/src/parseopt.c new file mode 100644 index 0000000..cd175c5 --- /dev/null +++ b/src/parseopt.c @@ -0,0 +1,585 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +# include "autoconf.h" +# include "gdbm.h" +# include "gdbmapp.h" +# include "gdbmdefs.h" +# include <stdio.h> +# include <stdarg.h> +# include <errno.h> +# include <string.h> +# include <ctype.h> +# ifdef HAVE_GETOPT_H +# include <getopt.h> +# endif + +static int argc; +static char **argv; + +static struct gdbm_option *option_tab; +static size_t option_count; +static size_t option_max; +static char *short_options; +static size_t short_option_count; +static size_t short_option_max; +#ifdef HAVE_GETOPT_LONG +static struct option *long_options; +static size_t long_option_count; +static size_t long_option_max; +#endif + +#define OPT_USAGE -2 + +struct gdbm_option parseopt_default_options[] = { + { 0, NULL, NULL, "" }, + { 'h', "help", NULL, N_("give this help list") }, + { 'V', "version", NULL, N_("print program version") }, + { OPT_USAGE, "usage", NULL, N_("give a short usage message") }, + { 0 } +}; + +#define OPT_END(opt) \ + ((opt)->opt_short == 0 && (opt)->opt_long == 0 && (opt)->opt_descr == NULL) +#define IS_OPTION(opt) \ + ((opt)->opt_short || (opt)->opt_long) +#define IS_GROUP_HEADER(opt) \ + (!IS_OPTION(opt) && (opt)->opt_descr) +#define IS_VALID_SHORT_OPTION(opt) \ + ((opt)->opt_short > 0 && (opt)->opt_short < 127 && \ + isalnum ((opt)->opt_short)) +#define IS_VALID_LONG_OPTION(opt) \ + ((opt)->opt_long != NULL) + + +static int +optcmp (const void *a, const void *b) +{ + struct gdbm_option const *ap = (struct gdbm_option const *)a; + struct gdbm_option const *bp = (struct gdbm_option const *)b; + + while (ap->opt_flags & PARSEOPT_ALIAS) + ap--; + while (bp->opt_flags & PARSEOPT_ALIAS) + bp--; + + if (IS_VALID_SHORT_OPTION(ap) && IS_VALID_SHORT_OPTION(bp)) + return ap->opt_short - bp->opt_short; + if (IS_VALID_LONG_OPTION(ap) && IS_VALID_LONG_OPTION(bp)) + return strcmp (ap->opt_long, bp->opt_long); + if (IS_VALID_LONG_OPTION(ap)) + return 1; + return -1; +} + +static void +sort_options (int start, int count) +{ + qsort (option_tab + start, count, sizeof (option_tab[0]), optcmp); +} + +static size_t +sort_group (size_t start) +{ + size_t i; + + for (i = start; i < option_count && !IS_GROUP_HEADER (&option_tab[i]); i++) + ; + sort_options (start, i - start); + return i + 1; +} + +static void +sort_all_options (void) +{ + size_t start; + + /* Ensure sane start of options. This is necessary because optcmp backs up + until it finds an element with cleared PARSEOPT_ALIAS flag bit. */ + option_tab[0].opt_flags &= PARSEOPT_ALIAS; + for (start = 0; start < option_count; ) + { + if (IS_GROUP_HEADER (&option_tab[start])) + start = sort_group (start + 1); + else + start = sort_group (start); + } +} + +static void +add_options (struct gdbm_option *options) +{ + size_t optcnt = 0; + size_t argcnt = 0; + size_t count = 0; + struct gdbm_option *opt; + + for (opt = options; !OPT_END(opt); opt++) + { + count++; + if (IS_OPTION(opt)) + { + optcnt++; + if (opt->opt_arg) + argcnt++; + } + } + + if (option_count + count + 1 > option_max) + { + option_max = option_count + count + 1; + option_tab = erealloc (option_tab, + sizeof (option_tab[0]) * option_max); + } + +#ifdef HAVE_GETOPT_LONG + if (long_option_count + optcnt + 1 > long_option_max) + { + long_option_max = long_option_count + optcnt + 1; + long_options = erealloc (long_options, + sizeof (long_options[0]) * long_option_max); + } +#endif + if (short_option_count + optcnt + argcnt + 1 > short_option_max) + { + short_option_max = short_option_count + optcnt + argcnt + 1; + short_options = erealloc (short_options, + sizeof (short_options[0]) * short_option_max); + } + + for (opt = options; !OPT_END(opt); opt++) + { + option_tab[option_count++] = *opt; + if (!IS_OPTION (opt)) + continue; + if (IS_VALID_SHORT_OPTION (opt)) + { + short_options[short_option_count++] = opt->opt_short; + if (opt->opt_arg) + short_options[short_option_count++] = ':'; + } +#ifdef HAVE_GETOPT_LONG + if (IS_VALID_LONG_OPTION (opt)) + { + long_options[long_option_count].name = opt->opt_long; + long_options[long_option_count].has_arg = opt->opt_arg != NULL; + long_options[long_option_count].flag = NULL; + long_options[long_option_count].val = opt->opt_short; + long_option_count++; + } +#endif + } + short_options[short_option_count] = 0; +#ifdef HAVE_GETOPT_LONG + memset (&long_options[long_option_count], 0, + sizeof long_options[long_option_count]); +#endif +} + +int +parseopt_first (int pc, char **pv, struct gdbm_option *opts) +{ + free (option_tab); + free (short_options); + short_option_count = short_option_max = 0; +#ifdef HAVE_GETOPT_LONG + free (long_options); + long_option_count = long_option_max = 0; +#endif + add_options (opts); + add_options (parseopt_default_options); + opterr = 0; + argc = pc; + argv = pv; + return parseopt_next (); +} + +#define LMARGIN 2 +#define DESCRCOLUMN 30 +#define RMARGIN 79 +#define GROUPCOLUMN 2 +#define USAGECOLUMN 13 + +static void +indent (size_t start, size_t col) +{ + for (; start < col; start++) + putchar (' '); +} + +static void +print_option_descr (const char *descr, size_t lmargin, size_t rmargin) +{ + while (*descr) + { + size_t s = 0; + size_t i; + size_t width = rmargin - lmargin; + + for (i = 0; ; i++) + { + if (descr[i] == 0 || descr[i] == ' ' || descr[i] == '\t') + { + if (i > width) + break; + s = i; + if (descr[i] == 0) + break; + } + } + printf ("%*.*s\n", s, s, descr); + descr += s; + if (*descr) + { + indent (0, lmargin); + descr++; + } + } +} + +char *parseopt_program_name; +char *parseopt_program_doc; +char *parseopt_program_args; +const char *program_bug_address = "<" PACKAGE_BUGREPORT ">"; +void (*parseopt_help_hook) (FILE *stream); + +static int argsused; + +size_t +print_option (size_t num) +{ + struct gdbm_option *opt = option_tab + num; + size_t next, i; + int delim; + int w; + + if (IS_GROUP_HEADER (opt)) + { + if (num) + putchar ('\n'); + indent (0, GROUPCOLUMN); + print_option_descr (gettext (opt->opt_descr), + GROUPCOLUMN, RMARGIN); + putchar ('\n'); + return num + 1; + } + + /* count aliases */ + for (next = num + 1; + next < option_count && option_tab[next].opt_flags & PARSEOPT_ALIAS; + next++); + + if (opt->opt_flags & PARSEOPT_HIDDEN) + return next; + + w = 0; + for (i = num; i < next; i++) + { + if (IS_VALID_SHORT_OPTION (&option_tab[i])) + { + if (w == 0) + { + indent (0, LMARGIN); + w = LMARGIN; + } + else + w += printf (", "); + w += printf ("-%c", option_tab[i].opt_short); + delim = ' '; + } + } +#ifdef HAVE_GETOPT_LONG + for (i = num; i < next; i++) + { + if (IS_VALID_LONG_OPTION (&option_tab[i])) + { + if (w == 0) + { + indent (0, LMARGIN); + w = LMARGIN; + } + else + w += printf (", "); + w += printf ("--%s", option_tab[i].opt_long); + delim = '='; + } + } +#else + if (!w) + return next; +#endif + if (opt->opt_arg) + { + argsused = 1; + w += printf ("%c%s", delim, gettext (opt->opt_arg)); + } + if (w >= DESCRCOLUMN) + { + putchar ('\n'); + w = 0; + } + indent (w, DESCRCOLUMN); + print_option_descr (gettext (opt->opt_descr), DESCRCOLUMN, RMARGIN); + + return next; +} + +void +parseopt_print_help (void) +{ + unsigned i; + + argsused = 0; + + printf ("%s %s [%s]... %s\n", _("Usage:"), + parseopt_program_name ? parseopt_program_name : progname, + _("OPTION"), + gettext (parseopt_program_args)); + if (parseopt_program_doc) + print_option_descr (gettext (parseopt_program_doc), 0, RMARGIN); + putchar ('\n'); + + sort_all_options (); + for (i = 0; i < option_count; ) + { + i = print_option (i); + } + putchar ('\n'); +#ifdef HAVE_GETOPT_LONG + if (argsused) + { + print_option_descr (_("Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options."), 0, RMARGIN); + putchar ('\n'); + } +#endif + if (parseopt_help_hook) + parseopt_help_hook (stdout); + + /* TRANSLATORS: The placeholder indicates the bug-reporting address + for this package. Please add _another line_ saying + "Report translation bugs to <...>\n" with the address for translation + bugs (typically your translation team's web or email address). */ + printf (_("Report bugs to %s.\n"), program_bug_address); + +#ifdef PACKAGE_URL + printf (_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL); +#endif +} + +static int +cmpidx_short (const void *a, const void *b) +{ + unsigned const *ai = (unsigned const *)a; + unsigned const *bi = (unsigned const *)b; + + return option_tab[*ai].opt_short - option_tab[*bi].opt_short; +} + +#ifdef HAVE_GETOPT_LONG +static int +cmpidx_long (const void *a, const void *b) +{ + unsigned const *ai = (unsigned const *)a; + unsigned const *bi = (unsigned const *)b; + struct gdbm_option const *ap = option_tab + *ai; + struct gdbm_option const *bp = option_tab + *bi; + return strcmp (ap->opt_long, bp->opt_long); +} +#endif + +void +print_usage (void) +{ + unsigned i; + unsigned n; + char buf[RMARGIN+1]; + unsigned *idxbuf; + unsigned nidx; + +#define FLUSH \ + do \ + { \ + buf[n] = 0; \ + printf ("%s\n", buf); \ + n = USAGECOLUMN; \ + memset (buf, ' ', n); \ + } \ + while (0) +#define ADDC(c) \ + do \ + { \ + if (n == RMARGIN) FLUSH; \ + buf[n++] = c; \ + } \ + while (0) + + idxbuf = ecalloc (option_count, sizeof (idxbuf[0])); + + n = snprintf (buf, sizeof buf, "%s %s ", _("Usage:"), + parseopt_program_name ? parseopt_program_name : progname); + + /* Print a list of short options without arguments. */ + for (i = nidx = 0; i < option_count; i++) + if (IS_VALID_SHORT_OPTION (&option_tab[i]) && !option_tab[i].opt_arg) + idxbuf[nidx++] = i; + + if (nidx) + { + qsort (idxbuf, nidx, sizeof (idxbuf[0]), cmpidx_short); + + ADDC ('['); + ADDC ('-'); + for (i = 0; i < nidx; i++) + { + ADDC (option_tab[idxbuf[i]].opt_short); + } + ADDC (']'); + } + + /* Print a list of short options with arguments. */ + for (i = nidx = 0; i < option_count; i++) + { + if (IS_VALID_SHORT_OPTION (&option_tab[i]) && option_tab[i].opt_arg) + idxbuf[nidx++] = i; + } + + if (nidx) + { + qsort (idxbuf, nidx, sizeof (idxbuf[0]), cmpidx_short); + + for (i = 0; i < nidx; i++) + { + struct gdbm_option *opt = option_tab + idxbuf[i]; + const char *arg = gettext (opt->opt_arg); + size_t len = 5 + strlen (arg) + 1; + + if (n + len > RMARGIN) FLUSH; + buf[n++] = ' '; + buf[n++] = '['; + buf[n++] = '-'; + buf[n++] = opt->opt_short; + buf[n++] = ' '; + strcpy (&buf[n], arg); + n += strlen (arg); + buf[n++] = ']'; + } + } + +#ifdef HAVE_GETOPT_LONG + /* Print a list of long options */ + for (i = nidx = 0; i < option_count; i++) + { + if (IS_VALID_LONG_OPTION (&option_tab[i])) + idxbuf[nidx++] = i; + } + + if (nidx) + { + qsort (idxbuf, nidx, sizeof (idxbuf[0]), cmpidx_long); + + for (i = 0; i < nidx; i++) + { + struct gdbm_option *opt = option_tab + idxbuf[i]; + const char *arg = opt->opt_arg ? gettext (opt->opt_arg) : NULL; + size_t len = 3 + strlen (opt->opt_long) + + (arg ? 1 + strlen (arg) : 0); + if (n + len > RMARGIN) FLUSH; + buf[n++] = ' '; + buf[n++] = '['; + buf[n++] = '-'; + buf[n++] = '-'; + strcpy (&buf[n], opt->opt_long); + n += strlen (opt->opt_long); + if (opt->opt_arg) + { + buf[n++] = '='; + strcpy (&buf[n], arg); + n += strlen (arg); + } + buf[n++] = ']'; + } + } +#endif + FLUSH; + free (idxbuf); +} + +const char version_etc_copyright[] = + /* Do *not* mark this string for translation. First %s is a copyright + symbol suitable for this locale, and second %s are the copyright + years. */ + "Copyright %s %s Free Software Foundation, Inc"; + +const char license_text[] = + "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law."; + +void +print_version_only (void) +{ + printf ("%s (%s) %s\n", + parseopt_program_name ? parseopt_program_name : progname, + PACKAGE_NAME, + PACKAGE_VERSION); + /* TRANSLATORS: Translate "(C)" to the copyright symbol + (C-in-a-circle), if this symbol is available in the user's + locale. Otherwise, do not translate "(C)"; leave it as-is. */ + printf (version_etc_copyright, _("(C)"), "2011"); + puts (license_text); + putchar ('\n'); +} + + +static int +handle_option (int c) +{ + switch (c) + { + case 'h': + parseopt_print_help (); + exit (0); + + case 'V': + print_version_only (); + exit (0); + + case OPT_USAGE: + print_usage (); + exit (0); + + default: + break; + } + return 0; +} + +int +parseopt_next () +{ + int rc; + + do + { +#ifdef HAVE_GETOPT_LONG + rc = getopt_long (argc, argv, short_options, long_options, NULL); +#else + rc = getopt (argc, argv, short_options); +#endif + } + while (handle_option (rc)); + return rc; +} diff --git a/src/progname.c b/src/progname.c new file mode 100644 index 0000000..f008615 --- /dev/null +++ b/src/progname.c @@ -0,0 +1,35 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 2011, 2013 Free Software Foundation, Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +# include "autoconf.h" +# include "gdbm.h" +# include "gdbmapp.h" +# include <string.h> + +const char *progname; + +void +set_progname (const char *arg) +{ + const char *p = strrchr (arg, '/'); + if (p) + ++p; + else + p = arg; + if (strncmp (p, "lt-", 3) == 0) + p += 3; + progname = p; +} diff --git a/src/proto.h b/src/proto.h new file mode 100644 index 0000000..1b7a40a --- /dev/null +++ b/src/proto.h @@ -0,0 +1,77 @@ +/* proto.h - The prototypes for the dbm routines. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + + +/* From bucket.c */ +void _gdbm_new_bucket (GDBM_FILE, hash_bucket *, int); +void _gdbm_get_bucket (GDBM_FILE, int); +int _gdbm_read_bucket_at (GDBM_FILE dbf, off_t off, hash_bucket *bucket, + size_t size); + +void _gdbm_split_bucket (GDBM_FILE, int); +void _gdbm_write_bucket (GDBM_FILE, cache_elem *); + +/* From falloc.c */ +off_t _gdbm_alloc (GDBM_FILE, int); +void _gdbm_free (GDBM_FILE, off_t, int); +int _gdbm_put_av_elem (avail_elem, avail_elem [], int *, int); + +/* From findkey.c */ +char *_gdbm_read_entry (GDBM_FILE, int); +int _gdbm_findkey (GDBM_FILE, datum, char **, int *); + +/* From hash.c */ +int _gdbm_hash (datum); + +/* From update.c */ +void _gdbm_end_update (GDBM_FILE); +void _gdbm_fatal (GDBM_FILE, const char *); + +/* From gdbmopen.c */ +int _gdbm_init_cache (GDBM_FILE, size_t); + +/* From mmap.c */ +int _gdbm_mapped_init (GDBM_FILE); +void _gdbm_mapped_unmap (GDBM_FILE); +ssize_t _gdbm_mapped_read (GDBM_FILE, void *, size_t); +ssize_t _gdbm_mapped_write (GDBM_FILE, void *, size_t); +off_t _gdbm_mapped_lseek (GDBM_FILE, off_t, int); +int _gdbm_mapped_sync (GDBM_FILE); + +/* From lock.c */ +void _gdbm_unlock_file (GDBM_FILE); +int _gdbm_lock_file (GDBM_FILE); + +/* From fullio.c */ +int _gdbm_full_read (GDBM_FILE, void *, size_t); +int _gdbm_full_write (GDBM_FILE, void *, size_t); + +/* From base64.c */ +int _gdbm_base64_encode (const unsigned char *input, size_t input_len, + unsigned char **output, size_t *output_size, + size_t *outbytes); +int _gdbm_base64_decode (const unsigned char *input, size_t input_len, + unsigned char **output, size_t *output_size, + size_t *inbytes, size_t *outbytes); + +int _gdbm_load (FILE *fp, GDBM_FILE *pdbf, unsigned long *line); +int _gdbm_dump (GDBM_FILE dbf, FILE *fp); + + + diff --git a/src/systems.h b/src/systems.h new file mode 100644 index 0000000..3ebfe21 --- /dev/null +++ b/src/systems.h @@ -0,0 +1,81 @@ +/* systems.h - Most of the system dependant code and defines are here. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include all system headers first. */ +#include <sys/types.h> +#include <stdio.h> +#if HAVE_SYS_FILE_H +# include <sys/file.h> +#endif +#include <sys/stat.h> +#include <stdlib.h> +#if HAVE_STRING_H +# include <string.h> +#else +# include <strings.h> +#endif +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#ifndef SEEK_SET +# define SEEK_SET 0 +#endif + +#ifndef O_CLOEXEC +# define O_CLOEXEC 0 +#endif + +/* Default block size. Some systems do not have blocksize in their + stat record. This code uses the BSD blocksize from stat. */ + +#if HAVE_STRUCT_STAT_ST_BLKSIZE +# define STATBLKSIZE file_stat.st_blksize +#else +# define STATBLKSIZE 1024 +#endif + +/* Do we have ftruncate? */ +#if HAVE_FTRUNCATE +# define TRUNCATE(dbf) ftruncate (dbf->desc, 0) +#else +# define TRUNCATE(dbf) close( open (dbf->name, O_RDWR|O_TRUNC, mode)); +#endif + +#ifndef STDERR_FILENO +# define STDERR_FILENO 2 +#endif + +/* I/O macros. */ +#if HAVE_MMAP +# define __read(_dbf, _buf, _size) _gdbm_mapped_read(_dbf, _buf, _size) +# define __write(_dbf, _buf, _size) _gdbm_mapped_write(_dbf, _buf, _size) +# define __lseek(_dbf, _off, _whn) _gdbm_mapped_lseek(_dbf, _off, _whn) +# define __fsync(_dbf) _gdbm_mapped_sync(_dbf) +#else +# define __read(_dbf, _buf, _size) read(_dbf->desc, _buf, _size) +# define __write(_dbf, _buf, _size) write(_dbf->desc, _buf, _size) +# define __lseek(_dbf, _off, _whn) lseek(_dbf->desc, _off, _whn) +# if HAVE_FSYNC +# define __fsync(_dbf) fsync(_dbf->desc) +# else +# define __fsync(_dbf) { sync(); sync(); } +# endif +#endif + diff --git a/src/update.c b/src/update.c new file mode 100644 index 0000000..60e46a9 --- /dev/null +++ b/src/update.c @@ -0,0 +1,114 @@ +/* update.c - The routines for updating the file to a consistent state. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +/* Include system configuration before all else. */ +#include "autoconf.h" + +#include "gdbmdefs.h" + +static void write_header (GDBM_FILE); + +/* This procedure writes the header back to the file described by DBF. */ + +static void +write_header (GDBM_FILE dbf) +{ + off_t file_pos; /* Return value for lseek. */ + int rc; + + file_pos = __lseek (dbf, 0L, SEEK_SET); + if (file_pos != 0) _gdbm_fatal (dbf, _("lseek error")); + rc = _gdbm_full_write (dbf, dbf->header, dbf->header->block_size); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); + + /* Sync the file if fast_write is FALSE. */ + if (dbf->fast_write == FALSE) + __fsync (dbf); +} + + +/* After all changes have been made in memory, we now write them + all to disk. */ +void +_gdbm_end_update (GDBM_FILE dbf) +{ + off_t file_pos; /* Return value for lseek. */ + int rc; + + /* Write the current bucket. */ + if (dbf->bucket_changed && (dbf->cache_entry != NULL)) + { + _gdbm_write_bucket (dbf, dbf->cache_entry); + dbf->bucket_changed = FALSE; + } + + /* Write the other changed buckets if there are any. */ + if (dbf->second_changed) + { + if (dbf->bucket_cache != NULL) + { + int index; + + for (index = 0; index < dbf->cache_size; index++) + { + if (dbf->bucket_cache[index].ca_changed) + _gdbm_write_bucket (dbf, &dbf->bucket_cache[index]); + } + } + dbf->second_changed = FALSE; + } + + /* Write the directory. */ + if (dbf->directory_changed) + { + file_pos = __lseek (dbf, dbf->header->dir, SEEK_SET); + if (file_pos != dbf->header->dir) _gdbm_fatal (dbf, _("lseek error")); + rc = _gdbm_full_write (dbf, dbf->dir, dbf->header->dir_size); + if (rc) + _gdbm_fatal (dbf, gdbm_strerror (rc)); + dbf->directory_changed = FALSE; + if (!dbf->header_changed && dbf->fast_write == FALSE) + __fsync (dbf); + } + + /* Final write of the header. */ + if (dbf->header_changed) + { + write_header (dbf); + dbf->header_changed = FALSE; + } +} + + +/* If a fatal error is detected, come here and exit. VAL tells which fatal + error occured. */ + +void +_gdbm_fatal (GDBM_FILE dbf, const char *val) +{ + if ((dbf != NULL) && (dbf->fatal_err != NULL)) + (*dbf->fatal_err) (val); + else + { + fprintf (stderr, _("gdbm fatal: %s\n"), val ? val : ""); + } + exit (1); + /* NOTREACHED */ +} diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..d46325b --- /dev/null +++ b/src/util.c @@ -0,0 +1,127 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +#include "gdbmtool.h" +#include <pwd.h> + +char * +mkfilename (const char *dir, const char *file, const char *suf) +{ + char *tmp; + size_t dirlen = strlen (dir); + size_t suflen = suf ? strlen (suf) : 0; + size_t fillen = strlen (file); + size_t len; + + while (dirlen > 0 && dir[dirlen-1] == '/') + dirlen--; + + len = dirlen + (dir[0] ? 1 : 0) + fillen + suflen; + tmp = emalloc (len + 1); + memcpy (tmp, dir, dirlen); + if (dir[0]) + tmp[dirlen++] = '/'; + memcpy (tmp + dirlen, file, fillen); + if (suf) + memcpy (tmp + dirlen + fillen, suf, suflen); + tmp[len] = 0; + return tmp; +} + +char * +tildexpand (char *s) +{ + if (s[0] == '~') + { + char *p = s + 1; + size_t len = strcspn (p, "/"); + struct passwd *pw; + + if (len == 0) + pw = getpwuid (getuid ()); + else + { + char *user = emalloc (len + 1); + + memcpy (user, p, len); + user[len] = 0; + pw = getpwnam (user); + free (user); + } + if (pw) + return mkfilename (pw->pw_dir, p + len + 1, NULL); + } + return estrdup (s); +} + +int +vgetyn (const char *prompt, va_list ap) +{ + int state = 0; + int c, resp; + + do + { + switch (state) + { + case 1: + if (c == ' ' || c == '\t') + continue; + resp = c; + state = 2; + /* fall through */ + case 2: + if (c == '\n') + { + switch (resp) + { + case 'y': + case 'Y': + return 1; + case 'n': + case 'N': + return 0; + default: + fprintf (stdout, "%s\n", _("Please, reply 'y' or 'n'")); + } + state = 0; + } else + break; + + case 0: + vfprintf (stdout, prompt, ap); + fprintf (stdout, " [y/n]?"); + fflush (stdout); + state = 1; + break; + } + } while ((c = getchar ()) != EOF); + exit (EXIT_USAGE); +} + +int +getyn (const char *prompt, ...) +{ + va_list ap; + int rc; + + va_start (ap, prompt); + rc = vgetyn (prompt, ap); + va_end (ap); + return rc; +} + diff --git a/src/var.c b/src/var.c new file mode 100644 index 0000000..fb6ecf0 --- /dev/null +++ b/src/var.c @@ -0,0 +1,386 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +#include "gdbmtool.h" + +#define VARF_DFL 0x00 /* Default flags -- everything disabled */ +#define VARF_SET 0x01 /* Variable is set */ +#define VARF_INIT 0x02 /* Variable is initialized */ +#define VARF_PROT 0x04 /* Variable is protected, i.e. cannot be unset */ +#define VARF_OCTAL 0x08 /* For integer variables -- use octal base */ + +#define VAR_IS_SET(v) ((v)->flags & (VARF_SET|VARF_INIT)) + +union value +{ + char *string; + int bool; + int num; +}; + +struct variable +{ + char *name; + int type; + int flags; + union value v; + int (*hook) (struct variable *, union value *); +}; + +static int open_hook (struct variable *, union value *); + +static struct variable vartab[] = { + /* Top-level prompt */ + { "ps1", VART_STRING, VARF_INIT, { "%p>%_" } }, + /* Second-level prompt (used within "def" block) */ + { "ps2", VART_STRING, VARF_INIT, { "%_>%_" } }, + /* This delimits array members */ + { "delim1", VART_STRING, VARF_INIT|VARF_PROT, { "," } }, + /* This delimits structure members */ + { "delim2", VART_STRING, VARF_INIT|VARF_PROT, { "," } }, + { "confirm", VART_BOOL, VARF_INIT, { num: 1 } }, + { "cachesize", VART_INT, VARF_DFL }, + { "blocksize", VART_INT, VARF_DFL }, + { "open", VART_STRING, VARF_DFL, { NULL }, open_hook }, + { "lock", VART_BOOL, VARF_INIT, { num: 1 } }, + { "mmap", VART_BOOL, VARF_INIT, { num: 1 } }, + { "sync", VART_BOOL, VARF_INIT, { num: 0 } }, + { "filemode", VART_INT, VARF_INIT|VARF_OCTAL|VARF_PROT, { num: 0644 } }, + { "pager", VART_STRING, VARF_DFL }, + { "quiet", VART_BOOL, VARF_DFL }, + { NULL } +}; + +static int +open_hook (struct variable *var, union value *v) +{ + static struct { + char *s; + int t; + } trans[] = { + { "newdb", GDBM_NEWDB }, + { "wrcreat", GDBM_WRCREAT }, + { "rw", GDBM_WRCREAT }, + { "reader", GDBM_READER }, + { "readonly", GDBM_READER }, + { NULL } + }; + int i; + + if (!v) + return VAR_ERR_BADVALUE; + + for (i = 0; trans[i].s; i++) + if (strcmp (trans[i].s, v->string) == 0) + { + open_mode = trans[i].t; + return VAR_OK; + } + + return VAR_ERR_BADVALUE; +} + +static struct variable * +varfind (const char *name) +{ + struct variable *vp; + + for (vp = vartab; vp->name; vp++) + if (strcmp (vp->name, name) == 0) + return vp; + + return NULL; +} + +typedef int (*setvar_t) (union value *, void *, int); + +static int +s2s (union value *vp, void *val, int flags) +{ + vp->string = estrdup (val); + return VAR_OK; +} + +static int +b2s (union value *vp, void *val, int flags) +{ + vp->string = estrdup (*(int*)val ? "true" : "false"); + return VAR_OK; +} + +static int +i2s (union value *vp, void *val, int flags) +{ + char buf[128]; + snprintf (buf, sizeof buf, "%d", *(int*)val); + vp->string = estrdup (buf); + return VAR_OK; +} + +static int +s2b (union value *vp, void *val, int flags) +{ + static char *trueval[] = { "on", "true", "yes", NULL }; + static char *falseval[] = { "off", "false", "no", NULL }; + int i; + unsigned long n; + char *p; + + for (i = 0; trueval[i]; i++) + if (strcasecmp (trueval[i], val) == 0) + { + vp->bool = 1; + return VAR_OK; + } + + for (i = 0; falseval[i]; i++) + if (strcasecmp (falseval[i], val) == 0) + { + vp->bool = 0; + return VAR_OK; + } + + n = strtoul (val, &p, 0); + if (*p) + return VAR_ERR_BADTYPE; + vp->bool = !!n; + return VAR_OK; +} + +static int +s2i (union value *vp, void *val, int flags) +{ + char *p; + int n = strtoul (val, &p, (flags & VARF_OCTAL) ? 8 : 10); + + if (*p) + return VAR_ERR_BADTYPE; + + vp->num = n; + return VAR_OK; +} + +static int +b2b (union value *vp, void *val, int flags) +{ + vp->bool = !!*(int*)val; + return VAR_OK; +} + +static int +b2i (union value *vp, void *val, int flags) +{ + vp->num = *(int*)val; + return VAR_OK; +} + +static int +i2i (union value *vp, void *val, int flags) +{ + vp->num = *(int*)val; + return VAR_OK; +} + +static int +i2b (union value *vp, void *val, int flags) +{ + vp->bool = *(int*)val; + return VAR_OK; +} + +static setvar_t setvar[3][3] = { + /* s b i */ + /* s */ { s2s, b2s, i2s }, + /* b */ { s2b, b2b, i2b }, + /* i */ { s2i, b2i, i2i } +}; + +int +variable_set (const char *name, int type, void *val) +{ + struct variable *vp = varfind (name); + int rc; + union value v, *valp; + + if (!vp) + return VAR_ERR_NOTDEF; + + if (val) + { + memset (&v, 0, sizeof (v)); + rc = setvar[vp->type][type] (&v, val, vp->flags); + if (rc) + return rc; + valp = &v; + } + else + { + if (vp->flags & VARF_PROT) + return VAR_ERR_BADVALUE; + valp = NULL; + } + + if (vp->hook && (rc = vp->hook (vp, valp)) != VAR_OK) + return rc; + + if (vp->type == VART_STRING && (vp->flags & VARF_SET)) + free (vp->v.string); + + if (!val) + { + vp->flags &= (VARF_INIT|VARF_SET); + } + else + { + vp->v = v; + vp->flags &= ~VARF_INIT; + vp->flags |= VARF_SET; + } + + return VAR_OK; +} + +int +variable_unset (const char *name) +{ + struct variable *vp = varfind (name); + int rc; + + if (!vp) + return VAR_ERR_NOTDEF; + if (vp->flags & VARF_PROT) + return VAR_ERR_BADVALUE; + + if (vp->hook && (rc = vp->hook (vp, NULL)) != VAR_OK) + return rc; + + vp->flags &= ~(VARF_INIT|VARF_SET); + + return VAR_OK; +} + +int +variable_get (const char *name, int type, void **val) +{ + struct variable *vp = varfind (name); + + if (!vp) + return VAR_ERR_NOTDEF; + + if (type != vp->type) + return VAR_ERR_BADTYPE; + + if (!VAR_IS_SET (vp)) + return VAR_ERR_NOTSET; + + switch (vp->type) + { + case VART_STRING: + *val = vp->v.string; + break; + + case VART_BOOL: + *(int*)val = vp->v.bool; + break; + + case VART_INT: + *(int*)val = vp->v.num; + break; + } + + return VAR_OK; +} + +static int +varcmp (const void *a, const void *b) +{ + return strcmp (((struct variable const *)a)->name, + ((struct variable const *)b)->name); +} + +void +variable_print_all (FILE *fp) +{ + struct variable *vp; + char *s; + static int sorted; + + if (!sorted) + { + qsort (vartab, sizeof (vartab) / sizeof (vartab[0]) - 1, + sizeof (vartab[0]), varcmp); + sorted = 1; + } + + for (vp = vartab; vp->name; vp++) + { + if (!VAR_IS_SET (vp)) + { + fprintf (fp, "# %s is unset", vp->name); + } + else + { + switch (vp->type) + { + case VART_INT: + fprintf (fp, (vp->flags & VARF_OCTAL) ? "%s=%03o" : "%s=%d", + vp->name, vp->v.num); + break; + + case VART_BOOL: + fprintf (fp, "%s%s", vp->v.bool ? "" : "no", vp->name); + break; + + case VART_STRING: + fprintf (fp, "%s=\"", vp->name); + for (s = vp->v.string; *s; s++) + { + int c; + + if (isprint (*s)) + fputc (*s, fp); + else if ((c = escape (*s))) + fprintf (fp, "\\%c", c); + else + fprintf (fp, "\\%03o", *s); + } + fprintf (fp, "\""); + } + } + fputc ('\n', fp); + } +} + +int +variable_is_set (const char *name) +{ + struct variable *vp = varfind (name); + + if (!vp) + return 0; + return VAR_IS_SET (vp); +} + +int +variable_is_true (const char *name) +{ + int n; + + if (variable_get (name, VART_BOOL, (void **) &n) == VAR_OK) + return n; + return 0; +} diff --git a/src/version.c b/src/version.c new file mode 100644 index 0000000..f301a75 --- /dev/null +++ b/src/version.c @@ -0,0 +1,57 @@ +/* version.c - This is file contains the version number for gdbm source. */ + +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 1990, 1991, 1993, 2007, 2011, 2013 Free Software Foundation, + Inc. + + GDBM is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */ + +#include "autoconf.h" +#include "gdbm.h" + +/* Keep a string with the version number in it. + The DIST_DATE magic below is replaced by the actual date when + making the distdir. */ +const char * gdbm_version = "GDBM version " PACKAGE_VERSION ". " +"25/12/2013" +#if defined(__STDC__) && defined(__DATE__) && defined(__TIME__) + " (built " __DATE__ " " __TIME__ ")" +#endif +; +int const gdbm_version_number[3] = { + GDBM_VERSION_MAJOR, + GDBM_VERSION_MINOR, + GDBM_VERSION_PATCH +}; + +int +gdbm_version_cmp (int const a[], int const b[]) +{ + if (a[0] > b[0]) + return 1; + else if (a[0] < b[0]) + return -1; + + if (a[1] > b[1]) + return 1; + else if (a[1] < b[1]) + return -1; + + if (a[2] > b[2]) + return 1; + else if (a[2] < b[2]) + return -1; + + return 0; +} |