[0001]
[0002]
[0003]
[0004]
[0005]
[0006]
[0007]
[0008]
[0009]
[0010]
[0011]
[0012]
[0013]
[0014]
[0015]
[0016]
[0017]
[0018]
[0019]
[0020]
[0021]
[0022]
[0023]
[0024]
[0025]
[0026]
[0027]
[0028]
[0029]
[0030]
[0031]
[0032]
[0033]
[0034]
[0035]
[0036]
[0037]
[0038]
[0039]
[0040]
[0041]
[0042]
[0043]
[0044]
[0045]
[0046]
[0047]
[0048]
[0049]
[0050]
[0051]
[0052]
[0053]
[0054]
[0055]
[0056]
[0057]
[0058]
[0059]
[0060]
[0061]
[0062]
[0063]
[0064]
[0065]
[0066]
[0067]
[0068]
[0069]
[0070]
[0071]
[0072]
[0073]
[0074]
[0075]
[0076]
[0077]
[0078]
[0079]
[0080]
[0081]
[0082]
[0083]
[0084]
[0085]
[0086]
[0087]
[0088]
[0089]
[0090]
[0091]
[0092]
[0093]
[0094]
[0095]
[0096]
[0097]
[0098]
[0099]
[0100]
[0101]
[0102]
[0103]
[0104]
[0105]
[0106]
[0107]
[0108]
[0109]
[0110]
[0111]
[0112]
[0113]
[0114]
[0115]
[0116]
[0117]
[0118]
[0119]
[0120]
[0121]
[0122]
[0123]
[0124]
[0125]
[0126]
[0127]
[0128]
[0129]
[0130]
[0131]
[0132]
[0133]
[0134]
[0135]
[0136]
[0137]
[0138]
[0139]
[0140]
[0141]
[0142]
[0143]
[0144]
[0145]
[0146]
[0147]
[0148]
[0149]
[0150]
[0151]
[0152]
[0153]
[0154]
[0155]
[0156]
[0157]
[0158]
[0159]
[0160]
[0161]
[0162]
[0163]
[0164]
[0165]
[0166]
[0167]
[0168]
[0169]
[0170]
[0171]
[0172]
[0173]
[0174]
[0175]
[0176]
[0177]
[0178]
[0179]
[0180]
[0181]
[0182]
[0183]
[0184]
[0185]
[0186]
[0187]
[0188]
[0189]
[0190]
[0191]
[0192]
[0193]
[0194]
[0195]
[0196]
[0197]
[0198]
[0199]
[0200]
[0201]
[0202]
[0203]
[0204]
[0205]
[0206]
[0207]
[0208]
[0209]
[0210]
[0211]
[0212]
[0213]
[0214]
[0215]
[0216]
[0217]
[0218]
[0219]
[0220]
[0221]
[0222]
[0223]
[0224]
[0225]
[0226]
[0227]
[0228]
[0229]
[0230]
[0231]
[0232]
[0233]
[0234]
[0235]
[0236]
[0237]
[0238]
[0239]
[0240]
[0241]
[0242]
[0243]
[0244]
[0245]
[0246]
[0247]
[0248]
[0249]
[0250]
[0251]
[0252]
[0253]
[0254]
[0255]
[0256]
[0257]
[0258]
[0259]
[0260]
[0261]
[0262]
[0263]
[0264]
[0265]
[0266]
[0267]
[0268]
[0269]
[0270]
[0271]
[0272]
[0273]
[0274]
[0275]
[0276]
[0277]
[0278]
[0279]
[0280]
[0281]
[0282]
[0283]
[0284]
[0285]
[0286]
[0287]
[0288]
[0289]
[0290]
[0291]
[0292]
[0293]
[0294]
[0295]
[0296]
[0297]
[0298]
[0299]
[0300]
[0301]
[0302]
[0303]
[0304]
[0305]
[0306]
[0307]
[0308]
[0309]
[0310]
[0311]
[0312]
[0313]
[0314]
[0315]
[0316]
[0317]
[0318]
[0319]
[0320]
[0321]
[0322]
[0323]
[0324]
[0325]
[0326]
[0327]
[0328]
[0329]
[0330]
[0331]
[0332]
[0333]
[0334]
[0335]
[0336]
[0337]
[0338]
[0339]
[0340]
[0341]
[0342]
[0343]
[0344]
[0345]
[0346]
[0347]
[0348]
[0349]
/*****************************************************************************/
/*
                                    test.c

Test a condition returning true or false.


REGEX EVALUATION
----------------
Uses the GNU RX1.5 regular expression package.
Evaluation is done using case-insensitive, EGREP-compatible matching.

A detailed tutorial on regular expression capabilities and usage is well
beyond the scope of this document.  This summary is only to serve as a quick
mnemonic.  VWcms regular expressions support the following set of operators.

  Operator Overview

  Description                    Usage
  -----------                    -----
  Match-self Operator            Ordinary characters
  Match-any-character Operator   .
  Concatenation Operator         Juxtaposition.
  Repetition Operators           *  +  ? {}
  Alternation Operator           |
  List Operators                 [...]  [^...]
  Grouping Operators             (...)
  Back-reference Operator        \digit
  Anchoring Operators            ^  $
  Backslash Operator             Escape meta-character (e.g. \ ^ . $ | [ ()

The following operators are used to match one, or in conjunction with the
repetition operators more, characters of the target string.  These single and
leading characters are reserved meta-characters and must be escaped using a
leading backslash ("\") if required as a literal character in the matching
pattern.

  Matching Operators

  Expression        Purpose
  ----------        -------
  ^                 Match the beginning of the line
  .                 Match any character
  $                 Match the end of the line
  |                 Alternation (or)
  [abc]             Match only a, b or c
  [^abc]            Match anything except a, b and c
  [a-z0-9]          Match any character in the range a to z or 0 to 9

Repetition operators control the extent, or number, of whatever the matching
operators match. These are also reserved meta-characters and must be escaped
using a leading backslash if required as a literal character.

  Repetition Operators

  Expression        Function
  ----------        --------
  *                 Match 0 or more times
  +                 Match 1 or more times
  ?                 Match 1 or zero times
  {n}               Match exactly n times
  {n,}              Match at least n times
  {n,m}             Match at least n but not more than m times

Regular expression equivalents of these common wildcards

  asterisk ('*')           .*
  question mark ('?')      .
  percentage ('%')         .


VERSION HISTORY
---------------
04-JUL-2019  MGD  initial
*/
/*****************************************************************************/

