New method of handling quotes and comments that examines lines character by character like most languages do, instead of just searching for the start of a comment. If a comment character is inside a quoted string, it will not kick off a new comment. (This actually fixes the core design flaw that caused semicolons to truncate messages, so the $semicolon tag is technically no longer necessary. It will still be kept, of course.) Fixes Mantis #3001.

git-svn-id: svn://svn.icculus.org/fs2open/trunk/fs2_open@10492 387891d4-d844-0410-90c0-e4c51a9137d3
This commit is contained in:
Goober5000 authored and niffiwan committed 2014-03-14 19:50:32 +10:00
1 parent 0eb5606527
commit ff675c3653
1 file changed
+183 -197
+183 -197
View File
@@ -1642,239 +1642,224 @@ void maybe_convert_foreign_characters(SCP_string &line)
}
// Goober5000
int get_number_before_separator(char *text, char separator)
bool get_number_before_separator(int &number, int &number_chars, const char *text, char separator)
{
char buf[10];
char *ch;
char buf[8];
const char *ch = text;
int len = 0;
memset(buf, 0, 10);
strncpy(buf, text, 9);
while (true)
{
// didn't find separator
if (*ch == '\0' || len == 8)
return false;
ch = strchr(buf, separator);
if (ch == NULL)
return 0;
*ch = '\0';
// found separator
if (*ch == separator)
break;
return atoi(buf);
// found nondigit
if (!isdigit(*ch))
return false;
// copying in progress
buf[len] = *ch;
len++;
ch++;
}
// got an integer
buf[len] = '\0';
number = atoi(buf);
number_chars = len;
return true;
}
// Goober5000
int get_number_before_separator(SCP_string &text, char separator)
bool get_number_before_separator(int &number, int &number_chars, const SCP_string &text, SCP_string::iterator text_pos, char separator)
{
char buf[10];
char *ch;
char buf[8];
SCP_string::iterator ch = text_pos;
int len = 0;
memset(buf, 0, 10);
text.copy(buf, 9);
while (true)
{
// didn't find separator
if (ch == text.end() || len == 8)
return false;
ch = strchr(buf, separator);
if (ch == NULL)
return 0;
*ch = '\0';
// found separator
if (*ch == separator)
break;
return atoi(buf);
// found nondigit
if (!isdigit(*ch))
return false;
// copying in progress
buf[len] = *ch;
len++;
ch++;
}
// got an integer
buf[len] = '\0';
number = atoi(buf);
number_chars = len;
return true;
}
bool matches_version_specific_tag(const char *line_start, bool &compatible_version, int &tag_len)
{
// special version-specific comment
// formatted like e.g. ;;FSO 3.7.0;;
if (strnicmp(line_start, ";;FSO ", 6))
return false;
int major, minor, build, num_len;
const char *ch;
ch = line_start + 6;
if (!get_number_before_separator(major, num_len, ch, '.'))
return false;
ch += (num_len + 1);
if (!get_number_before_separator(minor, num_len, ch, '.'))
return false;
ch += (num_len + 1);
if (!get_number_before_separator(build, num_len, ch, ';'))
return false;
ch += (num_len + 1);
if (*ch != ';')
return false;
ch++;
// tag is a match!
tag_len = ch - line_start;
compatible_version = true;
// check whether major, minor, and build line up with this version
if (major > FS_VERSION_MAJOR)
{
compatible_version = false;
}
else if (major == FS_VERSION_MAJOR)
{
if (minor > FS_VERSION_MINOR)
{
compatible_version = false;
}
else if (minor == FS_VERSION_MINOR)
{
if (build > FS_VERSION_BUILD)
{
compatible_version = false;
}
}
}
// true for tag match
return true;
}
// Strip comments from a line of input.
// Goober5000 - rewritten to make a lot more sense and to be extensible
int strip_comments(char *line, int in_multiline_comment)
// Goober5000 - rewritten for the second time
void strip_comments(char *line, bool &in_multiline_comment_a, bool &in_multiline_comment_b)
{
char *ch;
bool in_quote = false;
char *writep = line;
char *readp = line;
// if we're in a comment, see if we can close it
if (in_multiline_comment)
// copy all characters from read to write, unless they're commented
while (*readp != '\r' && *readp != '\n' && *readp != '\0')
{
ch = strstr(line, "*/");
if (ch == NULL)
ch = strstr(line, "*!");
if (ch != NULL)
// only check for comments if not quoting
if (!in_quote)
{
char *writep = line;
char *readp = ch + 2;
bool compatible_version;
int tag_len;
// copy all characters past the close of the comment
while (*readp != '\0')
// see what sort of comment characters we recognize
if (!strncmp(readp, "/*", 2))
{
*writep = *readp;
writep++;
readp++;
// comment styles are mutually exclusive
if (!in_multiline_comment_b)
in_multiline_comment_a = true;
}
*writep = '\0';
// recurse with the other characters
return strip_comments(line, 0);
}
// can't close it, so drop the whole line
ch = line;
goto done_with_line;
}
/* Goober5000 - this interferes with hyperlinks, heh
// search for //
ch = strstr(line, "//");
if (ch != NULL)
goto done_with_line;
*/
// special version-specific comment
// formatted like e.g. ;;FSO 3.7.0;;
ch = stristr(line, ";;FSO ");
if (ch != NULL)
{
int major, minor, build;
char *numch, *sep, *linech;
numch = ch + 6;
sep = strchr(numch, '.');
if (sep == NULL)
goto done_with_line;
major = get_number_before_separator(numch, '.');
numch = sep + 1;
sep = strchr(numch, '.');
if (sep == NULL)
goto done_with_line;
minor = get_number_before_separator(numch, '.');
numch = sep + 1;
sep = strchr(numch, ';');
if (sep == NULL)
goto done_with_line;
build = get_number_before_separator(numch, ';');
if (*(sep + 1) != ';')
goto done_with_line;
linech = sep + 2;
// check whether major, minor, and build line up with this version
if (major > FS_VERSION_MAJOR)
{
goto done_with_line;
}
else if (major == FS_VERSION_MAJOR)
{
if (minor > FS_VERSION_MINOR)
else if (!strncmp(readp, "!*", 2))
{
goto done_with_line;
// comment styles are mutually exclusive
if (!in_multiline_comment_a)
in_multiline_comment_b = true;
}
else if (minor == FS_VERSION_MINOR)
else if (!strncmp(readp, "*/", 2))
{
if (build > FS_VERSION_BUILD)
if (in_multiline_comment_a)
{
goto done_with_line;
in_multiline_comment_a = false;
readp += 2;
continue;
}
}
else if (!strncmp(readp, "*!", 2))
{
if (in_multiline_comment_b)
{
in_multiline_comment_b = false;
readp += 2;
continue;
}
}
// special version-specific comment
// formatted like e.g. ;;FSO 3.7.0;;
else if (matches_version_specific_tag(readp, compatible_version, tag_len))
{
// comment passes, so advance pass the tag and keep reading
if (compatible_version)
{
readp += tag_len;
continue;
}
// comment does not pass, so ignore the line
else
{
break;
}
}
// standard comment
else if (*readp == ';')
{
break;
}
}
// maybe toggle quoting
if (*readp == '\"')
in_quote = !in_quote;
// this version is compatible, so copy the line past the tag
// if not inside a comment, copy the characters
if (!in_multiline_comment_a && !in_multiline_comment_b)
{
char *writep = ch;
char *readp = linech;
// copy all characters past the close of the comment
while (*readp != '\0')
{
if (writep != readp)
*writep = *readp;
writep++;
readp++;
}
*writep = '\0';
// recurse with the other characters
return strip_comments(ch, 0);
}
}
// search for ;
ch = strchr(line, ';');
if (ch != NULL)
goto done_with_line;
// start of a multi-line comment?
// (You can now use !* *! in addition to /* */ because prior to 3.7.1, a /* would be flagged
// even if it appeared after an initial ; such as in a version-specific comment.
ch = strstr(line, "/*");
if (ch == NULL)
ch = strstr(line, "!*");
if (ch != NULL)
{
// treat it as the beginning of a new line and recurse
return strip_comments(ch, 1);
}
// no comments found... try to find the newline
ch = strchr(line, '\n');
if (ch != NULL)
goto done_with_line;
// just skip to the end of the line
ch = line + strlen(line);
done_with_line:
ch[0] = EOLN;
ch[1] = 0;
return in_multiline_comment;
}
/*#if 0
void strip_all_comments( char *readp, char *writep )
{
int ch;
//char *writep = readp;
while ( *readp != EOF_CHAR ) {
ch = *readp;
if ( ch == COMMENT_CHAR ) {
while ( *readp != EOLN )
readp++;
*writep = EOLN;
writep++;
// get to next character after EOLN
readp++;
} else if ( (ch == '/') && (readp[1] == '*')) { // Start of multi-line comment
int done;
done = 0;
while ( !done ) {
while ( *readp != '*' )
readp++;
if ( readp[1] == '/' ) {
readp += 2;
done = 1;
} else {
readp++;
}
}
} else {
*writep = (char)ch;
*writep++;
readp++;
}
// read the next character
readp++;
}
*writep = EOF_CHAR;
// if we moved any characters, or if we haven't reached the end of the string, then mark end-of-line and terminate string
if (writep != readp || *readp != '\0')
{
writep[0] = EOLN;
writep[1] = '\0';
}
}
#endif*/
int parse_get_line(char *lineout, int max_line_len, char *start, int max_size, char *cur)
{
@@ -2112,7 +2097,8 @@ void process_raw_file_text(char *processed_text, char *raw_text)
char *mp;
char *mp_raw;
char outbuf[PARSE_BUF_SIZE], *str;
int in_multiline_comment = 0;
bool in_multiline_comment_a = false;
bool in_multiline_comment_b = false;
int raw_text_len = strlen(raw_text);
if (processed_text == NULL)
@@ -2132,7 +2118,7 @@ void process_raw_file_text(char *processed_text, char *raw_text)
while ( (num_chars_read = parse_get_line(outbuf, PARSE_BUF_SIZE, raw_text, raw_text_len, mp_raw)) != 0 ) {
mp_raw += num_chars_read;
in_multiline_comment = strip_comments(outbuf, in_multiline_comment);
strip_comments(outbuf, in_multiline_comment_a, in_multiline_comment_b);
maybe_convert_foreign_characters(outbuf);