AtSyntax.c++
上传用户:weiyuanprp
上传日期:2020-05-20
资源大小:1169k
文件大小:12k
源码类别:

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: AtSyntax.c++,v 1.2 2008/04/26 22:34:29 faxguy Exp $ */
  2. /*
  3.  * Copyright (c) 1993-1996 Sam Leffler
  4.  * Copyright (c) 1993-1996 Silicon Graphics, Inc.
  5.  * HylaFAX is a trademark of Silicon Graphics
  6.  *
  7.  * Permission to use, copy, modify, distribute, and sell this software and 
  8.  * its documentation for any purpose is hereby granted without fee, provided
  9.  * that (i) the above copyright notices and this permission notice appear in
  10.  * all copies of the software and related documentation, and (ii) the names of
  11.  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  12.  * publicity relating to the software without the specific, prior written
  13.  * permission of Sam Leffler and Silicon Graphics.
  14.  * 
  15.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  16.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  17.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  18.  * 
  19.  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  20.  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  21.  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  22.  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
  23.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
  24.  * OF THIS SOFTWARE.
  25.  */
  26. #include "Str.h"
  27. #include <stdlib.h>
  28. #include <time.h>
  29. #include <ctype.h>
  30. #define TM_YEAR_BASE 1900 // tm times all start here...
  31. #define EPOCH_YEAR 1970
  32. #define EPOCH_WDAY 4 // January 1, 1970 was a Thursday
  33. #define DAYSPERWEEK 7
  34. #define HOUR 60 // minutes per hour
  35. #define HALFDAY (12 * HOUR) // half a day (12 hours)
  36. #define FULLDAY (24 * HOUR) // a full day (24 hours)
  37. static int
  38. isLeapYear(const tm& at) {
  39.     int y = at.tm_year + TM_YEAR_BASE;
  40.     return (((y % 4) == 0 && (y % 100) != 0) || (y % 400) == 0);
  41. }
  42. #ifdef streq
  43. #undef streq
  44. #endif
  45. #define streq(a,b,n) (strncasecmp(a,b,n) == 0)
  46. static bool checkDay(const char*& cp, int& day);
  47. static void adjustDay(struct tm& at, int day, const struct tm&);
  48. static bool checkMonth(const char*& cp, int& month);
  49. static bool parseMonthAndYear(const char*&, const struct tm& ref,
  50.     struct tm& at, fxStr& emsg);
  51. static bool parseMultiplier(const char* cp, struct tm& at, fxStr& emsg);
  52. static const char* whitespace(const char* cp);
  53. static void fixup(struct tm& at);
  54. static void _atSyntax(fxStr& emsg, const char* fmt, ...);
  55. static void _atError(fxStr& emsg, const char* fmt, ...);
  56. static int
  57. operator<(const struct tm& a, const struct tm& b)
  58. {
  59.     return (
  60. (a.tm_year < b.tm_year)
  61.      || (a.tm_year == b.tm_year && a.tm_yday < b.tm_yday)
  62.      || (a.tm_year == b.tm_year && a.tm_yday == b.tm_yday &&
  63.  a.tm_hour < b.tm_hour)
  64.      || (a.tm_year == b.tm_year && a.tm_yday == b.tm_yday &&
  65.  a.tm_hour == b.tm_hour && a.tm_min < b.tm_min)
  66.     );
  67. }
  68. /*
  69.  * Parse at-style time [date] [+increment] syntax
  70.  * and return the resultant time, relative to the
  71.  * specified reference time.
  72.  */
  73. int
  74. parseAtSyntax(const char* s, const struct tm& ref, struct tm& at0, fxStr& emsg)
  75. {
  76.     struct tm at = ref;
  77.     /*
  78.      * Time specification.
  79.      */
  80.     const char* cp = s = whitespace(s);
  81.     int v = 0;
  82.     if (isdigit(cp[0])) {
  83. do
  84.     v = v*10 + (*cp - '0');
  85. while (isdigit(*++cp));
  86. if (cp-s < 3) // only hours specified
  87.     v *= HOUR;
  88. else
  89.     v = (v/100)*HOUR + v%100;
  90. if (cp[0] == ':') { // HH::MM notation
  91.     if (isdigit(cp[1]) && isdigit(cp[2])) {
  92. int min = 10*(cp[1]-'0') + (cp[2]-'0');
  93. if (min >= 60) {
  94.     _atError(emsg, "Illegal minutes value %u", min);
  95.     return (false);
  96. }
  97. v += min;
  98. cp += 3;
  99.     } else {
  100. _atSyntax(emsg, "expecting HH:MM");
  101. return (false);
  102.     }
  103. }
  104. cp = whitespace(cp);
  105. if (streq(cp, "am", 2)) {
  106.     if (v >= HALFDAY+HOUR) {
  107. _atError(emsg, "%u:%02u is not an AM value", v/HOUR, v%HOUR);
  108. return (false);
  109.     }
  110.     if (HALFDAY <= v && v < HALFDAY+HOUR)
  111. v -= HALFDAY;
  112.     cp += 2;
  113. } else if (streq(cp, "pm", 2)) {
  114.     if (v >= HALFDAY+HOUR) {
  115. _atError(emsg, "%u:%02u is not a PM value", v/HOUR, v%HOUR);
  116. return (false);
  117.     }
  118.     if (v < HALFDAY)
  119. v += HALFDAY;
  120.     cp += 2;
  121. }
  122.     } else {
  123. if (streq(cp, "noon", 4)) {
  124.     v = HALFDAY;
  125.     cp += 4;
  126. } else if (streq(cp, "midnight", 8)) {
  127.     v = 0;
  128.     cp += 8;
  129. } else if (streq(cp, "now", 3)) {
  130.     v = at.tm_hour*HOUR + at.tm_min;
  131.     cp += 3;
  132. } else if (streq(cp, "next", 4)) {
  133.     v = at.tm_hour*HOUR + at.tm_min;
  134.     cp += 4;
  135. } else {
  136.     _atSyntax(emsg, "unrecognized symbolic time "%s"", cp);
  137.     return (false);
  138. }
  139.     }
  140.     if ((unsigned) v >= FULLDAY) {
  141. _atError(emsg, "Illegal time value; out of range");
  142. return (false);
  143.     }
  144.     at.tm_hour = v/HOUR;
  145.     at.tm_min = v%HOUR;
  146.     at.tm_sec = 0; // NB: no way to specify seconds
  147.     /*
  148.      * Check for optional date.
  149.      */
  150.     cp = whitespace(cp);
  151.     if (checkMonth(cp, v)) {
  152. at.tm_mon = v;
  153. if (!parseMonthAndYear(cp, ref, at, emsg))
  154.     return (false);
  155.     } else if (checkDay(cp, v)) {
  156. adjustDay(at, v, ref);
  157.     } else {
  158. if (streq(cp, "today", 5)) {
  159.     cp += 5;
  160. } else if (streq(cp, "tomorrow", 8)) {
  161.     at.tm_yday++;
  162.     cp += 8;
  163. } else if (cp[0] != '' && cp[0] != '+') {
  164.     _atSyntax(emsg, "expecting "+" after time");
  165.     return (false);
  166. }
  167. /*
  168.  * Adjust the date according to whether it is before ``now''.
  169.  */
  170. if (at < ref)
  171.     at.tm_yday++;
  172.     }
  173.     /*
  174.      * Process any increment expression.
  175.      */
  176.     if (cp[0] == '+' && !parseMultiplier(++cp, at, emsg))
  177. return (false);
  178.     fixup(at);
  179.     if (at < ref) {
  180. _atError(emsg, "Invalid date/time; time must be in the future");
  181. return (false);
  182.     }
  183.     at0 = at;
  184.     return (true);
  185. }
  186. /*
  187.  * Return a pointer to the next non-ws
  188.  * item in the character string.
  189.  */
  190. static const char*
  191. whitespace(const char* cp)
  192. {
  193.     while (isspace(*cp))
  194. cp++;
  195.     return (cp);
  196. }
  197. #define N(a) (sizeof (a) / sizeof (a[0]))
  198. /*
  199.  * Check for a day argument and, if found,
  200.  * return the day number [0..6] and update
  201.  * the character pointer.
  202.  */
  203. static bool
  204. checkDay(const char*& cp, int& day)
  205. {
  206.     static const char* days[] = {
  207. "sunday", "monday", "tuesday", "wednesday",
  208. "thursday", "friday", "saturday"
  209.     };
  210.     for (u_int i = 0; i < N(days); i++) {
  211. u_int len = strlen(days[i]);
  212. again:
  213. if (streq(cp, days[i], len)) {
  214.     cp += len;
  215.     day = i;
  216.     return (true);
  217. }
  218. if (len != 3) { // an Algol-style for loop...
  219.     len = 3;
  220.     goto again;
  221. }
  222.     }
  223.     return (false);
  224. }
  225. /*
  226.  * Adjust tm_yday according to whether or not the
  227.  * specified day [0...6] is before or after the
  228.  * current week day.  Note that if they are the
  229.  * same day, then the specified day is assumed to
  230.  * in the next week.  This routine assumes that
  231.  * tm_wday is correct; which is only true when
  232.  * setup from ``now''.
  233.  */
  234. static void
  235. adjustDay(struct tm& at, int day, const struct tm& ref)
  236. {
  237.     if (at.tm_wday < day)
  238. at.tm_yday += day - at.tm_wday;
  239.     else if (at.tm_wday > day)
  240. at.tm_yday += (DAYSPERWEEK - at.tm_wday) + day;
  241.     else if (at < ref)
  242. at.tm_yday += DAYSPERWEEK;
  243. }
  244. static const char* months[] = {
  245.     "January", "February", "March", "April", "May", "June", "July",
  246.     "August", "September", "October", "November", "December"
  247. };
  248. /*
  249.  * Check for a month parameter and if found,
  250.  * return the month index [0..11] and update
  251.  * the character pointer.
  252.  */
  253. static bool
  254. checkMonth(const char*& cp, int& month)
  255. {
  256.     for (u_int i = 0; i < N(months); i++) {
  257. u_int len = strlen(months[i]);
  258. again:
  259. if (streq(cp, months[i], len)) {
  260.     cp += len;
  261.     month = i;
  262.     return (true);
  263. }
  264. if (len != 3) { // an Algol-style for loop...
  265.     len = 3;
  266.     goto again;
  267. }
  268.     }
  269.     return (false);
  270. }
  271. /*
  272.  * The number of days in each month of the year.
  273.  */
  274. static const int nonLeapYear[12] =
  275.     { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  276. static const int leapYear[12] =
  277.     { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  278. static const int* daysInMonth[2] = { nonLeapYear, leapYear };
  279. static void
  280. adjustYDay(struct tm& t)
  281. {
  282.     // adjust year day according to month
  283.     const int* days = daysInMonth[isLeapYear(t)];
  284.     t.tm_yday = t.tm_mday;
  285.     for (int i = 0; i < t.tm_mon; i++)
  286. t.tm_yday += days[i];
  287. }
  288. /*
  289.  * Parse an expected day [, year] expression.
  290.  */
  291. static bool
  292. parseMonthAndYear(const char*& cp, const struct tm& ref, struct tm& at, fxStr& emsg)
  293. {
  294.     char* tp;
  295.     at.tm_mday = (u_int) strtoul(cp, &tp, 10);
  296.     if (tp == cp) {
  297. _atSyntax(emsg, "missing or invalid day of month");
  298. return (false);
  299.     }
  300.     at.tm_mday--; // tm_mday is [0..31]
  301.     tp = (char*) whitespace(tp);
  302.     if (*tp == ',') { // ,[ ]*YYYY
  303. char* xp;
  304. u_int year = (u_int) strtoul(++tp, &xp, 10);
  305. if (tp == xp) {
  306.     _atSyntax(emsg, "missing year");
  307.     return (false);
  308. }
  309. if (year < TM_YEAR_BASE) {
  310.     _atError(emsg, "Sorry, cannot handle dates before %u",
  311. TM_YEAR_BASE);
  312.     return (false);
  313. }
  314. at.tm_year = year - TM_YEAR_BASE;
  315. adjustYDay(at);
  316. tp = xp;
  317.     } else { // implicit year
  318. /*
  319.  * If the specified date is before ``now'', then it
  320.  * must be for next year.  Note that we have to do
  321.  * this in two steps since yday can vary because of
  322.  * leap years.
  323.  */
  324. adjustYDay(at);
  325. if (at.tm_yday < ref.tm_yday ||
  326.   (at.tm_yday == ref.tm_yday && at.tm_hour < ref.tm_hour)) {
  327.     at.tm_year++;
  328.     adjustYDay(at);
  329. }
  330.     }
  331.     const int* days = daysInMonth[isLeapYear(at)];
  332.     if (at.tm_mday > days[at.tm_mon]) {
  333. _atError(emsg, "Invalid day of month, %s has only %u days",
  334.     months[at.tm_mon], days[at.tm_mon]);
  335. return (false);
  336.     }
  337.     cp = tp;
  338.     return (true);
  339. }
  340. /*
  341.  * Parse an expected multiplier expression.
  342.  */
  343. static bool
  344. parseMultiplier(const char* cp, struct tm& at, fxStr& emsg)
  345. {
  346.     cp = whitespace(cp);
  347.     if (!isdigit(cp[0])) {
  348. _atSyntax(emsg, "expecting number after "+"");
  349. return (false);
  350.     }
  351.     int v = 0;
  352.     for (; isdigit(*cp); cp++)
  353. v = v*10 + (*cp - '0');
  354.     cp = whitespace(cp);
  355.     if (*cp == '') {
  356. _atSyntax(emsg, ""+%u" without unit", v);
  357. return (false);
  358.     }
  359.     if (streq(cp, "minute", 6))
  360. at.tm_min += v;
  361.     else if (streq(cp, "hour", 4))
  362. at.tm_hour += v;
  363.     else if (streq(cp, "day", 3))
  364. at.tm_yday += v;
  365.     else if (streq(cp, "week", 4))
  366. at.tm_yday += DAYSPERWEEK*v;
  367.     else if (streq(cp, "month", 5)) {
  368. at.tm_mon += v;
  369. while (at.tm_mon >= 12)
  370.     at.tm_mon -= 12, at.tm_year++;
  371. adjustYDay(at);
  372.     } else if (streq(cp, "year", 4))
  373. at.tm_year += v;
  374.     else {
  375. _atError(emsg, "Unknown increment unit "%s"", cp);
  376. return (false);
  377.     }
  378.     return (true);
  379. }
  380. /*
  381.  * Correct things in case we've slopped over as a result
  382.  * of an increment expression or similar calculation. 
  383.  * Note that this routine ``believes'' the minutes, hours,
  384.  * year, and yday values and recalculates month, mday,
  385.  * and wday values based on the believed values.
  386.  */
  387. static void
  388. fixup(struct tm& at)
  389. {
  390.     while (at.tm_min >= HOUR)
  391. at.tm_hour++, at.tm_min -= HOUR;
  392.     while (at.tm_hour >= FULLDAY)
  393. at.tm_yday++, at.tm_hour -= FULLDAY;
  394.     bool leap;
  395.     int daysinyear;
  396.     for (;;) {
  397. leap = isLeapYear(at);
  398. daysinyear = leap ? 366 : 365;
  399. if (at.tm_yday < daysinyear)
  400.     break;
  401. at.tm_yday -= daysinyear, at.tm_year++;
  402.     }
  403.     /*
  404.      * Now recalculate derivative values
  405.      * to insure everything is consistent.
  406.      */
  407.     const int* days = daysInMonth[leap];
  408.     at.tm_mday = at.tm_yday;
  409.     for (at.tm_mon = 0; at.tm_mday >= days[at.tm_mon]; at.tm_mon++)
  410. at.tm_mday -= days[at.tm_mon];
  411.     at.tm_mday++; // NB: [1..31]
  412.     int eday = at.tm_yday;
  413.     tm temp;
  414.     temp.tm_year = EPOCH_YEAR - TM_YEAR_BASE;
  415.     while (temp.tm_year < at.tm_year) {
  416.         eday += isLeapYear(temp) ? 366 : 365;
  417.         temp.tm_year++;
  418.     }
  419.     at.tm_wday = (EPOCH_WDAY + eday) % DAYSPERWEEK;
  420. }
  421. static void
  422. _atSyntax(fxStr& emsg, const char* fmt, ...)
  423. {
  424.     va_list ap;
  425.     va_start(ap, fmt);
  426.     emsg = "Syntax error, " | fxStr::vformat(fmt, ap);
  427.     va_end(ap);
  428. }
  429. static void
  430. _atError(fxStr& emsg, const char* fmt, ...)
  431. {
  432.     va_list ap;
  433.     va_start(ap, fmt);
  434.     emsg = fxStr::vformat(fmt, ap);
  435.     va_end(ap);
  436. }