the "-D" switch can now assign strings in double quotes (but check your shell's quoting rules!)

git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@433 4df02467-bbd4-4a76-a152-e7ce94205b78
This commit is contained in:
marcobaye
2024-11-25 00:44:37 +00:00
parent e5f2ec2dbc
commit 9ad03311c5
12 changed files with 186 additions and 81 deletions

View File

@@ -511,11 +511,19 @@ static void set_starting_cpu(const char cpu_name[])
}
static void could_not_parse(const char strange[])
// helper function to complain about error in cli args, does not return
static void exit__cli_arg_error(const char format[], const char arg[])
{
fprintf(stderr, "%sCould not parse '%s'.\n", cliargs_error, strange);
fprintf(stderr, "%s", cliargs_error);
fprintf(stderr, format, arg);
fprintf(stderr, "\n");
exit(EXIT_FAILURE);
}
// exit with "Error in CLI arguments: ..." message
void ACME_cli_args_error(const char msg[])
{
exit__cli_arg_error("%s", msg);
}
// return signed long representation of string.
@@ -544,7 +552,7 @@ static signed long string_to_number(const char *string)
}
result = strtol(string, &end, base);
if (*end)
could_not_parse(end);
exit__cli_arg_error("Could not parse \"%s\" as number.", end);
return result;
}
// wrapper for fn above: complain about negative numbers
@@ -552,10 +560,8 @@ static signed long string_to_nonneg_number(const char *string)
{
signed long result = string_to_number(string);
if (result < 0) {
fprintf(stderr, "%sInvalid value, number is negative: '%s'.\n", cliargs_error, string);
exit(EXIT_FAILURE);
}
if (result < 0)
exit__cli_arg_error("Invalid value, number is negative: \"%s\".", string);
return result;
}
@@ -564,29 +570,64 @@ static signed long string_to_nonneg_number(const char *string)
static void set_mem_contents(const char expression[])
{
config.mem_init_value = string_to_number(expression);
if ((config.mem_init_value < -128) || (config.mem_init_value > 255)) {
fprintf(stderr, "%sInitmem value out of range (0-0xff).\n", cliargs_error);
exit(EXIT_FAILURE);
}
if ((config.mem_init_value < -128) || (config.mem_init_value > 255))
exit__cli_arg_error("%s", "Initmem value must be in 0..0xff range.");
}
// define symbol
// FIXME - replace all this with a call to some kind of "hey parser, parse this string" function?
// but then we would have to pass a "do backslash escapes even if older dialect selected" flag...
static void define_symbol(const char definition[])
{
const char *walk = definition;
signed long value;
struct symbol *symbol;
signed long intvalue;
boolean escaped;
// copy definition to GlobalDynaBuf until '=' reached
// copy symbol name (everything up to the '=' char) to GlobalDynaBuf and add terminator:
dynabuf_clear(GlobalDynaBuf);
while ((*walk != '=') && (*walk != '\0'))
dynabuf_append(GlobalDynaBuf, *walk++);
if ((*walk == '\0') || (walk[1] == '\0'))
could_not_parse(definition);
// TODO - if first char is double quote, maybe interpret as string instead of number?
value = string_to_number(walk + 1);
dynabuf_append(GlobalDynaBuf, '\0');
symbol_define(value);
// use name to make symbol
symbol = symbol_for_cli_def();
// now we can clobber GlobalDynaBuf
// no '=' or nothing after? then complain and die:
if ((*walk == '\0') || (walk[1] == '\0'))
exit__cli_arg_error("\"%s\" does not have SYMBOL=VALUE format.", definition);
++walk; // eat '='
// now check value type (at the moment only integers and strings are possible)
if (*walk == '"') {
// it starts with a doublequote, so it must be a string
++walk; // eat '"'
dynabuf_clear(GlobalDynaBuf);
escaped = FALSE;
for (;;) {
if (*walk == '\0')
exit__cli_arg_error("Closing quote not found after -D %s.", definition);
if (escaped) {
// previous byte was backslash, so do not check for closing quote nor backslash
escaped = FALSE;
} else {
// non-escaped: only closing quote and backslash are of interest
if (*walk == '"')
break; // leave loop
if (*walk == '\\')
escaped = TRUE;
}
DYNABUF_APPEND(GlobalDynaBuf, *walk++);
}
if (walk[1] != '\0')
exit__cli_arg_error("Garbage after closing quote at -D %s.", definition);
unescape_dynabuf();
symbol_define_string(symbol);
} else {
// integer
intvalue = string_to_number(walk);
symbol_define_int(symbol, intvalue);
}
}
@@ -605,7 +646,7 @@ struct dialect_info dialects[] = {
{V0_94_12__NEW_FOR_SYNTAX, "0.94.12", "new \"!for\" syntax"},
{V0_95_2__NEW_ANC_OPCODE, "0.95.2", "changed ANC#8 from 0x2b to 0x0b"},
{V0_97__BACKSLASH_ESCAPING, "0.97", "backslash escaping and strings"},
// {V0_98__PATHS_AND_SYMBOLCHANGE, "0.98", "paths are relative to current file"},
{V0_98__PATHS_AND_SYMBOLCHANGE, "0.98", "paths are relative to current file"},
// {V__CURRENT_VERSION, "default", "default"},
{V__FUTURE_VERSION, "future", "enable all experimental features"},
{0, NULL, NULL} // NULLs terminate
@@ -758,8 +799,7 @@ static char short_option(const char *argument)
config.warn_on_type_mismatch = TRUE;
goto done;
} else {
fprintf(stderr, "%sUnknown warning level.\n", cliargs_error);
exit(EXIT_FAILURE);
exit__cli_arg_error("Unknown warning level \"%s\".", argument);
}
break;
default: // unknown ones: program termination
@@ -794,35 +834,31 @@ int main(int argc, const char *argv[])
// this may read the library path from an environment variable.
PLATFORM_INIT;
// handle command line arguments
// (this will handle all "-D SYMBOL=VALUE" assignments)
cliargs_handle_options(short_option, long_option);
// generate list of files to process
cliargs_get_rest(&toplevel_src_count, &toplevel_sources_plat, "No top level sources given");
// now that we have processed all cli switches, check a few values for
// valid range:
if ((config.initial_pc != NO_VALUE_GIVEN) && (config.initial_pc >= OUTBUF_MAXSIZE)) {
fprintf(stderr, "%sProgram counter exceeds maximum outbuffer size.\n", cliargs_error);
exit(EXIT_FAILURE);
}
if ((config.outfile_start != NO_VALUE_GIVEN) && (config.outfile_start >= OUTBUF_MAXSIZE)) {
fprintf(stderr, "%sStart address of output file exceeds maximum outbuffer size.\n", cliargs_error);
exit(EXIT_FAILURE);
}
if ((config.initial_pc != NO_VALUE_GIVEN) && (config.initial_pc >= OUTBUF_MAXSIZE))
throw_error("Program counter exceeds maximum outbuffer size.");
if ((config.outfile_start != NO_VALUE_GIVEN) && (config.outfile_start >= OUTBUF_MAXSIZE))
throw_error("Start address of output file exceeds maximum outbuffer size.");
// "limit" is end+1 and therefore we need ">" instead of ">=":
if ((config.outfile_limit != NO_VALUE_GIVEN) && (config.outfile_limit > OUTBUF_MAXSIZE)) {
fprintf(stderr, "%sEnd+1 of output file exceeds maximum outbuffer size.\n", cliargs_error);
exit(EXIT_FAILURE);
}
if ((config.outfile_limit != NO_VALUE_GIVEN) && (config.outfile_limit > OUTBUF_MAXSIZE))
throw_error("End+1 of output file exceeds maximum outbuffer size.");
if ((config.outfile_start != NO_VALUE_GIVEN)
&& (config.outfile_limit != NO_VALUE_GIVEN)
&& (config.outfile_start >= config.outfile_limit)) {
fprintf(stderr, "%sStart address of output file exceeds end+1.\n", cliargs_error);
exit(EXIT_FAILURE);
}
&& (config.outfile_start >= config.outfile_limit))
throw_error("Start address of output file exceeds end+1.");
// since version 0.98, "--strict-segments" are default:
if (config.dialect >= V0_98__PATHS_AND_SYMBOLCHANGE)
config.strict_segments = TRUE;
// make throw_error() behave normally instead of exiting with "Error in CLI arguments: ..."
throw__done_with_cli_args();
// do the actual work
do_actual_work();
return ACME_finalize(EXIT_SUCCESS); // dump labels, if wanted

