From 45b37eb9727db3d46a8b6e2bb84549130fb902da Mon Sep 17 00:00:00 2001 From: Richard Cornwell Date: Mon, 21 Jun 2021 18:53:28 -0400 Subject: [PATCH] SCP: Updated to current. --- scp.c | 8 +- sim_fio.c | 94 ++++++++++----------- sim_tmxr.c | 242 +++++++++++++++++++++++++++++++++++++---------------- 3 files changed, 220 insertions(+), 124 deletions(-) diff --git a/scp.c b/scp.c index fff2bcf..90b8f3f 100644 --- a/scp.c +++ b/scp.c @@ -5644,12 +5644,14 @@ else { lvl = MTAB_VDV; /* device match */ uptr = dptr->units; /* first unit */ } -if (*cptr == 0) /* must be more */ +if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#')) /* must be more */ return SCPE_2FARG; GET_SWITCHES (cptr); /* get more switches */ while (*cptr != 0) { /* do all mods */ cptr = get_glyph (svptr = cptr, gbuf, ','); /* get modifier */ + if (0 == strcmp (gbuf, ";")) + break; if ((cvptr = strchr (gbuf, '='))) /* = value? */ *cvptr++ = 0; for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) { @@ -5902,7 +5904,7 @@ MTAB *mptr; SHTAB *shtb = NULL, *shptr; GET_SWITCHES (cptr); /* get switches */ -if (*cptr == 0) /* must be more */ +if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#')) /* must be more */ return SCPE_2FARG; cptr = get_glyph (svptr = cptr, gbuf, 0); /* get next glyph */ @@ -5950,7 +5952,7 @@ else { } } -if (*cptr == 0) { /* now eol? */ +if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#')) { /* now eol? */ return (lvl == MTAB_VDV)? show_device (ofile, dptr, 0): show_unit (ofile, dptr, uptr, -1); diff --git a/sim_fio.c b/sim_fio.c index e912e2d..4d76dc4 100644 --- a/sim_fio.c +++ b/sim_fio.c @@ -248,8 +248,25 @@ if ((0 != fstat (fileno (fp), &statb)) || return TRUE; } -static void _sim_expand_homedir (const char *file, char *dest, size_t dest_size) +static char *_sim_expand_homedir (const char *file, char *dest, size_t dest_size) { +uint8 *without_quotes = NULL; +uint32 dsize = 0; + +errno = 0; +if (((*file == '"') && (file[strlen (file) - 1] == '"')) || + ((*file == '\'') && (file[strlen (file) - 1] == '\''))) { + without_quotes = (uint8*)malloc (strlen (file) + 1); + if (without_quotes == NULL) + return NULL; + if (SCPE_OK != sim_decode_quoted_string (file, without_quotes, &dsize)) { + free (without_quotes); + errno = EINVAL; + return NULL; + } + file = (const char*)without_quotes; +} + if (memcmp (file, "~/", 2) != 0) strlcpy (dest, file, dest_size); else { @@ -269,6 +286,8 @@ else { while ((strchr (dest, '\\') != NULL) && ((cptr = strchr (dest, '/')) != NULL)) *cptr = '\\'; } +free (without_quotes); +return dest; } #if defined(_WIN32) @@ -283,7 +302,8 @@ int sim_stat (const char *fname, struct stat *stat_str) { char namebuf[PATH_MAX + 1]; -_sim_expand_homedir (fname, namebuf, sizeof (namebuf)); +if (NULL == _sim_expand_homedir (fname, namebuf, sizeof (namebuf))) + return -1; return stat (namebuf, stat_str); } @@ -291,7 +311,8 @@ int sim_chdir(const char *path) { char pathbuf[PATH_MAX + 1]; -_sim_expand_homedir (path, pathbuf, sizeof (pathbuf)); +if (NULL == _sim_expand_homedir (path, pathbuf, sizeof (pathbuf))) + return -1; return chdir (pathbuf); } @@ -299,7 +320,8 @@ int sim_mkdir(const char *path) { char pathbuf[PATH_MAX + 1]; -_sim_expand_homedir (path, pathbuf, sizeof (pathbuf)); +if (NULL == _sim_expand_homedir (path, pathbuf, sizeof (pathbuf))) + return -1; #if defined(_WIN32) return mkdir (pathbuf); #else @@ -311,7 +333,8 @@ int sim_rmdir(const char *path) { char pathbuf[PATH_MAX + 1]; -_sim_expand_homedir (path, pathbuf, sizeof (pathbuf)); +if (NULL == _sim_expand_homedir (path, pathbuf, sizeof (pathbuf))) + return -1; return rmdir (pathbuf); } @@ -323,22 +346,9 @@ FILE* sim_fopen (const char *file, const char *mode) { FILE *f; char namebuf[PATH_MAX + 1]; -uint8 *without_quotes = NULL; -uint32 dsize = 0; -if (((*file == '"') && (file[strlen (file) - 1] == '"')) || - ((*file == '\'') && (file[strlen (file) - 1] == '\''))) { - without_quotes = (uint8*)malloc (strlen (file) + 1); - if (without_quotes == NULL) - return NULL; - if (SCPE_OK != sim_decode_quoted_string (file, without_quotes, &dsize)) { - free (without_quotes); - errno = EINVAL; - return NULL; - } - file = (const char*)without_quotes; -} -_sim_expand_homedir (file, namebuf, sizeof (namebuf)); +if (NULL == _sim_expand_homedir (file, namebuf, sizeof (namebuf))) + return NULL; #if defined (VMS) f = fopen (namebuf, mode, "ALQ=32", "DEQ=4096", "MBF=6", "MBC=127", "FOP=cbt,tef", "ROP=rah,wbh", "CTX=stm"); @@ -347,7 +357,6 @@ f = fopen64 (namebuf, mode); #else f = fopen (namebuf, mode); #endif -free (without_quotes); return f; } @@ -481,8 +490,10 @@ t_stat sim_copyfile (const char *source_file, const char *dest_file, t_bool over { char sourcename[PATH_MAX + 1], destname[PATH_MAX + 1]; -_sim_expand_homedir (source_file, sourcename, sizeof (sourcename)); -_sim_expand_homedir (dest_file, destname, sizeof (destname)); +if (NULL == _sim_expand_homedir (source_file, sourcename, sizeof (sourcename))) + return sim_messagef (SCPE_ARG, "Error Copying - Problem Parsing Source Filename '%s'\n", source_file); +if (NULL == _sim_expand_homedir (dest_file, destname, sizeof (destname))) + return sim_messagef (SCPE_ARG, "Error Copying - Problem Parsing Destination Filename '%s'\n", dest_file); if (CopyFileA (sourcename, destname, !overwrite_existing)) return SCPE_OK; return sim_messagef (SCPE_ARG, "Error Copying '%s' to '%s': %s\n", source_file, dest_file, sim_get_os_error_text (GetLastError ())); @@ -846,7 +857,6 @@ return getcwd (buf, buf_size); char *sim_filepath_parts (const char *filepath, const char *parts) { size_t tot_len = 0, tot_size = 0; -char *tempfilepath = NULL; char *fullpath = NULL, *result = NULL; char *c, *name, *ext; char chr; @@ -855,21 +865,10 @@ char filesizebuf[32] = ""; char filedatetimebuf[32] = ""; char namebuf[PATH_MAX + 1]; -/* Remove quotes if they're present */ -if (((*filepath == '\'') || (*filepath == '"')) && - (filepath[strlen (filepath) - 1] == *filepath)) { - size_t temp_size = 1 + strlen (filepath); - - tempfilepath = (char *)malloc (temp_size); - if (tempfilepath == NULL) - return NULL; - strlcpy (tempfilepath, 1 + filepath, temp_size); - tempfilepath[strlen (tempfilepath) - 1] = '\0'; - filepath = tempfilepath; - } /* Expand ~/ home directory */ -_sim_expand_homedir (filepath, namebuf, sizeof (namebuf)); +if (NULL == _sim_expand_homedir (filepath, namebuf, sizeof (namebuf))) + return NULL; filepath = namebuf; /* Check for full or current directory relative path */ @@ -878,26 +877,20 @@ if ((filepath[1] == ':') || (filepath[0] == '\\')){ tot_len = 1 + strlen (filepath); fullpath = (char *)malloc (tot_len); - if (fullpath == NULL) { - free (tempfilepath); + if (fullpath == NULL) return NULL; - } strcpy (fullpath, filepath); } else { /* Need to prepend current directory */ char dir[PATH_MAX+1] = ""; char *wd = sim_getcwd(dir, sizeof (dir)); - if (wd == NULL) { - free (tempfilepath); + if (wd == NULL) return NULL; - } tot_len = 1 + strlen (filepath) + 1 + strlen (dir); fullpath = (char *)malloc (tot_len); - if (fullpath == NULL) { - free (tempfilepath); + if (fullpath == NULL) return NULL; - } strlcpy (fullpath, dir, tot_len); if ((dir[strlen (dir) - 1] != '/') && /* if missing a trailing directory separator? */ (dir[strlen (dir) - 1] != '\\')) @@ -935,7 +928,7 @@ if (ext == NULL) ext = name + strlen (name); tot_size = 0; if (*parts == '\0') /* empty part specifier means strip only quotes */ - tot_size = strlen (tempfilepath); + tot_size = strlen (filepath); if (strchr (parts, 't') || /* modification time or */ strchr (parts, 'z')) { /* or size requested? */ struct stat filestat; @@ -1006,7 +999,6 @@ for (p = parts; *p; p++) { } } free (fullpath); -free (tempfilepath); return result; } @@ -1019,7 +1011,8 @@ WIN32_FIND_DATAA File; struct stat filestat; char WildName[PATH_MAX + 1]; -_sim_expand_homedir (cptr, WildName, sizeof (WildName)); +if (NULL == _sim_expand_homedir (cptr, WildName, sizeof (WildName))) + return SCPE_ARG; cptr = WildName; sim_trim_endspc (WildName); if ((hFind = FindFirstFileA (cptr, &File)) != INVALID_HANDLE_VALUE) { @@ -1082,7 +1075,8 @@ char DirName[PATH_MAX + 1], WholeName[PATH_MAX + 1], WildName[PATH_MAX + 1], Mat memset (DirName, 0, sizeof(DirName)); memset (WholeName, 0, sizeof(WholeName)); memset (MatchName, 0, sizeof(MatchName)); -_sim_expand_homedir (cptr, WildName, sizeof (WildName)); +if (NULL == _sim_expand_homedir (cptr, WildName, sizeof (WildName))) + return SCPE_ARG; cptr = WildName; sim_trim_endspc (WildName); c = sim_filepath_parts (cptr, "f"); diff --git a/sim_tmxr.c b/sim_tmxr.c index d3a64bb..78969ea 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -5146,117 +5146,157 @@ return SCPE_OK; /* Set the line connection order. - Example command for eight-line multiplexer: + This validation routine is called to set the connection order for the + multiplexer whose TMXR pointer is passed in the "desc" parameter. It parses + the line order list, specified by the "cptr" parameter, of commands such as: - SET LINEORDER=1;5;2-4;7 + SET LINEORDER=4-7 + SET LINEORDER=1;5;2-4;7;ALL + SET LINEORDER=ALL - Resulting connection order: 1,5,2,3,4,7,0,6. + Assuming an 8-channel multiplexer, the first form sets the connection order + to line numbers 4, 5, 6, and 7. The remaining lines will not be connected; a + connection attempt will be refused with "All connections busy." The second + form sets the connection order to line 1, 5, 2, 3, 4, 7, 0, and 6. The + trailing "ALL" parameter causes any unspecified lines to be added to the + connection order in ascending value. The third form sets the order to lines + 0-7, which is the default order in the absence of a line connection order + array. - Parameters: - - uptr = (not used) - - val = (not used) - - cptr = pointer to first character of range specification - - desc = pointer to multiplexer's TMXR structure + The range of accepted line numbers, including those implied by "ALL", can be + restricted by specifying a non-zero "val" parameter, with the upper 16 bits + specifying the maximum line number, and the lower 16 bits specifying the + minimum line number. If a minimum is specified but a maximum is not (i.e., + is zero), the maximum is the last line number defined by the multiplexer + descriptor. - On entry, cptr points to the value portion of the command string, which may - be either a semicolon-separated list of line ranges or the keyword ALL. + The "uptr" parameter is not used. + + On entry, "cptr" points to the value portion of the command string, which may + be either a semicolon-separated list of line ranges or the keyword "ALL". If + "ALL" is specified, it must be the last (or only) item in the list. If a line connection order array is not defined in the multiplexer - descriptor, the command is rejected. If the specified range encompasses all - of the lines, the first value of the connection order array is set to -1 to + descriptor, or a line range string is not present, or the optional minimum + and maximum restrictions in the "val" parameter are not valid, the command is + rejected. If the specified range encompasses all of the lines defined by the + multiplexer, the first value of the connection order array is set to -1 to indicate sequential connection order. Otherwise, the line values in the - array are set to the order specified by the command string. All values are - populated, first with those explicitly specified in the command string, and - then in ascending sequence with those not specified. + array are set to the order specified by the command string. If fewer values + are supplied than there are lines supported by the device, and the final + parameter is not ALL, the remaining lines will be inaccessible and are + indicated by a -1 value after the last specified value. If an error occurs, the original line order is not disturbed. */ -t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, CONST char *carg, void *desc) +t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { -TMXR *mp = (TMXR *) desc; -char *tbuf; -char *tptr; -CONST char *cptr; -t_addr low, high, max = (t_addr) mp->lines - 1; -int32 *list; +TMXR *mp = (TMXR *) desc; +char *tbuf = NULL; +char *tptr; +t_addr low, high, min, max; +int32 *list; t_bool *set; -uint32 line, idx = 0; +uint32 line, idx; +t_addr lncount = (t_addr) (mp->lines - 1); t_stat result = SCPE_OK; -if (mp->lnorder == NULL) /* line connection order undefined? */ - return SCPE_NXPAR; /* "Non-existent parameter" error */ +if (mp->lnorder == NULL) /* if the connection order array is not defined */ + return SCPE_NXPAR; /* then report a "Non-existent parameter" error */ -else if ((carg == NULL) || (*carg == '\0')) /* line range not supplied? */ - return SCPE_MISVAL; /* "Missing value" error */ +else if ((cptr == NULL) || (*cptr == '\0')) /* otherwise if a line range was not supplied */ + return SCPE_MISVAL; /* then report a "Missing value" error */ -list = (int32 *) calloc (mp->lines, sizeof (int32)); /* allocate new line order array */ +else { /* otherwise */ + min = (t_addr) (val & 0xFFFF); /* split the restriction into */ + max = (t_addr) ((val >> 16) & 0xFFFF); /* minimum and maximum line numbers */ -if (list == NULL) /* allocation failed? */ - return SCPE_MEM; /* report it */ + if (max == 0) /* if the maximum line number isn't specified */ + max = lncount; /* then use the defined maximum */ -set = (t_bool *) calloc (mp->lines, sizeof (t_bool)); /* allocate line set tracking array */ - -if (set == NULL) { /* allocation failed? */ - free (list); /* free successful list allocation */ - return SCPE_MEM; /* report it */ + if (min > lncount || max > lncount || min > max) /* if the restriction isn't valid */ + return SCPE_IERR; /* then report an "Internal error" */ } -tbuf = (char *) calloc (strlen(carg)+2, sizeof(*carg)); -strcpy (tbuf, carg); +list = (int32 *) calloc (mp->lines, sizeof (int32)); /* allocate the new line order array */ + +if (list == NULL) /* if the allocation failed */ + return SCPE_MEM; /* then report a "Memory exhausted" error */ + +set = (t_bool *) calloc (mp->lines, sizeof (t_bool)); /* allocate the line set tracking array */ + +if (set == NULL) { /* if the allocation failed */ + free (list); /* then free the successful list allocation */ + return SCPE_MEM; /* and report a "Memory exhausted" error */ + } + +tbuf = (char *) calloc (strlen(cptr)+2, sizeof(*cptr)); +if (tbuf == NULL) { /* if the allocation failed */ + free (tbuf); /* then free the successful list allocation */ + return SCPE_MEM; /* and report a "Memory exhausted" error */ + } + +strcpy (tbuf, cptr); tptr = tbuf + strlen (tbuf); /* append a semicolon */ *tptr++ = ';'; /* to the command string */ *tptr = '\0'; /* to make parsing easier for get_range */ cptr = tbuf; -while (*cptr) { /* parse command string */ - cptr = get_range (NULL, cptr, &low, &high, 10, max, ';');/* get a line range */ +idx = 0; /* initialize the index of ordered values */ - if (cptr == NULL) { /* parsing error? */ - result = SCPE_ARG; /* "Invalid argument" error */ - break; +while (*cptr != '\0') { /* while characters remain in the command string */ + if (strncasecmp (cptr, "ALL;", 4) == 0) { /* if the parameter is "ALL" */ + if (val != 0 || idx > 0 && idx <= max) /* then if some lines are restrictied or unspecified */ + for (line = (uint32) min; line <= max; line++) /* then fill them in sequentially */ + if (set [line] == FALSE) /* setting each unspecified line */ + list [idx++] = line; /* into the line order */ + + cptr = cptr + 4; /* advance past "ALL" and the trailing semicolon */ + + if (*cptr != '\0') /* if "ALL" is not the last parameter */ + result = sim_messagef (SCPE_2MARG, /* then report extraneous items */ + "Too many args: %s\n", cptr); + + break; /* "ALL" terminates the order list */ } - else if ((low > max) || (high > max)) { /* line out of range? */ - result = SCPE_SUB; /* "Subscript out of range" error */ - break; + cptr = get_range (NULL, cptr, &low, &high, 10, max, ';'); /* get a line range */ + + if (cptr == NULL) { /* if a parsing error occurred */ + result = SCPE_ARG; /* then report an invalid argument */ + break; /* and terminate the parse */ } - else if ((low == 0) && (high == max)) { /* entire line range specified? */ - list [0] = -1; /* set sequential order flag */ - idx = (uint32) max + 1; /* indicate no fill-in needed */ - break; + else if (low < min || low > max || high > max) { /* otherwise if the line number is invalid */ + result = SCPE_SUB; /* then report the subscript is out of range */ + break; /* and terminate the parse */ } - else - for (line = (uint32) low; line <= (uint32) high; line++) /* see if previously specified */ - if (set [line] == FALSE) { /* not already specified? */ - set [line] = TRUE; /* now it is */ - list [idx] = line; /* add line to connection order */ - idx = idx + 1; /* bump "specified" count */ + else /* otherwise it's a valid range */ + for (line = (uint32) low; line <= high; line++) /* so add the line(s) to the order */ + if (set [line] == FALSE) { /* if the line number has not been specified */ + set [line] = TRUE; /* then now it is */ + list [idx++] = line; /* and add it to the connection order */ } } -if (result == SCPE_OK) { /* assignment successful? */ - if (idx <= max) /* any lines not specified? */ - for (line = 0; line <= max; line++) /* fill them in sequentially */ - if (set [line] == FALSE) { /* specified? */ - list [idx] = line; /* no, so add it */ - idx = idx + 1; - } +if (result == SCPE_OK) { /* if the assignment succeeded */ + if (idx <= max) /* then if any lines were not specified */ + list [idx] = -1; /* then terminate the order list after the last one */ - memcpy (mp->lnorder, list, mp->lines * sizeof (int32)); /* copy working array to connection array */ + memcpy (mp->lnorder, list, /* copy the working array to the connection array */ + mp->lines * sizeof (int32)); } -free (list); /* free list allocation */ -free (set); /* free set allocation */ -free (tbuf); /* free arg copy with ; */ +free (list); /* free the list allocation */ +free (set); /* and the set allocation */ +free (tbuf); /* and the arg copy with ; */ -return result; +return result; /* return the status */ } - -/* Show line connection order. +/* Show the line connection order. Parameters: - st = stream on which output is to be written @@ -5268,7 +5308,8 @@ return result; command is rejected. If the first value of the connection order array is set to -1, then the connection order is sequential. Otherwise, the line values in the array are printed as a semicolon-separated list. Ranges are printed - where possible to shorten the output. + where possible to shorten the output. A -1 value within the array indicates + the end of the order list. */ t_stat tmxr_show_lnorder (FILE *st, UNIT *uptr, int32 val, CONST void *desc) @@ -5287,7 +5328,7 @@ if (*iptr < 0) /* sequential order indi else { low = last = *iptr++; /* set first line value */ - for (j = 1; j <= mp->lines; j++) { /* print remaining lines in order list */ + for (j = 1; last != -1; j++) { /* print the remaining lines in the order list */ if (j < mp->lines) /* more lines to process? */ i = *iptr++; /* get next line in list */ else /* final iteration */ @@ -5622,6 +5663,64 @@ if ((dptr) && (dbits & dptr->dctrl)) { /* Testing of sim_sock and tmxr */ +static struct lnorder_test { + const char *orderspec; + int32 valspec; + t_stat expected_stat; + int32 expected_orderlist[8]; + } lnorders[] = { + {NULL, 0, SCPE_MISVAL}, + {"", 0, SCPE_MISVAL}, + {"4-7", 0x3FFF3FFF, SCPE_IERR}, + {"6-8", 0, SCPE_SUB}, + {"9-11", 0, SCPE_SUB}, + {"4-7", 0, SCPE_OK, + { 4, 5, 6, 7, -1}}, + {"1;5;2-4;7;ALL", 0, SCPE_OK, + { 1, 5, 2, 3, 4, 7, 0, 6}}, + {"ALL", 0, SCPE_OK, + { -1}}, + }; + +static t_stat _lnorder_test (TMXR *tmxr, + struct lnorder_test *t) +{ +t_stat r; +char msg[80] = ""; +int i; + +r = tmxr_set_lnorder (NULL, t->valspec, t->orderspec, tmxr); +if (r != t->expected_stat) { + snprintf (msg, sizeof (msg), "Unexpected lnorder result status for \"%s\" Expected: %s", t->orderspec, sim_error_text (t->expected_stat)); + return sim_messagef (SCPE_ARG, "%s, Got: %s\n", msg, sim_error_text (r)); + } +if (r == SCPE_OK) { + for (i = 0; i<8; i++) + if (t->expected_orderlist[i] != tmxr->lnorder[i]) + return sim_messagef (SCPE_ARG, "Unexpected order entry for line %d: %d vs %d\n", i, tmxr->lnorder[i], t->expected_orderlist[i]); + } +return SCPE_OK; +} + +static t_stat sim_tmxr_test_lnorder (TMXR *tmxr) +{ +int i; +int32 *saved_lnorder = tmxr->lnorder; +int32 saved_lines = tmxr->lines; + +tmxr->lnorder = calloc (tmxr->lines, sizeof (*tmxr->lnorder)); +if (tmxr->lines >= 8) { + tmxr->lines = 8; + for (i = 0; i < (sizeof (lnorders)/sizeof (lnorders[0])); ++i) + _lnorder_test (tmxr, &lnorders[i]); + } +free (tmxr->lnorder); +tmxr->lnorder = saved_lnorder; +tmxr->lines = saved_lines; +return SCPE_OK; +} + + #include t_stat tmxr_sock_test (DEVICE *dptr) @@ -5676,6 +5775,7 @@ if (tmxr->lines > 1) { sim_close_sock (sock_line); sock_line = INVALID_SOCKET; SIM_TEST(detach_cmd (0, dptr->name)); + SIM_TEST(sim_tmxr_test_lnorder (tmxr)); } return stat; }