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