[PATCH] tdb <-> flatfile utilities

Peter Samuelson peter at cadcamlab.org
Fri Feb 2 10:07:26 GMT 2001


I wrote these for Samba-TNG but I just tested them with HEAD tdb and
they seem to work.  'tdbexport' takes a tdb and spits out a flat file,
and 'tdbimport' does the converse.

The basic idea is to make it possible to hand-edit data stored in tdb
structures.  Simply export to text, run 'vi', and re-import.  Of
course, there are other uses -- Elrond told me he used tdbexport to
debug server code at one point.


When I wrote tdbexport many months ago, something was wrong with my
head and I overdid the output formats.  There are actually four:

  literal (-l): consists of lines of the form 'key:value'.  Lossy,
    because keys cannot contain ':' and neither keys nor values can
    have newlines or nulls.

  hex (-x): consists of lines of the form '71236498:27648008', where
    the key and value alike are converted to pairs of hex digits.  Long
    lines are broken up, and all but the first line of a record is
    indented with whitespace.  Other whitespace between digit pairs is
    ignored on import.

  quoted-printable (-p, and the default): looks just like "literal",
    but is lossless, because all troublesome characters are represented
    by '=XX' where XX are hex digits.  Long lines are broken up with
    '=\n'.  Trailing spaces are not allowed (use "=20" at the end of a
    line).  See RFC1341 ("Multipurpose Internet Mail Extensions"),
    section 5.1.

  base64 (-b): I don't know what got into me, but having done
    quoted-printable, I decided to do "the other MIME encoding" as
    well.  See RFC1341, section 5.2.

tdbimport can import any of these formats, and will autodetect them as
long as you indicate the format in the header of the text file (consult
the output of tdbexport for details).  "Literal" format is detected by
lack of a parseable header..

Oh - tdbimport does have one limitation: it uses fixed buffer sizes and
no bounds checking (yet), so don't let a raw key or value go longer
than the compiled-in constant, currently 1600 bytes.

Peter
-------------- next part --------------
Index: Makefile
===================================================================
RCS file: /cvsroot/samba/source/tdb/Makefile,v
retrieving revision 1.6
diff -u -u -r1.6 Makefile
--- Makefile	2001/01/29 21:34:08	1.6
+++ Makefile	2001/02/02 09:27:20
@@ -5,7 +5,7 @@
 CFLAGS = -DSTANDALONE -DTDB_DEBUG -g -DHAVE_MMAP=1
 CC = gcc
 
-PROGS = tdbtest tdbtool tdbtorture
+PROGS = tdbtest tdbtool tdbtorture tdbimport tdbexport
 TDB_OBJ = tdb.o spinlock.o
 
 default: $(PROGS)
@@ -18,6 +18,12 @@
 
 tdbtorture: tdbtorture.o $(TDB_OBJ)
 	$(CC) $(CFLAGS) -o tdbtorture tdbtorture.o $(TDB_OBJ)
+
+tdbimport: tdbimport.o $(TDB_OBJ)
+	$(CC) $(CFLAGS) -o $@ tdbimport.o $(TDB_OBJ)
+
+tdbexport: tdbexport.o $(TDB_OBJ)
+	$(CC) $(CFLAGS) -o $@ tdbexport.o $(TDB_OBJ)
 
 clean:
 	rm -f $(PROGS) *.o *~ *% core test.db test.tdb test.gdbm
