From fd714cdcec660f915917edd239324e7544daa234 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Sun, 13 Nov 2011 15:28:04 +0000 Subject: Implement new dump format. Add new utilities: gdbm_dump and gdbm_load. * configure.ac (AC_CHECK_HEADERS): Don't check for files that must always be present. Check for getopt.h. (AC_CHECK_FUNCS): Add getopt_long * src/systems.h: Include useless #if's. * src/flatfile.c: Split into two files: * src/gdbmexp.c: ... this and ... * src/gdbmimp.c: .., this * src/mem.c: New file. * src/base64.c: New file. * src/gdbm_dump.c: New file. * src/gdbm_load.c: New file. * src/gdbmapp.h: New file. * src/gdbmdump.c: New file. * src/gdbmload.c: New file. * src/parseopt.c: New file. * src/progname.c: New file. * src/.cvsignore: Update. * src/Makefile.am (libgdbm_la_SOURCES): Add new files. (noinst_LIBRARIES): New variable. Build libgdbmapp.a. (libgdbmapp_a_SOURCES): New variable. (bin_PROGRAMS): Add gdbm_load and gdbm_dump (testgdbm_LDADD, gdbm_load_LDADD) (gdbm_dump_LDADD): Add ./libgdbmapp.a * src/gdbm.h.in: Include (gdbm_export_to_file) (gdbm_import_from_file): New prototypes. (GDBM_DUMP_FMT_BINARY,GDBM_DUMP_FMT_ASCII): New constants. (gdbm_dump,gdbm_load) (gdbm_dump_to_file,gdbm_load_from_file): New prototypes. (GDBM_NO_DBNAME): New error code. (_GDBM_MAX_ERRNO): Update. * src/gdbmdefs.h (_GDBM_MAX_DUMP_LINE_LEN): New constant. * src/gdbmerrno.c (gdbm_errlist): Add entry for GDBM_NO_DBNAME. * src/proto.h (_gdbm_base64_encode,_gdbm_base64_decode) (_gdbm_load,_gdbm_dump): New prototypes. * src/testgdbm.c: Use gdbmapp interface for option parsing. * export/export.c: Include gdbmexp.c * export/.cvsignore: Update. * doc/gdbm.texinfo: Update. --- ChangeLog | 49 +++++ configure.ac | 4 +- doc/gdbm.texinfo | 43 +++- export/.cvsignore | 2 + export/export.c | 5 +- src/.cvsignore | 2 + src/Makefile.am | 35 +++- src/base64.c | 127 ++++++++++++ src/flatfile.c | 246 ----------------------- src/gdbm.h.in | 23 ++- src/gdbm_dump.c | 124 ++++++++++++ src/gdbm_load.c | 140 +++++++++++++ src/gdbmapp.h | 52 +++++ src/gdbmdefs.h | 2 + src/gdbmdump.c | 193 ++++++++++++++++++ src/gdbmerrno.c | 1 + src/gdbmexp.c | 125 ++++++++++++ src/gdbmimp.c | 157 +++++++++++++++ src/gdbmload.c | 502 ++++++++++++++++++++++++++++++++++++++++++++++ src/mem.c | 74 +++++++ src/parseopt.c | 585 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/progname.c | 35 ++++ src/proto.h | 12 ++ src/systems.h | 16 +- src/testgdbm.c | 142 +++++-------- 25 files changed, 2322 insertions(+), 374 deletions(-) create mode 100644 src/base64.c delete mode 100644 src/flatfile.c create mode 100644 src/gdbm_dump.c create mode 100644 src/gdbm_load.c create mode 100644 src/gdbmapp.h create mode 100644 src/gdbmdump.c create mode 100644 src/gdbmexp.c create mode 100644 src/gdbmimp.c create mode 100644 src/gdbmload.c create mode 100644 src/mem.c create mode 100644 src/parseopt.c create mode 100644 src/progname.c diff --git a/ChangeLog b/ChangeLog index f90b8a4..8072ea2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,52 @@ +2011-11-13 Sergey Poznyakoff + + Implement new dump format. Add new utilities: gdbm_dump and gdbm_load. + + * configure.ac (AC_CHECK_HEADERS): Don't check for files that must + always be present. Check for getopt.h. + (AC_CHECK_FUNCS): Add getopt_long + * src/systems.h: Include useless #if's. + + * src/flatfile.c: Split into two files: + * src/gdbmexp.c: ... this and ... + * src/gdbmimp.c: .., this + + * src/mem.c: New file. + * src/base64.c: New file. + * src/gdbm_dump.c: New file. + * src/gdbm_load.c: New file. + * src/gdbmapp.h: New file. + * src/gdbmdump.c: New file. + * src/gdbmload.c: New file. + * src/parseopt.c: New file. + * src/progname.c: New file. + * src/.cvsignore: Update. + * src/Makefile.am (libgdbm_la_SOURCES): Add new files. + (noinst_LIBRARIES): New variable. Build libgdbmapp.a. + (libgdbmapp_a_SOURCES): New variable. + (bin_PROGRAMS): Add gdbm_load and gdbm_dump + (testgdbm_LDADD, gdbm_load_LDADD) + (gdbm_dump_LDADD): Add ./libgdbmapp.a + + * src/gdbm.h.in: Include + (gdbm_export_to_file) + (gdbm_import_from_file): New prototypes. + (GDBM_DUMP_FMT_BINARY,GDBM_DUMP_FMT_ASCII): New constants. + (gdbm_dump,gdbm_load) + (gdbm_dump_to_file,gdbm_load_from_file): New prototypes. + (GDBM_NO_DBNAME): New error code. + (_GDBM_MAX_ERRNO): Update. + * src/gdbmdefs.h (_GDBM_MAX_DUMP_LINE_LEN): New constant. + * src/gdbmerrno.c (gdbm_errlist): Add entry for GDBM_NO_DBNAME. + * src/proto.h (_gdbm_base64_encode,_gdbm_base64_decode) + (_gdbm_load,_gdbm_dump): New prototypes. + + * src/testgdbm.c: Use gdbmapp interface for option parsing. + + * export/export.c: Include gdbmexp.c + * export/.cvsignore: Update. + * doc/gdbm.texinfo: Update. + 2011-11-13 Sergey Poznyakoff Version 1.10 diff --git a/configure.ac b/configure.ac index 421e230..fec5d0d 100644 --- a/configure.ac +++ b/configure.ac @@ -91,11 +91,11 @@ dnl Internationalization macros. AM_GNU_GETTEXT([external], [need-ngettext]) AM_GNU_GETTEXT_VERSION(0.18) -AC_CHECK_HEADERS([stdlib.h string.h sys/file.h unistd.h fcntl.h sys/types.h memory.h sys/termios.h locale.h]) +AC_CHECK_HEADERS([sys/file.h sys/termios.h string.h locale.h getopt.h]) AC_CHECK_LIB(dbm, main) AC_CHECK_LIB(ndbm, main) -AC_CHECK_FUNCS([rename ftruncate flock lockf fsync setlocale]) +AC_CHECK_FUNCS([rename ftruncate flock lockf fsync setlocale getopt_long]) if test x$mapped_io = xyes then diff --git a/doc/gdbm.texinfo b/doc/gdbm.texinfo index efd8cb5..ff93a1a 100644 --- a/doc/gdbm.texinfo +++ b/doc/gdbm.texinfo @@ -951,27 +951,34 @@ existing @acronym{GDBM} database or to create a new one. @cindex default database, @command{testgdbm} @cindex @option{-g}, @command{testgdbm} option +@cindex @option{-f}, @command{testgdbm} option +@cindex @option{--file}, @command{testgdbm} option @flindex junk.gdbm When invoked without options, it tries to open a database file called @file{junk.gdbm}, located in the current working directory. You can -change this default using the @option{-g} command line option. This +change this default using the @option{--file} (@option{-f}) command +line option@footnote{For compatibility with earlier versions, the +@option{-g} option acts as an alias to @option{-f}}. This option takes a single argument, specifying the file name to open, e.g.: @example -$ testgdbm -g file.db +$ testgdbm -f file.db @end example @cindex read-only mode, @command{testgdbm} @cindex @option{-r}, @command{testgdbm} option -The database will be opened in read-write mode, unless the @option{-r} -option is specified, in which case it will be opened only for reading. +@cindex @option{--read-only}, @command{testgdbm} option +The database will be opened in read-write mode, unless the +@option{-r} (@option{--read-only}) option is specified, in which case +it will be opened only for reading. @cindex creating a database, @command{testgdbm} @cindex @option{-n}, @command{testgdbm} option +@cindex @option{--newdb}, @command{testgdbm} option If the database does not exist, @command{testgdbm} will create it. -There is a special option @option{-n}, which instructs the utility to -create a new database. If it is used and if the database already -exists, it will be deleted, so use it sparingly. +There is a special option @option{-n} (@option{--newdb}, which +instructs the utility to create a new database. If it is used and if +the database already exists, it will be deleted, so use it sparingly. @menu * invocation:: @@ -987,21 +994,39 @@ options: @table @option @item -b @var{size} +@itemx --block-size=@var{size} Set block size. @item -c @var{size} +@itemx --cache-size=@var{size} Set cache size. -@item -g @var{file} +@item -f @var{file} +@itemx -g @var{file} +@itemx --file=@var{file} Operate on @var{file} instead of the default @file{junk.gdbm}. @item -h +@itemx --help Print a concise help summary. @item -n +@itemx --newdb Create the database. +@item -l +@itemx --no-lock +Disable file locking. +@item -m +@itemx --no-mmap +Disable mmap. @item -r +@itemx --read-only Open the database in read-only mode. @item -s +@itemx --synchronize Synchronize to the disk after each write. -@item -v +@item -V +@itemx --version Print program version and licensing information and exit. +@item --usage +Print a terse invocation syntax summary along with a list of available +command line options. @end table @node shell diff --git a/export/.cvsignore b/export/.cvsignore index e995588..62e7a85 100644 --- a/export/.cvsignore +++ b/export/.cvsignore @@ -1,3 +1,5 @@ .deps +.libs Makefile Makefile.in +gdbmexport diff --git a/export/export.c b/export/export.c index d2a56a1..2ef4e6c 100644 --- a/export/export.c +++ b/export/export.c @@ -23,9 +23,8 @@ #include -/* Include flatfile.c to pull in gdbm_export() */ -#define _GDBMEXPORT_ -#include "flatfile.c" +/* Pull in gdbm_export() */ +#include "gdbmexp.c" void usage (char *s) diff --git a/src/.cvsignore b/src/.cvsignore index ee2dbd5..46838d7 100644 --- a/src/.cvsignore +++ b/src/.cvsignore @@ -7,3 +7,5 @@ gdbm.h *.lo libgdbm.la testgdbm +gdbm_dump +gdbm_load diff --git a/src/Makefile.am b/src/Makefile.am index 1c55a4e..2d7a81a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -37,22 +37,26 @@ lib_LTLIBRARIES = libgdbm.la libgdbm_la_LIBADD = @LTLIBINTL@ libgdbm_la_SOURCES = \ - gdbmopen.c\ - gdbmdelete.c\ - gdbmfetch.c\ - gdbmstore.c\ gdbmclose.c\ - gdbmreorg.c\ - gdbmseq.c\ - gdbmsync.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\ - flatfile.c\ fullio.c\ hash.c\ lock.c\ @@ -62,6 +66,17 @@ libgdbm_la_SOURCES = \ 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 = testgdbm -testgdbm_LDADD = ./libgdbm.la +bin_PROGRAMS = testgdbm gdbm_load gdbm_dump +testgdbm_LDADD = ./libgdbmapp.a ./libgdbm.la +gdbm_load_LDADD = ./libgdbmapp.a ./libgdbm.la +gdbm_dump_LDADD = ./libgdbmapp.a ./libgdbm.la diff --git a/src/base64.c b/src/base64.c new file mode 100644 index 0000000..bf2bf6a --- /dev/null +++ b/src/base64.c @@ -0,0 +1,127 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 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 . */ + +# 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/flatfile.c b/src/flatfile.c deleted file mode 100644 index c0f9422..0000000 --- a/src/flatfile.c +++ /dev/null @@ -1,246 +0,0 @@ -/* flatfile.c - Import/export a GDBM database. */ - -/* This file is part of GDBM, the GNU data base manager. - 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 . */ - -#ifndef _GDBMEXPORT_ - -/* Include system configuration before all else. */ -# include "autoconf.h" -# include - -# include "gdbmdefs.h" -# include "gdbm.h" - -#endif - -int -gdbm_export (GDBM_FILE dbf, const char *exportfile, int flags, int mode) -{ - int nfd, 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; - - /* 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; - } - - /* Write out the text header. */ - if (write (nfd, header1, strlen (header1)) != strlen (header1)) - goto write_fail; - if (write (nfd, gdbm_version, strlen (gdbm_version)) != strlen (gdbm_version)) - goto write_fail; - if (write (nfd, header2, strlen (header2)) != strlen (header2)) - 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 (write (nfd, &size, sizeof(int)) != sizeof (int)) - goto write_fail; - if (write (nfd, key.dptr, key.dsize) != key.dsize) - goto write_fail; - - size = htonl (data.dsize); - if (write (nfd, &size, sizeof(int)) != sizeof (int)) - goto write_fail; - if (write (nfd, data.dptr, data.dsize) != data.dsize) - goto write_fail; - } - nextkey = gdbm_nextkey (dbf, key); - free (key.dptr); - free (data.dptr); - key = nextkey; - - count++; - } - close (nfd); - - return count; - - write_fail: - - gdbm_errno = GDBM_FILE_WRITE_ERROR; - return -1; -} - -#ifndef _GDBMEXPORT_ - -int -gdbm_import (GDBM_FILE dbf, const char *importfile, int flag) -{ - int ifd, seenbang, seennewline, rsize, size, kbufsize, dbufsize, rret; - char c, *kbuffer, *dbuffer; - datum key, data; - int count = 0; - - ifd = open (importfile, O_RDONLY, 0); - if (ifd == -1) - { - gdbm_errno = GDBM_FILE_OPEN_ERROR; - return -1; - } - - seenbang = 0; - seennewline = 0; - kbuffer = NULL; - dbuffer = NULL; - - /* Read (and discard) four lines begining with ! and ending with \n. */ - while (1) - { - if (read (ifd, &c, 1) != 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; - close (ifd); - return -1; - } - dbufsize = 512; - dbuffer = malloc (dbufsize); - if (dbuffer == NULL) - { - gdbm_errno = GDBM_MALLOC_ERROR; - close (ifd); - return -1; - } - - /* Insert/replace records in the database until we run out of file. */ - while ((rret = read (ifd, &rsize, sizeof(rsize))) != 0) - { - if (rret != sizeof(rsize)) - goto read_fail; - - /* Read the key. */ - size = ntohl (rsize); - if (size > kbufsize) - { - kbufsize = (size + 512); - kbuffer = realloc (kbuffer, kbufsize); - if (kbuffer == NULL) - { - gdbm_errno = GDBM_MALLOC_ERROR; - close (ifd); - return -1; - } - } - if (read (ifd, kbuffer, size) != size) - goto read_fail; - - key.dptr = kbuffer; - key.dsize = size; - - /* Read the data. */ - if (read (ifd, &rsize, sizeof(rsize)) != sizeof(rsize)) - goto read_fail; - - size = ntohl (rsize); - if (size > dbufsize) - { - dbufsize = (size + 512); - dbuffer = realloc (dbuffer, dbufsize); - if (dbuffer == NULL) - { - gdbm_errno = GDBM_MALLOC_ERROR; - close (ifd); - return -1; - } - } - if (read (ifd, dbuffer, size) != size) - 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); - close (ifd); - return -1; - } - - count++; - } - - close (ifd); - return count; - -read_fail: - - if (kbuffer != NULL) - free (kbuffer); - if (dbuffer != NULL) - free (dbuffer); - - close (ifd); - - gdbm_errno = GDBM_FILE_READ_ERROR; - return -1; -} -#endif diff --git a/src/gdbm.h.in b/src/gdbm.h.in index 21168de..81e9943 100644 --- a/src/gdbm.h.in +++ b/src/gdbm.h.in @@ -29,6 +29,8 @@ #ifndef _GDBM_H_ # define _GDBM_H_ +# include + /* GDBM C++ support */ # if defined(__cplusplus) || defined(c_plusplus) extern "C" { @@ -111,8 +113,24 @@ 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); + +#define GDBM_DUMP_FMT_BINARY 0 +#define GDBM_DUMP_FMT_ASCII 1 + +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, + unsigned long *line); +extern int gdbm_load_from_file (GDBM_FILE *, FILE *, int replace, + unsigned long *line); # define GDBM_NO_ERROR 0 # define GDBM_MALLOC_ERROR 1 @@ -140,9 +158,10 @@ extern int gdbm_import (GDBM_FILE, const char *, int); # define GDBM_BAD_OPEN_FLAGS 23 # define GDBM_FILE_STAT_ERROR 24 # define GDBM_FILE_EOF 25 - +# define GDBM_NO_DBNAME 26 + # define _GDBM_MIN_ERRNO 0 -# define _GDBM_MAX_ERRNO GDBM_FILE_EOF +# define _GDBM_MAX_ERRNO GDBM_NO_DBNAME typedef int gdbm_error; /* For compatibilities sake. */ extern gdbm_error gdbm_errno; extern const char * const gdbm_errlist[]; diff --git a/src/gdbm_dump.c b/src/gdbm_dump.c new file mode 100644 index 0000000..6ea94e6 --- /dev/null +++ b/src/gdbm_dump.c @@ -0,0 +1,124 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 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 . */ + +# 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", N_("0|1"), N_("select dump format") }, + { 'b', "binary", NULL, N_("use binary output 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; + + set_progname (argv[0]); + + for (opt = parseopt_first (argc, argv, optab); + opt != EOF; + opt = parseopt_next ()) + { + switch (opt) + { + case 'b': + format = GDBM_DUMP_FMT_BINARY; + break; + + case 'H': + format = atoi (optarg); + switch (format) + { + case GDBM_DUMP_FMT_BINARY: + case GDBM_DUMP_FMT_ASCII: + break; + default: + error (_("unknown dump format")); + exit (2); + } + break; + + default: + error (_("unknown option")); + exit (2); + } + } + + argc -= optind; + argv += optind; + + if (argc == 0) + { + parseopt_print_help (); + exit (0); + } + + if (argc > 2) + { + error (_("too many arguments; try `%s -h' for more info"), progname); + exit (2); + } + + dbname = argv[0]; + if (argc == 2) + filename = argv[1]; + else + filename = NULL; + + if (!filename || strcmp (filename, "-") == 0) + { + filename = ""; + fp = stdout; + } + else + { + fp = fopen (filename, "w"); + if (!fp) + { + sys_perror (errno, _("cannot open %s"), filename); + exit (1); + } + } + + dbf = gdbm_open (dbname, 0, GDBM_READER, 0600, NULL); + if (!dbf) + { + gdbm_perror (_("gdbm_open failed")); + exit (1); + } + + rc = gdbm_dump_to_file (dbf, fp, format); + if (rc) + { + gdbm_perror (_("dump error"), filename); + } + + gdbm_close (dbf); + + exit (!!rc); +} + diff --git a/src/gdbm_load.c b/src/gdbm_load.c new file mode 100644 index 0000000..4e14207 --- /dev/null +++ b/src/gdbm_load.c @@ -0,0 +1,140 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 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 . */ + +# include "autoconf.h" +# include "gdbm.h" +# include "gdbmapp.h" +# include "gdbmdefs.h" + +/* usage: gdbm_load [OPTIONS] FILE [DB_FILE] + FILE is either "-" or a file name. + OPTIONS are: + -h, --help + -u, --usage + -v, --version + + -p, --permissions OOO + -u, --user NAME-OR-UID + -g, --group NAME-OR-GID +*/ + +int replace = 0; + +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") }, + { 0 } +}; + +int +main (int argc, char **argv) +{ + GDBM_FILE dbf = NULL; + int rc, opt; + char *dbname, *filename; + FILE *fp; + unsigned long err_line; + + set_progname (argv[0]); + + for (opt = parseopt_first (argc, argv, optab); + opt != EOF; + opt = parseopt_next ()) + { + switch (opt) + { + case 'r': + replace = 1; + break; + + default: + error (_("unknown option")); + exit (2); + } + } + + argc -= optind; + argv += optind; + + if (argc == 0) + { + parseopt_print_help (); + exit (0); + } + + if (argc > 2) + { + error (_("too many arguments; try `%s -h' for more info"), progname); + exit (2); + } + + filename = argv[0]; + if (argc == 2) + dbname = argv[1]; + else + dbname = NULL; + + if (strcmp (filename, "-") == 0) + { + filename = ""; + fp = stdin; + } + else + { + fp = fopen (filename, "r"); + if (!fp) + { + sys_perror (errno, _("cannot open %s"), filename); + exit (1); + } + } + + if (dbname) + { + dbf = gdbm_open (dbname, 0, GDBM_NEWDB, 0600, NULL); + if (!dbf) + { + gdbm_perror (_("gdbm_open failed")); + exit (1); + } + } + + rc = gdbm_load_from_file (&dbf, fp, replace, &err_line); + if (rc) + { + if (err_line) + gdbm_perror ("%s:%lu", filename, err_line); + else + gdbm_perror ("cannot load from %s", filename); + } + + if (dbf) + { + 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..e7917f9 --- /dev/null +++ b/src/gdbmapp.h @@ -0,0 +1,52 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 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 . */ + +#include +#include + +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 (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; diff --git a/src/gdbmdefs.h b/src/gdbmdefs.h index 519d688..4ebb190 100644 --- a/src/gdbmdefs.h +++ b/src/gdbmdefs.h @@ -217,5 +217,7 @@ struct gdbm_file_info { } \ while (0) \ +#define _GDBM_MAX_DUMP_LINE_LEN 76 + /* Now define all the routines in use. */ #include "proto.h" diff --git a/src/gdbmdump.c b/src/gdbmdump.c new file mode 100644 index 0000000..9380931 --- /dev/null +++ b/src/gdbmdump.c @@ -0,0 +1,193 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 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 . */ + +# include "autoconf.h" +# include "gdbmdefs.h" +# include "gdbm.h" +# include +# include +# include + +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); + + if (ferror(fp)) + rc = gdbm_errno = GDBM_FILE_WRITE_ERROR; + + 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; + } + 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 index d9c5c3f..de1aab7 100644 --- a/src/gdbmerrno.c +++ b/src/gdbmerrno.c @@ -54,6 +54,7 @@ const char * const gdbm_errlist[_GDBM_MAX_ERRNO+1] = { 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 */ }; const char * diff --git a/src/gdbmexp.c b/src/gdbmexp.c new file mode 100644 index 0000000..fdafcd7 --- /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 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 . */ + +/* Include system configuration before all else. */ +# include "autoconf.h" +# include + +# 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/gdbmimp.c b/src/gdbmimp.c new file mode 100644 index 0000000..3b4fdcc --- /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 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 . */ + +# include "autoconf.h" +# include + +# 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..afd4866 --- /dev/null +++ b/src/gdbmload.c @@ -0,0 +1,502 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 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 . */ + +# include "autoconf.h" +# include "gdbmdefs.h" +# include "gdbm.h" +# include +# include +# include + +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) +{ + unsigned long n; + uid_t owner_uid; + uid_t owner_gid; + mode_t mode; + int meta_flags = 0; + const char *p; + char *end; + + 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; + } + } + + 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)) + /* FIXME: set error code */; + } + if ((meta_flags & META_MODE) && fchmod (fd, mode)) + /* FIXME: set error code */; + } + return 0; +} + +int +_gdbm_load_file (struct dump_file *file, GDBM_FILE dbf, GDBM_FILE *ofp, + int replace) +{ + 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) + { + _set_gdbm_meta_info (dbf, file->header); + *ofp = dbf; + } + else if (tmp) + gdbm_close (tmp); + + return rc; +} + +int +gdbm_load_from_file (GDBM_FILE *pdbf, FILE *fp, int replace, + 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; + rc = _gdbm_load_file (&df, *pdbf, pdbf, replace); + 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, + 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, line); + fclose (fp); + return rc; +} + diff --git a/src/mem.c b/src/mem.c new file mode 100644 index 0000000..34c55f4 --- /dev/null +++ b/src/mem.c @@ -0,0 +1,74 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 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 . */ + +# include "autoconf.h" +# include "gdbm.h" +# include "gdbmapp.h" +# include "gdbmdefs.h" + +void +ealloc_die () +{ + error ("%s", strerror (ENOMEM)); + exit (1); +} + +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 (char *str) +{ + char *p; + + if (!str) + return NULL; + p = emalloc (strlen (str) + 1); + strcpy (p, str); + return p; +} + + diff --git a/src/parseopt.c b/src/parseopt.c new file mode 100644 index 0000000..7536a79 --- /dev/null +++ b/src/parseopt.c @@ -0,0 +1,585 @@ +/* This file is part of GDBM, the GNU data base manager. + Copyright (C) 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 . */ + +# include "autoconf.h" +# include "gdbm.h" +# include "gdbmapp.h" +# include "gdbmdefs.h" +# include +# include +# include +# include +# include +# ifdef HAVE_GETOPT_H +# include +# 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 = n