mirror of
https://github.com/uffejakobsen/acme.git
synced 2024-11-26 15:49:18 +00:00
moved segment checks to final pass, made v0.98 force --strict-segments
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@416 4df02467-bbd4-4a76-a152-e7ce94205b78
This commit is contained in:
parent
a037bcc41e
commit
01313f1182
13
src/acme.c
13
src/acme.c
@ -330,10 +330,9 @@ static void save_output_file(void)
|
|||||||
|
|
||||||
|
|
||||||
// definitions for pass flags:
|
// definitions for pass flags:
|
||||||
#define PF_IS_ERROR_PASS (1u << 0) // throw "Symbol not defined" and other errors that could be suppressed
|
#define PF_IS_FINAL_PASS (1u << 0) // mostly for special warnings
|
||||||
#define PF_THROW_SEGMENT_MESSAGES (1u << 1) // throw segment warnings/errors
|
#define PF_IS_ERROR_PASS (1u << 1) // throw "Symbol not defined" and other errors that could be suppressed
|
||||||
#define PF_GENERATE_OUTPUT (1u << 2) // generate output and/or report file
|
#define PF_GENERATE_OUTPUT (1u << 2) // generate output and/or report file
|
||||||
#define PF_IS_FINAL_PASS (1u << 3) // mostly for special warnings
|
|
||||||
|
|
||||||
// perform a single pass
|
// perform a single pass
|
||||||
static void perform_pass(bits passflags)
|
static void perform_pass(bits passflags)
|
||||||
@ -351,7 +350,6 @@ static void perform_pass(bits passflags)
|
|||||||
pass.flags.complain_about_undefined = !!(passflags & PF_IS_ERROR_PASS);
|
pass.flags.complain_about_undefined = !!(passflags & PF_IS_ERROR_PASS);
|
||||||
pass.flags.throw_all_errors = !!(passflags & PF_IS_ERROR_PASS);
|
pass.flags.throw_all_errors = !!(passflags & PF_IS_ERROR_PASS);
|
||||||
pass.flags.is_final_pass = !!(passflags & PF_IS_FINAL_PASS);
|
pass.flags.is_final_pass = !!(passflags & PF_IS_FINAL_PASS);
|
||||||
pass.flags.throw_segment_messages = !!(passflags & PF_THROW_SEGMENT_MESSAGES);
|
|
||||||
pass.flags.generate_output = !!(passflags & PF_GENERATE_OUTPUT);
|
pass.flags.generate_output = !!(passflags & PF_GENERATE_OUTPUT);
|
||||||
|
|
||||||
if (config.process_verbosity >= 2)
|
if (config.process_verbosity >= 2)
|
||||||
@ -424,7 +422,7 @@ static void do_actual_work(void)
|
|||||||
output_init();
|
output_init();
|
||||||
|
|
||||||
// first pass:
|
// first pass:
|
||||||
perform_pass(PF_THROW_SEGMENT_MESSAGES); // FIXME - check segments in all passes, but only throw errors/warnings in final pass!
|
perform_pass(0);
|
||||||
// pretend there has been a previous pass, with one more undefined result
|
// pretend there has been a previous pass, with one more undefined result
|
||||||
undefs_before = pass.counters.undefineds + 1;
|
undefs_before = pass.counters.undefineds + 1;
|
||||||
|
|
||||||
@ -683,7 +681,7 @@ static const char *long_option(const char *string)
|
|||||||
else if (strcmp(string, OPTION_IGNORE_ZEROES) == 0)
|
else if (strcmp(string, OPTION_IGNORE_ZEROES) == 0)
|
||||||
config.honor_leading_zeroes = FALSE;
|
config.honor_leading_zeroes = FALSE;
|
||||||
else if (strcmp(string, OPTION_STRICT_SEGMENTS) == 0)
|
else if (strcmp(string, OPTION_STRICT_SEGMENTS) == 0)
|
||||||
config.debuglevel_segmentprobs = DEBUGLEVEL_ERROR;
|
config.strict_segments = TRUE;
|
||||||
else if (strcmp(string, OPTION_STRICT) == 0)
|
else if (strcmp(string, OPTION_STRICT) == 0)
|
||||||
config.all_warnings_are_errors = TRUE;
|
config.all_warnings_are_errors = TRUE;
|
||||||
else if (strcmp(string, OPTION_DIALECT) == 0)
|
else if (strcmp(string, OPTION_DIALECT) == 0)
|
||||||
@ -821,6 +819,9 @@ int main(int argc, const char *argv[])
|
|||||||
fprintf(stderr, "%sStart address of output file exceeds end+1.\n", cliargs_error);
|
fprintf(stderr, "%sStart address of output file exceeds end+1.\n", cliargs_error);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
// since version 0.98, "--strict-segments" are default:
|
||||||
|
if (config.dialect >= V0_98__PATHS_AND_SYMBOLCHANGE)
|
||||||
|
config.strict_segments = TRUE;
|
||||||
|
|
||||||
// do the actual work
|
// do the actual work
|
||||||
do_actual_work();
|
do_actual_work();
|
||||||
|
@ -118,7 +118,7 @@ void config_default(struct config *conf)
|
|||||||
conf->format_color = FALSE; // enabled by --color
|
conf->format_color = FALSE; // enabled by --color
|
||||||
conf->msg_stream = stderr; // set to stdout by --use-stdout
|
conf->msg_stream = stderr; // set to stdout by --use-stdout
|
||||||
conf->honor_leading_zeroes = TRUE; // disabled by --ignore-zeroes
|
conf->honor_leading_zeroes = TRUE; // disabled by --ignore-zeroes
|
||||||
conf->debuglevel_segmentprobs = DEBUGLEVEL_WARNING; // changed to ERROR by --strict-segments TODO - toggle default?
|
conf->strict_segments = FALSE; // enabled by --strict-segments and V0.98
|
||||||
conf->all_warnings_are_errors = FALSE; // enabled by --strict
|
conf->all_warnings_are_errors = FALSE; // enabled by --strict
|
||||||
conf->test_new_features = FALSE; // enabled by --test
|
conf->test_new_features = FALSE; // enabled by --test
|
||||||
conf->dialect = V__CURRENT_VERSION; // changed by --dialect
|
conf->dialect = V__CURRENT_VERSION; // changed by --dialect
|
||||||
|
@ -85,7 +85,7 @@ struct config {
|
|||||||
boolean format_color; // enabled by --color
|
boolean format_color; // enabled by --color
|
||||||
FILE *msg_stream; // defaults to stderr, changed to stdout by --use-stdout
|
FILE *msg_stream; // defaults to stderr, changed to stdout by --use-stdout
|
||||||
boolean honor_leading_zeroes; // TRUE, disabled by --ignore-zeroes
|
boolean honor_leading_zeroes; // TRUE, disabled by --ignore-zeroes
|
||||||
enum debuglevel debuglevel_segmentprobs; // WARNING, changed to ERROR by --strict-segments
|
boolean strict_segments; // FALSE, enabled by --strict-segments and V0.98
|
||||||
boolean all_warnings_are_errors; // FALSE, enabled by --strict
|
boolean all_warnings_are_errors; // FALSE, enabled by --strict
|
||||||
boolean test_new_features; // FALSE, enabled by --test
|
boolean test_new_features; // FALSE, enabled by --test
|
||||||
enum dialect dialect; // set by --dialect (and --test --test)
|
enum dialect dialect; // set by --dialect (and --test --test)
|
||||||
@ -127,7 +127,6 @@ struct pass {
|
|||||||
boolean complain_about_undefined; // will be FALSE until error pass is needed
|
boolean complain_about_undefined; // will be FALSE until error pass is needed
|
||||||
boolean throw_all_errors; // will be FALSE until error pass is needed
|
boolean throw_all_errors; // will be FALSE until error pass is needed
|
||||||
boolean is_final_pass; // needed for warnings like "converted float to int for xor"
|
boolean is_final_pass; // needed for warnings like "converted float to int for xor"
|
||||||
boolean throw_segment_messages; // atm only used in pass 1, should be used in _last_ pass!
|
|
||||||
boolean generate_output; // create output and/or report file
|
boolean generate_output; // create output and/or report file
|
||||||
} flags;
|
} flags;
|
||||||
};
|
};
|
||||||
|
108
src/output.c
108
src/output.c
@ -10,13 +10,13 @@
|
|||||||
// 22 Sep 2015 Added big-endian output functions
|
// 22 Sep 2015 Added big-endian output functions
|
||||||
// 20 Apr 2019 Prepared for "make segment overlap warnings into errors" later on
|
// 20 Apr 2019 Prepared for "make segment overlap warnings into errors" later on
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
|
#include <stdlib.h> // for free()
|
||||||
#include <string.h> // for memset()
|
#include <string.h> // for memset()
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
|
|
||||||
// constants
|
// types
|
||||||
#define NO_SEGMENT_START (-1) // invalid value to signal "not in a segment"
|
|
||||||
|
|
||||||
|
|
||||||
// structure for linked list of segment data
|
// structure for linked list of segment data
|
||||||
@ -37,8 +37,8 @@ struct output {
|
|||||||
intval_t forced_start_idx; // first index to go into output file
|
intval_t forced_start_idx; // first index to go into output file
|
||||||
intval_t forced_limit_idx; // first index to not go into output file
|
intval_t forced_limit_idx; // first index to not go into output file
|
||||||
struct {
|
struct {
|
||||||
intval_t start; // start of current segment (or NO_SEGMENT_START)
|
intval_t start; // start of current segment (or NO_VALUE_GIVEN if none)
|
||||||
intval_t max; // highest address segment may use
|
intval_t max; // highest address segment may use FIXME - try to change max to limit (+1)
|
||||||
bits flags; // segment flags ("overlay" and "invisible", see header file)
|
bits flags; // segment flags ("overlay" and "invisible", see header file)
|
||||||
struct segment list_head; // head element of doubly-linked ring list
|
struct segment list_head; // head element of doubly-linked ring list
|
||||||
} segm;
|
} segm;
|
||||||
@ -78,6 +78,16 @@ static void report_binary(char value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// wrapper fn for segment problems -> either warning or error
|
||||||
|
static void throwsegmentproblem(const char msg[])
|
||||||
|
{
|
||||||
|
if (config.strict_segments)
|
||||||
|
countorthrow_value_error(msg);
|
||||||
|
else
|
||||||
|
throw_finalpass_warning(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// set up new out->segment.max value according to the given address.
|
// set up new out->segment.max value according to the given address.
|
||||||
// just find the next segment start and subtract 1.
|
// just find the next segment start and subtract 1.
|
||||||
static void find_segment_max(intval_t new_pc)
|
static void find_segment_max(intval_t new_pc)
|
||||||
@ -87,7 +97,6 @@ static void find_segment_max(intval_t new_pc)
|
|||||||
// search for smallest segment start address that
|
// search for smallest segment start address that
|
||||||
// is larger than given address
|
// is larger than given address
|
||||||
// use list head as sentinel
|
// use list head as sentinel
|
||||||
// FIXME - if +1 overflows intval_t, we have an infinite loop!
|
|
||||||
out->segm.list_head.start = new_pc + 1;
|
out->segm.list_head.start = new_pc + 1;
|
||||||
while (test_segment->start <= new_pc)
|
while (test_segment->start <= new_pc)
|
||||||
test_segment = test_segment->next;
|
test_segment = test_segment->next;
|
||||||
@ -106,10 +115,10 @@ static void border_crossed(int current_offset)
|
|||||||
// further:
|
// further:
|
||||||
if (current_offset >= OUTBUF_MAXSIZE)
|
if (current_offset >= OUTBUF_MAXSIZE)
|
||||||
throw_serious_error("Reached memory limit.");
|
throw_serious_error("Reached memory limit.");
|
||||||
if (pass.flags.throw_segment_messages) {
|
throwsegmentproblem("Segment reached another one, overwriting it."); // FIXME - add segment name to msg!
|
||||||
throw_message(config.debuglevel_segmentprobs, "Segment reached another one, overwriting it.", NULL);
|
find_segment_max(current_offset + 1); // find new (next) limit
|
||||||
find_segment_max(current_offset + 1); // find new (next) limit
|
// FIXME - the line above adds 1 and the fn adds 1 -> one address of potential
|
||||||
}
|
// segment start is skipped!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -143,6 +152,7 @@ static void real_output(intval_t byte)
|
|||||||
// FIXME - change this to BUG and add code to make sure it does not happen!
|
// FIXME - change this to BUG and add code to make sure it does not happen!
|
||||||
// or maybe at least enlarge the buffer by 16 bytes and place a canary in
|
// or maybe at least enlarge the buffer by 16 bytes and place a canary in
|
||||||
// it so we can do a sanity check at the end!
|
// it so we can do a sanity check at the end!
|
||||||
|
// (but that only helps for sequential writes without holes...)
|
||||||
}
|
}
|
||||||
out->buffer[out->write_idx] = (byte & 0xff) ^ out->xor;
|
out->buffer[out->write_idx] = (byte & 0xff) ^ out->xor;
|
||||||
}
|
}
|
||||||
@ -219,6 +229,10 @@ void outbuf_set_outfile_limit(void)
|
|||||||
|
|
||||||
|
|
||||||
// link segment data into segment ring
|
// link segment data into segment ring
|
||||||
|
// segments are sorted first by start address and then by length, example:
|
||||||
|
// start $0400, length $0100
|
||||||
|
// start $0401, length $0100
|
||||||
|
// start $0401, length $0101
|
||||||
static void link_segment(intval_t start, intval_t length)
|
static void link_segment(intval_t start, intval_t length)
|
||||||
{
|
{
|
||||||
struct segment *new_segment,
|
struct segment *new_segment,
|
||||||
@ -244,8 +258,6 @@ static void link_segment(intval_t start, intval_t length)
|
|||||||
|
|
||||||
|
|
||||||
// check whether given PC is inside segment.
|
// check whether given PC is inside segment.
|
||||||
// only call in first pass, otherwise too many warnings might be thrown
|
|
||||||
// FIXME - do it the other way round and only complain if there were no other errors!
|
|
||||||
// FIXME - move this fn to its single caller!
|
// FIXME - move this fn to its single caller!
|
||||||
static void check_segment(intval_t new_pc)
|
static void check_segment(intval_t new_pc)
|
||||||
{
|
{
|
||||||
@ -258,10 +270,9 @@ static void check_segment(intval_t new_pc)
|
|||||||
while (test_segment->start <= new_pc) {
|
while (test_segment->start <= new_pc) {
|
||||||
if ((test_segment->start + test_segment->length) > new_pc) {
|
if ((test_segment->start + test_segment->length) > new_pc) {
|
||||||
// TODO - include overlap size in error message!
|
// TODO - include overlap size in error message!
|
||||||
throw_message(config.debuglevel_segmentprobs, "Segment starts inside another one, overwriting it.", NULL);
|
throwsegmentproblem("Segment starts inside another one, overwriting it.");
|
||||||
return;
|
return; // we found one problem, no need to go on looking for more
|
||||||
}
|
}
|
||||||
|
|
||||||
test_segment = test_segment->next;
|
test_segment = test_segment->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -270,19 +281,33 @@ static void check_segment(intval_t new_pc)
|
|||||||
// called once on startup, inits structs
|
// called once on startup, inits structs
|
||||||
void output_init(void)
|
void output_init(void)
|
||||||
{
|
{
|
||||||
// init ring list of segments (FIXME - move to passinit)
|
// init ring list of segments
|
||||||
out->segm.list_head.next = &out->segm.list_head;
|
out->segm.list_head.next = &out->segm.list_head;
|
||||||
out->segm.list_head.prev = &out->segm.list_head;
|
out->segm.list_head.prev = &out->segm.list_head;
|
||||||
// init the one field not initialized by output_passinit:
|
// init the one field not initialized by output_passinit:
|
||||||
out->needed_bufsize = NO_VALUE_GIVEN;
|
out->needed_bufsize = NO_VALUE_GIVEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// helper fn to clear segment list
|
||||||
|
static void free_list(struct segment *list_head)
|
||||||
|
{
|
||||||
|
struct segment *next,
|
||||||
|
*tmp;
|
||||||
|
|
||||||
|
next = list_head->next;
|
||||||
|
while (next != list_head) {
|
||||||
|
tmp = next;
|
||||||
|
next = next->next;
|
||||||
|
free(tmp);
|
||||||
|
}
|
||||||
|
// re-init head element
|
||||||
|
list_head->next = list_head;
|
||||||
|
list_head->prev = list_head;
|
||||||
|
}
|
||||||
|
|
||||||
// called before each pass, clears segment list and disables output
|
// called before each pass, clears segment list and disables output
|
||||||
void output_passinit(void)
|
void output_passinit(void)
|
||||||
{
|
{
|
||||||
//struct segment *temp;
|
|
||||||
|
|
||||||
// init output struct:
|
// init output struct:
|
||||||
if (pass.flags.generate_output) {
|
if (pass.flags.generate_output) {
|
||||||
// we are supposed to actually generate correct output, so
|
// we are supposed to actually generate correct output, so
|
||||||
@ -316,20 +341,12 @@ void output_passinit(void)
|
|||||||
out->forced_start_idx = NO_VALUE_GIVEN;
|
out->forced_start_idx = NO_VALUE_GIVEN;
|
||||||
out->forced_limit_idx = NO_VALUE_GIVEN;
|
out->forced_limit_idx = NO_VALUE_GIVEN;
|
||||||
// not in a segment
|
// not in a segment
|
||||||
out->segm.start = NO_SEGMENT_START; // TODO - "no active segment" could be made a segment flag!
|
out->segm.start = NO_VALUE_GIVEN; // TODO - "no active segment" could be made a segment flag!
|
||||||
out->segm.max = OUTBUF_MAXSIZE - 1;
|
out->segm.max = OUTBUF_MAXSIZE - 1;
|
||||||
out->segm.flags = 0;
|
out->segm.flags = 0;
|
||||||
//FIXME - why clear ring list in every pass?
|
// clear list of segments, otherwise most segments would collide with
|
||||||
// Because later pass shouldn't complain about overwriting the same segment from earlier pass!
|
// their older selves from earlier passes:
|
||||||
// Currently this does not happen because segment warnings are only generated in first pass.
|
free_list(&out->segm.list_head);
|
||||||
// FIXME - in future we want to _output_ the errors/warnings in the final pass only,
|
|
||||||
// but we need to _check_ for them in every pass because only the results of the second-to-last
|
|
||||||
// pass are important. so for the time being, keep this code:
|
|
||||||
// delete segment list (and free blocks)
|
|
||||||
// while ((temp = segment_list)) {
|
|
||||||
// segment_list = segment_list->next;
|
|
||||||
// free(temp);
|
|
||||||
// }
|
|
||||||
// no "encryption":
|
// no "encryption":
|
||||||
out->xor = 0;
|
out->xor = 0;
|
||||||
// needed size of buffer will be calculated at end of pass, so
|
// needed size of buffer will be calculated at end of pass, so
|
||||||
@ -357,13 +374,8 @@ static void end_segment(void)
|
|||||||
{
|
{
|
||||||
intval_t amount;
|
intval_t amount;
|
||||||
|
|
||||||
// only do in first or last pass
|
|
||||||
// FIXME - do the _checking_ in all passes, but only throw errors/warnings in final pass!
|
|
||||||
if (!pass.flags.throw_segment_messages)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// if there is no segment, there is nothing to do
|
// if there is no segment, there is nothing to do
|
||||||
if (out->segm.start == NO_SEGMENT_START)
|
if (out->segm.start == NO_VALUE_GIVEN)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// ignore "invisible" segments
|
// ignore "invisible" segments
|
||||||
@ -378,26 +390,32 @@ static void end_segment(void)
|
|||||||
// link to segment list
|
// link to segment list
|
||||||
link_segment(out->segm.start, amount);
|
link_segment(out->segm.start, amount);
|
||||||
// announce
|
// announce
|
||||||
if (config.process_verbosity >= 2)
|
if (pass.flags.is_final_pass && (config.process_verbosity >= 2)) {
|
||||||
// TODO - change output to start, limit, size, name:
|
// TODO - change output to start, limit, size, name:
|
||||||
// TODO - output hex numbers as %04x? What about limit 0x10000?
|
printf("Segment size is %d (0x%04x) bytes (0x%04x - 0x%04x exclusive).\n",
|
||||||
printf("Segment size is %d (0x%x) bytes (0x%x - 0x%x exclusive).\n",
|
|
||||||
amount, amount, out->segm.start, out->write_idx);
|
amount, amount, out->segm.start, out->write_idx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// called after each pass, closes last code segment and calculates outbuffer size
|
// called after each pass, closes last code segment and calculates outbuffer size
|
||||||
void output_endofpass(void)
|
void output_endofpass(void)
|
||||||
{
|
{
|
||||||
|
intval_t bufsize;
|
||||||
|
|
||||||
// properly finalize previous segment (link to list, announce)
|
// properly finalize previous segment (link to list, announce)
|
||||||
end_segment();
|
end_segment();
|
||||||
|
|
||||||
// calculate size of output buffer
|
// calculate size of output buffer
|
||||||
if (out->highest_written >= out->lowest_written) {
|
if (out->highest_written >= out->lowest_written) {
|
||||||
out->needed_bufsize = out->highest_written + 1;
|
bufsize = out->highest_written + 1;
|
||||||
} else {
|
} else {
|
||||||
out->needed_bufsize = NO_VALUE_GIVEN;
|
bufsize = NO_VALUE_GIVEN;
|
||||||
}
|
}
|
||||||
|
// changing the size counts as a symbol change
|
||||||
|
if (out->needed_bufsize != bufsize)
|
||||||
|
++pass.counters.symbolchanges;
|
||||||
|
out->needed_bufsize = bufsize;
|
||||||
//fprintf(stderr, "Need outbuf size of 0x%04x bytes.\n", out->needed_bufsize);
|
//fprintf(stderr, "Need outbuf size of 0x%04x bytes.\n", out->needed_bufsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,12 +437,10 @@ static void start_segment(intval_t address_change, bits segment_flags)
|
|||||||
out->segm.flags = segment_flags;
|
out->segm.flags = segment_flags;
|
||||||
// allow "writing to buffer" (even though buffer does not really exist until final pass)
|
// allow "writing to buffer" (even though buffer does not really exist until final pass)
|
||||||
output_byte = real_output;
|
output_byte = real_output;
|
||||||
// in first/last pass, check for other segments and maybe issue warning
|
// check for other segments and maybe count/throw warning or error
|
||||||
if (pass.flags.throw_segment_messages) {
|
if (!(segment_flags & SEGMENT_FLAG_OVERLAY))
|
||||||
if (!(segment_flags & SEGMENT_FLAG_OVERLAY))
|
check_segment(out->segm.start);
|
||||||
check_segment(out->segm.start);
|
find_segment_max(out->segm.start);
|
||||||
find_segment_max(out->segm.start);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
#define RELEASE "0.97" // update before release FIXME
|
#define RELEASE "0.97" // update before release FIXME
|
||||||
#define CODENAME "Zem" // update before release
|
#define CODENAME "Zem" // update before release
|
||||||
#define CHANGE_DATE "18 Aug" // update before release FIXME
|
#define CHANGE_DATE "19 Aug" // update before release FIXME
|
||||||
#define CHANGE_YEAR "2024" // update before release
|
#define CHANGE_YEAR "2024" // update before release
|
||||||
//#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/"
|
//#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/"
|
||||||
#define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME
|
#define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME
|
||||||
|
Loading…
Reference in New Issue
Block a user