
static char rcsid[] = "@(#)$Id: parsarpdat.c,v 5.3 1993/08/03 19:28:39 syd Exp $";

/*******************************************************************************
 *  The Elm Mail System  -  $Revision: 5.3 $   $State: Exp $
 *
 * 			Copyright (c) 1993 USENET Community Trust
 *******************************************************************************
 * Bug reports, patches, comments, suggestions should be sent to:
 *
 *	Syd Weinstein, Elm Coordinator
 *	elm@DSI.COM			dsinc!elm
 *
 *******************************************************************************
 * $Log: parsarpdat.c,v $
 * Revision 5.3  1993/08/03  19:28:39  syd
 * Elm tries to replace the system toupper() and tolower() on current
 * BSD systems, which is unnecessary.  Even worse, the replacements
 * collide during linking with routines in isctype.o.  This patch adds
 * a Configure test to determine whether replacements are really needed
 * (BROKE_CTYPE definition).  The <ctype.h> header file is now included
 * globally through hdrs/defs.h and the BROKE_CTYPE patchup is handled
 * there.  Inclusion of <ctype.h> was removed from *all* the individual
 * files, and the toupper() and tolower() routines in lib/opt_utils.c
 * were dropped.
 * From: chip@chinacat.unicom.com (Chip Rosenthal)
 *
 * Revision 5.2  1993/04/12  02:16:32  syd
 * Fix year handling bug in date(1) time formats.
 * From: chip@chinacat.unicom.com (Chip Rosenthal)
 *
 * Revision 5.1  1993/01/19  04:46:21  syd
 * Initial Checkin
 *
 *
 ******************************************************************************/


#include "headers.h"


/*

Quoting from RFC 822:
     5.  DATE AND TIME SPECIFICATION

     5.1.  SYNTAX

     date-time   =  [ day "," ] date time        ; dd mm yy
						 ;  hh:mm:ss zzz

     day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
		 /  "Fri"  / "Sat" /  "Sun"

     date        =  1*2DIGIT month 2DIGIT        ; day month year
						 ;  e.g. 20 Jun 82

     month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
		 /  "May"  /  "Jun" /  "Jul"  /  "Aug"
		 /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"

     time        =  hour zone                    ; ANSI and Military

     hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT]
						 ; 00:00:00 - 23:59:59

     zone        =  "UT"  / "GMT"                ; Universal Time
						 ; North American : UT
		 /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4
		 /  "CST" / "CDT"                ;  Central:  - 6/ - 5
		 /  "MST" / "MDT"                ;  Mountain: - 7/ - 6
		 /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7
		 /  1ALPHA                       ; Military: Z = UT;
						 ;  A:-1; (J not used)
						 ;  M:-12; N:+1; Y:+12
		 / ( ("+" / "-") 4DIGIT )        ; Local differential
						 ;  hours+min. (HHMM)
*/


int parse_arpa_date(str, entry)
char *str;
struct header_rec *entry;
{
    /*
     * Parse a date field in either RFC-822 or Unix date(1) format.
     * We will fill in the "time_zone", "tz_offset", and "time_sent"
     * parts of the "entry" structure.  Return TRUE on success, FALSE
     * on failure.
     */

    char field[STRING], save_tz[STRING];
    int month, day, year, hours, mins, secs, tz, len, i;

    /*
     * Since this is an RFC-822 field, there might be parenthetical
     * comments.  Yank them out.  Note that strip_parens() returns
     * a pointer to static data.
     */
    str = strip_parens(str);

    /*
     * The first field is an optional day of the week.  If it exists
     * it is supposed to have a trailing comma by RFC-822, but we won't
     * complain if it doesn't.  If the date string was generated by
     * the Unix date(1) command then it won't have the comma.  We don't
     * do anything with this information, just skip over it if it exists.
     */
    if ((len = get_word(str, 0, field, sizeof(field))) < 0)
	goto failed;
    if (cvt_dayname_to_daynum(field, &i))
	str += len;

