Update to GCC 15.1

This commit is contained in:
Eugen Wissner 2025-05-14 23:05:52 +02:00
parent a02e053ed2
commit 981059e745
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
5 changed files with 218 additions and 393 deletions

View File

@ -8,8 +8,7 @@ require 'rake/clean'
require_relative 'tools/support'
# Dependencies.
GCC_VERSION = "14.2.0"
GCC_PATCH = 'https://raw.githubusercontent.com/Homebrew/formula-patches/f30c309442a60cfb926e780eae5d70571f8ab2cb/gcc/gcc-14.2.0-r2.diff'
GCC_VERSION = "15.1.0"
# Paths.
HOST_GCC = TMP + 'host/gcc'
@ -22,7 +21,7 @@ directory HOST_GCC
directory HOST_INSTALL
task default: [TMP + 'elna'] do
sh (TMP + 'elna').to_path, '--tokenize', 'source.elna'
sh (TMP + 'elna').to_path, '--lex', 'source.elna'
end
namespace :boot do
@ -34,7 +33,6 @@ namespace :boot do
frontend_link = source_directory + 'gcc'
download_and_pipe url, source_directory.dirname, ['tar', '-Jxv']
download_and_pipe URI.parse(GCC_PATCH), source_directory, ['patch', '-p1']
sh 'contrib/download_prerequisites', chdir: source_directory.to_path
File.symlink Pathname.new('.').relative_path_from(frontend_link), (frontend_link + 'elna')

View File

