This is addr.c in view mode; [Download] [Up]
/* @(#)src/addr.c 1.2 24 Oct 1990 05:21:33 */ /* * 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(); /* * 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 */ } /* * 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; } /* * 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; } } } /* * 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 */ } /* * 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; } /* * 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; } #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 */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.