    /*
     * Peek at the next character to determine what format to
     * parse the rest of the line as.
     */
    while (isspace(*str))
	++str;
    if (!isdigit(*str)) {

	/*
	 * Parse the line in Unix date(1) format.  The syntax is:
	 *
	 *	month day hh:mm:ss [tz] year
	 *
	 * e.g. "Jun 21 06:45:44 CDT 1989".  The timezone is optional.
	 */

	dprint(7, (debugfile,
	    "parse_arpa_date parsing \"%s\" in time(1) format\n", str));

	/* <month> */
	if ((len = get_word(str, 0, field, sizeof(field))) < 0 ||
		!cvt_monthname_to_monthnum(field, &month))
	    goto failed;
	str += len;

	/* <day> */
	if ((len = get_word(str, 0, field, sizeof(field))) < 0 ||
		(day = atonum(field)) < 0)
	    goto failed;
	str += len;

	/* <hh:mm:ss> */
	if ((len = get_word(str, 0, field, sizeof(field))) < 0 ||
		!cvt_timestr_to_hhmmss(field, &hours, &mins, &secs))
	    goto failed;
	str += len;

	/* optional <tz> */
	save_tz[0] = save_tz[1] = '\0';
	tz = 0;
	while ((len = get_word(str, 0, field, sizeof(field))) > 0 &&
		cvt_timezone_to_offset(field, &i)) {
	    (void) strcat(save_tz, " ");
	    (void) strcat(save_tz, field);
	    tz += i;
	    str += len;
	}

	/* <year> */
	if ((len = get_word(str, 0, field, sizeof(field))) < 0 ||
		(year = atonum(field)) < 0)
	    goto failed;
	str += len;

	/* there might be more...but we ignore it */

    } else {

	/*
	 * Parse the line in RFC-822 format.  The syntax is:
	 *
	 *	day month year hh:mm:ss zone
	 *
	 * e.g. "17 Nov 92 23:34:25 CST".
	 */

	dprint(7, (debugfile,
	    "parse_arpa_date parsing \"%s\" in RFC-822 format\n", str));

	/* <day> */
	if ((len = get_word(str, 0, field, sizeof(field))) < 0 ||
		(day = atonum(field)) < 0)
	    goto failed;
	str += len;

	/* <month> */
	if ((len = get_word(str, 0, field, sizeof(field))) < 0 ||
		!cvt_monthname_to_monthnum(field, &month))
	    goto failed;
	str += len;

	/* <year> */
	if ((len = get_word(str, 0, field, sizeof(field))) < 0 ||
		!cvt_yearstr_to_yearnum(field, &year))
	    goto failed;
	str += len;

	/* <hh:mm:ss> */
	if ((len = get_word(str, 0, field, sizeof(field))) < 0 ||
		!cvt_timestr_to_hhmmss(field, &hours, &mins, &secs))
	    goto failed;
	str += len;

	/* <tz> - silently ignore bogus or missing timezones */
	save_tz[0] = save_tz[1] = '\0';
	tz = 0;
	while ((len = get_word(str, 0, field, sizeof(field))) > 0 &&
		cvt_timezone_to_offset(field, &i)) {
	    (void) strcat(save_tz, " ");
	    (void) strcat(save_tz, field);
	    tz += i;
	    str += len;
	}

	/* there might be more...but we ignore it */

    }

    strfcpy(entry->time_zone, save_tz+1, sizeof(entry->time_zone));
    entry->tz_offset = tz*60;
    entry->time_sent = make_gmttime(year, month, day, hours, mins-tz, secs);

    dprint(7, (debugfile, "  year=%d month=%d day=%d\n", year, month, day));
    dprint(7, (debugfile, "  hours=%d mins=%d secs=%d tz=%d\n",
	hours, mins, secs, tz));
    dprint(7, (debugfile, "  return success\n"));
    return TRUE;

failed:
    dprint(4, (debugfile, "parse_arpa_date failed at \"%s\"\n",
	(len <= 0 ? "<premature eol>" : field)));
    return FALSE;
}

#ifdef _TEST
int debug = 9999;
FILE *debugfile = stderr;
main()
{
    struct header_rec hdr;
    char buf[1024];
    while (gets(buf) != NULL) {
	if (!parse_arpa_date(buf, &hdr))
	    fprintf(stderr, "FAIL %s\n", buf);
	else {
	    fprintf(stderr, "OK %s\n", buf);
	    fprintf(stderr, "time_zone=%s tz_offset=%d time_sent=%ld\n",
		hdr.time_zone, hdr.tz_offset, hdr.time_sent);
	}
	putc('\n', stderr);
    }
    exit(0);
}
#endif

