static char rcsid[] = "@(#)$Id: elmalias.c,v 1.19 2006/04/09 07:37:30 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.19 $   $State: Exp $
 *
 *  Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI> 
 *                           (was hurtta+elm@ozone.FMI.FI)
 ******************************************************************************
 *  The Elm Mail System 
 *
 * 			Copyright (c) 1988-1992 USENET Community Trust
 * 			Copyright (c) 1986,1987 Dave Taylor
 *****************************************************************************/

/*
 * elmalias - Elm alias database access.
 *
 * This utility will display information from the Elm user and system
 * alias databases.  It will expand alias names specified on the command
 * line and display the alias value.  If an item on the command line is
 * not a valid alias, then it's value is returned.  If no names are
 * specified on the command line then all alises are displayed.  Output
 * is formatted by either a user-defined format specifier or one of a
 * number of predefined specifications.  The default output format
 * displays just the alias value.
 *
 * The format specifiers are:
 *
 *	%a	alias (the alias name)
 *	%l	last_name
 *	%n	name
 *	%c	comment
 *	%v	address (the alias value)
 *	%t	type (Person, Group, or Unknown)
 *
 * In the case that a command line argument does not correspond to
 * an alias, then %a and %v will evaluate to the argument value and
 * all other items will be empty.
 *
 * Printf-like field widths may be used, e.g. "%-20.20a".  Conditionals
 * are bracketed by question marks.  For example "?n(%n)?" means format
 * "(%n)" if the "name" is defined, otherwise produce no output.  Some
 * backslash sequences (e.g. "\t" == tab) are recognized.  Any other
 * characters are displayed as is.
 *
 * Example:
 *	$ elmalias -f "alias %a is address \"%v\" ?n(%n)?" chip
 *	alias chip is address "chip@chinacat.unicom.com" (Chip Rosenthal)
 *
 * Synopsis:
 *	elmalias [-adensuvV] [-f format] [alias ...]
 *
 * Options:
 *	-a	Display alias name.  Equivalent to -f "%-20.20a %v".
 *	-d	Turn debugging on. Only useful if DEBUG is defined.
 *	-e	Fully expand alias values.
 *	-f fmt	User-specified output format.
 *	-n	Display name.  Equivalent to -f "%v?n (%n)?".
 *	-r	Complain about arguments that are not valid aliases.
 *	-s	Consult system-wide alias file.
 *	-u	Consult user-specific alias file.
 *	-v	Verbose output.  Equivalent to -f "%-20.20a %v?n (%n)?".
 *	-V	Babble about *everything* we known about.
 *
 * If neither -s or -u are specified, then the default is to search
 * both alias files.
 */

#include "elmutil.h"
#include "ndbz.h"
#include "s_elmalias.h"
#include <pwd.h>


/*
 * Maximum number of alias files we can consult.
 */
#define MAXDB		2	/* user and system alias files		*/

/*
 * These are used by the "dbz" routines.
 */

char *Progname;

/*
 * "aliasdb" library routines.
 */

/*
 * Local procedures.
 */
static DBZ *open_user_aliases P_((void));
static DBZ *open_system_aliases P_((void));
static struct alias_rec *make_dummy_rec P_((char *val));
static void exp_print_alias P_((DBZ *dblist[], char *fmt,  
				struct alias_rec *ar));
static void print_alias P_((char *fmt, struct alias_rec *ar));
static char *sel_alias_mem P_((struct alias_rec *ar,
			       int sel));

static void usage_error P_((void));
static void usage_error()
{
    lib_error(CATGETS(elm_msg_cat, ElmaliasSet, ElmaliasUsage,
		      "usage: %s [-adenrsuvV] [-f format] [alias ...]\n"), 
	      Progname);
    exit(1);
    /*NOTREACHED*/
}


static void malloc_fail_handler P_((char *proc, unsigned size));