-------------- next part --------------
/*
 * tdbimport.c
 * Copyright (C) 2001 Peter Samuelson <peter at cadcamlab.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>
#include <stdarg.h>
#include <fcntl.h>
#include "tdb.h"

#define MODE_AUTODETECT	0
#define MODE_LITERAL	1
#define MODE_HEX	2
#define MODE_BASE64	3
#define MODE_QPRINT	4

#define MAX_KEYVAL_LEN	1600
#define MAX_INPUTLINE	2048

static char *progname, *input_filename;
static int input_linenum;

static void usage (int status)
{
  FILE *out = stdout;
  if (status)
    out = stderr;

  fprintf (out,
	   "usage: %s [-x|-b|-p|-l] [-s hashsize] [-a|-c] -o output [inputfile]\n"
	   "  -x    input file is in hexadecimal\n"
	   "  -b    input file is MIME Base64\n"
	   "  -p    input file is MIME Quoted-Printable\n"
	   "  -l    input file is literal\n"
	   "(If none of the above are specified, input format is auto-detected.)\n"
	   "  -s    advisory hash size for new tdb\n"
	   "  -a    augment tdb file rather than overwriting it (default)\n"
	   "  -c    start with clean tdb file (overwrite rather than augment)\n"
	   "  -o    name of output tdb file (required)\n"
	   "  -h    this help message\n",
	   progname);
  exit (status);
}

static void warn (char *fmt, ...)
{
  va_list args;

  va_start (args, fmt);
  fprintf (stderr, "%s:%d: ", input_filename, input_linenum);
  vfprintf (stderr, fmt, args);
  fprintf (stderr, "\n");
  va_end (args);
}

/* inspired by Perl 5 */
static void chomp (char *str)
{
  int l = strlen (str) - 1;
  while (l >= 0 &&
	 (str[l] == '\n' || str[l] == '\r'))
    str[l--] = '\0';
}

/*
 * get_hex_byte: extracts two hex digits from char buffer
 * Just like sscanf("%2x") but I don't know if sscanf is
 * reliable about not seeing *more* than 2 digits
 */
static int get_hex_byte (char *buf)
{
  int c1 = 0, c2;
  if (!isspace (*buf)) {
    c1 = *buf - '0' - ('A'-'9'-1) * (*buf >= 'A') - ('a'-'A') * (*buf >= 'a');
    buf++;
  }
  c2 = *buf - '0' - ('A'-'9'-1) * (*buf >= 'A') - ('a'-'A') * (*buf >= 'a');
  if (c1 < 0 || c1 > 15 || c2 < 0 || c2 > 15)
    return -1;
  c2 += c1*16;
  return c2;
}

/*
 * decode RFC1341 BASE-64, in situ
 * See 'tdbexport.c' for a clearer look at the algorithm.
 * Returns the final length of the buffer, 0 for invalid char(s)
 */
static int decode_base64 (char *buf, int len)
{
  /* how much each ASCII character is worth (0-63) */
  /* ...plus one, because zero is so handy for 'invalid' */
  static int base64[256] = {
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,63, 0, 0, 0,64,	/* + / */
    53,54,55,56,57,58,59,60,61,62, 0, 0, 0,-1, 0, 0,	/* 0-9 = */
     0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,	/* A-O */
    16,17,18,19,20,21,22,23,24,25,26, 0, 0, 0, 0, 0,	/* P-Z */
     0,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,	/* a-o */
    42,43,44,45,46,47,48,49,50,51,52, 0, 0, 0, 0, 0,	/* p-z */
  };
  int i, j, total_len = 0;
  unsigned char *ub = buf;	/* 'unsigned char' -- stay on the safe side */

  for (i=0; i < len/4; i++) {
    total_len += 3;
    for (j=0; j<4; j++) {
      int tmp = base64[ub[4*i+j]];
      if (tmp == -1)
	total_len--;
      else if (!tmp) {
	warn ("unexpected character in input");
	return 0;
      } else
	ub[4*i+j] = tmp - 1;
    }

    ub[3*i]   = ub[4*i]   << 2 | ub[4*i+1] >> 4;
    ub[3*i+1] = ub[4*i+1] << 4 | ub[4*i+2] >> 2;
    ub[3*i+2] = ub[4*i+2] << 6 | ub[4*i+3];
  }
  return total_len;
}


/*********************************************************************
 * raw_line: process one line of "literal" input
 *
 * For all these input formats, the intended "key" and "value" are
 * separated by the ':' character.  In this one, key and value must be
 * on a single line and no escaping of any kind is done.  That means no
 * comments in the file, no ':' characters in keys, and no newlines in
 * either keys or values.  The other formats (hex, base64 and quoted-
 * printable) do not have these limitations.
 */
static void raw_line (TDB_CONTEXT *t, char *line)
{
  TDB_DATA k, v;
  char *val;

  chomp (line);
  val = strchr (line, ':');
  if (!val) {
    if (line[0])
      warn ("malformed input line");
    return;
  }
  *val++ = '\0';

  k.dptr = line;
  k.dsize = strlen (line);
  v.dptr = val;
  v.dsize = strlen (val);
  tdb_store (t, k, v, 0);
}


