/*
#ident	"@(#)smail/util:RELEASE-3_2_0_121:mkdbm.c,v 1.45 2005/11/15 01:17:23 woods Exp"
 */

/*
 *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
 *    Copyright (C) 1992  Ronald S. Karr
 * 
 * See the file COPYING, distributed with smail, for restriction
 * and warranty information.
 */

/*
 * mkdbm.c:
 *	Take a list of lines with a key followed by a colon character
 *	or white space followed by data and build a dbm database from
 *	them.  If the flag -f is given, downcase the keys.
 *
 *	Usage:  mkdbm [-o database] [-fvndy] [input_file ...]
 */

#include "defs.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <limits.h>

#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
#  include <stdlib.h>
# endif
#endif

#ifdef HAVE_STRING_H
# if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H)
#  include <memory.h>
# endif
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif

#ifdef __STDC__
# include <stdarg.h>
#else
# include <varargs.h>
#endif

#if defined(HAVE_UNISTD_H)
# include <unistd.h>
#endif

#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# ifdef HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif

#if defined(UNIX_SYS5) || defined(POSIX_OS) || defined(USE_FCNTL)
# include <fcntl.h>
#else
# if defined(UNIX_BSD)
#  include <sys/file.h>
# endif
#endif
#if !defined(HAVE_GETHOSTNAME) && defined(HAVE_UNAME)
#include <sys/utsname.h>
#endif

#include "config.h"
#include "smail.h"
#include "alloc.h"
#include "list.h"
#include "main.h"
#include "parse.h"
#include "addr.h"
#include "smailstring.h"
#include "dys.h"
#include "smailsock.h"
#include "route.h"
#include "bindlib.h"
#include "transports/smtplib.h"			/* rfc821_is_*() decls. */
#include "spool.h"
#include "exitcodes.h"
#include "log.h"
#include "dbm_compat.h"
#include "extern.h"
#include "smailport.h"

/* variables provided by this file */
char *program;				/* argv[0] from main */
int debug = 0;
FILE *errfile;
int only_testing = TRUE;

/* variables local to this file */
static char *out_name = NULL;
static char *temp_dir = NULL;
#ifndef DBM_SUFFIX
static char *temp_pag = NULL;
#endif
static int downcase = FALSE;
static int ypstamps = FALSE;
static int verbose = FALSE;
static int no_at_record = FALSE;
static int no_nul_byte = FALSE;
static int reccnt = 0;
static int maxlen = 0;

/* functions local to this file */
static void done __P((int));
static char **read_args __P((char **));
static char *build_temp_name __P((char *));
static void create_database __P((char *));
static void add_to_database __P((FILE *));
static void add_ending_record __P((void));
static char *getline __P((FILE *));
static void rename_database __P((char *, char *));
static void add_yp_stamps __P((void));

/*ARGSUSED*/
int
main(argc, argv)
    int argc GCC_UNUSED_HACK;
    char **argv;
{
    char **files;
    char *temp_name;

    program = *argv++;
    errfile = stderr;

    files = read_args(argv);

    if (out_name == NULL) {
	if (files[0] && ! EQ(files[0], "-")) {
	    out_name = files[0];
	} else {
	    out_name = "dbm";
	}
    }

    if (!(temp_name = build_temp_name(out_name))) {
	    (void) fprintf(stderr, "%s: %s: %s\n", program, out_name, strerror(errno));
	    done(EX_CANTCREAT);
    }
    create_database(temp_name);

    if (*files) {
	while (*files) {
	    FILE *f;

	    if (EQ(*files, "-")) {
		add_to_database(stdin);
		files++;
		continue;
	    }
	    f = fopen(*files, "r");
	    if (f == NULL) {
		(void) fprintf(stderr, "%s: cannot open ", program);
		(void) perror(*files);
		done(EX_NOINPUT);
	    }
	    add_to_database(f);
	    files++;
	}
    } else {
	add_to_database(stdin);
    }


    if (ypstamps) {
	add_yp_stamps();
    }

    if (! no_at_record) {
	add_ending_record();
    }

    rename_database(temp_name, out_name);

    if (verbose) {
	(void) printf("Added %d records, longest record was %d bytes\n",
		      reccnt, maxlen);
    }

    (void) dbmclose();
    exit(EX_OK);

    /* NOTREACHED */
}

