/*****************************************************************************/ /* 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 #include #include 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 ? "✓" : "✗", 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 ? "✓" : "✗", 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 ? "✓" : "✗", 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 (®exp); rexptr = NULL; return (NULL); } if (!rexptr) { retval = regcomp (®exp, thisRegEx, RegexCflags); if (retval) { regerror (retval, ®exp, RegExErrMsg, sizeof(RegExErrMsg)); if (docptr->insight >= 2) wasDocInsight(docptr, "%s", RegExErrMsg); regfree (®exp); return (NULL); } rexptr = ®exp; } retval = regexec (rexptr, that, 1, &pmatch, RegexEflags); if (retval) { regfree (®exp); rexptr = NULL; return (NULL); } return (that + pmatch.rm_so); } /*****************************************************************************/