384 lines
15 KiB
C
384 lines
15 KiB
C
/// _ _
|
|
/// __ _____ _ __(_)_ __ | |_
|
|
/// \ \/ / __| '__| | '_ \| __|
|
|
/// > < (__| | | | |_) | |_
|
|
/// /_/\_\___|_| |_| .__/ \__|
|
|
/// |_|
|
|
///
|
|
/// Copyright (c) 1997 - Ognjen 'xolatile' Milan Robovic
|
|
///
|
|
/// xolatile@chud.cyou - xcript - Whitespace insignificant INI/CFG-like script parser.
|
|
///
|
|
/// This program is free software, free as in freedom and as in free beer, you can redistribute it and/or modify it under the terms of the GNU
|
|
/// General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version if you wish...
|
|
///
|
|
/// This program is distributed in the hope that it will be useful, but it is probably not, and without any warranty, without even the implied
|
|
/// warranty of merchantability or fitness for a particular purpose, because it is pointless. Please see the GNU (Geenoo) General Public License
|
|
/// for more details, if you dare, it is a lot of text that nobody wants to read...
|
|
|
|
typedef enum {
|
|
script_unknown, script_comment, script_string, script_number, script_marker, script_header, script_assign, script_end,
|
|
script_from, script_to, script_next
|
|
} script_word_type;
|
|
|
|
typedef struct {
|
|
character * path;
|
|
character * source;
|
|
natural prefix;
|
|
natural length;
|
|
natural suffix;
|
|
natural offset;
|
|
natural line;
|
|
natural last_length;
|
|
character * last_string;
|
|
boolean force;
|
|
boolean range;
|
|
} script_data_structure;
|
|
|
|
typedef struct {
|
|
natural counter;
|
|
character * * identifier;
|
|
natural * index;
|
|
} script_structure;
|
|
|
|
static procedure script_warning (script_data_structure * script, boolean condition, character * message) {
|
|
if (condition == true) {
|
|
print ("/w %s: %i: %s\n", script->path, script->line, message);
|
|
}
|
|
}
|
|
|
|
static procedure script_failure (script_data_structure * script, boolean condition, character * message) {
|
|
if (condition == true) {
|
|
print ("/f %s: %i: %s\n", script->path, script->line, message);
|
|
|
|
print ("/1%s/-", & script->source [script->offset]);
|
|
|
|
exit (log_failure);
|
|
}
|
|
}
|
|
|
|
static script_data_structure * script_open (character * path) {
|
|
script_data_structure * script = allocate (sizeof (* script));
|
|
|
|
script->path = string_duplicate (path);
|
|
script->source = file_import (path);
|
|
|
|
script->prefix = 0;
|
|
script->length = 0;
|
|
script->suffix = 0;
|
|
script->offset = 0;
|
|
script->line = 1;
|
|
script->last_length = 0;
|
|
script->last_string = & script->source [0];
|
|
|
|
return (script);
|
|
}
|
|
|
|
static script_data_structure * script_close (script_data_structure * script) {
|
|
script->path = deallocate (script->path);
|
|
script->source = deallocate (script->source);
|
|
|
|
return (deallocate (script));
|
|
}
|
|
|
|
static boolean script_compare (script_data_structure * script, character * string) {
|
|
return (string_compare_limit (string, script->last_string, script->last_length));
|
|
}
|
|
|
|
static boolean script_check (script_structure * information, natural index, character * identifier) {
|
|
return (string_compare (identifier, information->identifier [index]));
|
|
}
|
|
|
|
static character * script_export_string (script_data_structure * script) {
|
|
return (string_duplicate_limit (script->last_string, script->last_length));
|
|
}
|
|
|
|
static natural script_export_number (script_data_structure * script) {
|
|
return (string_limit_to_number (script->last_string, script->last_length));
|
|
}
|
|
|
|
static natural script_export_marker (script_structure * information, script_data_structure * script) {
|
|
for (natural counter = 0; counter < information->counter; ++counter) {
|
|
if (script_compare (script, information->identifier [counter]) == true) {
|
|
return (information->index [counter]);
|
|
}
|
|
}
|
|
|
|
script_failure (script, true, "No such identifier defined so far in any of the headers!");
|
|
|
|
return (~ 0u);
|
|
}
|
|
|
|
static script_word_type script_parser (script_data_structure * script) {
|
|
script_word_type word = script_unknown;
|
|
|
|
script->prefix = 0;
|
|
script->length = 0;
|
|
script->suffix = 0;
|
|
|
|
for (; character_is_blank (script->source [script->offset + script->prefix]) == true; ++script->prefix) {
|
|
if (script->source [script->offset + script->prefix] == '\n') {
|
|
++script->line;
|
|
}
|
|
}
|
|
|
|
if (script->source [script->offset + script->prefix] == '\0') {
|
|
word = script_end;
|
|
} else if (script->source [script->offset + script->prefix] == '(') {
|
|
script_failure (script, script->range == true, "You are already defining a range, only one pair of () is allowed.");
|
|
script->range = true;
|
|
++script->length;
|
|
word = script_from;
|
|
} else if (script->source [script->offset + script->prefix] == ',') {
|
|
script_failure (script, script->range == false, "You can't use ',' outside of a range.");
|
|
++script->length;
|
|
word = script_next;
|
|
} else if (script->source [script->offset + script->prefix] == ')') {
|
|
script_failure (script, script->range == false, "You already defined a range, only one pair of () is allowed.");
|
|
script->range = false;
|
|
++script->length;
|
|
word = script_to;
|
|
} else if (script->source [script->offset + script->prefix] == ';') {
|
|
for (; script->source [script->offset + script->prefix + script->length] != '\n'; ++script->length) {
|
|
script_warning (script, script->source [script->offset + script->prefix + script->length] == '\0',
|
|
"Expected at least a trailing new line or some blank character after a comment!");
|
|
}
|
|
word = script_comment;
|
|
} else if (script->source [script->offset + script->prefix] == '#') {
|
|
for (; script->source [script->offset + script->prefix + script->length] != '\n'; ++script->length) {
|
|
script_warning (script, script->source [script->offset + script->prefix + script->length] == '\0',
|
|
"Expected at least a trailing new line or some blank character after a comment!");
|
|
}
|
|
word = script_comment;
|
|
} else if (script->source [script->offset + script->prefix] == '=') {
|
|
++script->length;
|
|
word = script_assign;
|
|
} else if (script->source [script->offset + script->prefix] == '"') {
|
|
script_failure (script, script->range == true, "You can't use string inside of a range.");
|
|
for (script->length = 1; script->source [script->offset + script->prefix + script->length] != '"'; ++script->length) {
|
|
script_failure (script, script->source [script->offset + script->prefix + script->length] == '\0',
|
|
"Unterminated string literal, missing '\"' character.");
|
|
}
|
|
++script->prefix;
|
|
--script->length;
|
|
++script->suffix;
|
|
word = script_string;
|
|
} else if (script->source [script->offset + script->prefix] == '\'') {
|
|
script_failure (script, script->range == true, "You can't use string inside of a range.");
|
|
for (script->length = 1; script->source [script->offset + script->prefix + script->length] != '\''; ++script->length) {
|
|
script_failure (script, script->source [script->offset + script->prefix + script->length] == '\0',
|
|
"Unterminated string literal, missing ''' character.");
|
|
}
|
|
++script->prefix;
|
|
--script->length;
|
|
++script->suffix;
|
|
word = script_string;
|
|
} else if (script->source [script->offset + script->prefix] == '[') {
|
|
script_failure (script, script->range == true, "You can't use header inside of a range.");
|
|
for (; script->source [script->offset + script->prefix + script->length] != ']'; ++script->length) {
|
|
script_failure (script, script->source [script->offset + script->prefix + script->length] == '\0',
|
|
"Unterminated header element, missing ']' character.");
|
|
}
|
|
++script->prefix;
|
|
--script->length;
|
|
++script->suffix;
|
|
word = script_header;
|
|
} else if (character_is_digit (script->source [script->offset + script->prefix]) == true) {
|
|
for (; character_is_digit (script->source [script->offset + script->prefix + script->length]) == true; ++script->length) {
|
|
script_warning (script, script->source [script->offset + script->prefix + script->length] == '\0',
|
|
"Expected at least a trailing new line or some blank character after a number!");
|
|
}
|
|
word = script_number;
|
|
} else if (character_is_identifier (script->source [script->offset + script->prefix]) == true) {
|
|
for (; character_is_identifier (script->source [script->offset + script->prefix + script->length]) == true; ++script->length) {
|
|
script_warning (script, script->source [script->offset + script->prefix + script->length] == '\0',
|
|
"Expected at least a trailing new line or some blank character after a marker!");
|
|
}
|
|
word = script_marker;
|
|
} else {
|
|
script_failure (script, true, format ("Illegal character '%c' in script.", script->source [script->offset + script->prefix]));
|
|
}
|
|
|
|
script->last_string = & script->source [script->offset + script->prefix];
|
|
script->last_length = script->length;
|
|
|
|
script->offset += script->prefix + script->length + script->suffix;
|
|
|
|
return (word);
|
|
}
|
|
|
|
static character * script_expect_header (script_structure * information, script_data_structure * script, natural index, boolean accept) {
|
|
if (accept == true) {
|
|
++information->counter;
|
|
|
|
information->identifier = reallocate (information->identifier, information->counter * sizeof (* information->identifier));
|
|
information->index = reallocate (information->index, information->counter * sizeof (* information->index));
|
|
|
|
information->identifier [information->counter - 1] = string_duplicate_limit (script->last_string, script->last_length);
|
|
information->index [information->counter - 1] = index;
|
|
}
|
|
|
|
return (script_export_string (script));
|
|
}
|
|
|
|
static character * script_expect_string (script_data_structure * script) {
|
|
script_word_type word = script_unknown;
|
|
|
|
script_failure (script, (word = script_parser (script)) != script_assign, "Expected '=', assignment operator.");
|
|
script_failure (script, (word = script_parser (script)) != script_string, "Expected string literal.");
|
|
|
|
return (script_export_string (script));
|
|
}
|
|
|
|
static natural script_expect_number (script_data_structure * script) {
|
|
script_word_type word = script_unknown;
|
|
|
|
script_failure (script, (word = script_parser (script)) != script_assign, "Expected '=', assignment operator.");
|
|
script_failure (script, (word = script_parser (script)) != script_number, "Expected number literal.");
|
|
|
|
return (script_export_number (script));
|
|
}
|
|
|
|
static natural script_expect_marker (script_structure * information, script_data_structure * script) {
|
|
script_word_type word = script_unknown;
|
|
|
|
script_failure (script, (word = script_parser (script)) != script_assign, "Expected '=', assignment operator.");
|
|
script_failure (script, (word = script_parser (script)) != script_marker, "Expected marker literal.");
|
|
|
|
return (script_export_marker (information, script));
|
|
}
|
|
|
|
static natural script_expect_number_or_marker (script_structure * information, script_data_structure * script) {
|
|
script_word_type word = script_unknown;
|
|
|
|
script_failure (script, (word = script_parser (script)) != script_assign, "Expected '=', assignment operator.");
|
|
|
|
word = script_parser (script);
|
|
|
|
if (word == script_number) {
|
|
return (script_export_number (script));
|
|
} else if (word == script_marker) {
|
|
return (script_export_marker (information, script));
|
|
} else {
|
|
script_failure (script, true, "Expected number or marker literal.");
|
|
}
|
|
|
|
return (~ 0u);
|
|
}
|
|
|
|
static natural * script_expect_ordered_array (script_structure * information, script_data_structure * script, natural * count) {
|
|
script_word_type word = script_unknown;
|
|
|
|
natural found = 0;
|
|
natural * array = null;
|
|
|
|
script_failure (script, (word = script_parser (script)) != script_assign, "Expected '=', assignment operator.");
|
|
script_failure (script, (word = script_parser (script)) != script_from, "Expected '(', begin range operator.");
|
|
|
|
for (word = script_parser (script); word != script_to; word = script_parser (script)) {
|
|
++found;
|
|
|
|
array = reallocate (array, found * sizeof (* array));
|
|
|
|
if (word == script_number) {
|
|
array [found - 1] = script_export_number (script);
|
|
} else if (word == script_marker) {
|
|
array [found - 1] = script_export_marker (information, script);
|
|
} else {
|
|
script_failure (script, true, "Expected number or marker!");
|
|
}
|
|
|
|
if ((word = script_parser (script)) == script_to) break;
|
|
|
|
script_failure (script, word != script_next, "Expected ranged next ','.");
|
|
script_failure (script, word == script_end, "Expected ranged to ')'.");
|
|
}
|
|
|
|
(* count) = found;
|
|
|
|
return (array);
|
|
}
|
|
|
|
static natural * script_expect_unordered_array (script_structure * information, script_data_structure * script, natural count) {
|
|
script_word_type word = script_unknown;
|
|
|
|
natural * array = allocate (count * sizeof (* array));
|
|
|
|
script_failure (script, (word = script_parser (script)) != script_assign, "Expected '=', assignment operator.");
|
|
script_failure (script, (word = script_parser (script)) != script_from, "Expected '(', begin range operator.");
|
|
|
|
for (word = script_parser (script); word != script_to; word = script_parser (script)) {
|
|
natural index = script_export_marker (information, script);
|
|
|
|
script_failure (script, word != script_marker, "Expected ranged marker.");
|
|
|
|
script_failure (script, (word = script_parser (script)) != script_assign, "Expected '=', assignment operator.");
|
|
|
|
word = script_parser (script);
|
|
|
|
if (word == script_number) {
|
|
array [index] = script_export_number (script);
|
|
} else if (word == script_marker) {
|
|
array [index] = script_export_marker (information, script);
|
|
} else {
|
|
script_failure (script, true, "Expected number or marker!");
|
|
}
|
|
|
|
if ((word = script_parser (script)) == script_to) break;
|
|
|
|
script_failure (script, word != script_next, "Expected ranged next ','.");
|
|
script_failure (script, word == script_end, "Expected ranged to ')'.");
|
|
}
|
|
|
|
return (array);
|
|
}
|
|
|
|
static script_structure * script_initialize (character * general_script_file_path) {
|
|
script_structure * script = allocate (sizeof (* script));
|
|
|
|
script_word_type word = script_unknown;
|
|
|
|
script_data_structure * general = script_open (general_script_file_path);
|
|
|
|
for (word = script_parser (general); word != script_end; word = script_parser (general)) {
|
|
if (word == script_header) {
|
|
++script->counter;
|
|
script->identifier = reallocate (script->identifier, script->counter * sizeof (* script->identifier));
|
|
script->index = reallocate (script->index, script->counter * sizeof (* script->index));
|
|
script->identifier [script->counter - 1] = string_duplicate_limit (general->last_string, general->last_length);
|
|
script->index [script->counter - 1] = script->counter - 1;
|
|
} else if ((word == script_end) || (word == script_comment)) {
|
|
continue;
|
|
} else {
|
|
script_failure (general, true, "Expected header in general script.");
|
|
}
|
|
}
|
|
|
|
general = script_close (general);
|
|
|
|
return (script);
|
|
}
|
|
|
|
static script_structure * script_deinitialize (script_structure * script) {
|
|
for (natural index = 0; index < script->counter; ++index) {
|
|
script->identifier [index] = deallocate (script->identifier [index]);
|
|
}
|
|
|
|
script->identifier = deallocate (script->identifier);
|
|
script->index = deallocate (script->index);
|
|
|
|
return (deallocate (script));
|
|
}
|
|
|
|
static natural script_indexer (script_structure * information, character * identifier) {
|
|
for (natural counter = 0; counter < information->counter; ++counter) {
|
|
if (string_compare (identifier, information->identifier [counter]) == true) {
|
|
return (information->index [counter]);
|
|
}
|
|
}
|
|
|
|
fatal_failure (true, "script_indexer: No such identifier defined so far in any of the headers!");
|
|
|
|
return (~ 0u);
|
|
}
|