From f1340745c00617bead8352d314306642138ed0bd Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Wed, 2 Jul 2014 22:49:58 -0700 Subject: [PATCH] glsl/glcpp: Fix line-continuation code to handle multiple newline flavors Sometimes the newline separator is a single character, and sometimes it is two characters. Before we can fold away and line-continuation backslashes, we identify the flavor of line separator that is in use. With this identified, we then correctly search for backslashes followed immediately by the first character of the line separator. Also, when re-inserting newlines to replace collapsed newlines, we carefully insert newlines of the same flavor. With this commit, almost all remaining test are fixed as tested by glcpp-test-cr-lf: \r: 142/143 tests pass \r\n: 142/143 tests pass \n\r: 143/143 tests pass (The only remaining failures have nothing to do with the actual pre-processor code, but are due to a bug in the way the test suite uses grep to try to extract test-specific command-line options from the source files.) Reviewed-by: Ian Romanick --- src/glsl/glcpp/pp.c | 96 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 9 deletions(-) diff --git a/src/glsl/glcpp/pp.c b/src/glsl/glcpp/pp.c index 4a623f81eb6..a54bcbe16d1 100644 --- a/src/glsl/glcpp/pp.c +++ b/src/glsl/glcpp/pp.c @@ -70,6 +70,42 @@ glcpp_warning (YYLTYPE *locp, glcpp_parser_t *parser, const char *fmt, ...) &parser->info_log_length, "\n"); } +/* Given str, (that's expected to start with a newline terminator of some + * sort), return a pointer to the first character in str after the newline. + * + * A newline terminator can be any of the following sequences: + * + * "\r\n" + * "\n\r" + * "\n" + * "\r" + * + * And the longest such sequence will be skipped. + */ +static const char * +skip_newline (const char *str) +{ + const char *ret = str; + + if (ret == NULL) + return ret; + + if (*ret == '\0') + return ret; + + if (*ret == '\r') { + ret++; + if (*ret && *ret == '\n') + ret++; + } else if (*ret == '\n') { + ret++; + if (*ret && *ret == '\r') + ret++; + } + + return ret; +} + /* Remove any line continuation characters in the shader, (whether in * preprocessing directives or in GLSL code). */ @@ -78,10 +114,49 @@ remove_line_continuations(glcpp_parser_t *ctx, const char *shader) { char *clean = ralloc_strdup(ctx, ""); const char *backslash, *newline, *search_start; + const char *cr, *lf; + char newline_separator[3]; int collapsed_newlines = 0; search_start = shader; + /* Determine what flavor of newlines this shader is using. GLSL + * provides for 4 different possible ways to separate lines, (using + * one or two characters): + * + * "\n" (line-feed, like Linux, Unix, and new Mac OS) + * "\r" (carriage-return, like old Mac files) + * "\r\n" (carriage-return + line-feed, like DOS files) + * "\n\r" (line-feed + carriage-return, like nothing, really) + * + * This code explicitly supports a shader that uses a mixture of + * newline terminators and will properly handle line continuation + * backslashes followed by any of the above. + * + * But, since we must also insert additional newlines in the output + * (for any collapsed lines) we attempt to maintain consistency by + * examining the first encountered newline terminator, and using the + * same terminator for any newlines we insert. + */ + cr = strchr(search_start, '\r'); + lf = strchr(search_start, '\n'); + + newline_separator[0] = '\n'; + newline_separator[1] = '\0'; + newline_separator[2] = '\0'; + + if (cr == NULL) { + /* Nothing to do. */ + } else if (lf == NULL) { + newline_separator[0] = '\r'; + } else if (lf == cr + 1) { + newline_separator[0] = '\r'; + newline_separator[1] = '\n'; + } else if (cr == lf + 1) { + newline_separator[0] = '\n'; + newline_separator[1] = '\r'; + } + while (true) { backslash = strchr(search_start, '\\'); @@ -91,17 +166,24 @@ remove_line_continuations(glcpp_parser_t *ctx, const char *shader) * line numbers. */ if (collapsed_newlines) { - newline = strchr(search_start, '\n'); + cr = strchr (search_start, '\r'); + lf = strchr (search_start, '\n'); + if (cr && lf) + newline = cr < lf ? cr : lf; + else if (cr) + newline = cr; + else + newline = lf; if (newline && (backslash == NULL || newline < backslash)) { ralloc_strncat(&clean, shader, newline - shader + 1); while (collapsed_newlines) { - ralloc_strcat(&clean, "\n"); + ralloc_strcat(&clean, newline_separator); collapsed_newlines--; } - shader = newline + 1; + shader = skip_newline (newline); search_start = shader; } } @@ -116,15 +198,11 @@ remove_line_continuations(glcpp_parser_t *ctx, const char *shader) * advance the shader pointer to the character after the * newline. */ - if (backslash[1] == '\n' || - (backslash[1] == '\r' && backslash[2] == '\n')) + if (backslash[1] == '\r' || backslash[1] == '\n') { collapsed_newlines++; ralloc_strncat(&clean, shader, backslash - shader); - if (backslash[1] == '\n') - shader = backslash + 2; - else - shader = backslash + 3; + shader = skip_newline (backslash + 1); search_start = shader; } } -- 2.30.2