[comp.mail.mh] 01/00 date bug fix

rousseau@osf.org (John Rousseau) (04/22/91)

  FYI. Here is a copy of a letter I sent to a few people who also
were seeing the 01/00 date problem.

  Thanks to the help of a few people and a lot of hacking I have fixed the 
problem. I had to do two things to get it to work. I do not know if
Jim Valero's lex start state fix is required, but I currently don't
have time to back it out of my code and test it. I will send you the
changes below.
  The other fix was that the version of lex that I am using creates
a slightly different output file than other lex'es. MH can not handle
these changes.
  What I did was convert Jim Valero's MH 6.5 changes to the MH 6.7.1
dtimep.lex (in zotnet/tws). Then I ran 'make dtimep.c' from an Ultrix
machine. This created a good dtimep.c (which is where the problem lies).
I compiled from there without difficulty.

  I would appreciate if one of you would try building without the 
dtimep.lex changes to see if they are necessary.
  If you need any help building MH, let me know. NetWisdom got me
through this, and it's only fair that I offer my help now too.

  If you do not have the ability to run lex on a different machine, 
try making dtimep.c and then look at the types for yymatch and yyextra.
They should be just 'char' for MH to grok it. My lex generated 'unsigned
char' for both of these which thoroughly broke dtimep.c. Change these to
'char' and compile. The _right_ way to fix this is to find where MH 
is depending on these types and #ifdef it appropriately. Hopefully I
will get to it someday.

  The following enclosure is my patched zotnet/tws/dtimep.lex for MH 6.7.1 .

Good luck!
-John

John Rousseau              rousseau@osf.org  
Postmaster                 uunet!osf.org!rousseau
Open Software Foundation   (617) 621-8719
-------------------------------------------------

----cut here----
%e 2000
%p 5000
%n 1000
%a 4000
%START	X,Z
sun	(sun(day)?)
mon	(mon(day)?)
tue	(tue(sday)?)
wed	(wed(nesday)?)
thu	(thu(rsday)?)
fri	(fri(day)?)
sat	(sat(urday)?)

DAY	({sun}|{mon}|{tue}|{wed}|{thu}|{fri}|{sat})

jan	(jan(uary)?)
feb	(feb(ruary)?)
mar	(mar(ch)?)
apr	(apr(il)?)
may	(may)
jun	(jun(e)?)
jul	(jul(y)?)
aug	(aug(ust)?)
sep	(sep(tember)?)
oct	(oct(ober)?)
nov	(nov(ember)?)
dec	(dec(ember)?)

MONTH	({jan}|{feb}|{mar}|{apr}|{may}|{jun}|{jul}|{aug}|{sep}|{oct}|{nov}|{dec})

w	([ \t]*)
W	([ \t]+)
D	([0-9]?[0-9])
d	[0-9]
%{
#ifndef	lint
static char ident[] = "@(#)$Id: dtimep.lex,v 1.10 90/04/05 15:55:07 sources Exp $";
#endif
#include "tws.h"
#include "../h/strings.h"
#include <ctype.h>
#include <sys/types.h>
#ifndef SYS5
#include <sys/timeb.h>
#endif not SYS5

#ifdef SYS5
extern int  daylight;
extern long timezone;
extern char *tzname[];
#endif SYS5

/*
 * Patchable flag that says how to interpret NN/NN/NN dates. When
 * true, we do it European style: DD/MM/YY. When false, we do it
 * American style: MM/DD/YY.  Of course, these are all non-RFC822
 * compliant.
 */
int europeandate = 0;

/*
 * Table to convert month names to numeric month.  We use the
 * fact that the low order 5 bits of the sum of the 2nd & 3rd
 * characters of the name is a hash with no collisions for the 12
 * valid month names.  (The mask to 5 bits maps any combination of
 * upper and lower case into the same hash value).
 */
static	int month_map[] = {
	0,
	6,	/* 1 - Jul */
	3,	/* 2 - Apr */
	5,	/* 3 - Jun */
	0,
	10,	/* 5 - Nov */
	0,
	1,	/* 7 - Feb */
	11,	/* 8 - Dec */
	0,
	0,
	0,
	0,
	0,
	0,
	0,	/*15 - Jan */
	0,
	0,
	0,
	2,	/*19 - Mar */
	0,
	8,	/*21 - Sep */
	0,
	9,	/*23 - Oct */
	0,
	0,
	4,	/*26 - May */
	0,
	7	/*28 - Aug */
};
/*
 * Same trick for day-of-week using the hash function
 *  (c1 & 7) + (c2 & 4)
 */
static	int day_map[] = {
	0,
	0,
	0,
	6,	/* 3 - Sat */
	4,	/* 4 - Thu */
	0,
	5,	/* 6 - Fri */
	0,	/* 7 - Sun */
	2,	/* 8 - Tue */
	1	/* 9 - Mon */,
	0,
	3	/*11 - Wed */
};
#define SETDAY	{ tw.tw_wday= day_map[(cp[0] & 7) + (cp[1] & 4)];\
		tw.tw_flags &= ~TW_SDAY; tw.tw_flags |= TW_SEXP;\
		cp += 2; }