@ -128,8 +128,8 @@ return {
break {
return yy::parser::make_BREAK(this->location);
}
repeat {
return yy::parser::make_REPEAT(this->location);
program {
return yy::parser::make_PROGRAM(this->location);
}
cast {
return yy::parser::make_CAST(this->location);

View File

@ -108,7 +108,7 @@ along with GCC; see the file COPYING3. If not see
ELSE "else"
ELSIF "elsif"
RETURN "return"
REPEAT "repeat"
PROGRAM "program"
BREAK "break"
BEGIN_BLOCK "begin"
END_BLOCK "end"
@ -166,15 +166,15 @@ along with GCC; see the file COPYING3. If not see
%type <std::vector<std::string>> identifiers;
%%
program:
constant_part type_part variable_part procedure_part "begin" statements "end" "."
"program" constant_part type_part variable_part procedure_part "begin" statements "end" "."
{
auto tree = new boot::program(boot::make_position(@5));
auto tree = new boot::program(boot::make_position(@6));
std::swap(tree->constants, $1);
std::swap(tree->types , $2);
std::swap(tree->variables, $3);
std::swap(tree->procedures, $4);
std::swap(tree->body, $6);
std::swap(tree->constants, $2);
std::swap(tree->types , $3);
std::swap(tree->variables, $4);
std::swap(tree->procedures, $5);
std::swap(tree->body, $7);
driver.tree.reset(tree);
}
@ -211,12 +211,12 @@ procedure_heading:
}
}
procedure_definition:
"proc" identifier_definition procedure_heading ";" block
"proc" identifier_definition procedure_heading block
{
$$ = new boot::procedure_definition(boot::make_position(@1), std::move($2), $3.second, $5);
$$ = new boot::procedure_definition(boot::make_position(@1), std::move($2), $3.second, $4);
std::swap($3.first, $$->parameter_names);
}
| "proc" identifier_definition procedure_heading ";" "extern"
| "proc" identifier_definition procedure_heading "extern"
{
$$ = new boot::procedure_definition(boot::make_position(@1), std::move($2), $3.second);
std::swap($3.first, $$->parameter_names);
@ -419,8 +419,6 @@ statement:
| return_statement { $$ = $1; }
| "break" IDENTIFIER
{ $$ = new boot::escape_statement(boot::make_position(@1), boot::escape_direction::end, $2); }
| "repeat" IDENTIFIER
{ $$ = new boot::escape_statement(boot::make_position(@1), boot::escape_direction::begin, $2); }
| call_expression { $$ = $1; }
| "defer" statements "end" { $$ = new boot::defer_statement(boot::make_position(@1), std::move($2)); }
| "case" expression "of" switch_cases else_statements "end"

View File

@ -1,7 +1,12 @@
program
const
SEEK_SET* := 0
SEEK_CUR* := 1
SEEK_END* := 2
STDIN := 0
STDOUT := 1
STDERR := 2
type
TokenKind* = (
@ -19,12 +24,12 @@ type
_extern,
_const,
_var,
array,
_case,
_of,
_type,
_record,
_union,
pointer,
pipe,
to,
boolean,
_nil,
@ -64,7 +69,9 @@ type
string,
_defer,
exclamation,
arrow
arrow,
trait,
_program
)
Position* = record
line: Word;
@ -106,56 +113,58 @@ type
end
CommandLine* = record
input: ^Char;
tokenize: Bool;
syntax_tree: Bool
lex: Bool;
parse: Bool
end
(*
External procedures.
*)
proc fopen(pathname: ^Char, mode: ^Char) -> ^FILE; extern
proc fclose(stream: ^FILE) -> Int; extern
proc fseek(stream: ^FILE, off: Int, whence: Int) -> Int; extern
proc rewind(stream: ^FILE); extern
proc ftell(stream: ^FILE) -> Int; extern
proc fread(ptr: ^Byte, size: Word, nmemb: Word, stream: ^FILE) -> Word; extern
proc write(fd: Int, buf: ^Byte, Word: Int) -> Int; extern
proc malloc(size: Word) -> ^Byte; extern
proc free(ptr: ^Byte); extern
proc calloc(nmemb: Word, size: Word) -> ^Byte; extern
proc realloc(ptr: ^Byte, size: Word) -> ^Byte; extern
proc fopen(pathname: ^Char, mode: ^Char) -> ^FILE extern
proc fclose(stream: ^FILE) -> Int extern
proc fseek(stream: ^FILE, off: Int, whence: Int) -> Int extern
proc rewind(stream: ^FILE) extern
proc ftell(stream: ^FILE) -> Int extern
proc fread(ptr: ^Byte, size: Word, nmemb: Word, stream: ^FILE) -> Word extern
proc write(fd: Int, buf: ^Byte, Word: Int) -> Int extern
proc memset(ptr: ^Char, c: Int, n: Int) -> ^Char; extern
proc malloc(size: Word) -> ^Byte extern
proc free(ptr: ^Byte) extern
proc calloc(nmemb: Word, size: Word) -> ^Byte extern
proc realloc(ptr: ^Byte, size: Word) -> ^Byte extern
proc strcmp(s1: ^Char, s2: ^Char) -> Int; extern
proc strncmp(s1: ^Char, s2: ^Char, n: Word) -> Int; extern
proc strncpy(dst: ^Char, src: ^Char, dsize: Word) -> ^Char; extern
proc strcpy(dst: ^Char, src: ^Char) -> ^Char; extern
proc strlen(ptr: ^Char) -> Word; extern
proc memset(ptr: ^Char, c: Int, n: Int) -> ^Char extern
proc perror(s: ^Char); extern
proc exit(code: Int) -> !; extern
proc strcmp(s1: ^Char, s2: ^Char) -> Int extern
proc strncmp(s1: ^Char, s2: ^Char, n: Word) -> Int extern
proc strncpy(dst: ^Char, src: ^Char, dsize: Word) -> ^Char extern
proc strcpy(dst: ^Char, src: ^Char) -> ^Char extern
proc strlen(ptr: ^Char) -> Word extern
proc perror(s: ^Char) extern
proc exit(code: Int) -> ! extern
(*
Standard procedures.
*)
proc reallocarray(ptr: ^Byte, n: Word, size: Word) -> ^Byte;
proc reallocarray(ptr: ^Byte, n: Word, size: Word) -> ^Byte
begin
return realloc(ptr, n * size)
end
proc write_s(value: String);
proc write_s(value: String)
begin
write(0, cast(value.ptr: ^Byte), cast(value.length: Int))
end
proc write_z(value: ^Char);
proc write_z(value: ^Char)
begin
write(0, cast(value: ^Byte), cast(strlen(value): Int))
end
proc write_b(value: Bool);
proc write_b(value: Bool)
begin
if value then
write_s("true")
@ -164,12 +173,12 @@ begin
end
end
proc write_c(value: Char);
proc write_c(value: Char)
begin
write(0, cast(@value: ^Byte), 1)
end
proc write_i(value: Int);
proc write_i(value: Int)
var
digit: Int
n: Word
@ -193,42 +202,42 @@ begin
end
end
proc write_u(value: Word);
proc write_u(value: Word)
begin
write_i(cast(value: Int))
end
proc is_digit(c: Char) -> Bool;
proc is_digit(c: Char) -> Bool
begin
return cast(c: Int) >= cast('0': Int) & cast(c: Int) <= cast('9': Int)
end
proc is_alpha(c: Char) -> Bool;
proc is_alpha(c: Char) -> Bool
begin
return cast(c: Int) >= cast('A': Int) & cast(c: Int) <= cast('z': Int)
end
proc is_alnum(c: Char) -> Bool;
proc is_alnum(c: Char) -> Bool
begin
return is_digit(c) or is_alpha(c)
end
proc is_space(c: Char) -> Bool;
proc is_space(c: Char) -> Bool
begin
return c = ' ' or c = '\n' or c = '\t'
end
proc substring(string: String, start: Word, count: Word) -> String;
proc substring(string: String, start: Word, count: Word) -> String
begin
return String(string.ptr + start, count)
end
proc open_substring(string: String, start: Word) -> String;
proc open_substring(string: String, start: Word) -> String
begin
return substring(string, start, string.length - start)
end
proc string_dup(origin: String) -> String;
proc string_dup(origin: String) -> String
var
copy: ^Char
begin
@ -238,7 +247,7 @@ begin
return String(copy, origin.length)
end
proc string_buffer_new() -> StringBuffer;
proc string_buffer_new() -> StringBuffer
var
result: StringBuffer
begin
@ -249,7 +258,7 @@ begin
return result
end
proc string_buffer_push(buffer: ^StringBuffer, char: Char);
proc string_buffer_push(buffer: ^StringBuffer, char: Char)
begin
if buffer^.size >= buffer^.capacity then
buffer^.capacity := buffer^.capacity + 1024u;
@ -259,12 +268,12 @@ begin
buffer^.size := buffer^.size + 1u
end
proc string_buffer_pop(buffer: ^StringBuffer, count: Word);
proc string_buffer_pop(buffer: ^StringBuffer, count: Word)
begin
buffer^.size := buffer^.size - count
end
proc string_buffer_clear(buffer: ^StringBuffer) -> String;
proc string_buffer_clear(buffer: ^StringBuffer) -> String
var
result: String
begin
@ -274,15 +283,10 @@ begin
end
(*
End of standard procedures.
Source code stream procedures.
*)
proc make_position() -> Position;
begin
return Position(1u, 1u)
end
proc read_source(filename: ^Char) -> ^SourceFile;
proc read_source(filename: ^Char) -> ^SourceFile
var
result: ^SourceFile
file_handle: ^FILE
@ -298,7 +302,70 @@ begin
return result
end
proc escape_char(escape: Char, result: ^Char) -> Bool;
proc source_file_empty(source_input: ^Byte) -> Bool
var
source_file: ^SourceFile
begin
source_file := cast(source_input: ^SourceFile);
if source_file^.index > source_file^.size then
source_file^.size := fread(cast(@source_file^.buffer: ^Byte), 1u, 1024u, source_file^.handle);
source_file^.index := 1u
end;
return source_file^.size = 0u
end
proc source_file_head(source_input: ^Byte) -> Char
var
source_file: ^SourceFile
begin
source_file := cast(source_input: ^SourceFile);
return source_file^.buffer[source_file^.index]
end
proc source_file_advance(source_input: ^Byte)
var
source_file: ^SourceFile
begin
source_file := cast(source_input: ^SourceFile);
source_file^.index := source_file^.index + 1u
end
proc source_code_empty(source_code: ^SourceCode) -> Bool
begin
return source_code^.empty(source_code^.input)
end
proc source_code_head(source_code: SourceCode) -> Char
begin
return source_code.head(source_code.input)
end
proc source_code_advance(source_code: ^SourceCode)
begin
source_code^.advance(source_code^.input);
source_code^.position.column := source_code^.position.column
end
proc source_code_break(source_code: ^SourceCode)
begin
source_code^.position.line := source_code^.position.line + 1u;
source_code^.position.column := 0u
end
proc source_code_expect(source_code: ^SourceCode, expected: Char) -> Bool
begin
return ~source_code_empty(source_code) & source_code_head(source_code^) = expected
end
(*
Token procedures.
*)
proc escape_char(escape: Char, result: ^Char) -> Bool
var
successful: Bool
begin
@ -344,66 +411,7 @@ begin
return successful
end
proc source_file_empty(source_input: ^Byte) -> Bool;
var
source_file: ^SourceFile
begin
source_file := cast(source_input: ^SourceFile);
if source_file^.index > source_file^.size then
source_file^.size := fread(cast(@source_file^.buffer: ^Byte), 1u, 1024u, source_file^.handle);
source_file^.index := 1u
end;
return source_file^.size = 0u
end
proc source_file_head(source_input: ^Byte) -> Char;
var
source_file: ^SourceFile
begin
source_file := cast(source_input: ^SourceFile);
return source_file^.buffer[source_file^.index]
end
proc source_file_advance(source_input: ^Byte);
var
source_file: ^SourceFile
begin
source_file := cast(source_input: ^SourceFile);
source_file^.index := source_file^.index + 1u
end
proc source_code_empty(source_code: ^SourceCode) -> Bool;
begin
return source_code^.empty(source_code^.input)
end
proc source_code_head(source_code: SourceCode) -> Char;
begin
return source_code.head(source_code.input)
end
proc source_code_advance(source_code: ^SourceCode);
begin
source_code^.advance(source_code^.input);
source_code^.position.column := source_code^.position.column
end
proc source_code_break(source_code: ^SourceCode);
begin
source_code^.position.line := source_code^.position.line + 1u;
source_code^.position.column := 0u
end
proc source_code_expect(source_code: ^SourceCode, expected: Char) -> Bool;
begin
return ~source_code_empty(source_code) & source_code_head(source_code^) = expected
end
proc skip_spaces(source_code: ^SourceCode);
proc skip_spaces(source_code: ^SourceCode)
var
current: Char
begin
@ -419,12 +427,12 @@ begin
end
end
proc is_ident(char: Char) -> Bool;
proc is_ident(char: Char) -> Bool
begin
return is_alnum(char) or char = '_'
end
proc lex_identifier(source_code: ^SourceCode, token_content: ^StringBuffer);
proc lex_identifier(source_code: ^SourceCode, token_content: ^StringBuffer)
var
content_length: Word
begin
@ -434,7 +442,7 @@ begin
end
end
proc lex_comment(source_code: ^SourceCode, token_content: ^StringBuffer) -> Bool;
proc lex_comment(source_code: ^SourceCode, token_content: ^StringBuffer) -> Bool
var
trailing: Word
begin
@ -457,7 +465,7 @@ begin
return trailing = 2u
end
proc lex_character(source_code: ^SourceCode, token_content: ^Char) -> Bool;
proc lex_character(source_code: ^SourceCode, token_content: ^Char) -> Bool
var
successful: Bool
begin
@ -479,7 +487,7 @@ begin
return successful
end
proc lex_string(source_code: ^SourceCode, token_content: ^StringBuffer) -> Bool;
proc lex_string(source_code: ^SourceCode, token_content: ^StringBuffer) -> Bool
var
token_end, constructed_string: ^Char
token_length: Word
@ -504,7 +512,7 @@ begin
return is_valid
end
proc lex_number(source_code: ^SourceCode, token_content: ^Int);
proc lex_number(source_code: ^SourceCode, token_content: ^Int)
begin
token_content^ := 0;
@ -515,7 +523,7 @@ begin
end
end
proc print_tokens(tokens: ^Token, tokens_size: Word);
proc print_tokens(tokens: ^Token, tokens_size: Word)
var
current_token: ^Token
i: Word
@ -549,8 +557,8 @@ begin
write_s("CONST")
| TokenKind._var:
write_s("VAR")
| TokenKind.array:
write_s("ARRAY")
| TokenKind._case:
write_s("CASE")
| TokenKind._of:
write_s("OF")
| TokenKind._type:
@ -559,8 +567,8 @@ begin
write_s("RECORD")
| TokenKind._union:
write_s("UNION")
| TokenKind.pointer:
write_s("POINTER")
| TokenKind.pipe:
write_s("|")
| TokenKind.to:
write_s("TO")
| TokenKind.boolean:
@ -570,11 +578,11 @@ begin
| TokenKind._nil:
write_s("NIL")
| TokenKind.and:
write_s("AND")
write_s("&")
| TokenKind._or:
write_s("OR")
| TokenKind.not:
write_s("NOT")
write_s("~")
| TokenKind._return:
write_s("RETURN")
| TokenKind._cast:
@ -587,6 +595,9 @@ begin
write_c('<');
write_s(current_token^.value.string);
write_c('>')
| TokenKind.trait:
write_c('#');
write_s(current_token^.value.string)
| TokenKind.left_paren:
write_s("(")
| TokenKind.right_paren:
@ -653,6 +664,8 @@ begin
write_c('!')
| TokenKind.arrow:
write_s("->")
| TokenKind._program:
write_s("PROGRAM")
else
write_s("UNKNOWN<");
write_i(cast(current_token^.kind: Int));
@ -665,68 +678,62 @@ begin
write_c('\n')
end
proc categorize_identifier(token_content: String) -> Token;
proc categorize_identifier(token_content: String) -> Token
var
current_token: Token
begin
if "if" = token_content then
if token_content = "if" then
current_token.kind := TokenKind._if
elsif "then" = token_content then
elsif token_content = "then" then
current_token.kind := TokenKind._then
elsif "else" = token_content then
elsif token_content = "else" then
current_token.kind := TokenKind._else
elsif "elsif" = token_content then
elsif token_content = "elsif" then
current_token.kind := TokenKind._elsif
elsif "while" = token_content then
elsif token_content = "while" then
current_token.kind := TokenKind._while
elsif "do" = token_content then
elsif token_content = "do" then
current_token.kind := TokenKind._do
elsif "proc" = token_content then
elsif token_content = "proc" then
current_token.kind := TokenKind._proc
elsif "begin" = token_content then
elsif token_content = "begin" then
current_token.kind := TokenKind._begin
elsif "end" = token_content then
elsif token_content = "end" then
current_token.kind := TokenKind._end
elsif "extern" = token_content then
elsif token_content = "extern" then
current_token.kind := TokenKind._extern
elsif "const" = token_content then
elsif token_content = "const" then
current_token.kind := TokenKind._const
elsif "var" = token_content then
elsif token_content = "var" then
current_token.kind := TokenKind._var
elsif "array" = token_content then
current_token.kind := TokenKind.array
elsif "of" = token_content then
elsif token_content = "case" then
current_token.kind := TokenKind._case
elsif token_content = "of" then
current_token.kind := TokenKind._of
elsif "type" = token_content then
elsif token_content = "type" then
current_token.kind := TokenKind._type
elsif "record" = token_content then
elsif token_content = "record" then
current_token.kind := TokenKind._record
elsif "union" = token_content then
elsif token_content = "union" then
current_token.kind := TokenKind._union
elsif "pointer" = token_content then
current_token.kind := TokenKind.pointer
elsif "to" = token_content then
current_token.kind := TokenKind.to
elsif "true" = token_content then
elsif token_content = "true" then
current_token.kind := TokenKind.boolean;
current_token.value.boolean_value := true
elsif "false" = token_content then
elsif token_content = "false" then
current_token.kind := TokenKind.boolean;
current_token.value.boolean_value := false
elsif "nil" = token_content then
elsif token_content = "nil" then
current_token.kind := TokenKind._nil
elsif "and" = token_content then
current_token.kind := TokenKind.and
elsif "or" = token_content then
elsif token_content = "or" then
current_token.kind := TokenKind._or
elsif "not" = token_content then
current_token.kind := TokenKind.not
elsif "return" = token_content then
elsif token_content = "return" then
current_token.kind := TokenKind._return
elsif "cast" = token_content then
elsif token_content = "cast" then
current_token.kind := TokenKind._cast
elsif "defer" = token_content then
elsif token_content = "defer" then
current_token.kind := TokenKind._defer
elsif token_content = "program" then
current_token.kind := TokenKind._program
else
current_token.kind := TokenKind.identifier;
current_token.value.string := string_dup(token_content)
@ -735,7 +742,7 @@ begin
return current_token
end
proc tokenize(source_code: SourceCode, tokens_size: ^Word) -> ^Token;
proc tokenize(source_code: SourceCode, tokens_size: ^Word) -> ^Token
var
tokens, current_token: ^Token
first_char: Char
@ -755,6 +762,12 @@ begin
if is_alpha(first_char) or first_char = '_' then
lex_identifier(@source_code, @token_buffer);
current_token^ := categorize_identifier(string_buffer_clear(@token_buffer))
elsif first_char = '#' then
source_code_advance(@source_code);
lex_identifier(@source_code, @token_buffer);
current_token^.kind := TokenKind.trait;
current_token^.value.string := string_dup(string_buffer_clear(@token_buffer))
elsif is_digit(first_char) then
lex_number(@source_code, @current_token^.value.int_value);
@ -894,6 +907,15 @@ begin
elsif first_char = '!' then
current_token^.kind := TokenKind.exclamation;
source_code_advance(@source_code)
elsif first_char = '&' then
current_token^.kind := TokenKind.and;
source_code_advance(@source_code)
elsif first_char = '~' then
current_token^.kind := TokenKind.not;
source_code_advance(@source_code)
elsif first_char = '|' then
current_token^.kind := TokenKind.pipe;
source_code_advance(@source_code)
else
current_token^.kind := TokenKind.unknown;
source_code_advance(@source_code)
@ -912,7 +934,11 @@ begin
return tokens
end
proc parse_command_line*(argc: Int, argv: ^^Char) -> ^CommandLine;
(*
Command line handling.
*)
proc parse_command_line*(argc: Int, argv: ^^Char) -> ^CommandLine
var
parameter: ^^Char
i: Int
@ -920,23 +946,26 @@ var
begin
i := 1;
result := cast(malloc(#size(CommandLine)): ^CommandLine);
result^.tokenize := false;
result^.syntax_tree := false;
result^.lex := false;
result^.parse := false;
result^.input := nil;
while i < argc do
parameter := argv + i;
if strcmp(parameter^, "--tokenize\0".ptr) = 0 then
result^.tokenize := true
elsif strcmp(parameter^, "--syntax-tree\0".ptr) = 0 then
result^.syntax_tree := true
if strcmp(parameter^, "--lex\0".ptr) = 0 then
result^.lex := true
elsif strcmp(parameter^, "--parse\0".ptr) = 0 then
result^.parse := true
elsif parameter^^ <> '-' then
if result^.input <> nil then
write_s("Fatal error: Only one source file can be given.\n");
return nil
end;
result^.input := parameter^
else
write_s("Fatal error: Unknown command line options:");
write_s("Fatal error: Unknown command line options: ");
write_c(' ');
write_z(parameter^);
write_s(".\n");
@ -953,7 +982,11 @@ begin
return result
end
proc process(argc: Int, argv: ^^Char) -> Int;
(*
Compilation entry.
*)
proc process(argc: Int, argv: ^^Char) -> Int
var
tokens: ^Token
tokens_size: Word
@ -969,7 +1002,7 @@ begin
end;
if return_code = 0 then
source_code.position := make_position();
source_code.position := Position(1u, 1u);
source_code.input := cast(read_source(command_line^.input): ^Byte);
source_code.empty := source_file_empty;
@ -986,7 +1019,7 @@ begin
fclose(cast(source_code.input: ^SourceFile)^.handle);
if command_line^.tokenize then
if command_line^.lex then
print_tokens(tokens, tokens_size)
end
end;

View File

@ -1,204 +0,0 @@
#include <stdio.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/reboot.h>
#define FILENAME_BUFFER_SIZE 256
size_t read_command(int descriptor, char *command_buffer)
{
ssize_t bytes_read = 0;
size_t read_so_far = 0;
while ((bytes_read = read(descriptor, command_buffer + read_so_far, FILENAME_BUFFER_SIZE - read_so_far - 1)) > 0)
{
read_so_far += bytes_read;
if (read_so_far >= FILENAME_BUFFER_SIZE - 1)
{
break;
}
}
command_buffer[read_so_far] = 0;
return read_so_far;
}
enum status
{
status_success,
status_failure,
status_warning,
status_fatal
};
unsigned int make_path(char *destination, const char *directory, const char *filename, const char *extension)
{
unsigned int i = 0;
for (; i < FILENAME_BUFFER_SIZE; i++)
{
if (directory[i] == 0)
{
break;
}
destination[i] = directory[i];
}
for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++)
{
if (filename[j] == 0)
{
break;
}
destination[i] = filename[j];
}
if (extension == NULL)
{
goto done;
}
for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++)
{
if (extension[j] == 0)
{
break;
}
destination[i] = extension[j];
}
done:
destination[i] = 0;
return i;
}
enum status run_test(const char *file_entry_name)
{
printf("Running %s. ", file_entry_name);
char filename[FILENAME_BUFFER_SIZE];
char command_buffer[FILENAME_BUFFER_SIZE];
char file_buffer[256];
int pipe_ends[2];
if (pipe(pipe_ends) == -1)
{
perror("pipe");
return status_fatal;
}
make_path(filename, "./tests/", file_entry_name, NULL);
int child_pid = fork();
if (child_pid == -1)
{
return status_fatal;
}
else if (child_pid == 0)
{
close(STDIN_FILENO);
close(STDERR_FILENO);
close(pipe_ends[0]); // Close the read end.
if (dup2(pipe_ends[1], STDOUT_FILENO) == -1)
{
perror("dup2");
}
else
{
execl(filename, filename);
perror("execl");
}
close(STDOUT_FILENO);
close(pipe_ends[1]);
_exit(1);
}
else
{
close(pipe_ends[1]); // Close the write end.
read_command(pipe_ends[0], command_buffer);
close(pipe_ends[0]);
int wait_status = 0;
make_path(filename, "./expectations/", file_entry_name, ".txt");
FILE *expectation_descriptor = fopen(filename, "r");
if (expectation_descriptor == NULL)
{
return status_warning;
}
size_t read_from_file = fread(file_buffer, 1, sizeof(file_buffer) - 1, expectation_descriptor);
fclose(expectation_descriptor);
file_buffer[read_from_file] = 0;
for (unsigned int i = 0; ; ++i)
{
if (command_buffer[i] == 0 && file_buffer[i] == 0)
{
fwrite("\n", 1, 1, stdout);
return status_success;
}
else if (command_buffer[i] != file_buffer[i])
{
printf("Failed. Got:\n%s", command_buffer);
return status_failure;
}
}
}
}
struct summary
{
size_t total;
size_t failure;
size_t success;
};
void walk()
{
DIR *directory_stream = opendir("./tests");
struct dirent *file_entry;
struct summary test_summary = { .total = 0, .failure = 0, .success = 0 };
while ((file_entry = readdir(directory_stream)) != NULL)
{
if (file_entry->d_name[0] == '.')
{
continue;
}
++test_summary.total;
switch (run_test(file_entry->d_name))
{
case status_failure:
++test_summary.failure;
break;
case status_success:
++test_summary.success;
break;
case status_warning:
break;
case status_fatal:
goto end_walk;
}
}
printf("Successful: %lu, Failed: %lu, Total: %lu.\n",
test_summary.success, test_summary.failure, test_summary.total);
end_walk:
closedir(directory_stream);
}
int main()
{
int dev_console = open("/dev/console", O_WRONLY);
if (dev_console != -1)
{
dup2(dev_console, STDOUT_FILENO);
walk();
close(dev_console);
}
sync();
reboot(RB_POWER_OFF);
return 1;
}