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 */ };