/*ARGSUSED*/
static void malloc_fail_handler(proc, size)
     char *proc;
     unsigned size;
{
   /* NOTE:
       Can't use elm_fprintf (or routines of lib/output.c)
       because here also malloc may fail, therefore
       can not use CATGETS macro;
       And can't use catgets because it may have given 
       incorrent format string from catalog ...
    */

      fprintf(stderr,"%s: out of memory [could not allocate %d bytes]\n", 
	      Progname, size);
      exit(1);
    /*NOTREACHED*/
}

int main P_((int argc, char *argv[]));
int main(argc, argv)
     int argc;
     char *argv[];
{
    char *out_fmt;		/* output printing format		*/
    int do_user_alias;		/* TRUE to examine user alias file	*/
    int do_system_alias;	/* TRUE to examine system alias file	*/
    int do_expand;		/* TRUE to recursively expand aliases	*/
    int do_complain;		/* TRUE to insist args are valid aliases*/
    int numdb;			/* number of alias files to consult	*/
    DBZ *dblist[MAXDB+1];	/* NULL terminated list of files	*/
    struct alias_rec *ar;	/* scratch ptr to expansion of curr name*/
    int d, i;
    extern int optind;
    extern char *optarg;

    /*
     * Initialize.
     */

#if DEBUG
    init_debugfile("ELMALIAS");
#endif
    locale_init();

    safe_malloc_fail_handler =	/* install procedure to trap errors in	*/
	malloc_fail_handler;	/*   the safe_malloc() routines		*/
    Progname = argv[0];		/* program name for diag messages	*/
    do_user_alias = FALSE;	/* indicate the user hasn't selected	*/
    do_system_alias = FALSE;	/*   any alias files (yet)		*/
    do_expand = FALSE;		/* do not recursively expand groups	*/
    do_complain = FALSE;	/* allow non-aliases on cmd line	*/
    out_fmt = "%v";		/* default is to just show alias value	*/
    numdb = 0;			/* no alias files opened yet		*/

    user_init();
    init_defaults();
    read_rc_file(0);

    /*
     * Crack command line options.
     */
    while ((i = getopt(argc, argv, "ad:ef:nrsuvV")) != EOF) {
	switch (i) {
	case 'a':			/* show alias name and value	*/
	    out_fmt = "%-20.20a %v";
	    break;
	
	case 'd' : 
#if DEBUG
	  set_debugging(optarg);	  
#else
 	  lib_error(CATGETS(elm_msg_cat, ElmaliasSet, ElmaliasArgsIngoringDebug,
			    "Warning: system created without debugging enabled - request ignored\n"));
#endif
	  break;
	case 'e':			/* recursively expand aliases	*/
	    do_expand = TRUE;
	    break;
	case 'f':			/* user-specified format	*/
	    out_fmt = optarg;
	    break;
	case 'n':			/* show alias value and fullname*/
	    out_fmt = "%v?n (%n)?";
	    break;
	case 'r':			/* insist args are valid aliases*/
	    do_complain = TRUE;
	    break;
	case 's':			/* use system-wide alias file	*/
	    do_system_alias = TRUE;
	    break;
	case 'u':			/* use per-user alias file	*/
	    do_user_alias = TRUE;
	    break;
	case 'v':			/* verbose output format	*/
	    out_fmt = "%-20.20a %v?n (%n)?";
	    break;
	case 'V':			/* show the user's life story	*/
	    out_fmt = "\
Alias:\t\t%a\n\
  Address:\t%v\n\
  Type:\t\t%t\n\
?n  Name:\t\t%n\n?\
?l  Last Name:\t%l\n?\
?c  Comment:\t%c\n?\
";
	    break;
	default:
	    usage_error();
	    /*NOTREACHED*/
	}
    }

    elm_sfprintf(version_buff, sizeof version_buff,
		 FRM("%s PL%s"), VERSION, PATCHLEVEL);

#ifdef DEBUG
    { 
	int d = panic_dprint("\n\
======================================================\n\
Debug output of the ELMALIAS program (version %s).\n",
			     version_buff);

	if (d >= 50) {
#if 0	
	    panic_dprint("WARNING: Edit manually out sensitive information from that file!\n");
    
	    lower_prompt("WARNING: Debug file may include passwords -- edit it!");
	    sleep(5+sleepmsg);	    
#endif
	}
    }
#endif


    /*
     * If user didn't request specific alias files then use them all.
     */
    if (!do_system_alias && !do_user_alias)
	do_system_alias = do_user_alias = TRUE;

    /*
     * Open up the alias files we need to access, in the order of priority.
     */
    if (do_user_alias && (dblist[numdb] = open_user_aliases()) != NULL)
	++numdb;
    if (do_system_alias && (dblist[numdb] = open_system_aliases()) != NULL)
	++numdb;
    dblist[numdb] = NULL;

    /*
     * If no names specified on command line then dump all alias files..
     */
    if (optind == argc) {
	if (do_expand) {
	  lib_error(CATGETS(elm_msg_cat, ElmaliasSet,
			    ElmaliasCannotSpecifyExpand,
			    "%s: cannot specify \"-e\" when dumping all aliases\n"),
		    Progname);
	  exit(1);
	}
	for (d = 0 ; dblist[d] != NULL ; ++d) {
	    /* assumes file pointer left at first key immediately after open */
	    while ((ar = fetch_alias(dblist[d], (char *)NULL)) != NULL) {
		print_alias(out_fmt, ar);
		(void) free((malloc_t)ar);
	    }
	}
	exit(0);
    }

    /*
     * Expand each name on the command line.
     */
    for (i = optind ; i < argc ; ++i) {

	/*
	 * Try each of the alias files for a match.
	 */
	ar = NULL;
	for (d = 0 ; dblist[d] != NULL ; ++d) {
	    if ((ar = fetch_alias(dblist[d], argv[i])) != NULL)
		break;
	}

	/*
	 * Print the result.
	 */
	if (ar == NULL) {
	    if (do_complain) {
		lib_error(CATGETS(elm_msg_cat, ElmaliasSet,
				  ElmaliasUnknownAlias, 
				  "%s: \"%s\" is not a known alias\n"),
			  Progname, argv[i]);
		exit(1);
	    }
	    ar = make_dummy_rec(argv[i]);
	    print_alias(out_fmt, ar);
	} else if (do_expand && (ar->type & GROUP)) {
	    exp_print_alias(dblist, out_fmt, ar);
	} else {
	    print_alias(out_fmt, ar);
	}

	(void) free((malloc_t)ar);

    }

    exit(0);
    /*NOTREACHED*/
}


