1 /***********************************************************************
2  *
3  *  AVRA - Assembler for the Atmel AVR microcontroller series
4  *
5  *  Copyright (C) 1998-2020 The AVRA Authors
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; see the file COPYING.  If not, write to
19  *  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  *  Boston, MA 02111-1307, USA.
21  *
22  *
23  *  Authors of AVRA can be reached at:
24  *     email: jonah@omegav.ntnu.no, tobiw@suprafluid.com
25  *     www: https://github.com/Ro5bert/avra
26  */
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <time.h>
33 
34 #include "misc.h"
35 #include "avra.h"
36 #include "args.h"
37 
38 
39 /* Special fgets. Like fgets, but with better check for CR, LF and FF and without the ending \n char */
40 /* size must be >=2. No checks for s=NULL, size<2 or stream=NULL.  B.A. */
41 char *
fgets_new(struct prog_info * pi,char * s,int size,FILE * stream)42 fgets_new(struct prog_info *pi, char *s, int size, FILE *stream)
43 {
44 	int c;
45 	char *ptr=s;
46 	do {
47 		if ((c=fgetc(stream))==EOF || IS_ENDLINE(c)) 	/* Terminate at chr$ 10,12,13,0 and EOF */
48 			break;
49 		/* concatenate lines terminated with \ only... */
50 		if (c == '\\') {
51 			/* only newline and cr may follow... */
52 			if ((c=fgetc(stream))==EOF)
53 				break;
54 
55 			if (!IS_ENDLINE(c)) {           /* Terminate at chr$ 10,12,13,0 and EOF */
56 				*ptr++ = '\\';              /* no concatenation, insert it */
57 			} else {
58 				/* mit be additional LF (DOS) */
59 				c=fgetc(stream);
60 				if (IS_ENDLINE(c))
61 					c=fgetc(stream);
62 
63 				if (c == EOF)
64 					break;
65 			}
66 		}
67 
68 		*ptr++=c;
69 	} while (--size);
70 	if ((c==EOF) && (ptr==s))				/* EOF and no chars read -> that's all folks */
71 		return NULL;
72 	if (!size) {
73 		print_msg(pi, MSGTYPE_ERROR, "Line too long");
74 		return NULL;
75 	}
76 	*ptr=0;
77 	if (c==12)						/* Check for Formfeed */
78 		print_msg(pi, MSGTYPE_WARNING, "Found Formfeed char. Please remove it.");
79 	if (c==13) { 						/* Check for CR LF sequence (DOS/ Windows line termination) */
80 		if ((c=fgetc(stream)) != 10) {
81 			ungetc(c,stream);
82 		}
83 	}
84 	return s;
85 }
86 
87 
88 /* Parse given assembler file. */
89 int
parse_file(struct prog_info * pi,const char * filename)90 parse_file(struct prog_info *pi, const char *filename)
91 {
92 #if debug == 1
93 	printf("parse_file\n");
94 #endif
95 	int ok;
96 	int loopok;
97 	struct file_info *fi;
98 	struct include_file *include_file;
99 	ok = True;
100 	if ((fi=malloc(sizeof(struct file_info)))==NULL) {
101 		print_msg(pi, MSGTYPE_OUT_OF_MEM,NULL);
102 		return (False);
103 	}
104 	pi->fi = fi;
105 	if (pi->pass == PASS_1) {
106 		if ((include_file = malloc(sizeof(struct include_file)))==NULL) {
107 			print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
108 			free(fi);
109 			return (False);
110 		}
111 		include_file->next = NULL;
112 		if (pi->last_include_file) {
113 			pi->last_include_file->next = include_file;
114 			include_file->num = pi->last_include_file->num + 1;
115 		} else {
116 			pi->first_include_file = include_file;
117 			include_file->num = 0;
118 		}
119 		pi->last_include_file = include_file;
120 		if ((include_file->name = malloc(strlen(filename) + 1))==NULL) {
121 			print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
122 			free(fi);
123 			return (False);
124 		}
125 		strcpy(include_file->name, filename);
126 	} else { /* PASS 2 */
127 		for (include_file = pi->first_include_file; include_file; include_file = include_file->next) {
128 			if (!strcmp(include_file->name, filename))
129 				break;
130 		}
131 	}
132 	if (!include_file) {
133 		print_msg(pi, MSGTYPE_ERROR, "Internal assembler error");
134 		free(fi);
135 		return (False);
136 	}
137 	fi->include_file = include_file;
138 	fi->line_number = 0;
139 	fi->exit_file = False;
140 #if debug == 1
141 	printf("Opening %s\n",filename);
142 #endif
143 	if ((fi->fp = fopen(filename, "r"))==NULL) {
144 		perror(filename);
145 		free(fi);
146 		return (False);
147 	}
148 	loopok = True;
149 	while (loopok && !fi->exit_file) {
150 		if (fgets_new(pi,fi->buff, LINEBUFFER_LENGTH, fi->fp)) {
151 			fi->line_number++;
152 			pi->list_line = fi->buff;
153 			ok = parse_line(pi, fi->buff);
154 #if debug == 1
155 			printf("parse_line was %i\n", ok);
156 #endif
157 			if (ok) {
158 				if ((pi->pass == PASS_2) && pi->list_line && pi->list_on)
159 					fprintf(pi->list_file, "         %s\n", pi->list_line);
160 				if (pi->error_count >= pi->max_errors) {
161 					print_msg(pi, MSGTYPE_MESSAGE, "Maximum error count reached. Exiting...");
162 					loopok = False;
163 				}
164 			} else {
165 				loopok = False;
166 			}
167 		} else {
168 			loopok = False;
169 			if (!feof(fi->fp)) {
170 				ok = False;
171 				perror(filename);
172 			}
173 		}
174 	}
175 	fclose(fi->fp);
176 	free(fi);
177 	return (ok);
178 }
179 
180 /* Parse one line. */
181 int
parse_line(struct prog_info * pi,char * line)182 parse_line(struct prog_info *pi, char *line)
183 {
184 	char *ptr=NULL;
185 	int k;
186 	int flag=0, i;
187 	int global_label = False;
188 	char temp[LINEBUFFER_LENGTH];
189 	struct label *label = NULL;
190 	struct macro_call *macro_call;
191 	int len;
192 
193 	while (IS_HOR_SPACE(*line)) line++;			/* At first remove leading spaces / tabs */
194 	if (IS_END_OR_COMMENT(*line))				/* Skip comment line or empty line */
195 		return (True);
196 	/* Filter out .stab debugging information */
197 	/* .stabs sometimes contains colon : symbol - might be interpreted as label */
198 	if (*line == '.') {					/* minimal slowdown of existing code */
199 		if (strncmp(temp,".stabs ",7) == 0) {		/* compiler output is always lower case */
200 			strcpy(temp,line);			/* TODO : Do we need this temp variable ? Please check */
201 			return parse_stabs(pi, temp);
202 		}
203 		if (strncmp(temp,".stabn ",7) == 0) {
204 			strcpy(temp,line);
205 			return parse_stabn(pi, temp);
206 		}
207 	}
208 	/* Meta information translation */
209 	ptr=line;
210 	k=0;
211 	len = strlen(ptr);
212 	while ((ptr=strchr(ptr, '%')) != NULL) {
213 		if (!strncmp(ptr, "%MINUTE%", 8)) {		/* Replacement always shorter than tag -> no length check */
214 			k=strftime(ptr, 3, "%M", localtime(&pi->time));
215 			memmove(ptr+k, ptr+8, len - (ptr+8 - line) + 1);
216 			ptr += k;
217 			len -= 8-k;
218 		} else if (!strncmp(ptr, "%HOUR%", 6)) {
219 			k=strftime(ptr, 3, "%H", localtime(&pi->time));
220 			memmove(ptr+k, ptr+6, len - (ptr+6 - line) + 1);
221 			ptr += k;
222 			len -= 6-k;
223 		} else if (!strncmp(ptr, "%DAY%", 5)) {
224 			k=strftime(ptr, 3, "%d", localtime(&pi->time));
225 			memmove(ptr+k, ptr+5, len - (ptr+5 - line) + 1);
226 			ptr += k;
227 			len -= 5-k;
228 		} else if (!strncmp(ptr, "%MONTH%", 7)) {
229 			k=strftime(ptr, 3, "%m", localtime(&pi->time));
230 			memmove(ptr+k, ptr+7, len - (ptr+7 - line) + 1);
231 			ptr += k;
232 			len -= 7-k;
233 		} else if (!strncmp(ptr, "%YEAR%", 6)) {
234 			k=strftime(ptr, 5, "%Y", localtime(&pi->time));
235 			memmove(ptr+k, ptr+6, len - (ptr+6 - line) + 1);
236 			ptr += k;
237 			len -= 6-k;
238 		} else {
239 			ptr++;
240 		}
241 	}
242 
243 	strcpy(pi->fi->scratch,line);
244 
245 	for (i = 0; IS_LABEL(pi->fi->scratch[i]) || (pi->fi->scratch[i] == ':'); i++)
246 		if (pi->fi->scratch[i] == ':') {	/* it is a label */
247 			pi->fi->scratch[i] = '\0';
248 			if (pi->pass == PASS_1) {
249 				for (macro_call = pi->macro_call; macro_call; macro_call = macro_call->prev_on_stack) {
250 					for (label = pi->macro_call->first_label; label; label = label->next) {
251 						if (!nocase_strcmp(label->name, &pi->fi->scratch[0])) {
252 							print_msg(pi, MSGTYPE_ERROR, "Can't redefine local label %s", &pi->fi->scratch[0]);
253 							break;
254 						}
255 					}
256 				}
257 				if (test_label(pi,&pi->fi->scratch[0],"Can't redefine label %s")!=NULL)
258 					break;
259 				if (test_variable(pi,&pi->fi->scratch[0],"%s have already been defined as a .SET variable")!=NULL)
260 					break;
261 				if (test_constant(pi,&pi->fi->scratch[0],"%s has already been defined as a .EQU constant")!=NULL)
262 					break;
263 				label = malloc(sizeof(struct label));
264 				if (!label) {
265 					print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
266 					return (False);
267 				}
268 				label->next = NULL;
269 				label->name = malloc(strlen(&pi->fi->scratch[0]) + 1);
270 				if (!label->name) {
271 					print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
272 					return (False);
273 				}
274 				strcpy(label->name, &pi->fi->scratch[0]);
275 				label->value = pi->segment->addr;
276 
277 				if (pi->macro_call && !global_label) {
278 					if (pi->macro_call->last_label)
279 						pi->macro_call->last_label->next = label;
280 					else
281 						pi->macro_call->first_label = label;
282 					pi->macro_call->last_label = label;
283 				} else {
284 					if (pi->last_label)
285 						pi->last_label->next = label;
286 					else
287 						pi->first_label = label;
288 					pi->last_label = label;
289 				}
290 			}
291 			i++;
292 			while (IS_HOR_SPACE(pi->fi->scratch[i]) && !IS_END_OR_COMMENT(pi->fi->scratch[i])) i++;
293 			if (IS_END_OR_COMMENT(pi->fi->scratch[i])) {
294 				if ((pi->pass == PASS_2) && pi->list_on) { /* Diff tilpassing */
295 					fprintf(pi->list_file, "          %s\n", pi->list_line);
296 					pi->list_line = NULL;
297 				}
298 				return (True);
299 			}
300 			memmove(pi->fi->scratch, &pi->fi->scratch[i], strlen(&pi->fi->scratch[i])+1);
301 			break;
302 		}
303 
304 	if ((pi->fi->scratch[0] == '.') || (pi->fi->scratch[0] == '#')) {
305 		pi->fi->label = label;
306 		flag = parse_directive(pi);
307 		if ((pi->pass == PASS_2) && pi->list_on && pi->list_line) { /* Diff tilpassing */
308 			fprintf(pi->list_file, "          %s\n", pi->list_line);
309 			pi->list_line = NULL;
310 		}
311 		return (flag);
312 	} else {
313 		return parse_mnemonic(pi);
314 	}
315 }
316 
317 
318 /* Get the next token, and terminate the last one.
319  * Termination identifier is specified. */
320 char *
get_next_token(char * data,int term)321 get_next_token(char *data, int term)
322 {
323 	int i = 0, j, anti_comma = False;
324 	/* XXX: this does the same thing for TERM_END and TERM_COMMA? */
325 	switch (term) {
326 	case TERM_END:
327 		/* Skip to next comma or EOL or start of comment, taking into account
328 		 * the possibility for ',' or ';' to be inside quotes. */
329 		while (((data[i] != ',') || anti_comma) && ((data[i] != ';') || anti_comma) && !IS_ENDLINE(data[i]))  {
330 			if ((data[i] == '\'') || (data[i] == '"'))
331 				anti_comma = anti_comma ? False : True;
332 			i++;
333 		}
334 		break;
335 	case TERM_SPACE:
336 		/* Skip to next horizontal space or EOL or start of comment. */
337 		while (!IS_HOR_SPACE(data[i]) && !IS_END_OR_COMMENT(data[i])) i++;
338 		break;
339 	case TERM_DASH:
340 		/* Skip to next dash or EOL or start of comment. */
341 		while ((data[i] != '-') && !IS_END_OR_COMMENT(data[i])) i++;
342 		break;
343 	case TERM_COLON:
344 		/* Skip to next colon or EOL. */
345 		while ((data[i] != ':') && !IS_ENDLINE(data[i])) i++;
346 		break;
347 	case TERM_DOUBLEQUOTE:
348 		/* Skip to next double quote or EOL. */
349 		while ((data[i] != '"') && !IS_ENDLINE(data[i])) i++;
350 		break;
351 	case TERM_COMMA:
352 		/* Skip to next comma or EOL or start of comment, taking into account
353 		 * the possibility for ',' or ';' to be inside quotes. */
354 		while (((data[i] != ',') || anti_comma) && ((data[i] != ';') || anti_comma) && !IS_ENDLINE(data[i])) {
355 			if ((data[i] == '\'') || (data[i] == '"'))
356 				anti_comma = anti_comma ? False : True;
357 			i++;
358 		}
359 		break;
360 	case TERM_EQUAL:
361 		/* Skip to next equals or EOL or start of comment. */
362 		while ((data[i] != '=') && !IS_END_OR_COMMENT(data[i])) i++;
363 		break;
364 	}
365 	/* If we hit EOL or a comment, return null. */
366 	if (IS_END_OR_COMMENT(data[i])) {
367 		/* Null-out the EOL/start of comment. */
368 		data[i--] = '\0';
369 		/* Null-out everything until the first non-horizontal whitespace
370 		 * character. */
371 		while (IS_HOR_SPACE(data[i])) data[i--] = '\0';
372 		return (0);
373 	}
374 	j = i - 1;
375 	/* Null-out all horizontal whitespace before the terminator. */
376 	while (IS_HOR_SPACE(data[j])) data[j--] = '\0';
377 	/* Null-out the terminator itself. */
378 	data[i++] = '\0';
379 	/* Skip over all horizontal whitespace after the terminator. */
380 	while (IS_HOR_SPACE(data[i]) && !IS_END_OR_COMMENT(data[i])) i++;
381 	/* If we hit EOL or a comment, return null. */
382 	if (IS_END_OR_COMMENT(data[i]))
383 		return (0);
384 	/* i should now be the index of the first non-whitespace character after
385 	 * the terminator. */
386 	return (&data[i]);
387 }
388 
389 /* end of parser.c */
390 
391