static void
add_yp_stamps()
{
	datum key_lastmod, val_lastmod;
	datum key_master,  val_master;
	time_t now;
	char nows[32];

#ifdef HAVE_GETHOSTNAME
	char host[256];

	if(gethostname(host, sizeof(host)) < 0) {
		done(EX_DATAERR);
	}
#else
#ifdef HAVE_UNAME
	struct utsname utsnm;
	char *host;

	(void) uname(&utsnm);
	host = utsnm.nodename;
#else
#ifdef SITENAME_FILE
	char host[4096];
	FILE *f;
	char *p;

	f = fopen(SITENAME_FILE, "r");
	if (f == NULL) {
		fprintf(stderr, "%s: cannot open ", program);
		perror(SITENAME_FILE);
		done(EX_OSFILE);
	}
	host[0] = '\0';
	fgets(host, sizeof(host), f);
	if (host[0] == '\0') {
		fprintf(stderr, "%s: no hostname found in %s\n",
			program, SITENAME_FILE);
		exit(EX_OSFILE);
	}
	p = strchr(host, '\n');
	if (p)
		*p = '\0';
	fclose(f);
#else
	fprintf(stderr, "%s: No known method for computing hostname\n",
		program);
	done(EX_USAGE);
#endif	/* SITENAME_FILE */
#endif	/* HAVE_UNAME */
#endif	/* HAVE_GETHOSTNAME */

	(void) time(&now);
	sprintf(nows, "%10.10ld", (long) now);

	key_lastmod.dptr  = "YP_LAST_MODIFIED";
	key_lastmod.dsize = strlen(key_lastmod.dptr);
	val_lastmod.dptr  = nows;
	val_lastmod.dsize = strlen(val_lastmod.dptr);

	key_master.dptr   = "YP_MASTER_NAME";
	key_master.dsize  = strlen(key_master.dptr);
	val_master.dptr   = host;
	val_master.dsize  = strlen(val_master.dptr);

	if (store(key_lastmod, val_lastmod) < 0) {
		(void) fprintf(stderr, "%s: store failed for %s\n",
			       program, (char *) key_lastmod.dptr);
		done(EX_DATAERR);
	}

	if (store(key_master, val_master) < 0) {
		(void) fprintf(stderr, "%s: store failed for %s\n",
			       program, (char *) key_master.dptr);
		done(EX_DATAERR);
	}
}

/*
 * for errors or signals, remove the temp files
 */
static void
done(ex)
    int ex;
{
    (void) dbmclose();
    (void) unlink(temp_dir);
#ifndef DBM_SUFFIX
    (void) unlink(temp_pag);
#endif
    exit(ex);
}

/*
 * read through the arglist and turn argv into list of input files
 */
static char **
read_args(args)
    char **args;
{
    char **filev = args;
    char **filep = args;

    while (*args) {
	if ((*args)[0] == '-' && (*args)[1] != '\0') {
	    static char *end_arg = "";
	    char *s = &(*args)[1];

	    while (*s) {
		switch (*s++) {
		case 'y':
		    ypstamps = TRUE;
		    break;

		case 'f':
		    downcase = TRUE;
		    break;

		case 'v':
		    verbose = TRUE;
		    break;

		case 'n':
		    no_nul_byte = TRUE;
		    break;

		case 'd':
		    no_at_record = TRUE;
		    break;

		case 'o':
		    if (*s != '\0') {
			out_name = s;
			s = end_arg;
		    } else {
			out_name = *++args;
		    }
		    break;

		default:
		    (void) fprintf(stderr,
			      "Usage: %s [-yfvnd] [-o database] [file ...]\n",
				   program);
		    exit(EX_USAGE);
		}
	    }
	} else {
	    *filep++ = *args;
	}
	args++;
    }

    *filep = NULL;
    return filev;
}

/*
 * build a temp filename in the same directory as the given file
 */