static DBZ *open_system_aliases()
{
    return dbz_open(system_data_file, O_RDONLY, 0);
}


static DBZ *open_user_aliases()
{
    DBZ *db;
    db = dbz_open(user_data_file, O_RDONLY, 0);
    return db;
}


/*
 * Cobble up an alias record structure to hold some address info.
 */
static struct alias_rec *make_dummy_rec(val)
char *val;
{
    struct alias_rec *ar;
    ar = (struct alias_rec *) safe_malloc(sizeof(struct alias_rec));
    ar->status = 0;
    ar->alias = val;
    ar->last_name = "";
    ar->name = "";
    ar->comment = "";
    ar->address = val;
    ar->type = 0;
    ar->length = 0;
    return ar;
}


/*
 * Recursively expand out a list of addresses and print the expansions.
 */
static void exp_print_alias(dblist, fmt, ar)
     DBZ *dblist[];
     char *fmt;
     struct alias_rec *ar;
{
    char *abuf;		/* list of addresses we can scribble upon	*/
    char *acurr;	/* pointer to current address within "abuf"	*/
    char *anext;	/* pointer to next address within "abuf"	*/
    struct alias_rec *ar0 = NULL;
    int d;		/* dblist index					*/

    /*
     * Create a copy of this address we can scribble upon.
     */
    anext = abuf = safe_strdup(ar->address);
#ifdef lint
    *abuf = '\0'; /* shutup set but not used complaint */
#endif