View File

@@ -15,5 +15,8 @@
// tidy up before exiting by saving symbol dump
extern int ACME_finalize(int exit_code);
// exit with "Error in CLI arguments: ..." message
extern void ACME_cli_args_error(const char msg[]);
#endif

View File

@@ -404,7 +404,8 @@ static void parse_program_counter(void) // now GotByte = "*"
// make new string object
static void string_prepare_string(struct object *self, int len)
// (exported because symbol.c calls this for strings given on command line)
void string_prepare_string(struct object *self, int len)
{
self->type = &type_string;
self->u.string = safe_malloc(sizeof(*(self->u.string)) + len);
@@ -1285,6 +1286,7 @@ static void unsupported_operation(const struct object *optional, const struct op
// int:
// create byte-sized int object (for comparison results, converted characters, ...)
// FIXME - as this does not set the FITS_BYTE flag, why "byte-sized"?! why not int_create_int?
static void int_create_byte(struct object *self, intval_t byte)
{
self->type = &type_number;

View File

@@ -52,6 +52,13 @@ struct expression {
// labels that are undefined, we can't simply get the addressing mode
// from looking at the parameter's value. FIXME - rename to TAINTED :)
// prototypes
// make new string object
// (exported because symbol.c calls this for strings given on command line)
extern void string_prepare_string(struct object *self, int len);
/*
// FIXME - replace all the functions below with a single one using a "flags" arg!
// its return value would then be "error"/"ok".

View File

@@ -527,10 +527,22 @@ static void print_msg(const char *message, const char *ansicolor, const char *ty
}
}
// flag, tells throw_error how to behave. set to FALSE by throw__done_with_cli_args().
// this is needed because errors like "\xZZ is not a valid backslash sequence" are
// handled by calling throw_error(), but they can happen
// - in cli args, using the "-D SYMBOL=VALUE" syntax, where we want to exit, and
// - in source codes, where we want a "file=F, line=N" output and go on!
static boolean error_is_in_cli_args = TRUE;
// generate debug/info/warning/error message
// if the "optional alternative location" given is NULL, the current location is used
void throw_message(enum debuglevel level, const char msg[], struct location *opt_alt_loc)
{
// FIXME: we only expect normal errors in cli args, no warnings or
// other stuff, so enable this:
//if (error_is_in_cli_args)
// BUG("damn");
// if level is taken from source, ensure valid value:
if (level < DEBUGLEVEL_SERIOUS)
level = DEBUGLEVEL_SERIOUS;
@@ -590,7 +602,10 @@ void throw_warning(const char msg[])
// about more than one of his typos at a time.
void throw_error(const char msg[])
{
throw_message(DEBUGLEVEL_ERROR, msg, NULL);
if (error_is_in_cli_args)
ACME_cli_args_error(msg); // does not return...
else
throw_message(DEBUGLEVEL_ERROR, msg, NULL);
}
// output a serious error (assembly stops, for example if outbuffer overruns).
@@ -649,6 +664,12 @@ void throw_redef_error(const char error_msg[], struct location *old_def, const c
section_now->title = buffered_section_title;
}
// ugly kluge, switches throw_error() from cli args mode to normal mode
void throw__done_with_cli_args(void)
{
error_is_in_cli_args = FALSE;
}
// handle bugs
// FIXME - use a local buffer and sprintf/snprintf to put error code into message!
void BUG(const char *message, int code)

View File

@@ -242,6 +242,9 @@ extern void throw_finalpass_warning(const char msg[]);
// first output error as "error", then location of initial definition as "info"
extern void throw_redef_error(const char error_msg[], struct location *old_def, const char info_msg[]);
// ugly kluge, switches throw_error() from cli args mode to normal mode
extern void throw__done_with_cli_args(void);
// handle bugs
extern void BUG(const char *msg, int code);

View File

@@ -80,7 +80,7 @@ static void report_printline(int line_number)
{
int ii;
char hex_address[HEXBUFSIZE];
char hexdump[2 * REPORT_BINBUFSIZE + 2]; // +2 for '.' and terminator
char hexdump[2 * REPORT_BINBUFSIZE + 2 + 1]; // +2 for '.' and terminator, +1 to suppress compiler warning
// suppress empty lines
if (report->bin_used == 0) {
@@ -649,34 +649,17 @@ enum escape {
ESCAPE_HEX1, // after reading "\x", expecting first hex digit
ESCAPE_HEX2, // after reading "\x", expecting second hex digit
};
// clear dynabuf, read string to it until closing quote is found, then
// process backslash escapes (so size might shrink)
// returns 1 on error (unterminated or escaping error)
int input_read_string_literal(char closing_quote)
// process backslash escapes in GlobalDynaBuf (so size might shrink)
// returns 1 on excaping errors
int unescape_dynabuf(void)
{
int read_index,
write_index;
int read_index = 0,
write_index = 0;
char byte;
enum escape escape_state;
char hexbyte = 0;
enum escape escape_state = ESCAPE_NONE;
char hexbyte = 0;
int err = 0;
dynabuf_clear(GlobalDynaBuf);
if (quoted_to_dynabuf(closing_quote))
return 1; // unterminated
// eat closing quote
GetByte();
if (config.dialect >= V0_97__BACKSLASH_ESCAPING) {
// since v0.97 we need to process backslashes (see below)
} else {
return 0; // older versions did not support backslash escapes, so return "ok"
}
// now un-escape dynabuf contents:
read_index = 0;
write_index = 0;
escape_state = ESCAPE_NONE;
// CAUTION - contents of dynabuf are not terminated:
while (read_index < GlobalDynaBuf->size) {
byte = GLOBALDYNABUF_CURRENT[read_index++];
@@ -714,6 +697,7 @@ int input_read_string_literal(char closing_quote)
// TODO - 'a' to BEL? others?
default:
throw_error("Unsupported backslash sequence."); // TODO - add unexpected character to error message?
err = 1;
}
GLOBALDYNABUF_CURRENT[write_index++] = byte;
break;
@@ -721,13 +705,14 @@ int input_read_string_literal(char closing_quote)
hexbyte = 0;
if (shift_hex_nibble(&hexbyte, byte)) {
escape_state = ESCAPE_NONE; // error -> back to default state
err = 1;
} else {
escape_state = ESCAPE_HEX2; // ok -> expect second hex digit
}
break;
case ESCAPE_HEX2:
if (shift_hex_nibble(&hexbyte, byte)) {
// error will have been thrown already
err = 1;
} else {
// parsing "\x" has written "x", so overwrite it:
GLOBALDYNABUF_CURRENT[write_index - 1] = hexbyte;
@@ -738,6 +723,7 @@ int input_read_string_literal(char closing_quote)
BUG("StrangeEscapeState1", escape_state);
}
}
GlobalDynaBuf->size = write_index;
switch (escape_state) {
case ESCAPE_NONE:
break;
@@ -746,12 +732,33 @@ int input_read_string_literal(char closing_quote)
case ESCAPE_HEX1:
case ESCAPE_HEX2:
throw_error("Expected two hex digits after \\x sequence."); // TODO - add unexpected character to error message?
err = 1;
break;
default:
BUG("StrangeEscapeState2", escape_state);
}
GlobalDynaBuf->size = write_index;
return 0; // ok
return err;
}
// clear dynabuf, read string to it until closing quote is found, then
// process backslash escapes (so size might shrink)
// returns 1 on error (unterminated or escaping error)
int input_read_string_literal(char closing_quote)
{
dynabuf_clear(GlobalDynaBuf);
if (quoted_to_dynabuf(closing_quote))
return 1; // unterminated
// eat closing quote
GetByte();
// since v0.97 we need to process backslashes:
if (config.dialect >= V0_97__BACKSLASH_ESCAPING) {
return unescape_dynabuf();
} else {
return 0; // older versions did not support backslash escapes, so return "ok"
}
}

View File

@@ -71,6 +71,10 @@ extern void parser_skip_remainder(void);
// after mnemonics using implied addressing.
extern void parser_ensure_EOS(void);
// process backslash escapes in GlobalDynaBuf (so size might shrink)
// returns 1 on escaping errors
extern int unescape_dynabuf(void);
// clear dynabuf, read string to it until closing quote is found, then process
// backslash escapes (so size might shrink)
// returns 1 on error (unterminated or escaping errors)

View File

@@ -9,6 +9,7 @@
// 23 Nov 2014 Added label output in VICE format
#include "symbol.h"
#include <stdio.h>
#include <string.h> // for memcpy()
#include "alu.h"
#include "dynabuf.h"
#include "global.h"
@@ -222,21 +223,33 @@ void symbol_set_force_bit(struct symbol *symbol, bits force_bit)
}
// set global symbol to integer value, no questions asked (for "-D" switch)
// Name must be held in GlobalDynaBuf.
void symbol_define(intval_t value)
// create and return symbol for "-D" command line switch (with NULL type object, CAUTION!).
// name must be held in GlobalDynaBuf
extern struct symbol *symbol_for_cli_def(void)
{
struct object result;
struct symbol *symbol;
result.type = &type_number;
result.u.number.ntype = NUMTYPE_INT;
result.u.number.flags = 0;
result.u.number.val.intval = value;
symbol = symbol_find(SCOPE_GLOBAL);
symbol->object = result;
symbol->definition.plat_filename = "\"-D SYMBOL=VALUE\"";
symbol->definition.line_number = 1;
return symbol;
}
// set symbol to integer value, no questions asked (for "-D" switch)
// FIXME - remove and call int_create_byte instead?
void symbol_define_int(struct symbol *symbol, intval_t value)
{
symbol->object.type = &type_number;
symbol->object.u.number.ntype = NUMTYPE_INT;
symbol->object.u.number.flags = 0;
symbol->object.u.number.val.intval = value;
symbol->object.u.number.addr_refs = 0;
}
// set symbol to string value, no questions asked (for "-D" switch)
// string value must be held in GlobalDynaBuf
void symbol_define_string(struct symbol *symbol)
{
string_prepare_string(&symbol->object, GlobalDynaBuf->size);
memcpy(symbol->object.u.string->payload, GLOBALDYNABUF_CURRENT, GlobalDynaBuf->size);
}

View File

@@ -49,9 +49,14 @@ extern void symbol_set_object(struct symbol *symbol, struct object *new_obj, bit
// set force bit of symbol. trying to change to a different one will raise error.
extern void symbol_set_force_bit(struct symbol *symbol, bits force_bit);
// set global symbol to value, no questions asked (for "-D" switch)
// name must be held in GlobalDynaBuf.
extern void symbol_define(intval_t value);
// create and return symbol for "-D" command line switch (with NULL type object, CAUTION!).
// name must be held in GlobalDynaBuf
extern struct symbol *symbol_for_cli_def(void);
// set symbol to integer value, no questions asked (for "-D" switch)
extern void symbol_define_int(struct symbol *symbol, intval_t value);
// set symbol to string value, no questions asked (for "-D" switch)
// string value must be held in GlobalDynaBuf
extern void symbol_define_string(struct symbol *symbol);
// dump global symbols to file
extern void symbols_list(FILE *fd);

View File

@@ -9,7 +9,7 @@
#define RELEASE "0.97" // update before release FIXME
#define CODENAME "Zem" // update before release
#define CHANGE_DATE "22 Nov" // update before release FIXME
#define CHANGE_DATE "23 Nov" // update before release FIXME
#define CHANGE_YEAR "2024" // update before release
//#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/"
#define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME

View File

@@ -221,4 +221,8 @@
+a '\n' == 10
+a '\r' == 13
+a '\x41' == 'A'
+a '\xa0' == 160
+a '\xA0' == 160
+a '\xff' == 255
+a '\xFF' == 255
+a "uv\x41yz" == "uvAyz"