[comp.sources.unix] v17i094: Expand .mailrc aliases

rsalz@uunet.uu.net (Rich Salz) (02/09/89)

Submitted-by: Mark Sirota <msir_cif@uhura.cc.rochester.edu>
Posting-number: Volume 17, Issue 94
Archive-name: malias

malias: Decode mail aliases.

Returns true address of all arguments, decoded from alias
lines in your .mailrc.

Results are returned in stream form, so
	% malias mark larry
might return
	msir_ltd@uhura.cc.rochester.edu lm03_ltd@uhura.cc.rochester.edu
so that the results can be passed on to some other program, as in
	% finger `malias cif`
to finger everyone you know on CIF.

--
Mark Sirota - University of Rochester, Rochester, NY
 Internet: msir_cif@uhura.cc.rochester.edu
 Bitnet:   msir_ss@uordbv.bitnet
 UUCP:     ...!rochester!ur-cc!msir_cif

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README Makefile malias.c malias.1
# Wrapped by msir_cif@uhura.cc.rochester.edu on Tue Feb  7 15:38:48 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(393 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X#
X# README for malias
X#
X
XThis program is Copyright (c) 1988, 1989 by Mark Sirota.
XIt is provided to you without charge, and with no warranty.
XYou may give away copies if malias, including sources, provided that this
Xnotice is included in all the files.
X
XIn other words, you can do whatever you want with it, so long as you don't
Xmake any money from it and credit is given where credit is due.
END_OF_FILE
if test 393 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(396 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#
X# Makefile for malias
X#
X
X#
X# Update the following directories for your installation.
X#
XBINDIR = /u/cif/bin
XMANDIR = /u/cif/man
XMANEXT = 1
X
XCFLAGS = -O
X
Xmalias: malias.c
X	cc $(CFLAGS) -o malias
X
Xinstall: malias
X	install -s malias $(BINDIR)
X	install -m 0644 malias.1 $(MANDIR)/man$(MANEXT)/malias.$(MANEXT)
X
Xclean:
X	rm -f malias core
X
Xshar:
X	shar -o malias.shar README Makefile malias.c malias.1
END_OF_FILE
if test 396 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'malias.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'malias.c'\"
else
echo shar: Extracting \"'malias.c'\" \(8203 characters\)
sed "s/^X//" >'malias.c' <<'END_OF_FILE'
X/*
X * malias: Decode mail aliases.  Returns true address of all arguments,
X * decoded from alias lines in your .mailrc.
X *
X * Usage: malias <alias> [alias] ...
X *
X * Results are returned in stream form, so
X * 	% malias mark larry
X * might return
X *	msir_ltd@uhura.cc.rochester.edu lm03_ltd@uhura.cc.rochester.edu
X * so that the results can be passed on to some other program, as in
X *	finger `malias cif`
X * to finger everyone you know on CIF.
X *
X * Mark Sirota (msir_cif@uhura.cc.rochester.edu), Fall 1988
X */
X
X/*
X * The program reads the .mailrc and saves the aliases in trie format.
X * The leaves of the trie are linked lists of addresses.  Each address may
X * itself be an alias.
X *
X * The name of the .mailrc is determined by the environment variable MAILRC.
X * If not present, $HOME/.mailrc is assumed.  The "-f" option overrides this.
X */
X
X#include <stdio.h>
X#include <string.h>
X#include <malloc.h>
X
X/*
X * Aliases are maintained as a binding of a name to a mailing list (which may
X * contain one or more names).  Names in a list may themselves be aliases.
X * This structure is one of the names in a mailing list.
X */
Xstruct address {
X	char		*address;
X	struct address	*next,
X			*prev;
X};
X
X/*
X * Ways of finding an alias in the trie.
X */
X#define A_NOTFOUND	-1			/* Not in the trie */
X#define A_USED		0			/* Found, but already seen */
X#define A_FOUND		1			/* Found and okay */
X
X#define FALSE	0
X#define TRUE	1
X
Xvoid
Xusage(progname)
Xchar	*progname;
X{
X	fprintf(stderr, "Usage: %s [-,] [-1] [-f filename] <alias ...>\n", progname);
X}
X
Xvoid
Xmain(argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	int		c;
X	extern int	optind;
X	extern char	*optarg;
X	int		commaflag = FALSE;
X	int		oneflag = FALSE;
X	char		*filename = (char *) NULL;
X	char		**root;
X	extern char	*calloc();
X	void		mailrc();
X	struct address	*list = (struct address *) NULL,
X			*l,
X			*lprev;
X	void		lookup();
X
X	/*
X	 * Parse the arguments.
X	 */
X	while ((c = getopt(argc, argv, ",1f:")) != EOF)
X		switch (c) {
X		    case ',':
X			commaflag = TRUE;
X			break;
X		    case '1':
X			oneflag = TRUE;
X			break;
X		    case 'f':
X			filename = optarg;
X			break;
X		    case '?':
X		    default:
X			usage(argv[0]);
X			exit(1);
X		}
X
X	/*
X	 * If there are no aliases to process, just exit.  This is so that
X	 * malias can be used as a front end for other programs, which don't
X	 * necessarily take arguments, like finger.
X	 */
X	if (optind == argc)
X		exit(0);
X
X	/*
X	 * Allocate memory for the root node of the trie
X	 */
X	if (!(root = (char **) calloc(128, sizeof (char *)))) {
X		fprintf(stderr, "Out of memory.\n");
X		exit(1);
X	}
X
X	/*
X	 * Read lines out of the .mailrc, and add each alias to the trie
X	 */
X	mailrc(filename, root);
X
X	/*
X	 * Translate argv to a linked list.
X	 */
X	for (; optind < argc; optind++) {
X		if (!(l = (struct address *) malloc(sizeof (struct address)))) {
X			fprintf(stderr, "Out of memory.\n");
X			break;
X		}
X
X		if (!list) {
X			list = l;
X			list->prev = (struct address *) NULL;
X		} else {
X			l->prev = lprev;
X			lprev->next = l;
X		}
X		l->address = argv[optind];
X		l->next = (struct address *) NULL;
X		lprev = l;
X	}
X
X	/*
X	 * Now look up the list
X	 */
X	lookup(root, &list);
X
X	/*
X	 * Print the results.
X	 */
X	for (l = list; l; l = l->next) {
X		fputs(l->address, stdout);
X		if (l->next) {
X			if (commaflag)
X				putchar(',');
X			if (oneflag)
X				putchar('\n');
X			else
X				putchar(' ');
X		}
X	}
X	putchar('\n');
X}
X
X/*
X * Open the user's .mailrc and add each alias to the trie.  The .mailrc is
X * defined by the environment variable MAILRC.  If not found, $HOME/.mailrc
X * is assumed.
X *
X * The mail command "source", if found in the mailrc, is followed.
X */
Xvoid
Xmailrc(filename, root)
Xchar	*filename;
Xchar	**root;
X{
X	extern char	*getenv();
X	char		buffer[BUFSIZ];
X	FILE		*fp;
X	char		line[BUFSIZ];
X	char		*command;
X	char		*alias;
X	char		*salloc();
X	struct address	*real;
X	struct address	*lalloc();
X	void		addalias();
X
X	if (!filename && !(filename = getenv("MAILRC"))) {
X		sprintf(buffer, "%s/.mailrc", getenv("HOME"));
X		filename = buffer;
X	}
X
X	if (!(fp = fopen(filename, "r"))) {
X		perror(filename);
X		exit(1);
X	}
X
X	while (fgets(line, sizeof line, fp)) {
X		if (!(command = strtok(line, " \t\n")))
X			continue;
X
X		if (strcmp(command, "source") == 0)
X			mailrc(strtok((char *) NULL, "\n"), root);
X		else if (strcmp(command, "alias") == 0) {
X			alias = salloc(strtok((char *) NULL, " \t"));
X			real = lalloc(strtok((char *) NULL, "\n"));
X			addalias(root, alias, real);
X		}
X	}
X
X	fclose(fp);
X}
X
X/*
X * Add an alias to the trie
X */
Xvoid
Xaddalias(root, alias, real)
Xchar		**root;
Xchar		*alias;
Xstruct address	*real;
X{
X	char		**tp;
X	extern char	*calloc();
X
X	/*
X	 * Scan down the existing trie as far as we can
X	 */
X	tp = root;
X	for (; *alias; alias++) {
X		if (!tp[*alias])
X			break;
X		tp = (char **) tp[*alias];
X	}
X
X	/*
X	 * Add the rest of the alias to the trie
X	 */
X	for (; *alias; alias++) {
X		if (!(tp[*alias] = calloc(128, sizeof (char *)))) {
X			fprintf(stderr, "Out of memory.\n");
X			exit(1);
X		}
X		tp = (char **) tp[*alias];
X	}
X
X	/*
X	 * Set the end of the alias to point to the real name
X	 */
X	tp[NULL] = (char *) real;
X}
X
X/*
X * Look up an alias list in the trie.  When finished, the list will contain
X * only resolved names.
X */
Xvoid
Xlookup(root, aliasl)
Xchar		**root;
Xstruct address	**aliasl;
X{
X	struct address	*alias;		/* Current alias pointer */
X	struct address	*list,
X			*l;		/* list associated with alias */
X
X	/*
X	 * Look up each alias in this list
X	 */
X	alias = *aliasl;
X	while (alias) {
X		switch (findalias(root, alias->address, &list)) {
X		    case A_NOTFOUND:
X			/*
X			 * Leave this one alone.
X			 */
X			alias = alias->next;
X			break;
X		    case A_USED:
X			/*
X			 * This alias has been processed before.
X			 * just remove this alias and move on.
X			 */
X			if (alias->prev)
X				alias->prev->next = alias->next;
X			else
X				*aliasl = alias->next;
X			if (alias->next)
X				alias->next->prev = alias->prev;
X			alias = alias->next;
X			break;
X		    case A_FOUND:
X			/*
X			 * Splice the list in here in place of the alias.
X			 */
X			if (alias->prev)
X				alias->prev->next = list;
X			else
X				*aliasl = list;
X			list->prev = alias->prev;
X			for (l = list; l->next; l = l->next)
X				;
X			l->next = alias->next;
X			if (alias->next)
X				alias->next->prev = l;
X			alias = list;
X			break;
X		}
X	}
X}
X
X/*
X * Find an alias in the trie.  When an alias is fonund, the alias is removed
X * so that it will not be repeated.  The result is placed in list, and the
X * status is returned (NOTFOUND, USED, or FOUND).
X */
Xint
Xfindalias(root, alias, list)
Xchar		**root;
Xchar		*alias;
Xstruct address	**list;
X{
X	char	**tp;
X
X	/*
X	 * Scan down the trie
X	 */
X	tp = root;
X	for (; *alias; alias++) {
X		if (!tp[*alias])
X			break;
X		tp = (char **) tp[*alias];
X	}
X
X	/*
X	 * If we didn't make it to the end of the alias, return NOTFOUND.
X	 * Otherwise, return the list.
X	 */
X	if (*alias) {
X		*list = (struct address *) NULL;
X		return A_NOTFOUND;
X	}
X
X	/*
X	 * Found.  If used, the leaf will be NULL, and so we'll return USED.
X	 */
X	if ((struct address *) tp[NULL] == (struct address *) NULL) {
X		*list = (struct address *) NULL;
X		return A_USED;
X	}
X
X	/*
X	 * Save a pointer to the list and then remove the trie
X	 * reference.  Next time this alias is referenced, it will return a
X	 * NULL list.
X	 */
X	*list = (struct address *) tp[NULL];
X	tp[NULL] = (char *) NULL;
X	return A_FOUND;
X}
X
X/*
X * Allocate enough new memory to hold string, copy it there, and return a
X * pointer to the new copy.
X */
Xchar *
Xsalloc(string)
Xchar	*string;
X{
X	return strcpy(malloc((unsigned) strlen(string) + 1), string);
X}
X
X/*
X * Take a string and turn it into a linked list of addresses, using spaces
X * and tabs as delimiters.
X */
Xstruct address *
Xlalloc(string)
Xchar	*string;
X{
X	char		*s;
X	struct address	*l,
X			*lprev,
X			*list = (struct address *) NULL;
X	char		*salloc();
X
X	for (s = strtok(string, " \t"); s; s = strtok((char *) NULL, " \t")) {
X		if (!(l = (struct address *) malloc(sizeof (struct address)))) {
X			fprintf(stderr, "Out of memory.\n");
X			return (struct address *) NULL;
X		}
X		if (!list) {
X			list = l;
X			list->prev = (struct address *) NULL;
X		} else {
X			list->prev = lprev;
X			lprev->next = l;
X		}
X		l->address = salloc(s);
X		l->next = (struct address *) NULL;
X		lprev = l;
X	}
X
X	return list;
X}
END_OF_FILE
if test 8203 -ne `wc -c <'malias.c'`; then
    echo shar: \"'malias.c'\" unpacked with wrong size!
fi
# end of 'malias.c'
fi
if test -f 'malias.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'malias.1'\"
else
echo shar: Extracting \"'malias.1'\" \(2311 characters\)
sed "s/^X//" >'malias.1' <<'END_OF_FILE'
X.TH MALIAS 1 "21 January 1989"
X.SH NAME
Xmalias \- expand mail aliases
X.SH SYNOPSIS
X\fBmalias\fP [\fB-,\fP] [\fB-1\fP] [\fB-f \fIfilename\fP] [\fIalias ...\fP]
X.SH DESCRIPTION
X\fBmalias\fP decodes alias lines from a mailrc and returns the true
Xaddresses.  By default, the results are returned in stream format so that
Xthe results may be passed on to some other program (See the EXAMPLES section
Xbelow).
X
XThe mailrc is determined by the environment variable \fBMAILRC\fP.  If not
Xset, \fBmalias\fP tries \fB$HOME/.mailrc\fP.  An alternate filename may be
Xspecified on the command line using the \fB-f\fP option (See the OPTIONS
Xsection below).  If the mail command \fBsource\fP is found in the mailrc, it
Xis followed properly.
X
XIf an alias on the command line is not found in the mailrc, then it will be
Xleft alone.  An alias which is found twice (on the command line, as an
Xalias, or as part of a mailing list) will only be returned once.  Aliases
Xmay be more than one level deep.
X
XIf no \fIalias\fP is given on the command line, \fBmalias\fP simply
Xexits.  This is so that it can be used as a front end processor for
Xprograms which don't necessarily take arguments, such as \fBfinger(1)\fP.
XSee the EXAMPLES section below.
X.SH OPTIONS
X.IP \fB-,\fP
XDelimit output with commas.  This is provided for compatibility with RFC822.
X.IP \fB-1\fP
XReturn addresses one per line instead of in stream format.
X.IP "\fB-f \fIfilename\fP"
XUse \fIfilename\fP as the mailrc instead of checking the environment
Xvariable.
X.SH EXAMPLES
X.IP "\fBmalias -1 -f ~joss/.mailrc all | wc -l\fP"
Xwould tell you how many people the user \fBjoss\fP has in his \fBall\fP
Xlist.
X.IP "\fBfinger `malias rochester`"
Xwould \fBfinger\fP all the people in your \fBrochester\fP mailing list.
X.IP "\fBtalk `malias mark`\fP"
Xwould run the command \fBtalk msir_cif@uhura.cc.rochester.edu\fP.  This
Xcan be used in an alias, to make your life easier, as in...
X.IP "\fBalias talk 'talk `malias \e!*`'\fP"
Xso that whenever you run \fBtalk\fP, it will first look through your mail
Xaliases for the address you specify.
X.ad
X.SH ENVIRONMENT
X.IP \fBMAILRC\fP
XName of mailrc file.
X.SH FILES
X.IP \fB~/.mailrc\fP
Xdefault mailrc, if environment variable isn't found.
X.SH "SEE ALSO"
Xmail(1), Mail(1)
X.SH AUTHOR
XMark Sirota (msir_cif@uhura.cc.rochester.edu), Fall 1988
END_OF_FILE
if test 2311 -ne `wc -c <'malias.1'`; then
    echo shar: \"'malias.1'\" unpacked with wrong size!
fi
# end of 'malias.1'
fi
echo shar: End of shell archive.
exit 0

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.