static char *
build_temp_name(name)
    char *name;
{
    char *fn;
    size_t dirlen;
    char *slash = strrchr(name, '/');

    if (slash) {
	dirlen = slash - name;
	fn = xmalloc(dirlen + sizeof("/dbmXXXXXX"));
	(void) strncpy(fn, name, dirlen);
	(void) strcpy(fn + dirlen, "/dbmXXXXXX");
    } else {
	fn = xmalloc(sizeof("dbmXXXXXX"));
	strcpy(fn, "dbmXXXXXX");
    }
    return mktemp(fn);
}

/*
 * create a DBM database with the given basename
 */
static void
create_database(dbm_name)
    char *dbm_name;
{
#ifdef DBM_SUFFIX	/* indicates db(3) emulation of ndbm */
    temp_dir = xmalloc(strlen(dbm_name) + sizeof(DBM_SUFFIX));
    (void) sprintf(temp_dir, "%s%s", dbm_name, DBM_SUFFIX);
#endif

#if defined(HAVE_DBM) && !defined(HAVE_NDBM)
    mkdbfiles(dbm_name); /* ancient dbm won't create its own files */
#endif

    /* open the new DBM database */
    if (dbminit(dbm_name) < 0) {
	(void) fprintf(stderr, "%s: dbminit failed ", program);
	(void) perror(dbm_name);
	done(EX_TEMPFAIL);
    }
}

#if defined(HAVE_DBM) && !defined(HAVE_NDBM)
static void
mkdbfiles(dbm_name)
    char *dbm_name;
{
    int temp_len = strlen(dbm_name);

    temp_dir = xmalloc(temp_len + sizeof(".dir"));
    temp_pag = xmalloc(temp_len + sizeof(".pag"));
    (void) sprintf(temp_dir, "%s.dir", dbm_name);
    (void) sprintf(temp_pag, "%s.pag", dbm_name);

    fd = creat(temp_dir, 0644);
    if (fd < 0) {
	(void) fprintf(stderr, "%s: cannot create ", program);
	(void) perror(temp_dir);
	exit(EX_TEMPFAIL);
    }
    (void) close(fd);

    fd = creat(temp_pag, 0644);
    if (fd < 0) {
	(void) fprintf(stderr, "%s: cannot create ", program);
	(void) perror(temp_pag);
	(void) unlink(temp_dir);
	exit(EX_TEMPFAIL);
    }
    (void) close(fd);
}
#endif

/*
 * add all entries in the file to the DBM database
 */
static void
add_to_database(f)
    FILE *f;
{
    register char *s;

    while ((s = getline(f))) {
	register char *data;
	datum key, content;

	for (data = s; *data && *data != ':' && !isspace((int) *data); data++) {
	    ;
	}
	if (*data) {
	    *data++ = '\0';
	    key.dptr = s;
	    key.dsize = data - s - 1;
	    content.dptr = data;
	    content.dsize = strlen(data);
	    if (! no_nul_byte) {
		key.dsize++;
		content.dsize++;
	    }
	    if (downcase) {
		register char *sp;

		for (sp = s; *sp; sp++) {
		    *sp = tolower((int) *sp);
		}
	    }
	    if (store(key, content) < 0) {
		(void) fprintf(stderr, "%s: store failed for %s\n",
			       program, s);
		done(EX_DATAERR);
	    }
	    reccnt++;
	    if ((size_t) (content.dsize + key.dsize) > (size_t) maxlen) {
		maxlen = content.dsize + key.dsize;
	    }
	} else {
	    (void) fprintf(stderr, "%s: no value for key %s, ignored\n",
			   program, s);
	}
    }
}

/*
 * add the ending `@' record, to make sendmail happy.
 */
static void
add_ending_record()
{
    datum key, content;

    content.dptr = key.dptr = "@";
    content.dsize = key.dsize = (no_nul_byte? 1: 2);
    if (store(key, content) < 0) {
	(void) fprintf(stderr, "%s: store failed for end record\n", program);
    }
}

/*
 * read and return one line from the given file.
 *
 * return NULL on end of input.
 */
