summaryrefslogtreecommitdiffabout
Side-by-side diff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--ChangeLog49
-rw-r--r--configure.ac4
-rw-r--r--doc/gdbm.texinfo43
-rw-r--r--export/.cvsignore2
-rw-r--r--export/export.c5
-rw-r--r--src/.cvsignore2
-rw-r--r--src/Makefile.am35
-rw-r--r--src/base64.c127
-rw-r--r--src/flatfile.c246
-rw-r--r--src/gdbm.h.in23
-rw-r--r--src/gdbm_dump.c124
-rw-r--r--src/gdbm_load.c140
-rw-r--r--src/gdbmapp.h52
-rw-r--r--src/gdbmdefs.h2
-rw-r--r--src/gdbmdump.c193
-rw-r--r--src/gdbmerrno.c1
-rw-r--r--src/gdbmexp.c125
-rw-r--r--src/gdbmimp.c157
-rw-r--r--src/gdbmload.c502
-rw-r--r--src/mem.c74
-rw-r--r--src/parseopt.c585
-rw-r--r--src/progname.c35
-rw-r--r--src/proto.h12
-rw-r--r--src/systems.h16
-rw-r--r--src/testgdbm.c142
25 files changed, 2322 insertions, 374 deletions
diff --git a/ChangeLog b/ChangeLog
index f90b8a4..8072ea2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,54 @@
2011-11-13 Sergey Poznyakoff <gray@gnu.org.ua>
+ 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 <stdio.h>
+ (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 <gray@gnu.org.ua>
+
Version 1.10
* NEWS: Raise version number.
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 <gdbm.h>
-/* 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
--- a/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 <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/flatfile.c b/src/flatfile.c
deleted file mode 100644
index c0f9422..0000000
--- a/src/flatfile.c
+++ b/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 <http://www.gnu.org/licenses/>. */
-
-#ifndef _GDBMEXPORT_
-
-/* Include system configuration before all else. */
-# include "autoconf.h"
-# include <arpa/inet.h>
-
-# 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 <stdio.h>
+
/* 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
--- a/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 <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", 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 = "<stdout>";
+ 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
--- a/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 <http://www.gnu.org/licenses/>. */
+
+# 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 = "<stdin>";
+ 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
--- a/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 <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+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
--- a/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 <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);
+
+ 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
--- a/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 <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/gdbmimp.c b/src/gdbmimp.c
new file mode 100644
index 0000000..3b4fdcc
--- a/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 <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..afd4866
--- a/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 <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)
+{
+ 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
--- a/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 <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 (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
--- a/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 <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..4bcba4c
--- a/dev/null
+++ b/src/progname.c
@@ -0,0 +1,35 @@
+/* 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 <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
index 39a5e31..f7a1a1a 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -59,4 +59,16 @@ int _gdbm_lock_file (GDBM_FILE);
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
index c83cd9c..7bf072a 100644
--- a/src/systems.h
+++ b/src/systems.h
@@ -18,28 +18,20 @@
along with GDBM. If not, see <http://www.gnu.org/licenses/>. */
/* Include all system headers first. */
-#if HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
+#include <sys/types.h>
#include <stdio.h>
#if HAVE_SYS_FILE_H
# include <sys/file.h>
#endif
#include <sys/stat.h>
-#if HAVE_STDLIB_H
-# include <stdlib.h>
-#endif
+#include <stdlib.h>
#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif
-#if HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-#if HAVE_FCNTL_H
-# include <fcntl.h>
-#endif
+#include <unistd.h>
+#include <fcntl.h>
#include <errno.h>
#ifndef SEEK_SET
diff --git a/src/testgdbm.c b/src/testgdbm.c
index b2b9c6c..cf5fc88 100644
--- a/src/testgdbm.c
+++ b/src/testgdbm.c
@@ -23,6 +23,7 @@
#include "gdbmdefs.h"
#include "gdbm.h"
+#include "gdbmapp.h"
#include <errno.h>
#include <ctype.h>
@@ -52,7 +53,7 @@ int data_z = 1; /* Data are nul-terminated strings */
void
-error (int code, const char *fmt, ...)
+terror (int code, const char *fmt, ...)
{
va_list ap;
if (!interactive)
@@ -118,23 +119,23 @@ _gdbm_avail_list_size (GDBM_FILE dbf, size_t min_size)
+ sizeof (avail_block));
av_stk = (avail_block *) malloc (size);
if (av_stk == NULL)
- error (2, _("Out of memory"));
+ terror (2, _("Out of memory"));
/* Traverse the stack. */
while (temp)
{
if (__lseek (dbf, temp, SEEK_SET) != temp)
{
- error (0, "lseek: %s", strerror (errno));
+ terror (0, "lseek: %s", strerror (errno));
break;
}
if ((rc = _gdbm_full_read (dbf, av_stk, size)))
{
if (rc == GDBM_FILE_EOF)
- error (0, "read: %s", gdbm_strerror (rc));
+ terror (0, "read: %s", gdbm_strerror (rc));
else
- error (0, "read: %s (%s)", gdbm_strerror (rc), strerror (errno));
+ terror (0, "read: %s (%s)", gdbm_strerror (rc), strerror (errno));
break;
}
@@ -172,23 +173,23 @@ _gdbm_print_avail_list (FILE *fp, GDBM_FILE dbf)
+ sizeof (avail_block));
av_stk = (avail_block *) malloc (size);
if (av_stk == NULL)
- error (2, _("Out of memory"));
+ terror (2, _("Out of memory"));
/* Print the stack. */
while (temp)
{
if (__lseek (dbf, temp, SEEK_SET) != temp)
{
- error (0, "lseek: %s", strerror (errno));
+ terror (0, "lseek: %s", strerror (errno));
break;
}
if ((rc = _gdbm_full_read (dbf, av_stk, size)))
{
if (rc == GDBM_FILE_EOF)
- error (0, "read: %s", gdbm_strerror (rc));
+ terror (0, "read: %s", gdbm_strerror (rc));
else
- error (0, "read: %s (%s)", gdbm_strerror (rc), strerror (errno));
+ terror (0, "read: %s (%s)", gdbm_strerror (rc), strerror (errno));
break;
}
@@ -230,37 +231,6 @@ _gdbm_print_bucket_cache (FILE *fp, GDBM_FILE dbf)
fprintf (fp, _("Bucket cache has not been initialized.\n"));
}
-void
-usage ()
-{
- printf (_("Usage: %s OPTIONS\n"), progname);
- printf (_("Test and modify a GDBM database.\n"));
- printf ("\n");
- printf (_("OPTIONS are:\n\n"));
- printf (_(" -b SIZE set block size\n"));
- printf (_(" -c SIZE set cache size\n"));
- printf (_(" -g FILE operate on FILE instead of `junk.gdbm'\n"));
- printf (_(" -h print this help summary\n"));
- printf (_(" -l disable file locking\n"));
- printf (_(" -m disable file mmap\n"));
- printf (_(" -n create database\n"));
- printf (_(" -r open database in read-only mode\n"));
- printf (_(" -s synchronize to the disk after each write\n"));
- printf (_(" -v print program version\n"));
- printf ("\n");
- printf (_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
-}
-
-void
-version ()
-{
- printf ("testgdbm (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
- printf ("Copyright (C) 2007-2011 Free Software Foundation, Inc.\n");
- printf ("License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n");
- printf ("This is free software: you are free to change and redistribute it.\n");
- printf ("There is NO WARRANTY, to the extent permitted by law.\n");
-}
-
int
trimnl (char *str)
{
@@ -287,7 +257,7 @@ read_from_file (const char *name, int replace)
fp = fopen (name, "r");
if (!fp)
{
- error (0, _("cannot open file `%s' for reading: %s"),
+ terror (0, _("cannot open file `%s' for reading: %s"),
name, strerror (errno));
return;
}
@@ -298,7 +268,7 @@ read_from_file (const char *name, int replace)
if (!trimnl (buf))
{
- error (0, _("%s:%d: line too long"), name, line);
+ terror (0, _("%s:%d: line too long"), name, line);
continue;
}
@@ -306,7 +276,7 @@ read_from_file (const char *name, int replace)
p = strchr (buf, ' ');
if (!p)
{
- error (0, _("%s:%d: malformed line"), name, line);
+ terror (0, _("%s:%d: malformed line"), name, line);
continue;
}
@@ -317,7 +287,7 @@ read_from_file (const char *name, int replace)
data.dptr = p;
data.dsize = strlen (p) + data_z;
if (gdbm_store (gdbm_file, key, data, flag) != 0)
- error (0, _("%d: item not inserted: %s"),
+ terror (0, _("%d: item not inserted: %s"),
line, gdbm_strerror (gdbm_errno));
}
fclose (fp);
@@ -392,9 +362,9 @@ delete_handler (char *arg[NARGS], FILE *fp, void *call_data ARG_UNUSED)
if (gdbm_delete (gdbm_file, key_data) != 0)
{
if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
- error (0, _("Item not found"));
+ terror (0, _("Item not found"));
else
- error (0, _("Can't delete: %s"), gdbm_strerror (gdbm_errno));
+ terror (0, _("Can't delete: %s"), gdbm_strerror (gdbm_errno));
}
}
@@ -686,7 +656,7 @@ list_handler (char *arg[NARGS] ARG_UNUSED, FILE *fp, void *call_data)
data = gdbm_fetch (gdbm_file, key);
if (!data.dptr)
- error (0, _("cannot fetch data (key %.*s)"), key.dsize, key.dptr);
+ terror (0, _("cannot fetch data (key %.*s)"), key.dsize, key.dptr);
else
{
fprintf (fp, "%.*s %.*s\n", key.dsize, key.dptr, data.dsize,
@@ -719,7 +689,7 @@ export_handler (char *arg[NARGS], FILE *fp, void *call_data ARG_UNUSED)
flags = GDBM_NEWDB;
if (gdbm_export (gdbm_file, arg[0], flags, 0600) == -1)
- error (0, _("gdbm_export failed, %s"), gdbm_strerror (gdbm_errno));
+ terror (0, _("gdbm_export failed, %s"), gdbm_strerror (gdbm_errno));
}
/* i file [replace] - import from a flat file */
@@ -732,7 +702,7 @@ import_handler (char *arg[NARGS], FILE *fp, void *call_data ARG_UNUSED)
flag = GDBM_REPLACE;
if (gdbm_import (gdbm_file, arg[0], flag) == -1)
- error (0, _("gdbm_import failed, %s"), gdbm_strerror (gdbm_errno));
+ terror (0, _("gdbm_import failed, %s"), gdbm_strerror (gdbm_errno));
}
static const char *
@@ -999,6 +969,23 @@ getword (char *s, char **endp)
The commands are single letter commands. The user is prompted for all other
information. See the help command (?) for a list of all commands. */
+char *parseopt_program_doc = "Test and modify a GDBM database";
+char *parseopt_program_args = "";
+
+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_("operate on FILE instead of `junk.gdbm'") },
+ { 'g', NULL, NULL, NULL, PARSEOPT_ALIAS },
+ { 'l', "no-lock", NULL, N_("disable file locking") },
+ { 'm', "no-mmap", NULL, N_("disable file mmap") },
+ { 'n', "newdb", NULL, N_("create database") },
+ { 'r', "read-only", NULL, N_("open database in read-only mode") },
+ { 's', "synchronize", NULL, N_("synchronize to disk after each write") },
+ { 0 }
+};
+
int
main (int argc, char *argv[])
{
@@ -1012,12 +999,8 @@ main (int argc, char *argv[])
char newdb = FALSE;
int flags = 0;
char *pager = getenv ("PAGER");
-
- progname = strrchr (argv[0], '/');
- if (progname)
- progname++;
- else
- progname = argv[0];
+
+ set_progname (argv[0]);
#ifdef HAVE_SETLOCALE
setlocale (LC_ALL, "");
@@ -1027,29 +1010,11 @@ main (int argc, char *argv[])
set_minimal_abbreviations ();
- /* Argument checking. */
- if (argc == 2)
- {
- if (strcmp (argv[1], "--help") == 0)
- {
- usage ();
- exit (0);
- }
- else if (strcmp (argv[1], "--version") == 0)
- {
- version ();
- exit (0);
- }
- }
-
- opterr = 0;
- while ((opt = getopt (argc, argv, "lmsrnc:b:g:hv")) != -1)
+ for (opt = parseopt_first (argc, argv, optab);
+ opt != EOF;
+ opt = parseopt_next ())
switch (opt)
{
- case 'h':
- usage ();
- exit (0);
-
case 'l':
flags = flags | GDBM_NOLOCK;
break;
@@ -1060,21 +1025,21 @@ main (int argc, char *argv[])
case 's':
if (reader)
- error (2, _("-s is incompatible with -r"));
+ terror (2, _("-s is incompatible with -r"));
flags = flags | GDBM_SYNC;
break;
case 'r':
if (newdb)
- error (2, _("-r is incompatible with -n"));
+ terror (2, _("-r is incompatible with -n"));
reader = TRUE;
break;
case 'n':
if (reader)
- error (2, _("-n is incompatible with -r"));
+ terror (2, _("-n is incompatible with -r"));
newdb = TRUE;
break;
@@ -1088,15 +1053,12 @@ main (int argc, char *argv[])
break;
case 'g':
+ case 'f':
file_name = optarg;
break;
- case 'v':
- version ();
- exit (0);
-
default:
- error (2, _("unknown option; try `%s -h' for more info\n"), progname);
+ terror (2, _("unknown option; try `%s -h' for more info\n"), progname);
}
if (file_name == NULL)
@@ -1120,11 +1082,11 @@ main (int argc, char *argv[])
gdbm_open (file_name, block_size, GDBM_WRCREAT | flags, 00664, NULL);
}
if (gdbm_file == NULL)
- error (2, _("gdbm_open failed: %s"), gdbm_strerror (gdbm_errno));
+ terror (2, _("gdbm_open failed: %s"), gdbm_strerror (gdbm_errno));
if (gdbm_setopt (gdbm_file, GDBM_CACHESIZE, &cache_size, sizeof (int)) ==
-1)
- error (2, _("gdbm_setopt failed: %s"), gdbm_strerror (gdbm_errno));
+ terror (2, _("gdbm_setopt failed: %s"), gdbm_strerror (gdbm_errno));
signal (SIGPIPE, SIG_IGN);
@@ -1162,7 +1124,7 @@ main (int argc, char *argv[])
cmd = find_command (p);
if (!cmd)
{
- error (0,
+ terror (0,
interactive ? _("Invalid command. Try ? for help.") :
_("Unknown command"));
continue;
@@ -1179,12 +1141,12 @@ main (int argc, char *argv[])
/* Optional argument */
break;
if (!interactive)
- error (1, _("%s: not enough arguments"), cmd->name);
+ terror (1, _("%s: not enough arguments"), cmd->name);
printf ("%s? ", arg);
if (fgets (argbuf[i], sizeof argbuf[i], stdin) == NULL)
- error (1, _("unexpected eof"));
+ terror (1, _("unexpected eof"));
trimnl (argbuf[i]);
args[i] = argbuf[i];
@@ -1205,7 +1167,7 @@ main (int argc, char *argv[])
out = popen (pager, "w");
if (!out)
{
- error (0, _("cannot run pager `%s': %s"), pager,
+ terror (0, _("cannot run pager `%s': %s"), pager,
strerror (errno));
pager = NULL;
}

Return to:

Send suggestions and report system problems to the System administrator.