home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / mm / mm-ccmd-0.91-20031009.tar.gz / mm-ccmd-0.91-20031009.tar / work / ccmd / dtpat.h < prev    next >
C/C++ Source or Header  |  2002-02-19  |  26KB  |  742 lines

  1. /*
  2.  Copyright (c) 1986, 1990 by The Trustees of Columbia University in
  3.  the City of New York.  Permission is granted to any individual or
  4.  institution to use, copy, or redistribute this software so long as it
  5.  is not sold for profit, provided this copyright notice is retained.
  6.  
  7.  Author: Andrew Lowry
  8. */
  9. /* dtpat.h
  10. **
  11. ** Time and date patterns.  Each pattern consists of a pattern string
  12. ** and an array of argument pointers.  The pattern is sort of
  13. ** a printf-style string, with percent-signs marking special pattern
  14. ** elements.  Spaces in the pattern are ignored, unless preceded by
  15. ** a percent sign.  The argument pointers point to objects
  16. ** referenced by the special pattern elements, or to locations where
  17. ** values returned by the pattern elements are to be placed.  Normal
  18. ** characters in the pattern must be matched exactly, except that case
  19. ** of letters does not matter.  Special pattern elements are as follows:
  20. **
  21. **   %= - Stores the last value returned by a pattern element
  22. **     into the variable pointed to by the next argument pointer.
  23. **   %% - Matches a percent sign.  Zero is always returned.
  24. **   %  - (percent space) Matches a space.  Zero is always returned.
  25. **     (cf. %w below).
  26. **   %c - Matches the next pattern character, exempting it from
  27. **     any special processing.  Returns 0.
  28. **   %k - Matches a keyword from the table pointed to by the next
  29. **     argument pointer.  The table is a NULL-terminated array
  30. **     of string pointers.  A keyword may contain a vertical bar,
  31. **     which marks the minimum match allowed for the keyword.  For
  32. **     example, "JAN|UARY" will match "JAN" but not "JA".  If no bar
  33. **     is present, nothing less than the complete keyword will match.
  34. **     No check is made for an ambiguous match -- the bars should be
  35. **     placed so as to take care of this.  Case of letters is ignored.
  36. **     Returns the index of the matching keyword.
  37. **   %#m - Matches a month number (range 1-12).  Returns the number
  38. **     minus 1.
  39. **   %#d - Matches a day number (range 1-31).  Returns the number
  40. **     minus 1.  No check is made as to month-specific value constraints.
  41. **   %#y - Matches a two-digit year number.  Returns the number plus 1900.
  42. **   %#Y - Matches a four-digit year number.
  43. **   %#h - Matches an hour number in the range 1-12.  Returns the number.
  44. **   %#H - Matches an hour number in the range 0-24.  Returns the number.
  45. **   %#M - Matches a four-digit hour-minute number (hhmm).  Returns two
  46. **     numbers - the first %= gets the hour part (number / 100), the second
  47. **     gets the minutes part (number % 100).  For normal, 2-digit minute
  48. **     use %#s.
  49. **   %#s - Matches a second number, in the range 0-59.  Returns the number
  50. **   %p - Matches a punctuation character (not letter or digit).  Returns
  51. **     the ASCII code of the character, but does not consume it in the
  52. **     parse.
  53. **   %w - Matches a run of whitespace.  Returns 0.
  54. **   %d - Matches a delimiter character - next argument pointer gives a
  55. **     string containing the allowed delimiters.  Returns the index of
  56. **     the matching delimiter in the given string.
  57. **   %r - Recursively attempts to match against an array of
  58. **     patterns pointed to by the next argument pointer.  Returns the index
  59. **     in the pattern array of the pattern that succeeded.
  60. **   %f - Function pointed to by next argument pointer is invoked with
  61. **     a single argument - an int pointer which is used to return a value
  62. **     for the pattern element.  The function returns TRUE or FALSE
  63. **     according to whether or not it succeeded.
  64. **   %z - Assigns 0 to the next argument (like a %= after an element
  65. **     that returned 0).
  66. **   %n - Assigns -1 to the next argumnet (like a %= after an element
  67. **     that returned -1).
  68. **   %+ - Skips the next arg without other side-effects.
  69. **
  70. ** Any pattern element can be specified with a question mark after the
  71. ** percent sign, to indicate that the element is optional.  An optional
  72. ** pattern element that fails always returns -1 but does not cause overall
  73. ** failure of the pattern.
  74. **
  75. ** Along with the pattern string and argument pointers, each pattern is
  76. ** declared with a base rank, a qualifying flag word, and a list of
  77. ** rank adjusters.  The base rank is the rank normally assigned to the
  78. ** pattern, and can be used to arbitrate among several patterns that
  79. ** might match a given input.  The qualifying flag word contains parse
  80. ** flags whose presence in the parse cause the pattern to be disallowed.
  81. ** So, for example, any time pattern that includes a seconds field is
  82. ** disallowed by the DTP_NIS flag.  Each item in a rank adjustment list
  83. ** contains a flag word and an adjustment value.  If any of the given
  84. ** parse flags are present in the parse, the adjustment is added to the
  85. ** pattern's rank for the duration of the parse.  The adjustment can
  86. ** be negative, but if a pattern rank becomes negative as a result of
  87. ** adjustments, the pattern is disqualified.
  88. **/
  89.  
  90.  
  91.  
  92. /* Here are the structure types to be used to hold pattern information */
  93.  
  94. typedef struct RANKADJ {        /* rank adjustments */
  95.     int _raflg;            /* flags triggering this adjustment */
  96.                     /*  (-1 signals end of adjustments) */
  97.     int _raadj;            /* amount of adjustment */
  98. } rankadj;
  99.  
  100.  
  101. typedef struct DTPAT {            /* time/date parsing pattern */
  102.     char *_dppat;            /* pattern string */
  103.                     /*  (NULL for end of pattern array) */
  104.     int  **_dparg;            /* arg pointer list */
  105.     int  _dpqal;            /* (dis)qualifier flag word */
  106.     int  _dprnk;            /* base rank for this pattern */
  107.     rankadj *_dpraj;        /* array of rank adjustments */
  108. } dtpat;
  109.  
  110.  
  111.  
  112. /* Keyword tables for use in parsing patterns */
  113.  
  114. /* Timezone keywords array, must be kept parallel with translation
  115. ** array matching keys with TZ_xxx values (possibly with TZ_DST flag
  116. ** or TZ_STD included).  EST and CST are taken to mean their USA versions
  117. ** in this table.  AEST and ACST are provided for australian versions.
  118. ** AWST is also allowed for australian WST.
  119. **/
  120.  
  121. static char *(tznames[]) = {
  122.   "gmt|", "green|wich",
  123.   "ast|", "adt|", "atl|antic",
  124.   "est|", "edt|", "east|ern",
  125.   "cst|", "cdt|", "cen|tral",
  126.   "mst|", "mdt|", "moun|tain",
  127.   "pst|", "pdt|", "pac|ific",
  128.   "yst|", "ydt|", "yuk|on",
  129.   "hst|", "hdt|", "ala|ska", "haw|aii",
  130.   "bst|", "bdt|", "ber|ing",
  131.   "wet|", 
  132.   "met|", 
  133.   "eet|", 
  134.   "aest|", 
  135.   "acst|", 
  136.   "wst|", "awst|",
  137.   "jst|", "jdt|", "jap|an",
  138.   NULL
  139. };
  140.  
  141. /* timezone name translation table - Must be kept synchronized with name
  142. ** array declared above.
  143. **/
  144.  
  145. static int tztrans[(sizeof(tznames)/sizeof(char*))-1] = {
  146.   TZ_GMT,  TZ_GMT,
  147.   TZ_STD+TZ_UAST, TZ_DST+TZ_UAST, TZ_UAST,
  148.   TZ_STD+TZ_UEST, TZ_DST+TZ_UEST, TZ_UEST,
  149.   TZ_STD+TZ_UCST, TZ_DST+TZ_UCST, TZ_UCST,
  150.   TZ_STD+TZ_UMST, TZ_DST+TZ_UMST, TZ_UMST,
  151.   TZ_STD+TZ_UPST, TZ_DST+TZ_UPST, TZ_UPST,
  152.   TZ_STD+TZ_UYST, TZ_DST+TZ_UYST, TZ_UYST,
  153.   TZ_STD+TZ_UHST, TZ_DST+TZ_UHST, TZ_UHST, TZ_UHST,
  154.   TZ_STD+TZ_UBST, TZ_DST+TZ_UBST, TZ_UBST,
  155.   TZ_EWET, 
  156.   TZ_EMET,
  157.   TZ_EEET,
  158.   TZ_AEST,
  159.   TZ_ACST,
  160.   TZ_AWST, TZ_AWST,
  161.   TZ_STD+TZ_JST, TZ_DST+TZ_JST, TZ_JST
  162. };
  163.  
  164. /* Keyword table for daylight-savings-time specifications */
  165.  
  166. static char *(dstnames[]) = {
  167.   "day|light", "dst|",
  168.   "stan|dard", "std|",
  169.   NULL
  170. };
  171.  
  172. /* Keyword table for AM/PM specifications */
  173.  
  174. static char *(ampmnames[]) = {
  175.   "m|idnight", "a|m", "n|oon", "p|m",
  176.   NULL
  177. };
  178.  
  179. /* Keyword table for days of the week */
  180.  
  181. static char *(downames[]) = {
  182.   "sun|day", "mon|day", "tue|sday", "wed|nesday", "thu|rsday", 
  183.   "fri|day", "sat|urday",
  184.   NULL
  185. };
  186.  
  187. /* Keyword table for months */
  188.  
  189. static char *(monnames[]) = {
  190.   "jan|uary", "feb|ruary", "mar|ch", "apr|il", "may|", "jun|e",
  191.   "jul|y", "aug|ust", "sep|tember", "oct|ober", "nov|ember", "dec|ember",
  192.   NULL
  193. };
  194.  
  195.  
  196.  
  197.  
  198. /* Global variables referenced by the patterns */
  199.  
  200. /* Make sure this global var does not get misinterpreted */
  201. #ifdef min
  202. #undef min
  203. #endif
  204.  
  205. static int mon,day,yr,dow;        /* date and day-of-week */
  206. static int dowspec;            /* -1 iff dow not specified */
  207. static int hr,min,sec;            /* time fields */
  208. static int ampm;            /* -1 if not specified, else */
  209.                     /*  0-3 for midnight, am, noon, pm */
  210. static int tz,tzc;            /* specified tz - offset & code */
  211. static int tzspec;            /* -1 iff timezone not given */
  212. static int dst;                /* daylight savings time spec */
  213. static int dstspec;            /* -1 iff dst not specified */
  214. static int tzsign,tzhr,tzmin;        /* pieces of GMT+hh:mm style tz */
  215.  
  216.  
  217.  
  218. /* Patterns for matching a timezone specification */
  219.  
  220. /* aux functions invoked by timezone patterns */
  221. static int tzlkup();            /* translate keyword index to */
  222.                     /*  TZ_xxx value, and interpret */
  223.                     /*  dst specification */
  224. static int tzcalc();            /* combine hrs and mins in offset */
  225.                     /*  from GMT, and flag that no dst */
  226.                     /*  indication was given */
  227.  
  228. /* arg lists for timezone patterns */
  229. static int *(tz1arg[]) = {        /* patterns with named timezones */
  230.     (int *) tznames, &tz,        /* keyword lookup of timezone name */
  231.     (int *) dstnames, &dst,        /* optional dst specification */
  232.     (int *) tzlkup            /* fn to convert key idx to TZ_xxx */
  233. };
  234. static int *(tz2arg[]) = {         /* Patterns with offset from GMT */
  235.     (int *) "+-", &tzsign,        /* direction of offset (+ for east) */
  236.     &tzhr, &tzmin,            /* hours and minutes in offset */
  237.     (int *) dstnames, &dst,        /* optional dst specifier */    
  238.     (int *) tzcalc            /* fn to combine the pieces */
  239. };
  240.  
  241. /* the timezone patterns */
  242. static dtpat tzpats[] = {        /* timezone patterns */
  243.   { "%?w %?c- %?w %k%= %w %k%= %p %f",    /* name with dst indicator, like */
  244.     tz1arg, DTP_NTZ, 1, NULL },        /*  "-EASTERN DAYLIGHT" */
  245.   { "%?w %?c- %?w %k%= %p %+%n %f",    /* named zone, like "-EST" */
  246.     tz1arg, DTP_NTZ, 0, NULL },
  247.   { "%?w %?c- %?w GMT %?w %d%= %?w %#h%= : %#s%= %?w %?k%= %p %f",
  248.     tz2arg, DTP_NTZ+DTP_NAC, 4, NULL },    /* offset "-GMT+4:30" */
  249.   { "%?w %?c- %?w GMT %?w %d%= %?w %#h%= %z %?w %?k%= %p %f",
  250.     tz2arg, DTP_NTZ+DTP_AMS, 3, NULL },    /* offset like "-GMT+4" */
  251.   { "%?w %?c- %?w GMT %?w %d%= %?w %#M%=%= %?w %?k%= %p %f",
  252.     tz2arg, DTP_NTZ+DTP_AAC+DTP_AHM , 2, NULL }, /* offset like "-GMT+430" */
  253.   NULL                    /* end of pattern array */
  254. };
  255.  
  256.  
  257.  
  258.  
  259. /* Patterns for matching a time specification */
  260.  
  261. /* aux functions invoked by time patterns */
  262. static int fixampm();            /* modify time according to am/pm */
  263.                     /*  specification to yield 24-hour */
  264.                     /*  time */
  265.  
  266. /* arg list for time specifications */
  267. static int *(timarg[]) = {        /* one list suffices for all */
  268.     &hr, &min, &sec,        /* individual time fields */
  269.     (int *) ampmnames, &m,    /* optional am/pm keyword */
  270.     (int *) fixampm,        /* adjust time according to am/pm */
  271.     (int *) tzpats, &tzspec        /* optional call to timezone pats */
  272. };
  273.  
  274.  
  275. /* the time patterns - each finishes with a recursive pattern call to
  276. ** the timezone patterns
  277. **/
  278. static dtpat timpats[] = {
  279.   { "%#H%= : %#s%= : %#s%= %+%n %f %?r%= %p",    /* hh:mm:ss, no am/pm */
  280.     timarg, DTP_N24+DTP_NIS+DTP_NAC, 4, NULL },
  281.   { "%#H%= : %#s%= %z %+%n %f %?r%= %p",    /* hh:mm, no am/pm */
  282.     timarg, DTP_N24+DTP_AIS+DTP_NAC+DTP_AMS, 3, NULL },
  283.   { "%#M%=%= : %#s%= %+%n %f %?r%= %p",        /* hhmm:ss, no am/pm */
  284.     timarg, DTP_N24+DTP_NIS+DTP_AAC+DTP_AHM, 2, NULL },
  285.   { "%#H%= %z %z %+%n %f %?r%= %p",        /* hh, no am/pm */
  286.     timarg, DTP_N24+DTP_AIS+DTP_AMS, 1, NULL },
  287.   { "%#M%=%= %z %+%n %f %?r%= %p",          /* hhmm, no am/pm */
  288.     timarg, DTP_N24+DTP_AIS+DTP_AAC+DTP_AHM, 0, NULL },
  289.   { "%#h%= : %#s%= : %#s%= %?w %k%= %f %?r%= %p", /* hh:mm:ss, with am/pm */
  290.     timarg, DTP_NTM+DTP_NIS+DTP_NAC, 9, NULL },
  291.   { "%#h%= : %#s%= %z %?w %k%= %f %?r%= %p",    /* hh:mm, with am/pm */
  292.     timarg, DTP_NTM+DTP_AIS+DTP_NAC+DTP_AMS, 8, NULL },
  293.   { "%#M%=%= : %#s%= %?w %k%= %f %?r%= %p",    /* hhmm:ss, with am/pm */
  294.     timarg, DTP_NTM+DTP_NIS+DTP_AAC+DTP_AHM, 7, NULL },
  295.   { "%#h%= %z %z %?w %k%= %f %?r%= %p",        /* hh, with am/pm */
  296.     timarg, DTP_NTM+DTP_AIS+DTP_AMS, 6, NULL },
  297.   { "%#M%=%= %z %?w %k%= %f %?r%= %p",        /* hhmm, with am/pm */
  298.     timarg, DTP_NTM+DTP_AIS+DTP_AAC+DTP_AHM, 5, NULL },
  299.   NULL                        /* end of pattern list */
  300. };
  301.  
  302.  
  303.  
  304. /* Date patterns are split into a month-day section and a year section,
  305. ** to allow BSD ctime-style time strings to be parsed, with the time
  306. ** portion inserted into the date in front of the year.  This style
  307. ** of specification is disallowed by the DTP_NSP (No SPlit) flag.
  308. **
  309. ** In addition, day-of-week is split off to simplify the month-day 
  310. ** patterns.
  311. **/
  312.  
  313. /* Year patterns */
  314.  
  315. /* Aux function invoked by year patterns */
  316. static int fixyr();            /* Check for two-digit years, and */
  317.                     /* adjust them to the current */
  318.                     /* century */
  319.  
  320. /* arg list for year patterns */
  321. static int *(yrarg[]) = {
  322.     (int *) "'",            /* optional apostrophe for 2 digits */
  323.     &yr,                /* year number */
  324.     (int *) fixyr            /* fn to adjust 2-digit years */
  325. };
  326.  
  327. /* the year patterns */
  328. static dtpat yrpats[] = {
  329.   { "%?d %#y%= %p %f",            /* two digits w/optional apostrophe */
  330.     yrarg, 0, 1, NULL },
  331.   { "%+ %#Y%= %p %+",            /* four digits, no apostrophe */
  332.     yrarg, 0, 0, NULL },
  333.   NULL                    /* end of pattern list */
  334. };
  335.  
  336.  
  337.  
  338. /* Day of week patterns */
  339.  
  340. /* arg list for day-of-week patterns */
  341. static int *(dowarg[]) = {
  342.     (int *) downames,&dow        /* day-of-week via keyword table */
  343. };
  344.  
  345. /* the day-of-week patterns */
  346. static dtpat dowpats[] = {
  347.   { "%k%= %?w , %?w",            /* dow w/comma, opt whitespace */
  348.     dowarg, DTP_NDW, 1, NULL },
  349.   { "%k%= %w",                /* dow w/out comma, wspace required */
  350.     dowarg, DTP_NDW, 0, NULL },
  351.   NULL                    /* end of pattern list */
  352. };
  353.  
  354.  
  355.  
  356.  
  357. /* Month-day patterns */
  358.  
  359. /* arg lists for month-day patterns */
  360. static int *(md1arg[]) = {         /* month name, then day number */
  361.     (int *) monnames, &mon,        /* month by name */
  362.     &day                /* day number */
  363. };
  364. static int *(md2arg[]) = {        /* day number then month name */
  365.     &day, (int *) "-/.",        /* day number, then delimiter */
  366.     (int *) monnames, &mon        /* then month by name */
  367. };
  368. static int *(md3arg[]) = {        /* two numbers, month first */
  369.     &mon, (int *) "-/.",        /* month number, then delimiter */
  370.     &day                /* then day number */
  371. };
  372. static int *(md4arg[]) = {        /* two numbers, day first */
  373.     &day, (int *) "-/.",        /* day number, then delimiter */
  374.     &mon                /* then month number */
  375. };
  376.  
  377. /* rank adjustments for the two-number patterns.  Normally, month-first
  378. ** patterns will be ranked one and day-first patterns zero.  If DTP_SNM
  379. ** is on, the situation is reversed by adding one to dd-mm patterns, and
  380. ** subtracting one from mm-dd patterns.  If DTP_ERR is on, both types
  381. ** of pattern are dropped by 1, putting the lower ranked type into the
  382. ** red and thereby disqualifying it.
  383. **/
  384. static rankadj md1adj[] = {         /* adjustments for mm-dd patterns */
  385.   { DTP_SNM, -1 },            /* drop rank if dd-mm prefered */
  386.   { DTP_ERR, -1 },            /* and to -1 if no swap allowed */
  387.   -1
  388. };
  389. static rankadj md2adj[] = {        /* adjustments for dd-mm patterns */
  390.   { DTP_SNM, 1 },            /* bump rank if dd-mm preferred */
  391.   { DTP_ERR, -1 },            /* but drop if no swap allowed */
  392.   -1
  393. };
  394.  
  395. static dtpat mdpats[] = {        /* examples illustrate formats */
  396.   { "%k%= %w %#d%= %?w ,",        /* January 10, */
  397.     md1arg, 0, 4, NULL },
  398.   { "%k%= %w %#d%=",            /* January 10 (no comma) */
  399.     md1arg, 0, 3, NULL },
  400.   { "%#d%= %?w %?d %?w %k%=",        /* 10-Jan */
  401.     md2arg, 0, 2, NULL },
  402.   { "%#m%= %?w %?d %?w %#d%=",        /* mm-dd */
  403.     md3arg, DTP_NNM, 1, md1adj },
  404.   { "%#d%= %?w %?d %?w %#m%=",        /* dd-mm */
  405.     md4arg, DTP_NNM, 0, md2adj },
  406.   NULL
  407. };
  408.  
  409.  
  410.  
  411. /* Date patterns - optional dow pattern, then a month-day pattern
  412. ** followed by a year pattern, followed by a call to the date
  413. ** checking routine 
  414. **/
  415.  
  416.  
  417. /* aux function invoked by date patterns */
  418. static int datchk();            /* make sure the date makes sense */
  419.  
  420. /* arg list for date parses */
  421. static int *(datarg[]) = {
  422.     (int *) dowpats, &dowspec,    /* optional day of week */
  423.     (int *) mdpats,            /* month and day first */    
  424.     (int *) "-/.",             /* then delimiter */
  425.     (int *) yrpats,            /* then a year */
  426.     (int *) datchk             /* and a validity check */
  427. };    
  428.            
  429. static dtpat datpats[] = {
  430.   { "%?r%= %r %?w %?d %?w %r %f",    /* dow, mm-dd, delimiter, year */
  431.    datarg, 0, 0, NULL },
  432.   NULL
  433. };
  434.  
  435.  
  436.  
  437.  
  438. /* Date/time patterns - Either a date followed by a time, or a split date
  439. ** with the time in front of the year.
  440. **/
  441.  
  442.  
  443. /* arg lists for date/time patterns */
  444. static int *(dt1arg[]) = {        /* normal date/time */
  445.     (int *) datpats,        /* first the date */
  446.     (int *) timpats            /* then the time */
  447. };
  448. static int *(dt2arg[]) = {        /* split date format */
  449.     (int *) dowpats, &dowspec,    /* optional day of week */
  450.     (int *) mdpats,            /* month/date first */
  451.     (int *) timpats,         /* then a complete time spec */
  452.     (int *) yrpats,            /* then a year */
  453.     (int *) datchk            /* and finally check the date */
  454. };
  455.  
  456. /* the date/time patterns */
  457. static dtpat dtpats[] = {
  458.   { "%r %w %r",            /* date, time , separated by whitespace */
  459.     dt1arg, 0, 2, NULL },
  460.   { "%?r%= %r %w %r %w %r %f",    /* dow, month/day, time, year */
  461.     dt2arg, 0, 1, NULL },
  462.   { "%?r%= %r %w %r %?w , %?w %r %f", /* dow, month/day, time, comma, year */
  463.     dt2arg, 0, 0, NULL },
  464.   NULL
  465. };
  466.  
  467.  
  468.  
  469. /* Auxiliary functions invoked by the patterns.  All share common calling
  470. ** conventions:
  471. **
  472. ** Input arguments: None.
  473. ** Output arguments
  474. **   val - Pointer to integer where pattern element return value is to
  475. **     be placed by a successful invocation.
  476. ** Returns: TRUE on success, FALSE otherwise.
  477. **/
  478.  
  479. /* tzlkup
  480. **
  481. ** Purpose:
  482. **   Global variable tz has the index in the tznames array of the timezone
  483. **   keyword typed by the user.  Global variable dst has the index in
  484. **   array dstnames of the daylight-savings-time keyword typed by the user,
  485. **   or -1 if none was given.  This function uses the tztrans translation
  486. **   table to get one of the TZ_xxx timezone codes, possibly with TZ_DST
  487. **   or TZ_STD set as well.  The timezone code is translated into a number
  488. **   of minutes west of Greenwich, which is then stored into tz.  The
  489. **   dst value is translated into a boolean flag indicating whether or not
  490. **   daylight savings time was specified, and a check is made to ensure that
  491. **   any such specification does not clash with TZ_DST or TZ_STD flags from
  492. **   the timezone table.  Variable dst is left with a value TRUE or FALSE,
  493. **   depending on whether or not daylight savings time is meant to be on.
  494. **   If dst is unspecified both by keyword and by flags from the table
  495. **   lookup, variable dstspec is set to FALSE, otherwise it is set to
  496. **   TRUE.  In the former case, the appropriate timezone rules should
  497. **   be used to decide dst or not according to the complete time and
  498. **   date specification.
  499. **
  500. **   Pattern element return value is always zero.
  501. **/
  502.  
  503. static int
  504. tzlkup(val)
  505. int *val;
  506. {
  507.   int dstk,dstf;            /* dst by keyword and by flag: */
  508.                     /*  1 for on, 0 for off, -1 for */
  509.                     /*  unspecified */
  510.   tzinf *tzi;                /* timezone information structure */
  511.   int dstadj;                /* adjustment for dst in this zone */
  512.  
  513. #ifdef TRACE
  514.   fprintf(stderr,"LOOKUP TZONE ");
  515. #endif
  516.   tzc = tztrans[tz];            /* get TZ code and dst flags */
  517.   if (tzc & TZ_DST)            /* check for implicit dst spec */
  518.     dstf = 1;
  519.   else if (tzc & TZ_STD)
  520.     dstf = 0;
  521.   else                    /* unspecified in flags */
  522.     dstf = -1;
  523.   tzc &= ~(TZ_DST | TZ_STD);        /* remove flags from tz code */
  524.   tzi = dttzinf(tzc | TZ_CODE);        /* lookup timezone info by code */
  525.   if (tzi == NULL)
  526.     return(FALSE);            /* internal error - no such zone */
  527.   tz = tzi->_tzoff;            /* get offset from Greenwich */
  528.   
  529.   dstadj = tzi->_tzdadj;        /* get dst adjustment for zone */
  530.   if (dst == -1)            /* dst keyword given? */
  531.     dstk = -1;                /* nope */
  532.   else if (dst <= 1)            /* decode keyword for on or off? */
  533.     dstk = 1;                /* DAYLIGHT or DST -- on */
  534.   else
  535.     dstk = 0;                /* STANDARD or STD -- off */
  536.  
  537.   dstspec = 0;                /* assume dst specified somewhere */
  538.   if (dstf == -1)            /* unspecified in flags? */
  539.     if (dstk == -1)            /* and unspecified by keyword? */
  540.       dstspec = -1;            /* yup, totally unspecified */
  541.     else
  542.       dst = (dstk == 1 ? dstadj : 0);    /* specified by keyword - set dst */
  543.   else if (dstk == -1)            /* given by flags, not by keyword? */
  544.     dst = (dstf == 1 ? dstadj : 0);    /* then set according to flags */
  545.   else if (dstk == dstf)        /* given by both - do they agree? */
  546.     dst = (dstf == 1 ? dstadj : 0);    /* yup, set global flag */
  547.   else
  548.     return(FALSE);            /* disagreement means failure */
  549.   *val = 0;                /* set return value */
  550.   return(TRUE);                /* and succeed */
  551. }
  552.  
  553.  
  554.  
  555. /* tzcalc
  556. **
  557. ** Purpose:
  558. **   Invoked by patterns where user specifies timezone by means of
  559. **   an explicit offset from Greenwich.  The data is available in
  560. **   three pieces - sign (negative for east of Greenwich), hours, and 
  561. **   minutes.  Also, dst contains the index of a selected keyword
  562. **   specifying daylight-savings-time or standard time, or -1 if
  563. **   no such keyword was given.  The offset is combined into a
  564. **   signed number of minutes west of Greenwich after checking that
  565. **   the magnitude is no greater than 12:00, and the result is stored
  566. **   in global variable tz.  The dst keyword is translated into an
  567. **   adjustment value, which is stored back into variable dst.  If
  568. **   a dst keyword was given, dstspec is set to 0, else to -1.
  569. **
  570. **   Pattern element return value is always zero.
  571. **/
  572.  
  573. static int
  574. tzcalc(val)
  575. int *val;
  576. {
  577.   tzinf *tzi;                /* timezone information */
  578.   
  579. #ifdef TRACE
  580.   fprintf(stderr,"CALC TZONE ");
  581. #endif
  582.   if ((tzhr == 12) && (tzmin > 0))    /* offset magnitude too large? */
  583.     return(FALSE);
  584.   tz = (tzhr * 60) + tzmin;        /* calculate offset magnitude */
  585.   if (tzsign == 0)            /* sign is '+' (east of Greenwich)? */
  586.     tz = -tz;                /* negate the offset */
  587.   if (tz == -12*60)            /* 12 hours east? */
  588.     tz = 12*60;                /* switch to 12 hours west */
  589.   
  590.   tzc = -1;                /* no timezone code available */
  591.  
  592.   if (dst == -1)            /* no dst keyword given? */
  593.     dstspec = -1;            /* then dst not specified */
  594.   else if (dst >= 2) {            /* STANDARD or STD given? */
  595.     dstspec = 0;            /* then dst specified */
  596.     dst = 0;                /* and adjustment is zero */
  597.   }
  598.   else {                /* DAYLIGHT or DST given */
  599.     dstspec = 0;            /* signal that dst was specified */
  600.     tzi = dttzinf(tz);            /* look up timezone info */
  601.     if (tzi == NULL)            /* no such timezone? */
  602.       dst = -60;            /* then assume we subtract 60 mins */
  603.                     /*  to get standard time */
  604.     else
  605.       dst = tzi->_tzdadj;        /* known zone - get dst adjustment */
  606.   }
  607.   return(TRUE);                /* successful operation */
  608. }
  609.  
  610.  
  611.  
  612. /* fixampm
  613. **
  614. ** Purpose:
  615. **   Invoked after a time has been parsed, to check and adjust the time
  616. **   according to the am/pm keyword or lack thereof.  If NOON or MIDNIGHT
  617. **   was specified, the time must have been 12:00:00, and anything else
  618. **   results in an error.  MIDNIGHT is changed to 00:00:00.  If AM or
  619. **   PM is specified, the time must be from 1:00:00 to 12:59:59 inclusive.
  620. **   Hour 12 is changed to 0, and then 12 is added if PM was specified.
  621. **   In particular, this means that 12am is interpreted as midnight, and
  622. **   12pm as noon.  If no keyword was given, the time must be in the range
  623. **   00:00:00 to 24:00:00, and 24:00:00 is converted to 00:00:00.
  624. **
  625. **   Pattern element return value is always 0.
  626. **/
  627.  
  628. static int
  629. fixampm(val)
  630. int *val;
  631. {
  632. #ifdef TRACE
  633.   fprintf(stderr,"FIX AMPM ");
  634. #endif
  635.   if (ampm == -1) {            /* no keyword given? */
  636.     if (hr == 24)            /* check special case of 24:mm:ss */
  637.       if ((min != 0) || (sec != 0))    /* anything but 24:00:00? */
  638.     return(FALSE);
  639.       else
  640.     hr = 0;                /* 24:00:00 becomes 0:00:00 */
  641.   }
  642.   else if ((ampm == 0) || (ampm == 2)) { /* noon or midnight? */
  643.     if ((hr != 12) || (min != 0) || (sec != 0)) /* only 12:00:00 allowed */
  644.       return(FALSE);
  645.     if (ampm == 0)            /* midnight? */
  646.       hr = 0;                /* change to 00:00:00 */
  647.   }
  648.   else {                /* AM or PM specified */
  649.     if ((hr < 1) || (hr > 12))        /* hrs must be 1-12 */
  650.       return(FALSE);
  651.     else if (hr == 12)            /* change 12:mm:ss to 00:mm:ss */
  652.       hr = 0;
  653.     if (ampm == 3)            /* adjust PM up by 12 hours */
  654.       hr += 12;
  655.   }
  656.   
  657.   *val = 0;                /* all ok - set return value */
  658.   return(TRUE);                /* and succeed */
  659. }
  660.  
  661.  
  662.  
  663. /* fixyr
  664. **
  665. ** Purpose:
  666. **   Adjust a two-digit year by adding the current, previous, or next
  667. **   century depending on the context.
  668. **   Pattern element return value always zero
  669. **/
  670.  
  671. static int
  672. fixyr(val)
  673. int *val;
  674. {
  675.   datime curdt;            /* holds current date/time */
  676.  
  677. #ifdef TRACE
  678.   fprintf(stderr,"FIX YEAR ");
  679. #endif
  680.   if (yr < 100) {            /* two-digit year given? */
  681.     dtnow(&curdt);            /* get current date */
  682. #if 0 /* Pre-Y2K */
  683.     yr += (curdt._dtyr/100)*100;    /* add in current century */
  684. #else
  685.     if (yr < 50 && (curdt._dtyr % 100 > 85))
  686.       yr += (curdt._dtyr/100+1)*100;    /* add in next century */
  687.     else if (yr > 70 && curdt._dtyr % 100 < 20)
  688.       yr += (curdt._dtyr/100-1)*100;    /* add in previous century */
  689.     else
  690.       yr += (curdt._dtyr/100)*100;    /* add in current century */
  691. #endif
  692.   }
  693.   *val = 0;                /* set return value */
  694.   return(TRUE);                /* and succeed */
  695. }
  696.  
  697.  
  698.  
  699. /* datchk
  700. **
  701. ** Purpose:
  702. **   Check the date that has been parsed, to make sure it is legitimate.
  703. **   If no day-of-week was specified in the parse, compute the correct
  704. **   day-of-week and store it in global variable dow.  Otherwise make
  705. **   sure that the given day-of-week is correct.
  706. **
  707. **   Computations work under the incorrect assumption that the present
  708. **   Gregorian calendar system has been in effect since the year 0000.
  709. **   This is an ok assumption for all major cultures since 1923, when
  710. **   Greece adopted the system, but before then, local calendar systems
  711. **   varied.
  712. **
  713. **   Pattern element return value is always zero.
  714. **/
  715.  
  716. static int
  717. datchk(val)
  718. int *val;
  719. {
  720.   /* lengths of the months, assuming a leap year */
  721.   static int mthlens[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  722.   int cdow;                /* calculated day-of-week */
  723.   int i;
  724.   
  725. #ifdef TRACE
  726.   fprintf(stderr,"CHECK DATE ");
  727. #endif
  728.   if (day >= mthlens[mon])
  729.     return(FALSE);            /* day out of range for given month */
  730.   if ((yr % 4 != 0) || ((yr % 100 == 0) && (yr % 400 != 0))) /* non-leap? */
  731.     if ((mon == 1) && (day == 28))    /* then disallow February 29 */
  732.      return(FALSE);
  733.   cdow = dtdow(mon,day,yr);        /* compute day of week for date */
  734.  
  735.   if (dowspec == -1)            /* day-of-week not specified? */
  736.     dow = cdow;                /* then install calculated value */
  737.   else if (dow != cdow)            /* specified wrong? */
  738.     return(FALSE);            /* then fail */
  739.   *val = 0;                /* set return value */
  740.   return(TRUE);                /* and succeed */
  741. }
  742.