    /*
     * Go through all of the addresses and expand them out.
     */
    while ((acurr = next_addr_in_list(&anext)) != NULL) {
	for (d = 0 ; dblist[d] != NULL ; ++d) {
	    if ((ar0 = fetch_alias(dblist[d], acurr)) != NULL)
		break;
	}
	if (ar0 == NULL)
	    ar0 = make_dummy_rec(acurr);
	if (ar0->type & GROUP)
	    exp_print_alias(dblist, fmt, ar0);
	else
	    print_alias(fmt, ar0);
	(void) free((malloc_t)ar0);
    }

    (void) free((malloc_t)abuf);
}


/*
 * Print out alias information according to a format specification.
 */
void print_alias(fmt, ar)
     char *fmt;
     struct alias_rec *ar;
{
    char pfmt[64];		/* buffer to hold "%m.ns" formats	*/
    int in_conditional;		/* TRUE if in middle of cond expression	*/
    int print_enab;		/* TRUE if OK to print output		*/
    char *s;
    int n, c;

    print_enab = TRUE;
    in_conditional = FALSE;

    while (*fmt != '\0') {

	switch (*fmt) {

	/*
	 * Formatted output.
	 */
	case '%':

	    /*
	     * Extract the "%m.n" portion of the format.
	     */
	    pfmt[0] = *fmt++;
	    n = 1;
	    while (index("-.0123456789", *fmt) != NULL) {
		if (n < sizeof(pfmt)-2)
		    pfmt[n++] = *fmt;
		++fmt;
	    }
	    pfmt[n++] = 's';
	    pfmt[n] = '\0';

	    /*
	     * Determine what we are printing.
	     */
	    if ((s = sel_alias_mem(ar, *fmt)) == NULL) {
		s = catgets(elm_msg_cat, ElmaliasSet, ElmaliasIllegalFmtChar,
		    "<illegal format char>");
	    }

	    /*
	     * Print out the formatted field.
	     */
	    if (print_enab)
		printf(pfmt, s);
	    break;

	/*
	 * Conditional printing.
	 */
	case '?':
	    if (in_conditional) {
		in_conditional = FALSE;
		print_enab = TRUE;
	    } else {
		in_conditional = TRUE;
		s = sel_alias_mem(ar, *++fmt);
		print_enab = (s != NULL && *s != '\0');
	    }
	    break;
	
	/*
	 * Backslash escapes.
	 */
	case '\\':
	    switch (*++fmt) {
		case 'b':  c = '\b'; break;
		case 'f':  c = '\f'; break;
		case 'n':  c = '\n'; break;
		case 'r':  c = '\r'; break;
		case 't':  c = '\t'; break;
		default:   c = *fmt; break;
	    }
	    if (print_enab && c != '\0')
		putchar(c);
	    break;

	/*
	 * Non-special character to print.
	 */
	default:
	    if (print_enab)
		putchar(*fmt);
	    break;

	}

	if (*fmt != '\0')
	    ++fmt;

    }

    putchar('\n');
}


/*
 * Select a member of the alias record structure.
 */
static char *sel_alias_mem(ar, sel)
     struct alias_rec *ar;
     int sel;
{
    switch (sel) {
    case 'a':
	return ar->alias;
    case 'l':
	return ar->last_name;
    case 'n':
	return ar->name;
    case 'c':
	return ar->comment;
    case 'v':
	return ar->address;
    case 't':
	switch (ar->type & (PERSON|GROUP)) {
	case PERSON:
	    return catgets(elm_msg_cat, ElmaliasSet,
		ElmaliasTypePerson, "Person");
	case GROUP:
	    return catgets(elm_msg_cat, ElmaliasSet,
		ElmaliasTypeGroup, "Group");
	default:
	    return catgets(elm_msg_cat, ElmaliasSet,
		ElmaliasTypeUnknown, "Unknown");
	}
    default:
	return (char *) NULL;
    }
    /*NOTREACHED*/
}

/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1