prefix bytecodeproc with xasm_
[xorcyst.git] / script.c
blob55c387a2aea840f8ee61daced6f6569efca67bc2
1 /*
2 * $Id: script.c,v 1.3 2007/07/22 13:33:26 khansen Exp $
3 * $Log: script.c,v $
4 * Revision 1.3 2007/07/22 13:33:26 khansen
5 * convert tabs to whitespaces
7 * Revision 1.2 2004/12/18 16:59:50 kenth
8 * fixed command parser bug
10 * Revision 1.1 2004/06/30 07:55:53 kenth
11 * Initial revision
15 /**
16 * (C) 2004 Kent Hansen
18 * The XORcyst is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
23 * The XORcyst is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with The XORcyst; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 /**
34 * This file contains functions to parse, manage, process and query XORcyst
35 * linker scripts and their commands.
36 * Such a script is just a text file with a series of commands of the following
37 * form:
38 * command_name{arg_name=value, arg_name=value, ...}
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <stdarg.h>
44 #include <string.h>
45 #include "script.h"
47 #define SAFE_FREE(a) if (a) { free(a); a = NULL; }
49 /*---------------------------------------------------------------------------*/
51 /** Describes a mapping from string to command type */
52 struct tag_string_to_command_type_mapping {
53 const char *string;
54 xlnk_command_type type;
57 typedef struct tag_string_to_command_type_mapping string_to_command_type_mapping;
59 /**
60 * Attempts to map a string to a script command type.
61 * @param s String representation of command
62 * @return The corresponding command
64 static xlnk_command_type string_to_command_type(const char *s) {
65 int i;
66 /* Table of mappings */
67 static string_to_command_type_mapping map[] = {
68 { "ram", XLNK_RAM_COMMAND },
69 { "output", XLNK_OUTPUT_COMMAND },
70 { "copy", XLNK_COPY_COMMAND },
71 { "bank", XLNK_BANK_COMMAND },
72 { "link", XLNK_LINK_COMMAND },
73 { "options", XLNK_OPTIONS_COMMAND },
74 { "pad", XLNK_PAD_COMMAND},
75 { NULL, -1}
77 /* Try all the mappings */
78 for (i=0; map[i].string != NULL; i++) {
79 if (strcmp(s, map[i].string) == 0) {
80 /* Found it */
81 return map[i].type;
84 /* Not in map table */
85 return XLNK_BAD_COMMAND;
88 /** Describes the arguments that a command should acknowledge */
89 struct tag_valid_command_args
91 xlnk_command_type type;
92 char **args;
95 typedef struct tag_valid_command_args valid_command_args;
97 /**
98 * Tests if the given command argument name is valid.
100 static int is_valid_command_arg(xlnk_command_type type, const char *candidate_arg)
102 int i;
103 int j;
104 char **args;
105 /* Lists of valid args for each command */
106 static char *ram_args[] = { "end", "start", NULL };
107 static char *output_args[] = { "file", NULL };
108 static char *copy_args[] = { "file", NULL };
109 static char *bank_args[] = { "size", "origin", NULL };
110 static char *link_args[] = { "file", "origin", NULL };
111 static char *pad_args[] = { "size", "origin", "offset", NULL };
112 /* Table of valid args */
113 static valid_command_args ok_args[] = {
114 { XLNK_RAM_COMMAND, ram_args },
115 { XLNK_OUTPUT_COMMAND, output_args },
116 { XLNK_COPY_COMMAND, copy_args },
117 { XLNK_BANK_COMMAND, bank_args },
118 { XLNK_LINK_COMMAND, link_args },
119 { XLNK_PAD_COMMAND, pad_args }
121 /* Find arg array for command */
122 for (i=0; ok_args[i].type != -1; i++) {
123 if (ok_args[i].type == type) {
124 /* Now go through array of valid args for command */
125 args = ok_args[i].args;
126 for (j=0; args[j] != NULL; j++) {
127 if (strcmp(args[j], candidate_arg) == 0) {
128 /* Found it. Valid. */
129 return 1;
132 /* Didn't find it. Invalid. */
133 return 0;
136 /* Not valid argument */
137 return 0;
140 /*---------------------------------------------------------------------------*/
142 #define IS_SPACE(c) ( ((c) == '\t') || ((c) == ' ') )
145 * Eats whitespace.
146 * @param s String with whitespace (possibly)
147 * @param i Start index in string, will be incremented beyond whitespace
149 static void eat_ws(const char *s, int *i)
151 while (IS_SPACE(s[*i])) (*i)++;
155 * Tests if a character is in a set of delimiters.
157 static int is_delim(unsigned char c, const char *delim)
159 int i;
160 /* Compare to all delimiters */
161 for (i=0; i<strlen(delim)+1; i++) {
162 if (delim[i] == c) return 1;
164 /* Not a delimiter */
165 return 0;
169 * Gets a token.
170 * @param s String containing token
171 * @param i Start index in string, will be incremented beyond token
172 * @param delim Set of delimiters which may mark end of token
173 * @param dest Where to store the grabbed token
175 static void get_token(const char *s, int *i, char *delim, char *dest)
177 unsigned char c;
178 int j = 0;
179 dest[0] = '\0';
180 while (1) {
181 /* Get next character */
182 c = s[*i];
183 /* Test if token delimiter */
184 if (is_delim(c, delim)) {
185 /* End token */
186 dest[j] = '\0';
187 return;
189 else {
190 /* check if escape character */
191 if (c == '\\') {
192 /* Increase i */
193 (*i)++;
194 /* Get next character */
195 c = s[*i];
196 /* Convert to C escape char if applicable */
197 switch (c) {
198 case '0': c = '\0'; break;
199 case 'a': c = '\a'; break;
200 case 'b': c = '\b'; break;
201 case 't': c = '\t'; break;
202 case 'f': c = '\f'; break;
203 case 'n': c = '\n'; break;
204 case 'r': c = '\r'; break;
207 /* Copy to dest */
208 dest[j++] = c;
209 /* Increase i */
210 (*i)++;
215 /*---------------------------------------------------------------------------*/
218 * Displays an error message generated during script parsing.
219 * @param filename Name of file where error was found
220 * @param line Line of file
221 * @param fmt printf-style format string
223 static void err(const char *filename, int line, const char *fmt, ...)
225 va_list ap;
226 va_start(ap, fmt);
228 /* Print error message w/ location info */
229 fprintf(stderr, "%s:%d: error: ", filename, line);
230 vfprintf(stderr, fmt, ap);
231 fprintf(stderr, "\n");
233 va_end(ap);
236 /*---------------------------------------------------------------------------*/
239 * Adds an argument to script command.
240 * @param cmd Command
241 * @param arg Argument to add
243 static void add_arg(xlnk_script_command *cmd, xlnk_command_arg *arg)
245 xlnk_command_arg *a;
246 if (cmd->first_arg == NULL) {
247 /* Start list */
248 cmd->first_arg = arg;
250 else {
251 /* Add to end of list */
252 for (a = cmd->first_arg; a->next != NULL; a = a->next) ;
253 a->next = arg;
255 arg->next = NULL;
259 * Adds a command to script.
260 * @param sc Script
261 * @param cmd Command to add
263 static void add_command(xlnk_script *sc, xlnk_script_command *cmd)
265 xlnk_script_command *c;
266 if (sc->first_command == NULL) {
267 /* Start list */
268 sc->first_command = cmd;
270 else {
271 /* Add to end of list */
272 for (c = sc->first_command; c->next != NULL; c = c->next) ;
273 c->next = cmd;
275 cmd->next = NULL;
279 * Finalizes a script command.
280 * @param cmd Command
282 static void finalize_command(xlnk_script_command *cmd)
284 xlnk_command_arg *a;
285 xlnk_command_arg *t;
286 /* Finalize all arguments */
287 for(a = cmd->first_arg; a != NULL; a = t) {
288 t = a->next;
289 SAFE_FREE(a->key);
290 SAFE_FREE(a->value);
291 SAFE_FREE(a);
293 SAFE_FREE(cmd);
297 * Gets the processor function for a script command type from a map.
298 * @param type The command type
299 * @param map A mapping from command types to processor functions
301 static xlnk_script_commandproc command_type_to_proc(xlnk_command_type type, xlnk_script_commandprocmap *map)
303 for (; map->proc != NULL; map += 1) {
304 if (map->type == type) {
305 return map->proc;
308 return NULL;
311 /*---------------------------------------------------------------------------*/
314 * Parses a script from a file to a script struct.
315 * @param filename File to parse
316 * @param sc Destination script
317 * @return 0 if fail, 1 if OK
319 int xlnk_script_parse(const char *filename, xlnk_script *sc)
321 FILE *fp;
322 xlnk_command_arg *arg;
323 xlnk_script_command *cmd;
324 xlnk_command_type type;
325 int i;
326 char line[1024];
327 char cmdname[256];
328 char argname[256];
329 char argvalue[256];
330 static int lineno = 0;
331 sc->name = filename;
332 sc->first_command = NULL;
333 /* Attempt to open script */
334 fp = fopen(filename, "rt");
335 if (fp == NULL) {
336 fprintf(stderr, "error: could not open `%s' for reading\n", filename);
337 return 0;
339 /* Read commands */
340 while (fgets(line, 1023, fp) != NULL) {
341 /* Increase line number */
342 lineno++;
343 /* Skip white space */
344 i = 0;
345 eat_ws(line, &i);
346 /* Skip line if comment or end */
347 if ( (line[i] == '#') || (line[i] == '\0') || (line[i] == '\n') ) continue;
348 /* Get command name */
349 get_token(line, &i, " \t{", cmdname);
350 /* Check that it's a valid command */
351 if (strlen(cmdname) == 0) {
352 err(filename, lineno, "command expected");
353 continue;
355 else {
356 /* Convert from string to command type */
357 type = string_to_command_type(cmdname);
358 if (type == XLNK_BAD_COMMAND) {
359 err(filename, lineno, "unknown command `%s'", cmdname);
360 continue;
362 else {
363 /* Allocate space for command */
364 cmd = (xlnk_script_command *)malloc( sizeof(xlnk_script_command) );
365 if (cmd != NULL) {
366 /* Set the type */
367 cmd->type = type;
368 /* No arguments (yet) */
369 cmd->first_arg = NULL;
370 /* Store line number */
371 cmd->line = lineno;
372 /* Add command to script */
373 add_command(sc, cmd);
377 /* Skip white space */
378 eat_ws(line, &i);
379 /* Next token should be '{' */
380 if (line[i] != '{') {
381 err(filename, lineno, "{ expected");
382 continue;
384 i++; /* Eat '{' */
385 /* Get argument(s) */
386 while (line[i] != '}') {
387 if (line[i] == '\0') {
388 break;
390 /* */
391 if (cmd->first_arg != NULL) {
392 /* Skip white space */
393 eat_ws(line, &i);
394 /* Next token should be , */
395 if (line[i] != ',') {
396 err(filename, lineno, ", expected");
397 continue;
399 i++; /* Eat , */
401 /* Skip white space */
402 eat_ws(line, &i);
403 /* Get argument name */
404 get_token(line, &i, " \t=", argname);
405 if (strlen(argname) == 0) {
406 err(filename, lineno, "argument name expected");
407 continue;
409 /* Skip white space */
410 eat_ws(line, &i);
411 /* Next token should be '=' */
412 if (line[i] != '=') {
413 err(filename, lineno, "= expected");
414 continue;
416 i++; /* Eat '=' */
417 /* Skip white space */
418 eat_ws(line, &i);
419 /* Get value */
420 get_token(line, &i, " \t},", argvalue);
421 if (strlen(argvalue) == 0) {
422 err(filename, lineno, "value expected for argument `%s'", argname);
423 continue;
425 // Check if the argument name is valid for this command */
426 if (is_valid_command_arg(cmd->type, argname) ) {
427 /* Create argument struct */
428 arg = (xlnk_command_arg *)malloc( sizeof(xlnk_command_arg) );
429 if (arg != NULL) {
430 arg->key = (char *)malloc( strlen(argname)+1 );
431 arg->value = (char *)malloc( strlen(argvalue)+1 );
432 /* Copy fields */
433 strcpy(arg->key, argname);
434 strcpy(arg->value, argvalue);
435 /* Store argument in list */
436 add_arg(cmd, arg);
439 else {
440 /* Not valid argument name */
441 err(filename, lineno, "invalid argument `%s'", argname);
442 continue;
444 /* Skip white space */
445 eat_ws(line, &i);
448 /* Close script */
449 fclose(fp);
450 /* Success */
451 return 1;
455 * Finalizes a script.
456 * @param sc Script
458 void xlnk_script_finalize(xlnk_script *sc)
460 xlnk_script_command *c;
461 xlnk_script_command *t;
462 if (sc == NULL) { return; }
463 for(c = sc->first_command; c != NULL; c = t) {
464 t = c->next;
465 finalize_command(c);
470 * Gets the length (that is, number of commands) of a script.
471 * @param sc Script
472 * @return Number of commands in script
474 int xlnk_script_length(xlnk_script *sc)
476 xlnk_script_command *c;
477 int i;
478 if (sc == NULL) { return 0; }
479 for (i=0, c=sc->first_command; c != NULL; i++, c = c->next) ;
480 return i;
484 * Gets a command from a script.
485 * @param sc Script
486 * @param index Command index
488 xlnk_script_command *xlnk_script_get_command(xlnk_script *sc, int index)
490 xlnk_script_command *c;
491 int i;
492 if (sc == NULL) { return NULL; }
493 for (i=0, c=sc->first_command; (c != NULL) && (i != index); i++, c = c->next) ;
494 return c;
498 * Processes commands in script.
499 * @param sc Script
500 * @param map Map from command to processor function
502 void xlnk_script_walk(xlnk_script *sc, xlnk_script_commandprocmap *map, void *arg)
504 xlnk_script_command *c;
505 xlnk_script_commandproc p;
506 if (sc == NULL) { return; }
507 /* Walk all the commands */
508 for (c=sc->first_command; c != NULL; c = c->next) {
509 /* Process this command if it has a processor function */
510 p = command_type_to_proc(c->type, map);
511 if (p != NULL) {
512 p(sc, c, arg);
518 * Gets value of argument for script command.
519 * @param c Command
520 * @param key Key (argument name)
522 const char *xlnk_script_get_command_arg(xlnk_script_command *c, const char *key)
524 xlnk_command_arg *a;
525 if (c == NULL) { return NULL; }
526 /* Go through all args */
527 for (a = c->first_arg; a != NULL; a = a->next) {
528 if (strcmp(key, a->key) == 0) {
529 /* Found it, return its value */
530 return a->value;
533 /* Not found */
534 return NULL;
538 * Gets the string representation of a command type.
539 * @param type Command type
541 const char *xlnk_script_command_type_to_string(xlnk_command_type type)
543 switch (type) {
544 case XLNK_RAM_COMMAND: return "ram";
545 case XLNK_OUTPUT_COMMAND: return "output";
546 case XLNK_COPY_COMMAND: return "copy";
547 case XLNK_BANK_COMMAND: return "bank";
548 case XLNK_LINK_COMMAND: return "link";
549 case XLNK_OPTIONS_COMMAND: return "options";
550 case XLNK_PAD_COMMAND: return "pad";
551 default:
552 /* Invalid command */
553 break;
555 return "Invalid command!";
559 * Counts the number of commands of the given type in a script.
560 * @param sc Script
561 * @param type Command type
563 int xlnk_script_count_command_type(xlnk_script *sc, xlnk_command_type type)
565 xlnk_script_command *c;
566 int count;
567 if (sc == NULL) { return 0; }
568 for (count=0, c=sc->first_command; c != NULL; c = c->next) {
569 if (c->type == type) {
570 count++;
573 return count;