Home | History | Annotate | Download | only in glcpp
      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