/*********************************************************************
 * hex_line: process one line of hex
 *
 * Each record is a sequence of pairs of hex digits on a line - these
 * are octet codes.  If the line gets too long, it is continued by
 * indenting the next line.  (This makes it really awkward to decode,
 * because you do not know if a record is finished until the *next*
 * line.  I must have been sleep-deprived when I came up with the
 * format.)
 *
 * Note: whitespace in input lines does not matter, except (1) do *not*
 * indent the first line of a record, (2) *do* indent subsequent lines
 * of a record, and (3) do not split up a pair of hex digits.
 */
static void hex_line (TDB_CONTEXT *t, char *line)
{
  static char kbuf[MAX_KEYVAL_LEN], vbuf[MAX_KEYVAL_LEN];
  static int klen = 0, vlen = 0;
  static int have_key = 0, have_partial = 0;

  int byte;

  if (line[0] == ';')
    return;

  chomp (line);

  /* a new record is not indented, or is an empty line */
  if (!*line || !isspace (*line)) {
    if (have_key) {
      TDB_DATA k, v;
      k.dptr = kbuf;
      k.dsize = klen;
      v.dptr = vbuf;
      v.dsize = vlen;
      tdb_store (t, k, v, 0);
    } else if (have_partial) {
      warn ("malformed line - expected indented");
      return;
    }
    klen = vlen = have_key = have_partial = 0;

  } else if (!have_partial) {
    warn ("malformed line - expected non-indented");
    return;
  }

  while (1) {
    while (isspace (*line))
      line++;
    if (*line == ':' && !have_key) {
      line++;
      have_key = 1;
    }
    while (isspace (*line))
      line++;

    if ((byte = get_hex_byte (line)) == -1)
      break;
    if (have_key)
      *(vbuf + vlen++) = byte;
    else
      *(kbuf + klen++) = byte;
    have_partial = 1;
    line += 2;
  }

  /* at this point we *should* be at the end of the line */
  if (*line && *line != ';')
    warn ("unexpected character '%c' (%d) encountered, truncating line",
	  *line, *line);
}


/*********************************************************************
 * base64_line: process one line of BASE64
 *
 * BASE64 is defined by RFC1341.  Every 3 octets are converted to 4
 * bytes of 6 significant bits -- the 6 bits are an offset into the
 * string "[A-Z][a-z][0-9]+/".  Bits are packed MSb->LSb from first to
 * last byte of each set of 4.  If the input is not divisible by 3
 * octets, the last 4-byte group is padded with either one or two '='
 * characters.
 *
 * Once again (see comment on hex_line) I must have been mad when
 * designing this TDB export format -- lines are continued by indenting
 * the next line, so this function cannot finish processing a line
 * until it gets the next line.
 *
 * Based on this design, the modus operandi here is to collect an
 * entire buffer of non-decoded input, then process it all at once,
 * when we find out that we have the whole encoded string.
 */
static void base64_line (TDB_CONTEXT *t, char *line)
{
  static char kbuf[MAX_KEYVAL_LEN], vbuf[MAX_KEYVAL_LEN];
  static int klen = 0, vlen = 0;
  static int have_key = 0, have_partial = 0;

  if (line[0] == ';')
    return;
  chomp (line);

  if (!*line || !isspace (*line)) {	/* end of previous record */
    if (have_key) {
      TDB_DATA k, v;

      klen = decode_base64 (kbuf, klen);
      vlen = decode_base64 (vbuf, vlen);
      k.dptr = kbuf;
      k.dsize = klen;
      v.dptr = vbuf;
      v.dsize = vlen;
      tdb_store (t, k, v, 0);
    } else if (have_partial) {
      warn ("malformed line - expected indented");
      return;
    }
    klen = vlen = have_key = have_partial = 0;

  } else if (!have_partial) {
    warn ("malformed line - expected non-indented");
    return;
  }

  /* take care of indent */
  while (isspace (*line))
    line++;

  for (; *line; line++) {
    if (!have_key && *line == ':')
      have_key = 1;
    else if (have_key)
      vbuf[vlen++] = *line;
    else {
      kbuf[klen++] = *line;
      have_partial = 1;
    }
  }
}



