home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / os2 / remind / src / main.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-10-31  |  41.4 KB  |  1,507 lines

  1. /***************************************************************/
  2. /*                                                             */
  3. /*  MAIN.C                                                     */
  4. /*                                                             */
  5. /*  Main program loop, as well as miscellaneous conversion     */
  6. /*  routines, etc.                                             */
  7. /*                                                             */
  8. /*  This file is part of REMIND.                               */
  9. /*  Copyright (C) 1992, 1993 by David F. Skoll.                */
  10. /*                                                             */
  11. /***************************************************************/
  12. #include "config.h"
  13. #ifdef HAVE_STDLIB_H
  14. #include <stdlib.h>
  15. #endif
  16. #ifdef HAVE_MALLOC_H
  17. #include <malloc.h>
  18. #endif
  19. #ifdef HAVE_UNISTD
  20. #include <unistd.h>
  21. #endif
  22. #include <stdio.h>
  23. #include <signal.h>
  24. #include <string.h>
  25. #ifdef HAVE_STDARG
  26. #include <stdarg.h>
  27. #else
  28. #include <varargs.h>
  29. #endif
  30. #include <ctype.h>
  31. #include <time.h>
  32.  
  33. #if defined(__MSDOS__) || defined(__OS2__)
  34. #include <dos.h>
  35. #else
  36. #include <sys/types.h>
  37. #ifndef SYSV
  38. #include <sys/time.h>
  39. #endif
  40. #endif /* if defined(__MSDOS__)... */
  41.  
  42. #include "types.h"
  43. #include "protos.h"
  44. #include "expr.h"
  45. #include "globals.h"
  46. #include "err.h"
  47.  
  48. PRIVATE void DoReminders ARGS ((void));
  49.  
  50. #if defined(NEED_TIMEGM) && !defined(HAVE_MKTIME)
  51. PRIVATE long time_cheat ARGS ((int year, int month));
  52. long timegm ARGS((struct tm *tm));
  53. long timelocal ARGS((struct tm *tm));
  54. #endif
  55.  
  56. static char TPushBuffer[TOKSIZE+1]; /* Buffer for pushing back a token. */
  57. static char *TokenPushed = NULL;
  58.  
  59. #ifdef OS2_POPUP
  60. #define Putchar(c) {if (AsPopUp) PutcPopUp(c); else putchar(c);}
  61. #else
  62. #define Putchar(c) putchar(c)
  63. #endif
  64.  
  65.  
  66. /***************************************************************/
  67. /***************************************************************/
  68. /**                                                           **/
  69. /**  Main Program Loop                                        **/
  70. /**                                                           **/
  71. /***************************************************************/
  72. /***************************************************************/
  73. #ifdef HAVE_PROTOS
  74. PUBLIC int main(int argc, char *argv[])
  75. #else
  76. int main(argc, argv)
  77. int argc;
  78. char *argv[];
  79. #endif
  80. {
  81. #ifdef HAVE_QUEUED
  82.    int pid;
  83. #endif
  84.  
  85. /* The very first thing to do is to set up ErrFp to be stderr */
  86.    ErrFp = stderr;
  87.  
  88. /* Set up global vars */
  89.    ArgC = argc;
  90.    ArgV = argv;
  91.  
  92.    InitRemind(argc, argv);
  93.    if(DoCalendar || DoSimpleCalendar) {
  94.       ProduceCalendar();
  95.       return 0;
  96.    }
  97.  
  98.    /* Not doing a calendar.  Do the regular remind loop */
  99.    ShouldCache = (Iterations > 1);
  100.  
  101.    while (Iterations--) {
  102.       DoReminders();
  103.  
  104.       if (DebugFlag & DB_DUMP_VARS) {
  105.          DumpVarTable();
  106.          DumpSysVarByName(NULL);
  107.       }
  108.  
  109.       if (!Hush) {
  110.          if (DestroyOmitContexts())
  111.          Eprint("%s", E_PUSH_NOPOP);
  112. #ifdef HAVE_QUEUED
  113.          if (!Daemon && !NextMode && !NumTriggered && !NumQueued) printf("%s\n", ErrMsg[E_NOREMINDERS]);
  114.       else
  115.          if (!Daemon && !NextMode && !NumTriggered) printf(ErrMsg[M_QUEUED],
  116.             NumQueued);
  117. #else
  118.          if (!NextMode && !NumTriggered) printf("%s\n", ErrMsg[E_NOREMINDERS]);
  119. #endif
  120.       }
  121.  
  122.    /* If it's MS-DOS, reset the file access date.           */
  123.    /* Note that OS/2 and DOS bound programs have __MSDOS__  */
  124.    /* defined, so this test should probably be modified.    */
  125. #if defined(__MSDOS__)
  126.       if (!UseStdin && (RealToday == JulianToday))
  127.          SetAccessDate(InitialFile, RealToday);
  128. #endif
  129.  
  130.    /* If there are sorted reminders, handle them */
  131.       if (SortByDate) IssueSortedReminders();
  132.  
  133.    /* If there are any background reminders queued up, handle them */
  134. #ifdef HAVE_QUEUED
  135.       if (NumQueued || Daemon) {
  136.  
  137.          if (DontFork) {
  138.            HandleQueuedReminders();
  139.           return 0;
  140.          } else {
  141.         pid = fork();
  142.         if (pid == 0) {
  143.            HandleQueuedReminders();
  144.            return 0;
  145.         }
  146.         if (pid == -1) {
  147.            fprintf(ErrFp, "%s", ErrMsg[E_CANTFORK]);
  148.            return 1;
  149.         }
  150.          }
  151.       }
  152. #endif
  153.       if (Iterations) {
  154.          ClearGlobalOmits();
  155.          DestroyOmitContexts();
  156.          DestroyVars(0);
  157.          NumTriggered = 0;
  158.      JulianToday++;
  159.       }
  160.    }
  161.    return 0;
  162. }
  163.  
  164. /***************************************************************/
  165. /*                                                             */
  166. /*  DoReminders                                                */
  167. /*                                                             */
  168. /*  The normal case - we're not doing a calendar.              */
  169. /*                                                             */
  170. /***************************************************************/
  171. #ifdef HAVE_PROTOS
  172. PRIVATE void DoReminders(void)
  173. #else
  174. static void DoReminders()
  175. #endif
  176. {
  177.    int r;
  178.    Token tok;
  179.    char *s;
  180.    Parser p;
  181.  
  182.    if (!UseStdin) FileAccessDate = GetAccessDate(InitialFile);
  183.    else          FileAccessDate = JulianToday;
  184.  
  185.    if (FileAccessDate < 0) {
  186.       fprintf(ErrFp, "%s: '%s'.\n", ErrMsg[E_CANTACCESS], InitialFile);
  187.       exit(1);
  188.    }
  189.  
  190.    r=OpenFile(InitialFile);
  191.    if (r) {
  192.       fprintf(ErrFp, "%s %s: %s\n", ErrMsg[E_ERR_READING],
  193.                                     InitialFile, ErrMsg[r]);
  194.       exit(1);
  195.    }
  196.  
  197.    while(1) {
  198.       r = ReadLine();
  199.       if (r == E_EOF) return;
  200.       if (r) {
  201.      Eprint("%s: %s", ErrMsg[E_ERR_READING], ErrMsg[r]);
  202.      exit(1);
  203.       }
  204.       s = FindInitialToken(&tok, CurLine);
  205.  
  206.       /* Should we ignore it? */
  207.       if (NumIfs &&
  208.           tok.type != T_If &&
  209.           tok.type != T_Else &&
  210.           tok.type != T_EndIf &&
  211.           tok.type != T_IfTrig &&
  212.           ShouldIgnoreLine())
  213.       {
  214.           /*** IGNORE THE LINE ***/
  215.       }
  216.       else {
  217.          /* Create a parser to parse the line */
  218.          CreateParser(s, &p);
  219.          switch(tok.type) {
  220.  
  221.             case T_Empty:
  222.         case T_Comment:
  223.            break;
  224.  
  225.         case T_Rem:     r=DoRem(&p);     break;
  226.         case T_ErrMsg:  r=DoErrMsg(&p);  break;
  227.         case T_If:      r=DoIf(&p);      break;
  228.         case T_IfTrig:  r=DoIfTrig(&p);  break;
  229.         case T_Else:    r=DoElse(&p);    break;
  230.         case T_EndIf:   r=DoEndif(&p);   break;
  231.         case T_Include: r=DoInclude(&p); break;
  232.         case T_Exit:    DoExit(&p);      break;
  233.         case T_Flush:   r=DoFlush(&p);   break;
  234.         case T_Set:     r=DoSet(&p);     break;
  235.         case T_Fset:    r=DoFset(&p);    break;
  236.         case T_UnSet:   r=DoUnset(&p);   break;
  237.         case T_Clr:     r=DoClear(&p);   break;
  238.             case T_Debug:   r=DoDebug(&p);   break;
  239.         case T_Dumpvars: r=DoDump(&p);   break;
  240.         case T_Banner:  r=DoBanner(&p);  break;
  241.         case T_Omit:    r=DoOmit(&p);
  242.                        if (r == E_PARSE_AS_REM) {
  243.                    DestroyParser(&p);
  244.                    CreateParser(s, &p);
  245.                    r=DoRem(&p);
  246.                 }
  247.                     break;
  248.         case T_Pop:     r=PopOmitContext(&p);     break;
  249.         case T_Preserve: r=DoPreserve(&p);  break;
  250.         case T_Push:    r=PushOmitContext(&p);    break;
  251.         case T_RemType: if (tok.val == RUN_TYPE) {
  252.                                r=DoRun(&p);
  253.                                break;
  254.                  } else {
  255.                     CreateParser(CurLine, &p);
  256.                 r=DoRem(&p);
  257.                 break;
  258.                  }
  259.  
  260.  
  261.      /* If we don't recognize the command, do a REM by default */
  262.      /* Note:  Since the parser hasn't been used yet, we don't */
  263.      /* need to destroy it here. */
  264.  
  265.         default:        CreateParser(CurLine, &p); r=DoRem(&p); break;
  266.          }
  267.          if (r && (!Hush || r != E_RUN_DISABLED)) Eprint("%s", ErrMsg[r]);
  268.  
  269.          /* Destroy the parser - free up resources it may be tying up */
  270.          DestroyParser(&p);
  271.       }
  272.    }
  273. }
  274.  
  275. /***************************************************************/
  276. /*                                                             */
  277. /*  Julian                                                     */
  278. /*                                                             */
  279. /*  Given day, month, year, return Julian date in days since   */
  280. /*  1 January 1990.                                            */
  281. /*                                                             */
  282. /***************************************************************/
  283. #ifdef HAVE_PROTOS
  284. PUBLIC int Julian(int year, int month, int day)
  285. #else
  286. int Julian(year, month, day)
  287. int day, month, year;
  288. #endif
  289. {
  290.    int y1 = BASE-1, y2 = year-1;
  291.  
  292.    int y4 = (y2 / 4) - (y1 / 4);  /* Correct for leap years */
  293.    int y100 = (y2 / 100) - (y1 / 100); /* Don't count multiples of 100... */
  294.    int y400 = (y2 / 400) - (y1 / 400); /* ... but do count multiples of 400 */
  295.  
  296.    return 365 * (year-BASE) + y4 - y100 + y400 +
  297.           MonthIndex[IsLeapYear(year)][month] + day - 1;
  298. }
  299.  
  300. /***************************************************************/
  301. /*                                                             */
  302. /*  FromJulian                                                 */
  303. /*                                                             */
  304. /*  Convert a Julian date to year, month, day.                 */
  305. /*                                                             */
  306. /***************************************************************/
  307. #ifdef HAVE_PROTOS
  308. PUBLIC void FromJulian(int jul, int *y, int *m, int *d)
  309. #else
  310. void FromJulian(jul, y, m, d)
  311. int jul;
  312. int *y, *m, *d;
  313. #endif
  314. {
  315.    int try_yr = (jul / 365) + BASE;
  316.    int try_mon = 0;
  317.    int t;
  318.  
  319.    /* Inline code for speed... */
  320.    int y1 = BASE-1, y2 = try_yr-1;
  321.    int y4 = (y2 / 4) - (y1 / 4);  /* Correct for leap years */
  322.    int y100 = (y2 / 100) - (y1 / 100); /* Don't count multiples of 100... */
  323.    int y400 = (y2 / 400) - (y1 / 400); /* ... but do count multiples of 400 */
  324.  
  325.    int try_jul= 365 * (try_yr-BASE) + y4 - y100 + y400;
  326.  
  327.    while (try_jul > jul) {
  328.       try_yr--;
  329.       try_jul -= DaysInYear(try_yr);
  330.    }
  331.    jul -= try_jul;
  332.  
  333.    t = DaysInMonth(try_mon, try_yr);
  334.    while (jul >= t) {
  335.       jul -= t;
  336.       try_mon++;
  337.       t = DaysInMonth(try_mon, try_yr);
  338.    }
  339.    *y = try_yr;
  340.    *m = try_mon;
  341.    *d = jul + 1;
  342.    return;
  343. }
  344.  
  345. /***************************************************************/
  346. /*                                                             */
  347. /*  ParseChar                                                  */
  348. /*                                                             */
  349. /*  Parse a character from a parse pointer.  If peek is non-   */
  350. /*  zero, then just peek ahead; don't advance pointer.         */
  351. /*                                                             */
  352. /***************************************************************/
  353. #ifdef HAVE_PROTOS
  354. PUBLIC int ParseChar(ParsePtr p, int *err, int peek)
  355. #else
  356. int ParseChar(p, err, peek)
  357. ParsePtr p;
  358. int *err;
  359. int peek;
  360. #endif
  361. {
  362.    Value val;
  363.    int r;
  364.  
  365.    *err = 0;
  366.    if (TokenPushed && *TokenPushed)
  367.       if (peek) return *TokenPushed;
  368.       else      return *TokenPushed++;
  369.  
  370.    while(1) {
  371.       if (p->isnested) {
  372.      if (*(p->epos))
  373.         if (peek) return *(p->epos);
  374.         else      return *(p->epos++);
  375.      free(p->etext);  /* End of substituted expression */
  376.      p->etext = NULL;
  377.      p->epos = NULL;
  378.      p->isnested = 0;
  379.       }
  380.       if (!*(p->pos)) {
  381.      return 0;
  382.       }
  383.       if (*p->pos != BEG_OF_EXPR || !p->allownested)
  384.      if (peek) return *(p->pos);
  385.      else      return *(p->pos++);
  386.       p->pos++;
  387.       r = EvalExpr(&(p->pos), &val);
  388.       if (r) {
  389.      *err = r;
  390.      DestroyParser(p);
  391.      return 0;
  392.       }
  393.       if (*p->pos != END_OF_EXPR) {
  394.      *err = E_MISS_END;
  395.      DestroyParser(p);
  396.      DestroyValue(&val);
  397.      return 0;
  398.       }
  399.       p->pos++;
  400.       r = DoCoerce(STR_TYPE, &val);
  401.       if (r) { *err = r; return 0; }
  402.       p->etext = val.v.str;
  403.       val.type = ERR_TYPE; /* So it's not accidentally destroyed! */
  404.       p->isnested = 1;
  405.       p->epos = p->etext;
  406.    }
  407. }
  408.  
  409. /***************************************************************/
  410. /*                                                             */
  411. /*  ParseNonSpaceChar                                          */
  412. /*                                                             */
  413. /*  Parse the next non-space character.                        */
  414. /*                                                             */
  415. /***************************************************************/
  416. #ifdef HAVE_PROTOS
  417. PUBLIC int ParseNonSpaceChar(ParsePtr p, int *err, int peek)
  418. #else
  419. int ParseNonSpaceChar(p, err, peek)
  420. ParsePtr p;
  421. int *err;
  422. int peek;
  423. #endif
  424. {
  425.    int ch;
  426.  
  427.    ch = ParseChar(p, err, 1);
  428.    if (*err) return 0;
  429.  
  430.    while (isspace(ch)) {
  431.       ParseChar(p, err, 0);   /* Guaranteed to work */
  432.       ch = ParseChar(p, err, 1);
  433.       if (*err) return 0;
  434.    }
  435.    if (!peek) ch = ParseChar(p, err, 0);  /* Guaranteed to work */
  436.    return ch;
  437. }
  438.  
  439. /***************************************************************/
  440. /*                                                             */
  441. /*  ParseToken                                                 */
  442. /*                                                             */
  443. /*  Parse a token delimited by whitespace.                     */
  444. /*                                                             */
  445. /***************************************************************/
  446. #ifdef HAVE_PROTOS
  447. PUBLIC int ParseToken(ParsePtr p, char *out)
  448. #else
  449. int ParseToken(p, out)
  450. ParsePtr p;
  451. char *out;
  452. #endif
  453. {
  454.    int c, err;
  455.    int len = 0;
  456.  
  457.    *out = 0;
  458.  
  459.    c = ParseChar(p, &err, 0);
  460.    if (err) return err;
  461.    while (c && isspace(c)) {
  462.       c = ParseChar(p, &err, 0);
  463.       if (err) return err;
  464.    }
  465.    if (!c) return OK;
  466.    *out++ = c;
  467.    len++;
  468.  
  469.    while (c && !isspace(c)) {
  470.       c = ParseChar(p, &err, 0);
  471.       if (err) return err;
  472.       if (len < TOKSIZE && c && !isspace(c)) {
  473.          *out++ = c;
  474.      len++;
  475.       }
  476.    }
  477.    *out = 0;
  478.    return OK;
  479. }
  480.  
  481. /***************************************************************/
  482. /*                                                             */
  483. /*  ParseIdentifier                                            */
  484. /*                                                             */
  485. /*  Parse a valid identifier - ie, alpha or underscore         */
  486. /*  followed by alphanum.  Return E_BAD_ID if identifier is    */
  487. /*  invalid.                                                   */
  488. /*                                                             */
  489. /***************************************************************/
  490. #ifdef HAVE_PROTOS
  491. PUBLIC int ParseIdentifier(ParsePtr p, char *out)
  492. #else
  493. int ParseIdentifier(p, out)
  494. ParsePtr p;
  495. char *out;
  496. #endif
  497. {
  498.    int c, err;
  499.    int len = 0;
  500.  
  501.    *out = 0;
  502.  
  503.    c = ParseChar(p, &err, 0);
  504.    if (err) return err;
  505.    while (c && isspace(c)) {
  506.       c = ParseChar(p, &err, 0);
  507.       if (err) return err;
  508.    }
  509.    if (!c) return E_EOLN;
  510.    if (c != '$' && c != '_' && !isalpha(c)) return E_BAD_ID;
  511.    *out++ = c;
  512.    *out = 0;
  513.    len++;
  514.  
  515.    while (1) {
  516.       c = ParseChar(p, &err, 1);
  517.       if (err) return err;
  518.       if (c != '_' && !isalnum(c)) return OK;
  519.  
  520.       if (len < TOKSIZE) {
  521.      c = ParseChar(p, &err, 0);  /* Guaranteed to work */
  522.      *out++ = c;
  523.      *out = 0;
  524.      len++;
  525.       }
  526.    }
  527. }
  528. /***************************************************************/
  529. /*                                                             */
  530. /* EvaluateExpr                                                */
  531. /*                                                             */
  532. /* We are expecting an expression here.  Evaluate it and       */
  533. /* return the value.                                           */
  534. /*                                                             */
  535. /***************************************************************/
  536. #ifdef HAVE_PROTOS
  537. PUBLIC int EvaluateExpr(ParsePtr p, Value *v)
  538. #else
  539. int EvaluateExpr(p, v)
  540. ParsePtr p;
  541. Value *v;
  542. #endif
  543. {
  544.  
  545.    int bracketed = 0;
  546.    int r;
  547.  
  548.    if (p->isnested) return E_PARSE_ERR;  /* Can't nest expressions */
  549.    while (isspace(*p->pos)) (p->pos)++;
  550.    if (!p->pos) return E_PARSE_ERR;      /* Missing expression */
  551.    if (*p->pos == BEG_OF_EXPR) {
  552.       (p->pos)++;
  553.       bracketed = 1;
  554.    }
  555.    r = EvalExpr(&(p->pos), v);
  556.    if (r) return r;
  557.    if (bracketed) {
  558.       if (*p->pos != END_OF_EXPR) return E_MISS_END;
  559.       (p->pos)++;
  560.    }
  561.    return OK;
  562. }
  563.  
  564. /***************************************************************/
  565. /*                                                             */
  566. /*  Eprint - print an error message.                           */
  567. /*                                                             */
  568. /***************************************************************/
  569. #ifdef HAVE_STDARG
  570. #ifdef HAVE_PROTOS
  571. PUBLIC void Eprint(const char *fmt, ...)
  572. #else
  573. void Eprint(fmt)
  574. char *fmt;
  575. #endif
  576. #else
  577. /*VARARGS0*/
  578. void Eprint(va_alist)
  579. va_dcl
  580. #endif
  581. {
  582.    va_list argptr;
  583. #ifndef HAVE_STDARG
  584.    char *fmt;
  585. #endif
  586.  
  587.    /* Check if more than one error msg. from this line */
  588.    if (!FreshLine && !ShowAllErrors) return;
  589.  
  590.    if (FreshLine) {
  591.       FreshLine = 0;
  592.       if (strcmp(FileName, "-"))
  593.          (void) fprintf(ErrFp, "%s(%d): ", FileName, LineNo);
  594.       else
  595.          (void) fprintf(ErrFp, "-stdin-(%d): ", LineNo);
  596.       if (DebugFlag & DB_PRTLINE) OutputLine(ErrFp);
  597.    } else fprintf(ErrFp, "       ");
  598.  
  599. #ifdef HAVE_STDARG
  600.    va_start(argptr, fmt);
  601. #else
  602.    va_start(argptr);
  603.    fmt = va_arg(argptr, char *);
  604. #endif
  605.    (void) vfprintf(ErrFp, fmt, argptr);
  606.    (void) fputc('\n', ErrFp);
  607. #ifndef HAVE_STDARG
  608.    va_end(argptr);
  609. #endif
  610.    return;
  611. }
  612.  
  613. /***************************************************************/
  614. /*                                                             */
  615. /*  OutputLine                                                 */
  616. /*                                                             */
  617. /*  Output a line from memory buffer to a file pointer.  This  */
  618. /*  simply involves escaping newlines.                         */
  619. /*                                                             */
  620. /***************************************************************/
  621. #ifdef HAVE_PROTOS
  622. PUBLIC void OutputLine(FILE *fp)
  623. #else
  624. void OutputLine(fp)
  625. FILE *fp;
  626. #endif
  627. {
  628.    register char *s = CurLine;
  629.    register char c = 0;
  630.  
  631.    while (*s) {
  632.       if (*s == '\n') putc('\\', fp);
  633.       putc(*s, fp);
  634.       c = *s++;
  635.    }
  636.    if (c != '\n') putc('\n', fp);
  637. }
  638.  
  639. /***************************************************************/
  640. /*                                                             */
  641. /*  CreateParser                                               */
  642. /*                                                             */
  643. /*  Create a parser given a string buffer                      */
  644. /*                                                             */
  645. /***************************************************************/
  646. #ifdef HAVE_PROTOS
  647. PUBLIC void CreateParser(char *s, ParsePtr p)
  648. #else
  649. void CreateParser(s, p)
  650. char *s;
  651. ParsePtr p;
  652. #endif
  653. {
  654.    p->text = s;
  655.    p->pos = s;
  656.    p->isnested = 0;
  657.    p->epos = NULL;
  658.    p->etext = NULL;
  659.    p->allownested = 1;
  660.    TokenPushed = NULL;
  661. }
  662.  
  663. /***************************************************************/
  664. /*                                                             */
  665. /*  DestroyParser                                              */
  666. /*                                                             */
  667. /*  Destroy a parser, freeing up resources used.               */
  668. /*                                                             */
  669. /***************************************************************/
  670. #ifdef HAVE_PROTOS
  671. PUBLIC void DestroyParser(ParsePtr p)
  672. #else
  673. void DestroyParser(p)
  674. ParsePtr p;
  675. #endif
  676. {
  677.    if (p->isnested && p->etext) {
  678.       free(p->etext);
  679.       p->etext = NULL;
  680.       p->isnested = 0;
  681.    }
  682. }
  683.  
  684. /***************************************************************/
  685. /*                                                             */
  686. /*  PushToken - one level of token pushback.  This is          */
  687. /*  GLOBAL, not on a per-parser basis.                         */
  688. /*                                                             */
  689. /***************************************************************/
  690. #ifdef HAVE_PROTOS
  691. PUBLIC void PushToken(const char *tok)
  692. #else
  693. void PushToken(tok)
  694. char *tok;
  695. #endif
  696. {
  697.    TokenPushed = TPushBuffer;
  698.    strcpy(TPushBuffer, tok);
  699.    strcat(TPushBuffer, " ");  /* Separate the pushed token from the next
  700.                          token */
  701.  
  702. }
  703.  
  704. /***************************************************************/
  705. /*                                                             */
  706. /*  SystemTime                                                 */
  707. /*                                                             */
  708. /*  Return the system time in seconds past midnight            */
  709. /*                                                             */
  710. /***************************************************************/
  711. #ifdef HAVE_PROTOS
  712. PUBLIC long SystemTime(void)
  713. #else
  714. long SystemTime()
  715. #endif
  716. {
  717. #if defined( __MSDOS__ ) && defined( __TURBOC__ )
  718. /* Get time in Turbo C */
  719.  
  720.    struct time t;
  721.  
  722.    gettime(&t);
  723.    return (long) t.ti_hour * 3600L + (long) t.ti_min * 60L +
  724.       (long) t.ti_sec;
  725. #else
  726. /* Get time in Unix or with MSC */
  727.   time_t tloc;
  728.   struct tm *t;
  729.  
  730.    (void) time(&tloc);
  731.    t = localtime(&tloc);
  732.    return (long) t->tm_hour * 3600L + (long) t->tm_min * 60L +
  733.       (long) t->tm_sec;
  734. #endif
  735. }
  736. /***************************************************************/
  737. /*                                                             */
  738. /*  SystemDate                                                 */
  739. /*                                                             */
  740. /*  Obtains today's date.  Returns Julian date or -1 for       */
  741. /*  failure.  (Failure happens if sys date is before BASE      */
  742. /*  year.)                                                     */
  743. /*                                                             */
  744. /***************************************************************/
  745. #ifdef HAVE_PROTOS
  746. PUBLIC int SystemDate(int *y, int *m, int *d)
  747. #else
  748. int SystemDate(y, m, d)
  749. int *d;
  750. int *m;
  751. int *y;
  752. #endif
  753. {
  754. #if defined( __MSDOS__ ) && defined( __TURBOC__ )
  755. /* Get today's date in Turbo C */
  756.    struct date da;
  757.  
  758.    getdate(&da);
  759.    *y = da.da_year;
  760.    *m = da.da_mon - 1;
  761.    *d = da.da_day;
  762. #else
  763. /* Get today's date in UNIX or with MSC */
  764.    time_t tloc;
  765.    struct tm *t;
  766.  
  767.    (void) time(&tloc);
  768.    t = localtime(&tloc);
  769.  
  770.    *d = t->tm_mday;
  771.    *m = t->tm_mon;
  772.    *y = t->tm_year + 1900;
  773. #endif
  774.    return Julian(*y, *m, *d);
  775. }
  776.  
  777.  
  778. /***************************************************************/
  779. /*                                                             */
  780. /*  DoIf - handle the IF command.                              */
  781. /*                                                             */
  782. /***************************************************************/
  783. #ifdef HAVE_PROTOS
  784. PUBLIC int DoIf(ParsePtr p)
  785. #else
  786. int DoIf(p)
  787. ParsePtr p;
  788. #endif
  789. {
  790.    Value v;
  791.    int r;
  792.    unsigned syndrome;
  793.  
  794.    if (NumIfs >= IF_NEST) return E_NESTED_IF;
  795.  
  796.    if (ShouldIgnoreLine()) syndrome = IF_TRUE | BEFORE_ELSE;
  797.    else {
  798.       if ( (r = EvaluateExpr(p, &v)) ) {
  799.          syndrome = IF_TRUE | BEFORE_ELSE;
  800.      Eprint("%s", ErrMsg[r]);
  801.       } else
  802.          if ( (v.type != STR_TYPE && v.v.val) ||
  803.               (v.type == STR_TYPE && strcmp(v.v.str, "")) )
  804.             syndrome = IF_TRUE | BEFORE_ELSE;
  805.          else
  806.             syndrome = IF_FALSE | BEFORE_ELSE;
  807.    }
  808.  
  809.    NumIfs++;
  810.    IfFlags &= ~(IF_MASK << (2*NumIfs - 2));
  811.    IfFlags |= syndrome << (2 * NumIfs - 2);
  812.    if (ShouldIgnoreLine()) return OK;
  813.    return VerifyEoln(p);
  814. }
  815.  
  816.  
  817. /***************************************************************/
  818. /*                                                             */
  819. /*  DoElse - handle the ELSE command.                          */
  820. /*                                                             */
  821. /***************************************************************/
  822. #ifdef HAVE_PROTOS
  823. PUBLIC int DoElse(ParsePtr p)
  824. #else
  825. int DoElse(p)
  826. ParsePtr p;
  827. #endif
  828. {
  829.    unsigned syndrome;
  830.  
  831.    if (!NumIfs) return E_ELSE_NO_IF;
  832.  
  833.    syndrome = IfFlags >> (2 * NumIfs - 2);
  834.  
  835.    if ((syndrome & IF_ELSE_MASK) == AFTER_ELSE) return E_ELSE_NO_IF;
  836.  
  837.    IfFlags |= AFTER_ELSE << (2 * NumIfs - 2);
  838.    return VerifyEoln(p);
  839. }
  840.  
  841. /***************************************************************/
  842. /*                                                             */
  843. /*  DoEndif - handle the Endif command.                        */
  844. /*                                                             */
  845. /***************************************************************/
  846. #ifdef HAVE_PROTOS
  847. PUBLIC int DoEndif(ParsePtr p)
  848. #else
  849. int DoEndif(p)
  850. ParsePtr p;
  851. #endif
  852. {
  853.    if (!NumIfs) return E_ENDIF_NO_IF;
  854.    NumIfs--;
  855.    return VerifyEoln(p);
  856. }
  857.  
  858. /***************************************************************/
  859. /*                                                             */
  860. /*  DoIfTrig                                                   */
  861. /*                                                             */
  862. /*  Handle the IFTRIG command.                                 */
  863. /*                                                             */
  864. /***************************************************************/
  865. #ifdef HAVE_PROTOS
  866. PUBLIC int DoIfTrig(ParsePtr p)
  867. #else
  868. int DoIfTrig(p)
  869. ParsePtr p;
  870. #endif
  871. {
  872.    int r;
  873.    unsigned syndrome;
  874.    Trigger trig;
  875.    TimeTrig tim;
  876.    int jul;
  877.  
  878.  
  879.    if (NumIfs >= IF_NEST) return E_NESTED_IF;
  880.    if (ShouldIgnoreLine()) syndrome = IF_TRUE | BEFORE_ELSE;
  881.    else {
  882.       if ( (r=ParseRem(p, &trig, &tim)) ) return r;
  883.       if (trig.typ != NO_TYPE) return E_PARSE_ERR;
  884.       jul = ComputeTrigger(trig.scanfrom, &trig, &r);
  885.       if (r) syndrome = IF_TRUE | BEFORE_ELSE;
  886.       else {
  887.          if (ShouldTriggerReminder(&trig, &tim, jul))
  888.         syndrome = IF_TRUE | BEFORE_ELSE;
  889.      else
  890.         syndrome = IF_FALSE | BEFORE_ELSE;
  891.       }
  892.    }
  893.    NumIfs++;
  894.    IfFlags &= ~(IF_MASK << (2*NumIfs - 2));
  895.    IfFlags |= syndrome << (2 * NumIfs - 2);
  896.    return OK;
  897. }
  898.  
  899.  
  900. /***************************************************************/
  901. /*                                                             */
  902. /*  ShouldIgnoreLine - given the current state of the IF       */
  903. /*  stack, should we ignore the current line?                  */
  904. /*                                                             */
  905. /***************************************************************/
  906. #ifdef HAVE_PROTOS
  907. PUBLIC int ShouldIgnoreLine(void)
  908. #else
  909. int ShouldIgnoreLine()
  910. #endif
  911. {
  912.    register int i, syndrome;
  913.  
  914. /* Algorithm - go from outer to inner, and if any should be ignored, then
  915.    ignore the whole. */
  916.  
  917.    for (i=0; i<NumIfs; i++) {
  918.       syndrome = (IfFlags >> (i*2)) & IF_MASK;
  919.       if (syndrome == IF_TRUE+AFTER_ELSE ||
  920.           syndrome == IF_FALSE+BEFORE_ELSE) return 1;
  921.    }
  922.    return 0;
  923. }
  924.  
  925. /***************************************************************/
  926. /*                                                             */
  927. /*  VerifyEoln                                                 */
  928. /*                                                             */
  929. /*  Verify that current line contains no more tokens.          */
  930. /*                                                             */
  931. /***************************************************************/
  932. #ifdef HAVE_PROTOS
  933. PUBLIC int VerifyEoln(ParsePtr p)
  934. #else
  935. int VerifyEoln(p)
  936. ParsePtr p;
  937. #endif
  938. {
  939.    int r;
  940.  
  941.    if ( (r = ParseToken(p, TokBuffer)) ) return r;
  942.    if (*TokBuffer && (*TokBuffer != '#') && (*TokBuffer != ';')) {
  943.       Eprint("%s: '%s'", ErrMsg[E_EXPECTING_EOL], TokBuffer);
  944.       return E_EXTRANEOUS_TOKEN;
  945.    }
  946.    return OK;
  947. }
  948.  
  949. /***************************************************************/
  950. /*                                                             */
  951. /*  DoDebug                                                    */
  952. /*                                                             */
  953. /*  Set the debug options under program control.               */
  954. /*                                                             */
  955. /***************************************************************/
  956. #ifdef HAVE_PROTOS
  957. PUBLIC int DoDebug(ParsePtr p)
  958. #else
  959. int DoDebug(p)
  960. ParsePtr p;
  961. #endif
  962. {
  963.    int err;
  964.    int ch;
  965.    int val=1;
  966.  
  967.    while(1) {
  968.       ch = ParseChar(p, &err, 0);
  969.       if (err) return err;
  970.       switch(ch) {
  971.          case '#':
  972.      case ';':
  973.      case 0:
  974.         return OK;
  975.  
  976.      case ' ':
  977.      case '\t':
  978.         break;
  979.  
  980.      case '+':
  981.         val = 1;
  982.         break;
  983.  
  984.      case '-':
  985.         val = 0;
  986.         break;
  987.  
  988.       case 'e':
  989.       case 'E':
  990.         if (val) DebugFlag |=  DB_ECHO_LINE;
  991.         else     DebugFlag &= ~DB_ECHO_LINE;
  992.         break;
  993.  
  994.       case 'x':
  995.       case 'X':
  996.         if (val) DebugFlag |=  DB_PRTEXPR;
  997.         else     DebugFlag &= ~DB_PRTEXPR;
  998.         break;
  999.  
  1000.       case 't':
  1001.       case 'T':
  1002.         if (val) DebugFlag |=  DB_PRTTRIG;
  1003.         else     DebugFlag &= ~DB_PRTTRIG;
  1004.         break;
  1005.  
  1006.       case 'v':
  1007.       case 'V':
  1008.         if (val) DebugFlag |=  DB_DUMP_VARS;
  1009.         else     DebugFlag &= ~DB_DUMP_VARS;
  1010.         break;
  1011.  
  1012.       case 'l':
  1013.       case 'L':
  1014.         if (val) DebugFlag |=  DB_PRTLINE;
  1015.         else     DebugFlag &= ~DB_PRTLINE;
  1016.         break;
  1017.  
  1018.       }
  1019.    }
  1020. }
  1021.  
  1022. /***************************************************************/
  1023. /*                                                             */
  1024. /*  DoBanner                                                   */
  1025. /*                                                             */
  1026. /*  Set the banner to be printed just before the first         */
  1027. /*  reminder is issued.                                        */
  1028. /*                                                             */
  1029. /***************************************************************/
  1030. #ifdef HAVE_PROTOS
  1031. PUBLIC int DoBanner(ParsePtr p)
  1032. #else
  1033. int DoBanner(p)
  1034. ParsePtr p;
  1035. #endif
  1036. {
  1037.    int err;
  1038.    int c;
  1039.    char buf[LINELEN];   /* So we don't mess up the banner if an error occurs */
  1040.    char *s;
  1041.  
  1042.    c = ParseChar(p, &err, 0);
  1043.    if (err) return err;
  1044.    while (isspace(c)) {
  1045.       c = ParseChar(p, &err, 0);
  1046.       if (err) return err;
  1047.    }
  1048.    if (!c) return E_EOLN;
  1049.    s = buf;
  1050.  
  1051.    while(c) {
  1052.       *s++ = c;
  1053.       c = ParseChar(p, &err, 0);
  1054.       if (err) return err;
  1055.    }
  1056.    *s++ = 0;
  1057.    strcpy(Banner, buf);
  1058.    return OK;
  1059. }
  1060.  
  1061. /***************************************************************/
  1062. /*                                                             */
  1063. /*  DoRun                                                      */
  1064. /*                                                             */
  1065. /*  Enable or disable the RUN command under program control    */
  1066. /*                                                             */
  1067. /*                                                             */
  1068. /***************************************************************/
  1069. #ifdef HAVE_PROTOS
  1070. PUBLIC int DoRun(ParsePtr p)
  1071. #else
  1072. int DoRun(p)
  1073. ParsePtr p;
  1074. #endif
  1075. {
  1076.    int r;
  1077.  
  1078.    if ( (r=ParseToken(p, TokBuffer)) ) return r;
  1079.  
  1080. /* Only allow RUN ON in top-level script */
  1081.    if (! StrCmpi(TokBuffer, "ON")) {
  1082.       if (TopLevel()) RunDisabled &= ~RUN_SCRIPT;
  1083.    }
  1084. /* But allow RUN OFF anywhere */
  1085.    else if (! StrCmpi(TokBuffer, "OFF"))
  1086.       RunDisabled |= RUN_SCRIPT;
  1087.    else return E_PARSE_ERR;
  1088.  
  1089.    return VerifyEoln(p);
  1090. }
  1091.  
  1092. /***************************************************************/
  1093. /*                                                             */
  1094. /*  DoFlush                                                    */
  1095. /*                                                             */
  1096. /*  Flush stdout and stderr                                    */
  1097. /*                                                             */
  1098. /***************************************************************/
  1099. #ifdef HAVE_PROTOS
  1100. PUBLIC int DoFlush(ParsePtr p)
  1101. #else
  1102. int DoFlush(p)
  1103. ParsePtr p;
  1104. #endif
  1105. {
  1106.    fflush(stdout);
  1107.    fflush(stderr);
  1108.    return VerifyEoln(p);
  1109. }
  1110.  
  1111. /***************************************************************/
  1112. /*                                                             */
  1113. /*  DoExit                                                     */
  1114. /*                                                             */
  1115. /*  Handle the EXIT command.                                   */
  1116. /*                                                             */
  1117. /***************************************************************/
  1118. #ifdef HAVE_PROTOS
  1119. PUBLIC void DoExit(ParsePtr p)
  1120. #else
  1121. void DoExit(p)
  1122. ParsePtr p;
  1123. #endif
  1124. {
  1125.    int r;
  1126.    Value v;
  1127.  
  1128.    r = EvaluateExpr(p, &v);
  1129.    if (r || v.type != INT_TYPE) exit(99);
  1130.    exit(v.v.val);
  1131. }
  1132.  
  1133. /***************************************************************/
  1134. /*                                                             */
  1135. /*  DoErrMsg                                                   */
  1136. /*                                                             */
  1137. /*  Issue an error message under program control.              */
  1138. /*                                                             */
  1139. /***************************************************************/
  1140. #ifdef HAVE_PROTOS
  1141. PUBLIC int DoErrMsg(ParsePtr p)
  1142. #else
  1143. int DoErrMsg(p)
  1144. ParsePtr p;
  1145. #endif
  1146. {
  1147.    TimeTrig tt;
  1148.    Trigger t;
  1149.    int r;
  1150.    char *s;
  1151.  
  1152.    t.typ = MSG_TYPE;
  1153.    tt.ttime = SystemTime() / 60;
  1154.    if ( (r=DoSubst(p, SubstBuffer, &t, &tt, JulianToday, NORMAL_MODE)) )
  1155.       return r;
  1156.    s = SubstBuffer;
  1157.    while (isspace(*s)) s++;
  1158.    fprintf(ErrFp, "%s\n", s);
  1159.    return OK;
  1160. }
  1161.  
  1162. /***************************************************************/
  1163. /*                                                             */
  1164. /*  CalcMinsFromUTC                                            */
  1165. /*                                                             */
  1166. /*  Attempt to calculate the minutes from UTC for a specific   */
  1167. /*  date.                                                      */
  1168. /*                                                             */
  1169. /***************************************************************/
  1170.  
  1171. /* The array FoldArray[2][7] contains sample years which begin
  1172.    on the specified weekday.  For example, FoldArray[0][2] is a
  1173.    non-leapyear beginning on Wednesday, and FoldArray[1][5] is a
  1174.    leapyear beginning on Saturday.  Used to fold back dates which
  1175.    are too high for the standard Unix representation.
  1176.    NOTE:  This implies that you cannot set BASE > 2001!!!!! */
  1177. static int FoldArray[2][7] = {
  1178.    {2001, 2002, 2003, 2009, 2010, 2005, 2006},
  1179.    {2024, 2008, 2020, 2004, 2016, 2000, 2012}
  1180. };
  1181.  
  1182. #ifdef HAVE_PROTOS
  1183. PUBLIC int CalcMinsFromUTC(int jul, int tim, int *mins, int *isdst)
  1184. #else
  1185. int CalcMinsFromUTC(jul, tim, mins, isdst)
  1186. int jul, tim, *mins, *isdst;
  1187. #endif
  1188. {
  1189.  
  1190. /* Convert jul and tim to an Unix tm struct */
  1191.    int yr, mon, day;
  1192.    struct tm local, utc, *temp;
  1193.    time_t loc_t, utc_t;
  1194.  
  1195.    FromJulian(jul, &yr, &mon, &day);
  1196.  
  1197. /* If the year is greater than 2037, some Unix machines have problems.
  1198.    Fold it back to a "similar" year and trust that the UTC calculations
  1199.    are still valid... */
  1200.    if (FoldYear && yr>2037) {
  1201.       jul = Julian(yr, 0, 1);
  1202.       yr = FoldArray[IsLeapYear(yr)][jul%7];
  1203.    }
  1204.    local.tm_sec = 0;
  1205.    local.tm_min = tim % 60;
  1206.    local.tm_hour = tim / 60;
  1207.    local.tm_mday = day;
  1208.    local.tm_mon = mon;
  1209.    local.tm_year = yr-1900;
  1210.    local.tm_isdst = -1;  /* We don't know whether or not dst is in effect */
  1211.  
  1212. #if !defined(HAVE_MKTIME)
  1213.    loc_t = timelocal(&local);
  1214.    local.tm_isdst = 0;
  1215.    utc_t = timegm(&local);
  1216. #else
  1217.    loc_t = mktime(&local);
  1218.    if (loc_t == -1) return 1;
  1219.    temp = gmtime(&loc_t);
  1220.    utc = *temp;
  1221.    utc.tm_isdst = 0;
  1222.    utc_t = mktime(&utc);
  1223.    if (utc_t == -1) return 1;
  1224. #endif
  1225.    temp = localtime(&loc_t);
  1226. #ifdef HAVE_MKTIME
  1227.    if (mins) *mins = (int)  ( ((temp->tm_isdst) ? 60 : 0) +
  1228.                              (loc_t - utc_t) / 60 );  /* Should use difftime */
  1229. #else
  1230.    if (mins) *mins = (int) ((utc_t - loc_t) / 60);
  1231. #endif
  1232.    if (isdst) *isdst = temp->tm_isdst;
  1233.    return 0;
  1234. }
  1235.  
  1236. /***************************************************************/
  1237. /*                                                             */
  1238. /*  FillParagraph                                              */
  1239. /*                                                             */
  1240. /*  Write a string to standard output, formatting it as a      */
  1241. /*  paragraph according to the FirstIndent, FormWidth and      */
  1242. /*  SubsIndent variables.  Spaces are gobbled.  Double-spaces  */
  1243. /*  are inserted after '.', '?' and '!'.  Newlines in the      */
  1244. /*  source are treated as paragraph breaks.                    */
  1245. /*                                                             */
  1246. /***************************************************************/
  1247.  
  1248. /* A macro safe ONLY if used with arg with no side effects! */
  1249. #define ISBLANK(c) (isspace(c) && (c) != '\n')
  1250.  
  1251. #ifdef HAVE_PROTOS
  1252. PUBLIC void FillParagraph(char *s, int AsPopUp)
  1253. #else
  1254. void FillParagraph(s, AsPopUp)
  1255. char *s;
  1256. int AsPopUp;
  1257. #endif
  1258. {
  1259.  
  1260.    int line = 0;
  1261.    int i, j;
  1262.    int doublespace = 1;
  1263.    int pendspace;
  1264.    int len;
  1265.    char *t;
  1266.  
  1267.    int roomleft;
  1268.  
  1269.    if (!s || !*s) return;
  1270.  
  1271.    /* Skip leading spaces */
  1272.    while(ISBLANK(*s)) s++;
  1273.  
  1274.    /* Start formatting */
  1275.    while(1) {
  1276.  
  1277.       /* If it's a carriage return, output it and start new paragraph */
  1278.       if (*s == '\n') {
  1279.      Putchar('\n');
  1280.      s++;
  1281.      line = 0;
  1282.      while(ISBLANK(*s)) s++;
  1283.      continue;
  1284.       }
  1285.       if (!*s) {
  1286.          return;
  1287.       }
  1288.       /* Over here, we're at the beginning of a line.  Emit the correct
  1289.          number of spaces */
  1290.       j = line ? SubsIndent : FirstIndent;
  1291.       for (i=0; i<j; i++) {
  1292.       Putchar(' ');
  1293.         }
  1294.  
  1295.       /* Calculate the amount of room left on this line */
  1296.       roomleft = FormWidth - j;
  1297.       pendspace = 0;
  1298.  
  1299.       /* Emit words until the next one won't fit */
  1300.       while(1) {
  1301.          while(ISBLANK(*s)) s++;
  1302.      if (*s == '\n') break;
  1303.          t = s;
  1304.          while(*s && !isspace(*s)) s++;
  1305.      len = s - t;
  1306.      if (!len) {
  1307.         return;
  1308.          }
  1309.      if (!pendspace || len+pendspace <= roomleft) {
  1310.         for (i=0; i<pendspace; i++) {
  1311.            Putchar(' ');
  1312.         }
  1313.            while(t < s) {
  1314.            Putchar(*t);
  1315.            if (strchr(EndSent, *t)) doublespace = 2;
  1316.            else if (!strchr(EndSentIg, *t)) doublespace = 1;
  1317.            t++;
  1318.             }
  1319.          } else {
  1320.         s = t;
  1321.         Putchar('\n');
  1322.         line++;
  1323.         break;
  1324.          }
  1325.      roomleft -= len+doublespace;
  1326.      pendspace = doublespace;
  1327.       }
  1328.    }
  1329. }
  1330.  
  1331. #if defined(NEED_TIMEGM) && !defined(HAVE_MKTIME)
  1332. #define        TGM_SEC        (1)
  1333. #define        TGM_MIN        (60 * TGM_SEC)
  1334. #define        TGM_HR        (60 * TGM_MIN)
  1335. #define        TGM_DAY        (24 * TGM_HR)
  1336.  
  1337. #ifdef HAVE_PROTOS
  1338. PRIVATE long time_cheat(int year, int month)
  1339. #else
  1340. static long time_cheat (year, month)
  1341. int year;
  1342. int month;
  1343. #endif
  1344. {
  1345.     long guess = time((long *) NULL);
  1346.     struct tm g;
  1347.     int diff;
  1348.  
  1349.     g = *gmtime (&guess);
  1350.     while ((diff = year - g.tm_year) > 0)
  1351.     {
  1352.         guess += diff * (363 - TGM_DAY);
  1353.         g = *gmtime (&guess);
  1354.     }
  1355.     g.tm_mday--;
  1356.     guess -= g.tm_sec * TGM_SEC + g.tm_min * TGM_MIN +
  1357.          g.tm_hour * TGM_HR + g.tm_mday * TGM_DAY;
  1358.     return (guess);
  1359. }
  1360.  
  1361. #ifdef HAVE_PROTOS
  1362. PUBLIC long timegm (struct tm *tm)
  1363. #else
  1364. long timegm(tm)
  1365. struct tm *tm;
  1366. #endif
  1367. {
  1368.     long clock = time_cheat (tm->tm_year, tm->tm_mon);
  1369.  
  1370.     return (clock + tm->tm_sec * TGM_SEC +
  1371.             tm->tm_min * TGM_MIN +
  1372.             tm->tm_hour * TGM_HR +
  1373.             (tm->tm_mday - 1) * TGM_DAY);
  1374. }
  1375.  
  1376. #ifdef HAVE_PROTOS
  1377. PUBLIC long timelocal (struct tm *tm)
  1378. #else
  1379. long timelocal (tm)
  1380. struct tm *tm;
  1381. #endif
  1382. {
  1383.     long zero = 0;
  1384.     struct tm epoch;
  1385.     int tzmin;
  1386.     long clock;
  1387.     struct tm test;
  1388.  
  1389.     epoch = *localtime (&zero);
  1390.     tzmin = epoch.tm_hour * 60 + epoch.tm_min;
  1391.     if (tzmin > 0)
  1392.     {
  1393.         tzmin = 24 * 60 - tzmin;
  1394.         if (epoch.tm_year == 70)
  1395.             tzmin -= 24 * 60;
  1396.     }
  1397.     clock = timegm (tm) + tzmin * TGM_MIN;
  1398.     test = *localtime (&clock);
  1399.  
  1400.     if (test.tm_hour != tm->tm_hour)
  1401.         clock -= TGM_HR;
  1402.     return (clock);
  1403. }
  1404. #endif /* NEED_TIMEGM */
  1405.  
  1406. /***************************************************************/
  1407. /*                                                             */
  1408. /*  LocalToUTC                                                 */
  1409. /*                                                             */
  1410. /*  Convert a local date/time to a UTC date/time.              */
  1411. /*                                                             */
  1412. /***************************************************************/
  1413. #ifdef HAVE_PROTOS
  1414. PUBLIC void LocalToUTC(int locdate, int loctime, int *utcdate, int *utctime)
  1415. #else
  1416. void LocalToUTC(locdate, loctime, utcdate, utctime)
  1417. int locdate, loctime, *utcdate, *utctime;
  1418. #endif
  1419. {
  1420.    int diff;
  1421.    int dummy;
  1422.  
  1423.    if (!CalculateUTC || CalcMinsFromUTC(locdate, loctime, &diff, &dummy)) 
  1424.       diff=MinsFromUTC;
  1425.  
  1426.    loctime -= diff;
  1427.    if (loctime < 0) {
  1428.       loctime += 1440;
  1429.       locdate--;
  1430.    } else if (loctime >= 1440) {
  1431.       loctime -= 1440;
  1432.       locdate++;
  1433.    }
  1434.    *utcdate = locdate;
  1435.    *utctime = loctime;
  1436. }
  1437.  
  1438. /***************************************************************/
  1439. /*                                                             */
  1440. /*  UTCToLocal                                                 */
  1441. /*                                                             */
  1442. /*  Convert a UTC date/time to a local date/time.              */
  1443. /*                                                             */
  1444. /***************************************************************/
  1445. #ifdef HAVE_PROTOS
  1446. PUBLIC void UTCToLocal(int utcdate, int utctime, int *locdate, int *loctime)
  1447. #else
  1448. void UTCToLocal(utcdate, utctime, locdate, loctime)
  1449. int utcdate, utctime, *locdate, *loctime;
  1450. #endif
  1451. {
  1452.    int diff;
  1453.    int dummy;
  1454.  
  1455.    /* Hack -- not quite right when DST changes.  */
  1456.    if (!CalculateUTC || CalcMinsFromUTC(utcdate, utctime, &diff, &dummy))
  1457.       diff=MinsFromUTC;
  1458.  
  1459.    utctime += diff;
  1460.    if (utctime < 0) {
  1461.       utctime += 1440;
  1462.       utcdate--;
  1463.    } else if (utctime >= 1440) {
  1464.       utctime -= 1440;
  1465.       utcdate++;
  1466.    }
  1467.    *locdate = utcdate;
  1468.    *loctime = utctime;
  1469. }
  1470.  
  1471. /***************************************************************/
  1472. /*                                   */
  1473. /* SigIntHandler                           */
  1474. /*                                   */
  1475. /* For debugging purposes, when sent a SIGINT, we print the    */
  1476. /* contents of the queue.  This does NOT work when the -f      */
  1477. /* command-line flag is supplied.                   */
  1478. /*                                   */
  1479. /* For OS/2, this has to be in the main thread.            */
  1480. /*                                   */
  1481. /***************************************************************/
  1482. #ifdef __BORLANDC__
  1483. void __cdecl SigIntHandler(int d)
  1484. #else
  1485. #ifdef HAVE_PROTOS
  1486. void SigIntHandler(void)
  1487. #else
  1488. void SigIntHandler()
  1489. #endif
  1490. #endif
  1491. {
  1492. #ifdef SYSV
  1493.    signal(SIGINT, SigIntHandler);
  1494. #else
  1495. #ifdef __BORLANDC__
  1496.    signal(SIGINT, SIG_DFL);
  1497. #else
  1498. #ifdef __OS2__
  1499.    signal(SIGINT, SIG_ACK);
  1500. #endif
  1501. #endif
  1502. #endif
  1503.    GotSigInt();
  1504.    exit(0);
  1505. }
  1506.  
  1507.