From: sources@anomaly.sbs.risc.net
Subject: v01i109 - ELM v2.3.11, Electronic Mail Reader - Part 09/20
Reply-To: sources@anomaly.sbs.risc.net
Organization: The Rhode Island Internet Systems Cooperative Network
Date: Sun, 13 Sep 1992 17:41:26 GMT
Followup-To: biz.sco.general


#!/bin/sh
# this is part.09 (part 9 of a multipart archive)
# do not concatenate these parts, unpack them in order with /bin/sh
# file src/addr_util.c continued
#
if test ! -r shar3_seq_.tmp; then
	echo "Please unpack part 1 first!"
	exit 1
fi
(read Scheck
 if test "$Scheck" != 9; then
	echo "Please unpack part $Scheck next!"
	exit 1
 else
	exit 0
 fi
) < shar3_seq_.tmp || exit 1
echo "x - Continuing file src/addr_util.c"
sed 's/^X//' << 'SHAR_EOF' >> src/addr_util.c &&
X	    expanded_information++;
X	  }
X	  else if (strlen(word) > 0) {
X	    if (valid_name(word)) {
X	      if (j > 0 && next_word[0] == '(')	/* already has full name */
X		gecos = NULL;
X	      else				/* needs a full name */
X		gecos=get_full_name(word);
X#if defined(INTERNET) & defined(USE_DOMAIN)
X	      sprintf(full_to, "%s%s%s@%s%s%s%s",
X		      full_to,
X		      (full_to[0] != '\0'? ", " : ""),
X		      word,
X		      hostfullname,
X		      (gecos ? " (" : ""),
X		      (gecos ? gecos : ""),
X		      (gecos ? ")" : ""));
X#else /* INTERNET and USE_DOMAIN */
X	      sprintf(full_to, "%s%s%s%s%s%s",
X		      full_to,
X		      (full_to[0] != '\0'? ", " : ""),
X		      word,
X		      (gecos ? " (" : ""),
X		      (gecos ? gecos : ""),
X		      (gecos ? ")" : ""));
X#endif /* INTERNET and USE_DOMAIN */
X	    } else if (check_only) {
X	      printf("(alias \"%s\" is unknown)\n\r", word);
X	      changed++;
X	    }
X	    else if (! isatty(fileno(stdin)) ) {	/* batch mode error! */
X	      fprintf(stderr,"Cannot expand alias '%s'!\n\r", word);
X	      fprintf(stderr,"Use \"checkalias\" to find valid addresses!\n\r");
X	      dprint(1, (debugfile,
X		      "Can't expand alias %s - bailing out of build_address\n", 
X		      word));
X	      leave(0);
X	    }
X	    else {
X	      dprint(2,(debugfile,"Entered unknown address %s\n", word));
X	      sprintf(buffer, "'%s' is an unknown address.  Replace with: ", 
X	              word);
X	      word[0] = '\0';
X	      changed++;
X
X	      PutLine0(LINES, 0, buffer);
X		
X	      (void)optionally_enter(word, LINES, strlen(buffer), FALSE, FALSE);
X	      clear_error();
X	      if (strlen(word) > 0) {
X	        dprint(3,(debugfile, "Replaced with %s in build_address\n", 
X			 word));
X		goto try_new_word;
X	      }
X	      else
X		dprint(3,(debugfile, 
X		    "Address removed from TO list by build_address\n"));
X	      continue;
X	    }
X	  }
X
X	  /* and this word to the new to list */
X	  if(*new_to_list != '\0')
X	    strcat(new_to_list, " ");
X	  strcat(new_to_list, word);
X
X	  if((i = j) > 0)
X	    strcpy(word, next_word);
X	}
X
X	/* if new to list is different from original, update original */
X	if (changed)
X	  strcpy(to, new_to_list);
X
X	return( expanded_information > 0 ? 1 : 0 );
X}
X
Xint
Xreal_from(buffer, entry)
Xchar *buffer;
Xstruct header_rec *entry;
X{
X	/***** Returns true iff 's' has the seven 'from' fields, (or
X	       8 - some machines include the TIME ZONE!!!)
X	       Initialize the date and from entries in the record 
X	       and also the message received date/time if 'entry'
X	       is not NULL.  *****/
X
X	struct header_rec temp_rec, *rec_ptr;
X	char junk[STRING], timebuff[STRING], holding_from[SLEN], hold_tz[12];
X	char mybuf[BUFSIZ], *p, *q;
X	int  eight_fields = 0;
X        int mday, month, year, minutes, seconds, tz, i;
X        long gmttime;
X
X	/* set rec_ptr according to whether the data is to be returned
X	 * in the second argument */
X	rec_ptr = (entry == NULL ? &temp_rec : entry);
X
X	rec_ptr->year[0] = '\0';
X	timebuff[0] = '\0';
X	junk[0] = '\0';
X	hold_tz[0] = '\0';
X
X	/* From <user> <day> <month> <day> <hr:min:sec> <year> */
X
X	sscanf(buffer, "%*s %*s %*s %*s %*s %s %*s %s", timebuff, junk);
X
X	if (strlen(timebuff) < 3) {
X	  dprint(3,(debugfile, 
X		"Real_from returns FAIL [no time field] on\n-> %s\n", 
X		buffer));
X	  return(FALSE);
X	}
X
X	if (timebuff[1] != ':' && timebuff[2] != ':') { 
X	  dprint(3,(debugfile, 
X		"Real_from returns FAIL [bad time field] on\n-> %s\n", 
X		buffer));
X	  return(FALSE);
X	}
X	if (junk[0] != '\0') {	/* try for 8 field entry */
X	  junk[0] = '\0';
X	  sscanf(buffer, "%*s %*s %*s %*s %*s %s %*s %*s %s", timebuff, junk);
X	  if (junk[0] != '\0') {
X	    dprint(3, (debugfile, 
X		  "Real_from returns FAIL [too many fields] on\n-> %s\n", 
X		  buffer));
X	    return(FALSE);
X	  }
X	  eight_fields++;
X	}
X
X	/** now get the info out of the record! **/
X
X	if (eight_fields) 
X	  sscanf(buffer, "%s %s %s %s %s %s %s %s",
X	            junk, holding_from, rec_ptr->dayname, rec_ptr->month, 
X                    rec_ptr->day, rec_ptr->time, hold_tz, rec_ptr->year);
X	else
X	  sscanf(buffer, "%s %s %s %s %s %s %s",
X	            junk, holding_from, rec_ptr->dayname, rec_ptr->month, 
X                    rec_ptr->day, rec_ptr->time, rec_ptr->year);
X	
X	strncpy(rec_ptr->from, holding_from, STRING-1);
X	rec_ptr->from[STRING-1] = '\0';
X	resolve_received(rec_ptr);
X
X        /* first get everything into lower case */
X        for (p=mybuf, q=mybuf+sizeof mybuf; 
X	     *buffer && p<q; 
X	     p++, buffer++) {
X	  *p = isupper(*buffer) ? tolower(*buffer) : *buffer;
X        }
X	*p = 0;
X	p = mybuf;
X	while (!isspace(*p)) p++;	/* skip "from" */
X	SKIP_WS(p);
X	while (!isspace(*p)) p++;	/* skip from address */
X	SKIP_WS(p);
X	while (!isspace(*p)) p++;	/* skip day of week */
X	SKIP_WS(p);
X	month = prefix(month_name, p);
X	get_unix_date(p,&year, &mday, &minutes, &seconds, &tz);
X	month_len[1] = (year%4) ? 28 : 29;
X	if (mday < 0 || mday>month_len[month]) {
X	  dprint(5,(debugfile, "ridiculous day %d of month %d\n",mday,month));
X	}
X
X	minutes -= tz;
X	if (tz > 0) { /* east of Greenwich */
X	  if (minutes < 0) {
X	    if (--mday < 0) {
X	      if (--month < 0) {
X		year--; /* don't worry about 1900! */
X		month = 11;
X	      }
X	      mday = month_len[month];
X	    }
X	    minutes += 24*60;
X	  }
X	}
X	if (tz < 0) { /* west of Greenwich */
X	  if (minutes <= 24*60) {
X	    if (++mday > month_len[month]) {
X	      if (++month >= 12) {
X		year++; /* don't worry about 1999! yet?? */
X		month = 0;
X	      }
X	      mday = 0;
X	    }
X	    minutes -= 24*60;
X	  }
X	}
X        gmttime = year - 70;		 /* make base year */
X        if (gmttime < 0)
X  	  gmttime += 100;
X        gmttime = gmttime * 365 + (gmttime + 1) / 4;  /* now we have days adjusted for leap years */
X        for (i = 0; i < month; i++)
X  	  gmttime += month_len[i];
X        if (month > 1 && (year % 4) == 0)
X  	  gmttime++;			/* now to month adjusted for leap year if after feb */
X        gmttime += mday - 1;		/* and now to the day */
X        gmttime *= 24 * 60;			/* convert to minutes */
X        gmttime += minutes;
X        rec_ptr->time_sent = gmttime * 60 + seconds;	/* now unix seconds since 1/1/70 00:00 GMT */
X
X	return(rec_ptr->year[0] != '\0');
X}
X
Xforwarded(buffer, entry)
Xchar *buffer;
Xstruct header_rec *entry;
X{
X	/** Change 'from' and date fields to reflect the ORIGINATOR of 
X	    the message by iteratively parsing the >From fields... 
X	    Modified to deal with headers that include the time zone
X	    of the originating machine... **/
X
X	char machine[SLEN], buff[SLEN], holding_from[SLEN];
X
X	machine[0] = holding_from[0] = '\0';
X
X	sscanf(buffer, "%*s %s %s %s %s %s %s %*s %*s %s",
X	            holding_from, entry->dayname, entry->month, 
X                    entry->day, entry->time, entry->year, machine);
X
X	if (isdigit(entry->month[0])) { /* try for veeger address */
X	  sscanf(buffer, "%*s %s %s%*c %s %s %s %s %*s %*s %s",
X	            holding_from, entry->dayname, entry->day, entry->month, 
X                    entry->year, entry->time, machine);
X	}
X	if (isalpha(entry->year[0])) { /* try for address including tz */
X	  sscanf(buffer, "%*s %s %s %s %s %s %*s %s %*s %*s %s",
X	            holding_from, entry->dayname, entry->month, 
X                    entry->day, entry->time, entry->year, machine);
X	}
X
X	/* the following fix is to deal with ">From xyz ... forwarded by xyz"
X	   which occasionally shows up within AT&T.  Thanks to Bill Carpenter
X	   for the fix! */
X
X	if (strcmp(machine, holding_from) == 0)
X	  machine[0] = '\0';
X
X	if (machine[0] == '\0')
X	  strcpy(buff, holding_from[0] ? holding_from : "anonymous");
X	else
X	  sprintf(buff,"%s!%s", machine, holding_from);
X
X	strncpy(entry->from, buff, STRING-1);
X	entry->from[STRING-1] = '\0';
X}
X
Xparse_arpa_who(buffer, newfrom, is_really_a_to)
Xchar *buffer, *newfrom;
Xint is_really_a_to;
X{
X	/** try to parse the 'From:' line given... It can be in one of
X	    two formats:
X		From: Dave Taylor <hplabs!dat>
X	    or  From: hplabs!dat (Dave Taylor)
X
X	    Added: removes quotes if name is quoted (12/12)
X	    Added: only copies STRING characters...
X	    Added: if no comment part, copy address instead! 
X	    Added: if is_really_a_to, this is really a 'to' line
X		   and treat as if we allow embedded addresses
X	**/
X
X	int use_embedded_addresses;
X	char temp_buffer[SLEN], *temp;
X	register int i, j = 0, in_parens;
X
X	temp = (char *) temp_buffer;
X	temp[0] = '\0';
X
X	no_ret(buffer);		/* blow away '\n' char! */
X
X	if (lastch(buffer) == '>') {
X	  for (i=strlen("From: "); buffer[i] != '\0' && buffer[i] != '<' &&
X	       buffer[i] != '('; i++)
X	    temp[j++] = buffer[i];
X	  temp[j] = '\0';
X	}
X	else if (lastch(buffer) == ')') {
X	  in_parens = 1;
X	  for (i=strlen(buffer)-2; buffer[i] != '\0' && buffer[i] != '<'; i--) {
X	    switch(buffer[i]) {
X	    case ')':	in_parens++;
X			break;
X	    case '(':	in_parens--;
X			break;
X	    }
X	    if(!in_parens) break;
X	    temp[j++] = buffer[i];
X	  }
X	  temp[j] = '\0';
X	  reverse(temp);
X	}
X
X#ifdef USE_EMBEDDED_ADDRESSES
X	use_embedded_addresses = TRUE;
X#else
X	use_embedded_addresses = FALSE;
X#endif
X
X	if(use_embedded_addresses || is_really_a_to) {
X	  /** if we have a null string at this point, we must just have a 
X	      From: line that contains an address only.  At this point we
X	      can have one of a few possibilities...
X
X		  From: address
X		  From: <address>
X		  From: address ()
X	  **/
X	    
X	  if (strlen(temp) == 0) {
X	    if (lastch(buffer) != '>') {       
X	      for (i=strlen("From:");buffer[i] != '\0' && buffer[i] != '('; i++)
X		temp[j++] = buffer[i];
X	      temp[j] = '\0';
X	    }
X	    else {	/* get outta '<>' pair, please! */
X	      for (i=strlen(buffer)-2;buffer[i] != '<' && buffer[i] != ':';i--)
X		temp[j++] = buffer[i];
X	      temp[j] = '\0';
X	      reverse(temp);
X	    }
X	  }
X	}
X	  
X	if (strlen(temp) > 0) {		/* mess with buffer... */
X
X	  /* remove leading spaces and quotes... */
X
X	  while (whitespace(temp[0]) || quote(temp[0]))
X	    temp = (char *) (temp + 1);		/* increment address! */
X
X	  /* remove trailing spaces and quotes... */
X
X	  i = strlen(temp) - 1;
X
X	  while (whitespace(temp[i]) || quote(temp[i]))
X	   temp[i--] = '\0';
X
X	  /* if anything is left, let's change 'from' value! */
X
X	  if (strlen(temp) > 0) {
X	    strncpy(newfrom, temp, STRING-1);
X	    newfrom[STRING-1] = '\0';
X	  }
X	}
X}
X
X/*
XQuoting from RFC 822:
X     5.  DATE AND TIME SPECIFICATION
X
X     5.1.  SYNTAX
X
X     date-time   =  [ day "," ] date time        ; dd mm yy
X						 ;  hh:mm:ss zzz
X
X     day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
X		 /  "Fri"  / "Sat" /  "Sun"
X
X     date        =  1*2DIGIT month 2DIGIT        ; day month year
X						 ;  e.g. 20 Jun 82
X
X     month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
X		 /  "May"  /  "Jun" /  "Jul"  /  "Aug"
X		 /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"
X
X     time        =  hour zone                    ; ANSI and Military
X
X     hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT]
X						 ; 00:00:00 - 23:59:59
X
X     zone        =  "UT"  / "GMT"                ; Universal Time
X						 ; North American : UT
X		 /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4
X		 /  "CST" / "CDT"                ;  Central:  - 6/ - 5
X		 /  "MST" / "MDT"                ;  Mountain: - 7/ - 6
X		 /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7
X		 /  1ALPHA                       ; Military: Z = UT;
X						 ;  A:-1; (J not used)
X						 ;  M:-12; N:+1; Y:+12
X		 / ( ("+" / "-") 4DIGIT )        ; Local differential
X						 ;  hours+min. (HHMM)
X*/
X
X/* Translate a symbolic timezone name (e.g. EDT or NZST) to a number of
X * minutes *east* of gmt (if the local time is t, the gmt equivalent is
X * t - tz_lookup(zone)).
X * Return 0 if the timezone is not recognized.
X */
Xstatic int tz_lookup(str)
Xchar *str;
X{
X    struct tzone *p; 
X
X    for (p = tzone_info; p->str; p++) {
X	if (strcmp(p->str,str)==0) return p->offset;
X    }
X    dprint(5,(debugfile,"unknown time zone %s\n",str));
X    return 0;
X}
X
X/* Return smallest i such that table[i] is a prefix of str.  Return -1 if not
X * found.
X */
Xstatic int prefix(table, str)
Xchar **table;
Xchar *str;
X{
X    int i;
X
X    for (i=0;table[i];i++)
X	if (strncmp(table[i],str,strlen(*table))==0)
X	    return i;
X    return -1;
X}
X
X/* The following routines, get_XXX(p,...), expect p to point to a string
X * of the appropriate syntax.  They return decoded values in result parameters,
X * and return p updated to point past the parsed substring (also stripping
X * trailing whitespace).
X * Return 0 on syntax errors.
X */
X
X/* Parse a year: ['1' '9'] digit digit WS
X */
Xstatic char *
Xget_year(p, result)
Xchar *p;
Xint *result;
X{
X    int year;
X
X    if (!isdigit(*p)) {
X	dprint(5,(debugfile,"missing year: %s\n",p));
X	return 0;
X    }
X    year = atoi(p);
X    /* be nice and allow 19xx, althought that's not really kosher */
X    if (year>=1900 && year <=1999) year -= 1900;
X    if (year<0 || year>99) {
X	dprint(5,(debugfile,"ridiculous year %d\n",year));
X	return 0;
X    }
X    SKIP_DIGITS(p);
X    SKIP_WS(p);
X    *result = year;
X    return p;
X}
X
X/* Parse a time: hours ':' minutes [ ':' seconds ] WS
X * Check that 0<=hours<24, 0<=minutes,seconds<60.
X * Also allow the syntax "digit digit digit digit" with implied ':' in the
X * middle.
X * Convert to minutes and seconds, with results in (*m,*s).
X */
Xstatic char *
Xget_time(p,m,s)
Xchar *p;
Xint *m, *s;
X{
X    int hours, minutes, seconds;
X
X    /* hour */
X    if (!isdigit(*p)) {
X	dprint(5,(debugfile,"missing time: %s\n",p));
X	return 0;
X    }
X    hours = atoi(p);
X    SKIP_DIGITS(p);
X    if (*p++ != ':') {
X	/* perhaps they just wrote hhmm instead of hh:mm */
X	minutes = hours % 60;
X	hours /= 60;
X    }
X    else {
X	if (hours<0 || hours>23) {
X	    dprint(5,(debugfile,"ridiculous hour: %d\n",hours));
X	    return 0;
X	}
X	minutes = atoi(p);
X	if (minutes<0 || minutes>59) {
X	    dprint(5,(debugfile,"ridiculous minutes: %d\n",minutes));
X	    return 0;
X	}
X    }
X    SKIP_DIGITS(p);
X    if (*p == ':') {
X	p++;
X	seconds = atoi(p);
X	if (seconds<0 || seconds>59) {
X	    dprint(5,(debugfile,"ridiculous seconds: %d\n",seconds));
X	    return 0;
X	}
X	SKIP_DIGITS(p);
X    }
X    else seconds = 0;
X    minutes += hours*60;
X    SKIP_WS(p);
X    *m = minutes;
X    *s = seconds;
X    return p;
X}
X
X/* Parse a Unix date from which the leading week-day has been stripped.
X * The syntax is "Jun 21 06:45:44 CDT 1989" with timezone optional.
X * i.e., month day time [ zone ] year
X * where day::=digit*, year and time are as defined above,
X * and month and zone are alpha strings starting with a known 3-char prefix.
X * The month has already been processed by the caller, so we just skip over
X * a leading alpha* WS.
X *
X * Unlike the preceding routines, the result is not an updated pointer, but
X * simply 1 for success and 0 for failure.
X */
Xstatic int
Xget_unix_date(p,y,d,m,s,t)
Xchar *p;
Xint *y, *d, *m, *s, *t;
X{
X
X    SKIP_ALPHA(p);
X    SKIP_WS(p);
X    if (!isdigit(*p)) return 0;
X    *d = atoi(p);  /* check the value for sanity after we know the month */
X    SKIP_DIGITS(p);
X    SKIP_WS(p);
X    p = get_time(p,m,s);
X    if (!p) return 0;
X    if (isalpha(*p)) {
X	*t = tz_lookup(p);
X	SKIP_ALPHA(p);
X	SKIP_WS(p);
X    }
X    else *t = 0;
X    p = get_year(p,y);
X    if (!p) return 0;
X    return 1;
X}
X
X
X/* Parse an rfc822 (with extensions) date.  Return 1 on success, 0 on failure.
X */
Xparse_arpa_date(string, entry)
Xchar *string;
Xstruct header_rec *entry;
X{
X    char buffer[BUFSIZ], *p, *q;
X    int mday, month, year, minutes, seconds, tz, i;
X    long gmttime;
X
X    /* first get everything into lower case */
X    for (p=buffer, q=buffer+sizeof buffer; *string && p<q; p++, string++) {
X	*p = isupper(*string) ? tolower(*string) : *string;
X    }
X    *p = 0;
X    p = buffer;
X    SKIP_WS(p);
X
X    if (prefix(day_name,p)>=0) {
X	/* accept anything that *starts* with a valid day name */
X	/* also, don't check whether it's right! */
X
X	(void)strncpy(entry->dayname, p, 3);
X	entry->dayname[3] = 0;
X	SKIP_ALPHA(p);
X	SKIP_WS(p);
X
X	if (*p==',') {
X	    p++;
X	    SKIP_WS(p);
X	}
X	/* A comma is required here, but we'll be nice guys and look the other
X	 * way if it's missing.
X	 */
X    }
X
X    /* date */
X
X    /* day of the month */
X    if (!isdigit(*p)) {
X	/* Missing day.  Maybe this is a Unix date?
X	 */
X	month = prefix(month_name,p);
X	if (month >= 0 &&
X	    get_unix_date(p, &year, &mday, &minutes, &seconds, &tz)) {
X		goto got_date;
X	}
X	dprint(5,(debugfile,"missing day: %s\n",p));
X	return 0;
X    }
X    mday = atoi(p);  /* check the value for sanity after we know the month */
X    SKIP_DIGITS(p);
X    SKIP_WS(p);
X
X    /* month name */
X    month = prefix(month_name,p);
X    if (month < 0) {
X	dprint(5,(debugfile,"missing month: %s\n",p));
X	return 0;
X    }
X    SKIP_ALPHA(p);
X    SKIP_WS(p);
X
X    /* year */
X    if (!(p = get_year(p,&year))) return 0;
X
X    /* time */
X    if (!(p = get_time(p,&minutes,&seconds))) return 0;
X
X    /* zone */
X    for (q=p; *q && !isspace(*q); q++) continue;
X    *q = 0;
X    if (*p=='-' || *p=='+') {
X	char sign = *p++;
X
X	if (isdigit(*p)) {
X	    for (i=0; i<4; i++) {
X		if (!isdigit(p[i])) {
X		    dprint(5,(debugfile,"ridiculous numeric timezone: %s\n",p));
X		    return 0;
X		}
X		p[i] -= '0';
X	    }
X	    tz = (p[0]*10 + p[1])*60 + p[2]*10 + p[3];
X	    if (sign=='-') tz = -tz;
X	    sprintf(entry->time_zone, "%d", tz);
X	}
X	else {
X	    /* some brain-damaged dates use a '-' before a symbolic time zone */
X	    SKIP_WS(p);
X	    strncpy(entry->time_zone, p, sizeof(entry->time_zone) - 1);
X	    tz = tz_lookup(p);
X	}
X    }
X    else {
X	tz = tz_lookup(p);
X	strncpy(entry->time_zone, p, sizeof(entry->time_zone) - 1);
X    }
X
Xgot_date:
X    month_len[1] = (year%4) ? 28 : 29;
X    if (mday<0 || mday>month_len[month]) {
X	dprint(5,(debugfile,"ridiculous day %d of month %d\n",mday,month));
X	return 0;
X    }
X
X    /* convert back to symbolic form (silly, but the rest of the program
X     * expects it and I'm not about to change all that!)
X     */
X    sprintf(entry->year, "%02d", year);
X    sprintf(entry->month, "%s", month_name[month]);
X    entry->month[0] = toupper(entry->month[0]);
X    sprintf(entry->day, "%d", mday);
X    sprintf(entry->time, "%02d:%02d:%02d",minutes/60,minutes%60,seconds);
X
X    /* shift everything to UTC (aka GMT) before making long time for sorting */
X    minutes -= tz;
X    if (tz > 0) { /* east of Greenwich */
X	if (minutes < 0) {
X	    if (--mday < 0) {
X		if (--month < 0) {
X		    year--; /* don't worry about 1900! */
X		    month = 11;
X		}
X		mday = month_len[month];
X	    }
X	    minutes += 24*60;
X	}
X    }
X    if (tz < 0) { /* west of Greenwich */
X	if (minutes >= 24*60) {
X	    if (++mday > month_len[month]) {
X		if (++month >= 12) {
X		    year++; /* don't worry about 1999! */
X		    month = 0;
X		}
X		mday = 0;
X	    }
X	    minutes -= 24*60;
X	}
X    }
X    gmttime = year - 70;		 /* make base year */
X    if (gmttime < 0)
X	gmttime += 100;
X    gmttime = gmttime * 365 + (gmttime + 1) / 4;  /* now we have days adjusted for leap years */
X    for (i = 0; i < month; i++)
X	gmttime += month_len[i];
X    if (month > 1 && (year % 4) == 0)
X	gmttime++;			/* now to month adjusted for leap year if after feb */
X    gmttime += mday - 1;		/* and now to the day */
X    gmttime *= 24 * 60;			/* convert to minutes */
X    gmttime += minutes;
X    entry->time_sent = gmttime * 60;	/* now unix seconds since 1/1/70 00:00 GMT */
X
X    return 1;
X}
X
Xfix_arpa_address(address)
Xchar *address;
X{
X	/** Given a pure ARPA address, try to make it reasonable.
X
X	    This means that if you have something of the form a@b@b make 
X            it a@b.  If you have something like a%b%c%b@x make it a%b@x...
X	**/
X
X	register int host_count = 0, i;
X	char     hosts[MAX_HOPS][NLEN];	/* array of machine names */
X	char     *host, *addrptr;
X
X	/*  break down into a list of machine names, checking as we go along */
X	
X	addrptr = (char *) address;
X
X	while ((host = get_token(addrptr, "%@", 2)) != NULL) {
X	  for (i = 0; i < host_count && ! equal(hosts[i], host); i++)
X	      ;
X
X	  if (i == host_count) {
X	    strcpy(hosts[host_count++], host);
X	    if (host_count == MAX_HOPS) {
X	       dprint(2, (debugfile, 
X           "Can't build return address - hit MAX_HOPS in fix_arpa_address\n"));
X	       error("Can't build return address - hit MAX_HOPS limit!");
X	       return(1);
X	    }
X	  }
X	  else 
X	    host_count = i + 1;
X	  addrptr = NULL;
X	}
X
X	/** rebuild the address.. **/
X
X	address[0] = '\0';
X
X	for (i = 0; i < host_count; i++)
X	  sprintf(address, "%s%s%s", address, 
X	          address[0] == '\0'? "" : 
X	 	    (i == host_count - 1 ? "@" : "%"),
X	          hosts[i]);
X
X	return(0);
X}
X
Xfigure_out_addressee(buffer, mail_to)
Xchar *buffer;
Xchar *mail_to;
X{
X	/** This routine steps through all the addresses in the "To:"
X	    list, initially setting it to the first entry (if mail_to
X	    is NULL) or, if the user is found (eg "alternatives") to
X	    the current "username".
X
X	    Modified to know how to read quoted names...
X	    also modified to look for a comma or eol token and then
X	    try to give the maximal useful information when giving the
X	    default "to" entry (e.g. "Dave Taylor <taylor@hpldat>"
X	    will now give "Dave Taylor" rather than just "Dave")
X	**/
X
X	char *address, *bufptr, mybuf[SLEN];
X	register int index2 = 0;
X	
X	if (equal(mail_to, username)) return;	/* can't be better! */
X
X	bufptr = (char *) buffer;	       /* use the string directly   */
X
X	if (index(buffer,'"') != NULL) {	/* we have a quoted string */
X	  while (*bufptr != '"')
X	    bufptr++;
X	  bufptr++;	/* skip the leading quote */
X	  while (*bufptr != '"' && *bufptr)
X	    mail_to[index2++] = *bufptr++;
X	  mail_to[index2] = '\0';
X	}
X
X	else  {
X
X	  while ((address = strtok(bufptr, ",\t\n\r")) != NULL) {
X
X	    if (! okay_address(address, "don't match me!")) {
X	      strcpy(mail_to, username);	/* it's to YOU! */
X	      return;
X	    }
X	    else if (strlen(mail_to) == 0) {	/* it's SOMEthing! */
X	
X	      /** this next bit is kinda gory, but allows us to use the
X		  existing routines to parse the address - by pretending
X		  it's a From: line and going from there...
X	          Ah well - you get what you pay for, right?
X	      **/
X
X	      if (strlen(address) > (sizeof mybuf) - 7)	/* ensure it ain't */
X		address[(sizeof mybuf)-7] = '\0';	/*  too long mon!  */
X
X	      sprintf(mybuf, "From: %s", address);
X	      parse_arpa_who(mybuf, mail_to, TRUE);
X/**
X	      get_return_name(address, mail_to, FALSE);
X**/
X	    }
X
X	    bufptr = (char *) NULL;	/* set to null */
X	  }
X	}
X
X	return;
X}
SHAR_EOF
echo "File src/addr_util.c is complete" &&
# ============= src/alias.c ==============
echo "x - extracting src/alias.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/alias.c &&
X
Xstatic char rcsid[] = "@(#)$Id: alias.c,v 4.1.1.1 90/06/05 21:20:26 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1.1.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein, Elm Coordinator
X *	elm@DSI.COM			dsinc!elm
X *
X *******************************************************************************
X * $Log:	alias.c,v $
X * Revision 4.1.1.1  90/06/05  21:20:26  syd
X * Make temp file name less than 14 chars to allow deleting
X * aliases on a 14 char limit file system
X * From: Syd
X * 
X * Revision 4.1  90/04/28  22:42:25  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
X/** This file contains alias stuff
X
X**/
X
X#include "headers.h"
X#include <errno.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X
X#ifdef BSD
X#undef        tolower
X#endif
X
X#define	ECHOIT	1 	/* echo on for prompting */
X
Xchar *get_alias_address();
Xchar *error_name(), *error_description(), *strip_parens(), *index();
X
Xextern int errno;
X
X#ifndef DONT_TOUCH_ADDRESSES
Xchar *expand_system();
X
Xextern int  findnode_has_been_initialized;
X#endif
X
Xint
Xok_alias_name(name)
Xchar *name;
X{
X	while ( *name != '\0' && ok_alias_char(*name) )
X	  ++name;
X	return ( *name == '\0' );
X}
X
X
Xread_alias_files()
X{
X	/** read the system and user alias files, if present, and if they
X	    have changed since last we read them.
X	**/
X
X	read_system_aliases();
X	read_user_aliases();
X}
X
Xread_system_aliases()
X{
X	/** read the system alias file, if present,
X	    and if it has changed since last we read it.
X	**/
X
X	struct stat hst;
X	static time_t system_ctime, system_mtime;
X
X	/* If hash file hasn't changed, don't bother re-reading. */
X
X	if (system_data != -1
X	 && stat(system_hash_file, &hst) == 0
X	 && hst.st_ctime == system_ctime
X	 && hst.st_mtime == system_mtime)
X	  return;
X
X	/* Close system data file if it was open. */
X
X	if (system_data != -1) {
X	  close(system_data);
X	  system_data = -1;
X	}
X
X	/* Read system hash table.  If we can't, just return. */
X
X	if (read_hash_file(system_hash_file, (char *) system_hash_table,
X			    sizeof system_hash_table) < 0)
X	  return;
X
X	/* Open system data table. */
X
X	if ((system_data = open(system_data_file, O_RDONLY)) == -1) {
X	  dprint(1, (debugfile,
X		      "Warning: Can't open system alias data file %s\n",
X		      system_data_file));
X	  return;
X	}
X
X	/* Remember hash file times. */
X
X	system_ctime = hst.st_ctime;
X	system_mtime = hst.st_mtime;
X}
X
Xread_user_aliases()
X{
X	/** read the system alias file, if present,
X	    and if it has changed since last we read it.
X	**/
X
X	struct stat hst;
X	char fname[SLEN];
X	static time_t user_ctime, user_mtime;
X
X	/* If hash file hasn't changed, don't bother re-reading. */
X
X	sprintf(fname, "%s/%s", home, ALIAS_HASH);
X
X	if (user_data != -1
X	 && stat(fname, &hst) == 0
X	 && hst.st_ctime == user_ctime
X	 && hst.st_mtime == user_mtime)
X	  return;
X
X	/* Close user data file if it was open. */
X
X	if (user_data != -1) {
X	  close(user_data);
X	  user_data = -1;
X	}
X
X	/* Read user hash table.  If we can't, just return. */
X
X	if (read_hash_file(fname, (char *) user_hash_table,
X			      sizeof user_hash_table) < 0)
X	  return;
X
X	/* Open user data table. */
X
X	sprintf(fname, "%s/%s", home, ALIAS_DATA);
X	if ((user_data = open(fname, O_RDONLY)) == -1) {
X	  dprint(1, (debugfile,
X	     "Warning: Can't open user alias data file %s\n", fname));
X	  return;
X	}
X
X	/* Remember hash file times. */
X
X	user_ctime = hst.st_ctime;
X	user_mtime = hst.st_mtime;
X}
X
Xint
Xread_hash_file(file, table, table_size)
Xchar *file, *table;
Xint table_size;
X{
X	/** read the specified alias hash file into the specified table.
X	    it must be _exactly_ the right size or forget it.
X	**/
X
X	struct stat st;
X	int hash;
X
X	/* Open the hash file. */
X
X	if ((hash = open(file, O_RDONLY)) == -1) {
X	  dprint(2, (debugfile,
X	    "Warning: Can't open alias hash file %s\n", file));
X	  return -1;
X	}
X
X	/* Be sure the hash file is the correct size. */
X
X	if (fstat(hash, &st) == 0 && st.st_size != table_size) {
X	  dprint(2, (debugfile,
X	    "Warning: Alias hash file %s is wrong size (%ld/%d)\n",
X	    file, st.st_size, table_size));
X	  close(hash);
X	  return -1;
X	}
X
X	/* Read the hash file into memory. */
X
X	if (read(hash, (char *) table, table_size) != table_size) {
X	  dprint(2, (debugfile,
X	    "Warning: Can't read alias hash file %s\n", file));
X	  close(hash);
X	  return -1;
X	}
X
X	/* Close the hash file; return success. */
X
X	close(hash);
X	return 0;
X}
X
Xint
Xadd_alias()
X{
X	/** add an alias to the user alias text file.  Return zero 
X	    if alias not added in actuality **/
X
X	char name[SLEN], *address, address1[LONG_STRING], buffer[LONG_STRING];
X	char comment[SLEN], ch;
X	char *strcpy();
X
X	strcpy(buffer, "Enter alias name: ");
X	PutLine0(LINES-2,0, buffer);
X	CleartoEOLN();
X	*name = '\0';
X	optionally_enter(name, LINES-2, strlen(buffer), FALSE, FALSE);
X	if (strlen(name) == 0) 
X	  return(0);
X        if ( !ok_alias_name(name) ) {
X	  error1("Bad character(s) in alias name %s.", name);
X	  return(0);
X	}
X	if ((address = get_alias_address(name, FALSE)) != NULL) {
X	  dprint(3, (debugfile, 
X		 "Attempt to add a duplicate alias [%s] in add_alias\n",
X		 address)); 
X	  if (address[0] == '!') {
X	    address[0] = ' ';
X	    error1("Already a group with name %s.", address);
X	  }
X	  else
X	    error1("Already an alias for %s.", address);
X	  return(0);
X	}
X
X	sprintf(buffer, "Enter full name for %s: ", name);
X	PutLine0(LINES-2,0, buffer);
X	CleartoEOLN();
X	*comment = '\0';
X	optionally_enter(comment, LINES-2, strlen(buffer), FALSE, FALSE);
X	if (strlen(comment) == 0) strcpy(comment, name);  
X
X	sprintf(buffer, "Enter address for %s: ", name);
X	PutLine0(LINES-2,0, buffer);
X	CleartoEOLN();
X	*address1 = '\0';
X	optionally_enter(address1, LINES-2, strlen(buffer), FALSE, FALSE);
X	Raw(ON);
X	if (strlen(address1) == 0) {
X	  error("No address specified!");
X	  return(0);
X	}
X	PutLine3(LINES-2,0,"%s (%s) = %s", comment, name, address1);
X	CleartoEOLN();
X	if((ch = want_to("Accept new alias? (y/n) ",'y')) == 'y')
X	  add_to_alias_text(name, comment, address1);
X	ClearLine(LINES-2);
X	return(ch == 'y' ? 1 : 0);
X}
X
Xint
Xdelete_alias()
X{
X	/** delete an alias from the user alias text file.  Return zero 
X	    if alias not deleted in actuality **/
X
X	char name[SLEN], *address, buffer[LONG_STRING];
X	char *strcpy();
X
X	strcpy(buffer, "Enter alias name for deletion: ");
X	PutLine0(LINES-2,0, buffer);
X	CleartoEOLN();
X	*name = '\0';
X	optionally_enter(name, LINES-2, strlen(buffer), FALSE, FALSE);
X	if (strlen(name) == 0) 
X	  return(0);
X        if ((address = get_alias_address(name, FALSE))!=NULL)
X        {
X                if (address[0] == '!')
X                {
X                  address[0] = ' ';
X                  PutLine1(LINES-1,0,"Group alias: %-60.60s", address);
X                  CleartoEOLN();
X	        }
X		else
X		  PutLine1(LINES-1,0,"Aliased address: %-60.60s", address);
X	}
X        else 
X	{
X		  dprint(3, (debugfile, 
X		 "Attempt to delete a non-existent alias [%s] in delete_alias\n",
X		 name)); 
X		 error1("No alias for %s.", name);
X	  	return(0);
X	}
X	if (want_to("Delete this alias? (y/n) ", 'y') == 'y')
X	{
X		if (!delete_from_alias_text(name))
X		{
X			CleartoEOS();
X			return(1);
X		}
X	}
X	CleartoEOS();
X	return(0);
X}
X
Xint
Xadd_current_alias()
X{
X	/** alias the current message to the specified name and
X	    add it to the alias text file, for processing as
X	    the user leaves the program.  Returns non-zero iff
X	    alias actually added to file **/
X
X	char name[SLEN], address1[LONG_STRING], buffer[LONG_STRING], *address;
X	char comment[SLEN], ch;
X	struct header_rec *current_header;
X
X	if (current == 0) {
X	 dprint(4, (debugfile, 
X		"Add current alias called without any current message!\n"));
X	 error("No message to alias to!");
X	 return(0);
X	}
X	current_header = headers[current - 1];
X	
X	strcpy(buffer, "Current message address aliased to: ");
X	PutLine0(LINES-2,0, buffer);
X	CleartoEOLN();
X	*name = '\0';
X	optionally_enter(name, LINES-2, strlen(buffer), FALSE, FALSE);
X	if (strlen(name) == 0)	/* cancelled... */
X	  return(0);
X        if ( !ok_alias_name(name) ) {
X	  error1("Bad character(s) in alias name %s.", name);
X	  return(0);
X	}
X	if ((address = get_alias_address(name, FALSE)) != NULL) {
X	 dprint(3, (debugfile,
X	         "Attempt to add a duplicate alias [%s] in add_current_alias\n",
X		 address)); 
X	  if (address[1] == '!') {
X	    address[0] = ' ';
X	    error1("Already a group with name %s.", address);
X	  }
X	  else 
X	    error1("Already an alias for %s.", address); 
X          return(0);
X	}
X
X	sprintf(buffer, "Enter full name for %s: ", name);
X	PutLine0(LINES-2,0, buffer);
X	CleartoEOLN();
X
X	/* use full name in current message for default comment */
X	tail_of(current_header->from, comment, current_header->to);
X	if(index(comment, '!') || index(comment, '@'))
X	  /* never mind - it's an address not a full name */
X	  *comment = '\0';
X
X	optionally_enter(comment, LINES-2, strlen(buffer), FALSE, FALSE);
X
X	/* grab the return address of this message */
X	get_return(address1, current-1);
X
X	strcpy(address1, strip_parens(address1));	/* remove parens! */
X#ifdef OPTIMIZE_RETURN
X	optimize_return(address1);
X#endif
X	PutLine3(LINES-2,0,"%s (%s) = %s", comment, name, address1);
X	CleartoEOLN();
X	if((ch = want_to("Accept new alias? (y/n) ",'y')) == 'y')
X	  add_to_alias_text(name, comment, address1);
X	ClearLine(LINES-2);
X	return(ch == 'y' ? 1 : 0);
X}
X
Xadd_to_alias_text(name, comment, address)
Xchar *name, *comment, *address;
X{
X	/** Add the data to the user alias text file.  Return zero if we
X	    succeeded, 1 if not **/
X	
X	FILE *file;
X	char fname[SLEN];
X	
X	sprintf(fname,"%s/%s", home, ALIAS_TEXT);
X	
X	save_file_stats(fname);
X	if ((file = fopen(fname, "a")) == NULL) {
X	  dprint(2, (debugfile, 
X		 "Failure attempting to add alias to file %s within %s",
X		   fname, "add_to_alias_text"));
X	  dprint(2, (debugfile, "** %s - %s **\n", error_name(errno), 
X		   error_description(errno)));
X	  error1("Couldn't open %s to add new alias!", fname);
X	  return(1);
X	}
X
X	if (fprintf(file,"%s = %s = %s\n", name, comment, address) == EOF) {
X	    dprint(2, (debugfile,
X		       "Failure attempting to write alias to file within %s",
X		       fname, "add_to_alias_text"));
X	    dprint(2, (debugfile, "** %s - %s **\n", error_name(errno),
X		       error_description(errno)));
X	    error1("Couldn't write alias to file %s!", fname);
X	    fclose(file);
X	    return(1);
X	}
X
X	fclose(file);
X
X	restore_file_stats(fname);
X
X	return(0);
X}
X
Xdelete_from_alias_text(name)
Xchar *name;
X{
X	/** Delete the data from the user alias text file.  Return zero if we
X	    succeeded, 1 if not **/
X	
X	FILE *file, *tmp_file;
X	char fname[SLEN], tmpfname[SLEN];
X	char line_in_file[SLEN+3+SLEN+3+LONG_STRING]; /* name = comment = address */
X	char name_with_equals[SLEN+2];
X
X	strcpy(name_with_equals, name);
X	strcat(name_with_equals, " ="); 
X
X	sprintf(fname,"%s/%s", home, ALIAS_TEXT);
X	sprintf(tmpfname,"%s/%s.t", home, ALIAS_TEXT);
X	
X	save_file_stats(fname);
X
X	if ((file = fopen(fname, "r")) == NULL) {
X	  dprint(2, (debugfile, 
X		 "Failure attempting to delete alias from file %s within %s",
X		   fname, "delete_from_alias_text"));
X	  dprint(2, (debugfile, "** %s - %s **\n", error_name(errno), 
X		   error_description(errno)));
X	  error1("Couldn't open %s to delete alias!", fname);
X	  return(1);
X	}
X
X	if ((tmp_file = fopen(tmpfname, "w")) == NULL) {
X	  dprint(2, (debugfile, 
X		 "Failure attempting to open temp file %s within %s",
X		   tmpfname, "delete_from_alias_text"));
X	  dprint(2, (debugfile, "** %s - %s **\n", error_name(errno), 
X		   error_description(errno)));
X	  error1("Couldn't open tempfile %s to delete alias!", tmpfname);
X	  return(1);
X	}
X
X	while (fgets(line_in_file, sizeof(line_in_file), file) != (char *)NULL)
X	{
X		if (strncmp(name_with_equals, line_in_file,
X				strlen(name_with_equals)) != 0)
X			if (fprintf(tmp_file,"%s", line_in_file) == EOF) {
X			    dprint(2, (debugfile, 
X				       "Failure attempting to write to temp file %s within %s",
X				       tmpfname, "delete_from_alias_text"));
X			    dprint(2, (debugfile, "** %s - %s **\n", error_name(errno), 
X				       error_description(errno)));
X			    error1("Couldn't write to tempfile %s!", tmpfname);
X			    fclose(file);
X			    fclose(tmp_file);
X			    unlink(tmpfname);
X			    return(1);
X			}
X	}
X	fclose(file);
X	fclose(tmp_file);
X	if (rename(tmpfname, fname) != 0)
X	{
X		error1("Couldn't rename tempfile %s after deleting alias!", tmpfname);
X		return(1);
X	}
X
X	restore_file_stats(fname);
X
X	return(0);
X}
X
Xshow_alias_menu()
X{
X	MoveCursor(LINES-7,0); CleartoEOS();	
X		
X	PutLine0(LINES-7,COLUMNS-45, "Alias commands");
X	Centerline(LINES-6,
X      "a)lias current message, d)elete an alias, check a p)erson or s)ystem,");
X	Centerline(LINES-5,
X	  "l)ist existing aliases, m)ake new alias, or r)eturn");
X}
X
Xalias()
X{
X	/** work with alias commands... **/
X	/** return non-0 if main part of screen overwritten, else 0 **/
X
X	char name[NLEN], *address, ch, buffer[SLEN];
X	int  newaliases = 0, redraw = 0;
X
X	if (mini_menu)
X	  show_alias_menu();
X
X	/** now let's ensure that we've initialized everything! **/
X
X#ifndef DONT_TOUCH_ADDRESSES
X	
X	if (! findnode_has_been_initialized) {
X	  if (warnings)
X	    error("Initializing internal tables...");
X#ifndef USE_DBM
X	  get_connections();
X	  open_domain_file();
X#endif
X	  init_findnode();
X	  clear_error();
X          findnode_has_been_initialized = TRUE;
X	}
X
X#endif
X
X	define_softkeys(ALIAS);
X
X	while (1) {
X	  prompt("Alias: ");
X	  CleartoEOLN();
X	  ch = ReadCh();
X	  MoveCursor(LINES-1,0); CleartoEOS();
X	  
X	  dprint(3, (debugfile, "\n-- Alias command: %c\n\n", ch));
X
X	  switch (tolower(ch)) {
X	    case '?': redraw += alias_help();			break;
X
X	    case 'a': newaliases += add_current_alias();	break;
X	    case 'd': if (delete_alias()) install_aliases();	break;
X	    case 'l': display_aliases();
X		      redraw++;
X		      if (mini_menu) show_alias_menu();
X		      break;
X	    case 'm': newaliases += add_alias(); 		break;
X
X	    case RETURN:
X	    case LINE_FEED:
X	    case 'q':
X	    case 'x':
X	    case 'r': if (newaliases) install_aliases();
X		      clear_error();
X		      return(redraw);
X	    case 'p': if (newaliases) 
X			error("Warning: new aliases not installed yet!");
X
X		      strcpy(buffer, "Check for person: ");
X		      PutLine0(LINES-2,0, buffer);
X		      CleartoEOLN();
X		      *name = '\0';
X		      optionally_enter(name, LINES-2, strlen(buffer),
X			FALSE, FALSE);
X
X		      if ((address = get_alias_address(name, FALSE))!=NULL) {
X	                if (address[0] == '!') {
X	                  address[0] = ' ';
X	                  PutLine1(LINES-1,0,"Group alias:%-60.60s", address);
X	                  CleartoEOLN();
X		        }
X			else
X			  PutLine1(LINES-1,0,"Aliased address: %-60.60s", 
X			  address);
X		      }
X	              else 
X			error("Not found.");
X		      break;
X
X	    case 's': strcpy(buffer, "Check for system: ");
X		      PutLine0(LINES-2,0, buffer);
X		      CleartoEOLN();
X		      *name = '\0';
X		      optionally_enter(name, LINES-2, strlen(buffer),
X			FALSE, FALSE);
X		      if (talk_to(name)) 
X#ifdef INTERNET
X			PutLine1(LINES-1,0,
X		"You have a direct connection. The address is USER@%s.", 
X			name);
X#else
X			PutLine1(LINES-1,0,
X		"You have a direct connection. The address is %s!USER.", 
X			name);
X#endif
X		      else {
X		        sprintf(buffer, "USER@%s", name);
X#ifdef DONT_TOUCH_ADDRESSES
X	 	        address = buffer;
X#else
X	 	        address = expand_system(buffer, FALSE);
X#endif
X		        if (strlen(address) > strlen(name) + 7)
X		          PutLine1(LINES-1,0,"Address is: %.65s", address);
X		        else
X		          error1("Couldn't expand system %s.", name);
X		      }
X		      break;
X
X	    case '@': strcpy(buffer, "Fully expand alias: ");
X		      PutLine0(LINES-2,0, buffer);
X		      CleartoEOS();
X		      *name = '\0';
X		      optionally_enter(name, LINES-2, strlen(buffer),
X			FALSE, FALSE);
X		      if ((address = get_alias_address(name, TRUE)) != NULL) {
X	                ClearScreen();
X			PutLine1(3,0,"Aliased address:\n\r%s", address);
X	                PutLine0(LINES-1,0,"Press <return> to continue.");
X			(void) getchar();
X			redraw++;
X		      }
X	              else 
X			error("Not found.");
X		      if (mini_menu) show_alias_menu();
X		      break;
X	    default : error("Invalid input!");
X	  }
X	}
X}
X
Xinstall_aliases()
X{
X	/** run the 'newalias' program and update the
X	    aliases before going back to the main program! 
X	**/
X
X
X	error("Updating aliases...");
X	sleep(2);
X
X	if (system_call(newalias, SH, FALSE, FALSE) == 0) {
X	  error("Re-reading the database in...");
X	  sleep(2);
X	  read_alias_files();
X	  set_error("Aliases updated successfully.");
X	}
X	else
X	  set_error("'Newalias' failed.  Please check alias_text.");
X}
X
Xalias_help()
X{
X	/** help section for the alias menu... **/
X	/** return non-0 if main part of screen overwritten, else 0 */
X	
X	char ch;
X	int  redraw=0;
X	char *alias_prompt = mini_menu ? "Key: " : "Key you want help for: ";
X
X	MoveCursor(LINES-3, 0);	CleartoEOS();
X
X	if (mini_menu) {
X	  Centerline(LINES-3,
X "Press the key you want help for, '?' for a key list, or '.' to exit help");
X	}
X
X	lower_prompt(alias_prompt);
X
X	while ((ch = ReadCh()) != '.') {
X	  ch = tolower(ch);
X	  switch(ch) {
X	    case '?' : display_helpfile(ALIAS_HELP);	
X		       redraw++;
X	               if (mini_menu) show_alias_menu();
X		       return(redraw);
X	    case 'a': error(
X	    "a = Add (return) address of current message to alias database.");
X		      break;
X	    case 'd': error("d = Delete a user alias from alias database.");
X		      break;
X	    case 'l': error("l = List all aliases in database.");
X		      break;
X	    case 'm': error(
X	    "m = Make a new user alias, adding to alias database when done.");
X		      break;
X
X	    case RETURN:
X	    case LINE_FEED:
X	    case 'q':
X	    case 'x':
X	    case 'r': error("Return from alias menu.");
X	   	      break;
X		      
X	    case 'p': error("p = Check for a person in the alias database.");
X		      break;
X	
X	    case 's': error(
X		"s = Check for a system in the host routing/domain database.");
X		      break;
X	
X	    default : error("That key isn't used in this section.");
X	   	      break;
X	  }
X	  lower_prompt(alias_prompt);
X	}
X	return(redraw);
X}
X
Xdisplay_aliases()
X{
X	char fname[SLEN];
X	
X	sprintf(fname,"%s/%s", home, ALIAS_TEXT);
X	display_file(fname);
X	ClearScreen();
X	return;
X}
SHAR_EOF
# ============= src/aliasdb.c ==============
echo "x - extracting src/aliasdb.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/aliasdb.c &&
X
Xstatic char rcsid[] = "@(#)$Id: aliasdb.c,v 4.1.1.1 90/06/21 23:21:47 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1.1.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein, Elm Coordinator
X *	elm@DSI.COM			dsinc!elm
X *
X *******************************************************************************
X * $Log:	aliasdb.c,v $
X * Revision 4.1.1.1  90/06/21  23:21:47  syd
X * Add missing check for eof and for failure of scanf
X * From: Ross Johnson
X * 
X * Revision 4.1  90/04/28  22:42:28  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
X/** Alias database files...
X
X**/
X
X
X#include "headers.h"
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <errno.h>
X
Xextern int errno;
X
X#ifdef USE_DBM
X# include <dbm.h>
X#endif
X
X#define  absolute(x)		((x) > 0 ? x : -(x))
X
Xchar *find_path_to(), *strcat(), *strcpy();
Xunsigned long sleep();
X
X#ifndef DONT_TOUCH_ADDRESSES
Xint  findnode_has_been_initialized = FALSE;
X#endif
X
Xfindnode(name, display_error)
Xchar *name;
Xint   display_error;
X{
X	/** break 'name' into machine!user or user@machine and then
X	    see if you can find 'machine' in the path database..
X	    If so, return name as the expanded address.  If not,
X	    return what was given to us!   If display_error, then
X	    do so...
X	**/
X
X#ifndef DONT_TOUCH_ADDRESSES
X	
X	char   old_name[SLEN];
X	char   address[SLEN];
X
X	if (strlen(name) == 0)
X	  return;
X	
X	if (! findnode_has_been_initialized) {
X	  if (warnings)
X	    error("Initializing internal tables...");
X#ifndef USE_DBM
X	  get_connections();
X	  open_domain_file();
X#endif
X	  init_findnode();
X	  clear_error();
X          findnode_has_been_initialized = TRUE;
X	}
X
X	strcpy(old_name, name);		/* save what we were given */
X
X	if (expand_site(name, address) == -1) {
X          if (display_error && name[0] != '!') {
X	    dprint(3, (debugfile, "Couldn't expand host %s in address.\n",
X	             name));
X	    if (! check_only && warnings) {
X	      error1("Couldn't expand system %s.", name);
X	      sleep(1);
X	    }
X	  }
X	  strcpy(name, old_name);	/* and restore... */
X	}
X	else
X	  strcpy(name, address);
X#endif
X	return;
X}
X
X#if defined(OPTIMIZE_RETURN) || !defined(DONT_TOUCH_ADDRESSES)
Xint
Xexpand_site(cryptic, expanded)
Xchar *cryptic, *expanded;
X{
X
X	/** Given an address of the form 'xyz@site' or 'site!xyz'
X	    return an address of the form <expanded address for site>
X            with 'xyz' embedded according to the path database entry.
X	    Note that 'xyz' can be eiher a simple address (as in "joe")
X	    or a complex address (as in "joe%xerox.parc@Xerox.ARPA")!
X	    0 = found, -1 return means unknown site code 
X	
X	    Modified to strip out parenthetical comments...
X	**/
X
X#ifdef ACSNET
X
X	strcpy(expanded, cryptic);	/* fast and simple */
X	return(0);
X
X#else
X# ifdef USE_DBM
X	datum  key, contents;
X# endif
X
X	char   name[VERY_LONG_STRING], sitename[VERY_LONG_STRING], 
X               temp[VERY_LONG_STRING], old_name[VERY_LONG_STRING],
X	       comment[LONG_STRING];
X	char   *expand_domain(), *addr;
X	register int i = 0, j = 0, in_parens = 0, domain_name;
X
X	strcpy(old_name, cryptic);	/* remember what we were given */
X
X	/** break down **/
X
X	/** first, rip out the comment, if any, noting nested parens **/
X
X	if ((i = chloc(cryptic, '(')) > -1) {
X	  comment[j++] = ' ';			/* leading space */
X	  do {
X  	    switch(comment[j++] = cryptic[i++]) {
X	    case '(':	in_parens++;
X			break;
X	    case ')':	in_parens--;
X			break;
X	    }
X	  } while(in_parens && cryptic[i] != '\0');
X	  comment[j] = '\0';
X
X	  /* and remove this from cryptic string too... */
X	  if (cryptic[(j = chloc(cryptic,'('))-1] == ' ')
X	    cryptic[j-1] = '\0';
X	  else
X	    cryptic[j] = '\0';
X	}
X	else
X	  comment[0] = '\0';
X
X	i = j = 0;	/* reset */
X
X	while (cryptic[i] != AT_SIGN && cryptic[i] != BANG && 
X	       cryptic[i] != '\0' && cryptic[i] != '(')
X	  sitename[j++] = cryptic[i++];
X
X	sitename[j++] = '\0';
X
X	j = 0;
X	
X	if (cryptic[i] == '\0') return(-1);	/* nothing to expand! */
X
X	domain_name = (cryptic[i] == AT_SIGN);
X
X	i++;
X
X	while (cryptic[i] != '\0' && cryptic[i] != '(' && 
X               ! whitespace(cryptic[i]))
X	  name[j++] = cryptic[i++];
X
X	name[j] = '\0';
X
X	if (domain_name) {
X	  strcpy(temp, name);
X	  strcpy(name, sitename);
X	  strcpy(sitename, temp);
X	}
X
X	dprint(5, (debugfile, "\nBroke address into '%s' @ '%s' '%s'\n\n",
X		name, sitename, comment));
X
X#ifdef USE_DBM
X
X	if (size_of_pathfd == 0)
X	  return(-1);
X
X	key.dptr  = sitename;
X	key.dsize = strlen(sitename) + 1;
X
X	contents = fetch(key);
X
X	if (contents.dptr == 0) 
X	  return(-1);			/* can't find it! */
X
X	sprintf(expanded, contents.dptr, name);
X	strcat(expanded, " ");			/* add a single space... */
X	strcat(expanded, comment);		/*    ...and add comment */
X	return(0);
X#else
X
X#ifndef LOOK_CLOSE_AFTER_SEARCH
X
X	if (talk_to(sitename)) {
X	  strcpy(expanded, old_name);	/* restore! */
X	  return(0);
X	}
X#endif
X
X	if ((addr = find_path_to(sitename, TRUE)) == NULL) {
X
X#ifdef LOOK_CLOSE_AFTER_SEARCH
X
X	    if (talk_to(sitename)) {
X	      strcpy(expanded, old_name);	/* restore! */
X	      return(0);
X	    }
X	    else
X#endif
X	    if ((addr = expand_domain(cryptic)) != NULL) {
X	       strcpy(expanded, addr);	/* into THIS buffer */
X	       strcat(expanded, comment);	/* patch in comment */
X	       return(0);
X	    }
X	    else  if (size_of_pathfd == 0) {	/* no path database! */
X	      strcpy(expanded, old_name);	/* restore! */
X	      return(0);
X	    }
X	    else {			     /* We just can't get there! */
X	      strcpy(expanded, old_name);	/* restore! */
X	      return(-1);
X	    }
X	}
X	else {			/* search succeeded */
X	   sprintf(expanded, addr, name);
X	   strcat(expanded, comment);		/* add comment */
X	   return(0);
X	}
X#endif
X#endif
X}
X#endif /* defined(OPTIMIZE_RETURN) || !defined(DONT_TOUCH_ADDRESSES) */
X
Xint
Xbinary_search(name, address)
Xchar *name, *address;
X{
X	/* binary search file for name.  Return 0 if found, -1 if not */
X
X	char machine[40];
X	register long first = 0, last, middle;
X	register int  compare;
X
X	address[0] = '\0';
X
X	last = size_of_pathfd;
X
X	do {
X
X	  middle = (long) ((first+last) / 2);
X
X	  get_entry(machine, address, pathfd, middle);
X
X	  compare = strcmp(name, machine);
X
X	  if (compare < 0) 
X	    last = middle - 1;
X	  else if (compare == 0)
X	    return(0);
X	  else  /* greater */
X	    first = middle + 1; 
X	} while (absolute(last) - absolute(first) > FIND_DELTA);
X
X	/* It could be that our target entry lies exactly at `first'.
X	 * Since get_entry() compares at the entry beginning after
X	 * the passed offset (unless it's 0), we need to decrement it
X	 * (unless it's 0), and give it one more try.
X	 */
X	get_entry(machine, address, pathfd, (first == 0L ? first : --first));
X	compare = strcmp(name, machine);
X	return(compare == 0 ? 0 : -1);
X}
X
Xget_entry(machine, address, fileid, offset)
Xchar *machine, *address;
XFILE *fileid;
Xlong offset;
X{
X	int	ch;
X
X	/** get entry...return machine and address immediately
X	    following given offset in fileid.  **/
X
X	(void) fseek(fileid, offset, 0);
X
X	/* To get to the beginning of a record, if we are not at offset 0,
X	 * read until we hit an end-of-line */
X
X	if(offset != 0L)
X	  while ((ch = getc(fileid)) != EOF)
X	    if (ch == '\n')
X	      break;
X
X	*machine = '\0';
X	*address = '\0';
X	fscanf(fileid, "%s\t%s", machine, address);
X}
X
X#ifndef DONT_TOUCH_ADDRESSES
Xinit_findnode()
X{
X	/** Initialize the FILE and 'size_of_file' values for the 
X	    findnode procedure **/
X
X	struct stat buffer;
X	char   *path_filename;
X
X#ifdef USE_DBM
X	char buf[BUFSIZ];
X
X	sprintf(buf,"%s.pag", pathfile);
X	path_filename = buf;
X#else
X	path_filename = pathfile;
X#endif
X
X	if (stat(path_filename, &buffer) == -1) {
X	  dprint(2, (debugfile, 
X		  "Warning: pathalias file \"%s\" wasn't found by %s\n", 
X		  path_filename, "init_findnode"));
X	  size_of_pathfd = 0;
X	  return;
X	}
X
X	size_of_pathfd = (long) buffer.st_size;
X
X#ifdef USE_DBM
X	
X	if (dbminit(pathfile) != 0) {
X	  dprint(2, (debugfile,
X		 "Warning: couldn't initialize DBM database %s\n", 
X		 pathfile));
X	  dprint(2, (debugfile, "** %s - %s **\n\n", error_name(errno),
X		     error_description(errno)));
X	  size_of_pathfd = 0;	/* error flag, in this case */
X	  return;
X	}
X 	
X	return;
X#else
X
X	if ((pathfd = fopen(pathfile,"r")) == NULL) {
X	  dprint(2, (debugfile,
X		"Warning: Can't read pathalias file \"%s\" within %s\n", 
X		   pathfile, "init_findnode"));
X	  size_of_pathfd = 0;
X	}
X	else
X	  dprint(3, (debugfile, "\nOpened '%s' as pathalias database.\n\n", 
X		  pathfile));
X#endif
X}
X#endif /* DONT_TOUCH_ADDRESSES */
X
Xchar *find_path_to(machine, printf_format)
Xchar *machine;
Xint   printf_format;
X{
X	/** Returns either the path to the specified machine or NULL if
X	    not found.  If "printf_format" is TRUE, then it leaves the
X	    '%s' intact, otherwise it assumes that the address is a uucp
X	    address for the domain expansion program and removes the
X	    last three characters of the expanded name ("!%s") since
X	    they're redundant with the expansion!
X        **/
X
X	static char buffer[SLEN];	/* space for path */
X
X	if (size_of_pathfd > 0)
X	  if (binary_search(machine, buffer) != -1) {   	/* found it! */
X	    if (! printf_format && strlen(buffer) > 3)
X	      buffer[strlen(buffer)-3] = '\0';
X	    return( (char *) buffer);
X	  }
X
X	return(NULL);			            /* failed if it's here! */
X}
SHAR_EOF
# ============= src/aliaslib.c ==============
echo "x - extracting src/aliaslib.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/aliaslib.c &&
X
Xstatic char rcsid[] = "@(#)$Id: aliaslib.c,v 4.1.1.4 90/08/02 21:57:53 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1.1.4 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein, Elm Coordinator
X *	elm@DSI.COM			dsinc!elm
X *
X *******************************************************************************
X * $Log:	aliaslib.c,v $
X * Revision 4.1.1.4  90/08/02  21:57:53  syd
X * The newly introduced function 'stricmp' has a name conflict with a libc
X * function under SunOS 4.1.  Changed name to istrcmp.
X * From: scs@lokkur.dexter.mi.us (Steve Simmons)
X * 
X * Revision 4.1.1.3  90/07/12  23:18:17  syd
X * Make domain name checking case independent
X * From: Syd, reported by Steven Baur
X * 
X * Revision 4.1.1.2  90/06/05  21:31:34  syd
X * Fix now spurious error message for alias recursive expansion
X * when alias lookup is on a string over 20 chars long.  If that
X * long, its just not an alias, so just return.
X * From: Syd
X * 
X * Revision 4.1.1.1  90/06/05  20:41:25  syd
X * Fix boundary condition in add_name_to_list() where it fails to
X * print error message.
X * From: Chip Rosenthal <chip@chinacat.Unicom.COM>
X * 
X * Revision 4.1  90/04/28  22:42:29  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
X/** Library of functions dealing with the alias system...
X
X **/
X
X#include "headers.h"
X#include <ctype.h>
X
Xchar *get_alias_address(), *get_token(), *strpbrk(), *index();
Xlong lseek();
X
X#ifdef DONT_TOUCH_ADDRESSES
Xchar *expand_system();
X#endif
X
X/*
X * Expand "name" as an alias and return a pointer to static data containing
X * the expansion.  If "name" is not an alias, then NULL is returned.
X */
Xchar *get_alias_address(name, mailing)
Xchar *name;	/* name to expand as an alias				*/
Xint mailing;	/* TRUE to fully expand group names & recursive aliases	*/
X{
X	static char buffer[VERY_LONG_STRING];
X	char *bufptr;
X	int bufsize;
X
X	/* reads files iff changed since last read */
X	read_alias_files();
X
X	/* if name is an alias then return its expansion */
X	bufptr = buffer;
X	bufsize = sizeof(buffer);
X	if ( do_get_alias(name, &bufptr, &bufsize, mailing, FALSE, 0) )
X	  return buffer+2;	/* skip comma/space from add_name_to_list() */
X
X	/* nope...not an alias */
X	return (char *) NULL;
X}
X
X
X/*
X * Determine if "name" is an alias, and if so expand it and store the result in
X * "*bufptr".  TRUE returned if any expansion occurs, else FALSE is returned.
X */
Xint do_get_alias(name, bufptr, bufsizep, mailing, sysalias, depth)
Xchar *name;	/* name to expand as an alias				*/
Xchar **bufptr;	/* place to store result of expansion			*/
Xint *bufsizep;	/* available space in the buffer			*/
Xint mailing;	/* TRUE to fully expand group names & recursive aliases	*/
Xint sysalias;	/* TRUE to suppress checks of the user's aliases	*/
Xint depth;	/* recursion depth - initially call at depth=0		*/
X{
X	char abuf[LONG_STRING];
X	int loc;
X
X	/* update the recursion depth counter */
X	++depth;
X
X	dprint(6, (debugfile, "%*s->attempting alias expansion on \"%s\"\n",
X		(depth*2), "", name));
X
X	/* strip out (comments) and leading/trailing whitespace */
X	remove_possible_trailing_spaces( name = strip_parens(name) );
X	for ( ; isspace(*name)  ; ++name ) ;
X
X	/* throw back empty addresses */
X	if ( *name == '\0' )
X	  return FALSE;
X
X	/* check for a user alias, unless in the midst of sys alias expansion */
X	if ( !sysalias && user_data != -1 ) {
X	  if ( (loc = find(name, user_hash_table, MAX_UALIASES)) >= 0 ) {
X	    lseek(user_data, ntohl(user_hash_table[loc].byte), 0L);
X	    get_line(user_data, abuf);
X	    goto do_expand;
X	  }
X	}
X
X	/* check for a system alias */
X	if ( system_data != -1 ) {
X	  if ( (loc = find(name, system_hash_table, MAX_SALIASES)) >= 0 ) {
X	    lseek(system_data, ntohl(system_hash_table[loc].byte), 0L);
X	    get_line(system_data, abuf);
X	    sysalias = TRUE;
X	    goto do_expand;
X	  }
X	}
X
X	/* nope...this name wasn't an alias */
X	return FALSE;
X
Xdo_expand:
X
X	/* at this point, alias is expanded into "abuf" - now what to do... */
X
X	dprint(7, (debugfile, "%*s  ->expanded alias to \"%s\"\n",
X	    (depth*2), "", abuf));
X
X	/* check for an exact match */
X	loc = strlen(name);
X	if (strncmp(name, abuf, loc) == 0 && (abuf[loc] == ' ' || abuf[loc] == '\0'))
X#ifdef DONT_TOUCH_ADDRESSES
X	  return add_name_to_list(abuf, bufptr, bufsizep);
X#else
X	  return add_name_to_list(expand_system(abuf, TRUE), bufptr, bufsizep);
X#endif
X
X	/* see if we are stuck in a loop */
X	if ( depth > 12 ) {
X	  dprint(2, (debugfile,
X	      "alias expansion loop detected at \"%s\" - bailing out\n", name));
X	    error1("Error expanding \"%s\" - probable alias definition loop.",
X	      name);
X	    return FALSE;
X	}
X
X	/* see if the alias equivalence is a group name */
X	if ( mailing && abuf[0] == '!' )
X	  return do_expand_group(abuf+1, bufptr, bufsizep, sysalias, depth);
X
X	/* see if the alias equivalence is an email address */
X	if ( strpbrk(abuf,"!@:") != NULL ) {
X#ifdef DONT_TOUCH_ADDRESSES
X	  return add_name_to_list(abuf, bufptr, bufsizep);
X#else
X	  return add_name_to_list(expand_system(abuf, TRUE), bufptr, bufsizep);
X#endif
X	}
X
X	/* see if the alias equivalence is itself an alias */
X	if ( mailing && do_get_alias(abuf,bufptr,bufsizep,TRUE,sysalias,depth) )
X	  return TRUE;
X
X	/* the alias equivalence must just be a local address */
X	return add_name_to_list(abuf, bufptr, bufsizep);
X}
X
X
X/*
X * Expand the comma-delimited group of names in "group", storing the result
X * in "*bufptr".  Returns TRUE if expansion occurs OK, else FALSE in the
X * event of errors.
X */
Xint do_expand_group(group, bufptr, bufsizep, sysalias, depth)
Xchar *group;	/* group list to expand					*/
Xchar **bufptr;	/* place to store result of expansion			*/
Xint *bufsizep;	/* available space in the buffer			*/
Xint sysalias;	/* TRUE to suppress checks of the user's aliases	*/
Xint depth;	/* nesting depth					*/
X{
X	char *name;
X
X	/* go through each comma-delimited name in the group */
X	while ( group != NULL ) {
X
X	  /* extract the next name from the list */
X	  for ( name = group ; isspace(*name) ; ++name ) ;
X	  if ( (group = index(name,',')) != NULL )
X	      *group++ = '\0';
X	  remove_possible_trailing_spaces(name);
X	  if ( *name == '\0' )
X	    continue;
X
X	  /* see if this name is really an alias */
X	  if ( do_get_alias(name, bufptr, bufsizep, TRUE, sysalias, depth) )
X	    continue;
X
X	  /* verify it is a valid address */
X	  if ( !valid_name(name) ) {
X	    dprint(3, (debugfile,
X		"Illegal address %s during list expansion in %s\n",
X		name, "do_get_alias"));
X	    error1("%s is an illegal address!", name);
X	    return FALSE;
X	  }
X
X	  /* add it to the list */
X	  if ( !add_name_to_list(name, bufptr, bufsizep) )
X	    return FALSE;
X
X	}
X
X	return TRUE;
X}
X
X
X/*
X * Append "<comma><space>name" to the list, checking to ensure the buffer
X * does not overflow.  Upon return, *bufptr and *bufsizep will be updated to
X * reflect the stuff added to the buffer.  If a buffer overflow would occur,
X * an error message is printed and FALSE is returned, else TRUE is returned.
X */
Xint add_name_to_list(name,bufptr,bufsizep)
Xregister char *name;	/* name to append to buffer			*/
Xregister char **bufptr;	/* pointer to pointer to end of buffer		*/
Xregister int *bufsizep;	/* pointer to space remaining in buffer		*/
X{
X	if ( *bufsizep < 0 )
X	    return FALSE;
X
X	*bufsizep -= strlen(name)+2;
X	if ( *bufsizep <= 0 ) {
X	    *bufsizep = -1;
X	    error("Alias expansion is too long.");
X	    return FALSE;
X	}
X
X	*(*bufptr)++ = ',';
X	*(*bufptr)++ = ' ';
X	while ( *name != '\0' )
X	  *(*bufptr)++ = *name++ ;
X	**bufptr = '\0';
X
X	return TRUE;
X}
X
X
X#ifndef DONT_TOUCH_ADDRESSES
Xchar *expand_system(buffer, show_errors)
Xchar *buffer;
Xint   show_errors;
X{
X	/** This routine will check the first machine name in the given path
X	    (if any) and expand it out if it is an alias...if not, it will
X	    return what it was given.  If show_errors is false, it won't
X	    display errors encountered...
X	**/
X
X	dprint(6, (debugfile, "expand_system(%s, show-errors=%s)\n", buffer,
X		onoff(show_errors)));
X	findnode(buffer, show_errors);
X
X	return( (char *) buffer);
X}
X#endif
X
Xint
Xfind(word, table, size)
Xchar *word;
Xstruct alias_rec table[];
Xint size;
X{
X	/** find word and return loc, or -1 **/
X	register int loc;
X
X	/** cannot be an alias if its longer than 20 chars **/
X	if (strlen(word) > 20)
X	  return(-1);
X
X	loc = hash_it(word, size);
X
X	while (istrcmp(word, table[loc].name) != 0) {
X	  if (table[loc].name[0] == '\0')
X	    return(-1);
X	  loc = (loc + 1) % size;
X	}
X
X	return(loc);
X}
X
Xint
Xistrcmp(s1,s2)
Xregister char *s1, *s2;
X{
X	/* case insensitive comparison */
X	register int d;
X	for (;;) {
X	  d = ( isupper(*s1) ? tolower(*s1) : *s1 )
X		  - ( isupper(*s2) ? tolower(*s2) : *s2 ) ;
X	  if ( d != 0 || *s1 == '\0' || *s2 == '\0' )
X	    return d;
X	  ++s1;
X	  ++s2;
X	}
X	/*NOTREACHED*/
X}
X
Xint
Xstrincmp(s1,s2,n)
Xregister char *s1, *s2;
Xregister int n;
X{
X	/* case insensitive comparison */
X	register int d;
X	while (--n >= 0) {
X	  d = ( isupper(*s1) ? tolower(*s1) : *s1 )
X		  - ( isupper(*s2) ? tolower(*s2) : *s2 ) ;
X	  if ( d != 0 || *s1 == '\0' || *s2 == '\0' )
X	    return d;
X	  ++s1;
X	  ++s2;
X	}
X	return(0);
X}
X
Xint
Xhash_it(string, table_size)
Xregister char *string;
Xint   table_size;
X{
X	/** compute the hash function of the string, returning
X	    it (mod table_size) **/
X
X	register int sum = 0;
X	for ( ; *string != '\0' ; ++string )
X	  sum += (int) ( isupper(*string) ? tolower(*string) : *string );
X
X	return(sum % table_size);
X}
X
Xget_line(fd, buffer)
Xint fd;
Xchar *buffer;
X{
X	/* Read from file fd.  End read upon reading either
X	   EOF or '\n' character (this is where it differs
X	   from a straight 'read' command!) */
X
X	register int i= 0;
X	char     ch;
X
X	while (read(fd, &ch, 1) > 0)
X	  if (ch == '\n' || ch == '\r') {
X	    buffer[i] = 0;
X	    return;
X	  }
X	  else
X	    buffer[i++] = ch;
X}
SHAR_EOF
# ============= src/args.c ==============
echo "x - extracting src/args.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > src/args.c &&
X
Xstatic char rcsid[] = "@(#)$Id: args.c,v 4.1 90/04/28 22:42:31 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein, Elm Coordinator
X *	elm@DSI.COM			dsinc!elm
X *
X *******************************************************************************
X * $Log:	args.c,v $
X * Revision 4.1  90/04/28  22:42:31  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
SHAR_EOF
echo "End of  part 9"
echo "File src/args.c is continued in part 10"
echo "10" > shar3_seq_.tmp
exit 0