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 _mesa_string_buffer_printf(parser->info_log, 36 "%u:%u(%u): " 37 "preprocessor error: ", 38 locp->source, 39 locp->first_line, 40 locp->first_column); 41 va_start(ap, fmt); 42 _mesa_string_buffer_vprintf(parser->info_log, fmt, ap); 43 va_end(ap); 44 _mesa_string_buffer_append_char(parser->info_log, '\n'); 45 } 46 47 void 48 glcpp_warning (YYLTYPE *locp, glcpp_parser_t *parser, const char *fmt, ...) 49 { 50 va_list ap; 51 52 _mesa_string_buffer_printf(parser->info_log, 53 "%u:%u(%u): " 54 "preprocessor warning: ", 55 locp->source, 56 locp->first_line, 57 locp->first_column); 58 va_start(ap, fmt); 59 _mesa_string_buffer_vprintf(parser->info_log, fmt, ap); 60 va_end(ap); 61 _mesa_string_buffer_append_char(parser->info_log, '\n'); 62 } 63 64 /* Given str, (that's expected to start with a newline terminator of some 65 * sort), return a pointer to the first character in str after the newline. 66 * 67 * A newline terminator can be any of the following sequences: 68 * 69 * "\r\n" 70 * "\n\r" 71 * "\n" 72 * "\r" 73 * 74 * And the longest such sequence will be skipped. 75 */ 76 static const char * 77 skip_newline (const char *str) 78 { 79 const char *ret = str; 80 81 if (ret == NULL) 82 return ret; 83 84 if (*ret == '\0') 85 return ret; 86 87 if (*ret == '\r') { 88 ret++; 89 if (*ret && *ret == '\n') 90 ret++; 91 } else if (*ret == '\n') { 92 ret++; 93 if (*ret && *ret == '\r') 94 ret++; 95 } 96 97 return ret; 98 } 99 100 /* Initial output buffer size, 4096 minus ralloc() overhead. It was selected 101 * to minimize total amount of allocated memory during shader-db run. 102 */ 103 #define INITIAL_PP_OUTPUT_BUF_SIZE 4048 104 105 /* Remove any line continuation characters in the shader, (whether in 106 * preprocessing directives or in GLSL code). 107 */ 108 static char * 109 remove_line_continuations(glcpp_parser_t *ctx, const char *shader) 110 { 111 struct _mesa_string_buffer *sb = 112 _mesa_string_buffer_create(ctx, INITIAL_PP_OUTPUT_BUF_SIZE); 113 114 const char *backslash, *newline, *search_start; 115 const char *cr, *lf; 116 char newline_separator[3]; 117 int collapsed_newlines = 0; 118 int separator_len; 119 120 backslash = strchr(shader, '\\'); 121 122 /* No line continuations were found in this shader, our job is done */ 123 if (backslash == NULL) 124 return (char *) shader; 125 126 search_start = shader; 127 128 /* Determine what flavor of newlines this shader is using. GLSL 129 * provides for 4 different possible ways to separate lines, (using 130 * one or two characters): 131 * 132 * "\n" (line-feed, like Linux, Unix, and new Mac OS) 133 * "\r" (carriage-return, like old Mac files) 134 * "\r\n" (carriage-return + line-feed, like DOS files) 135 * "\n\r" (line-feed + carriage-return, like nothing, really) 136 * 137 * This code explicitly supports a shader that uses a mixture of 138 * newline terminators and will properly handle line continuation 139 * backslashes followed by any of the above. 140 * 141 * But, since we must also insert additional newlines in the output 142 * (for any collapsed lines) we attempt to maintain consistency by 143 * examining the first encountered newline terminator, and using the 144 * same terminator for any newlines we insert. 145 */ 146 cr = strchr(search_start, '\r'); 147 lf = strchr(search_start, '\n'); 148 149 newline_separator[0] = '\n'; 150 newline_separator[1] = '\0'; 151 newline_separator[2] = '\0'; 152 153 if (cr == NULL) { 154 /* Nothing to do. */ 155 } else if (lf == NULL) { 156 newline_separator[0] = '\r'; 157 } else if (lf == cr + 1) { 158 newline_separator[0] = '\r'; 159 newline_separator[1] = '\n'; 160 } else if (cr == lf + 1) { 161 newline_separator[0] = '\n'; 162 newline_separator[1] = '\r'; 163 } 164 separator_len = strlen(newline_separator); 165 166 while (true) { 167 /* If we have previously collapsed any line-continuations, 168 * then we want to insert additional newlines at the next 169 * occurrence of a newline character to avoid changing any 170 * line numbers. 171 */ 172 if (collapsed_newlines) { 173 cr = strchr (search_start, '\r'); 174 lf = strchr (search_start, '\n'); 175 if (cr && lf) 176 newline = cr < lf ? cr : lf; 177 else if (cr) 178 newline = cr; 179 else 180 newline = lf; 181 if (newline && 182 (backslash == NULL || newline < backslash)) 183 { 184 _mesa_string_buffer_append_len(sb, shader, 185 newline - shader + 1); 186 while (collapsed_newlines) { 187 _mesa_string_buffer_append_len(sb, 188 newline_separator, 189 separator_len); 190 collapsed_newlines--; 191 } 192 shader = skip_newline (newline); 193 search_start = shader; 194 } 195 } 196 197 search_start = backslash + 1; 198 199 if (backslash == NULL) 200 break; 201 202 /* At each line continuation, (backslash followed by a 203 * newline), copy all preceding text to the output, then 204 * advance the shader pointer to the character after the 205 * newline. 206 */ 207 if (backslash[1] == '\r' || backslash[1] == '\n') 208 { 209 collapsed_newlines++; 210 _mesa_string_buffer_append_len(sb, shader, backslash - shader); 211 shader = skip_newline (backslash + 1); 212 search_start = shader; 213 } 214 215 backslash = strchr(search_start, '\\'); 216 } 217 218 _mesa_string_buffer_append(sb, shader); 219 220 return sb->buf; 221 } 222 223 int 224 glcpp_preprocess(void *ralloc_ctx, const char **shader, char **info_log, 225 glcpp_extension_iterator extensions, void *state, 226 struct gl_context *gl_ctx) 227 { 228 int errors; 229 glcpp_parser_t *parser = 230 glcpp_parser_create(&gl_ctx->Extensions, extensions, state, gl_ctx->API); 231 232 if (! gl_ctx->Const.DisableGLSLLineContinuations) 233 *shader = remove_line_continuations(parser, *shader); 234 235 glcpp_lex_set_source_string (parser, *shader); 236 237 glcpp_parser_parse (parser); 238 239 if (parser->skip_stack) 240 glcpp_error (&parser->skip_stack->loc, parser, "Unterminated #if\n"); 241 242 glcpp_parser_resolve_implicit_version(parser); 243 244 ralloc_strcat(info_log, parser->info_log->buf); 245 246 /* Crimp the buffer first, to conserve memory */ 247 _mesa_string_buffer_crimp_to_fit(parser->output); 248 249 ralloc_steal(ralloc_ctx, parser->output->buf); 250 *shader = parser->output->buf; 251 252 errors = parser->error; 253 glcpp_parser_destroy (parser); 254 return errors; 255 } 256