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 	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
     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 *
     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 *
    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
    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