#include "wasdoc.h"
#include "regex.h"
#include <descrip.h>
#include <starlet.h>
#include <unixlib.h>

extern int  dbug;

/*****************************************************************************/
/*
NULL or empty value is false.

If test NULL or empty then positive number value is true, zero/negative false,
and if empty then the fact the value exists is true (otherwise it would have
been false).

If test contains a string then test by matching it (keyword or regex) against
the value.
*/

int testCondition
(
struct wasdoc_st *docptr,
char *value,
char *test
)
{
   /* if the value is empty then false */
   if (!value || !*value) return (0);

   /* if no keyword or regex */
   if (!test || !*test)
   {
      /* if value an integer then negative and zero is false, positive true */
      if (isdigit(*value)) return (atoi(value) > 0);

      /* no keyword/regex then mere existance is considered true */
      return (1);
   }

   return (testMatch (docptr, value, test) != NULL);
}

/*****************************************************************************/
/*
The time: conditional allows document behaviour to change according to the time
of day (or even year), comparing the supplied parameter to the current system
time in one of three forms.

1) A twenty-four hour clock range as 'hhmm-hhmm', for example '1200-1759',
which should be read as "twelve noon to five fifty-nine PM" (i.e.  as a time
range in minutes), where the first is the start time and the second the end
time of a range.  If the current time is within that range (inclusive) the
conditional returns true, otherwise false.  If the range doesn't look correct
false is always returned.

2) A single digit, '1'..'7', representing the day of the week, where 1 is
Monday, 2 is Tuesday .. 7 is Sunday.  Not exactly the historical way of
numbering weekdays (ask any student of Judaism) but it is what
lib$day_of_week() returns :^)

3) The final way (if neither of the above) is as a string match with a VMS
comparision time (i.e. 'yyyy-mmm-dd hh-mm-ss.hh').
*/

