1 /* 2 * Copyright 2010 Intel Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 */ 23 24 #include <assert.h> 25 #include <string.h> 26 #include <ctype.h> 27 #include "glcpp.h" 28 29 void 30 glcpp_error (YYLTYPE *locp, glcpp_parser_t *parser, const char *fmt, ...) 31 { 32 va_list ap; 33 34 parser->error = 1; 35 ralloc_asprintf_rewrite_tail(&parser->info_log, 36 &parser->info_log_length, 37 "%u:%u(%u): " 38 "preprocessor error: ", 39 locp->source, 40 locp->first_line, 41 locp->first_column); 42 va_start(ap, fmt); 43 ralloc_vasprintf_rewrite_tail(&parser->info_log, 44 &parser->info_log_length, 45 fmt, ap); 46 va_end(ap); 47 ralloc_asprintf_rewrite_tail(&parser->info_log, 48 &parser->info_log_length, "\n"); 49 } 50 51 void 52 glcpp_warning (YYLTYPE *locp, glcpp_parser_t *parser, const char *fmt, ...) 53 { 54 va_list ap; 55 56 ralloc_asprintf_rewrite_tail(&parser->info_log, 57 &parser->info_log_length, 58 "%u:%u(%u): " 59 "preprocessor warning: ", 60 locp->source, 61 locp->first_line, 62 locp->first_column); 63 va_start(ap, fmt); 64 ralloc_vasprintf_rewrite_tail(&parser->info_log, 65 &parser->info_log_length, 66 fmt, ap); 67 va_end(ap); 68 ralloc_asprintf_rewrite_tail(&parser->info_log, 69 &parser->info_log_length, "\n"); 70 } 71 72 /* Given str, (that's expected to start with a newline terminator of some 73 * sort), return a pointer to the first character in str after the newline. 74 * 75 * A newline terminator can be any of the following sequences: 76 * 77 * "\r\n" 78 * "\n\r" 79 * "\n" 80 * "\r" 81 * 82 * And the longest such sequence will be skipped. 83 */ 84 static const char * 85 skip_newline (const char *str) 86 { 87 const char *ret = str; 88 89 if (ret == NULL) 90 return ret; 91 92 if (*ret == '\0') 93 return ret; 94 95 if (*ret == '\r') { 96 ret++; 97 if (*ret && *ret == '\n') 98 ret++; 99 } else if (*ret == '\n') { 100 ret++; 101 if (*ret && *ret == '\r') 102 ret++; 103 } 104 105 return ret; 106 } 107 108 /* Remove any line continuation characters in the shader, (whether in 109 * preprocessing directives or in GLSL code). 110 */ 111 static char * 112 remove_line_continuations(glcpp_parser_t *ctx, const char *shader) 113 { 114 char *clean = ralloc_strdup(ctx, ""); 115 const char *backslash, *newline, *search_start; 116 const char *cr, *lf; 117 char newline_separator[3]; 118 int collapsed_newlines = 0; 119 120 search_start = shader; 121 122 /* Determine what flavor of newlines this shader is using. GLSL 123 * provides for 4 different possible ways to separate lines, (using 124 * one or two characters): 125 * 126 * "\n" (line-feed, like Linux, Unix, and new Mac OS) 127 * "\r" (carriage-return, like old Mac files) 128 * "\r\n" (carriage-return + line-feed, like DOS files) 129 * "\n\r" (line-feed + carriage-return, like nothing, really) 130 * 131 * This code explicitly supports a shader that uses a mixture of 132 * newline terminators and will properly handle line continuation 133 * backslashes followed by any of the above. 134 * 135 * But, since we must also insert additional newlines in the output 136 * (for any collapsed lines) we attempt to maintain consistency by 137 * examining the first encountered newline terminator, and using the 138 * same terminator for any newlines we insert. 139 */ 140 cr = strchr(search_start, '\r'); 141 lf = strchr(search_start, '\n'); 142 143 newline_separator[0] = '\n'; 144 newline_separator[1] = '\0'; 145 newline_separator[2] = '\0'; 146 147 if (cr == NULL) { 148 /* Nothing to do. */ 149 } else if (lf == NULL) { 150 newline_separator[0] = '\r'; 151 } else if (lf == cr + 1) { 152 newline_separator[0] = '\r'; 153 newline_separator[1] = '\n'; 154 } else if (cr == lf + 1) { 155 newline_separator[0] = '\n'; 156 newline_separator[1] = '\r'; 157 } 158 159 while (true) { 160 backslash = strchr(search_start, '\\'); 161 162 /* If we have previously collapsed any line-continuations, 163 * then we want to insert additional newlines at the next 164 * occurrence of a newline character to avoid changing any 165 * line numbers. 166 */ 167 if (collapsed_newlines) { 168 cr = strchr (search_start, '\r'); 169 lf = strchr (search_start, '\n'); 170 if (cr && lf) 171 newline = cr < lf ? cr : lf; 172 else if (cr) 173 newline = cr; 174 else 175 newline = lf; 176 if (newline && 177 (backslash == NULL || newline < backslash)) 178 { 179 ralloc_strncat(&clean, shader, 180 newline - shader + 1); 181 while (collapsed_newlines) { 182 ralloc_strcat(&clean, newline_separator); 183 collapsed_newlines--; 184 } 185 shader = skip_newline (newline); 186 search_start = shader; 187 } 188 } 189 190 search_start = backslash + 1; 191 192 if (backslash == NULL) 193 break; 194 195 /* At each line continuation, (backslash followed by a 196 * newline), copy all preceding text to the output, then 197 * advance the shader pointer to the character after the 198 * newline. 199 */ 200 if (backslash[1] == '\r' || backslash[1] == '\n') 201 { 202 collapsed_newlines++; 203 ralloc_strncat(&clean, shader, backslash - shader); 204 shader = skip_newline (backslash + 1); 205 search_start = shader; 206 } 207 } 208 209 ralloc_strcat(&clean, shader); 210 211 return clean; 212 } 213 214 int 215 glcpp_preprocess(void *ralloc_ctx, const char **shader, char **info_log, 216 glcpp_extension_iterator extensions, void *state, 217 struct gl_context *gl_ctx) 218 { 219 int errors; 220 glcpp_parser_t *parser = 221 glcpp_parser_create(extensions, state, gl_ctx->API); 222 223 if (! gl_ctx->Const.DisableGLSLLineContinuations) 224 *shader = remove_line_continuations(parser, *shader); 225 226 glcpp_lex_set_source_string (parser, *shader); 227 228 glcpp_parser_parse (parser); 229 230 if (parser->skip_stack) 231 glcpp_error (&parser->skip_stack->loc, parser, "Unterminated #if\n"); 232 233 glcpp_parser_resolve_implicit_version(parser); 234 235 ralloc_strcat(info_log, parser->info_log); 236 237 ralloc_steal(ralloc_ctx, parser->output); 238 *shader = parser->output; 239 240 errors = parser->error; 241 glcpp_parser_destroy (parser); 242 return errors; 243 } 244