2 * $Id: script.c,v 1.3 2007/07/22 13:33:26 khansen Exp $
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
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
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
38 * command_name{arg_name=value, arg_name=value, ...}
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
{
54 xlnk_command_type type
;
57 typedef struct tag_string_to_command_type_mapping string_to_command_type_mapping
;
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
) {
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
},
77 /* Try all the mappings */
78 for (i
=0; map
[i
].string
!= NULL
; i
++) {
79 if (strcmp(s
, map
[i
].string
) == 0) {
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
;
95 typedef struct tag_valid_command_args valid_command_args
;
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
)
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. */
132 /* Didn't find it. Invalid. */
136 /* Not valid argument */
140 /*---------------------------------------------------------------------------*/
142 #define IS_SPACE(c) ( ((c) == '\t') || ((c) == ' ') )
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
)
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 */
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
)
181 /* Get next character */
183 /* Test if token delimiter */
184 if (is_delim(c
, delim
)) {
190 /* check if escape character */
194 /* Get next character */
196 /* Convert to C escape char if applicable */
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;
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
, ...)
228 /* Print error message w/ location info */
229 fprintf(stderr
, "%s:%d: error: ", filename
, line
);
230 vfprintf(stderr
, fmt
, ap
);
231 fprintf(stderr
, "\n");
236 /*---------------------------------------------------------------------------*/
239 * Adds an argument to script command.
241 * @param arg Argument to add
243 static void add_arg(xlnk_script_command
*cmd
, xlnk_command_arg
*arg
)
246 if (cmd
->first_arg
== NULL
) {
248 cmd
->first_arg
= arg
;
251 /* Add to end of list */
252 for (a
= cmd
->first_arg
; a
->next
!= NULL
; a
= a
->next
) ;
259 * Adds a command to 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
) {
268 sc
->first_command
= cmd
;
271 /* Add to end of list */
272 for (c
= sc
->first_command
; c
->next
!= NULL
; c
= c
->next
) ;
279 * Finalizes a script command.
282 static void finalize_command(xlnk_script_command
*cmd
)
286 /* Finalize all arguments */
287 for(a
= cmd
->first_arg
; a
!= NULL
; a
= t
) {
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
) {
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
)
322 xlnk_command_arg
*arg
;
323 xlnk_script_command
*cmd
;
324 xlnk_command_type type
;
330 static int lineno
= 0;
332 sc
->first_command
= NULL
;
333 /* Attempt to open script */
334 fp
= fopen(filename
, "rt");
336 fprintf(stderr
, "error: could not open `%s' for reading\n", filename
);
340 while (fgets(line
, 1023, fp
) != NULL
) {
341 /* Increase line number */
343 /* Skip white space */
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");
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
);
363 /* Allocate space for command */
364 cmd
= (xlnk_script_command
*)malloc( sizeof(xlnk_script_command
) );
368 /* No arguments (yet) */
369 cmd
->first_arg
= NULL
;
370 /* Store line number */
372 /* Add command to script */
373 add_command(sc
, cmd
);
377 /* Skip white space */
379 /* Next token should be '{' */
380 if (line
[i
] != '{') {
381 err(filename
, lineno
, "{ expected");
385 /* Get argument(s) */
386 while (line
[i
] != '}') {
387 if (line
[i
] == '\0') {
391 if (cmd
->first_arg
!= NULL
) {
392 /* Skip white space */
394 /* Next token should be , */
395 if (line
[i
] != ',') {
396 err(filename
, lineno
, ", expected");
401 /* Skip white space */
403 /* Get argument name */
404 get_token(line
, &i
, " \t=", argname
);
405 if (strlen(argname
) == 0) {
406 err(filename
, lineno
, "argument name expected");
409 /* Skip white space */
411 /* Next token should be '=' */
412 if (line
[i
] != '=') {
413 err(filename
, lineno
, "= expected");
417 /* Skip white space */
420 get_token(line
, &i
, " \t},", argvalue
);
421 if (strlen(argvalue
) == 0) {
422 err(filename
, lineno
, "value expected for argument `%s'", argname
);
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
) );
430 arg
->key
= (char *)malloc( strlen(argname
)+1 );
431 arg
->value
= (char *)malloc( strlen(argvalue
)+1 );
433 strcpy(arg
->key
, argname
);
434 strcpy(arg
->value
, argvalue
);
435 /* Store argument in list */
440 /* Not valid argument name */
441 err(filename
, lineno
, "invalid argument `%s'", argname
);
444 /* Skip white space */
455 * Finalizes a 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
) {
470 * Gets the length (that is, number of commands) of a script.
472 * @return Number of commands in script
474 int xlnk_script_length(xlnk_script
*sc
)
476 xlnk_script_command
*c
;
478 if (sc
== NULL
) { return 0; }
479 for (i
=0, c
=sc
->first_command
; c
!= NULL
; i
++, c
= c
->next
) ;
484 * Gets a command from a script.
486 * @param index Command index
488 xlnk_script_command
*xlnk_script_get_command(xlnk_script
*sc
, int index
)
490 xlnk_script_command
*c
;
492 if (sc
== NULL
) { return NULL
; }
493 for (i
=0, c
=sc
->first_command
; (c
!= NULL
) && (i
!= index
); i
++, c
= c
->next
) ;
498 * Processes commands in 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
);
518 * Gets value of argument for script command.
520 * @param key Key (argument name)
522 const char *xlnk_script_get_command_arg(xlnk_script_command
*c
, const char *key
)
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 */
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
)
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";
552 /* Invalid command */
555 return "Invalid command!";
559 * Counts the number of commands of the given type in a 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
;
567 if (sc
== NULL
) { return 0; }
568 for (count
=0, c
=sc
->first_command
; c
!= NULL
; c
= c
->next
) {
569 if (c
->type
== type
) {