static char *
getline(f)
    register FILE *f;
{
    static struct str str;
    static int inited = FALSE;
    register int c;

    if (! inited) {
	STR_INIT(&str);
	inited = TRUE;
    } else {
	str.i = 0;
    }
    while ((c = getc(f)) != EOF && c != '\n') {
	STR_NEXT(&str, c);
    }
    if (c == EOF && str.i == 0) {
	return NULL;
    }
    STR_NEXT(&str, '\0');
    return str.p;
}

/*
 * rename the database from the old name to a new one.
 *
 * Allow some time after unlinking the old name so that smail will not
 * open the .dir file of the old database and the .pag file of the new
 * one accidentally.
 */
static void
rename_database(from, to)
    char *from;
    char *to;
{
    unsigned int fromlen = (unsigned int) strlen(from);
    unsigned int tolen = (unsigned int) strlen(to);
#ifdef DBM_SUFFIX
    char *from_dir = xmalloc(fromlen + sizeof(DBM_SUFFIX));
    char *to_dir = xmalloc(tolen + sizeof(DBM_SUFFIX));

    (void) sprintf(from_dir, "%s%s", from, DBM_SUFFIX);
    (void) sprintf(to_dir, "%s%s", to, DBM_SUFFIX);

#else /* !DBM_SUFFIX */

    char *from_dir = xmalloc(fromlen + sizeof(".dir"));
    char *to_dir = xmalloc(tolen + sizeof(".dir"));
    char *to_pag = xmalloc(tolen + sizeof(".pag"));
    char *from_pag = xmalloc(fromlen + sizeof(".pag"));

    (void) sprintf(from_dir, "%s.dir", from);
    (void) sprintf(from_pag, "%s.pag", from);
    (void) sprintf(to_dir, "%s.dir", to);
    (void) sprintf(to_pag, "%s.pag", to);

    (void) unlink(to_pag);

#endif /* DBM_SUFFIX */

    (void) unlink(to_dir);
    (void) sleep(2);			/* sleep at least 1 second */
    if (link(from_dir, to_dir) < 0) {
	(void) fprintf(stderr, "%s: cannot link %s to ", program, from_dir);
	(void) perror(to_dir);
	done(EX_CANTCREAT);
    }
#ifndef DBM_SUFFIX
    if (link(from_pag, to_pag) < 0) {
	(void) fprintf(stderr, "%s: cannot link %s to ", program, from_pag);
	(void) perror(to_pag);
	done(EX_CANTCREAT);
    }
    (void) unlink(from_pag);
#endif
    (void) unlink(from_dir);
}

/*
 * standalone versions of some referenced routines
 */
/*ARGSUSED*/
char *
qualify_domain(s)
    char *s GCC_UNUSED_HACK;
{
    return NULL;
}

void
freeze_message()
{
    return;
}

char *
bind_compute_domain ()
{
    return NULL;
}

int
rfc821_is_dot_string(s)
    char *s GCC_UNUSED_HACK;
{
    return 0;
}

int
rfc821_is_quoted_string(s)
    char *s GCC_UNUSED_HACK;
{
    return 0;
}

char *
bind_lookup_txt_rr(s, ep)
    char *s GCC_UNUSED_HACK;
    struct error **ep GCC_UNUSED_HACK;
{
    return NULL;
}

/*
 * standalone versions of some referenced variables
 */

enum er_proc error_processing = MAIL_BACK;
int force_zero_exitvalue = FALSE;
pid_t daemon_pid = 0xDEAD;
char *message_id = NULL;		/* unique ID for this message */
enum op_mode operation_mode = MODE_DEFAULT;	/* mode of operation */
char *spool_dir;			/* directory used to spool message */
char *spool_fn = NULL;			/* basename of open spool file */
int islocal = TRUE;
char *sender = "nsc!tron";
char *sender_name = "MasterControlProgram";
char *smtp_local_net = NULL;
char *local_sender;
uid_t real_uid = 0;
gid_t prog_egid = 0;

/* 
 * Local Variables:
 * c-file-style: "smail"
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1