1 /*	$Id: cunloop.c,v 1.10 2001/07/13 19:09:56 sandro Exp $	*/
2 
3 /*
4  * Copyright (c) 1997-2001 Sandro Sigala.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <ctype.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <err.h>
33 
34 #include "config.h"
35 #include "tokens.h"
36 
37 #define DEFAULT_PREFIX	"l_"
38 
39 /* From lexer.c */
40 #ifdef YYTEXT_POINTER
41 extern char *yytext;
42 #else
43 extern char yytext[];
44 #endif
45 extern char *token_buffer;
46 extern FILE *yyin;
47 extern int yylex(void);
48 extern void init_lex(void);
49 extern void done_lex(void);
50 
51 static int lookahead;
52 static FILE *output_file;
53 
54 static int opt_prefix;			/* Indentifier prefix option. */
55 static char *opt_prefix_arg;		/* Option argument. */
56 
57 #define next_token()	(lookahead = yylex())
58 #define outstr(s)	fputs(s, output_file)
59 #define outch(c)	fputc(c, output_file)
60 
outtk(int tk)61 static void outtk(int tk)
62 {
63 	switch (tk) {
64 	case COMMENT:
65 	case STRING:
66 	case DIRECTIVE:
67 		outstr(token_buffer);
68 		break;
69 	case KW_BREAK:
70 	case KW_CONTINUE:
71 	case KW_DO:
72 	case KW_ELSE:
73 	case KW_FOR:
74 	case KW_IF:
75 	case KW_SWITCH:
76 	case KW_WHILE:
77 	case KEYWORD:
78 	case IDENTIFIER:
79 	case CONSTANT:
80 	case CHARACTER:
81 	case OPERATOR:
82 		outstr(yytext);
83 		break;
84 	default:
85 		outch(tk);
86 	}
87 }
88 
89 static int label_counter;
90 static int label_continue;
91 static int label_break;
92 
93 static int parse_until(int untiltk);
94 
do_while(void)95 static void do_while(void)
96 {
97 	int label_c = ++label_counter;
98 	int label_b = ++label_counter;
99 	int save_label_c = label_continue;
100 	int save_label_b = label_break;
101 
102 	label_continue = label_c;
103 	label_break = label_b;
104 
105 	while (lookahead != '(')
106 		next_token();
107 	next_token();
108 
109 	outstr("{\n");
110 	fprintf(output_file, "%s%d:\n", opt_prefix_arg, label_c);
111 	outstr("if (!(");
112 	parse_until(UNTIL_CLOSEPAREN);
113 	outstr(")\n");
114 	fprintf(output_file, "goto %s%d;\n", opt_prefix_arg, label_b);
115 	parse_until(UNTIL_ENDOFINSTR);
116 	fprintf(output_file, "goto %s%d;\n", opt_prefix_arg, label_c);
117 	fprintf(output_file, "%s%d:;\n", opt_prefix_arg, label_b);
118 	outstr("}\n");
119 
120 	label_continue = save_label_c;
121 	label_break = save_label_b;
122 }
123 
do_do(void)124 static void do_do(void)
125 {
126 	int label_c = ++label_counter;
127 	int label_b = ++label_counter;
128 	int save_label_c = label_continue;
129 	int save_label_b = label_break;
130 
131 	label_continue = label_c;
132 	label_break = label_b;
133 
134 	outstr("{\n");
135 	fprintf(output_file, "%s%d:\n", opt_prefix_arg, label_c);
136 	parse_until(UNTIL_ENDOFINSTR);
137 
138 	while (lookahead != '(')
139 		next_token();
140 	next_token();
141 
142         outstr("if (");
143 	parse_until(UNTIL_CLOSEPAREN);
144 	fprintf(output_file, "\ngoto %s%d;\n", opt_prefix_arg, label_c);
145 	fprintf(output_file, "%s%d:;\n", opt_prefix_arg, label_b);
146 	outstr("}\n");
147 
148 	label_continue = save_label_c;
149 	label_break = save_label_b;
150 }
151 
do_for(void)152 static void do_for(void)
153 {
154 	int label_l = ++label_counter;
155 	int label_c = ++label_counter;
156 	int label_b = ++label_counter;
157 	int label_i = ++label_counter;
158 	int save_label_c = label_continue;
159 	int save_label_b = label_break;
160 
161 	label_continue = label_c;
162 	label_break = label_b;
163 
164 	outstr("{\n");
165 
166 	while (lookahead != '(')
167 		next_token();
168 	next_token();
169 
170 	parse_until(UNTIL_ENDOFINSTR);
171 
172 	fprintf(output_file, "%s%d:\n", opt_prefix_arg, label_l);
173 	outstr("if (!(");
174 	if (!parse_until(UNTIL_ENDOFINSTR_NOECHO))
175 		fprintf(output_file, "1");
176 	outstr("))\n");
177 	fprintf(output_file, "goto %s%d;\n", opt_prefix_arg, label_b);
178 	fprintf(output_file, "goto %s%d;\n", opt_prefix_arg, label_i);
179 	fprintf(output_file, "%s%d:", opt_prefix_arg, label_c);
180 	parse_until(UNTIL_CLOSEPAREN_NOECHO);
181 	outstr(";\n");
182 	fprintf(output_file, "goto %s%d;\n", opt_prefix_arg, label_l);
183 	fprintf(output_file, "%s%d:\n", opt_prefix_arg, label_i);
184 	parse_until(UNTIL_ENDOFINSTR);
185 	fprintf(output_file, "goto %s%d;\n", opt_prefix_arg, label_c);
186 	fprintf(output_file, "%s%d:;\n", opt_prefix_arg, label_b);
187 	outstr("}\n");
188 
189 	label_continue = save_label_c;
190 	label_break = save_label_b;
191 }
192 
do_switch(void)193 static void do_switch(void)
194 {
195 	int save_label_b = label_break;
196 
197 	label_break = 0;
198 
199 	outstr("switch");
200 	parse_until(UNTIL_ENDOFINSTR);
201 
202 	label_break = save_label_b;
203 }
204 
do_if(void)205 static void do_if(void)
206 {
207 	int label_l = ++label_counter;
208 	int label_e;
209 
210 	while (lookahead != '(')
211 		next_token();
212 	next_token();
213 
214 	outstr("{\n");
215 	outstr("if (!(");
216 	parse_until(UNTIL_CLOSEPAREN);
217 	outstr(")\n");
218 	fprintf(output_file, "goto %s%d;\n", opt_prefix_arg, label_l);
219 
220 	parse_until(UNTIL_ENDOFINSTR);
221 
222 	while (isspace(lookahead))
223 		next_token();
224 	if (lookahead == KW_ELSE) {
225 		label_e = ++label_counter;
226 		fprintf(output_file, "goto %s%d;\n", opt_prefix_arg, label_e);
227 	}
228 	fprintf(output_file, "%s%d:;\n", opt_prefix_arg, label_l);
229 	if (lookahead == KW_ELSE) {
230 		next_token();
231 		parse_until(UNTIL_ENDOFINSTR);
232 		fprintf(output_file, "%s%d:;\n", opt_prefix_arg, label_e);
233 	}
234 
235 	outstr("}\n");
236 }
237 
238 /*
239  * The main parsing function.
240  */
parse_until(int untiltk)241 static int parse_until(int untiltk)
242 {
243 	int nparens = 0, nblocks = 0;
244 	int isexpr = 0;
245 
246 	if (untiltk == UNTIL_CLOSEPAREN || untiltk == UNTIL_CLOSEPAREN_NOECHO)
247 		nparens++;
248 
249 	while (lookahead != 0)
250 		switch (lookahead) {
251 		case '(':
252 			next_token();
253 			isexpr = 1;
254 			if (nblocks == 0)
255 				nparens++;
256 			outch('(');
257 			break;
258 		case ')':
259 			next_token();
260 			isexpr = 1;
261 			if (nblocks == 0)
262 				nparens--;
263 			if (untiltk == UNTIL_CLOSEPAREN_NOECHO && nparens == 0)
264 				return isexpr;
265 			outch(')');
266 			if (untiltk == UNTIL_CLOSEPAREN && nparens == 0)
267 				return isexpr;
268 			break;
269 		case '{':
270 			next_token();
271 			isexpr = 1;
272 			nblocks++;
273 			outch('{');
274 			break;
275 		case '}':
276 			next_token();
277 			isexpr = 1;
278 			nblocks--;
279 			outch('}');
280 			if (untiltk == UNTIL_ENDOFINSTR && nblocks == 0)
281 				return isexpr;
282 			break;
283 		case ';':
284 			next_token();
285 			if (untiltk == UNTIL_ENDOFINSTR_NOECHO && nblocks == 0)
286 				return isexpr;
287 			outch(';');
288 			if (untiltk == UNTIL_ENDOFINSTR && nblocks == 0)
289 				return isexpr;
290 			break;
291 		case KW_DO:
292 			next_token();
293 			do_do();
294 			if (untiltk == UNTIL_ENDOFINSTR && nblocks == 0)
295 				return isexpr;
296 			break;
297 		case KW_WHILE:
298 			next_token();
299 			do_while();
300 			if (untiltk == UNTIL_ENDOFINSTR && nblocks == 0)
301 				return isexpr;
302 			break;
303 		case KW_FOR:
304 			next_token();
305 			do_for();
306 			if (untiltk == UNTIL_ENDOFINSTR && nblocks == 0)
307 				return isexpr;
308 			break;
309 		case KW_SWITCH:
310 			next_token();
311 			do_switch();
312 			if (untiltk == UNTIL_ENDOFINSTR && nblocks == 0)
313 				return isexpr;
314 			break;
315 		case KW_IF:
316 			next_token();
317 			do_if();
318 			if (untiltk == UNTIL_ENDOFINSTR && nblocks == 0)
319 				return isexpr;
320 			break;
321 		case KW_BREAK:
322 			next_token();
323 			if (label_break > 0)
324 				fprintf(output_file, "goto %s%d", opt_prefix_arg, label_break);
325 			else
326 				outstr("break");
327 			break;
328 		case KW_CONTINUE:
329 			next_token();
330 			if (label_continue > 0)
331 				fprintf(output_file, "goto %s%d", opt_prefix_arg, label_continue);
332 			else
333 				outstr("continue");
334 			break;
335 		default:
336 			if (!isspace(lookahead))
337 				isexpr = 1;
338 			outtk(lookahead);
339 			next_token();
340 		}
341 
342 	return isexpr;
343 }
344 
parse(void)345 static void parse(void)
346 {
347 	next_token();
348 	parse_until(0);
349 }
350 
process_file(char * filename)351 static void process_file(char *filename)
352 {
353 	if (filename != NULL && strcmp(filename, "-") != 0) {
354 		if ((yyin = fopen(filename, "r")) == NULL)
355 			err(1, "%s", filename);
356 	} else
357 		yyin = stdin;
358 
359 	init_lex();
360 	label_continue = label_break = label_counter = 0;
361 	parse();
362 	done_lex();
363 
364 	if (yyin != stdin)
365 		fclose(yyin);
366 }
367 
368 /*
369  * Output the program syntax then exit.
370  */
usage(void)371 static void usage(void)
372 {
373 	fprintf(stderr, "usage: cunroll [-V] [-o file] [-p prefix] [file ...]\n");
374 	exit(1);
375 }
376 
377 /*
378  * Used by the err() functions.
379  */
380 char *progname;
381 
main(int argc,char ** argv)382 int main(int argc, char **argv)
383 {
384 	int c;
385 
386 	progname = argv[0];
387 	output_file = stdout;
388 
389 	while ((c = getopt(argc, argv, "Vo:p:")) != -1)
390 		switch (c) {
391 		case 'o':
392 			if (output_file != stdout)
393 				fclose(output_file);
394 			if ((output_file = fopen(optarg, "w")) == NULL)
395 				err(1, "%s", optarg);
396 			break;
397 		case 'p':
398 			opt_prefix = 1;
399 			opt_prefix_arg = optarg;
400 			break;
401 		case 'V':
402 			fprintf(stderr, "%s\n", CUTILS_VERSION);
403 			exit(0);
404 		case '?':
405 		default:
406 			usage();
407 			/* NOTREACHED */
408 		}
409 	argc -= optind;
410 	argv += optind;
411 
412 	if (!opt_prefix)
413 		opt_prefix_arg = DEFAULT_PREFIX;
414 
415 	if (argc < 1)
416 		process_file(NULL);
417 	else
418 		while (*argv)
419 			process_file(*argv++);
420 
421 	if (output_file != stdout)
422 		fclose(output_file);
423 
424 	return 0;
425 }
426