home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume36 / remind / patch04b / patch.04.B
Encoding:
Text File  |  1993-03-07  |  56.9 KB  |  1,794 lines

  1. ***************
  2. *** 81,85 ****
  3. --- 93,103 ----
  4.   
  5.   /* Define any overrides here, such as L_ORDINAL_OVERRIDE, L_A_OVER, etc.
  6.      See the file dosubst.c for more info. */
  7. + #define L_AMPM_OVERRIDE(ampm, hour)    ampm = (hour < 12) ? (hour<5) ? " nachts" : " vormittags" : (hour > 17) ? " abends" : " nachmittags";
  8. + #define L_ORDINAL_OVERRIDE        plu = ".";
  9. + #define L_A_OVER            sprintf(s, "%s %s, den %d. %s %d", L_ON, DayName[jul%7], d, MonthName[m], y);
  10. + #define    L_G_OVER            sprintf(s, "%s %s, den %d. %s", L_ON, DayName[jul%7], d, MonthName[m]);
  11. + #define L_U_OVER            L_A_OVER
  12. + #define L_V_OVER            L_G_OVER
  13.   
  14.   #endif /* L_IN_DOSUBST */
  15. *** ../patch3/globals.h    Thu Jan 21 16:02:55 1993
  16. --- ./globals.h    Mon Mar  1 16:56:30 1993
  17. ***************
  18. *** 36,41 ****
  19. --- 36,42 ----
  20.   EXTERN  char    LineBuffer[LINELEN];
  21.   EXTERN  char    SubstBuffer[LINELEN];
  22.   EXTERN  char    TokBuffer[TOKSIZE+1];
  23. + EXTERN  INIT(   char    *MsgCommand, NULL);
  24.   EXTERN  INIT(    char    ShowAllErrors, 0);
  25.   EXTERN  INIT(    int     DebugFlag, 0);
  26.   EXTERN  INIT(   char    DoCalendar, 0);
  27. ***************
  28. *** 50,55 ****
  29. --- 51,59 ----
  30.   EXTERN  INIT(   char    RunDisabled, 0);
  31.   EXTERN  INIT(   char    IgnoreOnce, 0);
  32.   EXTERN  INIT(   char    Banner[LINELEN], L_BANNER);
  33. + EXTERN  INIT(   char    SortByTime, 0);
  34. + EXTERN  INIT(   char    SortByDate, 0);
  35.   EXTERN    char    *InitialFile;
  36.   EXTERN    int    FileAccessDate;
  37.   
  38. *** ../patch3/init.c    Mon Feb  8 14:41:06 1993
  39. --- ./init.c    Mon Mar  1 17:37:26 1993
  40. ***************
  41. *** 62,67 ****
  42. --- 62,68 ----
  43.    *  -bn      = Time format for cal (0, 1, or 2)
  44.    *  -xn      = Max. number of iterations for SATISFY
  45.    *  -uname   = Run as user 'name' - only valid when run by root.
  46. +  *  -kcmd    = Run 'cmd' for MSG-type reminders instead of printing to stdout
  47.    *
  48.    **************************************************************/
  49.   
  50. ***************
  51. *** 156,161 ****
  52. --- 157,178 ----
  53.              Hush = 1;
  54.              break;
  55.   
  56. +         case 'g':
  57. +         case 'G':
  58. +            SortByDate = SORT_ASCEND;
  59. +            SortByTime = SORT_ASCEND;
  60. +            if (*arg) {
  61. +               if (*arg == 'D' || *arg == 'd')
  62. +              SortByDate = SORT_DESCEND;
  63. +           arg++;
  64. +                }
  65. +            if (*arg) {
  66. +               if (*arg == 'D' || *arg == 'd')
  67. +              SortByTime = SORT_DESCEND;
  68. +           arg++;
  69. +                }
  70. +            break;
  71.   #if defined(UNIX) && defined(WANT_U_OPTION)
  72.           case 'u':
  73.           case 'U':
  74. ***************
  75. *** 269,274 ****
  76. --- 286,297 ----
  77.              if (MaxSatIter < 10) MaxSatIter=10;
  78.              break;
  79.   
  80. +         case 'k':
  81. +         case 'K':
  82. +            MsgCommand = arg;
  83. +            while (*arg) arg++;  /* Chew up remaining chars in this arg */
  84. +            break;
  85.           default:
  86.              fprintf(ErrFp, "Unknown option '%c'\n", *(arg-1));
  87.        }
  88. ***************
  89. *** 345,352 ****
  90.   void Usage()
  91.   #endif
  92.   {
  93. !    fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992, 1993 by David F. Skoll\n\n", VERSION, L_LANGNAME);
  94. !    fprintf(ErrFp, "        Usage: remind [options] filename [date]\n");
  95.      fprintf(ErrFp, "Options:\n");
  96.      fprintf(ErrFp, " -n     Output next occurrence of reminders in simple format\n");
  97.      fprintf(ErrFp, " -r     Disable RUN directives\n");
  98. --- 368,375 ----
  99.   void Usage()
  100.   #endif
  101.   {
  102. !    fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992, 1993 by David F. Skoll\n", VERSION, L_LANGNAME);
  103. !    fprintf(ErrFp, "Usage: remind [options] filename [date]\n");
  104.      fprintf(ErrFp, "Options:\n");
  105.      fprintf(ErrFp, " -n     Output next occurrence of reminders in simple format\n");
  106.      fprintf(ErrFp, " -r     Disable RUN directives\n");
  107. ***************
  108. *** 369,374 ****
  109. --- 392,399 ----
  110.      fprintf(ErrFp, " -e     Divert messages normally sent to stderr to stdout\n");
  111.      fprintf(ErrFp, " -b[n]  Time format for cal: 0=am/pm, 1=24hr, 2=none\n");
  112.      fprintf(ErrFp, " -x[n]  Iteration limit for SATISFY clause (def=150)\n");
  113. +    fprintf(ErrFp, " -kcmd  Run 'cmd' for MSG-type reminders\n");
  114. +    fprintf(ErrFp, " -g[d[d]] Sort reminders by date and time before issuing\n");
  115.      exit(1);
  116.   }
  117.   
  118. ***************
  119. *** 399,405 ****
  120.   
  121.      static char *NoEnvMem = "Remind: Out of memory for environment\n";
  122.      struct passwd *pwent;
  123. !    static char *home, *shell, *username;
  124.   
  125.      myuid = getuid();
  126.      if (myuid) {
  127. --- 424,430 ----
  128.   
  129.      static char *NoEnvMem = "Remind: Out of memory for environment\n";
  130.      struct passwd *pwent;
  131. !    static char *home, *shell, *username, *logname;
  132.   
  133.      myuid = getuid();
  134.      if (myuid) {
  135. ***************
  136. *** 441,453 ****
  137.      putenv(shell);
  138.   
  139.      if (pwent->pw_uid) {
  140. !       username = malloc(strlen(pwent->pw_dir) + 6);
  141.         if (!username) {
  142.            fprintf(ErrFp, NoEnvMem);
  143.            exit(1);
  144.         }
  145. !       sprintf(username, "USER=%s", pwent->pw_dir);
  146.         putenv(username);
  147.      }
  148.   }
  149.   #endif /* UNIX && WANT_U_OPTION */
  150. --- 466,485 ----
  151.      putenv(shell);
  152.   
  153.      if (pwent->pw_uid) {
  154. !       username = malloc(strlen(pwent->pw_name) + 6);
  155.         if (!username) {
  156.            fprintf(ErrFp, NoEnvMem);
  157.            exit(1);
  158.         }
  159. !       sprintf(username, "USER=%s", pwent->pw_name);
  160.         putenv(username);
  161. +       logname= malloc(strlen(pwent->pw_name) + 9);
  162. +       if (!logname) {
  163. +          fprintf(ErrFp, NoEnvMem);
  164. +          exit(1);
  165. +       }
  166. +       sprintf(logname, "LOGNAME=%s", pwent->pw_name);
  167. +       putenv(logname);
  168.      }
  169.   }
  170.   #endif /* UNIX && WANT_U_OPTION */
  171. *** ../patch3/lang.h    Fri Jan 29 13:39:59 1993
  172. --- ./lang.h    Mon Feb 15 13:45:00 1993
  173. ***************
  174. *** 11,18 ****
  175.   
  176.   /* I'm chauvinistic and name each language with its English name... */
  177.   
  178. ! #define ENGLISH    0
  179. ! #define GERMAN     1
  180.   
  181.   /* Add more languages here - but please e-mail dfs@doe.carleton.ca
  182.      to have your favorite language assigned a number.  If you add a
  183. --- 11,20 ----
  184.   
  185.   /* I'm chauvinistic and name each language with its English name... */
  186.   
  187. ! #define ENGLISH    0 /* original by David Skoll */
  188. ! #define GERMAN     1 /* translated by Wolfgang Thronicke */
  189. ! #define DUTCH      2 /* translated by Willem Kasdorp */
  190. ! #define FINNISH    3 /* translated by Mikko Silvonen */
  191.   
  192.   /* Add more languages here - but please e-mail dfs@doe.carleton.ca
  193.      to have your favorite language assigned a number.  If you add a
  194. ***************
  195. *** 27,33 ****
  196. --- 29,37 ----
  197.    *       Define the language you want to use here                       *
  198.    *                                                                      *
  199.    ************************************************************************/
  200. + #ifndef LANG  /* Allow for definition on compiler command line */
  201.   #define LANG ENGLISH
  202. + #endif
  203.   
  204.   /* If LANG is not defined, it defaults to English. */
  205.   #ifndef LANG
  206. ***************
  207. *** 40,45 ****
  208. --- 44,53 ----
  209.   #include "english.h"
  210.   #elif LANG == GERMAN
  211.   #include "german.h"
  212. + #elif LANG == DUTCH
  213. + #include "dutch.h"
  214. + #elif LANG == FINNISH
  215. + #include "finnish.h"
  216.   
  217.   /* If no sensible language, choose English.  I intended to use
  218.      the #error directive here, but some C compilers barf. */
  219. *** ../patch3/lnk.msc    Wed Dec 16 10:51:54 1992
  220. --- ./lnk.msc    Thu Mar  4 10:44:28 1993
  221. ***************
  222. *** 8,13 ****
  223. --- 8,14 ----
  224.   init.obj +
  225.   main.obj +
  226.   omit.obj +
  227. + sort.obj +
  228.   token.obj +
  229.   trigger.obj +
  230.   userfns.obj +
  231. *** ../patch3/lnk.tc    Wed Dec 16 10:51:54 1992
  232. --- ./lnk.tc    Thu Mar  4 10:44:16 1993
  233. ***************
  234. *** 9,14 ****
  235. --- 9,15 ----
  236.   init.obj
  237.   main.obj
  238.   omit.obj
  239. + sort.obj
  240.   token.obj
  241.   trigger.obj
  242.   userfns.obj
  243. *** ../patch3/main.c    Fri Feb  5 14:55:19 1993
  244. --- ./main.c    Mon Mar  1 17:34:01 1993
  245. ***************
  246. *** 112,117 ****
  247. --- 112,120 ----
  248.      if (RealToday == JulianToday) SetAccessDate(InitialFile, RealToday);
  249.   #endif
  250.   
  251. +    /* If there are sorted reminders, handle them */
  252. +    if (SortByDate) IssueSortedReminders();
  253.      /* If there are any background reminders queued up, handle them */
  254.   #ifdef HAVE_QUEUED
  255.      if (NumQueued || Daemon) {
  256. ***************
  257. *** 849,855 ****
  258.      else {
  259.         if ( (r=ParseRem(p, &trig, &tim)) ) return r;
  260.         if (trig.typ != NO_TYPE) return E_PARSE_ERR;
  261. !       jul = ComputeTrigger(JulianToday, &trig, &r);
  262.         if (r) syndrome = IF_TRUE | BEFORE_ELSE;
  263.         else {
  264.            if (ShouldTriggerReminder(&trig, &tim, jul))
  265. --- 852,858 ----
  266.      else {
  267.         if ( (r=ParseRem(p, &trig, &tim)) ) return r;
  268.         if (trig.typ != NO_TYPE) return E_PARSE_ERR;
  269. !       jul = ComputeTrigger(trig.scanfrom, &trig, &r);
  270.         if (r) syndrome = IF_TRUE | BEFORE_ELSE;
  271.         else {
  272.            if (ShouldTriggerReminder(&trig, &tim, jul))
  273. *** ../patch3/makefile.msc    Wed Dec 16 10:51:53 1992
  274. --- ./makefile.msc    Thu Mar  4 10:46:02 1993
  275. ***************
  276. *** 2,8 ****
  277.   
  278.   OBJS= calendar.obj dorem.obj dosubst.obj expr.obj files.obj funcs.obj \
  279.   globals.obj init.obj main.obj omit.obj token.obj trigger.obj userfns.obj \
  280. ! utils.obj var.obj
  281.   
  282.   DEFINES= /D__MSDOS__ /D__MSC__
  283.   
  284. --- 2,8 ----
  285.   
  286.   OBJS= calendar.obj dorem.obj dosubst.obj expr.obj files.obj funcs.obj \
  287.   globals.obj init.obj main.obj omit.obj token.obj trigger.obj userfns.obj \
  288. ! utils.obj var.obj sort.obj
  289.   
  290.   DEFINES= /D__MSDOS__ /D__MSC__
  291.   
  292. ***************
  293. *** 19,24 ****
  294. --- 19,27 ----
  295.   
  296.   expr.obj: expr.c
  297.       cl /c $(DEFINES) $(MODEL) /Foexpr.obj expr.c
  298. + sort.obj: sort.c
  299. +     cl /c $(DEFINES) $(MODEL) /Fosort.obj sort.c
  300.   
  301.   files.obj: files.c
  302.       cl /c $(DEFINES) $(MODEL) /Fofiles.obj files.c
  303. *** ../patch3/makefile.os2    Tue Feb  2 14:36:47 1993
  304. --- ./makefile.os2    Mon Mar  1 16:55:19 1993
  305. ***************
  306. *** 25,44 ****
  307.   # YOU SHOULDN'T EDIT ANYTHING BELOW HERE.  You may want to change some things
  308.   # in config.h; then, you should be able to type 'make'.
  309.   #-----------------------------------------------------------------------------
  310. ! VERSION= 03.00.03
  311.   
  312. ! HDRS= config.h err.h expr.h globals.h protos.h types.h version.h lang.h
  313.   
  314.   STDHDRS= config.h types.h protos.h globals.h err.h lang.h
  315.   
  316.   SRCS= calendar.c dorem.c dosubst.c expr.c files.c funcs.c globals.c init.c \
  317. ! main.c omit.c token.c trigger.c userfns.c utils.c var.c
  318.   
  319.   MANIFEST= README.UNIX README.DOS COPYRIGHT $(HDRS) $(SRCS) Makefile rem rem.1 \
  320.   remind.1 remind-all.csh remind-all.sh test.rem test-rem test.cmp makefile.tc \
  321.   makefile.msc lnk.msc lnk.tc MANIFEST.UNX MANIFEST.DOS WHATSNEW.30 kall kall.1 \
  322.   defs.rem README.OS2 makefile.os2 rem2ps.c rem2ps.h remind.def rem2ps.1 \
  323. ! lang.h english.h german.h tstlang.rem
  324.   
  325.   OBJS= $(SRCS:.c=$O)
  326.   
  327. --- 25,45 ----
  328.   # YOU SHOULDN'T EDIT ANYTHING BELOW HERE.  You may want to change some things
  329.   # in config.h; then, you should be able to type 'make'.
  330.   #-----------------------------------------------------------------------------
  331. ! VERSION= 03.00.04
  332.   
  333. ! HDRS= config.h err.h expr.h globals.h protos.h types.h version.h \
  334. ! lang.h english.h german.h dutch.h finish.h
  335.   
  336.   STDHDRS= config.h types.h protos.h globals.h err.h lang.h
  337.   
  338.   SRCS= calendar.c dorem.c dosubst.c expr.c files.c funcs.c globals.c init.c \
  339. ! main.c omit.c sort.c token.c trigger.c userfns.c utils.c var.c
  340.   
  341.   MANIFEST= README.UNIX README.DOS COPYRIGHT $(HDRS) $(SRCS) Makefile rem rem.1 \
  342.   remind.1 remind-all.csh remind-all.sh test.rem test-rem test.cmp makefile.tc \
  343.   makefile.msc lnk.msc lnk.tc MANIFEST.UNX MANIFEST.DOS WHATSNEW.30 kall kall.1 \
  344.   defs.rem README.OS2 makefile.os2 rem2ps.c rem2ps.h remind.def rem2ps.1 \
  345. ! tstlang.rem
  346.   
  347.   OBJS= $(SRCS:.c=$O)
  348.   
  349. ***************
  350. *** 75,80 ****
  351. --- 76,82 ----
  352.   init$O: init.c $(STDHDRS) expr.h version.h
  353.   main$O: main.c $(STDHDRS) expr.h
  354.   omit$O: omit.c $(STDHDRS)
  355. + sort$O: sort.c $(STDHDRS)
  356.   token$O: token.c $(STDHDRS)
  357.   trigger$O: trigger.c $(STDHDRS) expr.h
  358.   userfns$O: userfns.c $(STDHDRS) expr.h
  359. *** ../patch3/makefile.tc    Tue Feb  2 14:36:38 1993
  360. --- ./makefile.tc    Mon Mar  1 16:55:05 1993
  361. ***************
  362. *** 1,23 ****
  363.   # Makefile for REMIND for Turbo C for MSDOS
  364.   
  365. ! VERSION= 03.00.03
  366.   
  367. ! HDRS= config.h err.h expr.h globals.h protos.h types.h version.h lang.h
  368.   
  369.   STDHDRS= config.h types.h protos.h globals.h err.h lang.h
  370.   
  371.   SRCS= calendar.c dorem.c dosubst.c expr.c files.c funcs.c globals.c init.c \
  372. ! main.c omit.c token.c trigger.c userfns.c utils.c var.c
  373.   
  374.   OBJS=calendar.obj dorem.obj dosubst.obj expr.obj files.obj funcs.obj \
  375. ! globals.obj init.obj main.obj omit.obj token.obj trigger.obj \
  376.   utils.obj userfns.obj var.obj
  377.   
  378.   MANIFEST= readme.uni readme.dos copyrigh $(HDRS) $(SRCS) makefile rem rem.1 \
  379.   remind.1 remind-a.csh remind-a.sh test.rem test-rem test.cmp makefile.tc \
  380.   makefile.msc lnk.msc lnk.tc manifest.dos manifest.unx whatsnew.30 kall kall.1 \
  381. ! defs.rem readme.os2 makefile.os2 rem2ps.c rem2ps.h remind.def rem2ps.1 \
  382. ! lang.h english.h german.h tstlang.rem
  383.   
  384.   all: remind.exe rem2ps.exe
  385.   
  386. --- 1,24 ----
  387.   # Makefile for REMIND for Turbo C for MSDOS
  388.   
  389. ! VERSION= 03.00.04
  390.   
  391. ! HDRS= config.h err.h expr.h globals.h protos.h types.h version.h \
  392. ! lang.h english.h german.h dutch.h finnish.h
  393.   
  394.   STDHDRS= config.h types.h protos.h globals.h err.h lang.h
  395.   
  396.   SRCS= calendar.c dorem.c dosubst.c expr.c files.c funcs.c globals.c init.c \
  397. ! main.c omit.c sort.c token.c trigger.c userfns.c utils.c var.c
  398.   
  399.   OBJS=calendar.obj dorem.obj dosubst.obj expr.obj files.obj funcs.obj \
  400. ! globals.obj init.obj main.obj omit.obj sort.obj token.obj trigger.obj \
  401.   utils.obj userfns.obj var.obj
  402.   
  403.   MANIFEST= readme.uni readme.dos copyrigh $(HDRS) $(SRCS) makefile rem rem.1 \
  404.   remind.1 remind-a.csh remind-a.sh test.rem test-rem test.cmp makefile.tc \
  405.   makefile.msc lnk.msc lnk.tc manifest.dos manifest.unx whatsnew.30 kall kall.1 \
  406. ! tstlang.rem defs.rem readme.os2 makefile.os2 rem2ps.c rem2ps.h remind.def \
  407. ! rem2ps.1
  408.   
  409.   all: remind.exe rem2ps.exe
  410.   
  411. ***************
  412. *** 51,56 ****
  413. --- 52,59 ----
  414.   main.obj: main.c $(STDHDRS) expr.h
  415.   
  416.   omit.obj: omit.c $(STDHDRS)
  417. + sort.obj: sort.c $(STDHDRS)
  418.   
  419.   token.obj: token.c $(STDHDRS)
  420.   
  421. *** ../patch3/omit.c    Fri Feb  5 14:56:03 1993
  422. --- ./omit.c    Mon Mar  1 12:59:56 1993
  423. ***************
  424. *** 33,40 ****
  425.   static int NumFullOmits, NumPartialOmits;
  426.   
  427.   /* The structure for saving and restoring OMIT contexts */
  428. ! typedef struct _omitcontext {
  429. !    struct _omitcontext *next;
  430.      int numfull, numpart;
  431.      int *fullsave;
  432.      int *partsave;
  433. --- 33,40 ----
  434.   static int NumFullOmits, NumPartialOmits;
  435.   
  436.   /* The structure for saving and restoring OMIT contexts */
  437. ! typedef struct omitcontext {
  438. !    struct omitcontext *next;
  439.      int numfull, numpart;
  440.      int *fullsave;
  441.      int *partsave;
  442. *** ../patch3/protos.h    Fri Jan  8 13:24:46 1993
  443. --- ./protos.h    Tue Mar  2 16:20:16 1993
  444. ***************
  445. *** 33,38 ****
  446. --- 33,39 ----
  447.   int TriggerReminder ARGS ((ParsePtr p, Trigger *t, TimeTrig *tim, int jul));
  448.   int ShouldTriggerReminder ARGS ((Trigger *t, TimeTrig *tim, int jul));
  449.   int DoSubst ARGS ((ParsePtr p, char *out, Trigger *t, TimeTrig *tt, int jul, int mode));
  450. + int DoSubstFromString ARGS ((char *source, char *dest, int jul, int tim));
  451.   int EvalExpr ARGS ((char **e, Value *v));
  452.   int PushValStack ARGS ((Value *val));
  453.   int PopValStack ARGS ((Value *val));
  454. ***************
  455. *** 111,113 ****
  456. --- 112,119 ----
  457.   int DoSatRemind ARGS ((Trigger *trig, TimeTrig *tim, ParsePtr p));
  458.   int ParseNonSpaceChar ARGS ((ParsePtr p, int *err, int peek));
  459.   int HashVal ARGS ((const char *str));
  460. + int DateOK ARGS ((int y, int m, int d));
  461. + Operator *FindFunc ARGS ((char *name, Operator where[], int num));
  462. + int InsertIntoSortBuffer ARGS ((int jul, int tim, char *body, int typ));
  463. + void IssueSortedReminders ARGS ((void));    
  464. + int UserFuncExists ARGS ((char *fn));
  465. *** ../patch3/queue.c    Fri Jan  8 13:24:41 1993
  466. --- ./queue.c    Mon Mar  1 12:58:47 1993
  467. ***************
  468. *** 28,35 ****
  469.   #include "protos.h"
  470.   
  471.   /* List structure for holding queued reminders */
  472. ! typedef struct _queuedrem {
  473. !    struct _queuedrem *next;
  474.      int typ;
  475.      int RunDisabled;
  476.      char *text;
  477. --- 28,35 ----
  478.   #include "protos.h"
  479.   
  480.   /* List structure for holding queued reminders */
  481. ! typedef struct queuedrem {
  482. !    struct queuedrem *next;
  483.      int typ;
  484.      int RunDisabled;
  485.      char *text;
  486. ***************
  487. *** 270,278 ****
  488.      printf("Contents of AT queue:%s", NL);
  489.   
  490.      while (q) {
  491. !       printf("Trigger: %02d:%02d  Activate: %02d:%02d  Rep: %d  Delta: %d%s",
  492. !               q->tt.ttime / 60, q->tt.ttime % 60,
  493. !           q->tt.nexttime / 60, q->tt.nexttime % 60,
  494.                 q->tt.rep, q->tt.delta, NL);
  495.         printf("Text: %s %s%s%s", ((q->typ == MSG_TYPE) ? "MSG" : "RUN"),
  496.                 q->text,
  497. --- 270,278 ----
  498.      printf("Contents of AT queue:%s", NL);
  499.   
  500.      while (q) {
  501. !       printf("Trigger: %02d%c%02d  Activate: %02d%c%02d  Rep: %d  Delta: %d%s",
  502. !               q->tt.ttime / 60, TIMESEP, q->tt.ttime % 60,
  503. !           q->tt.nexttime / 60, TIMESEP, q->tt.nexttime % 60,
  504.                 q->tt.rep, q->tt.delta, NL);
  505.         printf("Text: %s %s%s%s", ((q->typ == MSG_TYPE) ? "MSG" : "RUN"),
  506.                 q->text,
  507. *** ../patch3/rem2ps.1    Fri Jan 29 13:37:01 1993
  508. --- ./rem2ps.1    Mon Feb 15 18:45:11 1993
  509. ***************
  510. *** 24,32 ****
  511.   not use this option, the default encoding is used.
  512.   .TP
  513.   .B \-m media
  514. ! Set the page size.  You must specify the media type, which can be one of the
  515. ! following.  (Sizes are approximate.  For a list of media types, type
  516. ! "rem2ps -m help".)
  517.   .RS
  518.   .TP
  519.   Letter
  520. --- 24,32 ----
  521.   not use this option, the default encoding is used.
  522.   .TP
  523.   .B \-m media
  524. ! Set the page size.  If you use the \-m option, you must specify the
  525. ! media type, which can be one of the
  526. ! following.  (Sizes are approximate.)
  527.   .RS
  528.   .TP
  529.   Letter
  530. ***************
  531. *** 69,75 ****
  532.   10 x 14 in.
  533.   .PP
  534.   Type "rem2ps -m help" for a list of available media.  Note that the media
  535. ! type (and all \fBRem2ps\fR options) are case-sensitive.
  536.   .RE
  537.   .TP
  538.   \fB\-f\fR[\fBshed\fR] \fIfont\fR
  539. --- 69,76 ----
  540.   10 x 14 in.
  541.   .PP
  542.   Type "rem2ps -m help" for a list of available media.  Note that the media
  543. ! type (and all \fBRem2ps\fR options) are case-sensitive.  If you don't use
  544. ! the \fB\-m\fR option, the media defaults to Letter.
  545.   .RE
  546.   .TP
  547.   \fB\-f\fR[\fBshed\fR] \fIfont\fR
  548. *** ../patch3/rem2ps.c    Mon Feb  8 14:31:59 1993
  549. --- ./rem2ps.c    Mon Mar  1 12:59:48 1993
  550. ***************
  551. *** 31,38 ****
  552.   #endif
  553.   #define NEW(type) ((type *) malloc(sizeof(type)))
  554.   
  555. ! typedef struct _CalEntry {
  556. !    struct _CalEntry *next;
  557.      char *entry;
  558.   } CalEntry;
  559.   
  560. --- 31,38 ----
  561.   #endif
  562.   #define NEW(type) ((type *) malloc(sizeof(type)))
  563.   
  564. ! typedef struct calentry {
  565. !    struct calentry *next;
  566.      char *entry;
  567.   } CalEntry;
  568.   
  569. *** ../patch3/remind.1    Tue Feb  2 14:36:32 1993
  570. --- ./remind.1    Fri Mar  5 11:51:37 1993
  571. ***************
  572. *** 37,42 ****
  573. --- 37,43 ----
  574.   empty boxes smaller.  \fISpc\fR specifies how many blank lines to leave
  575.   between the day number and the first reminder entry.  It defaults to 1.
  576.   .RS
  577. + .PP
  578.   Any of \fIcol\fR, \fIpad\fR or \fIspc\fR can be omitted, providing you
  579.   provide the correct number of commas.  Don't use any spaces in the option.
  580.   .RE
  581. ***************
  582. *** 106,111 ****
  583. --- 107,120 ----
  584.   Echo lines when displaying error messages
  585.   .RE
  586.   .TP
  587. + \fB\-g\fR[\fBa|d\fR[\fBa|d\fR]]
  588. + Normally, reminders are issued in the order in which they are encountered
  589. + in the reminder script.  The \fB\-g\fR option cause \fBRemind\fR to
  590. + sort reminders by date and time prior to issuing them.  The optional
  591. + \fBa\fR and \fBd\fR characters specify the sort order (ascending or
  592. + descending) for the date and time fields.  See the section "Sorting
  593. + Reminders" for more information.
  594. + .TP
  595.   \fB\-b\fR[\fIn\fR]
  596.   Set the time format for the calendar and simple-calendar outputs.  \fIN\fR
  597.   can range from 0 to 2, with the default 0.  A value of 0 causes times
  598. ***************
  599. *** 117,122 ****
  600. --- 126,165 ----
  601.   Sets the iteration limit for the \fBSATISFY\fR clause of a \fBREM\fR
  602.   command.  Defaults to 150.
  603.   .TP
  604. + \fB\-k\fR\fIcmd\fR
  605. + Instead of simply printing \fBMSG\fR-type reminders, this causes them to be
  606. + passed to the specific \fIcmd\fR.  You must use '%s' where you want the body to
  607. + appear, and may need to enclose this option in quotes.  Also, because
  608. + \fIcmd\fR is run using the \fBsystem()\fR library function, shell quotes in
  609. + the body of the message may cause problems.  Note that this option
  610. + \fBoverrides\fR the \fB\-r\fR option and the \fBRUN OFF\fR command.
  611. + .PP
  612. + .RS
  613. + As an example, suppose you have an X-Windows program called xmessage, which
  614. + pops up a window and displays its invocation arguments.  You could use:
  615. + .PP
  616. + .nf
  617. +         remind '-kxmessage %s &' ...
  618. + .fi
  619. + .PP
  620. + to have all of your \fBMSG\fR-type reminders processed using xmessage.
  621. + .PP
  622. + A word of warning:  It is very easy to spawn dozens of xmessage processes
  623. + with the above technique.  So be very careful.  Also, the \fIcmd\fR is passed
  624. + as an argument to \fBsprintf()\fR.  If you use formatting directives other
  625. + than %s, or use more than one %s directive, there's a good chance that
  626. + you'll crash \fBRemind\fR.  Finally, because \fIcmd\fR is executed using
  627. + the \fBsystem()\fR library function, shell delimiters in \fBMSG\fR-type
  628. + reminders could cause problems.  \fIIn particular, never run untrusted
  629. + reminders using the \fR\fB\-k\fR\fI option\fR.  A reminder like:
  630. + .PP
  631. + .nf
  632. +     REM msg foo ; rm -Rf .
  633. + .fi
  634. + .PP
  635. + would cause havoc if run with the \fB\-k\fR option.
  636. + .RE
  637. + .TP
  638.   \fB\-z\fR[\fIn\fR]
  639.   Runs \fBRemind\fR in the daemon mode.  If \fIn\fR is supplied, it
  640.   specifies how often (in minutes) \fBRemind\fR should wake up to
  641. ***************
  642. *** 129,135 ****
  643.   The \fB\-u\fR option is available only to root, and cannot be used by normal
  644.   users.  The option changes the uid and gid as described, and sets the
  645.   environment variables HOME, SHELL and USER to the home directory, shell,
  646. ! and user name, respectively, of the specified user.  This option is meant for
  647.   use in shell scripts which mail reminders to all users.
  648.   .PP
  649.   If you supply a \fIdate\fR on the command line, it must consist of
  650. --- 172,179 ----
  651.   The \fB\-u\fR option is available only to root, and cannot be used by normal
  652.   users.  The option changes the uid and gid as described, and sets the
  653.   environment variables HOME, SHELL and USER to the home directory, shell,
  654. ! and user name, respectively, of the specified user.  LOGNAME is also
  655. ! set to the specified user name.  This option is meant for
  656.   use in shell scripts which mail reminders to all users.
  657.   .PP
  658.   If you supply a \fIdate\fR on the command line, it must consist of
  659. ***************
  660. *** 188,193 ****
  661. --- 232,238 ----
  662.   [\fBOMIT\fR \fIomit_list\fR]
  663.   [\fBAT\fR \fItime\fR [\fItdelta\fR] [\fItrepeat\fR]]
  664.   [\fBUNTIL\fR \fIexpiry_date\fR]
  665. + [\fBSCANFROM\fR \fIscan_date\fR]
  666.   \fBMSG\fR | \fBRUN\fR | \fBCAL\fR | \fBSATISFY\fR
  667.   .I body
  668.   .RE
  669. ***************
  670. *** 206,214 ****
  671.   .PP
  672.   The keywords \fBMSG\fR, \fBRUN\fR and \fBCAL\fR denote the \fItype\fR
  673.   of the reminder.  (\fBSATISFY\fR is more complicated and will be explained
  674. ! later.)  A \fBMSG\fR type reminder simply prints a message to the standard
  675.   output, after passing the \fIbody\fR through a special substitution filter,
  676. ! described in the section "The Substitution Filter."  A \fBRUN\fR-type
  677.   reminder also passes the \fIbody\fR through the substitution filter, but
  678.   then executes the result as a system command.  A \fBCAL\fR-type reminder
  679.   is used only to place entries in the calendar produced when \fBRemind\fR
  680. --- 251,264 ----
  681.   .PP
  682.   The keywords \fBMSG\fR, \fBRUN\fR and \fBCAL\fR denote the \fItype\fR
  683.   of the reminder.  (\fBSATISFY\fR is more complicated and will be explained
  684. ! later.)  A \fBMSG\fR-type reminder normally prints a message to the standard
  685.   output, after passing the \fIbody\fR through a special substitution filter,
  686. ! described in the section "The Substitution Filter."  However, if you have
  687. ! used the \fB\-k\fR command-line option, then \fBMSG\fR-type reminders are
  688. ! passed to the appropriate program.  Note that the options \fB\-c\fR,
  689. ! \fB\-s\fR, \fB\-p\fR and \fB\-n\fR disable the \fB\-k\fR option.
  690. ! .PP
  691. ! A \fBRUN\fR-type
  692.   reminder also passes the \fIbody\fR through the substitution filter, but
  693.   then executes the result as a system command.  A \fBCAL\fR-type reminder
  694.   is used only to place entries in the calendar produced when \fBRemind\fR
  695. ***************
  696. *** 491,496 ****
  697. --- 541,553 ----
  698.   period.  Similarly, if you specify a weekday, it is used only to calculate
  699.   the initial date, and does not affect the repetition period.
  700.   .PP
  701. + .B SCANFROM
  702. + .PP
  703. + The \fBSCANFROM\fR keyword is for advanced \fBRemind\fR programmers
  704. + only, and will be explained in the section "Details about Trigger Computation"
  705. + near the end of this manual.  Note that \fBSCANFROM\fR is available only
  706. + in versions of \fBRemind\fR from 03.00.04 up.
  707. + .PP
  708.   .B EXPIRY DATES
  709.   .PP
  710.   Some reminders should be issued periodically for a certain time, but then
  711. ***************
  712. *** 578,583 ****
  713. --- 635,642 ----
  714.   by a \fItime\fR and optional \fItdelta\fR and \fItrepeat\fR.  The \fItime\fR
  715.   must be specified in 24-hour format, with 0:00 representing midnight,
  716.   12:00 representing noon, and 23:59 representing one minute to midnight.
  717. + You can use either a colon or a period to separate the hours from the
  718. + minutes.  That is, 13:39 and 13.39 are equivalent.
  719.   .PP
  720.   \fBRemind\fR treats timed reminders specially.  If the trigger date
  721.   for a timed reminder is the same as the current system date, the
  722. ***************
  723. *** 1043,1049 ****
  724.   .PP
  725.   If you run \fBRemind\fR with the \fB\-r\fR command-line option,
  726.   \fBRUN\fR-type reminders and the \fBshell()\fR function will be disabled,
  727. ! regardless of any \fBRUN\fR commands in the reminder script.
  728.   .PP
  729.   One use of the \fBRUN\fR command is to provide a secure interface
  730.   between \fBRemind\fR and the \fBElm\fR mail system.  The \fBElm\fR
  731. --- 1102,1109 ----
  732.   .PP
  733.   If you run \fBRemind\fR with the \fB\-r\fR command-line option,
  734.   \fBRUN\fR-type reminders and the \fBshell()\fR function will be disabled,
  735. ! regardless of any \fBRUN\fR commands in the reminder script.  However,
  736. ! any command supplied with the \fB\-k\fR option will still be executed.
  737.   .PP
  738.   One use of the \fBRUN\fR command is to provide a secure interface
  739.   between \fBRemind\fR and the \fBElm\fR mail system.  The \fBElm\fR
  740. ***************
  741. *** 1071,1076 ****
  742. --- 1131,1139 ----
  743.       Reminders for Friday, 30th October, 1992 (today):
  744.   .fi
  745.   .PP
  746. + (The banner is not printed if any of the calendar-producing options
  747. + is used, or if the \fB\-k\fR option is used.)
  748. + .PP
  749.   The \fBBANNER\fR command lets you change the format.  It should appear
  750.   before any \fBREM\fR commands.  The format is:
  751.   .PP
  752. ***************
  753. *** 1166,1175 ****
  754.   .RE
  755.   .TP
  756.   .B TIME constants
  757. ! 12:33, 0:01, 14:15, 16:42
  758.   .PP
  759.   .RS
  760. ! Note that \fBTIME\fR constants are written in 24-hour format
  761.   .RE
  762.   .TP
  763.   .B DATE constants
  764. --- 1229,1241 ----
  765.   .RE
  766.   .TP
  767.   .B TIME constants
  768. ! 12:33, 0:01, 14:15, 16:42, 12.16, 13.00, 1.11
  769.   .PP
  770.   .RS
  771. ! Note that \fBTIME\fR constants are written in 24-hour format.  Either the
  772. ! period or colon can be used to separate the minutes from the hours.
  773. ! However, Remind will consistently output times using only one separator
  774. ! character.  (The output separator character is chosen at compile-time.)
  775.   .RE
  776.   .TP
  777.   .B DATE constants
  778. ***************
  779. *** 1389,1394 ****
  780. --- 1455,1468 ----
  781.   function returns 0 if the file can be accessed with the specified \fImode\fR,
  782.   and -1 otherwise.
  783.   .TP
  784. + .B args(s_fname)
  785. + Returns the number of arguments expected by the user-defined function
  786. + \fIfname\fR, or -1 if no such user-defined function exists.  Note that
  787. + this function examines only user-defined functions, not built-in functions.
  788. + Its main use is to determine whether or not a particular user-defined
  789. + function has been defined previously.  The \fBargs()\fR function is
  790. + available only in versions of \fBRemind\fR from 03.00.04 and up.
  791. + .TP
  792.   .B asc(s_string)
  793.   Returns an \fBINT\fR which is the ASCII code of the first character
  794.   in \fIstring\fR.  As a special case, \fBasc("")\fR returns 0.
  795. ***************
  796. *** 1491,1496 ****
  797. --- 1565,1587 ----
  798.   error if it is undefined or not of type \fBSTRING\fR.
  799.   .RE
  800.   .TP
  801. + .B dosubst(s_str [,d_date [,t_time]])
  802. + Returns a \fBSTRING\fR which is the result of passing \fIstr\fR through
  803. + the substitution filter described earlier.  The parameters \fIdate\fR
  804. + and \fItime\fR establish the effective trigger date and time used by the
  805. + substitution filter.  If \fIdate\fR and \fItime\fR are omitted, they
  806. + default to \fBtoday()\fR and \fBnow()\fR.
  807. + .RS
  808. + .PP
  809. + Note that if \fIstr\fR does not end with "%", a newline character will be
  810. + added to the end of the result.  Also, calling \fBdosubst()\fR with a
  811. + \fIdate\fR which is in the past (i.e., if \fIdate\fR < \fBtoday()\fR)
  812. + will produce undefined results.
  813. + .PP
  814. + \fBDosubst()\fR is only available starting from version 03.00.04 of
  815. + \fBRemind\fR.
  816. + .RE
  817. + .TP
  818.   .B filename()
  819.   Returns (as a \fBSTRING\fR) the name of the current file being processed
  820.   by \fBRemind\fR.  Inside included files, returns the name of the
  821. ***************
  822. *** 1516,1521 ****
  823. --- 1607,1613 ----
  824.   string \fIsearch\fR.  The first character of a string is numbered 1.
  825.   If \fItarget\fR does not exist in \fIsearch\fR, then 0 is returned.
  826.   .RS
  827. + .PP
  828.   The optional parameter \fIstart\fR specifies the position in
  829.   \fIsearch\fR at which to start looking for \fItarget\fR.
  830.   .RE
  831. ***************
  832. *** 1579,1584 ****
  833. --- 1671,1677 ----
  834.   Can take from one to three arguments.  If one argument is supplied, returns
  835.   "s" if \fInum\fR is not 1, and "" if \fInum\fR is 1.
  836.   .RS
  837. + .PP
  838.   If two arguments are supplied, returns \fIstr1\fR + "s" if \fInum\fR is
  839.   not 1.  Otherwise, returns \fIstr1\fR.
  840.   .PP
  841. ***************
  842. *** 1622,1628 ****
  843.   calendar entry currently being computed.
  844.   .TP
  845.   .B trigdate()
  846. ! Returns the calculated trigger date of the last \fBREM\fR command.  If used
  847.   in the \fIbody\fR of a \fBREM\fR command, returns that command's trigger date.
  848.   .TP
  849.   .B trigger(d_date)
  850. --- 1715,1722 ----
  851.   calendar entry currently being computed.
  852.   .TP
  853.   .B trigdate()
  854. ! Returns the calculated trigger date of the last \fBREM\fR
  855. ! or \fBIFTRIG\fR command.  If used
  856.   in the \fIbody\fR of a \fBREM\fR command, returns that command's trigger date.
  857.   .TP
  858.   .B trigger(d_date)
  859. ***************
  860. *** 1656,1661 ****
  861. --- 1750,1756 ----
  862.   returns the value of variable XY, if it is defined.  If XY is not defined,
  863.   an error results.
  864.   .RS
  865. + .PP
  866.   However, if you supply a second argument, it is returned if the \fIvarname\fR
  867.   is not defined.  The expression value("XY", 0) will return 0 if XY is not
  868.   defined, and the value of XY if it is defined.
  869. ***************
  870. *** 1663,1669 ****
  871.   .TP
  872.   .B version()
  873.   Returns a string specifying the version of \fBRemind\fR.  For version 
  874. ! 03.00.03, returns "03.00.03".  It is guaranteed that as new versions of
  875.   \fBRemind\fR are released, the value returned by \fBversion()\fR will
  876.   strictly increase, according to the rules for string ordering.
  877.   .TP
  878. --- 1758,1764 ----
  879.   .TP
  880.   .B version()
  881.   Returns a string specifying the version of \fBRemind\fR.  For version 
  882. ! 03.00.04, returns "03.00.04".  It is guaranteed that as new versions of
  883.   \fBRemind\fR are released, the value returned by \fBversion()\fR will
  884.   strictly increase, according to the rules for string ordering.
  885.   .TP
  886. ***************
  887. *** 1725,1730 ****
  888. --- 1820,1826 ----
  889.           ["SET"] a 1
  890.   .fi
  891.   .RS
  892. + .PP
  893.   This restriction is because \fBRemind\fR must be able to unambiguously
  894.   determine the first token of a line for the flow-control commands (to
  895.   be discussed later.)
  896. ***************
  897. *** 1886,1891 ****
  898. --- 1982,1988 ----
  899.   The \fBvalue()\fR function \fIalways\fR accesses the "global" value of a
  900.   variable, even if it has the same name as an argument.  For example:
  901.   .RS
  902. + .PP
  903.   .nf
  904.           fset func(x) value("x")
  905.           set x 1
  906. ***************
  907. *** 1992,1999 ****
  908.   for September, 1992.  Labour Day was on Monday, 7 September, 1992.
  909.   However, when \fBRemind\fR gets around to calculating the trigger
  910.   for Tuesday, 8 September, 1992, the \fBOMIT\fR command will now be
  911. ! ommitting Labour Day for 1993, and the "Mon AFTER" command
  912. ! will not be triggered.
  913.   .PP
  914.   It is probably best to stay away from computing \fBOMIT\fR
  915.   trigger dates unless you keep these pitfalls in mind.
  916. --- 2089,2097 ----
  917.   for September, 1992.  Labour Day was on Monday, 7 September, 1992.
  918.   However, when \fBRemind\fR gets around to calculating the trigger
  919.   for Tuesday, 8 September, 1992, the \fBOMIT\fR command will now be
  920. ! omitting Labour Day for 1993, and the "Mon AFTER" command
  921. ! will not be triggered.  (But see the description of \fBSCANFROM\fR
  922. ! in the section "Details about Trigger Computation.")
  923.   .PP
  924.   It is probably best to stay away from computing \fBOMIT\fR
  925.   trigger dates unless you keep these pitfalls in mind.
  926. ***************
  927. *** 2178,2183 ****
  928. --- 2276,2355 ----
  929.   If you use \fBsh\fR or \fBbash\fR, you may have to use the "nohup" command
  930.   to ensure that the daemon is not killed when you log out.
  931.   .PP
  932. + .SH SORTING REMINDERS
  933. + .PP
  934. + The \fB\-g\fR option causes \fBRemind\fR to sort reminders by
  935. + trigger date and time before issuing them.  Note that reminders are
  936. + still calculated in the order encountered in the script.  However, rather
  937. + than being issued immediately, they are saved in an internal buffer.
  938. + When \fBRemind\fR has finished processing the script, it issues the
  939. + saved reminders in sorted order.  The \fB\-g\fR option can be followed
  940. + by one or two characters, which must be "a" or "d".  The first character
  941. + specifies the sort order by trigger date (ascending or descending) and
  942. + the second specifies the sort order by trigger time.  The default is
  943. + to sort both fields in ascending order.
  944. + .PP
  945. + In ascending order, reminders are issued with the most imminent first.
  946. + Descending order is the reverse.  Reminders are always sorted by
  947. + trigger date, and reminders with the same trigger date are then sorted
  948. + by trigger time.  Non-timed reminders are always issued after timed
  949. + reminders in this mode.
  950. + .PP
  951. + You can define a user-defined function called SORTBANNER which takes one
  952. + \fBDATE\fR-type argument.  In sort mode, the following sequence happens:
  953. + .PP
  954. + If \fBRemind\fR notices that the next reminder to issue has a different
  955. + trigger date from the previous one (or if it is the first one to be
  956. + issued), then SORTBANNER is called with the trigger date as its argument.
  957. + The result is coerced to a string, and passed through the substitution
  958. + filter with the appropriate trigger date.  The result is then displayed.
  959. + .PP
  960. + Here's an example - consider the following fragment:
  961. + .PP
  962. + .nf
  963. +     # Switch off the normal banner
  964. +     BANNER %
  965. +     REM 11 March 1993 ++1 MSG Not so important
  966. +     REM 17 March 1993 ++7 MSG Way in the future
  967. +     REM 10 March 1993 MSG Important Reminder
  968. +     REM 11 March 1993 ++1 MSG Not so important - B
  969. +     FSET sortbanner(x) iif(x == today(), \\
  970. +         "***** THINGS TO DO TODAY *****", \\
  971. +         "----- Things to do %b -----")
  972. + .fi
  973. + .PP
  974. + Running this with the \fB-gaa\fR option on 10 March 1993
  975. + produces the following output:
  976. + .PP
  977. + .nf
  978. +     ***** THINGS TO DO TODAY *****
  979. +     Important Reminder
  980. +     ----- Things to do tomorrow -----
  981. +     Not so important
  982. +     Not so important - B
  983. +     ----- Things to do in 7 days' time -----
  984. +     Way in the future
  985. + .fi
  986. + .PP
  987. + You can use the \fBargs()\fR built-in function to determine whether or
  988. + not SORTBANNER has been defined.  (This could be used, for example, to
  989. + provide a default definition for SORTBANNER in a system-wide file included
  990. + at the end of the user's file.)  Here's an example:
  991. + .PP
  992. + .nd
  993. +     # Create a default sortbanner function if it hasn't already
  994. +     # been defined
  995. +     if args("sortbanner") != 1
  996. +         fset sortbanner(x) "--- Things to do %b ---"
  997. +     endif
  998. + .fi
  999. + .PP
  1000.   .SH FOREIGN LANGUAGE SUPPORT
  1001.   .PP
  1002.   Your version of \fBRemind\fR may have been compiled to support a
  1003. ***************
  1004. *** 2197,2203 ****
  1005.   .PP
  1006.   .B COMMAND ABBREVIATIONS
  1007.   .PP
  1008. ! The following commands can be abbreviated:
  1009.   .TP
  1010.   o
  1011.   \fBREM\fR can be omitted - it is implied if no other valid command
  1012. --- 2369,2375 ----
  1013.   .PP
  1014.   .B COMMAND ABBREVIATIONS
  1015.   .PP
  1016. ! The following tokens can be abbreviated:
  1017.   .TP
  1018.   o
  1019.   \fBREM\fR can be omitted - it is implied if no other valid command
  1020. ***************
  1021. *** 2220,2225 ****
  1022. --- 2392,2400 ----
  1023.   .TP
  1024.   o
  1025.   \fBINCLUDE\fR --> \fBINC\fR
  1026. + .TP
  1027. + o
  1028. + \fBSCANFROM\fR --> \fBSCAN\fR
  1029.   .PP
  1030.   .B NIFTY EXAMPLES
  1031.   .PP
  1032. ***************
  1033. *** 2283,2293 ****
  1034.   multiples of 4.  The second \fBREM\fR command actually issues the
  1035.   reminder.
  1036.   .PP
  1037.   .B DETAILS ABOUT TRIGVALID()
  1038.   .PP
  1039.   The \fBtrigvalid()\fR function returns 1 if \fBRemind\fR could find a trigger
  1040.   date for the previous \fBREM\fR or \fBIFTRIG\fR command.  More specifically,
  1041. ! it returns 1 if \fBRemind\fR finds a date \fInot in the past\fR which
  1042.   satisfies the trigger.  In addition, there is one special case in which
  1043.   \fBtrigvalid()\fR returns 1 and \fBtrigdate()\fR returns a meaningful result:
  1044.   .PP
  1045. --- 2458,2532 ----
  1046.   multiples of 4.  The second \fBREM\fR command actually issues the
  1047.   reminder.
  1048.   .PP
  1049. + .B DETAILS ABOUT TRIGGER COMPUTATION
  1050. + .PP
  1051. + Here is a \fIconceptual\fR description of how triggers are calculated.
  1052. + Note that \fBRemind\fR actually uses a much more efficient procedure,
  1053. + but the results are the same as if the conceptual procedure had been
  1054. + followed.
  1055. + .PP
  1056. + \fBRemind\fR starts from the current date (that is, the value of
  1057. + \fBtoday()\fR) and scans forward, examining each day one at a time
  1058. + until it finds a date which satisfies the trigger, or can prove that
  1059. + no such dates (on or later than \fBtoday()\fR) exist.
  1060. + .PP
  1061. + If \fBRemind\fR is executing a \fBSATISFY\fR-type reminder, it evaluates
  1062. + the expression with \fBtrigdate()\fR set to the date found above.  If
  1063. + the expression evaluates to zero or the null string, \fBRemind\fR continues
  1064. + the scanning procedure described above, starting with the day after the
  1065. + trigger found above.
  1066. + .PP
  1067. + The \fBSCANFROM\fR clause (having a syntax similar to \fBUNTIL\fR)
  1068. + can modify the search strategy used.  In this case, \fBRemind\fR begins the
  1069. + scanning procedure at \fIscan_date\fR, which is the date specified in
  1070. + the \fBSCANFROM\fR clause.  For example:
  1071. + .PP
  1072. + .nf
  1073. +     REM Mon 1 SCANFROM 17 Jan 1992 MSG Foo
  1074. + .fi
  1075. + .PP
  1076. + The example above will always have a trigger date of Monday, 3 February 1992.
  1077. + That is because \fBRemind\fR starts scanning from 17 January 1992, and
  1078. + stops scanning as soon as it hits a date which satisfies "Mon 1."
  1079. + .PP
  1080. + The main use of \fBSCANFROM\fR is in situations where you want to
  1081. + calculate the positions of floating holidays.  Consider the Labour
  1082. + Day example shown much earlier.  Labour Day is the first Monday
  1083. + in September.  It can move over a range of 7 days.  Consider the
  1084. + following sequence:
  1085. + .PP
  1086. + .nf
  1087. +     REM Mon 1 Sept SCANFROM [trigger(today()-7)] SATISFY 1
  1088. +     OMIT [trigger(trigdate())]
  1089. +     REM Mon AFTER MSG Hello
  1090. + .fi
  1091. + .PP
  1092. + The \fBSCANFROM\fR clause makes sure that \fBRemind\fR begins scanning
  1093. + from 7 days before the current date.  This ensures that Labour Day for
  1094. + the current year will continue to be triggered until 7 days after it has
  1095. + occurred.  This allows you to safely use the AFTER keyword as shown.
  1096. + .PP
  1097. + In general, use \fBSCANFROM\fR as shown for safe moveable \fBOMITs\fR.  The
  1098. + amount you should scan back by (7 days in the example above) depends on
  1099. + the number of possible consecutive \fBOMITted\fR days which may occur, and
  1100. + on the range of the moveable holiday.  Generally, a value of 7 is safe.
  1101. + .PP
  1102. + Note that if you use one \fBREM\fR command to calculate a trigger date,
  1103. + perform date calculations (addition or subtraction, for example) and
  1104. + then use the modified date in a subsequent \fBREM\fR command, the results
  1105. + \fImay not be what you intended.\fR  This is because you have circumvented
  1106. + the normal scanning mechanism.  You should try to write \fBREM\fR commands
  1107. + which compute trigger dates that can be used unmodified in subsequent
  1108. + \fBREM\fR commands.  The file "defs.rem" which comes with the \fBRemind\fR
  1109. + distribution contains examples.
  1110. + .PP
  1111.   .B DETAILS ABOUT TRIGVALID()
  1112.   .PP
  1113.   The \fBtrigvalid()\fR function returns 1 if \fBRemind\fR could find a trigger
  1114.   date for the previous \fBREM\fR or \fBIFTRIG\fR command.  More specifically,
  1115. ! it returns 1 if \fBRemind\fR finds a date \fInot before the starting
  1116. ! date of the scanning\fR which
  1117.   satisfies the trigger.  In addition, there is one special case in which
  1118.   \fBtrigvalid()\fR returns 1 and \fBtrigdate()\fR returns a meaningful result:
  1119.   .PP
  1120. ***************
  1121. *** 2294,2300 ****
  1122.   If the \fBREM\fR or \fBIFTRIG\fR command did not contain an \fBUNTIL\fR
  1123.   clause, and contained all of the \fIday\fR, \fImonth\fR and \fIyear\fR
  1124.   components, then \fBRemind\fR will correctly compute a trigger date, even
  1125. ! if it happens to be in the past.  Note that this behaviour is not true for
  1126.   versions of \fBRemind\fR prior to 03.00.01.
  1127.   .SH AUTHOR
  1128.   .PP
  1129. --- 2533,2540 ----
  1130.   If the \fBREM\fR or \fBIFTRIG\fR command did not contain an \fBUNTIL\fR
  1131.   clause, and contained all of the \fIday\fR, \fImonth\fR and \fIyear\fR
  1132.   components, then \fBRemind\fR will correctly compute a trigger date, even
  1133. ! if it happens to be before the start of scanning.
  1134. ! Note that this behaviour is not true for
  1135.   versions of \fBRemind\fR prior to 03.00.01.
  1136.   .SH AUTHOR
  1137.   .PP
  1138. *** ../patch3/test.cmp    Tue Feb  2 14:36:18 1993
  1139. --- ./test.cmp    Wed Mar  3 17:01:52 1993
  1140. ***************
  1141. *** 199,204 ****
  1142. --- 199,226 ----
  1143.   ./test.rem(114): Trig = Monday, 18 February, 1991
  1144.   
  1145.   CLEAR-OMIT-CONTEXT
  1146. + # Test the scanfrom clause
  1147. + REM Fri SATISFY 1
  1148. + ./test.rem(118): Trig = Friday, 22 February, 1991
  1149. + OMIT [trigger(trigdate())]
  1150. + trigdate() => 1991/02/22
  1151. + trigger(1991/02/22) => "22 February 1991"
  1152. + REM Fri after MSG 23 Feb 1991
  1153. + ./test.rem(120): Trig = Saturday, 23 February, 1991
  1154. + CLEAR-OMIT-CONTEXT
  1155. + REM Fri SCANFROM [trigger(today()-7)] SATISFY 1
  1156. + today() => 1991/02/16
  1157. + 1991/02/16 - 7 => 1991/02/09
  1158. + trigger(1991/02/09) => "9 February 1991"
  1159. + ./test.rem(122): Trig = Friday, 15 February, 1991
  1160. + OMIT [trigger(trigdate())]
  1161. + trigdate() => 1991/02/15
  1162. + trigger(1991/02/15) => "15 February 1991"
  1163. + REM Fri after MSG 16 Feb 1991
  1164. + ./test.rem(124): Trig = Saturday, 16 February, 1991
  1165. + 16 Feb 1991
  1166. + CLEAR-OMIT-CONTEXT
  1167.   set a000 abs(1)
  1168.   abs(1) => 1
  1169.   set a001 abs(-1)
  1170. ***************
  1171. *** 221,227 ****
  1172.   coerce("string", 11:44) => "11:44"
  1173.   set a009 coerce("int", "badnews")
  1174.   coerce("int", "badnews") => Can't coerce
  1175. ! ./test.rem(126): Can't coerce
  1176.   set a010 coerce("int", "12")
  1177.   coerce("int", "12") => 12
  1178.   set a011 coerce("int", 11:44)
  1179. --- 243,249 ----
  1180.   coerce("string", 11:44) => "11:44"
  1181.   set a009 coerce("int", "badnews")
  1182.   coerce("int", "badnews") => Can't coerce
  1183. ! ./test.rem(135): Can't coerce
  1184.   set a010 coerce("int", "12")
  1185.   coerce("int", "12") => 12
  1186.   set a011 coerce("int", 11:44)
  1187. ***************
  1188. *** 233,239 ****
  1189.   date(1992, 2, 2) => 1992/02/02
  1190.   set a014 date(1993, 2, 29)
  1191.   date(1993, 2, 29) => Bad date specification
  1192. ! ./test.rem(131): Bad date specification
  1193.   set a015 day(today())
  1194.   today() => 1991/02/16
  1195.   day(1991/02/16) => 16
  1196. --- 255,261 ----
  1197.   date(1992, 2, 2) => 1992/02/02
  1198.   set a014 date(1993, 2, 29)
  1199.   date(1993, 2, 29) => Bad date specification
  1200. ! ./test.rem(140): Bad date specification
  1201.   set a015 day(today())
  1202.   today() => 1991/02/16
  1203.   day(1991/02/16) => 16
  1204. ***************
  1205. *** 328,342 ****
  1206.   set a050 substr(a049, 2)
  1207.   a049 => 21
  1208.   substr(21, 2) => Type mismatch
  1209. ! ./test.rem(169): Type mismatch
  1210.   set a051 substr(a050, 2, 6)
  1211. ! a050 => ./test.rem(170): Undefined variable: a050
  1212.   set a052 time(1+2, 3+4)
  1213.   1 + 2 => 3
  1214.   3 + 4 => 7
  1215.   time(3, 7) => 03:07
  1216.   rem 10 jan 1992 AT 11:22 CAL
  1217. ! ./test.rem(172): Trig = Friday, 10 January, 1992
  1218.   set a053 trigdate()
  1219.   trigdate() => 1992/01/10
  1220.   set a054 trigtime()
  1221. --- 350,364 ----
  1222.   set a050 substr(a049, 2)
  1223.   a049 => 21
  1224.   substr(21, 2) => Type mismatch
  1225. ! ./test.rem(178): Type mismatch
  1226.   set a051 substr(a050, 2, 6)
  1227. ! a050 => ./test.rem(179): Undefined variable: a050
  1228.   set a052 time(1+2, 3+4)
  1229.   1 + 2 => 3
  1230.   3 + 4 => 7
  1231.   time(3, 7) => 03:07
  1232.   rem 10 jan 1992 AT 11:22 CAL
  1233. ! ./test.rem(181): Trig = Friday, 10 January, 1992
  1234.   set a053 trigdate()
  1235.   trigdate() => 1992/01/10
  1236.   set a054 trigtime()
  1237. ***************
  1238. *** 349,355 ****
  1239.   "a05" + "6" => "a056"
  1240.   value("a056") => "SDFJHSDF KSJDFH KJSDFH KSJDFH"
  1241.   set a058 version()
  1242. ! version() => "03.00.03"
  1243.   set a059 wkday(today())
  1244.   today() => 1991/02/16
  1245.   wkday(1991/02/16) => "Saturday"
  1246. --- 371,377 ----
  1247.   "a05" + "6" => "a056"
  1248.   value("a056") => "SDFJHSDF KSJDFH KJSDFH KSJDFH"
  1249.   set a058 version()
  1250. ! version() => "03.00.04"
  1251.   set a059 wkday(today())
  1252.   today() => 1991/02/16
  1253.   wkday(1991/02/16) => "Saturday"
  1254. ***************
  1255. *** 389,401 ****
  1256.   fset g(x,y) max(x,y)
  1257.   fset h(x,y) min(g(x+y, x*y), g(x-y, x/y))
  1258.   set a071 g(1, 2)
  1259. ! UserFN g(1, 2)
  1260.   x => 1
  1261.   y => 2
  1262.   max(1, 2) => 2
  1263.   Leaving UserFN g() => 2
  1264.   set a072 h(2, 3)
  1265. ! UserFN h(2, 3)
  1266.   x => 2
  1267.   y => 3
  1268.   2 + 3 => 5
  1269. --- 411,423 ----
  1270.   fset g(x,y) max(x,y)
  1271.   fset h(x,y) min(g(x+y, x*y), g(x-y, x/y))
  1272.   set a071 g(1, 2)
  1273. ! Entering UserFN g(1, 2)
  1274.   x => 1
  1275.   y => 2
  1276.   max(1, 2) => 2
  1277.   Leaving UserFN g() => 2
  1278.   set a072 h(2, 3)
  1279. ! Entering UserFN h(2, 3)
  1280.   x => 2
  1281.   y => 3
  1282.   2 + 3 => 5
  1283. ***************
  1284. *** 402,408 ****
  1285.   x => 2
  1286.   y => 3
  1287.   2 * 3 => 6
  1288. ! UserFN g(5, 6)
  1289.   x => 5
  1290.   y => 6
  1291.   max(5, 6) => 6
  1292. --- 424,430 ----
  1293.   x => 2
  1294.   y => 3
  1295.   2 * 3 => 6
  1296. ! Entering UserFN g(5, 6)
  1297.   x => 5
  1298.   y => 6
  1299.   max(5, 6) => 6
  1300. ***************
  1301. *** 413,419 ****
  1302.   x => 2
  1303.   y => 3
  1304.   2 / 3 => 0
  1305. ! UserFN g(-1, 0)
  1306.   x => -1
  1307.   y => 0
  1308.   max(-1, 0) => 0
  1309. --- 435,441 ----
  1310.   x => 2
  1311.   y => 3
  1312.   2 / 3 => 0
  1313. ! Entering UserFN g(-1, 0)
  1314.   x => -1
  1315.   y => 0
  1316.   max(-1, 0) => 0
  1317. ***************
  1318. *** 421,427 ****
  1319.   min(6, 0) => 0
  1320.   Leaving UserFN h() => 0
  1321.   set a073 h("foo", 11:33)
  1322. ! UserFN h("foo", 11:33)
  1323.   x => "foo"
  1324.   y => 11:33
  1325.   "foo" + 11:33 => "foo11:33"
  1326. --- 443,449 ----
  1327.   min(6, 0) => 0
  1328.   Leaving UserFN h() => 0
  1329.   set a073 h("foo", 11:33)
  1330. ! Entering UserFN h("foo", 11:33)
  1331.   x => "foo"
  1332.   y => 11:33
  1333.   "foo" + 11:33 => "foo11:33"
  1334. ***************
  1335. *** 428,434 ****
  1336.   x => "foo"
  1337.   y => 11:33
  1338.   "foo" * 11:33 => Type mismatch
  1339. ! ./test.rem(195): Operator '*' Type mismatch
  1340.   Leaving UserFN h() => Type mismatch
  1341.   
  1342.       Variable  Value
  1343. --- 450,456 ----
  1344.   x => "foo"
  1345.   y => 11:33
  1346.   "foo" * 11:33 => Type mismatch
  1347. ! ./test.rem(204): Operator '*' Type mismatch
  1348.   Leaving UserFN h() => Type mismatch
  1349.   
  1350.       Variable  Value
  1351. ***************
  1352. *** 453,459 ****
  1353.           a048  "foo"
  1354.           a067  "INT"
  1355.           a039  "February"
  1356. !         a058  "03.00.03"
  1357.           a049  21
  1358.           a068  "STRING"
  1359.           a059  "Saturday"
  1360. --- 475,481 ----
  1361.           a048  "foo"
  1362.           a067  "INT"
  1363.           a039  "February"
  1364. !         a058  "03.00.04"
  1365.           a049  21
  1366.           a068  "STRING"
  1367.           a059  "Saturday"
  1368. *** ../patch3/test.rem    Wed Dec 16 10:51:50 1992
  1369. --- ./test.rem    Wed Mar  3 17:01:45 1993
  1370. ***************
  1371. *** 114,119 ****
  1372. --- 114,128 ----
  1373.   REM 18 Feb 1991 ++1 MSG 18 Feb 1991 ++1 (17Feb91 omitted)
  1374.   
  1375.   CLEAR-OMIT-CONTEXT
  1376. + # Test the scanfrom clause
  1377. + REM Fri SATISFY 1
  1378. + OMIT [trigger(trigdate())]
  1379. + REM Fri after MSG 23 Feb 1991
  1380. + CLEAR-OMIT-CONTEXT
  1381. + REM Fri SCANFROM [trigger(today()-7)] SATISFY 1
  1382. + OMIT [trigger(trigdate())]
  1383. + REM Fri after MSG 16 Feb 1991
  1384. + CLEAR-OMIT-CONTEXT
  1385.   set a000 abs(1)
  1386.   set a001 abs(-1)
  1387.   set a002 asc("foo")
  1388. *** ../patch3/token.c    Thu Jan 21 16:45:40 1993
  1389. --- ./token.c    Tue Mar  2 12:10:53 1993
  1390. ***************
  1391. *** 75,85 ****
  1392.      { "once",         3,     T_Once,        0 },
  1393.      { "pop-omit-context", 3,    T_Pop,        0 },
  1394.      { "preserve",        8,      T_Preserve,     0 },
  1395. !    { "Push-omit-context", 4,     T_Push,        0 },
  1396.      { "rem",        3,    T_Rem,        0 },
  1397.      { "run",         3,     T_RemType,     RUN_TYPE },
  1398.      { "satisfy",        7,    T_RemType,      SAT_TYPE },
  1399.      { "saturday",    3,    T_WkDay,    5 },
  1400.      { "september",     3,     T_Month,     8 },
  1401.      { "set",        3,    T_Set,        0 },
  1402.      { "skip",         3,     T_Skip,     SKIP_SKIP },
  1403. --- 75,86 ----
  1404.      { "once",         3,     T_Once,        0 },
  1405.      { "pop-omit-context", 3,    T_Pop,        0 },
  1406.      { "preserve",        8,      T_Preserve,     0 },
  1407. !    { "push-omit-context", 4,     T_Push,        0 },
  1408.      { "rem",        3,    T_Rem,        0 },
  1409.      { "run",         3,     T_RemType,     RUN_TYPE },
  1410.      { "satisfy",        7,    T_RemType,      SAT_TYPE },
  1411.      { "saturday",    3,    T_WkDay,    5 },
  1412. +    { "scanfrom",    4,    T_Scanfrom,    0 },
  1413.      { "september",     3,     T_Month,     8 },
  1414.      { "set",        3,    T_Set,        0 },
  1415.      { "skip",         3,     T_Skip,     SKIP_SKIP },
  1416. ***************
  1417. *** 173,178 ****
  1418. --- 174,180 ----
  1419.   #endif
  1420.   {
  1421.      register int top, bot, mid, r;
  1422. +    int l;
  1423.   
  1424.      tok->type = T_Illegal;
  1425.      if (! *s) {
  1426. ***************
  1427. *** 191,196 ****
  1428. --- 193,199 ----
  1429.         return;
  1430.      }
  1431.   
  1432. +    l = strlen(s);
  1433.      bot = 0;
  1434.      top = sizeof(TokArray) / sizeof(TokArray[0]) - 1;
  1435.   
  1436. ***************
  1437. *** 198,207 ****
  1438.         mid = (top + bot) / 2;
  1439.         r = TokStrCmp(&TokArray[mid], s);
  1440.         if (!r) {
  1441. !      tok->type = TokArray[mid].type;
  1442. !      tok->val  = TokArray[mid].val;
  1443. !      return;
  1444. !       }
  1445.         if (r > 0) top = mid-1; else bot=mid+1;
  1446.      }
  1447.   
  1448. --- 201,222 ----
  1449.         mid = (top + bot) / 2;
  1450.         r = TokStrCmp(&TokArray[mid], s);
  1451.         if (!r) {
  1452. !          if (l >= TokArray[mid].MinLen) {
  1453. !            tok->type = TokArray[mid].type;
  1454. !         tok->val  = TokArray[mid].val;
  1455. !         return;
  1456. !          } else {
  1457. !         while (mid && !TokStrCmp(&TokArray[mid-1],s)) mid--;
  1458. !         while (!TokStrCmp(&TokArray[mid], s) && l < TokArray[mid].MinLen)
  1459. !            mid++;
  1460. !         if (!TokStrCmp(&TokArray[mid], s)) {
  1461. !            tok->type = TokArray[mid].type;
  1462. !            tok->val = TokArray[mid].val;
  1463. !            return;
  1464. !         }
  1465. !          }
  1466. !      break;
  1467. !        }
  1468.         if (r > 0) top = mid-1; else bot=mid+1;
  1469.      }
  1470.   
  1471. ***************
  1472. *** 209,215 ****
  1473.      array. */
  1474.   #if LANG != ENGLISH
  1475.       for (r=0; r<(sizeof(NonEnglishToks) / sizeof(Token)); r++) {
  1476. !        if (!TokStrCmp(&NonEnglishToks[r], s)) {
  1477.             tok->type = NonEnglishToks[r].type;
  1478.             tok->val = NonEnglishToks[r].val;
  1479.         return;
  1480. --- 224,231 ----
  1481.      array. */
  1482.   #if LANG != ENGLISH
  1483.       for (r=0; r<(sizeof(NonEnglishToks) / sizeof(Token)); r++) {
  1484. !        if (l >= NonEnglishToks[r].MinLen && 
  1485. !                 !TokStrCmp(&NonEnglishToks[r], s)) {
  1486.             tok->type = NonEnglishToks[r].type;
  1487.             tok->val = NonEnglishToks[r].val;
  1488.         return;
  1489. ***************
  1490. *** 247,254 ****
  1491.      if (isdigit(*s)) {
  1492.         PARSENUM(t->val, s);
  1493.   
  1494. !       /* If we hit a colon, we've probably got a time hr:min */
  1495. !       if (*s == ':') {
  1496.        s++;
  1497.        hour = t->val;
  1498.        PARSENUM(min, s);
  1499. --- 263,270 ----
  1500.      if (isdigit(*s)) {
  1501.         PARSENUM(t->val, s);
  1502.   
  1503. !       /* If we hit a colon or a period, we've probably got a time hr:min */
  1504. !       if (*s == ':' || *s == '.' || *s == TIMESEP) {
  1505.        s++;
  1506.        hour = t->val;
  1507.        PARSENUM(min, s);
  1508. ***************
  1509. *** 312,327 ****
  1510.   #endif
  1511.   {
  1512.      register int r;
  1513. -    register int l=0;
  1514.      char *tk = t->name;
  1515.      while(*tk && *s) {
  1516.         r = UPPER(*tk) - UPPER(*s);
  1517.         tk++;
  1518.         s++;
  1519. -       l++;
  1520.         if (r) return r;
  1521.      }
  1522. -    if (l < t->MinLen) return 1;
  1523.      if (!*s) return 0;
  1524.      return (*tk - *s);
  1525.   }
  1526. --- 328,340 ----
  1527. *** ../patch3/types.h    Fri Jan  8 13:23:23 1993
  1528. --- ./types.h    Mon Mar  1 16:56:54 1993
  1529. ***************
  1530. *** 51,56 ****
  1531. --- 51,57 ----
  1532.      int until;
  1533.      int typ;
  1534.      int once;
  1535. +    int scanfrom;
  1536.   } Trigger;
  1537.   
  1538.   /* A time trigger */
  1539. ***************
  1540. *** 84,90 ****
  1541.   #define NO_UNTIL -1
  1542.   #define NO_ONCE 0
  1543.   #define ONCE_ONCE 1
  1544.   #define NO_SKIP 0
  1545.   #define SKIP_SKIP 1
  1546.   #define BEFORE_SKIP 2
  1547. --- 85,91 ----
  1548.   #define NO_UNTIL -1
  1549.   #define NO_ONCE 0
  1550.   #define ONCE_ONCE 1
  1551. ! #define NO_DATE -1
  1552.   #define NO_SKIP 0
  1553.   #define SKIP_SKIP 1
  1554.   #define BEFORE_SKIP 2
  1555. ***************
  1556. *** 121,127 ****
  1557.     T_Number,
  1558.     T_Clr,
  1559.     T_Debug,
  1560. !   T_Dumpvars
  1561.   };
  1562.   
  1563.   /* The structure of a token */
  1564. --- 122,129 ----
  1565.     T_Number,
  1566.     T_Clr,
  1567.     T_Debug,
  1568. !   T_Dumpvars,
  1569. !   T_Scanfrom
  1570.   };
  1571.   
  1572.   /* The structure of a token */
  1573. ***************
  1574. *** 154,156 ****
  1575. --- 156,162 ----
  1576.   #define SC_AMPM   0   /* Time shown as 3:00am, etc. */
  1577.   #define SC_MIL    1   /* 24-hour time format */
  1578.   #define SC_NOTIME 2   /* Do not display time in SC format. */
  1579. + /* Flags for sorting */
  1580. + #define SORT_ASCEND 1
  1581. + #define SORT_DESCEND 2
  1582. *** ../patch3/userfns.c    Fri Feb  5 14:57:30 1993
  1583. --- ./userfns.c    Tue Mar  2 11:39:01 1993
  1584. ***************
  1585. *** 27,34 ****
  1586.   #define FUNC_HASH_SIZE 32   /* Size of User-defined function hash table */
  1587.   
  1588.   /* Define the data structure used to hold a user-defined function */
  1589. ! typedef struct _udf_struct {
  1590. !    struct _udf_struct *next;
  1591.      char name[VAR_NAME_LEN+1];
  1592.      char *text;
  1593.      Var *locals;
  1594. --- 27,34 ----
  1595.   #define FUNC_HASH_SIZE 32   /* Size of User-defined function hash table */
  1596.   
  1597.   /* Define the data structure used to hold a user-defined function */
  1598. ! typedef struct udf_struct {
  1599. !    struct udf_struct *next;
  1600.      char name[VAR_NAME_LEN+1];
  1601.      char *text;
  1602.      Var *locals;
  1603. ***************
  1604. *** 40,45 ****
  1605. --- 40,49 ----
  1606.   /* The hash table */
  1607.   static UserFunc *FuncHash[FUNC_HASH_SIZE];
  1608.   
  1609. + /* Access to built-in functions */
  1610. + extern int NumFuncs;
  1611. + extern Operator Func[];
  1612.   /* We need access to the expression evaluation stack */
  1613.   extern Value ValStack[];
  1614.   extern int ValStackPtr;
  1615. ***************
  1616. *** 80,85 ****
  1617. --- 84,95 ----
  1618.      func = NEW(UserFunc);
  1619.      if (!func) return E_NO_MEM;
  1620.      StrnCpy(func->name, TokBuffer, VAR_NAME_LEN);
  1621. +    if (!Hush) {
  1622. +       if (FindFunc(TokBuffer, Func, NumFuncs)) {
  1623. +          Eprint("Warning:  Attempt to redefine built-in function '%s'",
  1624. +              TokBuffer);
  1625. +       }
  1626. +    }
  1627.      func->locals = NULL;
  1628.      func->text = NULL;
  1629.      func->IsCached = 1;
  1630. ***************
  1631. *** 252,262 ****
  1632.      /* Search for the function */
  1633.      f = FuncHash[h];
  1634.      while (f && !StrinEq(name, f->name, VAR_NAME_LEN)) f = f->next;
  1635. !    if (!f) return E_UNDEF_FUNC;
  1636.      /* Debugging stuff */
  1637.      if (DebugFlag & DB_PRTEXPR) {
  1638. !       fprintf(ErrFp, "UserFN %s(", f->name);
  1639.         for (i=0; i<nargs; i++) {
  1640.            PrintValue(&ValStack[ValStackPtr - nargs + i], ErrFp);
  1641.            if (i<nargs-1) fprintf(ErrFp, ", ");
  1642. --- 262,274 ----
  1643.      /* Search for the function */
  1644.      f = FuncHash[h];
  1645.      while (f && !StrinEq(name, f->name, VAR_NAME_LEN)) f = f->next;
  1646. !    if (!f) {
  1647. !       Eprint("Undefined function '%s'", name);
  1648. !       return E_UNDEF_FUNC;
  1649. !    }
  1650.      /* Debugging stuff */
  1651.      if (DebugFlag & DB_PRTEXPR) {
  1652. !       fprintf(ErrFp, "Entering UserFN %s(", f->name);
  1653.         for (i=0; i<nargs; i++) {
  1654.            PrintValue(&ValStack[ValStackPtr - nargs + i], ErrFp);
  1655.            if (i<nargs-1) fprintf(ErrFp, ", ");
  1656. ***************
  1657. *** 264,278 ****
  1658.         fprintf(ErrFp, ")\n");
  1659.      }
  1660.      /* Detect illegal recursive call */
  1661. !    if (f->IsActive) return E_RECURSIVE;
  1662.      /* Check number of args */
  1663. !    if (nargs != f->nargs)
  1664.         return (nargs < f->nargs) ? E_2FEW_ARGS : E_2MANY_ARGS;
  1665.      /* Found the function - set up a local variable frame */
  1666.      h = SetUpLocalVars(f);
  1667. !    if (h) return h;
  1668.   
  1669.      /* Evaluate the expression */
  1670.      f->IsActive = 1;
  1671. --- 276,307 ----
  1672.         fprintf(ErrFp, ")\n");
  1673.      }
  1674.      /* Detect illegal recursive call */
  1675. !    if (f->IsActive) {
  1676. !       if (DebugFlag &DB_PRTEXPR) {
  1677. !          fprintf(ErrFp, "Leaving UserFN %s() => ", name);
  1678. !          fprintf(ErrFp, "%s\n", ErrMsg[E_RECURSIVE]);
  1679. !       }
  1680. !       return E_RECURSIVE;
  1681. !    }
  1682. !    
  1683.      /* Check number of args */
  1684. !    if (nargs != f->nargs) {
  1685. !       if (DebugFlag &DB_PRTEXPR) {
  1686. !          fprintf(ErrFp, "Leaving UserFN %s() => ", name);
  1687. !          fprintf(ErrFp, "%s\n",
  1688. !         ErrMsg[(nargs < f->nargs) ? E_2FEW_ARGS : E_2MANY_ARGS]);
  1689. !       }
  1690.         return (nargs < f->nargs) ? E_2FEW_ARGS : E_2MANY_ARGS;
  1691. !    }
  1692.      /* Found the function - set up a local variable frame */
  1693.      h = SetUpLocalVars(f);
  1694. !    if (h) {
  1695. !       if (DebugFlag &DB_PRTEXPR) {
  1696. !          fprintf(ErrFp, "Leaving UserFN %s() => ", name);
  1697. !          fprintf(ErrFp, "%s\n", ErrMsg[h]);
  1698. !       }
  1699. !       return h;
  1700. !    }
  1701.   
  1702.      /* Evaluate the expression */
  1703.      f->IsActive = 1;
  1704. ***************
  1705. *** 343,345 ****
  1706. --- 372,398 ----
  1707.         v = v->next;
  1708.      }
  1709.   }
  1710. + /***************************************************************/
  1711. + /*                                                             */
  1712. + /*  UserFuncExists                                             */
  1713. + /*                                                             */
  1714. + /*  Return the number of arguments accepted by the function if */
  1715. + /*  it is defined, or -1 if it is not defined.                 */
  1716. + /*                                                             */
  1717. + /***************************************************************/
  1718. + #ifdef HAVE_PROTOS
  1719. + PUBLIC int UserFuncExists(char *fn)
  1720. + #else
  1721. + int UserFuncExists(fn)
  1722. + char *fn;
  1723. + #endif
  1724. + {
  1725. +    UserFunc *f;
  1726. +    int h = HashVal(fn) % FUNC_HASH_SIZE;
  1727. +    f = FuncHash[h];
  1728. +    while (f && !StrinEq(fn, f->name, VAR_NAME_LEN)) f = f->next;
  1729. +    if (!f) return -1;
  1730. +    else return f->nargs;
  1731. + }
  1732. *** ../patch3/utils.c    Thu Jan 21 16:33:51 1993
  1733. --- ./utils.c    Thu Feb 18 13:28:38 1993
  1734. ***************
  1735. *** 19,24 ****
  1736. --- 19,25 ----
  1737.   #include <malloc.h>
  1738.   #endif
  1739.   #include <ctype.h>
  1740. + #include "globals.h"
  1741.   
  1742.   #define UPPER(c) (islower(c) ? toupper(c) : c)
  1743.   
  1744. ***************
  1745. *** 218,220 ****
  1746. --- 219,243 ----
  1747.   }
  1748.   #endif
  1749.   
  1750. + /***************************************************************/
  1751. + /*                                                             */
  1752. + /*  DateOK                                                     */
  1753. + /*                                                             */
  1754. + /*  Return 1 if the date is OK, 0 otherwise.                   */
  1755. + /*                                                             */
  1756. + /***************************************************************/
  1757. + #ifdef HAVE_PROTOS
  1758. + PUBLIC int DateOK(int y, int m, int d)
  1759. + #else
  1760. + int DateOK(y, m, d)
  1761. + int y, m, d;
  1762. + #endif
  1763. + {
  1764. +    if (d < 1                 ||
  1765. +        m < 0                 ||
  1766. +        y < BASE              ||
  1767. +        m > 11                ||
  1768. +        y > BASE + YR_RANGE   ||
  1769. +        d > DaysInMonth(m, y) ) return 0;
  1770. +    else return 1;
  1771. + }
  1772.