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