ftp.nice.ch/pub/next/unix/mail/smail3.1.20.s.tar.gz#/smail3.1.20/src/lookup.c

This is lookup.c in view mode; [Download] [Up]

/* @(#)src/lookup.c	1.2 24 Oct 1990 05:23:27 */

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

/*
 * lookup.c:
 *	search for values corresponding to keys using a specified
 *	access method.
 *
 *	external functions:  open_database, close_database, lookup_database
 */

#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <errno.h>
#include "defs.h"
#ifdef	HAVE_NDBM
# include <ndbm.h>
#else
# ifdef	HAVE_DBM
#  undef NULL
#  include <dbm.h>
#  undef NULL			/* we aren't interested in dbm's NULL */
#  define NULL	0
# endif
#endif
#ifdef	UNIX_BSD
# include <sys/file.h>
#endif
#ifdef	HAVE_YP
# include <setjmp.h>
# include <rpcsvc/ypclnt.h>
#endif
#include "smail.h"
#include "lookup.h"
#include "dys.h"
#include "exitcodes.h"
#ifndef DEPEND
# include "extern.h"
# include "debug.h"
#endif

/* functions local to this file */
static int bsearch_open();
static void bsearch_close();
static int bsearch_lookup();

static int lsearch_open();
static void lsearch_close();
static int lsearch_lookup();

#if	defined(HAVE_DBM) || defined(HAVE_NDBM)
static int dbmbase_open();
static void dbmbase_close();
static int dbmbase_lookup();
#endif

#ifdef	HAVE_YP
static int yp_open();
static void yp_close();
static int yp_lookup();

static int aliasyp_open();
static void aliasyp_close();
static int aliasyp_lookup();
#endif

/*
 * the following structure defines the available access methods.
 * Methods are identified by name when open_database is called.
 */
static struct proto {
    char *proto;			/* name of the access method */
    int (*open)();			/* open database function */
    void (*close)();			/* close database function */
    int (*lookup)();			/* lookup in database function */
} protos[] = {
    { "bsearch",			/* binary search */
      bsearch_open, bsearch_close, bsearch_lookup },
    { "lsearch",
      lsearch_open, lsearch_close, lsearch_lookup },
#if	defined(HAVE_DBM) || defined(HAVE_NDBM)
    { "dbm",				/* DBM database search */
      dbmbase_open, dbmbase_close, dbmbase_lookup },
#endif
#ifdef	HAVE_YP
    { "yp",				/* YP remote database search */
      yp_open, yp_close, yp_lookup },
    { "aliasyp",			/* mail.aliases-style YP database */
      aliasyp_open, aliasyp_close, aliasyp_lookup },
#endif
};

/* point to end of protos table */
struct proto *end_protos = protos + sizeof(protos)/sizeof(protos[0]);

/* generic form of structure returned by database open calls */
struct generic_db {
    struct proto *proto;		/* access method */
};

/*
 * open_database - open a database of the specified type
 *
 * given a database name and an access method return a pointer to opaque
 * data that can be used to access that database.
 *
 * return:
 *   FILE_SUCCEED
 *		if the open was successful.  The database info will
 *		be stored in *db.
 *   FILE_AGAIN	if the open failed but may succeed at a later time
 *		(e.g., a remote host is down right now).  An error will
 *		be stored in *error.
 *   FILE_FAIL	if an unrecoverable failure occured.  An error will be
 *		stored in *error.
 *   FILE_NOMATCH
 *		if the database does not appear to exist.
 */
int
open_database(name, proto, retries, interval, statp, db, error)
    char *name;				/* name of database */
    char *proto;			/* access method name */
    int retries;			/* retry count */
    int interval;			/* retry interval */
    struct stat *statp;			/* return a stat buffer */
    char **db;				/* store open database info here */
    char **error;			/* store error message here */
{
    register struct proto *pp;

    for (pp = protos; pp < end_protos; pp++) {
	if (EQ(proto, pp->proto)) {
	    /* found the requested access method */
	    return (*pp->open)(name, pp, retries, interval, statp, db, error);
	}
    }

    /* access method was not found */
    *error = "unknown proto";
    return FILE_FAIL;
}

