|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T a
Length: 33305 (0x8219)
Types: TextFile
Names: »addr.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
└─⟦2fafebccf⟧ »EurOpenD3/mail/smail3.1.19.tar.Z«
└─⟦bcd2bc73f⟧
└─⟦this⟧ »src/addr.c«
/* @(#)addr.c 3.21 11/27/88 16:25:23 */
/*
* Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
*
* See the file COPYING, distributed with smail, for restriction
* and warranty information.
*/
/*
* addr.c:
* routines to parse addresses
*
* external functions: preparse_address, parse_address,
* build_uucp_route, build_partial_uucp_route,
* address_token, back_address_token, alloc_addr,
* insert_addr_list, addr_sort
*/
#include <stdio.h>
#include <ctype.h>
#include "defs.h"
#include "smail.h"
#include "addr.h"
#include "dys.h"
#include "exitcodes.h"
#ifndef DEPEND
# include "debug.h"
# include "extern.h"
#endif
/* functions local to this file */
static int check_target_and_remainder();
static char *escaped();
static char *internal_build_uucp_route();
static int addrcmp();
\f
/*
* preparse_address - do preliminary parsing that might be needed for address
*
* this routine should be used when an address is first extracted from a
* source. It transforms some mutant addressing forms into something more
* managable.
*
* Transformations:
*
* <string> becomes just string (recursively)
* host!(host!)*@route becomes a pure !-route
*
* NOTE: We don't handle @route:host!(host!)*@route, for now. Maybe later.
*
* input:
* address - address to be preparsed
* error - error message
*
* output:
* parsed address, or NULL for parsing error, message returned in error
* output is guarranteed to not be a pointer to the input
*/
char *
preparse_address(address, error)
char *address; /* address to be preparsed */
char **error; /* return error message here */
{
register char *ap; /* temp for scanning address */
char *mark_start = NULL; /* marked position of < */
char *mark_end = NULL; /* marked position of > */
int nest_cnt = 0; /* nesting count for angle brackets */
DEBUG1(DBG_ADDR_HI, "preparse_address(%s) entry:\n", address);
/*
* scan for < and > pairs and find the last or innermost matching
* pair.
*/
for (ap = address; ap && *ap; ap = address_token(ap)) {
if (*ap == '<') {
nest_cnt++;
mark_start = ap + 1;
mark_end = NULL;
} else if (*ap == '>') {
nest_cnt--;
if (mark_end == NULL) {
mark_end = ap;
}
}
}
if (ap == NULL) {
*error = "bad address token";
DEBUG1(DBG_ADDR_LO,
"preparse_address found error %s: returns (null)\n",
*error);
return NULL;
}
if (mark_start && mark_end == NULL) {
/* hmm, no match for the < token */
*error = "no match for `<' in address";
DEBUG1(DBG_ADDR_LO,
"preparse_address found error %s: returns (null)\n",
*error);
return NULL;
}
if (nest_cnt != 0) {
if (nest_cnt < 0) {
*error = "no match for > in address";
} else {
*error = "no match for < in address";
}
DEBUG1(DBG_ADDR_LO,
"preparse_address found error %s: returns (null)\n",
*error);
return NULL;
}
/* narrow to the route-addr */
if (mark_end) {
*mark_end = '\0';
address = ap = mark_start;
}
/*
* now search for the mutant form: path!@route-addr
*/
if (*ap == '@') {
/* valid route-addr, not a mutant one */
ap = xmalloc((unsigned)(strlen(address) + 1));
(void) strcpy(ap, address);
if (mark_end) {
*mark_end = '>'; /* widden the original address */
}
DEBUG1(DBG_ADDR_HI, "preparse_address returns: %s\n", ap);
return ap; /* no transformations */
}
while (*ap) {
ap = address_token(ap);
if (ap == NULL) {
*error = "bad address token";
DEBUG1(DBG_ADDR_LO,
"preparse_address found error %s: returns (null)\n",
*error);
return NULL;
}
if (*ap != '!') {
ap = xmalloc((unsigned)(strlen(address) + 1));
(void) strcpy(ap, address);
if (mark_end) {
*mark_end = '>'; /* widden the original address */
}
DEBUG1(DBG_ADDR_HI, "preparse address returns: %s\n", ap);
return ap; /* address should be okay */
}
ap++;
if (*ap == '@') {
/* matched host!(host!)*@route -- build the !-route */
register char *p = xmalloc((unsigned)strlen(address));
DEBUG(DBG_ADDR_MID, "found host!(host!)*@route form--ugh!\n");
/* first part already !-route */
(void) strncpy(p, address, ap-address);
if (mark_end) {
*mark_end = '>'; /* widden the original address */
}
ap = build_uucp_route(ap, error); /* build !-route */
if (ap == NULL) {
DEBUG(DBG_ADDR_MID, "preparse_address returns: (null)\n");
return NULL;
}
(void) strcat(p, ap); /* concatenate together */
xfree(ap);
DEBUG1(DBG_ADDR_HI, "preparse_address returns: %s\n", p);
return p; /* transformed */
}
}
ap = xmalloc((unsigned)(strlen(address) + 1));
(void) strcpy(ap, address);
if (mark_end) {
*mark_end = '>'; /* widden the original address */
}
DEBUG1(DBG_ADDR_HI, "preparse address returns: %s\n", ap);
return ap; /* no transformations */
}
\f
/*
* parse_address - extract a target and remainder from an address
*
* using the rules in section 3.2 of the mailer.design document,
* extract a target and a remainder from an address.
*
* The target is defined as the first destination host in an address,
* the remainder is defined as the remaining parat of the address
* after extracting the target.
*
* A short form of the rules for extraction is the following table
* of addressing forms in order of precedence:
*
* +---------------------------------------------------------------+
* | form | description | return |
* |-----------------------|-----------------------|---------------|
* | @target,remainder | route from route-addr | RFC_ROUTE |
* | @target:remainder | route from route-addr | RFC_ENDROUTE |
* | remainder@target | standard mailbox | MAILBOX |
* | target!remainder | UUCP !-route | UUCP_ROUTE |
* | target::remainder | decnet route | DECNET |
* | target:remainder | obsolete berkenet | BERKNET |
* | remainder%target | obsolete mailbox hack | PCT_MAILBOX |
* | remainder | local address form | LOCAL |
* +---------------------------------------------------------------+
*
* inputs:
* address - string containing the address to be parsed
* target - where to store pointer to computed target
* remainder - where to store pointer to computed target
*
* outut:
* return the address form as described in the above table. Also,
* return in target a pointer to to the target and return in
* remainder a pointer to the remainder. If an error is detected
* return FAIL and load the remainder with an error message.
* If target is NULL, then only a form is returned, a target and
* remainder are not returned, though an error message may still
* be loaded into remainder.
*
* NOTE: address will be overwritten unless it is in local form, or
* a target and remainder are not returned.
*
* calls: address_token, back_address_token
* called by: build_uucp_route
*/
int
parse_address(address, target, remainder)
char *address; /* address to parse (destructively) */
char **target; /* store pointer to target host here */
char **remainder; /* store pointer to remainder here */
{
char *ep; /* pointer to end of address */
register char *last_tokens; /* start of second to last token */
register char *ap; /* pointer for scanning address */
register char *p; /* temp */
DEBUG1(DBG_ADDR_HI, "parse_address called: address=%s\n", address);
/*
* make sure we have an address
*/
ap = address;
if (*ap == '\0') {
/* nothing to do with a zero-length address */
*remainder = "null address";
DEBUG1(DBG_ADDR_MID, "parse_address: %s\n", *remainder);
return FAIL;
}
/*
* does the address begin with @target[,:] ?
*/
if (*ap == '@') {
if (target) {
*target = ap + 1; /* mark the target */
}
ap = address_token(ap + 1); /* skip target */
if (ap == NULL) {
*remainder = "bad address token";
DEBUG1(DBG_ADDR_MID, "parse_address: %s\n", *remainder);
return FAIL;
}
/* ensure that the `,' or `:' is in the address */
if (!ap) {
/* interesting, address just contained '@' */
*remainder = "syntax error: no target host";
DEBUG1(DBG_ADDR_MID, "parse_address: %s\n", *remainder);
return FAIL;
}
if (*ap == ',' || *ap == ':') {
int retval = (*ap==','? RFC_ROUTE: RFC_ENDROUTE);
if (target) {
*ap++ = '\0'; /* null terminate target */
*remainder = ap;
if (check_target_and_remainder(target, remainder) == FAIL) {
return FAIL;
}
DEBUG2(DBG_ADDR_HI,
"parse_address: RFC_ROUTE: target=%s, remainder=%s\n",
*target, *remainder);
}
return retval;
}
/* we have a syntax error, missing , or : */
*remainder = "syntax error: , or : missing in route-addr";
DEBUG1(DBG_ADDR_MID, "parse_address: %s\n", *remainder);
return FAIL;
}
/*
* is the address a standard mailbox ?
* i.e., does the address end in @target ?
*/
ep = address + strlen(address);
last_tokens = back_address_token(ap, ep);
if (last_tokens && last_tokens > ap) {
last_tokens = back_address_token(ap, last_tokens);
}
if (last_tokens == NULL) {
*remainder = "bad address token";
DEBUG1(DBG_ADDR_MID, "parse_address: %s\n", *remainder);
return FAIL;
}
if (last_tokens > ap && *last_tokens == '@') {
/* it matches @token, null terminate the remainder and finish up */
if (target) {
*last_tokens = '\0';
*target = last_tokens+1;
*remainder = ap;
if (check_target_and_remainder(target, remainder) == FAIL) {
return FAIL;
}
DEBUG2(DBG_ADDR_HI,
"parse_address: MAILBOX: target=%s, remainder=%s\n",
*target, *remainder);
}
return MAILBOX;
}
/*
* is the address a UUCP !-route ?
* i.e., does the address begin with target! ?
*/
p = address_token(ap);
if (p && *p == '!') {
/* it matches target!, null terminate target and finish up */
if (target) {
*p = '\0';
*target = ap;
*remainder = p+1;
if (check_target_and_remainder(target, remainder) == FAIL) {
return FAIL;
}
DEBUG2(DBG_ADDR_HI,
"parse_address: UUCP_ROUTE: target=%s, remainder=%s\n",
*target, *remainder);
}
return UUCP_ROUTE;
}
/*
* is the address a BERKENET or DECNET syntax?
*/
if (p && *p == ':') {
if (*(p + 1) == ':') {
/* DECNET syntax */
if (target) {
*p = '\0';
*target = ap;
*remainder = p + 2;
if (check_target_and_remainder(target, remainder) == FAIL) {
return FAIL;
}
DEBUG2(DBG_ADDR_HI,
"parse_address: DECNET: target=%s, remainder=%s\n",
*target, *remainder);
}
return DECNET;
}
/* Berkenet syntax */
if (target) {
*p = '\0';
*target = ap;
*remainder = p + 1;
if (check_target_and_remainder(target, remainder) == FAIL) {
return FAIL;
}
DEBUG2(DBG_ADDR_HI,
"parse_address: BERKENET: target=%s, remainder=%s\n",
*target, *remainder);
}
return BERKENET;
}
/*
* is the address a non-standard mailbox ?
* i.e., does the address end in %target ?
*/
if (last_tokens && last_tokens - ap > 0 && *last_tokens == '%') {
/* it matches @target, null terminate the remainder and finish up */
if (target) {
*last_tokens = '\0';
*target = last_tokens+1;
*remainder = ap;
if (check_target_and_remainder(target, remainder) == FAIL) {
return FAIL;
}
DEBUG2(DBG_ADDR_HI,
"parse_address: PCT_MAILBOX: target=%s, remainder=%s\n",
*target, *remainder);
}
return PCT_MAILBOX;
}
/*
* we have a local form address
*/
if (target) {
*target = NULL;
*remainder = ap;
DEBUG1(DBG_ADDR_HI, "parse_address: LOCAL: remainder=%s\n",
*remainder);
}
return LOCAL;
}
/* check_target_and_remainder - check for glaring problems */
static int
check_target_and_remainder(target, remainder)
char **target;
char **remainder;
{
register int c;
register char *p;
if ((*remainder)[0] == '\0') {
*remainder = "no remainder address";
DEBUG1(DBG_ADDR_MID, "parse_address: %s\n", *remainder);
return FAIL;
}
/* the set of chars in the target should be limited to a small set */
p = *target;
if (*p == '-') {
*remainder = "target cannot begin with `-'";
DEBUG1(DBG_ADDR_MID, "parse_address: %s\n", *remainder);
return FAIL;
}
if (*p == '[') {
return SUCCEED;
}
while (c = *p++) {
if ( !(isalnum(c) || c == '.' || c == '-' || c == '_' ||
c == '+' || c == '=') )
{
*remainder = "illegal character in hostname";
DEBUG1(DBG_ADDR_MID, "parse_address: %s\n", *remainder);
return FAIL;
}
}
return SUCCEED;
}
\f
/*
* build_uucp_route - convert an address into a UUCP route.
*
* Given an address using any of the addressing forms known to the
* parse_address() routine, convert that address into a pure uucp
* !-route. The return value is always freeable with xfree().
*
* If there is an error, return NULL.
*
* inputs:
* address - the address to transform into a UUCP !-route
* error - on error, set this to error message, if non-NULL
*
* output:
* transformed address, or NULL if a syntax error occured
*/
char *
build_uucp_route(address, error)
char *address; /* address to transform into !-route */
char **error; /* return an error message here */
{
return internal_build_uucp_route(address, error, FALSE);
}
/*
* build_partial_uucp_route - convert an address into a partial UUCP route.
*
* Given an address using any of the addressing forms known to the
* parse_address routine, convert that address into a uucp !-route,
* possibly with %-forms left at the end. The return value is always
* freeable with xfree().
*
* If there is an error, return NULL.
*
* inputs:
* address - the address to transform into a UUCP !-route
* error - on error, set this to error message, if non-NULL
*
* output:
* transformed address, or NULL if a syntax error occured
*/
char *
build_partial_uucp_route(address, error)
char *address; /* address to transform into !-route */
char **error; /* return an error message here */
{
return internal_build_uucp_route(address, error, TRUE);
}
/*
* internal_build_uucp_route - internal form for uucp-route building
*
* called from build_uucp_route and build_partial_uucp_route. If the
* `partial' flag is TRUE then the latter style is used, otherwise a
* pure !-route is built.
*/
static char *
internal_build_uucp_route(address, error, partial)
char *address; /* address to transform into !-route */
char **error; /* return an error message here */
int partial; /* TRUE to allow %-form in route */
{
struct str str;
register struct str *sp = &str; /* dynamic string region */
int uucp_route = TRUE; /* TRUE if already pure !-route */
char *target; /* target returned by parse_address */
char *remainder; /* remainder from parse_address */
char *storage; /* malloc region for old address */
DEBUG1(DBG_ADDR_HI, "internal_build_uucp_route entry: address=%s\n",
address);
/*
* allocate a new copy of the address so it can be examined destructively.
*/
storage = remainder = xmalloc((unsigned)(strlen(address) + 1));
(void)strcpy(storage, address);
/* initialize for copy into string region */
STR_INIT(sp);
/* loop until we have a local form or a %-form an error occurs */
for (;;) {
int form = parse_address(remainder, &target, &remainder);
switch (form) {
case FAIL: /* something went wrong, somewhere */
*error = remainder;
DEBUG(DBG_ADDR_MID, "internal_build_uucp_route returns: (null)\n")
return NULL;
case UUCP_ROUTE: /* okay, this part is a !-route */
STR_CAT(sp, target); /* add target! to route */
STR_NEXT(sp, '!');
break;
case PCT_MAILBOX: /* matched something%host... */
/*
* If we are building a pure uucp route, then a%b is just
* another remote form. Otherwise, finding this form ends
* the parsing process.
*/
if (!partial) {
goto remote_form;
}
/* FALL THROUGH */
case LOCAL: /* local form, we are done */
/* if address was already a pure !-route, return the old one */
if (uucp_route) {
/* free garbage */
xfree(storage);
STR_FREE(sp);
DEBUG1(DBG_ADDR_HI,
"internal_build_uucp_route returns: %s (unchanged)\n",
address);
return COPY_STRING(address);
} else {
/* append final local-part */
STR_CAT(sp, remainder);
if (form == PCT_MAILBOX) {
/* %-form requires the target to be included */
STR_NEXT(sp, '%');
STR_CAT(sp, target);
}
STR_NEXT(sp, '\0');
xfree(storage); /* free garbage */
STR_DONE(sp);
DEBUG1(DBG_ADDR_HI, "internal_build_uucp_route returns: %s\n",
sp->p);
return sp->p; /* return completed !-route */
}
/*NOTREACHED*/
default: /* not pure !-route, other form */
remote_form:
STR_CAT(sp, target); /* add target! to route */
STR_NEXT(sp, '!');
uucp_route = FALSE;
}
}
}
\f
/*
* address_token - scan forward one token in an address
*
* an address token is delimited by a character from the set [@!%:,]
* a token can also be a domain literal between [ and ], or
* a quoted literal between double quotes. \ can precede a character
* to take away its special properties.
* domain literals and quoted literals and other tokens can be strung
* together into one single token if they are separated by `.'. Otherwise
* a domain literal or quoted literal represents one token.
*
* input:
* ap - pointer to start of a token
*
* output:
* the end of the input token. Return NULL on error.
*
* called by: parse_address
*/
char *
address_token(ap)
register char *ap; /* address to be scanned */
{
static enum state { /* states for the state machine */
s_normal, /* not in a literal or \ escape */
s_cquote, /* previous char was \ */
s_quote, /* scanning quoted literal */
s_domlit, /* scanning domain literal */
} state;
enum state save_state; /* previous state for \ escape */
int dot = FALSE; /* TRUE if last char was unescaped . */
/* setup initial state */
switch (*ap++) {
case '\0': /* no tokens */
return NULL; /* error */
case '@': /* delimiters are one token a piece */
case '!':
case '%':
case ':':
case ',':
case '>':
case '<':
return ap; /* so return that single token */
case '"': /* start in a quoted literal */
state = s_quote;
break;
case '[': /* start in a domain literal */
state = s_domlit;
break;
case '.': /* start with an initial dot */
state = s_normal;
dot = TRUE;
break;
case '\\': /* start initially with \ escape */
save_state = s_normal;
state = s_cquote;
break;
default: /* otherwise begin in normal state */
state = s_normal;
break;
}
/*
* scan until end of token
*/
while (*ap) {
switch (state) {
case s_normal: /* scan for token delimeter */
switch (*ap) {
case '\\': /* \ escape, save state, then cquote */
save_state = s_normal;
state = s_cquote;
break;
case '[': /* domain continue if last char is . */
if (dot) {
state = s_domlit;
} else {
return ap;
}
break;
case '"': /* quote continue if last char is . */
if (dot) {
state = s_quote;
} else {
return ap;
}
break;
case '@':
case '!':
case '%':
case ':':
case ',':
case '<':
case '>':
return ap; /* found the end of a token */
}
/* dot is TRUE if this char was a dot */
dot = ('.' == *ap++);
break;
case s_quote: /* scan for end of a quote */
if (*ap == '\\') {
/* \ escape in quote */
ap++;
save_state = s_quote;
state = s_cquote;
} else if (*ap++ == '"') {
/* end of quote -- check for . after it */
if (*ap == '.') {
/* if exists, continue scanning */
state = s_normal;
} else {
/* otherwise we have a complete token */
return ap;
}
}
break;
case s_domlit: /* scan for end of domain literal */
if (*ap == '\\') {
/* \ escape in domain literal */
ap++;
save_state = s_domlit;
state = s_cquote;
} else if (*ap++ == ']') {
/* end of domain literal -- check for . after it */
if (*ap == '.') {
/* if exists, continue scanning */
state = s_normal;
} else {
/* otherwise we have a complete token */
return ap;
}
}
break;
case s_cquote: /* process \ escape */
ap++; /* just skip the char */
state = save_state; /* and return to previous state */
break;
}
}
/*
* fell through -- error if we are not in the normal state
*/
if (state != s_normal) {
return NULL;
}
return ap; /* all done, return the token */
}
\f
/*
* back_address_token - scan backward one token in an address
*
* see the rules in address_token for how to delimit an address token.
* This procedure does it going backwards.
*
* Note: this routine is more complex than address_token, because
* addresses are intended to be scanned forward.
*
* inputs:
* ba - beginning of an address (firewall)
* ap - pointer to character past end of token
*
* output:
* return start of token that ap points past. Return NULL on error.
*
* called by: parse_address
* calls: escaped
*/
char *
back_address_token(ba, ap)
register char *ba; /* beginning of address (firewall) */
register char *ap; /* character past end of token */
{
static enum state { /* states for the state machine */
s_normal, /* not in a literal */
s_quote, /* scanning quoted literal */
s_domlit, /* scanning domain literal */
} state;
int dot = FALSE; /* TRUE if next char is unescaped . */
register char *p; /* temp */
/*
* trap no tokens
*/
if (ba == ap) {
return NULL;
}
/*
* setup initial state
*/
--ap; /* backup to end of token */
if (p = escaped(ba, ap)) {
/* if last char is escaped, we are in the normal state */
state = s_normal;
ap = p;
} else {
switch (*ap) {
case '@': /* delimiters are one token a piece */
case '!':
case '%':
case ':':
case ',':
case '>':
case '<':
return ap; /* so return that single token */
case '"': /* start in a quoted literal */
state = s_quote;
break;
case ']': /* start in a domain literal */
state = s_domlit;
break;
case '.': /* start with an initial dot */
state = s_normal;
dot = TRUE;
break;
default: /* otherwise begin in normal state */
state = s_normal;
break;
}
--ap; /* this char already processed */
}
/*
* scan until beginning of token
*/
while (ap - ba >= 0) {
switch (state) {
case s_normal: /* scan for token delimeter */
/* trap escaped character */
if (p = escaped(ba, ap)) {
ap = p;
} else {
/* not escaped, process it */
switch (*ap) {
case ']': /* domain okay if next char is . */
if (dot) {
state = s_domlit;
} else {
return ap+1;
}
break;
case '"': /* quote okay if next char is . */
if (dot) {
state = s_quote;
} else {
return ap+1;
}
break;
case '@':
case '!':
case '%':
case ':':
case ',':
case '>':
case '<':
return ap+1; /* found the end of a token */
}
/* dot is TRUE if this char was a dot */
dot = ('.' == *ap--);
}
break;
case s_quote: /* scan for end of a quote */
if (p = escaped(ba, ap)) {
/* trap \ escape */
ap = p;
} else if (*ap-- == '"') {
/* end of quote -- check for . before it */
if (ap - ba >= 0 && *ap == '.' && !escaped(ba, ap)) {
/* if exists, continue scanning */
state = s_normal;
} else {
/* otherwise we have a complete token */
return ap+1;
}
}
break;
case s_domlit: /* scan for end of domain literal */
if (p = escaped(ba, ap)) {
/* trap \ escape */
ap = p;
} else if (*ap-- == '[') {
/* end of domain literal -- check for . before it */
if (ap - ba >= 0 && *ap == '.' && !escaped(ba, ap)) {
/* if exists, continue scanning */
state = s_normal;
} else {
/* otherwise we have a complete token */
return ap+1;
}
}
break;
}
}
/*
* fell through -- error if we are not in the normal state
*/
if (state != s_normal) {
return NULL;
}
return ap+1; /* all done, return the token */
}
/*
* escaped - determine if a character is \ escaped, scanning backward
*
* given the beginning of a string and a character positition within
* it, determine if that character is \ escaped or not, tracing through
* multiple \ chars if necessary. Basically, if the character position
* is preceded by an odd number of \ chars, the current character is
* \ escaped.
*
* inputs:
* ba - beginning of string
* ap - character position in string
*
* output:
* beginning of set of \ chars previous to ap, or NULL if the
* character at ap is not backslash escaped.
*
* called by: back_address_token
*/
static char *
escaped(ba, ap)
register char *ba; /* beginning of string */
register char *ap; /* character position in string */
{
register unsigned i = 0; /* count of \ characters */
/*
* count the number of preceding \ characters, but don't go past
* the beginning of the string.
*/
--ap;
while (ap - ba >= 0 && *ap == '\\') {
i++; --ap;
}
/* if odd number of \ chars, then backslash escaped */
return (i%2==1)? ap: NULL;
}
\f
/*
* alloc_addr - allocate a struct addr
*
* NOTE: the caller must setup the addr fields correctly. This routine
* marks certain fields with improper values, which unless changed,
* will results in other routines doing a panic().
*/
struct addr *
alloc_addr()
{
register struct addr *addr; /* our new address */
/* grab it */
addr = (struct addr *)xmalloc(sizeof(*addr));
/* preset the proper values */
addr->succ = NULL;
addr->flags = 0;
addr->parent = NULL;
addr->true_addr = NULL;
addr->in_addr = NULL;
addr->target = NULL;
addr->remainder = NULL;
addr->work_addr = NULL;
addr->match_count = -1;
addr->route = NULL;
addr->router = NULL;
addr->director = NULL;
addr->next_host = NULL;
addr->next_addr = NULL;
addr->transport = NULL;
addr->home = NULL;
addr->uid = BOGUS_USER; /* the identity is not known yet */
addr->gid = BOGUS_GROUP; /* the identity is not known yet */
addr->error = NULL;
return addr;
}
/*
* insert_addr_list - insert a list of addrs into another list
*
* insert each addr in an input list at the beginning of an output list.
* In the process or in some addr flags and (possibly) set next_addr
* to an error message.
*/
void
insert_addr_list(in, out, error)
register struct addr *in; /* input list */
register struct addr **out; /* output list */
register struct error *error; /* error structure (if non-NULL) */
{
struct addr *next;
/* loop over all of the input addrs */
for (; in; in = next) {
next = in->succ;
if (error) {
in->error = error; /* set the error message, if given */
}
in->succ = *out;
*out = in;
}
}
/*
* addr_sort - sort an input list of addrs and return the new sorted list
*
* calling sequence is:
* sorted_list = addr_sort(input_list, OFFSET(addr, tag_name)
*
* where tag_name is the (char *) element name in the addr structure to
* sort on.
*/
static int sort_offset; /* pass offset to compare function */
struct addr *
addr_sort(in, offset)
struct addr *in;
int offset;
{
struct addr **addrv; /* array of addresses */
register int addrc; /* count of addresses */
register struct addr **addrp; /* temp addr pointer */
register struct addr *a; /* address list or current address */
/* pass offset value to addrcmp() by setting file local variable */
sort_offset = offset;
/* count the input addresses */
addrc = 0;
for (a = in; a; a = a->succ) {
addrc++;
}
/* allocate space for an array for that many pointers */
addrv = (struct addr **)xmalloc(addrc * sizeof(*addrv));
/* build the array from the input list */
for (addrp = addrv, a = in; a; a = a->succ) {
*addrp++ = a;
}
/* sort the array */
qsort((char *)addrv, addrc, sizeof(*addrv), addrcmp);
/*
* turn the sorted array into a sorted list
* Start from the end of the array so the generated list will start
* from the beginning.
*/
for (addrp = addrv + addrc, a = NULL; addrc > 0; --addrc) {
(*--addrp)->succ = a;
a = *addrp;
}
return a;
}
/*
* addrcmp - compare two addr structures based on a field at sort_offset.
*/
static int
addrcmp(a, b)
char **a;
char **b;
{
return strcmp(*(char **)(*a + sort_offset), *(char **)(*b + sort_offset));
}
/*
* note_error - create an error structure for inclusion in an addr structure
*/
struct error *
note_error(info, message)
long info;
char *message;
{
struct error *ret = (struct error *)xmalloc(sizeof(*ret));
ret->info = info;
ret->message = message;
return ret;
}
\f
#ifdef STANDALONE
#include "varargs.h"
int return_to_sender = FALSE;
int exitvalue = 0;
#ifdef DEBUG_LEVEL
int debug = DEBUG_LEVEL;
#else /* DEBUG_LEVEL */
int debug = 0;
#endif /* DEBUG_LEVEL */
/*
* test the above functions by calling parse_address for each
* argument given to the program.
*/
void
main(argc, argv)
int argc; /* count of arguments */
char **argv; /* vector of arguments */
{
char *s; /* temp string */
char *addr; /* preparsed address */
char *error; /* error message */
int form; /* form from parse_address */
char *target; /* target returned by parse_address */
char *remainder; /* remainder from parse_address */
/*
* if first argument is a number, change the debug level
*/
if (isdigit(argv[1][0])) {
debug = atoi(*++argv);
argc--;
}
/*
* loop over all arguments or read from standard input if none
*/
if (argc > 1) {
while (*++argv) {
(void)fprintf(stderr, "input: %s\n", *argv);
/* preparse the address to get rid of mutant forms */
addr = preparse_address(*argv, &error);
if (addr) {
(void)fprintf(stderr, "preparse_address: %s\n", addr);
} else {
(void)fprintf(stderr, "preparse_address: %s\n", error);
break;
}
/* see what build_uucp_route yields */
s = build_uucp_route(addr, &error);
if (s) {
(void)fprintf(stderr, "build_uucp_route: %s\n", s);
} else {
(void)fprintf(stderr, "build_uucp_route: %s\n", error);
}
/* see what parse_address yields */
form = parse_address(addr, &target, &remainder);
if (form == LOCAL) {
(void)printf("LOCAL %s\n", remainder);
} else if (form == FAIL) {
(void)fprintf(stderr, "parse_address: %s\n", remainder);
} else {
(void)printf("REMOTE %s@%s\n", remainder, target);
}
}
} else {
char line[4096];
while (gets(line) != NULL) {
(void)fprintf(stderr, "input: %s\n", line);
/* preparse the address to get rid of mutant forms */
addr = preparse_address(line, &error);
if (addr) {
(void)fprintf(stderr, "preparse_address: %s\n", addr);
} else {
(void)fprintf(stderr, "preparse_address: %s\n", error);
break;
}
/* see what build_uucp_route yields */
s = build_uucp_route(addr, &error);
if (s) {
(void)fprintf(stderr, "build_uucp_route: %s\n", s);
} else {
(void)fprintf(stderr, "build_uucp_route: %s\n", error);
}
/* see what parse_address yields */
form = parse_address(addr, &target, &remainder);
if (form == LOCAL) {
(void)printf("LOCAL %s\n", remainder);
} else if (form == FAIL) {
(void)fprintf(stderr, "parse_address: %s\n", remainder);
} else {
(void)printf("REMOTE %s@%s\n", remainder, target);
}
}
}
exit(exitvalue);
}
/*
* define panic, fatal and write_log here, rather than
* using the external routines. We are testing and just want
* the information displayed, not logged.
*/
/*VARARGS2*/
void
panic(exitcode, fmt, va_alist)
int exitcode; /* call exit(exitcode) */
char *fmt; /* printf(3) format */
va_dcl /* arguments for printf */
{
va_list ap;
va_start(ap);
(void)fprintf(stderr, "PANIC(%s): ", exitcode);
(void)vfprintf(stderr, fmt, ap);
putc('\n', stderr); /* fatal messages not \n terminated */
va_end(ap);
return_to_sender = TRUE;
exit(exitcode);
}
/*VARARGS2*/
void
write_log(log, fmt, va_alist)
int log; /* TRUE if to write global log file */
char *fmt; /* printf(3) format */
va_dcl /* arguments for printf */
{
va_list ap;
va_start(ap);
(void)fprintf(stderr, log? "PUBLIC: ": "PRIVATE: ");
(void)vfprintf(stderr, fmt, ap);
putc('\n', stderr);
va_end(ap);
}
#endif /* STANDALONE */