From 932d16da53d891a287ce3b3b93c9d5d1c7345a36 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Mon, 16 Oct 2017 13:10:10 -0700 Subject: [PATCH] SCP: Rework SEND and EXPECT commands to provide default and temporary arguments - EXPECT HALTAFTER=nnn --> sets "nnn" as the global/default value - EXPECT "some string" --> uses the "nnn" global/default value - EXPECT HALTAFTER=ttt "some string" --> uses the "ttt" temporary value for this EXPECT only and: - SEND AFTER=nnn --> sets "nnn" as the global/default value - SEND "some string" --> uses the "nnn" global/default value - SEND AFTER=ttt "some string" --> uses the "ttt" temporary value for this SEND only (and similarly for the SEND DELAY value). --- scp.c | 186 ++++++++++++++++++++++++++++++++++---------------- sim_console.c | 14 ++-- sim_defs.h | 2 +- sim_tmxr.c | 38 ++++++++++- sim_tmxr.h | 2 + 5 files changed, 177 insertions(+), 65 deletions(-) diff --git a/scp.c b/scp.c index 979f00ad..ff83a976 100644 --- a/scp.c +++ b/scp.c @@ -1621,24 +1621,29 @@ ASSERT failure have several different actions: " must match. Data in the string may contain escaped character strings.\n\n" " The SEND command can also insert input into any serial device on a\n" " simulated system as if it was entered by a user.\n\n" - "++SEND {-t} :line {after=nn,}{delay=nn,}\"\"\n\n" + "++SEND {-t} {:line} {after=nn,}{delay=nn,}\"\"\n" + "++NOSEND {:line}\n\n" + " The NOSEND command removes any undelivered input data which may be\n" + " pending on a line.\n" "4Delay\n" - " Specifies a positive integer representing a minimal instruction delay\n" - " between characters being sent. The value specified in a delay\n" - " argument persists across SEND commands to the same device (console or\n" - " serial device). The delay parameter can be set by itself with:\n\n" + " Specifies an integer (>=0) representing a minimal instruction delay\n" + " between characters being sent. The delay parameter can be set by\n" + " itself with:\n\n" "++SEND DELAY=n\n\n" - " The default value of the delay parameter is 1000.\n" + " which will set the default delay value for subsequent SEND commands\n" + " which don't specify an explicit DELAY parameter along with a string\n" + " If a SEND command is processed and no DELAY value has been specified,\n" + " the default value of the delay parameter is 1000.\n" /***************** 80 character line width template *************************/ "4After\n" - " Specifies a positive integer representing a minimal number of instructions\n" + " Specifies an integer (>=0) representing a minimal number of instructions\n" " which must execute before the first character in the string is sent.\n" - " The value specified as the after parameter persists across SEND commands\n" - " to the same device (console or serial device). The after parameter value\n" - " can be set by itself with:\n\n" + " The after parameter value can be set by itself with:\n\n" "++SEND AFTER=n\n\n" - " If the after parameter isn't explicitly set, it defaults to the value of\n" - " the delay parameter.\n" + " which will set the default after value for subsequent SEND commands\n" + " which don't specify an explicit AFTER parameter along with a string\n" + " If a SEND command is processed and no AFTER value has been specified,\n" + " the default value of the delay parameter is the DELAY parameter value.\n" "4Escaping String Data\n" " The following character escapes are explicitly supported:\n" "++\\r Sends the ASCII Carriage Return character (Decimal value 13)\n" @@ -1757,10 +1762,12 @@ ASSERT failure have several different actions: " Specifies the number of instructions which should be executed before\n" " simulator instruction execution should stop. The default is to stop\n" " executing instructions immediately (i.e. HALTAFTER=0).\n" - " The HaltAfter delay, once set, persists for all expect behaviors for\n" - " that device.\n" - " The HaltAfter parameter value can be set by itself with:\n\n" + " The default HaltAfter delay, once set, persists for all expect behaviors\n" + " for that device.\n" + " The default HaltAfter parameter value can be set by itself with:\n\n" "++EXPECT HALTAFTER=n\n\n" + " A unique HaltAfter value can be specified with each expect matching rule\n" + " which if it is not specified then the default value will be used.\n" " To avoid potentially unpredictable system hehavior that will happen\n" " if multiple expect rules are in effect and a haltafter value is large\n" " enough for more than one expect rule to match before an earlier haltafter\n" @@ -1955,7 +1962,8 @@ static CTAB cmd_table[] = { { "IGNORE", &noop_cmd, 0, HLP_IGNORE }, { "ECHO", &echo_cmd, 0, HLP_ECHO }, { "ASSERT", &assert_cmd, 1, HLP_ASSERT }, - { "SEND", &send_cmd, 0, HLP_SEND }, + { "SEND", &send_cmd, 1, HLP_SEND }, + { "NOSEND", &send_cmd, 0, HLP_SEND }, { "EXPECT", &expect_cmd, 1, HLP_EXPECT }, { "NOEXPECT", &expect_cmd, 0, HLP_EXPECT }, { "!", &spawn_cmd, 0, HLP_SPAWN }, @@ -3717,14 +3725,20 @@ return SCPE_OK; Syntax: SEND {After=m},{Delay=n},"string-to-send" - After - is a positive integer representing a number of instruction delay - before the initial characters is sent. The value specified - in a after argument persists across SEND commands. The after - parameter can be set by itself with SEND AFTER=n - Delay - is a positive integer representing a minimal instruction delay - before and between characters being sent. The value specified - in a delay argument persists across SEND commands. The delay - parameter can be set by itself with SEND DELAY=n + After - is an integer (>= 0) representing a number of instruction + delay before the initial characters is sent. The after + parameter can is set by itself (with SEND AFTER=n). + The value specified then persists across SEND commands, + and is the default value used in subsequent SEND commands + which don't specify an explicit AFTER parameter. This default + value is visible externally via an environment variable. + Delay - is an integer (>= 0) representing a number of instruction + delay before and between characters being sent. The + delay parameter can is set by itself (with SEND DELAY=n) + The value specified persists across SEND commands, and is + the default value used in subsequent SEND commands which + don't specify an explicit DELAY parameter. This default + value is visible externally via an environment variable. String - must be quoted. Quotes may be either single or double but the opening anc closing quote characters must match. Within quotes C style character escapes are allowed. @@ -3746,14 +3760,53 @@ return SCPE_OK; \xh{h} where each h is a hex digit (0-9A-Fa-f) */ +static uint32 get_default_env_parameter (const char *dev_name, const char *param_name, uint32 default_value) +{ +char varname[CBUFSIZE]; +uint32 val; +char *endptr; +const char *colon = strchr (dev_name, ':'); + +if (colon) + snprintf (varname, sizeof(varname), "%s_%*.*s_%s", param_name, (int)(colon-dev_name), (int)(colon-dev_name), dev_name, colon + 1); +else + snprintf (varname, sizeof(varname), "%s_%s", param_name, dev_name); +if (!getenv (varname)) + val = default_value; +else { + val = strtoul (getenv (varname), &endptr, 0); + if (*endptr) + val = default_value; + } +return val; +} + +static void set_default_env_parameter (const char *dev_name, const char *param_name, uint32 value) +{ +char varname[CBUFSIZE]; +char valbuf[CBUFSIZE]; + +const char *colon = strchr (dev_name, ':'); + +if (colon) + snprintf (varname, sizeof(varname), "%s_%*.*s_%s", param_name, (int)(colon-dev_name), (int)(colon-dev_name), dev_name, colon + 1); +else + snprintf (varname, sizeof(varname), "%s_%s", param_name, dev_name); +snprintf (valbuf, sizeof(valbuf), "%u", value); +setenv(varname, valbuf, 1); +} + t_stat send_cmd (int32 flag, CONST char *cptr) { char gbuf[CBUFSIZE]; CONST char *tptr; uint8 dbuf[CBUFSIZE]; uint32 dsize = 0; -uint32 delay = 0; -uint32 after = 0; +const char *dev_name; +uint32 delay; +t_bool delay_set = FALSE; +uint32 after; +t_bool after_set = FALSE; t_stat r; SEND *snd; @@ -3768,7 +3821,11 @@ if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) { } else snd = sim_cons_get_send (); - +dev_name = tmxr_send_line_name (snd); +if (!flag) + return sim_send_clear (snd); +delay = get_default_env_parameter (dev_name, "SIM_SEND_DELAY", SEND_DEFAULT_DELAY); +after = get_default_env_parameter (dev_name, "SIM_SEND_AFTER", delay); while (*cptr) { if ((!strncmp(gbuf, "DELAY=", 6)) && (gbuf[6])) { delay = (uint32)get_uint (&gbuf[6], 10, 10000000, &r); @@ -3776,6 +3833,9 @@ while (*cptr) { return sim_messagef (SCPE_ARG, "Invalid Delay Value\n"); cptr = tptr; tptr = get_glyph (cptr, gbuf, ','); + delay_set = TRUE; + if (!after_set) + after = delay; continue; } if ((!strncmp(gbuf, "AFTER=", 6)) && (gbuf[6])) { @@ -3784,24 +3844,28 @@ while (*cptr) { return sim_messagef (SCPE_ARG, "Invalid After Value\n"); cptr = tptr; tptr = get_glyph (cptr, gbuf, ','); + after_set = TRUE; continue; } if ((*cptr == '"') || (*cptr == '\'')) break; return SCPE_ARG; } -if (*cptr) { - if ((*cptr != '"') && (*cptr != '\'')) - return sim_messagef (SCPE_ARG, "String must be quote delimited\n"); - cptr = get_glyph_quoted (cptr, gbuf, 0); - if (*cptr != '\0') - return SCPE_2MARG; /* No more arguments */ - - if (SCPE_OK != sim_decode_quoted_string (gbuf, dbuf, &dsize)) - return sim_messagef (SCPE_ARG, "Invalid String\n"); +if (!*cptr) { + if ((!delay_set) && (!after_set)) + return SCPE_2FARG; + set_default_env_parameter (dev_name, "SIM_SEND_DELAY", delay); + set_default_env_parameter (dev_name, "SIM_SEND_AFTER", after); + return SCPE_OK; } -if ((dsize == 0) && (delay == 0) && (after == 0)) - return SCPE_2FARG; +if ((*cptr != '"') && (*cptr != '\'')) + return sim_messagef (SCPE_ARG, "String must be quote delimited\n"); +cptr = get_glyph_quoted (cptr, gbuf, 0); +if (*cptr != '\0') + return SCPE_2MARG; /* No more arguments */ + +if (SCPE_OK != sim_decode_quoted_string (gbuf, dbuf, &dsize)) + return sim_messagef (SCPE_ARG, "Invalid String\n"); return sim_send_input (snd, dbuf, dsize, after, delay); } @@ -10414,13 +10478,16 @@ t_stat sim_set_expect (EXPECT *exp, CONST char *cptr) char gbuf[CBUFSIZE]; CONST char *tptr; CONST char *c1ptr; +const char *dev_name; +uint32 after; t_bool after_set = FALSE; -uint32 after = exp->after; int32 cnt = 0; t_stat r; if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; +dev_name = tmxr_expect_line_name (exp); +after = get_default_env_parameter (dev_name, "SIM_EXPECT_HALTAFTER", 0); if (*cptr == '[') { cnt = (int32) strtotv (cptr + 1, &c1ptr, 10); if ((cptr == c1ptr) || (*c1ptr != ']')) @@ -10434,14 +10501,20 @@ if ((!strncmp(gbuf, "HALTAFTER=", 10)) && (gbuf[10])) { after = (uint32)get_uint (&gbuf[10], 10, 100000000, &r); if (r != SCPE_OK) return sim_messagef (SCPE_ARG, "Invalid Halt After Value\n"); - after_set = TRUE; cptr = tptr; + after_set = TRUE; } if ((*cptr != '\0') && (*cptr != '"') && (*cptr != '\'')) return sim_messagef (SCPE_ARG, "String must be quote delimited\n"); cptr = get_glyph_quoted (cptr, gbuf, 0); -return sim_exp_set (exp, gbuf, cnt, (after_set ? after : exp->after), sim_switches, cptr); +/* Hsndle a bare HALTAFTER=nnn command */ +if ((gbuf[0] == '\0') && (*cptr == '\0') && after_set) { + set_default_env_parameter (dev_name, "SIM_EXPECT_HALTAFTER", after); + return SCPE_OK; + } + +return sim_exp_set (exp, gbuf, cnt, after, sim_switches, cptr); } /* Clear expect */ @@ -10540,11 +10613,6 @@ uint8 *match_buf; uint32 match_size; int i; -/* Hsndle a bare HALTAFTER=nnn command */ -if ((*match == '\0') && (*act == '\0') && after) { - exp->after = after; - return SCPE_OK; - } /* Validate the match string */ match_buf = (uint8 *)calloc (strlen (match) + 1, 1); if (!match_buf) @@ -10598,8 +10666,8 @@ if (after && exp->size) exp->rules = (EXPTAB *) realloc (exp->rules, sizeof (*exp->rules)*(exp->size + 1)); ep = &exp->rules[exp->size]; exp->size += 1; -exp->after = after; /* set halt after value */ memset (ep, 0, sizeof(*ep)); +ep->after = after; /* set halt after value */ ep->match_pattern = (char *)malloc (strlen (match) + 1); if (ep->match_pattern) strcpy (ep->match_pattern, match); @@ -10660,6 +10728,9 @@ return SCPE_OK; t_stat sim_exp_show_tab (FILE *st, const EXPECT *exp, const EXPTAB *ep) { +const char *dev_name = dev_name = tmxr_expect_line_name (exp); +uint32 default_haltafter = get_default_env_parameter (dev_name, "SIM_EXPECT_HALTAFTER", 0); + if (!ep) return SCPE_OK; fprintf (st, " EXPECT"); @@ -10671,6 +10742,8 @@ if (ep->switches & EXP_TYP_REGEX) fprintf (st, " -r"); if (ep->switches & EXP_TYP_REGEX_I) fprintf (st, " -i"); +if (ep->after != default_haltafter) + fprintf (st, " HALTAFTER=%d", (int)ep->after); fprintf (st, " %s", ep->match_pattern); if (ep->cnt > 0) fprintf (st, " [%d]", ep->cnt); @@ -10683,6 +10756,8 @@ return SCPE_OK; t_stat sim_exp_show (FILE *st, CONST EXPECT *exp, const char *match) { CONST EXPTAB *ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 0); +const char *dev_name = dev_name = tmxr_expect_line_name (exp); +uint32 default_haltafter = get_default_env_parameter (dev_name, "SIM_EXPECT_HALTAFTER", 0); if (exp->buf_size) { char *bstr = sim_encode_quoted_string (exp->buf, exp->buf_ins); @@ -10690,10 +10765,10 @@ if (exp->buf_size) { fprintf (st, " Match Buffer Size: %d\n", exp->buf_size); fprintf (st, " Buffer Insert Offset: %d\n", exp->buf_ins); fprintf (st, " Buffer Contents: %s\n", bstr); + if (default_haltafter) + fprintf (st, " Default HaltAfter: %u instructions\n", (unsigned)default_haltafter); free (bstr); } -if (exp->after) - fprintf (st, " Halt After: %d instructions\n", exp->after); if (exp->dptr && (exp->dbit & exp->dptr->dctrl)) fprintf (st, " Expect Debugging via: SET %s DEBUG%s%s\n", sim_dname(exp->dptr), exp->dptr->debflags ? "=" : "", exp->dptr->debflags ? get_dbg_verb (exp->dbit, exp->dptr) : ""); fprintf (st, " Match Rules:\n"); @@ -10885,8 +10960,8 @@ if (i != exp->size) { /* Found? */ } sim_activate (&sim_expect_unit, /* schedule simulation stop when indicated */ (ep->switches & EXP_TYP_TIME) ? - (int32)((sim_timer_inst_per_sec ()*exp->after)/1000000.0) : - exp->after); + (int32)((sim_timer_inst_per_sec ()*ep->after)/1000000.0) : + ep->after); } /* Matched data is no longer available for future matching */ exp->buf_data = exp->buf_ins = 0; @@ -10911,12 +10986,8 @@ if (snd->insoff+size > snd->bufsize) { } memcpy(snd->buffer+snd->insoff, data, size); snd->insoff += size; -if (delay) - snd->delay = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*delay)/1000000.0) : delay; -if (after) - snd->after = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*after)/1000000.0) : after; -if (snd->after == 0) - snd->after = snd->delay; +snd->delay = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*delay)/1000000.0) : delay; +snd->after = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*after)/1000000.0) : after; snd->next_time = sim_gtime() + snd->after; return SCPE_OK; } @@ -10933,6 +11004,7 @@ return SCPE_OK; t_stat sim_show_send_input (FILE *st, const SEND *snd) { +fprintf (st, "%s\n", tmxr_send_line_name (snd)); if (snd->extoff < snd->insoff) { fprintf (st, " %d bytes of pending input Data:\n ", snd->insoff-snd->extoff); fprint_buffer_string (st, snd->buffer+snd->extoff, snd->insoff-snd->extoff); diff --git a/sim_console.c b/sim_console.c index 76eeef78..7623f1b5 100644 --- a/sim_console.c +++ b/sim_console.c @@ -234,7 +234,7 @@ TMLN sim_con_ldsc = { 0 }; /* console l TMXR sim_con_tmxr = { 1, 0, 0, &sim_con_ldsc, NULL, &sim_con_telnet };/* console line mux */ -SEND sim_con_send = {SEND_DEFAULT_DELAY, &sim_con_telnet, DBG_SND}; +SEND sim_con_send = {0, &sim_con_telnet, DBG_SND}; EXPECT sim_con_expect = {&sim_con_telnet, DBG_EXP}; static t_bool sim_con_console_port = TRUE; @@ -3969,7 +3969,8 @@ else { (sim_switches & SWMASK ('I')) ? "" : "\n"); free (mbuf); mbuf = sim_encode_quoted_string ((uint8 *)mbuf2, strlen (mbuf2)); - sim_exp_set (&sim_con_expect, mbuf, 0, sim_con_expect.after, EXP_TYP_PERSIST, NULL); + sim_switches = EXP_TYP_PERSIST; + sim_set_expect (&sim_con_expect, mbuf); free (mbuf); free (mbuf2); } @@ -3992,7 +3993,7 @@ else { rbuf = (uint8 *)malloc (1 + strlen(cptr)); - decode ((char *)rbuf, cptr); /* decod string */ + decode ((char *)rbuf, cptr); /* decode string */ sim_send_input (&sim_con_send, rbuf, strlen((char *)rbuf), 0, 0); /* queue it for output */ free (rbuf); } @@ -4011,9 +4012,12 @@ if (cptr == NULL || *cptr == 0) /* no argument string? * return SCPE_2FARG; /* need an argument */ val = (int32) get_uint (cptr, 10, INT_MAX, &r); /* parse the argument */ +if (r == SCPE_OK) { /* parse OK? */ + char gbuf[CBUFSIZE]; -if (r == SCPE_OK) /* parse OK? */ - sim_con_expect.after = val; /* save the delay value */ + snprintf (gbuf, sizeof (gbuf), "HALTAFTER=%d", val); + expect_cmd (1, gbuf); + } return r; } diff --git a/sim_defs.h b/sim_defs.h index 7a9c25bb..88dafd90 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -773,6 +773,7 @@ struct EXPTAB { uint32 size; /* match string size */ char *match_pattern; /* match pattern for format */ int32 cnt; /* proceed count */ + uint32 after; /* delay before halting */ 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 */ @@ -792,7 +793,6 @@ struct EXPECT { uint32 dbit; /* Debugging Bit */ EXPTAB *rules; /* match rules */ int32 size; /* count of match rules */ - uint32 after; /* delay before halting */ uint8 *buf; /* buffer of output data which has produced */ uint32 buf_ins; /* buffer insertion point for the next output data */ uint32 buf_size; /* buffer size */ diff --git a/sim_tmxr.c b/sim_tmxr.c index b4004f66..ba4a0f26 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -3542,8 +3542,7 @@ if (!found) { tmxr_open_devices = (TMXR **)realloc(tmxr_open_devices, (tmxr_open_device_count+1)*sizeof(*tmxr_open_devices)); tmxr_open_devices[tmxr_open_device_count++] = mux; for (i=0; ilines; i++) - if (0 == mux->ldsc[i].send.delay) - mux->ldsc[i].send.delay = SEND_DEFAULT_DELAY; + mux->ldsc[i].send.after = mux->ldsc[i].send.delay = 0; } #if defined(SIM_ASYNCH_MUX) pthread_mutex_unlock (&sim_tmxr_poll_lock); @@ -3612,6 +3611,41 @@ t_stat tmxr_locate_line_expect (const char *cptr, EXPECT **exp) return _tmxr_locate_line_send_expect (cptr, NULL, exp); } +static const char *_tmxr_send_expect_line_name (const SEND *snd, const EXPECT *exp) +{ +static char line_name[CBUFSIZE]; +int i, j; + +strcpy (line_name, ""); +for (i=0; ilines; ++j) + if ((snd == &tmxr_open_devices[i]->ldsc[j].send) || + (exp == &tmxr_open_devices[i]->ldsc[j].expect)) { + if (tmxr_open_devices[i]->lines > 1) + snprintf (line_name, sizeof (line_name), "%s:%d", tmxr_open_devices[i]->ldsc[j].send.dptr->name, j); + else + strncpy (line_name, tmxr_open_devices[i]->ldsc[j].send.dptr->name, sizeof (line_name)); + break; + } +return line_name; +} + +const char *tmxr_send_line_name (const SEND *snd) +{ +if (snd == sim_cons_get_send ()) + return "CONSOLE"; +else + return _tmxr_send_expect_line_name (snd, NULL); +} + +const char *tmxr_expect_line_name (const EXPECT *exp) +{ +if (exp == sim_cons_get_expect ()) + return "CONSOLE"; +else + return _tmxr_send_expect_line_name (NULL, exp); +} + t_stat tmxr_change_async (void) { #if defined(SIM_ASYNCH_IO) diff --git a/sim_tmxr.h b/sim_tmxr.h index ca74a49c..8fcffae5 100644 --- a/sim_tmxr.h +++ b/sim_tmxr.h @@ -287,6 +287,8 @@ t_stat tmxr_clock_coschedule_tmr_abs (UNIT *uptr, int32 tmr, int32 ticks); t_stat tmxr_change_async (void); t_stat tmxr_locate_line_send (const char *dev_line, SEND **snd); t_stat tmxr_locate_line_expect (const char *dev_line, EXPECT **exp); +const char *tmxr_send_line_name (const SEND *snd); +const char *tmxr_expect_line_name (const EXPECT *exp); t_stat tmxr_startup (void); t_stat tmxr_shutdown (void); t_stat tmxr_start_poll (void);