/*
 * close_database - close an open database and free its resources
 */
void
close_database(priv)
    char *priv;				/* database's private data */
{
    register struct generic_db *gp = (struct generic_db *)priv;

    (*gp->proto->close)(gp);
}

/*
 * lookup_database - find a value corresponding to a key
 *
 * given an open database, perform a lookup operation to find a match for
 * a given key.
 *
 * Return:
 *   DB_SUCCEED if the lookup was successful.  The matched value will
 * 		be stored in *value.
 *   DB_NOMATCH	if the lookup operation did not find a match for the key.
 *   DB_AGAIN	if the lookup operation failed but may succeed at a later
 *		time (e.g., a remote host is down right now).  An error
 *		will be stored in *error.
 *   DB_FAIL	if an unrecoverable failure occured.  An error will be
 *		stored in *error.
 *   FILE_AGAIN	if the lookup operation failed because of an error accessing
 *		the file.  The file should be considered unreachable until
 *		some later time.
 *   FILE_FAIL	if the lookup operation failed because of a permanent error
 *		accessing the file.  Retrying at a later time is not assumed
 *		to be possible.
 */
int
lookup_database(db, key, value, error)
    char *db;				/* open database */
    char *key;				/* search key */
    char **value;			/* store value here */
    char **error;			/* store error message here */
{
    register struct generic_db *gdb = (struct generic_db *)db;

    return (*gdb->proto->lookup)(gdb, key, value, error);
}

/*
 * bsearch access method:
 *
 *	access a file containing sorted lines of data.  keys
 *	are at the start of each line followed by a colon and/or
 *	white space.
 */

/* private data: */
struct bsearch_db {
    struct proto *proto;		/* access method table entry */
    char *name;				/* name of file */
    FILE *f;				/* open file */
    long size;				/* size of file */
};

/* bsearch_open - open a sorted file */
static int
bsearch_open(name, proto, retries, interval, statp, db, error)
    char *name;				/* name of file */
    struct proto *proto;		/* access method */
    int retries;			/* retry count */
    int interval;			/* retry interval */
    struct stat *statp;			/* save stat results here */
    char **db;				/* store open database info here */
    char **error;			/* store error message here */
{
    register struct bsearch_db *priv;
    register FILE *f;

    name = make_lib_fn(name);
    if (name == NULL) {
	*error = "No directory for file";
	return FILE_FAIL;
    }
    f = fopen(name, "r");
    if (f == NULL) {
	int left = retries;

	while (left-- > 0) {
	    (void) sleep(interval);
	    if (f = fopen(name, "r")) break;
	}
	if (f == NULL) {
	    if (errno == ENOENT) {
		return FILE_NOMATCH;
	    }
	    *error = strerrno();
	    return FILE_FAIL;
	}
    }

#ifdef lock_fd_rd_wait
    if (lock_fd_rd_wait(fileno(f)) < 0) {
	return FILE_AGAIN;
    }
#endif

    /* seek to the end of the file */
    (void) fseek(f, 0L, 2);

    /* build the private data */
    priv = (struct bsearch_db *)xmalloc(sizeof(*priv));
    priv->proto = proto;
    priv->name = name;
    priv->f = f;
    priv->size = ftell(f);		/* remember the fseek() */
    if (statp) {
	(void) fstat(fileno(f), statp);
    }

    *db = (char *)priv;
    return FILE_SUCCEED;
}

/* bsearch_close - close the file */
static void
bsearch_close(db)
    struct bsearch_db *db;
{
    (void) fclose(db->f);
    xfree((char *)db);
}

/*
 * bsearch_lookup - look up key in ascii sorted key/data line database.
 *
 * This routine taken from smail version 2.3, though it has been
 * modified to work with the new version and has also been
 * generalized from the original routine getpath().
 */
