101e04c3fSmrg/*
201e04c3fSmrg * Copyright © 2010 Intel Corporation
301e04c3fSmrg *
401e04c3fSmrg * Permission is hereby granted, free of charge, to any person obtaining a
501e04c3fSmrg * copy of this software and associated documentation files (the "Software"),
601e04c3fSmrg * to deal in the Software without restriction, including without limitation
701e04c3fSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
801e04c3fSmrg * and/or sell copies of the Software, and to permit persons to whom the
901e04c3fSmrg * Software is furnished to do so, subject to the following conditions:
1001e04c3fSmrg *
1101e04c3fSmrg * The above copyright notice and this permission notice (including the next
1201e04c3fSmrg * paragraph) shall be included in all copies or substantial portions of the
1301e04c3fSmrg * Software.
1401e04c3fSmrg *
1501e04c3fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1601e04c3fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1701e04c3fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1801e04c3fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1901e04c3fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2001e04c3fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2101e04c3fSmrg * DEALINGS IN THE SOFTWARE.
2201e04c3fSmrg */
2301e04c3fSmrg
2401e04c3fSmrg#include <assert.h>
2501e04c3fSmrg#include <string.h>
2601e04c3fSmrg#include <ctype.h>
2701e04c3fSmrg#include "glcpp.h"
2801e04c3fSmrg#include "main/mtypes.h"
2901e04c3fSmrg
3001e04c3fSmrgvoid
3101e04c3fSmrgglcpp_error (YYLTYPE *locp, glcpp_parser_t *parser, const char *fmt, ...)
3201e04c3fSmrg{
3301e04c3fSmrg	va_list ap;
3401e04c3fSmrg
3501e04c3fSmrg	parser->error = 1;
3601e04c3fSmrg	_mesa_string_buffer_printf(parser->info_log,
3701e04c3fSmrg				   "%u:%u(%u): "
3801e04c3fSmrg				   "preprocessor error: ",
3901e04c3fSmrg				   locp->source,
4001e04c3fSmrg				   locp->first_line,
4101e04c3fSmrg				   locp->first_column);
4201e04c3fSmrg	va_start(ap, fmt);
4301e04c3fSmrg	_mesa_string_buffer_vprintf(parser->info_log, fmt, ap);
4401e04c3fSmrg	va_end(ap);
4501e04c3fSmrg	_mesa_string_buffer_append_char(parser->info_log, '\n');
4601e04c3fSmrg}
4701e04c3fSmrg
4801e04c3fSmrgvoid
4901e04c3fSmrgglcpp_warning (YYLTYPE *locp, glcpp_parser_t *parser, const char *fmt, ...)
5001e04c3fSmrg{
5101e04c3fSmrg	va_list ap;
5201e04c3fSmrg
5301e04c3fSmrg	_mesa_string_buffer_printf(parser->info_log,
5401e04c3fSmrg				     "%u:%u(%u): "
5501e04c3fSmrg				     "preprocessor warning: ",
5601e04c3fSmrg				     locp->source,
5701e04c3fSmrg				     locp->first_line,
5801e04c3fSmrg				     locp->first_column);
5901e04c3fSmrg	va_start(ap, fmt);
6001e04c3fSmrg	_mesa_string_buffer_vprintf(parser->info_log, fmt, ap);
6101e04c3fSmrg	va_end(ap);
6201e04c3fSmrg	_mesa_string_buffer_append_char(parser->info_log, '\n');
6301e04c3fSmrg}
6401e04c3fSmrg
6501e04c3fSmrg/* Given str, (that's expected to start with a newline terminator of some
6601e04c3fSmrg * sort), return a pointer to the first character in str after the newline.
6701e04c3fSmrg *
6801e04c3fSmrg * A newline terminator can be any of the following sequences:
6901e04c3fSmrg *
7001e04c3fSmrg *	"\r\n"
7101e04c3fSmrg *	"\n\r"
7201e04c3fSmrg *	"\n"
7301e04c3fSmrg *	"\r"
7401e04c3fSmrg *
7501e04c3fSmrg * And the longest such sequence will be skipped.
7601e04c3fSmrg */
7701e04c3fSmrgstatic const char *
7801e04c3fSmrgskip_newline (const char *str)
7901e04c3fSmrg{
8001e04c3fSmrg	const char *ret = str;
8101e04c3fSmrg
8201e04c3fSmrg	if (ret == NULL)
8301e04c3fSmrg		return ret;
8401e04c3fSmrg
8501e04c3fSmrg	if (*ret == '\0')
8601e04c3fSmrg		return ret;
8701e04c3fSmrg
8801e04c3fSmrg	if (*ret == '\r') {
8901e04c3fSmrg		ret++;
9001e04c3fSmrg		if (*ret && *ret == '\n')
9101e04c3fSmrg			ret++;
9201e04c3fSmrg	} else if (*ret == '\n') {
9301e04c3fSmrg		ret++;
9401e04c3fSmrg		if (*ret && *ret == '\r')
9501e04c3fSmrg			ret++;
9601e04c3fSmrg	}
9701e04c3fSmrg
9801e04c3fSmrg	return ret;
9901e04c3fSmrg}
10001e04c3fSmrg
10101e04c3fSmrg/* Initial output buffer size, 4096 minus ralloc() overhead. It was selected
10201e04c3fSmrg * to minimize total amount of allocated memory during shader-db run.
10301e04c3fSmrg */
10401e04c3fSmrg#define INITIAL_PP_OUTPUT_BUF_SIZE 4048
10501e04c3fSmrg
10601e04c3fSmrg/* Remove any line continuation characters in the shader, (whether in
10701e04c3fSmrg * preprocessing directives or in GLSL code).
10801e04c3fSmrg */
10901e04c3fSmrgstatic char *
11001e04c3fSmrgremove_line_continuations(glcpp_parser_t *ctx, const char *shader)
11101e04c3fSmrg{
11201e04c3fSmrg	struct _mesa_string_buffer *sb =
11301e04c3fSmrg		_mesa_string_buffer_create(ctx, INITIAL_PP_OUTPUT_BUF_SIZE);
11401e04c3fSmrg
11501e04c3fSmrg	const char *backslash, *newline, *search_start;
11601e04c3fSmrg        const char *cr, *lf;
11701e04c3fSmrg        char newline_separator[3];
11801e04c3fSmrg	int collapsed_newlines = 0;
11901e04c3fSmrg	int separator_len;
12001e04c3fSmrg
12101e04c3fSmrg	backslash = strchr(shader, '\\');
12201e04c3fSmrg
12301e04c3fSmrg	/* No line continuations were found in this shader, our job is done */
12401e04c3fSmrg	if (backslash == NULL)
12501e04c3fSmrg		return (char *) shader;
12601e04c3fSmrg
12701e04c3fSmrg	search_start = shader;
12801e04c3fSmrg
12901e04c3fSmrg	/* Determine what flavor of newlines this shader is using. GLSL
13001e04c3fSmrg	 * provides for 4 different possible ways to separate lines, (using
13101e04c3fSmrg	 * one or two characters):
13201e04c3fSmrg	 *
13301e04c3fSmrg	 *	"\n" (line-feed, like Linux, Unix, and new Mac OS)
13401e04c3fSmrg	 *	"\r" (carriage-return, like old Mac files)
13501e04c3fSmrg	 *	"\r\n" (carriage-return + line-feed, like DOS files)
13601e04c3fSmrg	 *	"\n\r" (line-feed + carriage-return, like nothing, really)
13701e04c3fSmrg	 *
13801e04c3fSmrg	 * This code explicitly supports a shader that uses a mixture of
13901e04c3fSmrg	 * newline terminators and will properly handle line continuation
14001e04c3fSmrg	 * backslashes followed by any of the above.
14101e04c3fSmrg	 *
14201e04c3fSmrg	 * But, since we must also insert additional newlines in the output
14301e04c3fSmrg	 * (for any collapsed lines) we attempt to maintain consistency by
14401e04c3fSmrg	 * examining the first encountered newline terminator, and using the
14501e04c3fSmrg	 * same terminator for any newlines we insert.
14601e04c3fSmrg	 */
14701e04c3fSmrg	cr = strchr(search_start, '\r');
14801e04c3fSmrg	lf = strchr(search_start, '\n');
14901e04c3fSmrg
15001e04c3fSmrg	newline_separator[0] = '\n';
15101e04c3fSmrg	newline_separator[1] = '\0';
15201e04c3fSmrg	newline_separator[2] = '\0';
15301e04c3fSmrg
15401e04c3fSmrg	if (cr == NULL) {
15501e04c3fSmrg		/* Nothing to do. */
15601e04c3fSmrg	} else if (lf == NULL) {
15701e04c3fSmrg		newline_separator[0] = '\r';
15801e04c3fSmrg	} else if (lf == cr + 1) {
15901e04c3fSmrg		newline_separator[0] = '\r';
16001e04c3fSmrg		newline_separator[1] = '\n';
16101e04c3fSmrg	} else if (cr == lf + 1) {
16201e04c3fSmrg		newline_separator[0] = '\n';
16301e04c3fSmrg		newline_separator[1] = '\r';
16401e04c3fSmrg	}
16501e04c3fSmrg	separator_len = strlen(newline_separator);
16601e04c3fSmrg
16701e04c3fSmrg	while (true) {
16801e04c3fSmrg		/* If we have previously collapsed any line-continuations,
16901e04c3fSmrg		 * then we want to insert additional newlines at the next
17001e04c3fSmrg		 * occurrence of a newline character to avoid changing any
17101e04c3fSmrg		 * line numbers.
17201e04c3fSmrg		 */
17301e04c3fSmrg		if (collapsed_newlines) {
17401e04c3fSmrg			cr = strchr (search_start, '\r');
17501e04c3fSmrg			lf = strchr (search_start, '\n');
17601e04c3fSmrg			if (cr && lf)
17701e04c3fSmrg				newline = cr < lf ? cr : lf;
17801e04c3fSmrg			else if (cr)
17901e04c3fSmrg				newline = cr;
18001e04c3fSmrg			else
18101e04c3fSmrg				newline = lf;
18201e04c3fSmrg			if (newline &&
18301e04c3fSmrg			    (backslash == NULL || newline < backslash))
18401e04c3fSmrg			{
18501e04c3fSmrg				_mesa_string_buffer_append_len(sb, shader,
18601e04c3fSmrg							       newline - shader + 1);
18701e04c3fSmrg				while (collapsed_newlines) {
18801e04c3fSmrg					_mesa_string_buffer_append_len(sb,
18901e04c3fSmrg								       newline_separator,
19001e04c3fSmrg								       separator_len);
19101e04c3fSmrg					collapsed_newlines--;
19201e04c3fSmrg				}
19301e04c3fSmrg				shader = skip_newline (newline);
19401e04c3fSmrg				search_start = shader;
19501e04c3fSmrg			}
19601e04c3fSmrg		}
19701e04c3fSmrg
19801e04c3fSmrg		if (backslash == NULL)
19901e04c3fSmrg			break;
20001e04c3fSmrg
2017ec681f3Smrg		search_start = backslash + 1;
2027ec681f3Smrg
20301e04c3fSmrg		/* At each line continuation, (backslash followed by a
20401e04c3fSmrg		 * newline), copy all preceding text to the output, then
20501e04c3fSmrg		 * advance the shader pointer to the character after the
20601e04c3fSmrg		 * newline.
20701e04c3fSmrg		 */
20801e04c3fSmrg		if (backslash[1] == '\r' || backslash[1] == '\n')
20901e04c3fSmrg		{
21001e04c3fSmrg			collapsed_newlines++;
21101e04c3fSmrg			_mesa_string_buffer_append_len(sb, shader, backslash - shader);
21201e04c3fSmrg			shader = skip_newline (backslash + 1);
21301e04c3fSmrg			search_start = shader;
21401e04c3fSmrg		}
21501e04c3fSmrg
21601e04c3fSmrg		backslash = strchr(search_start, '\\');
21701e04c3fSmrg	}
21801e04c3fSmrg
21901e04c3fSmrg	_mesa_string_buffer_append(sb, shader);
22001e04c3fSmrg
22101e04c3fSmrg	return sb->buf;
22201e04c3fSmrg}
22301e04c3fSmrg
22401e04c3fSmrgint
22501e04c3fSmrgglcpp_preprocess(void *ralloc_ctx, const char **shader, char **info_log,
22601e04c3fSmrg                 glcpp_extension_iterator extensions, void *state,
22701e04c3fSmrg                 struct gl_context *gl_ctx)
22801e04c3fSmrg{
22901e04c3fSmrg	int errors;
23001e04c3fSmrg	glcpp_parser_t *parser =
2317ec681f3Smrg		glcpp_parser_create(gl_ctx, extensions, state);
23201e04c3fSmrg
23301e04c3fSmrg	if (! gl_ctx->Const.DisableGLSLLineContinuations)
23401e04c3fSmrg		*shader = remove_line_continuations(parser, *shader);
23501e04c3fSmrg
23601e04c3fSmrg	glcpp_lex_set_source_string (parser, *shader);
23701e04c3fSmrg
23801e04c3fSmrg	glcpp_parser_parse (parser);
23901e04c3fSmrg
24001e04c3fSmrg	if (parser->skip_stack)
24101e04c3fSmrg		glcpp_error (&parser->skip_stack->loc, parser, "Unterminated #if\n");
24201e04c3fSmrg
24301e04c3fSmrg	glcpp_parser_resolve_implicit_version(parser);
24401e04c3fSmrg
24501e04c3fSmrg	ralloc_strcat(info_log, parser->info_log->buf);
24601e04c3fSmrg
24701e04c3fSmrg	/* Crimp the buffer first, to conserve memory */
24801e04c3fSmrg	_mesa_string_buffer_crimp_to_fit(parser->output);
24901e04c3fSmrg
25001e04c3fSmrg	ralloc_steal(ralloc_ctx, parser->output->buf);
25101e04c3fSmrg	*shader = parser->output->buf;
25201e04c3fSmrg
25301e04c3fSmrg	errors = parser->error;
25401e04c3fSmrg	glcpp_parser_destroy (parser);
25501e04c3fSmrg	return errors;
25601e04c3fSmrg}
257