int testTime
(
struct wasdoc_st *docptr,
char *param
)
{
   static char  timeString [24];
   static $DESCRIPTOR (timeStringDsc, timeString);
   static $DESCRIPTOR (timeFaoDsc, "!4UL-!2ZL-!2ZL !2ZL:!2ZL:!2ZL.!2ZL\0");

   int  currentMinute,
        dayOfWeek,
        endMinute,
        result,
        startMinute,
        theDay;
   ulong  time64[2];
   ushort  numTime [7];
   char  *cptr, *pptr;

   /*********/
   /* begin */
   /*********/

   sys$gettim (&time64);

   pptr = param;

   if (docptr->insight >= 5) wasDocInsight (docptr, "%s", pptr);

   if (isdigit(pptr[0]) && !isdigit(pptr[1]))
   {
      /* day of week, 1 == Monday, 2 == Tuesday .. 7 == Sunday */
      lib$day_of_week (&time64, &dayOfWeek);
      result = 0;
      for (cptr = pptr; *cptr; cptr++)
      {
         if (!isdigit(*cptr)) continue;
         theDay = *cptr - '0';
         if (theDay == dayOfWeek) result = 1;
      }
      if (docptr->insight >= 5)
         wasDocInsight (docptr, "%s%d %s == %u",
                        result ? "&check;" : "&cross;",
                        result, param, dayOfWeek);
      return (result);
   }

   sys$numtim (numTime, time64);

   if (isdigit(pptr[0]) && isdigit(pptr[1]) &&
       isdigit(pptr[2]) && isdigit(pptr[3]) && pptr[4] == '-' &&
       isdigit(pptr[5]) && isdigit(pptr[6]) &&
       isdigit(pptr[7]) && isdigit(pptr[8]))
   {
      /* time range */
      result = 1;
      startMinute = (((pptr[0]-'0')*10) + (pptr[1]-'0')) * 60;
      if (startMinute > 1380) result = 0;
      startMinute += ((pptr[2]-'0')*10) + (pptr[3]-'0');
      if (startMinute >= 1440) result = 0;
      endMinute = (((pptr[5]-'0')*10) + (pptr[6]-'0')) * 60;
      if (endMinute > 1380) result = 0;
      endMinute += ((pptr[7]-'0')*10) + (pptr[8]-'0');
      if (endMinute >= 1440) result = 0;
      if (result)
      {
         currentMinute = numTime[3] * 60 + numTime[4];
         if (startMinute <= currentMinute && currentMinute <= endMinute)
            result = 1;
         else
            result = 0;
         if (docptr->insight >= 5)
            wasDocInsight (docptr, "%s%d %u <= %u <= %u",
                           result ? "&check;" : "&cross;",
                           result, startMinute, currentMinute, endMinute);
         return (result);
      }
      if (docptr->insight >= 5) wasDocInsight (docptr, "FALSE times?");
      return (result);
   }

   /* comparison time string */
   sys$fao (&timeFaoDsc, NULL, &timeStringDsc,
            numTime[0], numTime[1], numTime[2],
            numTime[3], numTime[4], numTime[5],
            numTime[6]);
   if (*pptr == '^')
      result = (testMatch (docptr, timeString, pptr+1) != NULL);
   else
      result = decc$match_wild (timeString, pptr);
   if (docptr->insight >= 5)
      wasDocInsight (docptr, "%s%d %s \'%s\'",
                     result ? "&check;" : "&cross;",
                     result, timeString, pptr);
   return (result);
}

/*****************************************************************************/
/*
Look for |this| in |that|.  Can be a keyword or a regular expression.
Return a pointer to the character immediately following the match, or a NULL.
*/

char* testMatch
(
struct wasdoc_st *docptr,
char *that,
char *this
)
{
   char  *cptr, *sptr;

   if (dbug>1) fprintf (stdout, "testMatch()\n");

   if (docptr->insight >= 5) wasDocInsight (docptr, "%s %s", this, that);

   if (*this == '^') return (testRegex (docptr, that, this+1));

   while (*that)
   {
      if (tolower(*that) != tolower(*this))
      {
         that++;
         continue;
      }
      cptr = that;
      sptr = this;
      for (;;)
      {
         if (!*cptr || !*sptr) break;
         if (tolower(*cptr) != tolower(*sptr)) break;
         cptr++;
         sptr++;
      }
      if (!*sptr) return (cptr);
      that++;
   }

   return (NULL);
}

/*****************************************************************************/
/*
Search for the supplied regular expression in the pointed-to text.  The search
is case-insensitive.  If a hit occurs return a pointer to the next character
after the hit.  If a hit does not occur return a NULL.
*/

char* testRegex
(
struct wasdoc_st *docptr,
char *that,
char *thisRegEx
)
{
   static int  RegexCflags = REG_EXTENDED | REG_ICASE,
               RegexEflags = 0;

   static regex_t  *rexptr;
   static regex_t  regexp;

   int  retval;
   char  RegExErrMsg [256];
   regmatch_t  pmatch;

   /*********/
   /* begin */
   /*********/

   if (dbug>1) fprintf (stdout, "testRegex()\n");

   if (!that)
   {
      /* initialise */
      if (!rexptr) return (NULL);
      regfree (&regexp);
      rexptr = NULL;
      return (NULL);
   }

   if (!rexptr)
   {
      retval = regcomp (&regexp, thisRegEx, RegexCflags);
      if (retval)
      {
         regerror (retval, &regexp, RegExErrMsg, sizeof(RegExErrMsg));
         if (docptr->insight >= 2) wasDocInsight(docptr, "%s", RegExErrMsg);
         regfree (&regexp);
         return (NULL);
      }
      rexptr = &regexp;
   }

   retval = regexec (rexptr, that, 1, &pmatch, RegexEflags);
   if (retval)
   {
      regfree (&regexp);
      rexptr = NULL;
      return (NULL);
   }

   return (that + pmatch.rm_so);
}

/*****************************************************************************/