/*ARGSUSED*/
static int
bsearch_lookup(db, key, value, error)
    register struct bsearch_db *db;
    char *key;
    char **value;
    char **error;
{
    long middle, hi, lo;
    int c;
    int flag;
    int len = strlen(key);		/* length of comparison */
    static struct str str;		/* string in which to store data */
    static int str_inited = FALSE;	/* TRUE if str has been STR_INIT'd */
    int i;				/* temp */

    DEBUG1(DBG_DRIVER_HI, "bsearch_lookup: looking for <%s>\n", key);

    if (!str_inited) {
	/*
	 * note, the string is reused in each call to bsearch.
	 */
	str_inited = TRUE;
	STR_INIT(&str);
    }

    lo = 0;
    hi = db->size;

    /*
     * "Binary search routines are never written right the first time around."
     * - Robert G. Sheldon.
     * << above comment retained 'cause I thought it was cute -- tron >>
     */
    for( ;; ) {
	int cnt;

	middle = (hi + lo + 1)/2;
	(void) fseek(db->f, middle, 0); /* find midpoint */
	if (middle != 0) {		/* to beginning of next line */
	    while((c = getc(db->f)) != EOF && c != '\n') ;
	    if (c == EOF && ferror(db->f)) {
		*error = strerrno();
		return FILE_FAIL;
	    }
	}
	str.i = 0;
	cnt = 0;
	while (cnt <= len && (c = getc(db->f)) != EOF && c != '\n') {
	    STR_NEXT(&str, c);
	    cnt++;
	}
	if (c == EOF && ferror(db->f)) {
	    *error = strerrno();
	    return FILE_FAIL;
	}
	STR_NEXT(&str, '\0');
	flag = strncmpic(str.p, key, len);
	if (flag == 0) {
	    /* make sure the names are the same length */
	    if (str.p[len] == ':' || isspace(str.p[len])) {
		break;			/* found it */
	    }
	    flag = 1;			/* name is longer than target */
	}
	if ( lo>=middle ) {		/* failure? */
	    return DB_NOMATCH;
	}
	if ( c != EOF && flag < 0 ) {	/* close window */
	    lo = middle;
	} else {
	    hi = middle - 1;
	}
    }

    /*
     * Now just copy the result.
     */
    i = -1;				/* index to last non-space */
    str.i = 0;				/* clear out the region */
    while(((c  = getc(db->f)) != EOF) && (c != '\n')) {
	if (!isspace(c)) {
	    if (c == '#') {
		/* comment puts an end to the entry */
		break;
	    }
	    i = str.i;
	}
	STR_NEXT(&str, c);
    }
    if (c == EOF && ferror(db->f)) {
	*error = strerrno();
	return FILE_FAIL;
    }
    str.i = i + 1;			/* backup to last interesting char */
    /* backup to last non-space character */
    STR_NEXT(&str, '\0');

    DEBUG2(DBG_DRIVER_HI, "bsearch_lookup: found <%s> for <%s>\n", str.p, key);
    *value = str.p;
    return DB_SUCCEED;
}

/*
 * lsearch access method:
 *
 *	access a file containing lines of data, in the format expected
 *	by read_entry() in parse.c.  Keys are at the start of each line
 *	followed by a colon and/or white space.
 */

/* private data: */
struct lsearch_db {
    struct proto *proto;		/* access method table entry */
    char *name;				/* name of file */
    FILE *f;				/* open file */
};

/* lsearch_open - open a file */
static int
lsearch_open(name, proto, retries, interval, statp, db, error)
    char *name;				/* name of file */
    struct proto *proto;		/* access method */
    int retries;			/* retry count */
    int interval;			/* retry interval */
    struct stat *statp;			/* save stat results here */
    char **db;				/* store open database info here */
    char **error;			/* store error message here */
{
    register struct lsearch_db *priv;
    register FILE *f;

    name = make_lib_fn(name);
    if (name == NULL) {
	*error = "No directory for file";
	return FILE_FAIL;
    }
    f = fopen(name, "r");
    if (f == NULL) {
	int left = retries;

	while (left-- > 0) {
	    (void) sleep(interval);
	    if (f = fopen(name, "r")) break;
	}
	if (f == NULL) {
	    if (errno == ENOENT) {
		return FILE_NOMATCH;
	    }
	    *error = strerrno();
	    return FILE_FAIL;
	}
    }

#ifdef lock_fd_rd_wait
    if (lock_fd_rd_wait(fileno(f)) < 0) {
	return FILE_AGAIN;
    }
#endif

    /* build the private data */
    priv = (struct lsearch_db *)xmalloc(sizeof(*priv));
    priv->proto = proto;
    priv->name = name;
    priv->f = f;
    if (statp) {
	(void) fstat(fileno(f), statp);
    }

    *db = (char *)priv;
    return FILE_SUCCEED;
}

