nv50: fix build-predicate function
[mesa.git] / src / glsl / pp / sl_pp_purify.c
1 /**************************************************************************
2 *
3 * Copyright 2009 VMware, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include "sl_pp_purify.h"
32
33
34 /*
35 * Preprocessor purifier performs the following tasks.
36 * - Convert all variants of newlines into a Unix newline.
37 * - Merge continued lines into a single long line.
38 * - Remove line comments and replace block comments with whitespace.
39 */
40
41
42 static unsigned int
43 _purify_newline(const char *input,
44 char *out,
45 unsigned int *current_line)
46 {
47 if (input[0] == '\n') {
48 *out = '\n';
49 (*current_line)++;
50 if (input[1] == '\r') {
51 /*
52 * The GLSL spec is not explicit about whether this
53 * combination is a valid newline or not.
54 * Let's assume it is acceptable.
55 */
56 return 2;
57 }
58 return 1;
59 }
60 if (input[0] == '\r') {
61 *out = '\n';
62 (*current_line)++;
63 if (input[1] == '\n') {
64 return 2;
65 }
66 return 1;
67 }
68 *out = input[0];
69 return 1;
70 }
71
72
73 static unsigned int
74 _purify_backslash(const char *input,
75 char *out,
76 unsigned int *current_line)
77 {
78 unsigned int eaten = 0;
79
80 for (;;) {
81 if (input[0] == '\\') {
82 char next;
83 unsigned int next_eaten;
84 unsigned int next_line = *current_line;
85
86 eaten++;
87 input++;
88
89 next_eaten = _purify_newline(input, &next, &next_line);
90 if (next == '\n') {
91 /*
92 * If this is really a line continuation sequence, eat
93 * it and do not exit the loop.
94 */
95 eaten += next_eaten;
96 input += next_eaten;
97 *current_line = next_line;
98 } else {
99 /*
100 * It is an error to put anything between a backslash
101 * and a newline and still expect it to behave like a line
102 * continuation sequence.
103 * Even if it is an innocent whitespace.
104 */
105 *out = '\\';
106 break;
107 }
108 } else {
109 eaten += _purify_newline(input, out, current_line);
110 break;
111 }
112 }
113 return eaten;
114 }
115
116
117 static void
118 _report_error(char *buf,
119 unsigned int cbbuf,
120 const char *msg,
121 ...)
122 {
123 va_list args;
124
125 va_start(args, msg);
126 vsnprintf(buf, cbbuf, msg, args);
127 va_end(args);
128 }
129
130
131 void
132 sl_pp_purify_state_init(struct sl_pp_purify_state *state,
133 const char *input,
134 const struct sl_pp_purify_options *options)
135 {
136 state->options = *options;
137 state->input = input;
138 state->current_line = 1;
139 state->inside_c_comment = 0;
140 }
141
142
143 static unsigned int
144 _purify_comment(struct sl_pp_purify_state *state,
145 char *output,
146 unsigned int *current_line,
147 char *errormsg,
148 unsigned int cberrormsg)
149 {
150 for (;;) {
151 unsigned int eaten;
152 char next;
153
154 eaten = _purify_backslash(state->input, &next, current_line);
155 state->input += eaten;
156 while (next == '*') {
157 eaten = _purify_backslash(state->input, &next, current_line);
158 state->input += eaten;
159 if (next == '/') {
160 *output = ' ';
161 state->inside_c_comment = 0;
162 return 1;
163 }
164 }
165 if (next == '\n') {
166 *output = '\n';
167 state->inside_c_comment = 1;
168 return 1;
169 }
170 if (next == '\0') {
171 _report_error(errormsg, cberrormsg, "expected `*/' but end of translation unit found");
172 return 0;
173 }
174 }
175 }
176
177
178 unsigned int
179 sl_pp_purify_getc(struct sl_pp_purify_state *state,
180 char *output,
181 unsigned int *current_line,
182 char *errormsg,
183 unsigned int cberrormsg)
184 {
185 unsigned int eaten;
186
187 if (state->inside_c_comment) {
188 return _purify_comment(state, output, current_line, errormsg, cberrormsg);
189 }
190
191 eaten = _purify_backslash(state->input, output, current_line);
192 state->input += eaten;
193 if (*output == '/') {
194 char next;
195 unsigned int next_line = *current_line;
196
197 eaten = _purify_backslash(state->input, &next, &next_line);
198 if (next == '/') {
199 state->input += eaten;
200 *current_line = next_line;
201
202 /* Replace a line comment with either a newline or nil. */
203 for (;;) {
204 eaten = _purify_backslash(state->input, &next, current_line);
205 state->input += eaten;
206 if (next == '\n' || next == '\0') {
207 *output = next;
208 return eaten;
209 }
210 }
211 } else if (next == '*') {
212 state->input += eaten;
213 *current_line = next_line;
214
215 return _purify_comment(state, output, current_line, errormsg, cberrormsg);
216 }
217 }
218 return eaten;
219 }
220
221
222 struct out_buf {
223 char *out;
224 unsigned int len;
225 unsigned int capacity;
226 unsigned int current_line;
227 char *errormsg;
228 unsigned int cberrormsg;
229 };
230
231
232 static int
233 _out_buf_putc(struct out_buf *obuf,
234 char c)
235 {
236 if (obuf->len >= obuf->capacity) {
237 unsigned int new_max = obuf->capacity;
238
239 if (new_max < 0x100) {
240 new_max = 0x100;
241 } else if (new_max < 0x10000) {
242 new_max *= 2;
243 } else {
244 new_max += 0x10000;
245 }
246
247 obuf->out = realloc(obuf->out, new_max);
248 if (!obuf->out) {
249 _report_error(obuf->errormsg, obuf->cberrormsg, "out of memory");
250 return -1;
251 }
252 obuf->capacity = new_max;
253 }
254
255 obuf->out[obuf->len++] = c;
256
257 return 0;
258 }
259
260
261 int
262 sl_pp_purify(const char *input,
263 const struct sl_pp_purify_options *options,
264 char **output,
265 char *errormsg,
266 unsigned int cberrormsg,
267 unsigned int *errorline)
268 {
269 struct out_buf obuf;
270 struct sl_pp_purify_state state;
271
272 obuf.out = NULL;
273 obuf.len = 0;
274 obuf.capacity = 0;
275 obuf.current_line = 1;
276 obuf.errormsg = errormsg;
277 obuf.cberrormsg = cberrormsg;
278
279 sl_pp_purify_state_init(&state, input, options);
280
281 for (;;) {
282 unsigned int eaten;
283 char c;
284
285 eaten = sl_pp_purify_getc(&state, &c, &obuf.current_line, errormsg, cberrormsg);
286 if (!eaten) {
287 *errorline = obuf.current_line;
288 return -1;
289 }
290 if (_out_buf_putc(&obuf, c)) {
291 *errorline = obuf.current_line;
292 return -1;
293 }
294
295 if (c == '\0') {
296 break;
297 }
298 }
299
300 *output = obuf.out;
301 return 0;
302 }