/*********************************************************************
 * qprint_line: process one line of quoted-printable
 *
 * Quoted-printable is defined by RFC1341.  Basically: any suspicious
 * characters are replaced by '=XX' where XX are hex digits.  Long
 * lines are broken up with '=\n'.  Trailing spaces on a line are not
 * allowed (they are stripped on decode), so replace the last space on
 * a line with '=20'.
 *
 * For tdbexport purposes, each record is one quoted line.  Embed
 * newlines with '=0a'.
 *
 * Unlike the others, I think this format is almost easier to decode
 * than to encode.
 */
static void qprint_line (TDB_CONTEXT *t, char *line)
{
  static char kbuf[MAX_KEYVAL_LEN], vbuf[MAX_KEYVAL_LEN];
  static int klen = 0, vlen = 0;
  static int have_key = 0;

  TDB_DATA k, v;
  int i;

  if (line[0] == ';')
    return;

  chomp (line);

  for (i=0; line[i]; i++) {
    if (line[i] == ':' && !have_key) {
      have_key = 1;
      continue;
    } else if (line[i] == '=') {
      int byte;
      i++;
      if ((byte = get_hex_byte (&line[i])) != -1) {
	i++;
	line[i] = byte;
      } else {		/* continuation: /=[[:space:]]*$/ */
	while (isspace (line[i]))
	  i++;
	if (!line[i])
	  return;
	warn ("malformed '=' escape");
	line[i] = '=';
      }
    }
    if (have_key)
      vbuf[vlen++] = line[i];
    else
      kbuf[klen++] = line[i];
  }

  if (!have_key) {
    if (klen)
      warn ("no ':' separator found");
    klen = vlen = have_key = 0;
    return;
  }

  k.dptr = kbuf;
  k.dsize = klen;
  v.dptr = vbuf;
  v.dsize = vlen;
  tdb_store (t, k, v, 0);
  klen = vlen = have_key = 0;
}



/*********************************************************************
 * do_file: process a single input file
 *
 * This auto-detects the input file type (if necessary), reads lines in
 * and passes them on to the correct function.
 */
static int do_file (TDB_CONTEXT *t, FILE *in, int input_mode)
{
  char inbuf[MAX_INPUTLINE];
  int lasttime = 0;

  while (fgets (inbuf, MAX_INPUTLINE, in) || !lasttime++) {
    input_linenum++;
    if (lasttime)
      inbuf[0] = '\0';

    if (input_mode == MODE_AUTODETECT) {
      if (inbuf[0] == ';') {
	char fmt[80], keyword[80];
	if (sscanf (inbuf, "; %s %s", keyword, fmt) == 2 &&
	    !strcasecmp (keyword, "format:")) {

	  if      (!strcasecmp (fmt, "Hex-Dump") ||
	           !strcasecmp (fmt, "Hex"))
	    input_mode = MODE_HEX;
	  else if (!strcasecmp (fmt, "Base64"))
	    input_mode = MODE_BASE64;
	  else if (!strcasecmp (fmt, "Quoted-Printable") ||
	           !strcasecmp (fmt, "QP") ||
	           !strcasecmp (fmt, "Q-P"))
	    input_mode = MODE_QPRINT;
	  else {
	    warn ("unrecognized input format '%s'", fmt);
	    return 1;
	  }
	}
	continue;
      } else
	/* non-comment material appearing before 'Format:' line */
	input_mode = MODE_LITERAL;
    }

    switch (input_mode) {
    case MODE_LITERAL: raw_line    (t, inbuf); break;
    case MODE_HEX:     hex_line    (t, inbuf); break;
    case MODE_BASE64:  base64_line (t, inbuf); break;
    case MODE_QPRINT:  qprint_line (t, inbuf); break;
    default:
      warn ("internal error: unrecognized mode %d", input_mode);
      return 1;
    }
  }
  return 0;
}