/* lsearch_close - close the file */
static void
lsearch_close(db)
    struct lsearch_db *db;
{
    (void) fclose(db->f);
    xfree((char *)db);
}

/*
 * lsearch_lookup - look up key with a linear search
 */
/*ARGSUSED*/
static int
lsearch_lookup(db, key, value, error)
    register struct lsearch_db *db;
    char *key;
    char **value;
    char **error;
{
    int len = strlen(key);		/* length of comparison */
    register char *entry;		/* entry from read_entry() */
    register char *p;			/* temp */

    DEBUG1(DBG_DRIVER_HI, "lsearch_lookup: looking for <%s>\n", key);

    /* always start from the beginning of the file */
    (void) fseek(db->f, 0L, 0);

    while (entry = read_entry(db->f)) {
	if (strncmpic(entry, key, len) == 0 &&
	    (entry[len] == ':' || isspace(entry[len])))
	{
	    char *ret;			/* value to be returned */

	    /* found a matching entry in the file, find the data */
	    entry += len;
	    /*
	     * skip <whitespace>:<whitespace>
	     */
	    while (isspace(*entry)) entry++;
	    if (*entry == ':') {
		entry++;
		while (isspace(*entry)) entry++;
	    }

	    /*
	     * skip comments
	     */
	    while (*entry == '#') {
		while (*entry && *entry != '\n') entry++;
		while (isspace(*entry)) entry++;
	    }
	    ret = entry;		/* return from this point on */

	    /* though do some more processing to the remainder */
	    p = entry - 1;
	    /* find the last character which is not white-space or comment */
	    while (*entry) {
		if (!isspace(*entry)) {
		    if (*entry == '#') {
			while (*entry && *entry != '\n') entry++;
			continue;
		    }
		    p = entry;
		}
		entry++;
	    }
	    /* throw away after the last interesting character */
	    p[1] = '\0';

	    DEBUG1(DBG_DRIVER_HI, "lsearch_lookup: return <%s>\n", ret);

	    *value = ret;
	    return DB_SUCCEED;
	}
    }
    if (ferror(db->f)) {
	*error = strerrno();
	return FILE_FAIL;
    }
    DEBUG1(DBG_DRIVER_HI, "lsearch_lookup: did not find <%s>\n", key);
    return DB_NOMATCH;
}

#if	defined(HAVE_DBM) || defined(HAVE_NDBM)
/*
 * dbmbase access method:
 *
 *	access a database stored as a DBM database.  Note that the
 *	DBM semantics only allow for one DBM file in the life of
 *	a process.  As a result, these files should never be closed
 *	and only one reference to a DBM-type database can exist in
 *	the application.
 *
 *	If NDBM is being used, then multiple databases can be used.
 *
 *	BUGS:  extensive use of #ifdef within functions is ugly.
 */

#ifdef	HAVE_NDBM
/*
 * form for private data:
 */
struct dbmbase_db {
    struct proto *proto;		/* access method table entry */
    char *name;				/* name of database */
    DBM *db;				/* open database */
};

#else	/* not HAVE_NDBM */
/* private data:
 *   NOTE:  we only allow for one data structure, which is held
 *	    as a static object. */
static struct dbmbase_db {
    struct proto *proto;		/* access method table entry */
    char *name;				/* name of database */
    struct stat statbuf;		/* stat on .pag part of database */
} dbmbase_private = {
    NULL, NULL,				/* initialize to unopened state */
};

#endif	/* not HAVE_NDBM */

