diff --git a/Visual Studio Projects/ALTAIR.vcproj b/Visual Studio Projects/ALTAIR.vcproj
index 733cb6f0..867eba94 100644
--- a/Visual Studio Projects/ALTAIR.vcproj
+++ b/Visual Studio Projects/ALTAIR.vcproj
@@ -27,7 +27,7 @@
+
+
@@ -126,8 +134,8 @@
FavorSizeOrSpeed="1"
OmitFramePointers="true"
WholeProgramOptimization="true"
- AdditionalIncludeDirectories="./;../;../VAX/;../pdp11/;"../../windows-build/winpcap/Wpdpack/Include";"../../windows-build/pthreads";"../../windows-build/libSDL/SDL2-2.0.0/include""
- PreprocessorDefinitions="USE_INT64;USE_ADDR64;VM_VAX;VAX_630;USE_SHARED;USE_SIM_VIDEO;HAVE_LIBSDL;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;PTW32_STATIC_LIB;USE_READER_THREAD;SIM_ASYNCH_IO;SIM_NEED_GIT_COMMIT_ID"
+ AdditionalIncludeDirectories="./;../;../VAX/;../pdp11/;"../../windows-build/winpcap/Wpdpack/Include";"../../windows-build/PCRE/include/";"../../windows-build/pthreads";"../../windows-build/libSDL/SDL2-2.0.0/include""
+ PreprocessorDefinitions="USE_INT64;USE_ADDR64;VM_VAX;VAX_630;USE_SHARED;USE_SIM_VIDEO;HAVE_LIBSDL;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;PTW32_STATIC_LIB;USE_READER_THREAD;SIM_ASYNCH_IO;SIM_NEED_GIT_COMMIT_ID;HAVE_PCREPOSIX_H;PCRE_STATIC"
KeepComments="false"
StringPooling="true"
RuntimeLibrary="0"
@@ -150,14 +158,16 @@
#endif
+#define MAX(a,b) (((a) >= (b)) ? (a) : (b))
+
#define EX_D 0 /* deposit */
#define EX_E 1 /* examine */
#define EX_I 2 /* interactive */
@@ -1656,6 +1658,13 @@ r = putenv(envstr);
#endif
return r;
}
+
+static
+int unsetenv(const char *envname)
+{
+setenv(envname, "", 1);
+return 0;
+}
#endif
@@ -2728,7 +2737,7 @@ for (; *ip && (op < oend); ) {
}
else
if ((*ip == '%') &&
- (isalnum(ip[1]) || (ip[1] == '*'))) { /* sub? */
+ (isalnum(ip[1]) || (ip[1] == '*') || (ip[1] == '_'))) { /* sub? */
if ((ip[1] >= '0') && (ip[1] <= ('9'))) { /* %n = sub */
ap = do_arg[ip[1] - '0'];
for (i=0; irules)
return NULL;
-match_buf = (uint8 *)malloc (strlen (match) + 1);
-if (!match_buf)
- return NULL;
-if (SCPE_OK != sim_decode_quoted_string (match, match_buf, &match_size)) {
- free (match_buf);
- return NULL;
- }
for (i=0; isize; i++)
- if ((match_size == exp->rules[i].size) &&
- (0 == memcmp (exp->rules[i].match, match_buf, match_size))) {
- free (match_buf);
+ if (!strcmp (exp->rules[i].match_pattern, match))
return &exp->rules[i];
- }
-free (match_buf);
return NULL;
}
-/* Add an expecct rule */
-
-EXPTAB *sim_exp_new (EXPECT *exp, const char *match)
-{
-uint8 *match_buf;
-uint32 match_size;
-EXPTAB *ep;
-int32 i;
-
-ep = sim_exp_fnd (exp, match);
-if (ep)
- return ep;
-match_buf = (uint8 *)malloc (strlen (match) + 1);
-if (!match_buf)
- return NULL;
-if (SCPE_OK != sim_decode_quoted_string (match, match_buf, &match_size)) {
- free (match_buf);
- return NULL;
- }
-exp->rules = (EXPTAB *) realloc (exp->rules, sizeof (*exp->rules)*(exp->size + 1));
-ep = &exp->rules[exp->size];
-exp->size += 1;
-memset (ep, 0, sizeof(*ep));
-ep->match = match_buf;
-ep->size = match_size;
-ep->match_pattern = (char *)malloc (strlen (match) + 1);
-strcpy (ep->match_pattern, match);
-/* Make sure that the production buffer is large enough to detect a match for all rules */
-for (i=0; isize; i++) {
- if (exp->rules[i].size > exp->buf_size) {
- free (exp->buf);
- exp->buf = (uint8 *)calloc (exp->rules[i].size, sizeof(*exp->buf));
- exp->buf_size = exp->rules[i].size;
- exp->buf_ins = 0;
- }
- }
-return ep;
-}
-
-/* Set a expect rule */
-
-t_stat sim_exp_set (EXPECT *exp, const char *match, int32 cnt, int32 switches, char *act)
-{
-EXPTAB *ep;
-uint8 *match_buf;
-uint32 match_size;
-
-/* Validate the match string */
-match_buf = (uint8 *)malloc (strlen (match) + 1);
-if (!match_buf)
- return SCPE_MEM;
-if (SCPE_OK != sim_decode_quoted_string (match, match_buf, &match_size)) {
- free (match_buf);
- return SCPE_ARG;
- }
-free (match_buf);
-ep = sim_exp_fnd (exp, match); /* present? */
-if (!ep) /* no, allocate */
- ep = sim_exp_new (exp, match);
-if (!ep) /* still no? mem err */
- return SCPE_MEM;
-ep->cnt = cnt; /* set proceed count */
-ep->switches = switches; /* set switches */
-if (ep->act) { /* replace old action? */
- free (ep->act); /* deallocate */
- ep->act = NULL; /* now no action */
- }
-if (act) while (isspace(*act)) ++act; /* skip leading spaces in action string */
-if ((act != NULL) && (*act != 0)) { /* new action? */
- char *newp = (char *) calloc (strlen (act)+1, sizeof (*act)); /* alloc buf */
- if (newp == NULL) /* mem err? */
- return SCPE_MEM;
- strcpy (newp, act); /* copy action */
- ep->act = newp; /* set pointer */
- }
-return SCPE_OK;
-}
-
/* Clear (delete) an expect rule */
t_stat sim_exp_clr_tab (EXPECT *exp, EXPTAB *ep)
@@ -8410,6 +8356,10 @@ if (!ep) /* not there? ok */
free (ep->match); /* deallocate match string */
free (ep->match_pattern); /* deallocate the display format match string */
free (ep->act); /* deallocate action */
+#if defined(USE_REGEX)
+if (ep->switches & EXP_TYP_REGEX)
+ regfree (&ep->regex); /* release compiled regex */
+#endif
for (i=ep-exp->rules; isize; i++) /* shuffle up remaining rules */
exp->rules[i] = exp->rules[i+1];
exp->size -= 1; /* decrement count */
@@ -8442,6 +8392,110 @@ exp->size = 0;
free (exp->buf);
exp->buf = NULL;
exp->buf_size = 0;
+exp->buf_ins = 0;
+return SCPE_OK;
+}
+
+/* Set/Add an expect rule */
+
+t_stat sim_exp_set (EXPECT *exp, const char *match, int32 cnt, int32 switches, char *act)
+{
+EXPTAB *ep;
+uint8 *match_buf;
+uint32 match_size;
+int i;
+
+/* Validate the match string */
+match_buf = (uint8 *)calloc (strlen (match) + 1, 1);
+if (!match_buf)
+ return SCPE_MEM;
+if (switches & EXP_TYP_REGEX) {
+#if !defined (USE_REGEX)
+ free (match_buf);
+ sim_printf ("RegEx support not available\n");
+ return SCPE_ARG|SCPE_NOMESSAGE; /* RegEx not available */
+ }
+#else /* USE_REGEX */
+ int res;
+ regex_t re;
+
+ memset (&re, 0, sizeof(re));
+ memcpy (match_buf, match+1, strlen(match)-2); /* extract string without surrounding quotes */
+ match_buf[strlen(match)-2] = '\0';
+ res = regcomp (&re, (char *)match_buf, REG_EXTENDED);
+ if (res) {
+ size_t err_size = regerror (res, &re, NULL, 0);
+ char *err_buf = calloc (err_size+1, 1);
+
+ regerror (res, &re, err_buf, err_size);
+ sim_printf ("Regular Expression Error: %s\n", err_buf);
+ free (err_buf);
+ free (match_buf);
+ return SCPE_ARG|SCPE_NOMESSAGE;
+ }
+ sim_debug (exp->dbit, exp->dptr, "Expect Regular Expression: \"%s\" has %d sub expressions\n", match_buf, (int)re.re_nsub);
+ regfree (&re);
+ }
+#endif
+else {
+ if (SCPE_OK != sim_decode_quoted_string (match, match_buf, &match_size)) {
+ free (match_buf);
+ return SCPE_ARG;
+ }
+ }
+free (match_buf);
+ep = sim_exp_fnd (exp, match); /* present? */
+if (ep) /* no, allocate */
+ sim_exp_clr_tab (exp, ep); /* clear it */
+exp->rules = (EXPTAB *) realloc (exp->rules, sizeof (*exp->rules)*(exp->size + 1));
+ep = &exp->rules[exp->size];
+exp->size += 1;
+memset (ep, 0, sizeof(*ep));
+ep->match_pattern = (char *)malloc (strlen (match) + 1);
+strcpy (ep->match_pattern, match);
+ep->cnt = cnt; /* set proceed count */
+ep->switches = switches; /* set switches */
+match_buf = (uint8 *)calloc (strlen (match) + 1, 1);
+if (!match_buf) {
+ sim_exp_clr_tab (exp, ep); /* clear it */
+ return SCPE_MEM;
+ }
+if (switches & EXP_TYP_REGEX) {
+#if defined(USE_REGEX)
+ memcpy (match_buf, match+1, strlen(match)-2); /* extract string without surrounding quotes */
+ match_buf[strlen(match)-2] = '\0';
+ regcomp (&ep->regex, (char *)match_buf, REG_EXTENDED);
+#endif
+ free (match_buf);
+ match_buf = NULL;
+ }
+else {
+ sim_decode_quoted_string (match, match_buf, &match_size);
+ ep->match = match_buf;
+ ep->size = match_size;
+ }
+ep->match_pattern = (char *)malloc (strlen (match) + 1);
+strcpy (ep->match_pattern, match);
+if (ep->act) { /* replace old action? */
+ free (ep->act); /* deallocate */
+ ep->act = NULL; /* now no action */
+ }
+if (act) while (isspace(*act)) ++act; /* skip leading spaces in action string */
+if ((act != NULL) && (*act != 0)) { /* new action? */
+ char *newp = (char *) calloc (strlen (act)+1, sizeof (*act)); /* alloc buf */
+ if (newp == NULL) /* mem err? */
+ return SCPE_MEM;
+ strcpy (newp, act); /* copy action */
+ ep->act = newp; /* set pointer */
+ }
+/* Make sure that the production buffer is large enough to detect a match for all rules */
+for (i=0; isize; i++) {
+ uint32 compare_size = (exp->rules[i].switches & EXP_TYP_REGEX) ? MAX(10 * strlen(ep->match_pattern), 1024) : exp->rules[i].size;
+ if (compare_size > exp->buf_size) {
+ exp->buf = (uint8 *)realloc (exp->buf, compare_size*sizeof(*exp->buf));
+ exp->buf_size = compare_size;
+ }
+ }
return SCPE_OK;
}
@@ -8451,8 +8505,14 @@ t_stat sim_exp_show_tab (FILE *st, EXPECT *exp, EXPTAB *ep)
{
if (!ep)
return SCPE_OK;
-fprintf (st, "EXPECT ");
-fprint_buffer_string (st, ep->match, ep->size);
+fprintf (st, "EXPECT");
+if (ep->switches & EXP_TYP_PERSIST)
+ fprintf (st, " -p");
+if (ep->switches & EXP_TYP_CLEARALL)
+ fprintf (st, " -c");
+if (ep->switches & EXP_TYP_REGEX)
+ fprintf (st, " -r");
+fprintf (st, " %s", ep->match_pattern);
if (ep->cnt > 0)
fprintf (st, " [%d]", ep->cnt);
if (ep->act)
@@ -8465,6 +8525,10 @@ t_stat sim_exp_show (FILE *st, EXPECT *exp, const char *match)
{
EXPTAB *ep = sim_exp_fnd (exp, match);
+if (exp->buf_size) {
+ fprintf (st, "Match Buffer Size: %d\n", exp->buf_size);
+ fprintf (st, "Buffer Insert Offset: %d\n", exp->buf_ins);
+ }
if (!*match)
return sim_exp_showall (st, exp);
if (!ep)
@@ -8489,6 +8553,7 @@ t_stat sim_exp_check (EXPECT *exp, uint8 data)
{
int32 i;
EXPTAB *ep;
+int regex_checks = 0;
if ((!exp) || (!exp->rules)) /* Anying to check? */
return SCPE_OK;
@@ -8497,23 +8562,72 @@ exp->buf[exp->buf_ins++] = data; /* Save new data */
for (i=0; i < exp->size; i++) {
ep = &exp->rules[i];
- if (exp->buf_ins < ep->size) {
- if (memcmp (exp->buf, &ep->match[ep->size-exp->buf_ins], exp->buf_ins))
- continue;
- if (memcmp (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->match, ep->size-exp->buf_ins))
- continue;
- break;
+ if (ep->switches & EXP_TYP_REGEX) {
+#if defined (USE_REGEX)
+ regmatch_t *matches;
+ static size_t sim_exp_match_sub_count = 0;
+
+ ++regex_checks;
+ matches = calloc ((ep->regex.re_nsub + 1), sizeof(*matches));
+ exp->buf[exp->buf_ins] = '\0';
+ if (!regexec (&ep->regex, (char *)exp->buf, ep->regex.re_nsub + 1, matches, 0)) {
+ size_t j;
+ char *buf = malloc (1 + exp->buf_ins);
+
+
+ for (j=0; jregex.re_nsub + 1; j++) {
+ char env_name[32];
+
+ sprintf (env_name, "_EXPECT_MATCH_GROUP_%d", (int)j);
+ memcpy (buf, &exp->buf[matches[j].rm_so], matches[j].rm_eo-matches[j].rm_so);
+ buf[matches[j].rm_eo-matches[j].rm_so] = '\0';
+ setenv (env_name, buf, 1); /* Make the match and substrings available as environment variables */
+ sim_debug (exp->dbit, exp->dptr, "%s=%s\n", env_name, buf);
+ }
+ for (; jregex.re_nsub;
+ free (matches);
+ free (buf);
+ break;
+ }
+ free (matches);
+#endif
}
else {
- if (memcmp (&exp->buf[exp->buf_ins-ep->size], ep->match, ep->size))
- continue;
- break;
+ if (exp->buf_ins < ep->size) {
+ if (memcmp (exp->buf, &ep->match[ep->size-exp->buf_ins], exp->buf_ins))
+ continue;
+ if (memcmp (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->match, ep->size-exp->buf_ins))
+ continue;
+ break;
+ }
+ else {
+ if (memcmp (&exp->buf[exp->buf_ins-ep->size], ep->match, ep->size))
+ continue;
+ break;
+ }
}
}
-if (exp->buf_ins == exp->buf_size) /* At end of match buffer? */
- exp->buf_ins = 0; /* wrap around to beginning */
+if (exp->buf_ins == exp->buf_size) { /* At end of match buffer? */
+ if (regex_checks) {
+ /* When processing regular expressions, let the match buffer fill
+ up and then shuffle the buffer contents down by half the buffer size
+ so that the regular expression has a single contiguous buffer to
+ match against instead of the wrapping buffer which is used otherwise */
+ memmove (exp->buf, &exp->buf[exp->buf_size/2], exp->buf_size-(exp->buf_size/2));
+ exp->buf_ins -= exp->buf_size/2;
+ }
+ else
+ exp->buf_ins = 0; /* wrap around to beginning */
+ }
if (i != exp->size) { /* Found? */
sim_debug (exp->dbit, exp->dptr, "Matched expect pattern: %s\n", ep->match_pattern);
+ setenv ("_EXPECT_MATCH_PATTERN", ep->match_pattern, 1); /* Make the match detail available as an environment variable */
if (ep->cnt > 0) {
ep->cnt -= 1;
sim_debug (exp->dbit, exp->dptr, "Waiting for %d more match%s before stopping\n",
diff --git a/sim_defs.h b/sim_defs.h
index d6911e3a..6fb37876 100644
--- a/sim_defs.h
+++ b/sim_defs.h
@@ -121,6 +121,17 @@
#include
#endif
+#ifdef USE_REGEX
+#undef USE_REGEX
+#endif
+#if defined(HAVE_PCREPOSIX_H)
+#include
+#define USE_REGEX 1
+#elif defined(HAVE_REGEX_H)
+#include
+#define USE_REGEX 1
+#endif
+
/* avoid macro names collisions */
#ifdef MAX
#undef MAX
@@ -643,6 +654,10 @@ struct sim_exptab {
int32 switches; /* flags */
#define EXP_TYP_PERSIST (SWMASK ('P')) /* rule persists after match, default is once a rule matches, it is removed */
#define EXP_TYP_CLEARALL (SWMASK ('C')) /* clear all rules after matching this rule, default is to once a rule matches, it is removed */
+#define EXP_TYP_REGEX (SWMASK ('R')) /* rule pattern is a regular expression */
+#if defined(USE_REGEX)
+ regex_t regex; /* compiled regular expression */
+#endif
char *act; /* action string */
};