• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
glcpp_error(YYLTYPE * locp,glcpp_parser_t * parser,const char * fmt,...)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
glcpp_warning(YYLTYPE * locp,glcpp_parser_t * parser,const char * fmt,...)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 *
skip_newline(const char * str)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 *
remove_line_continuations(glcpp_parser_t * ctx,const char * shader)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
glcpp_preprocess(void * ralloc_ctx,const char ** shader,char ** info_log,glcpp_extension_iterator extensions,void * state,struct gl_context * gl_ctx)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