/* dbmbase_open - open a DBM database */
static int
dbmbase_open(name, proto, retries, interval, statp, db, error)
    char *name;				/* name of database */
    struct proto *proto;		/* access method */
    int retries;			/* retry count */
    int interval;			/* retry interval */
    struct stat *statp;			/* return a stat structure */
    char **db;				/* store open database info here */
    char **error;			/* store error message here */
{
    char *pag_file;			/* name of .pag file */
    register struct dbmbase_db *priv;

    name = make_lib_fn(name);
    if (name == NULL) {
	*error = "No directory for file";
	return FILE_FAIL;
    }
#ifdef	HAVE_NDBM
    priv = (struct dbmbase_db *)xmalloc(sizeof(*priv));
#else	/* not HAVE_NDBM */
    priv = &dbmbase_private;
    if (priv->name && !EQ(priv->name, name)) {
	*error = "Can't have multiple DBM databases";
	return FILE_FAIL;
    }

    if (priv->name == NULL) {
#endif	/* not HAVE_NDBM */

	if (
#ifdef	HAVE_NDBM
	    (priv->db = dbm_open(name, 0)) == NULL
#else
	    dbminit(name) < 0
#endif
	    )
	{
	    int succeed = FAIL;
	    int left = retries;

	    if (left < 1) {
		/* DBM databases cannot be moved atomicly, so require
		 * at least two retries */
		left = 2;
	    }
	    if (interval < 2) {
		/* require a somewhat reasonable interval as well */
		interval = 2;
	    }

	    while (left-- > 0) {
		(void) sleep(interval);
#ifdef	HAVE_NDBM
		if (priv->db = dbm_open(name, 0)) {
		    succeed = SUCCEED;
		    break;
		}
		if (errno != ENOENT) {
		    break;
		}
#else
		if ((succeed = dbminit(name)) >= 0) {
		    break;
		}
#endif
	    }
	    if (succeed != DB_SUCCEED) {
		*error = strerrno();
		return FILE_FAIL;
	    }
	}

#ifdef	HAVE_NDBM
	/* if required, get a stat structure from the .pag file */
	if (statp) {
	    pag_file = xmalloc(strlen(name) + sizeof(".pag"));
	    (void) sprintf(pag_file, "%s.pag", name);
	    (void) stat(pag_file, statp);
	    xfree(pag_file);
	}
	priv->proto = proto;
	priv->name = name;
	*db = (char *)priv;
#ifdef lock_fd_rd_wait
	if (lock_fd_rd_wait(dbm_pagfno(priv->db)) < 0) {
	    return FILE_AGAIN;
	}
#endif
	return FILE_SUCCEED;
#else	/* not HAVE_NDBM */
	/* get a stat structure from the .pag file */
	pag_file = xmalloc(strlen(name) + sizeof(".pag"));
	(void) sprintf(pag_file, "%s.pag", name);
	(void) stat(pag_file, &priv->statbuf);
	xfree(pag_file);
    }

    priv->proto = proto;
    priv->name = name;
    if (statp) {
	*statp = priv->statbuf;
    }
    *db = (char *)priv;
    return FILE_SUCCEED;
#endif	/* not HAVE_NDBM */
}

/* dbmbase_close - if not NDBM don't close the database, since we can't */
/*ARGSUSED*/
static void
dbmbase_close(db)
    struct dbmbase_db *db;
{
#ifdef	HAVE_NDBM
    (void) dbm_close(db->db);
    xfree((char *)db);
#endif
    return;
}

