From 481e82303cffb706be7ba9d9966a35f2c7d9d593 Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Wed, 26 Jul 2023 14:54:54 -0400 Subject: [PATCH 01/10] use tokens in sexp help --- code/parse/sexp.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index 637638269e8..5de72f05044 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -36649,7 +36649,7 @@ SCP_vector Sexp_help = { "arrays and pointers.\r\n\r\nPlease note that only numeric variables are supported. Any " "attempt to access a string variable will result in a value of SEXP_NAN_FOREVER being returned.\r\n\r\n" "Takes 1 argument...\r\n" - "\t1:\tIndex of variable, from 0 to MAX_SEXP_VARIABLES - 1." }, + "\t1:\tIndex of variable, from 0 to " SCP_TOKEN_TO_STR(MAX_SEXP_VARIABLES) " - 1." }, { OP_SET_VARIABLE_BY_INDEX, "set-variable-by-index (originally variable-array-set)\r\n" "\tSets the value of the variable specified by the given index. This is an alternate way " @@ -36657,7 +36657,7 @@ SCP_vector Sexp_help = { "arrays and pointers.\r\n\r\nIn contrast to get-variable-by-index, note that this sexp " "*does* allow the modification of string variables.\r\n\r\n" "Takes 2 arguments...\r\n" - "\t1:\tIndex of variable, from 0 to MAX_SEXP_VARIABLES - 1.\r\n" + "\t1:\tIndex of variable, from 0 to " SCP_TOKEN_TO_STR(MAX_SEXP_VARIABLES) " - 1.\r\n" "\t2:\tValue to be set." }, { OP_COPY_VARIABLE_FROM_INDEX, "copy-variable-from-index\r\n" @@ -36665,14 +36665,14 @@ SCP_vector Sexp_help = { "This is very similar to get-variable-by-index, except the result is stored in a new variable rather than " "being returned by value. One important difference is that this sexp can be used to copy string variables as well as numeric variables.\r\n\r\n" "Takes 2 arguments...\r\n" - "\t1:\tIndex of source variable, from 0 to MAX_SEXP_VARIABLES - 1.\r\n" + "\t1:\tIndex of source variable, from 0 to " SCP_TOKEN_TO_STR(MAX_SEXP_VARIABLES) " - 1.\r\n" "\t2:\tDestination variable. The type of this variable must match the type of the variable referenced by the index." }, { OP_COPY_VARIABLE_BETWEEN_INDEXES, "copy-variable-between-indexes\r\n" "\tRetrieves the value of the variable specified by the first index and stores it in the variable specified by the second index. The first variable is not modified.\r\n\r\n" "Takes 2 arguments...\r\n" - "\t1:\tIndex of source variable, from 0 to MAX_SEXP_VARIABLES - 1.\r\n" - "\t2:\tIndex of destination variable, from 0 to MAX_SEXP_VARIABLES - 1. The types of both variables must match." }, + "\t1:\tIndex of source variable, from 0 to " SCP_TOKEN_TO_STR(MAX_SEXP_VARIABLES) " - 1.\r\n" + "\t2:\tIndex of destination variable, from 0 to " SCP_TOKEN_TO_STR(MAX_SEXP_VARIABLES) " - 1. The types of both variables must match." }, // Karajorma/jg18 { OP_CONTAINER_ADD_TO_LIST, "add-to-list\r\n" @@ -37489,7 +37489,7 @@ SCP_vector Sexp_help = { // Goober5000 { OP_STRING_CONCATENATE, "string-concatenate (deprecated in favor of string-concatenate-block)\r\n" "\tConcatenates two strings, putting the result into a string variable. If the length of the string will " - "exceed the sexp variable token limit (currently 32), it will be truncated.\r\n\r\n" + "exceed the sexp variable token limit (" SCP_TOKEN_TO_STR(TOKEN_LENGTH) " - 1), it will be truncated.\r\n\r\n" "Takes 3 arguments...\r\n" "\t1: First string\r\n" "\t2: Second string\r\n" @@ -37498,7 +37498,7 @@ SCP_vector Sexp_help = { // Goober5000 { OP_STRING_CONCATENATE_BLOCK, "string-concatenate-block\r\n" "\tConcatenates two or more strings, putting the result into a string variable. If the length of the string will " - "exceed the sexp variable token limit (currently 32), it will be truncated.\r\n\r\n" + "exceed the sexp variable token limit (" SCP_TOKEN_TO_STR(TOKEN_LENGTH) " - 1), it will be truncated.\r\n\r\n" "Takes 3 or more arguments...\r\n" "\t1: String variable to hold the result\r\n" "\tRest: Strings to concatenate. At least two of these are required; the rest are optional.\r\n" }, @@ -37506,7 +37506,7 @@ SCP_vector Sexp_help = { // Goober5000 { OP_STRING_GET_SUBSTRING, "string-get-substring\r\n" "\tExtracts a substring from a parent string, putting the result into a string variable. If the length of the string will " - "exceed the sexp variable token limit (currently 32), it will be truncated.\r\n\r\n" + "exceed the sexp variable token limit (" SCP_TOKEN_TO_STR(TOKEN_LENGTH) " - 1), it will be truncated.\r\n\r\n" "Takes 3 arguments...\r\n" "\t1: Parent string\r\n" "\t2: Index at which the substring begins (0-based)\r\n" @@ -37516,7 +37516,7 @@ SCP_vector Sexp_help = { // Goober5000 { OP_STRING_SET_SUBSTRING, "string-set-substring\r\n" "\tReplaces a substring from a parent string with a new string, putting the result into a string variable. If the length of the string will " - "exceed the sexp variable token limit (currently 32), it will be truncated.\r\n\r\n" + "exceed the sexp variable token limit (" SCP_TOKEN_TO_STR(TOKEN_LENGTH) " - 1), it will be truncated.\r\n\r\n" "Takes 3 arguments...\r\n" "\t1: Parent string\r\n" "\t2: Index at which the substring begins (0-based)\r\n" From 9480869e0a3135d300af268fe5f6d520184b1136 Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Wed, 26 Jul 2023 14:19:20 -0400 Subject: [PATCH 02/10] make variable parsing more robust; don't assume that an @ is always followed by a valid variable name; fix length checks; soften non-fatal Errors to error_display(0) --- code/parse/parselo.cpp | 44 ++++--- code/parse/sexp.cpp | 269 ++++++++++++++++++++++------------------- 2 files changed, 172 insertions(+), 141 deletions(-) diff --git a/code/parse/parselo.cpp b/code/parse/parselo.cpp index 63d51c8cf1b..649e5d86334 100644 --- a/code/parse/parselo.cpp +++ b/code/parse/parselo.cpp @@ -1114,17 +1114,22 @@ int get_string_or_variable (char *str) ignore_white_space(); // Variable - if (*Mp == '@') + if (*Mp == SEXP_VARIABLE_CHAR) { + auto saved_Mp = Mp; Mp++; stuff_string_white(str); int sexp_variable_index = get_index_sexp_variable_name(str); // We only want String variables - Assertion (sexp_variable_index != -1, "Didn't find variable name \"%s\"", str); - Assert (Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_STRING); - - result = PARSING_FOUND_VARIABLE; + if (sexp_variable_index >= 0) + result = PARSING_FOUND_VARIABLE; + else + { + Mp = saved_Mp; + stuff_string_white(str); + error_display(1, "Expected \"%s\" to be a variable", str); + } } // Quoted string else if (*Mp == '"') @@ -1135,7 +1140,7 @@ int get_string_or_variable (char *str) else { get_string(str); - Error(LOCATION, "Invalid entry \"%s\" found in get_string_or_variable. Must be a quoted string or a string variable name.", str); + error_display(1, "Invalid entry \"%s\" found in get_string_or_variable. Must be a quoted string or a string variable name.", str); } return result; @@ -1149,17 +1154,22 @@ int get_string_or_variable (SCP_string &str) ignore_white_space(); // Variable - if (*Mp == '@') + if (*Mp == SEXP_VARIABLE_CHAR) { + auto saved_Mp = Mp; Mp++; stuff_string_white(str); int sexp_variable_index = get_index_sexp_variable_name(str); // We only want String variables - Assertion (sexp_variable_index != -1, "Didn't find variable name \"%s\"", str.c_str()); - Assert (Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_STRING); - - result = PARSING_FOUND_VARIABLE; + if (sexp_variable_index >= 0) + result = PARSING_FOUND_VARIABLE; + else + { + Mp = saved_Mp; + stuff_string_white(str); + error_display(1, "Expected \"%s\" to be a variable", str.c_str()); + } } // Quoted string else if (*Mp == '"') @@ -1170,7 +1180,7 @@ int get_string_or_variable (SCP_string &str) else { get_string(str); - Error(LOCATION, "Invalid entry \"%s\" found in get_string_or_variable. Must be a quoted string or a string variable name.", str.c_str()); + error_display(1, "Invalid entry \"%s\" found in get_string_or_variable. Must be a quoted string or a string variable name.", str.c_str()); } return result; @@ -2762,13 +2772,14 @@ int stuff_int_optional(int *i) // index of the variable in the following slot. void stuff_int_or_variable(int *i, int *var_index, bool need_positive_value) { - if (*Mp == '@') + if (*Mp == SEXP_VARIABLE_CHAR) { - Mp++; int value = -1; SCP_string str; - stuff_string(str, F_NAME); + auto saved_Mp = Mp; + Mp++; + stuff_string(str, F_NAME); int index = get_index_sexp_variable_name(str); if (index > -1 && index < MAX_SEXP_VARIABLES) @@ -2784,7 +2795,8 @@ void stuff_int_or_variable(int *i, int *var_index, bool need_positive_value) } else { - + Mp = saved_Mp; + stuff_string(str, F_NAME); error_display(1, "Invalid variable name \"%s\" found.", str.c_str()); } diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index 5de72f05044..aaa1b70afba 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -4130,21 +4130,51 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, s } // Goober5000 -void get_unformatted_sexp_variable_name(char *unformatted, const char *formatted_pre) +bool get_unformatted_sexp_variable_name(char *unformatted, const char *formatted) { - const char *formatted; + // first character should be @ + if (*formatted != SEXP_VARIABLE_CHAR) + return false; + formatted++; - // Goober5000 - trim @ if needed - if (formatted_pre[0] == SEXP_VARIABLE_CHAR) - formatted = formatted_pre+1; - else - formatted = formatted_pre; + // get variable name (up to '[') + auto l_bracket = strchr(formatted, '['); - // get variable name (up to '[' - auto end_index = strcspn(formatted, "["); - Assert( (end_index != 0) && (end_index < TOKEN_LENGTH-1) ); - strncpy(unformatted, formatted, end_index); - unformatted[end_index] = '\0'; + auto n = (l_bracket != nullptr) ? (l_bracket - formatted) : strlen(formatted); + + if (n >= TOKEN_LENGTH - 1) + return false; + + strncpy(unformatted, formatted, n); + unformatted[n] = '\0'; + return true; +} + +// Goober5000 +// This function specifically checks the token that is parsed from a mission file. +// In this situation the variable would appear as @variable_name[variable_contents]. +// See also check_sexp_node_text_for_sexp_variable(). +int check_string_for_sexp_variable(const char *str, size_t n_chars) +{ + if (*str != SEXP_VARIABLE_CHAR) + return -1; + + constexpr size_t var_token_size = 2 * (TOKEN_LENGTH - 1) + 4; // @variable_token[contents_token]\0 + char variable_token[var_token_size]; + char variable_name[NAME_LENGTH]; + + // token is too long? + if (n_chars >= var_token_size) + return -1; + + strncpy(variable_token, str, n_chars); + variable_token[n_chars] = 0; + + // see if it's really a variable + if (!get_unformatted_sexp_variable_name(variable_name, variable_token)) + return -1; + + return get_index_sexp_variable_name(variable_name); } /** @@ -4153,18 +4183,13 @@ void get_unformatted_sexp_variable_name(char *unformatted, const char *formatted * If Fred_running - stuff Sexp_variables[].variable_name * otherwise - stuff index into Sexp_variables array. */ -void get_sexp_text_for_variable(char *text, const char *token) +void get_sexp_text_for_variable(char *text, int sexp_var_index) { - int sexp_var_index; - - get_unformatted_sexp_variable_name(text, token); + Assertion(sexp_var_index >= 0 && sexp_var_index < MAX_SEXP_VARIABLES, "sexp_var_index out of range!"); - if ( !Fred_running ) { - // freespace - get index into Sexp_variables array - sexp_var_index = get_index_sexp_variable_name(text); - if (sexp_var_index == -1) { - Error(LOCATION, "Invalid variable name [%s]!", text); - } + if (Fred_running) { + strcpy(text, Sexp_variables[sexp_var_index].variable_name); + } else { sprintf(text, "%d", sexp_var_index); } } @@ -4270,7 +4295,6 @@ int get_sexp() { int start, node, last, op; char token[TOKEN_LENGTH]; - char variable_text[TOKEN_LENGTH]; bool prune_extra_args = false; Assert(*(Mp-1) == '('); @@ -4286,7 +4310,7 @@ int get_sexp() while (*Mp != ')') { // end of string or end of file if (*Mp == '\0') { - Error(LOCATION, "Unexpected end of sexp!"); + error_display(0, "Unexpected end of sexp!"); return -1; } @@ -4301,31 +4325,21 @@ int get_sexp() auto len = strcspn(Mp + 1, "\""); // was closing quote not found? if (*(Mp + 1 + len) != '\"') { - Error(LOCATION, "Unexpected end of quoted string embedded in sexp!"); + error_display(0, "Unexpected end of quoted string embedded in sexp!"); return -1; } - // check if string variable - if ( *(Mp + 1) == SEXP_VARIABLE_CHAR ) { - char variable_token[2*TOKEN_LENGTH+2]; // variable_token[contents_token] - - // reduce length by 1 for end \" - auto length = len - 1; - if (length >= 2*TOKEN_LENGTH+2) { - Error(LOCATION, "Variable token %s is too long. Needs to be %d characters or shorter.", Mp, 2*TOKEN_LENGTH+2 - 1); - return -1; - } - - // start copying after skipping 1st char (i.e. variable char) - strncpy(variable_token, Mp + 2, length); - variable_token[length] = 0; - - get_sexp_text_for_variable(variable_text, variable_token); - node = alloc_sexp(variable_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_STRING, -1, -1); - } else { + // it could be a string variable + int sexp_var_index = check_string_for_sexp_variable(Mp + 1, len); + if (sexp_var_index >= 0) { + get_sexp_text_for_variable(token, sexp_var_index); + node = alloc_sexp(token, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_STRING, -1, -1); + } + // it's a regular string + else { // token is too long? - if (len >= TOKEN_LENGTH) { - Error(LOCATION, "Token %s is too long. Needs to be %d characters or shorter.", Mp, TOKEN_LENGTH - 1); + if (len >= TOKEN_LENGTH - 1) { + error_display(0, "Token %s is too long. Needs to be %d characters or shorter.", Mp, TOKEN_LENGTH - 1); return -1; } @@ -4334,9 +4348,8 @@ int get_sexp() node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1); } - // bump past closing \" by 1 char + // bump past closing \" Mp += (len + 2); - } // Sexp container @@ -4392,82 +4405,82 @@ int get_sexp() // Sexp operator or number else { - int len = 0; - bool variable = false; - while (*Mp != ')' && !is_white_space(*Mp)) { - // numeric variable? - if ( (len == 0) && (*Mp == SEXP_VARIABLE_CHAR) ) { - variable = true; - Mp++; - continue; - } - - // end of string or end of file? - if (*Mp == '\0') { - Error(LOCATION, "Unexpected end of sexp!"); + size_t len = 0; + auto ch = Mp; + while (*ch != ')' && !is_white_space(*ch)) { + // end of string or end of file + if (*ch == '\0') { + error_display(0, "Unexpected end of sexp!"); return -1; } + ch++; + len++; + } + // it could be a numeric variable + int sexp_var_index = check_string_for_sexp_variable(Mp, len); + if (sexp_var_index >= 0) { + get_sexp_text_for_variable(token, sexp_var_index); + node = alloc_sexp(token, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_NUMBER, -1, -1); + } + // it could be an operator + else { // token is too long? if (len >= TOKEN_LENGTH - 1) { - token[TOKEN_LENGTH - 1] = '\0'; - Error(LOCATION, "Token %s is too long. Needs to be %d characters or shorter.", token, TOKEN_LENGTH - 1); + error_display(0, "Token %s is too long. Needs to be %d characters or shorter.", token, TOKEN_LENGTH - 1); return -1; } - // build the token - token[len++] = *Mp++; - } - token[len] = 0; - - // maybe replace deprecated names - if (!stricmp(token, "set-ship-position")) - strcpy_s(token, "set-object-position"); - else if (!stricmp(token, "set-ship-facing")) - strcpy_s(token, "set-object-facing"); - else if (!stricmp(token, "set-ship-facing-object")) - strcpy_s(token, "set-object-facing-object"); - else if (!stricmp(token, "ai-chase-any-except")) { - strcpy_s(token, "ai-chase-any"); - prune_extra_args = true; - } else if (!stricmp(token, "change-ship-model")) - strcpy_s(token, "change-ship-class"); - else if (!stricmp(token, "radar-set-max-range")) - strcpy_s(token, "hud-set-max-targeting-range"); - else if (!stricmp(token, "ship-subsys-vanished")) - strcpy_s(token, "ship-subsys-vanish"); - else if (!stricmp(token, "directive-is-variable")) - strcpy_s(token, "directive-value"); - else if (!stricmp(token, "variable-array-get")) - strcpy_s(token, "get-variable-by-index"); - else if (!stricmp(token, "variable-array-set")) - strcpy_s(token, "set-variable-by-index"); - else if (!stricmp(token, "distance-ship-subsystem")) - strcpy_s(token, "distance-center-to-subsystem"); - else if (!stricmp(token, "remove-weapons")) - strcpy_s(token, "clear-weapons"); - else if (!stricmp(token, "hud-set-retail-gauge-active")) - strcpy_s(token, "hud-set-builtin-gauge-active"); - else if (!stricmp(token, "perform-actions")) - strcpy_s(token, "perform-actions-bool-first"); - else if (!stricmp(token, "add-to-collision-group2")) - strcpy_s(token, "add-to-collision-group-new"); - else if (!stricmp(token, "remove-from-collision-group2")) - strcpy_s(token, "remove-from-collision-group-new"); - - op = get_operator_index(token); - if (op >= 0) { - node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1); - } else { - if ( variable ) { - // convert token text for variable - get_sexp_text_for_variable(variable_text, token); + strncpy(token, Mp, len); + token[len] = 0; - node = alloc_sexp(variable_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_NUMBER, -1, -1); - } else { + // maybe replace deprecated names + if (!stricmp(token, "set-ship-position")) + strcpy_s(token, "set-object-position"); + else if (!stricmp(token, "set-ship-facing")) + strcpy_s(token, "set-object-facing"); + else if (!stricmp(token, "set-ship-facing-object")) + strcpy_s(token, "set-object-facing-object"); + else if (!stricmp(token, "ai-chase-any-except")) { + strcpy_s(token, "ai-chase-any"); + prune_extra_args = true; + } else if (!stricmp(token, "change-ship-model")) + strcpy_s(token, "change-ship-class"); + else if (!stricmp(token, "radar-set-max-range")) + strcpy_s(token, "hud-set-max-targeting-range"); + else if (!stricmp(token, "ship-subsys-vanished")) + strcpy_s(token, "ship-subsys-vanish"); + else if (!stricmp(token, "directive-is-variable")) + strcpy_s(token, "directive-value"); + else if (!stricmp(token, "variable-array-get")) + strcpy_s(token, "get-variable-by-index"); + else if (!stricmp(token, "variable-array-set")) + strcpy_s(token, "set-variable-by-index"); + else if (!stricmp(token, "distance-ship-subsystem")) + strcpy_s(token, "distance-center-to-subsystem"); + else if (!stricmp(token, "remove-weapons")) + strcpy_s(token, "clear-weapons"); + else if (!stricmp(token, "hud-set-retail-gauge-active")) + strcpy_s(token, "hud-set-builtin-gauge-active"); + else if (!stricmp(token, "perform-actions")) + strcpy_s(token, "perform-actions-bool-first"); + else if (!stricmp(token, "add-to-collision-group2")) + strcpy_s(token, "add-to-collision-group-new"); + else if (!stricmp(token, "remove-from-collision-group2")) + strcpy_s(token, "remove-from-collision-group-new"); + + op = get_operator_index(token); + if (op >= 0) { + node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1); + } + // it's not an operator, and we've checked for variables, so treat it as a number + else { node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_NUMBER, -1, -1); } } + + // bump past token + Mp += len; } // update links @@ -29090,7 +29103,7 @@ int get_sexp_main() if (buf[511] != '\0') strcpy(&buf[506], "[...]"); - Error(LOCATION, "Expected to find an open parenthesis in the following sexp:\n%s", buf); + error_display(0, "Expected to find an open parenthesis in the following sexp:\n%s", buf); return -1; } @@ -29103,7 +29116,7 @@ int get_sexp_main() op = get_operator_index(start_node); if (op < 0) { - Error(LOCATION, "Can't find operator %s in operator list!\n", CTEXT(start_node)); + error_display(0, "Can't find operator %s in operator list!\n", CTEXT(start_node)); return -1; } } @@ -33290,21 +33303,27 @@ int query_sexp_ai_goal_valid(int sexp_ai_goal, int ship_num) return ai_query_goal_valid(ship_num, Sexp_ai_goal_links[i].ai_goal); } -int check_text_for_variable_name(const char *text) +// Goober5000 +// This function specifically checks the sexp node text that can be referenced +// by a special argument. In this situation the variable could appear as an +// undecorated variable_name or as the full @variable_name[variable_contents]. +// There is also no need to copy the characters since the string is a small +// token rather than an entire file. See also check_string_for_sexp_variable(). +int check_sexp_node_text_for_sexp_variable(const char *text) { - char variable_name[TOKEN_LENGTH]; - - // if the text is a variable name, get the variable index - int sexp_variable_index = get_index_sexp_variable_name(text); - // if the text is a formatted variable name, get the variable index - if (sexp_variable_index < 0 && text[0] == SEXP_VARIABLE_CHAR) + if (text[0] == SEXP_VARIABLE_CHAR) { - get_unformatted_sexp_variable_name(variable_name, text); - sexp_variable_index = get_index_sexp_variable_name(variable_name); + char variable_name[TOKEN_LENGTH]; + + if (!get_unformatted_sexp_variable_name(variable_name, text)) + return -1; + + return get_index_sexp_variable_name(variable_name); } - return sexp_variable_index; + // try a straight lookup + return get_index_sexp_variable_name(text); } /** @@ -33345,7 +33364,7 @@ const char *CTEXT(int n) if (!(Sexp_nodes[arg_n].flags & SNF_CHECKED_ARG_FOR_VAR)) { - Sexp_nodes[arg_n].cached_variable_index = check_text_for_variable_name(text); + Sexp_nodes[arg_n].cached_variable_index = check_sexp_node_text_for_sexp_variable(text); Sexp_nodes[arg_n].flags |= SNF_CHECKED_ARG_FOR_VAR; } @@ -33354,7 +33373,7 @@ const char *CTEXT(int n) // just check the text of the argument for a variable else { - sexp_variable_index = check_text_for_variable_name(text); + sexp_variable_index = check_sexp_node_text_for_sexp_variable(text); } // if we have a variable, return the variable value, else return the regular argument From 7781d9fcda37227820ff24d3f393370c65fe7a71 Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Thu, 27 Jul 2023 02:19:54 -0400 Subject: [PATCH 03/10] fix sexp variables in argument lists --- code/parse/sexp.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index aaa1b70afba..0d4f5231ba1 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -33364,7 +33364,12 @@ const char *CTEXT(int n) if (!(Sexp_nodes[arg_n].flags & SNF_CHECKED_ARG_FOR_VAR)) { - Sexp_nodes[arg_n].cached_variable_index = check_sexp_node_text_for_sexp_variable(text); + // nodes that have an officially formatted variable will store the variable index in the token + if (Sexp_nodes[arg_n].type & SEXP_FLAG_VARIABLE) + Sexp_nodes[arg_n].cached_variable_index = atoi(text); + else + Sexp_nodes[arg_n].cached_variable_index = check_sexp_node_text_for_sexp_variable(text); + Sexp_nodes[arg_n].flags |= SNF_CHECKED_ARG_FOR_VAR; } From 859978fd84d22d3bfa9b80f6944ce646aff861d4 Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Thu, 27 Jul 2023 03:15:21 -0400 Subject: [PATCH 04/10] upgrade container parsing as well; and check for malformed variable tokens --- code/parse/sexp.cpp | 88 ++++++++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index 0d4f5231ba1..3142597cd03 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -4135,17 +4135,27 @@ bool get_unformatted_sexp_variable_name(char *unformatted, const char *formatted // first character should be @ if (*formatted != SEXP_VARIABLE_CHAR) return false; - formatted++; - // get variable name (up to '[') - auto l_bracket = strchr(formatted, '['); + auto intermediate = formatted + 1; + size_t n; - auto n = (l_bracket != nullptr) ? (l_bracket - formatted) : strlen(formatted); + // get variable name (up to '[') + auto l_bracket = strchr(intermediate, '['); + if (l_bracket) { + auto r_bracket = strchr(l_bracket, ']'); + if (!r_bracket) { + error_display(0, "Malformed variable token: %s", formatted); + return false; + } + n = l_bracket - intermediate; + } else { + n = strlen(intermediate); + } if (n >= TOKEN_LENGTH - 1) return false; - strncpy(unformatted, formatted, n); + strncpy(unformatted, intermediate, n); unformatted[n] = '\0'; return true; } @@ -4354,53 +4364,67 @@ int get_sexp() // Sexp container else if (*Mp == sexp_container::DELIM) { - Mp++; + size_t len = 0; + auto ch = Mp; + while (*ch != ')' && !is_white_space(*ch)) { + // end of string or end of file + if (*ch == '\0') { + error_display(0, "Unexpected end of sexp!"); + return -1; + } + ch++; + len++; + } - char container_name[sexp_container::NAME_MAX_LENGTH + 1]; + // token is too long? + if (len >= TOKEN_LENGTH - 1) { + error_display(0, "Token %s is too long. Needs to be %d characters or shorter.", token, TOKEN_LENGTH - 1); + return -1; + } - if (*Mp == sexp_container::DELIM) { - // container name - Mp++; + strncpy(token, Mp, len); + token[len] = 0; - // ' ' occurs if there are arguments after the container name - // ')' occurs if this is the SEXP's last argument - stuff_string(container_name, F_NAME, sizeof(container_name), " )"); - if (*Mp != ')') { - Mp++; - } + // bump past token + // (do it here because the container data path calls get_sexp()) + Mp += len; + + // container name + if (token[1] == sexp_container::DELIM) { + auto container_name = token + 2; if (get_sexp_container(container_name) == nullptr) { - Error(LOCATION, "Attempt to use unknown container '%s'", container_name); + error_display(0, "Attempt to use unknown container '%s'", token); return -1; } node = alloc_sexp(container_name, SEXP_ATOM, SEXP_ATOM_CONTAINER_NAME, -1, -1); - } else { - // container data - stuff_string(container_name, F_NAME, sizeof(container_name), sexp_container::DELIM_STR.c_str()); + } + // container data + else { + if (token[len - 1] != sexp_container::DELIM) { + error_display(0, "Malformed container data token: %s", token); + return -1; + } - // bump past closing '&' - Mp += 2; + char container_name[TOKEN_LENGTH]; + strcpy_s(container_name, token + 1); // skip first delimiter + container_name[len - 2] = 0; // truncate last delimiter if (get_sexp_container(container_name) == nullptr) { - Error(LOCATION, "Attempt to use data from unknown container '%s'", container_name); + error_display(0, "Attempt to use data from unknown container '%s'", token); return -1; } // advance to the container modifier, since we'll read them when calling get_sexp() below - while (*Mp != '(') { - // watch out for malformed input - if ('\n' == *Mp || '\0' == *Mp) { - break; - } - Mp++; - } + ignore_gray_space(); + // watch out for malformed input + if (*Mp != '(') + break; Mp++; node = alloc_sexp(container_name, SEXP_ATOM, SEXP_ATOM_CONTAINER_DATA, get_sexp(), -1); } - - ignore_white_space(); } // Sexp operator or number From 7fc75c0d6c61bb13d21b0f341a4b406aded1d85b Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Thu, 27 Jul 2023 15:04:38 -0400 Subject: [PATCH 05/10] more robust handling of Mp, especially after error --- code/parse/sexp.cpp | 59 +++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index 3142597cd03..303ec539586 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -4332,15 +4332,19 @@ int get_sexp() // Sexp string else if (*Mp == '\"') { + auto startp = Mp; auto len = strcspn(Mp + 1, "\""); // was closing quote not found? if (*(Mp + 1 + len) != '\"') { error_display(0, "Unexpected end of quoted string embedded in sexp!"); + Mp += (len + 1); // bump the right amount (closing quote not found) return -1; } + // bump past closing quote + Mp += (len + 2); // it could be a string variable - int sexp_var_index = check_string_for_sexp_variable(Mp + 1, len); + int sexp_var_index = check_string_for_sexp_variable(startp + 1, len); if (sexp_var_index >= 0) { get_sexp_text_for_variable(token, sexp_var_index); node = alloc_sexp(token, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_STRING, -1, -1); @@ -4348,47 +4352,42 @@ int get_sexp() // it's a regular string else { // token is too long? - if (len >= TOKEN_LENGTH - 1) { - error_display(0, "Token %s is too long. Needs to be %d characters or shorter.", Mp, TOKEN_LENGTH - 1); + if (len >= TOKEN_LENGTH) { + SCP_string long_token(startp + 1, len); + error_display(0, "Token is too long. Needs to be %d characters or shorter:\n%s", TOKEN_LENGTH - 1, long_token.c_str()); return -1; } - strncpy(token, Mp + 1, len); + strncpy(token, startp + 1, len); token[len] = 0; node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1); } - - // bump past closing \" - Mp += (len + 2); } // Sexp container else if (*Mp == sexp_container::DELIM) { + auto startp = Mp; size_t len = 0; - auto ch = Mp; - while (*ch != ')' && !is_white_space(*ch)) { + while (*Mp != ')' && !is_white_space(*Mp)) { // end of string or end of file - if (*ch == '\0') { + if (*Mp == '\0') { error_display(0, "Unexpected end of sexp!"); return -1; } - ch++; + Mp++; len++; } // token is too long? - if (len >= TOKEN_LENGTH - 1) { - error_display(0, "Token %s is too long. Needs to be %d characters or shorter.", token, TOKEN_LENGTH - 1); + if (len >= TOKEN_LENGTH) { + SCP_string long_token(startp, len); + error_display(0, "Token is too long. Needs to be %d characters or shorter:\n%s", TOKEN_LENGTH - 1, long_token.c_str()); return -1; } - strncpy(token, Mp, len); + strncpy(token, startp, len); token[len] = 0; - // bump past token - // (do it here because the container data path calls get_sexp()) - Mp += len; - // container name if (token[1] == sexp_container::DELIM) { auto container_name = token + 2; @@ -4429,20 +4428,20 @@ int get_sexp() // Sexp operator or number else { + auto startp = Mp; size_t len = 0; - auto ch = Mp; - while (*ch != ')' && !is_white_space(*ch)) { + while (*Mp != ')' && !is_white_space(*Mp)) { // end of string or end of file - if (*ch == '\0') { + if (*Mp == '\0') { error_display(0, "Unexpected end of sexp!"); return -1; } - ch++; + Mp++; len++; } // it could be a numeric variable - int sexp_var_index = check_string_for_sexp_variable(Mp, len); + int sexp_var_index = check_string_for_sexp_variable(startp, len); if (sexp_var_index >= 0) { get_sexp_text_for_variable(token, sexp_var_index); node = alloc_sexp(token, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_NUMBER, -1, -1); @@ -4450,12 +4449,13 @@ int get_sexp() // it could be an operator else { // token is too long? - if (len >= TOKEN_LENGTH - 1) { - error_display(0, "Token %s is too long. Needs to be %d characters or shorter.", token, TOKEN_LENGTH - 1); + if (len >= TOKEN_LENGTH) { + SCP_string long_token(startp, len); + error_display(0, "Token is too long. Needs to be %d characters or shorter:\n%s", TOKEN_LENGTH - 1, long_token.c_str()); return -1; } - strncpy(token, Mp, len); + strncpy(token, startp, len); token[len] = 0; // maybe replace deprecated names @@ -4502,9 +4502,6 @@ int get_sexp() node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_NUMBER, -1, -1); } } - - // bump past token - Mp += len; } // update links @@ -12529,8 +12526,8 @@ void sexp_hud_set_directive(int n) auto text = CTEXT(CDR(n)); SCP_string message = message_translate_tokens(text); - if (message.size() > MESSAGE_LENGTH) { - WarningEx(LOCATION, "Message %s is too long for use in a HUD gauge. Please shorten it to %d characters or less.", message.c_str(), MESSAGE_LENGTH); + if (message.size() >= MESSAGE_LENGTH) { + WarningEx(LOCATION, "Message %s is too long for use in a HUD gauge. Please shorten it to %d characters or less.", message.c_str(), MESSAGE_LENGTH - 1); return; } From da34e25bc30339dc69eed8269d81e97b6c4c8982 Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Thu, 27 Jul 2023 16:07:15 -0400 Subject: [PATCH 06/10] handle things properly on parse error --- code/parse/sexp.cpp | 62 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index 303ec539586..22bcb4414ef 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -4296,6 +4296,30 @@ void localize_sexp(int text_node, int id_node) lcl_ext_localize(xstr.c_str(), Sexp_nodes[text_node].text, TOKEN_LENGTH - 1); } +// Advance to and consume the closing parenthesis of a sexp, in case of a parse error. +// If a closing parenthesis is not found, this advances to the end of the string. +void skip_sexp() +{ + int level = 1; + + while (*Mp != '\0') + { + if (*Mp == ')') + { + level--; + if (level == 0) + { + Mp++; + return; + } + } + else if (*Mp == '(') + level++; + + Mp++; + } +} + /** * Returns the first sexp index of data this function allocates. (start of this sexp) * @@ -4321,7 +4345,7 @@ int get_sexp() // end of string or end of file if (*Mp == '\0') { error_display(0, "Unexpected end of sexp!"); - return -1; + return Locked_sexp_false; } // Sexp list @@ -4337,8 +4361,8 @@ int get_sexp() // was closing quote not found? if (*(Mp + 1 + len) != '\"') { error_display(0, "Unexpected end of quoted string embedded in sexp!"); - Mp += (len + 1); // bump the right amount (closing quote not found) - return -1; + skip_sexp(); + return Locked_sexp_false; } // bump past closing quote Mp += (len + 2); @@ -4355,7 +4379,8 @@ int get_sexp() if (len >= TOKEN_LENGTH) { SCP_string long_token(startp + 1, len); error_display(0, "Token is too long. Needs to be %d characters or shorter:\n%s", TOKEN_LENGTH - 1, long_token.c_str()); - return -1; + // here we can just truncate; we don't need to return + len = TOKEN_LENGTH - 1; } strncpy(token, startp + 1, len); @@ -4372,7 +4397,12 @@ int get_sexp() // end of string or end of file if (*Mp == '\0') { error_display(0, "Unexpected end of sexp!"); - return -1; + return Locked_sexp_false; + } + // bad format + if (*Mp == '(') { + error_display(1, "Mismatched parentheses while parsing SEXP! Current parse position:\n%s", Mp); + return Locked_sexp_false; } Mp++; len++; @@ -4382,7 +4412,8 @@ int get_sexp() if (len >= TOKEN_LENGTH) { SCP_string long_token(startp, len); error_display(0, "Token is too long. Needs to be %d characters or shorter:\n%s", TOKEN_LENGTH - 1, long_token.c_str()); - return -1; + skip_sexp(); + return Locked_sexp_false; } strncpy(token, startp, len); @@ -4394,7 +4425,8 @@ int get_sexp() if (get_sexp_container(container_name) == nullptr) { error_display(0, "Attempt to use unknown container '%s'", token); - return -1; + skip_sexp(); + return Locked_sexp_false; } node = alloc_sexp(container_name, SEXP_ATOM, SEXP_ATOM_CONTAINER_NAME, -1, -1); @@ -4403,7 +4435,8 @@ int get_sexp() else { if (token[len - 1] != sexp_container::DELIM) { error_display(0, "Malformed container data token: %s", token); - return -1; + skip_sexp(); + return Locked_sexp_false; } char container_name[TOKEN_LENGTH]; @@ -4412,7 +4445,8 @@ int get_sexp() if (get_sexp_container(container_name) == nullptr) { error_display(0, "Attempt to use data from unknown container '%s'", token); - return -1; + skip_sexp(); + return Locked_sexp_false; } // advance to the container modifier, since we'll read them when calling get_sexp() below @@ -4434,7 +4468,12 @@ int get_sexp() // end of string or end of file if (*Mp == '\0') { error_display(0, "Unexpected end of sexp!"); - return -1; + return Locked_sexp_false; + } + // bad format + if (*Mp == '(') { + error_display(1, "Mismatched parentheses while parsing SEXP! Current parse position:\n%s", Mp); + return Locked_sexp_false; } Mp++; len++; @@ -4452,7 +4491,8 @@ int get_sexp() if (len >= TOKEN_LENGTH) { SCP_string long_token(startp, len); error_display(0, "Token is too long. Needs to be %d characters or shorter:\n%s", TOKEN_LENGTH - 1, long_token.c_str()); - return -1; + skip_sexp(); + return Locked_sexp_false; } strncpy(token, startp, len); From b3970798e197ddcaf536df7ff392e905c4c43201 Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Thu, 27 Jul 2023 16:14:06 -0400 Subject: [PATCH 07/10] variables will only be referenced in nodes, not generators like for-counter --- code/parse/sexp.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index 22bcb4414ef..4f895be5832 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -33436,11 +33436,6 @@ const char *CTEXT(int n) sexp_variable_index = Sexp_nodes[arg_n].cached_variable_index; } - // just check the text of the argument for a variable - else - { - sexp_variable_index = check_sexp_node_text_for_sexp_variable(text); - } // if we have a variable, return the variable value, else return the regular argument if (sexp_variable_index >= 0) From feadc0bd69528ab4a0a6c57680638c8868b505f7 Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Fri, 28 Jul 2023 16:45:44 -0400 Subject: [PATCH 08/10] feedback --- code/parse/parselo.cpp | 18 ++++++++++++++++++ code/parse/parselo.h | 3 +++ code/parse/sexp.cpp | 38 +++++++++++++++++++++----------------- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/code/parse/parselo.cpp b/code/parse/parselo.cpp index 649e5d86334..ef22ef82c66 100644 --- a/code/parse/parselo.cpp +++ b/code/parse/parselo.cpp @@ -4100,6 +4100,24 @@ void consolidate_double_characters(char *src, char ch) } } +char *three_dot_truncate(char *buffer, const char *source, size_t buffer_size) +{ + Assertion(buffer && source, "Arguments must not be null!"); + + // this would be silly + if (buffer_size < 6) + { + *buffer = '\0'; + return buffer; + } + + strncpy(buffer, source, buffer_size); + if (buffer[buffer_size - 1] != '\0') + strcpy(&buffer[buffer_size - 6], "[...]"); + + return buffer; +} + // Goober5000 // Returns position of replacement, or a negative value if replacement failed: -1 if search string was not found, -2 if replacement would exceed max length, or -3 if any string argument is null // Note that the parameter here is max *length*, not max buffer size. Leave room for the null-terminator! diff --git a/code/parse/parselo.h b/code/parse/parselo.h index 0c449de3fb5..4e990f5bf1b 100644 --- a/code/parse/parselo.h +++ b/code/parse/parselo.h @@ -79,6 +79,9 @@ extern int get_index_of_first_hash_symbol(SCP_string &src, bool ignore_doubled_h extern void consolidate_double_characters(char *str, char ch); +// for limiting strings that may be very long; useful for dialog boxes +char *three_dot_truncate(char *buffer, const char *source, size_t buffer_size); + // white space extern int is_white_space(char ch); extern int is_white_space(unicode::codepoint_t cp); diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index 4f895be5832..3c351c09ac7 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -4156,7 +4156,7 @@ bool get_unformatted_sexp_variable_name(char *unformatted, const char *formatted return false; strncpy(unformatted, intermediate, n); - unformatted[n] = '\0'; + unformatted[n] = 0; return true; } @@ -4298,23 +4298,29 @@ void localize_sexp(int text_node, int id_node) // Advance to and consume the closing parenthesis of a sexp, in case of a parse error. // If a closing parenthesis is not found, this advances to the end of the string. -void skip_sexp() +void skip_sexp(bool within_quotes = false) { int level = 1; while (*Mp != '\0') { - if (*Mp == ')') + if (*Mp == '\"') + within_quotes = !within_quotes; + + if (!within_quotes) { - level--; - if (level == 0) + if (*Mp == ')') { - Mp++; - return; + level--; + if (level == 0) + { + Mp++; + return; + } } + else if (*Mp == '(') + level++; } - else if (*Mp == '(') - level++; Mp++; } @@ -4361,7 +4367,7 @@ int get_sexp() // was closing quote not found? if (*(Mp + 1 + len) != '\"') { error_display(0, "Unexpected end of quoted string embedded in sexp!"); - skip_sexp(); + skip_sexp(true); return Locked_sexp_false; } // bump past closing quote @@ -4401,7 +4407,8 @@ int get_sexp() } // bad format if (*Mp == '(') { - error_display(1, "Mismatched parentheses while parsing SEXP! Current parse position:\n%s", Mp); + char buf[512]; + error_display(1, "Mismatched parentheses while parsing SEXP! Current parse position:\n%s", three_dot_truncate(buf, Mp, 512)); return Locked_sexp_false; } Mp++; @@ -4472,7 +4479,8 @@ int get_sexp() } // bad format if (*Mp == '(') { - error_display(1, "Mismatched parentheses while parsing SEXP! Current parse position:\n%s", Mp); + char buf[512]; + error_display(1, "Mismatched parentheses while parsing SEXP! Current parse position:\n%s", three_dot_truncate(buf, Mp, 512)); return Locked_sexp_false; } Mp++; @@ -29160,11 +29168,7 @@ int get_sexp_main() if (*Mp != '(') { char buf[512]; - strncpy(buf, Mp, 512); - if (buf[511] != '\0') - strcpy(&buf[506], "[...]"); - - error_display(0, "Expected to find an open parenthesis in the following sexp:\n%s", buf); + error_display(0, "Expected to find an open parenthesis in the following sexp:\n%s", three_dot_truncate(buf, Mp, 512)); return -1; } From f64de84e7c7a739a6a3d601415e26aea5159d587 Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Fri, 28 Jul 2023 17:08:22 -0400 Subject: [PATCH 09/10] address feedback --- code/parse/sexp.cpp | 28 +++++++++++++++++++--------- fred2/freddoc.cpp | 8 +++++++- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index 3c351c09ac7..3b47fd7e1db 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -35574,6 +35574,16 @@ bool usable_in_campaign(int op_id) } } +// For tokenizing in SEXP help +#define MAX_SEXP_VARIABLES_1 249 +#define TOKEN_LENGTH_1 31 +#if (MAX_SEXP_VARIABLES_1) != (MAX_SEXP_VARIABLES - 1) +#error MAX_SEXP_VARIABLES_1 must be equal to MAX_SEXP_VARIABLES - 1! +#endif +#if (TOKEN_LENGTH_1) != (TOKEN_LENGTH - 1) +#error TOKEN_LENGTH_1 must be equal to TOKEN_LENGTH - 1! +#endif + // clang-format off SCP_vector Sexp_help = { { OP_PLUS, "Plus (Arithmetic operator)\r\n" @@ -36733,7 +36743,7 @@ SCP_vector Sexp_help = { "arrays and pointers.\r\n\r\nPlease note that only numeric variables are supported. Any " "attempt to access a string variable will result in a value of SEXP_NAN_FOREVER being returned.\r\n\r\n" "Takes 1 argument...\r\n" - "\t1:\tIndex of variable, from 0 to " SCP_TOKEN_TO_STR(MAX_SEXP_VARIABLES) " - 1." }, + "\t1:\tIndex of variable, from 0 to " SCP_TOKEN_TO_STR(MAX_SEXP_VARIABLES_1) "." }, { OP_SET_VARIABLE_BY_INDEX, "set-variable-by-index (originally variable-array-set)\r\n" "\tSets the value of the variable specified by the given index. This is an alternate way " @@ -36741,7 +36751,7 @@ SCP_vector Sexp_help = { "arrays and pointers.\r\n\r\nIn contrast to get-variable-by-index, note that this sexp " "*does* allow the modification of string variables.\r\n\r\n" "Takes 2 arguments...\r\n" - "\t1:\tIndex of variable, from 0 to " SCP_TOKEN_TO_STR(MAX_SEXP_VARIABLES) " - 1.\r\n" + "\t1:\tIndex of variable, from 0 to " SCP_TOKEN_TO_STR(MAX_SEXP_VARIABLES_1) ".\r\n" "\t2:\tValue to be set." }, { OP_COPY_VARIABLE_FROM_INDEX, "copy-variable-from-index\r\n" @@ -36749,14 +36759,14 @@ SCP_vector Sexp_help = { "This is very similar to get-variable-by-index, except the result is stored in a new variable rather than " "being returned by value. One important difference is that this sexp can be used to copy string variables as well as numeric variables.\r\n\r\n" "Takes 2 arguments...\r\n" - "\t1:\tIndex of source variable, from 0 to " SCP_TOKEN_TO_STR(MAX_SEXP_VARIABLES) " - 1.\r\n" + "\t1:\tIndex of source variable, from 0 to " SCP_TOKEN_TO_STR(MAX_SEXP_VARIABLES_1) ".\r\n" "\t2:\tDestination variable. The type of this variable must match the type of the variable referenced by the index." }, { OP_COPY_VARIABLE_BETWEEN_INDEXES, "copy-variable-between-indexes\r\n" "\tRetrieves the value of the variable specified by the first index and stores it in the variable specified by the second index. The first variable is not modified.\r\n\r\n" "Takes 2 arguments...\r\n" - "\t1:\tIndex of source variable, from 0 to " SCP_TOKEN_TO_STR(MAX_SEXP_VARIABLES) " - 1.\r\n" - "\t2:\tIndex of destination variable, from 0 to " SCP_TOKEN_TO_STR(MAX_SEXP_VARIABLES) " - 1. The types of both variables must match." }, + "\t1:\tIndex of source variable, from 0 to " SCP_TOKEN_TO_STR(MAX_SEXP_VARIABLES_1) ".\r\n" + "\t2:\tIndex of destination variable, from 0 to " SCP_TOKEN_TO_STR(MAX_SEXP_VARIABLES_1) ". The types of both variables must match." }, // Karajorma/jg18 { OP_CONTAINER_ADD_TO_LIST, "add-to-list\r\n" @@ -37573,7 +37583,7 @@ SCP_vector Sexp_help = { // Goober5000 { OP_STRING_CONCATENATE, "string-concatenate (deprecated in favor of string-concatenate-block)\r\n" "\tConcatenates two strings, putting the result into a string variable. If the length of the string will " - "exceed the sexp variable token limit (" SCP_TOKEN_TO_STR(TOKEN_LENGTH) " - 1), it will be truncated.\r\n\r\n" + "exceed the sexp variable token limit (" SCP_TOKEN_TO_STR(TOKEN_LENGTH_1) "), it will be truncated.\r\n\r\n" "Takes 3 arguments...\r\n" "\t1: First string\r\n" "\t2: Second string\r\n" @@ -37582,7 +37592,7 @@ SCP_vector Sexp_help = { // Goober5000 { OP_STRING_CONCATENATE_BLOCK, "string-concatenate-block\r\n" "\tConcatenates two or more strings, putting the result into a string variable. If the length of the string will " - "exceed the sexp variable token limit (" SCP_TOKEN_TO_STR(TOKEN_LENGTH) " - 1), it will be truncated.\r\n\r\n" + "exceed the sexp variable token limit (" SCP_TOKEN_TO_STR(TOKEN_LENGTH_1) "), it will be truncated.\r\n\r\n" "Takes 3 or more arguments...\r\n" "\t1: String variable to hold the result\r\n" "\tRest: Strings to concatenate. At least two of these are required; the rest are optional.\r\n" }, @@ -37590,7 +37600,7 @@ SCP_vector Sexp_help = { // Goober5000 { OP_STRING_GET_SUBSTRING, "string-get-substring\r\n" "\tExtracts a substring from a parent string, putting the result into a string variable. If the length of the string will " - "exceed the sexp variable token limit (" SCP_TOKEN_TO_STR(TOKEN_LENGTH) " - 1), it will be truncated.\r\n\r\n" + "exceed the sexp variable token limit (" SCP_TOKEN_TO_STR(TOKEN_LENGTH_1) "), it will be truncated.\r\n\r\n" "Takes 3 arguments...\r\n" "\t1: Parent string\r\n" "\t2: Index at which the substring begins (0-based)\r\n" @@ -37600,7 +37610,7 @@ SCP_vector Sexp_help = { // Goober5000 { OP_STRING_SET_SUBSTRING, "string-set-substring\r\n" "\tReplaces a substring from a parent string with a new string, putting the result into a string variable. If the length of the string will " - "exceed the sexp variable token limit (" SCP_TOKEN_TO_STR(TOKEN_LENGTH) " - 1), it will be truncated.\r\n\r\n" + "exceed the sexp variable token limit (" SCP_TOKEN_TO_STR(TOKEN_LENGTH_1) "), it will be truncated.\r\n\r\n" "Takes 3 arguments...\r\n" "\t1: Parent string\r\n" "\t2: Index at which the substring begins (0-based)\r\n" diff --git a/fred2/freddoc.cpp b/fred2/freddoc.cpp index dcd2d5720c1..522bb50ff78 100644 --- a/fred2/freddoc.cpp +++ b/fred2/freddoc.cpp @@ -581,6 +581,12 @@ BOOL CFREDDoc::OnOpenDocument(LPCTSTR pathname) { return TRUE; } +// For tokenizing +#define MAX_FILENAME_LEN_1 31 +#if (MAX_FILENAME_LEN_1) != (MAX_FILENAME_LEN - 1) +#error MAX_FILENAME_LEN_1 must be equal to MAX_FILENAME_LEN - 1! +#endif + BOOL CFREDDoc::OnSaveDocument(LPCTSTR pathname) { CFred_mission_save save; DWORD attrib; @@ -591,7 +597,7 @@ BOOL CFREDDoc::OnSaveDocument(LPCTSTR pathname) { auto len = strlen(filename); if (len >= MAX_FILENAME_LEN) - Fred_main_wnd->MessageBox("The filename is too long for FreeSpace. The game will not be able to read this file. Max length, including extension, is " SCP_TOKEN_TO_STR(MAX_FILENAME_LEN-1) " characters.", NULL, MB_OK | MB_ICONEXCLAMATION); + Fred_main_wnd->MessageBox("The filename is too long for FreeSpace. The game will not be able to read this file. Max length, including extension, is " SCP_TOKEN_TO_STR(MAX_FILENAME_LEN_1) " characters.", NULL, MB_OK | MB_ICONEXCLAMATION); // drop extension and copy to Mission_filename auto ext_ch = strrchr(filename, '.'); From a21876127d554fe6af22b7ac5a0225ac67dcb7ae Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Fri, 28 Jul 2023 19:54:42 -0400 Subject: [PATCH 10/10] comment --- code/parse/sexp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index 3b47fd7e1db..063e3c60f5c 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -4367,7 +4367,7 @@ int get_sexp() // was closing quote not found? if (*(Mp + 1 + len) != '\"') { error_display(0, "Unexpected end of quoted string embedded in sexp!"); - skip_sexp(true); + skip_sexp(true); // this will have the effect of skipping to the end of the file or string return Locked_sexp_false; } // bump past closing quote