int main (int argc, char *argv[])
{
  char *outfile = NULL;
  FILE *in = NULL;
  int mode = MODE_AUTODETECT, hashsize = 0, openflags = O_RDWR | O_CREAT;
  TDB_CONTEXT *t;
  int o, exit_status;

  if ((progname = strrchr (argv[0], '/')))
    progname++;
  else
    progname = argv[0];

  while ((o=getopt (argc, argv, "h?-blpxs:o:ac")) != -1) {
    switch (o) {
    case 'h': usage (0);
    case '?': case '-': usage(1);

    case 'b': mode = MODE_BASE64; break;
    case 'l': mode = MODE_LITERAL; break;
    case 'p': mode = MODE_QPRINT; break;
    case 'x': mode = MODE_HEX; break;

    case 's':
      /* sscanf: like atoi but more flexible (hex, etc)... */
      if (!sscanf (optarg, "%i", &hashsize))
	usage(1);

    case 'o': outfile = optarg; break;
    case 'a': openflags &= ~O_TRUNC; break;
    case 'c': openflags |=  O_TRUNC; break;
    }
  }

  if (!outfile)
    usage (1);

  if ((input_filename = argv[optind]) && strcmp ("-", input_filename)) {
    if (!(in = fopen (input_filename, "r"))) {
      char buf[64];
      sprintf (buf, "%s: open failed", progname);
      perror (buf);
      exit (1);
    }
  } else {
    in = stdin;
    input_filename = "<stdin>";
  }

  t = tdb_open (outfile, hashsize, 0, openflags, 0666);
  if (!t) {
    fprintf (stderr, "%s: cannot open TDB file '%s'\n", progname, outfile);
    exit (1);
  }

  exit_status = do_file (t, in, mode);
  exit_status |= tdb_close (t);
  return exit_status;
}
-------------- next part --------------
/*
 * tdbexport.c
 * Copyright (C) 2000 Peter Samuelson <peter at cadcamlab.org>
 *
 * Some bits stolen from tdbtool.c by Andrew Tridgell
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include "tdb.h"

#define MODE_LITERAL	1
#define MODE_HEX	2
#define MODE_BASE64	3
#define MODE_QPRINT	4

#define MODE_ESC_COLON	8 /* flag, so use distinct bit */


const static char *progname = "tdbexport";
static int verbose;
static int line_max = 76; /* for hex and quoted-printable displays */
static int line_len;
static int print_mode = MODE_QPRINT;
static FILE *outfp;

static void do_header(FILE *out, char *filename, int mode)
{
  char *fmt;
  time_t t = time(0);

  switch (mode) {
  case MODE_LITERAL:	return;
  case MODE_HEX:	fmt = "Hex-Dump"; break;
  case MODE_BASE64:	fmt = "Base64"; break;
  case MODE_QPRINT:	fmt = "Quoted-Printable"; break;
  }

  fprintf(out,
	  "; TDB dump file - import with `tdbimport'\n"
	  "; Source: %s\n"
	  "; Date: %s"		/* ctime adds \n */
	  "; Format: %s\n\n",
	  filename, ctime(&t), fmt);
}

static void do_output(FILE *out, unsigned char *buf, int len, int mode)
{
  int i, esc_colon;

  esc_colon = mode & MODE_ESC_COLON;
  mode &= ~MODE_ESC_COLON;

  switch (mode) {

  case MODE_LITERAL:	/* literal characters */
    fwrite(buf, len, 1, out);
    break;

  case MODE_HEX:	/* hexadecimal */
    for (i=0; i<len; i++) {
      int ch=buf[i];
      fprintf(out, "%02X", ch);
      line_len += 2;
      if(line_len >= line_max-1) {
	fputs("\n  ", out);
	line_len = 2;
      }
    }
    break;

  /*
   * MIME Base64 output
   * Believed to be basically RFC1341-compliant.
   * Should be (had better be!) endian-independent.
   */
  case MODE_BASE64:
    for (i=0; i<len; i+=3) {
      static char base64[] =
	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
      unsigned char ch[4], j;
      char *fmt;

      ch[0] = buf[i];
      ch[1] = i+1 < len ? buf[i+1] : 0;
      ch[2] = i+2 < len ? buf[i+2] : 0;

      ch[3] = base64[ 0x3f & ch[2] ];
      ch[2] = base64[ 0x3f & ((ch[1] << 2) | (ch[2] >> 6)) ];
      ch[1] = base64[ 0x3f & ((ch[0] << 4) | (ch[1] >> 4)) ];
      ch[0] = base64[ ch[0] >> 2 ];

      if (i+2 >= len)
	ch[3] = '=';
      if (i+1 == len)
	ch[2] = '=';

      switch (line_max - line_len) {
      case -1:
      case 0: fmt = "\n  %c%c%c%c"; line_len=6; break;
      case 1: fmt = "%c\n  %c%c%c"; line_len=5; break;
      case 2: fmt = "%c%c\n  %c%c"; line_len=4; break;
      case 3: fmt = "%c%c%c\n  %c"; line_len=3; break;
      default: fmt = "%c%c%c%c";    line_len+=4; break;
      }

      fprintf(out, fmt, ch[0], ch[1], ch[2], ch[3]);
    }
    break;


  /*
   * MIME Quoted-Printable output
   * Believed to be RFC1341-compliant.
   * Also believed to be lossless for any data,
   * which of course is the whole point.
   */
  case MODE_QPRINT:	/* quoted-printable */
    for (i=0; i<len; i++) {
      int ch = buf[i];
      static char tmpbuf[8];
      char *fmt;

      if (ch == '\n') {
	if (i == len-1)	/* final char is newline */
	  fmt = "=0A";
	else
	  fmt = "=0A=\r\n";
      }
      else if ((ch == ' ' && i == len-1) ||	/* space at end of output */
	       (ch == ':' && esc_colon) ||	/* colon in first field */
						/* line starting with ; or # */
	       ((ch == '#' || ch == ';') && esc_colon && line_len == 0) ||
	       (ch == '=' || ch < 32 || ch > 127)) /* non-printable character */
	fmt = "=%02X";
      else
	fmt = "%c";

      line_len += sprintf(tmpbuf, fmt, ch);
      if(line_len >= line_max) {
	fputs("=\r\n", out);
	line_len = strlen(tmpbuf);
      }
      fputs(tmpbuf, out);

    }
    break;
  default:
    fprintf(stderr, "%s: unknown output mode %d\n", progname, mode);
    exit(1);
  }
}