/* dbmbase_lookup - call on the DBM routines to find that data */
/*ARGSUSED*/
static int
dbmbase_lookup(db, key, value, error)
    register struct dbmbase_db *db;
    char *key;
    char **value;
    char **error;
{
    datum the_key;
    datum the_value;
    static int temp_size;		/* size of temp_key area */
    static char *temp_data = NULL;	/* growable temp area */
    int len;				/* length of key */
    register char *p;

    /*
     * convert the key to lower case, as fetch() is case-sensitive
     * for efficiency, keep around the malloc'd region used to store
     * the down-cased key.
     */
    len = strlen(key) + 1;
    if (temp_data == NULL) {
	temp_size = len;
	temp_data = xmalloc(temp_size);
    } else if (temp_size < len) {
	temp_size = len;
	temp_data = xrealloc(temp_data, temp_size);
    }
    for (p = temp_data; *key; p++, key++) {
	*p = lowercase(*key);
    }
    *p = '\0';

    the_key.dptr = temp_data;
    the_key.dsize = len;
#ifdef	HAVE_NDBM
    the_value = dbm_fetch(db->db, the_key);
    if (dbm_error(db->db)) {
	return FILE_FAIL;
    }
#else
    the_value = fetch(the_key);
#endif

    if (the_value.dptr) {
	if (temp_size < the_value.dsize + 1) {
	    temp_size = the_value.dsize + 1;
	    temp_data = xrealloc(temp_data, temp_size);
	}
	(void) strcpy(temp_data, the_value.dptr);
	*value = temp_data;
	return DB_SUCCEED;
    }
    return DB_NOMATCH;
}
#endif	/* HAVE_DBM || HAVE_NDBM */

#ifdef	HAVE_YP
/*
 * The YP database routines takes names of the form:
 *
 *	domain:database_name
 * or	database_name
 *
 * in the former case, the `domain' specified is the YP domain to use
 * for yp_match operations.  In the second case, the default YP domain
 * is used.
 *
 * There are two forms for the lookup: regular yp and aliasyp.  The
 * second form is used for accessing the standard Sun mail.aliases map
 * format, which does not fit the form of other YP maps.  The
 * difference is that a nul-byte is counted in the length of a key for
 * mail.aliases, while it is not counted for other maps.
 */

/*
 * form for private data:
 */
struct yp_db {
    struct proto *proto;		/* access method table entry */
    char *map;				/* name of database */
    char *domain;			/* yp domain */
};

static char *default_yp_domain = NULL;	/* from yp_get_default_domain(3N) */

#ifdef notyet
static jmp_buf alarm_jmp;		/* jump here on SIGALRM */

static void yp_sigalrm();		/* catch SIGALRM for YP timeouts */

#define YP_TIMEOUT	30		/* 30 second timeout for YP */
#endif

/*
 * yp_open, aliasyp_open - create a yp_private structure for a database
 *
 * yp_order(3N) is called to verify that the database is accessible.
 */
static int
aliasyp_open(name, proto, retries, interval, statp, db, error)
    char *name;				/* name of database */
    struct proto *proto;		/* access method */
    int retries;			/* retry count */
    int interval;			/* retry interval */
    struct stat *statp;			/* return a stat structure */
    char **db;				/* store open database info here */
    char **error;			/* store error message here */
{
    return yp_open(name, proto, retries, interval, statp, db, error);
}

