From: "Tim Van Holder" To: Subject: getdate prototype Date: Mon, 13 Aug 2001 00:31:15 +0200 Message-ID: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_NextPart_000_0007_01C1238F.42ECDF80" X-Priority: 3 (Normal) X-MSMail-Priority: Normal X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) Importance: Normal X-MimeOLE: Produced By Microsoft MimeOLE V5.50.4133.2400 Reply-To: djgpp-workers AT delorie DOT com This is a multi-part message in MIME format. ------=_NextPart_000_0007_01C1238F.42ECDF80 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 7bit (Finally got around to testing & tweaking the draft I created a while back - these are the results) Attached are a diff (for include/ and the makefile) and a source file for the first draft of an implementation of the POSIX getdate() function, which takes a string and tries to parse it into a struct tm based on a set of masks. This is a) far from optimal code; the current state is basically based literally on the standard. So there is probably a lot of room for optimization. b) not tested heavily c) incomplete, in that it does not support timezones (it always uses the current one). It probably also has issues with DST. It seems to work reasonably well, but is definitely not ready for production use. Any and all input is appreciated. ------=_NextPart_000_0007_01C1238F.42ECDF80 Content-Type: application/octet-stream; name="getdate.diff" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="getdate.diff" Index: include/dos.h=0A= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=0A= RCS file: /cvs/djgpp/djgpp/include/dos.h,v=0A= retrieving revision 1.9=0A= diff -u -r1.9 dos.h=0A= --- include/dos.h 2001/07/27 10:42:44 1.9=0A= +++ include/dos.h 2001/08/12 22:08:42=0A= @@ -164,10 +164,21 @@=0A= int getcbrk(void);=0A= int setcbrk(int _new_value);=0A= =0A= -void getdate(struct date *);=0A= +void _dos_getdate(struct date *);=0A= void gettime(struct time *);=0A= void setdate(struct date *);=0A= void settime(struct time *);=0A= +=0A= +/* This is to resolve the name clash between our getdate and the one=0A= + from the POSIX spec (in time.h).=0A= + IMPORTANT:=0A= + If both dos.h and time.h are included, the getdate function used=0A= + will always be the POSIX one, and never this DOS-oriented one (which=0A= + will then only be available as _dos_getdate). */=0A= +#ifndef __POSIX_GETDATE=0A= +# undef getdate=0A= +# define getdate _dos_getdate=0A= +#endif=0A= =0A= void getdfree(unsigned char _drive, struct dfree *_ptr);=0A= =0A= Index: include/time.h=0A= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=0A= RCS file: /cvs/djgpp/djgpp/include/time.h,v=0A= retrieving revision 1.5=0A= diff -u -r1.5 time.h=0A= --- include/time.h 2000/12/05 14:05:53 1.5=0A= +++ include/time.h 2001/08/12 22:08:42=0A= @@ -48,10 +48,15 @@=0A= int __tm_gmtoff;=0A= };=0A= =0A= +/* This helps with the nameclash for getdate (see dos.h for details) */=0A= +#undef getdate=0A= +#define __POSIX_GETDATE=0A= +=0A= char * asctime(const struct tm *_tptr);=0A= clock_t clock(void);=0A= char * ctime(const time_t *_cal);=0A= double difftime(time_t _t1, time_t _t0);=0A= +struct tm * getdate(const char * _spec);=0A= struct tm * gmtime(const time_t *_tod);=0A= struct tm * localtime(const time_t *_tod);=0A= time_t mktime(struct tm *_tptr);=0A= Index: src/libc/posix/time/makefile=0A= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=0A= --- /dev/null Mon Aug 13 00:11:42 2001=0A= +++ src/libc/posix/time/makefile Mon Jun 18 23:22:40 2001=0A= @@ -0,0 +1,7 @@=0A= +# Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details=0A= +=0A= +TOP=3D../..=0A= +=0A= +SRC +=3D getdate.c=0A= +=0A= +include $(TOP)/../makefile.inc=0A= ------=_NextPart_000_0007_01C1238F.42ECDF80 Content-Type: application/octet-stream; name="getdate.c" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="getdate.c" /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */ #include #include #include #include #include #include #include #include int getdate_err =3D 0; static time_t maskfile_modtime =3D 0; struct mask_entry { char* mask; struct mask_entry* next; }* mask_entries =3D NULL; /* These are "borrowed" from strftime.c - maybe they should be shared * data to reduce the library size? */ static const char *afmt[] =3D { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", }; static const char *Afmt[] =3D { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", }; static const char *bfmt[] =3D { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; static const char *Bfmt[] =3D { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", }; static void cleanup_entries(void) { struct mask_entry* e =3D mask_entries; while (e !=3D NULL) { mask_entries =3D mask_entries->next; if (e->mask !=3D NULL) free (e->mask); free (e); e =3D mask_entries; } } static const char* expand_mask(const char* mask) { static char expanded[256] =3D { "" }; char* e =3D expanded; while (mask && *mask && e < (expanded + sizeof expanded)) { if (*mask =3D=3D '%') { ++mask; switch (*mask) { case 'c': /* "normal" date/time -> %a %b %e %H:%M:%S %Y */ strcpy (e, "%a %b %e %H:%M:%S %Y"); e +=3D strlen ("%a %b %e %H:%M:%S %Y"); break; case 'D': case 'x': /* "normal" date */ strcpy (e, "%m/%d/%y"); e +=3D strlen ("%m/%d/%y"); break; case 'r': /* "normal" AM/PM time -> '%I:%M:%S %p' for POSIX */ strcpy (e, "%I:%M:%S %p"); e +=3D strlen ("%I:%M:%S %p"); break; case 'R': strcpy (e, "%H:%M"); e +=3D strlen ("%H:%M"); break; case 'T': case 'X': /* "normal" time */ strcpy (e, "%H:%M:%S"); e +=3D strlen ("%H:%M:%S"); break; default: *e++ =3D '%'; *e++ =3D *mask; break; } } else *e++ =3D *mask; ++mask; } *e =3D 0; return expanded; } static int maybe_read_maskfile(void) { static int cleanup_registered =3D 0; struct stat info; char* maskfilename =3D getenv ("DATEMSK"); if (maskfilename =3D=3D NULL) return 1; /* $DATEMSK not set */ if (stat (maskfilename, &info) !=3D 0) return 3; /* Could not get file status information */ if (!S_ISREG (info.st_mode)) return 4; /* Template file is not a regular file */ if (!cleanup_registered) { atexit (cleanup_entries); cleanup_registered =3D 1; } if (info.st_mtime > maskfile_modtime) { /* FIXME: Should this be fixed-size? If so, is this large enough? */ char maskline[256] =3D { "" }; FILE* maskfile =3D fopen (maskfilename, "r"); struct mask_entry* last_entry =3D NULL; cleanup_entries (); if (maskfile =3D=3D NULL) return 2; /* Template file cannot be opened for reading */ while (1) { if (fgets (maskline, 256, maskfile) =3D=3D NULL && !feof = (maskfile)) { fclose (maskfile); return 5; /* I/O error while reading */ } if (feof (maskfile)) break; { /* strip trailing newline */ char* end =3D maskline + strlen (maskline) - 1; while (end >=3D maskline && (*end =3D=3D '\r' || *end =3D=3D = '\n')) *end =3D '\0'; } expand_mask (maskline); if (*maskline =3D=3D '\0') continue; if (last_entry =3D=3D NULL) { mask_entries =3D (struct mask_entry*) malloc (sizeof (struct = mask_entry)); last_entry =3D mask_entries; } else { last_entry->next =3D (struct mask_entry*) malloc (sizeof (struct = mask_entry)); last_entry =3D last_entry->next; } last_entry->mask =3D (char*) malloc (strlen (maskline) + 1); strcpy (last_entry->mask, maskline); last_entry->next =3D NULL; } fclose (maskfile); maskfile_modtime =3D info.st_mtime; } return 0; } static int decode_mask(const char *datespec, const char *datemask, struct tm *dest) { time_t now_seconds =3D time (NULL); struct tm *now =3D localtime (&now_seconds); const char *spec =3D datespec; const char *mask =3D datemask; /* flags (FIXME: better a single bitfield for all format flags?) */ int century_seen =3D 0; /* Paranoia */ if (datespec =3D=3D NULL || datemask =3D=3D NULL || dest =3D=3D NULL) return 0; /* start with a clean slate */ getdate_err =3D 0; dest->tm_sec =3D -1; dest->tm_min =3D -1; dest->tm_hour =3D -1; dest->tm_mday =3D -1; dest->tm_mon =3D -1; dest->tm_year =3D -1; dest->tm_wday =3D -1; dest->tm_yday =3D -1; dest->tm_isdst =3D -1; dest->tm_gmtoff =3D 0; dest->tm_zone =3D NULL; /* Skip whitespace */ while (isspace (*mask)) ++mask; while (isspace (*spec)) ++spec; while (*mask) { if (*mask =3D=3D '%') { char numbuf[5] =3D { "" }; int i =3D 0; ++mask; /* Note that the c, D, r, R, T, x, and X formats are already = expanded */ switch (*mask) { case '%': if (*spec !=3D '%') return 0; ++spec; break; case 'a': /* abbreviated weekday name */ for (i =3D 0; i < 7; ++i) { const size_t len =3D strlen (afmt[i]); if (strncasecmp (spec, afmt[i], len) =3D=3D 0) { spec +=3D len; break; } } if (i =3D=3D 7) return 0; dest->tm_wday =3D i; break; case 'A': /* full weekday name */ for (i =3D 0; i < 7; ++i) { const size_t len =3D strlen (Afmt[i]); if (strncasecmp (spec, Afmt[i], len) =3D=3D 0) { spec +=3D len; break; } } if (i =3D=3D 7) return 0; dest->tm_wday =3D i; break; case 'b': /* abbreviated month name */ case 'h': /* same */ for (i =3D 0; i < 12; ++i) { const size_t len =3D strlen (bfmt[i]); if (strncasecmp (spec, bfmt[i], len) =3D=3D 0) { spec +=3D len; break; } } if (i =3D=3D 12) return 0; dest->tm_mon =3D i; break; case 'B': /* full month name */ for (i =3D 0; i < 12; ++i) { const size_t len =3D strlen (Bfmt[i]); if (strncasecmp (spec, Bfmt[i], len) =3D=3D 0) { spec +=3D len; break; } } if (i =3D=3D 12) return 0; dest->tm_mon =3D i; break; case 'C': /* century number (optional leading 0) */ if (!isdigit (*spec)) return 0; numbuf[0] =3D *spec++; if (isdigit (*spec)) numbuf[1] =3D *spec++; i =3D atoi (numbuf); /* lowest possible is 19 */ if (i < 19) getdate_err =3D 8; /* Invalid input */ else { if (dest->tm_year =3D=3D -1) dest->tm_year =3D 0; dest->tm_year =3D (dest->tm_year % 100) + 100 * (i - 19); century_seen =3D 1; } break; case 'd': /* day of month (optional leading 0) */ case 'e': /* same */ if (!isdigit (*spec)) return 0; numbuf[0] =3D *spec++; if (isdigit (*spec)) numbuf[1] =3D *spec++; i =3D atoi (numbuf); /* range is [1,31] */ if (i =3D=3D 0 || i > 31) getdate_err =3D 8; /* Invalid input */ else dest->tm_mday =3D i; break; case 'H': /* hour (24-hour base) */ if (!isdigit (*spec)) return 0; numbuf[0] =3D *spec++; if (isdigit (*spec)) numbuf[1] =3D *spec++; i =3D atoi (numbuf); /* range is [0,23] */ if (i > 23) getdate_err =3D 8; /* Invalid input */ else dest->tm_hour =3D i; break; case 'I': /* hour (12-hour base) */ if (!isdigit (*spec)) return 0; numbuf[0] =3D *spec++; if (isdigit (*spec)) numbuf[1] =3D *spec++; i =3D atoi (numbuf); /* range is [1,12] */ if (i =3D=3D 0 || i > 12) getdate_err =3D 8; /* Invalid input */ else /* FIXME: what if %p was already seen? */ dest->tm_hour =3D i - 1; break; case 'm': /* month number */ if (!isdigit (*spec)) return 0; numbuf[0] =3D *spec++; if (isdigit (*spec)) numbuf[1] =3D *spec++; i =3D atoi (numbuf); /* range is [1,12] */ if (i =3D=3D 0 || i > 12) getdate_err =3D 8; /* Invalid input */ else dest->tm_mon =3D i - 1; break; case 'M': /* minute */ if (!isdigit (*spec)) return 0; numbuf[0] =3D *spec++; if (isdigit (*spec)) numbuf[1] =3D *spec++; i =3D atoi (numbuf); /* range is [0,59] */ if (i > 59) getdate_err =3D 8; /* Invalid input */ else dest->tm_min =3D i; break; case 'n': /* a newline */ if (*spec !=3D '\n') return 0; ++spec; break; case 'p': /* AM/PM (or local equivalent) */ printf ("am/pm =3D=3D %.2s?\n", spec); if (strncasecmp (spec, "am", 2) =3D=3D 0) spec +=3D 2; else if (strncasecmp (spec, "pm", 2) =3D=3D 0) { spec +=3D 2; dest->tm_hour +=3D 12; } else return 0; break; case 'S': /* second */ if (!isdigit (*spec)) return 0; numbuf[0] =3D *spec++; if (isdigit (*spec)) numbuf[1] =3D *spec++; i =3D atoi (numbuf); /* range is [0,60] (60 for leap seconds) */ if (i > 60) getdate_err =3D 8; /* Invalid input */ else dest->tm_sec =3D i; break; case 't': /* a tab */ if (*spec !=3D '\t') return 0; ++spec; break; case 'w': /* weekday number */ if (!isdigit (*spec)) return 0; dest->tm_wday =3D *spec++ - '0'; break; case 'y': /* year within century */ if (!isdigit (*spec)) return 0; numbuf[0] =3D *spec++; if (isdigit (*spec)) numbuf[1] =3D *spec++; i =3D atoi (numbuf); /* range is [0,99] */ if (i > 99) getdate_err =3D 8; /* Invalid input */ else if (century_seen) { dest->tm_year -=3D (dest->tm_year % 100); dest->tm_year +=3D i; } else { dest->tm_year =3D i; if (i < 69) dest->tm_year +=3D 100; /* 2000-2068 */ } break; case 'Y': /* 4-digit year */ for (i =3D 0; i < 4; ++i) { if (!isdigit (*spec)) return 0; numbuf[i] =3D *spec++; } i =3D atoi (numbuf); /* range is [1900,...] */ if (i < 1900) getdate_err =3D 8; /* Invalid input */ else { dest->tm_year =3D i - 1900; century_seen =3D 1; } break; case 'Z': /* Timezone name or empty */ /* Only support current TZ for now */ if (now->tm_zone !=3D NULL) { size_t len =3D strlen (now->tm_zone); if (strncasecmp (spec, now->tm_zone, len) =3D=3D 0) spec +=3D len; /* No need to set it here; it's done unconditionally below */ } break; default: /* Behaviour for this is undefined; just ignore this mask */ return 0; } } else { if (*spec =3D=3D *mask) ++spec; else return 0; /* Match failed */ } /* Advance */ ++mask; /* Skip whitespace */ while (isspace (*mask)) ++mask; while (isspace (*spec)) ++spec; } if (*spec || *mask) return 0; /* Now the tricky bit: filling out the structure */ if (getdate_err =3D=3D 0) { int day_delta =3D 0; /* We only allow times in the current TZ, so copy the info */ if (dest->tm_zone =3D=3D NULL) dest->tm_zone =3D now->tm_zone; /* if only weekday, first day in the future that matches it */ if (dest->tm_mday =3D=3D -1 && dest->tm_wday !=3D -1) day_delta =3D (dest->tm_wday - now->tm_wday); /* if month but no year, first year in the future with that day & = month, assuming day 1 if no day given */ if (dest->tm_mon !=3D -1) { if (dest->tm_mday =3D=3D -1) dest->tm_mday =3D 1; if (dest->tm_year =3D=3D -1) { dest->tm_year =3D now->tm_year; if (dest->tm_mon < now->tm_mon || (dest->tm_mon =3D=3D now->tm_mon && (dest->tm_mday < now->tm_mday || (dest->tm_mday =3D=3D now->tm_mday && (dest->tm_hour < now->tm_hour || (dest->tm_hour =3D=3D now->tm_hour && (dest->tm_mon < now->tm_mon || (dest->tm_mon =3D=3D now->tm_mon && (dest->tm_sec < now->tm_sec))))))))) ++dest->tm_year; } } /* Missing time components: assume current time */ if (dest->tm_hour =3D=3D -1) dest->tm_hour =3D now->tm_hour; if (dest->tm_min =3D=3D -1) dest->tm_min =3D now->tm_min; if (dest->tm_sec =3D=3D -1) dest->tm_sec =3D now->tm_sec; /* If no date, first date in the future with correct time */ if (dest->tm_mday =3D=3D -1 && dest->tm_mon =3D=3D -1 && = dest->tm_year =3D=3D -1) { dest->tm_mday =3D now->tm_mday; dest->tm_wday =3D now->tm_wday; dest->tm_yday =3D now->tm_yday; dest->tm_mon =3D now->tm_mon; dest->tm_year =3D now->tm_year; if (dest->tm_hour < now->tm_hour || (dest->tm_hour =3D=3D now->tm_hour && (dest->tm_mon < now->tm_mon || (dest->tm_mon =3D=3D now->tm_mon && (dest->tm_sec < now->tm_sec))))) ++day_delta; } /* Anything still blank? */ if (dest->tm_mday =3D=3D -1) dest->tm_mday =3D now->tm_mday; if (dest->tm_mon =3D=3D -1) dest->tm_mon =3D now->tm_mon; if (dest->tm_year =3D=3D -1) dest->tm_year =3D now->tm_year; { /* Now the final validity check */ time_t numeric_dest =3D mktime (dest); struct tm * dest_checked =3D NULL; if (numeric_dest < 0) { getdate_err =3D 8; return 1; } numeric_dest +=3D (day_delta * 24 * 60 * 60); dest_checked =3D gmtime (&numeric_dest); numeric_dest +=3D now->tm_gmtoff; dest_checked =3D gmtime (&numeric_dest); if (dest_checked =3D=3D NULL) { getdate_err =3D 8; return 1; } memcpy (dest, dest_checked, sizeof *dest); return 1; } } return 1; } static int match_and_decode_mask(const char *datespec, struct tm *dest) { struct mask_entry* e =3D mask_entries; while (e !=3D NULL) { if (decode_mask (datespec, e->mask, dest)) return getdate_err; e =3D e->next; } return 7; /* No matching line */ } struct tm * getdate(const char * datespec) { static struct tm decoded_time; memset (&decoded_time, 0, sizeof decoded_time); getdate_err =3D maybe_read_maskfile (); if (getdate_err !=3D 0) return NULL; getdate_err =3D match_and_decode_mask (datespec, &decoded_time); return ((getdate_err =3D=3D 0) ? &decoded_time : NULL); } #ifdef TEST int main(int argc, char** argv) { char strftbuf[256]; int i =3D 1; for (; i < argc; ++i) { struct tm* t =3D getdate (argv[i]); if (t =3D=3D NULL) printf ("getdate(%s) failed with code %d\n", argv[i], = getdate_err); else printf ("getdate(%s) succeeded and returned %s", argv[i], asctime = (t)); } return 0; } #endif ------=_NextPart_000_0007_01C1238F.42ECDF80--