/* callback for db traversal */
int export_cb(TDB_CONTEXT *unused1, TDB_DATA key, TDB_DATA dbuf, void *unused2)
{
  do_output(outfp, key.dptr, key.dsize, print_mode | MODE_ESC_COLON);
  fputc(':', outfp); line_len++;
  do_output(outfp, dbuf.dptr, dbuf.dsize, print_mode);
  fputc('\n', outfp); line_len = 0;

  /*  free(key.dptr);	// README is wrong */
  /*  free(dbuf.dptr);	// README is wrong */
  return 0;
}

static void usage(void)
{
  fprintf(stderr,
	  "usage: %s [-x|-p|-l] [-m N] [-o output] dbfile\n"
	  "  -x    output in a hexadecimal format\n"
	  "  -b    output in MIME Base64 format\n"
	  "  -p    output in MIME Quoted-Printable format\n"
	  "  -l    output literal characters (warning: can be lossy!)\n"
	  "  -o    name of output file\n"
          "  -m    maximum number of characters per output line\n"
          "        (ignored in \"literal\" mode)\n"
	  "The database file is not optional.\n"
	  "Default output mode is Quoted-Printable.\n", progname);
  exit(1);
}

int main(int argc, char *argv[])
{
  char *dbfile;
  int o;
  char *outfile = NULL;
  TDB_CONTEXT *t;

  while((o=getopt(argc, argv, "h?-blpxo:m:")) != -1) {
    switch(o) {
    case 'h': case '?': case '-': usage(); break;

    case 'b': print_mode = MODE_BASE64; break;
    case 'l': print_mode = MODE_LITERAL; break;
    case 'p': print_mode = MODE_QPRINT; break;
    case 'x': print_mode = MODE_HEX; break;

    case 'o': outfile = optarg; break;

    case 'm':
      if(!sscanf(optarg, "%i", &line_max))
	usage();
      break;
    }
  }
  dbfile = argv[optind];
  if(!dbfile)
    usage();

  if(outfile) {
    outfp = fopen(outfile, "w");
    if(!outfp) {
      perror(progname);
      exit(1);
    }
  } else {
    outfp = stdout;
  }
  
  t = tdb_open(dbfile, 0, 0, O_RDONLY, 0);
  if(!t) {
    /*    fprintf(stderr, "%s: %s\n", progname, tdb_error(t)); */
    /* oops, that doesn't make much sense, does it? tdb_error(NULL)? */
    fprintf(stderr, "%s: cannot open TDB file %s\n", progname, dbfile);
    exit(1);
  }

  do_header(outfp, dbfile, print_mode);

  tdb_traverse(t, export_cb, 0);

  tdb_close(t);
  return 0;
}


More information about the samba-technical mailing list