/*ARGSUSED*/
static int
yp_open(name, proto, retries, interval, statp, db, error)
    char *name;				/* name of database */
    struct proto *proto;		/* access method */
    int retries;			/* retry count */
    int interval;			/* retry interval */
    struct stat *statp;			/* return a stat structure */
    char **db;				/* store open database info here */
    char **error;			/* store error message here */
{
    register struct yp_db *priv;
    register char *p;
    int err;				/* error from yp functions */
    int order;				/* output from yp_order */
    char *domain;
#ifdef notyet
    int save_time;			/* saved value from alarm() */
    void (*save_sigalrm)();		/* previous SIGALRM handler */
#endif

    priv = (struct yp_db *)xmalloc(sizeof(*priv));

    /* is a YP domain specified? */
    p = index(name, ':');
    if (p == NULL) {
	/* no, use the default YP domain */
	priv->domain = NULL;
	priv->map = name;
    } else {
	/* yes, make a copy and use it */
	priv->domain = xmalloc(p - name + 1);
	(void) memcpy(priv->domain, name, p - name);
	priv->domain[p - name] = '\0';
	priv->map = p + 1;
    }

    /* if stat required, just zero it out, there is nothing to put there */
    if (statp) {
	(void) bzero((char *)statp, sizeof(*statp));
    }

    if (priv->domain) {
	domain = priv->domain;
    } else {
	if (default_yp_domain == NULL &&
	    (err = yp_get_default_domain(&default_yp_domain)))
	{
	    /* this should only fail if the domainname is not set, right? */
	    *error = yperr_string(err);
	    return FILE_FAIL;
	}
	domain = default_yp_domain;
    }

#ifdef notyet
    save_time = alarm(0);

    /* verify that we can access the database */
    if (setjmp(alarm_jmp)) {
	(void) signal(SIGALRM, save_sigalrm);
	(void) alarm(save_time);

	*error = "YP timeout";
	return FILE_AGAIN;
    } else {

	/* arrange to timeout if the operation blocks */
	save_sigalrm = (void (*)())signal(SIGALRM, yp_sigalrm);
	(void) alarm(YP_TIMEOUT);
#endif

	/* potentially blocking operation */
	err = yp_order(domain, priv->map, &order);

#ifdef notyet
	/* restore previous alarm setting */
	(void) signal(SIGALRM, save_sigalrm);
	(void) alarm(save_time);
#endif

	if (err) {
	    /* analyze the reason for the failure */
	    *error = yperr_string(err);
	    switch (err) {
	    case YPERR_RPC:		/* cases where failure is temporary */
	    case YPERR_YPERR:
	    case YPERR_RESRC:
	    case YPERR_PMAP:
	    case YPERR_YPBIND:
	    case YPERR_YPSERV:
		return FILE_AGAIN;

	    case YPERR_MAP:		/* no such map */
	    case YPERR_DOMAIN:		/* no such domain */
		return FILE_NOMATCH;
	    }
	    return FILE_FAIL;
	}
#ifdef notyet
    }
#endif

    priv->proto = proto;
    *db = (char *)priv;
    return FILE_SUCCEED;
}

/* yp_close, aliasyp_close - free up data allocated for to a YP database */
static void
aliasyp_close(db)
    struct yp_db *db;
{
    yp_close(db);
}

/*ARGSUSED*/
static void
yp_close(db)
    struct yp_db *db;
{
    if (db->domain) {
	xfree(db->domain);
    }
    xfree((char *)db);
    return;
}

/* yp_lookup, aliasyp_lookup - call on the YP routines to find that data */
static int
yp_lookup(db, key, value, error)
    register struct yp_db *db;		/* open YP database */
    char *key;				/* search key */
    char **value;			/* return value here */
    char **error;			/* return error message here */
{
    return common_yp_lookup(db, key, value, error, FALSE);
}

static int
aliasyp_lookup(db, key, value, error)
    register struct yp_db *db;		/* open YP database */
    char *key;				/* search key */
    char **value;			/* return value here */
    char **error;			/* return error message here */
{
    return common_yp_lookup(db, key, value, error, TRUE);
}