#define SETMONTH { tw.tw_mon = month_map[(cp[0] + cp[1]) & 0x1f]; gotdate++;\
		 cp += 2;\
		 SKIPD;}
#define CVT2	(i=(*cp++ - '0'),isdigit(*cp)? i*10 + (*cp++ - '0') : i)
#define SKIPD	{ while ( !isdigit(*cp++) ) ;  --cp; }
#define EXPZONE	{ tw.tw_flags &= ~TW_SZONE; tw.tw_flags |= TW_SZEXP; }
#define ZONE(x)	{ tw.tw_zone=(x); EXPZONE; }
#define ZONED(x) { ZONE(x); tw.tw_flags |= TW_DST; }
#define	LC(c)	(isupper (c) ? tolower (c) : (c))

#ifdef	DSTXXX
#ifndef	BSD42
#include <time.h>
#else	BSD42
#include <sys/time.h>
#endif	BSD42

static	zonehack (tw)
register struct tws *tw;
{
    register struct tm *tm;

    if (twclock (tw) == -1L)
	return;

    tm = localtime (&tw -> tw_clock);
    if (tm -> tm_isdst) {
	tw -> tw_flags |= TW_DST;
	tw -> tw_zone -= 60;
    }
}
#endif	DSTXXX
%}
%%
%{
struct tws *dparsetime (str)
	char *str;
{
	register int i;
	static struct tws tw;
	register char *cp;
	register int gotdate = 0;
#ifndef SYS5
	struct timeb	tb;
#endif not SYS5

	start_cond = X;

	/* Zero out the struct. */
	bzero( (char *) &tw, sizeof tw);

	/* Set default time zone. */
#ifndef SYS5
	ftime( &tb );
	tw.tw_zone = -tb.timezone;
#else SYS5
	tzset( );
	tw.tw_zone = -(timezone / 60);
#endif SYS5

	while (isspace(*str))
		str++;
	while ( 1 )
		switch (cp = str, *cp ? lex_string( &str, start_cond) : 0) {

		case -1:
			if (!gotdate || tw.tw_year == 0)
				return 0;
			/* fall through */
		case 0:
			return &tw;

%}
<X>{DAY}","?{w}				SETDAY;
<X>"("{DAY}")"(","?)			{
					cp++;
					SETDAY;
					}
<X>{D}"/"{D}"/"(19)?[0-9][0-9]{w}		{
					if (europeandate) {
						/* European: DD/MM/YY */
						tw.tw_mday = CVT2;
						cp++;
						tw.tw_mon  = CVT2 - 1;
					} else {
						/* American: MM/DD/YY */
						tw.tw_mon  = CVT2 - 1;
						cp++;
						tw.tw_mday = CVT2;
					}
					cp++;
					for (i = 0; isdigit(*cp); )
						i = i*10 + (*cp++ - '0');
					tw.tw_year = i % 100;
					}
<X>{D}{w}(-)?{w}{MONTH}{w}(-)?{w}(19)?{D}{w}(\,{w}|at{W})? {
					tw.tw_mday = CVT2;
					while ( !isalpha(*cp++) )
						;
					SETMONTH;
					for (i = 0; isdigit(*cp); )
						i = i*10 + (*cp++ - '0');
					tw.tw_year = i % 100;
					}
<X>{MONTH}{W}{D}","{W}(19)?{D}{w}		{
					cp++;
					SETMONTH;
					tw.tw_mday = CVT2;
					SKIPD;
					for (i = 0; isdigit(*cp); )
						i = i*10 + (*cp++ - '0');
					tw.tw_year = i % 100;
					}

<X>{MONTH}{W}{D}{w}			{
					cp++;
					SETMONTH;
					tw.tw_mday = CVT2;
					}

<X>{D}:{D}:{D}{W}19[6-9]{d}		{	/* hack: ctime w/o TZ */
					tw.tw_hour = CVT2; cp++;
					tw.tw_min  = CVT2; cp++;
					tw.tw_sec  = CVT2;
					while( !isdigit(*cp++) )
						;
					cp++;
					tw.tw_year = CVT2;
					}
<X>{D}:{D}:{D}{w}				{
					tw.tw_hour = CVT2; cp++;
					tw.tw_min  = CVT2; cp++;
					tw.tw_sec  = CVT2;
					BEGIN Z;
					}
<X>{D}:{D}{w}				{
					tw.tw_hour = CVT2; cp++;
					tw.tw_min = CVT2;
					BEGIN Z;
					}
<X>{D}:{D}{w}am{w}				{
					tw.tw_hour = CVT2; cp++;
					if (tw.tw_hour == 12)
						tw.tw_hour = 0;
					tw.tw_min  = CVT2;
					BEGIN Z;
					}
<X>{D}:{D}{w}pm{w}				{
					tw.tw_hour = CVT2; cp++;
					if (tw.tw_hour != 12)
						tw.tw_hour += 12;
					tw.tw_min  = CVT2;
					BEGIN Z;
					}
<X>[0-2]{d}{d}{d}{d}{d}{w}			{
					tw.tw_hour = CVT2;
					tw.tw_min  = CVT2;
					tw.tw_sec  = CVT2;
					BEGIN Z;
					}
<X>19[6-9]{d}{w}				{
					/*
					 * Luckly, 4 digit times in the range
					 * 1960-1999 aren't legal as hour
					 * and minutes.
					 */
					cp += 2;
					tw.tw_year = CVT2;
					}
<X>[0-2]{d}{d}{d}{w}			{
					if (tw.tw_hour) {
					    cp += 2;
					    tw.tw_year = CVT2;
					    tw.tw_zone = 0;
					} else {
					    tw.tw_hour = CVT2;
					    tw.tw_min  = CVT2;
					    BEGIN Z;
					}
					}
<Z>"-"?ut				ZONE(0 * 60);
<Z>"-"?gmt				ZONE(0 * 60);
<Z>"-"?jst				ZONE(2 * 60);
<Z>"-"?jdt				ZONED(2 * 60);
<Z>"-"?est				ZONE(-5 * 60);
<Z>"-"?edt				ZONED(-5 * 60);
<Z>"-"?cst				ZONE(-6 * 60);
<Z>"-"?cdt				ZONED(-6 * 60);
<Z>"-"?mst				ZONE(-7 * 60);
<Z>"-"?mdt				ZONED(-7 * 60);
<Z>"-"?pst				ZONE(-8 * 60);
<Z>"-"?pdt				ZONED(-8 * 60);
<Z>"-"?nst				ZONE(-(3 * 60 + 30));
<Z>"-"?ast				ZONE(-4 * 60);
<Z>"-"?adt				ZONED(-4 * 60);
<Z>"-"?yst				ZONE(-9 * 60);
<Z>"-"?ydt				ZONED(-9 * 60);
<Z>"-"?hst				ZONE(-10 * 60);
<Z>"-"?hdt				ZONED(-10 * 60);
<Z>"-"?bst				ZONED(0 * 60);
<Z>[a-i]				{
					tw.tw_zone = 60 * (('a'-1) - LC(*cp));
					EXPZONE; 
					}
<Z>[k-m]				{
					tw.tw_zone = 60 * ('a' - LC(*cp));
					EXPZONE; 
					}
<Z>[n-y]				{
					tw.tw_zone = 60 * (LC(*cp) - 'm');
					EXPZONE; 
					}
<Z>"+"[0-1]{d}{d}{d}			{
					cp++;
					tw.tw_zone = ((cp[0] * 10 + cp[1])
						     -('0' * 10   + '0'))*60
						    +((cp[2] * 10 + cp[3])
						     -('0' * 10   + '0'));
					EXPZONE;
#ifdef	DSTXXX
					zonehack (&tw);
#endif	DSTXXX
					cp += 4;
					}
<Z>"-"[0-1]{d}{d}{d}			{
					cp++;
					tw.tw_zone = (('0' * 10   + '0')
						     -(cp[0] * 10 + cp[1]))*60
						    +(('0' * 10   + '0')
						     -(cp[2] * 10 + cp[3]));
					EXPZONE;
#ifdef	DSTXXX
					zonehack (&tw);
#endif  DSTXXX
                                        cp += 4;
                                        }
<Z>19[6-9]{d}                           {
                                        while( !isdigit(*cp++) )
                                                ;
                                        cp++;
                                        tw.tw_year = CVT2;
                                        }

\n      |
{W}     ;
%%

paul@caen.engin.umich.edu (Paul Killey) (04/23/91)

Are there cases of legal date syntaxes being turned into 01/00?
I've seen that, but only with not strictly proper syntax.

--paul