/*ARGSUSED*/
static int
common_yp_lookup(db, key, value, error, aliasflag)
    register struct yp_db *db;		/* open YP database */
    char *key;				/* search key */
    char **value;			/* return value here */
    char **error;			/* return error message here */
    int aliasflag;			/* TRUE for Sun mail.aliases format */
{
    int keylen;				/* length of key */
    static char *temp_key = NULL;	/* area for lower-case conversion */
    static int temp_size;		/* size of temp area */
    register char *p;
    char *matchval;			/* matched data */
    int matchlen;			/* length of matched data */
    int err;				/* yp error */
    char *domain;
#ifdef notyet
    int save_time;			/* saved value from alarm() */
    void (*save_sigalrm)();		/* previous SIGALRM handler */
#endif

    /*
     * convert the key to lower case, as fetch() is case-sensitive
     * for efficiency, keep around the malloc'd region used to store
     * the down-cased key.
     */
    keylen = strlen(key);
    if (temp_key == NULL) {
	temp_size = keylen + 1;
	temp_key = xmalloc(temp_size);
    } else if (temp_size <= keylen) {
	temp_size = keylen + 1;
	temp_key = xrealloc(temp_key, temp_size);
    }
    for (p = temp_key; *key; p++, key++) {
	*p = lowercase(*key);
    }
    *p = '\0';

    if (db->domain) {
	domain = db->domain;
    } else {
	domain = default_yp_domain;
    }

    if (aliasflag) {
	keylen++;
    }

#ifdef notyet
    save_time = alarm(0);

    if (setjmp(alarm_jmp)) {
	(void) signal(SIGALRM, save_sigalrm);
	(void) alarm(save_time);

	*error = "YP timeout";
	return FILE_AGAIN;
    } else {

	/* arrange to timeout if the operation blocks */
	save_sigalrm = (void (*)())signal(SIGALRM, yp_sigalrm);
	(void) alarm(YP_TIMEOUT);
#endif

	/* potentially blocking operation */
	err = yp_match(domain, db->map, temp_key, keylen,
		       &matchval, &matchlen);

#ifdef notyet
	/* restore previous alarm setting */
	(void) signal(SIGALRM, save_sigalrm);
	(void) alarm(save_time);
#endif

	if (err) {
	    /* analyze the reason for the failure */
	    *error = yperr_string(err);
	    switch (err) {
	    case YPERR_RPC:		/* cases where failure is temporary */
	    case YPERR_DOMAIN:
	    case YPERR_YPERR:
	    case YPERR_RESRC:
	    case YPERR_PMAP:
	    case YPERR_YPBIND:
	    case YPERR_YPSERV:
		return FILE_AGAIN;

	    case YPERR_KEY:		/* key not in database */
		return DB_NOMATCH;
	    }
	    return FILE_FAIL;
	}
#ifdef notyet
    }
#endif

    matchval[matchlen] = '\0';		/* we don't want the extra newline */
    *value = matchval;
    return DB_SUCCEED;
}

#ifdef notyet
static void
yp_sigalrm()
{
    longjmp(alarm_jmp, 1);
}
#endif

#endif	/* HAVE_YP */

#ifdef STANDALONE

int debug = 0;
FILE *errfile = stderr;

void
main(argc, argv)
    int argc;
    char **argv;
{
    char *program = (--argc, *argv++);
    char *proto;
    char *name;
    char *db;
    char *error;
    int interval = 3;
    int retries = 0;
    int success;

    for (;;) {
	if (*argv && EQ(*argv, "-r")) {
	    argv++;
	    retries = atoi(*argv++);
	    argc -= 2;
	    continue;
	}
	if (*argv && EQ(*argv, "-i")) {
	    argv++;
	    interval = atoi(*argv++);
	    argc -= 2;
	    continue;
	}
	break;
    }

    if (argc < 3) {
	fprintf(stderr,
		"Usage: %s [-r #retries] [-i interval] proto name key ...\n",
		program);
	exit(EX_USAGE);
    }

    proto = *argv++;
    name = *argv++;

    success = open_database(name, proto, retries, interval,
			    (struct stat *)NULL, &db, &error);
    switch (success) {
    case DB_AGAIN:
	fprintf(stderr, "%s: try again later: %s\n", program, error);
	exit(EX_TEMPFAIL);

    case DB_FAIL:
	fprintf(stderr, "%s: open failed: %s\n", program, error);
	exit(EX_UNAVAILABLE);
    }

    while (*argv) {
	char *value;

	switch (lookup_database(db, *argv, &value, &error)) {
	case DB_AGAIN:
	    fprintf(stderr, "%s: %s: try again later: %s\n",
		    program, *argv, error);
	    break;

	case DB_FAIL:
	    fprintf(stderr, "%s: %s: failed: %s\n", program, *argv, error);
	    break;

	case DB_NOMATCH:
	    fprintf(stderr, "%s: %s: no match\n", program, *argv);
	    break;

	case DB_SUCCEED:
	    printf("%s --> %s", *argv, value);
	    break;
	}
	argv++;
    }

    close_database(db);
    exit(EX_OK);
}

char *
xmalloc(size)
    int size;
{
    char *malloc();

    return malloc(size);
}

char *
xrealloc(region, size)
    char *region;
    int size;
{
    char *realloc();

    return realloc(region, size);
}

void
xfree(region)
    char *region;
{
    (void) free(region);
}
#endif	/* STANDALONE */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.