From 1fff96e088eda39dd7c759b7d059ac11f3d120be Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 31 Aug 2021 21:13:53 +0100 Subject: Make `-insert-semicolon` the default now --- src/main.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 5 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4edbc0420..23c770e37 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1307,7 +1307,7 @@ bool parse_build_flags(Array args) { break; case BuildFlag_InsertSemicolon: - build_context.insert_semicolon = true; + gb_printf_err("-insert-semicolon flag is not required any more\n"); break; case BuildFlag_StrictStyle: @@ -1644,7 +1644,7 @@ void print_show_help(String const arg0, String const &command) { } else if (command == "run") { print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable."); } else if (command == "check") { - print_usage_line(1, "check parse and type check .odin file"); + print_usage_line(1, "check parse and type check .odin file(s)"); } else if (command == "test") { print_usage_line(1, "test build ands runs procedures with the attribute @(test) in the initial package"); } else if (command == "query") { @@ -1656,14 +1656,18 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(3, "odin doc core/path core/path/filepath"); } else if (command == "version") { print_usage_line(1, "version print version"); - } + } else if (command == "strip-semicolon") { + print_usage_line(1, "strip-semicolon"); + print_usage_line(2, "parse and type check .odin file(s) and then remove unneeded semicolons from the entire project"); + } bool doc = command == "doc"; bool build = command == "build"; bool run_or_build = command == "run" || command == "build" || command == "test"; bool test_only = command == "test"; - bool check_only = command == "check"; - bool check = run_or_build || command == "check"; + bool strip_semicolon = command == "strip-semicolon"; + bool check_only = command == "check" || strip_semicolon; + bool check = run_or_build || check_only; print_usage_line(0, ""); print_usage_line(1, "Flags"); @@ -1730,6 +1734,10 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(1, "-keep-temp-files"); print_usage_line(2, "Keeps the temporary files generated during compilation"); print_usage_line(0, ""); + } else if (strip_semicolon) { + print_usage_line(1, "-keep-temp-files"); + print_usage_line(2, "Keeps the temporary files generated during stripping the unneeded semicolons from files"); + print_usage_line(0, ""); } if (check) { @@ -2002,6 +2010,36 @@ bool check_env(void) { return true; } +struct StripSemicolonFile { + String old_fullpath; + String new_fullpath; + AstFile *file; +}; + +int strip_semicolons(Parser *parser) { + #if 0 + isize file_count = 0; + for_array(i, parser->packages) { + AstPackage *pkg = parser->packages[i]; + file_count += pkg->files.count; + } + gb_printf_err("File count: %td\n", file_count); + + auto generated_files = array_make(permanent_allocator(), 0, file_count); + + for_array(i, parser->packages) { + AstPackage *pkg = parser->packages[i]; + for_array(j, pkg->files) { + AstFile *file = pkg->files[j]; + gb_printf_err("%.*s\n", LIT(file->fullpath)); + } + } + + #endif + return 0; +} + + int main(int arg_count, char const **arg_ptr) { #define TIME_SECTION(str) do { debugf("[Section] %s\n", str); timings_start_section(&global_timings, str_lit(str)); } while (0) @@ -2090,6 +2128,14 @@ int main(int arg_count, char const **arg_ptr) { build_context.command_kind = Command_check; build_context.no_output_files = true; init_filename = args[2]; + } else if (command == "strip-semicolon") { + if (args.count < 3) { + usage(args[0]); + return 1; + } + build_context.command_kind = Command_strip_semicolon; + build_context.no_output_files = true; + init_filename = args[2]; } else if (command == "query") { if (args.count < 3) { usage(args[0]); @@ -2209,6 +2255,10 @@ int main(int arg_count, char const **arg_ptr) { if (any_errors()) { return 1; } + + if (build_context.command_kind == Command_strip_semicolon) { + return strip_semicolons(parser); + } if (build_context.generate_docs) { if (global_error_collector.count != 0) { -- cgit v1.2.3 From b176af27427a6c39448a71a8023e4a9877f0a51c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 31 Aug 2021 22:20:36 +0100 Subject: Add semicolon stripping command: `odin strip-semicolon`, has the same parameters as `odin check` --- src/main.cpp | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/parser.cpp | 19 +++++--- src/parser.hpp | 1 + 3 files changed, 145 insertions(+), 11 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 23c770e37..ba09f7b31 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -733,7 +733,7 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_ShowUnusedWithLocation, str_lit("show-unused-with-location"), BuildFlagParam_None, Command_check); add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer, Command_all); - add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None, Command__does_build|Command_strip_semicolon); add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String, Command__does_check); add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String, Command__does_check, true); add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String, Command__does_build); // Commands_build is not used to allow for a better error message @@ -2012,12 +2012,43 @@ bool check_env(void) { struct StripSemicolonFile { String old_fullpath; + String old_fullpath_backup; String new_fullpath; AstFile *file; + i64 written; }; +gbFileError write_file_with_stripped_tokens(gbFile *f, AstFile *file, i64 *written_) { + i64 written = 0; + gbFileError err = gbFileError_None; + u8 const *file_data = file->tokenizer.start; + i32 prev_offset = 0; + i32 const end_offset = cast(i32)(file->tokenizer.end - file->tokenizer.start); + for_array(i, file->tokens) { + Token *token = &file->tokens[i]; + if (token->flags & TokenFlag_Remove) { + i32 offset = token->pos.offset; + i32 to_write = offset-prev_offset; + if (!gb_file_write(f, file_data+prev_offset, to_write)) { + return gbFileError_Invalid; + } + written += to_write; + prev_offset = token_pos_end(*token).offset; + } + } + if (end_offset > prev_offset) { + i32 to_write = end_offset-prev_offset; + if (!gb_file_write(f, file_data+prev_offset, end_offset-prev_offset)) { + return gbFileError_Invalid; + } + written += to_write; + } + + if (written_) *written_ = written; + return err; +} + int strip_semicolons(Parser *parser) { - #if 0 isize file_count = 0; for_array(i, parser->packages) { AstPackage *pkg = parser->packages[i]; @@ -2031,12 +2062,107 @@ int strip_semicolons(Parser *parser) { AstPackage *pkg = parser->packages[i]; for_array(j, pkg->files) { AstFile *file = pkg->files[j]; - gb_printf_err("%.*s\n", LIT(file->fullpath)); + String old_fullpath = copy_string(permanent_allocator(), file->fullpath); + + // assumes .odin extension + String fullpath_base = substring(old_fullpath, 0, old_fullpath.len-5); + + String old_fullpath_backup = concatenate_strings(permanent_allocator(), fullpath_base, str_lit("~backup.odin-temp")); + String new_fullpath = concatenate_strings(permanent_allocator(), fullpath_base, str_lit("~temp.odin-temp")); + + array_add(&generated_files, StripSemicolonFile{old_fullpath, old_fullpath_backup, new_fullpath, file}); } } - #endif - return 0; + isize generated_count = 0; + bool failed = false; + + for_array(i, generated_files) { + auto *file = &generated_files[i]; + char const *filename = cast(char const *)file->new_fullpath.text; + gbFileError err = gbFileError_None; + defer (if (err != gbFileError_None) { + failed = true; + }); + + gbFile f = {}; + err = gb_file_create(&f, filename); + if (err) { + break; + } + defer (err = gb_file_close(&f)); + generated_count += 1; + + i64 written = 0; + defer (gb_file_truncate(&f, written)); + + err = write_file_with_stripped_tokens(&f, file->file, &written); + if (err) { + break; + } + file->written = written; + } + + if (failed) { + for (isize i = 0; i < generated_count; i++) { + auto *file = &generated_files[i]; + char const *filename = nullptr; + filename = cast(char const *)file->new_fullpath.text; + GB_ASSERT_MSG(gb_file_remove(filename), "unable to delete file %s", filename); + } + return 1; + } + + isize overwritten_files = 0; + + for_array(i, generated_files) { + auto *file = &generated_files[i]; + + char const *old_fullpath = cast(char const *)file->old_fullpath.text; + char const *old_fullpath_backup = cast(char const *)file->old_fullpath_backup.text; + char const *new_fullpath = cast(char const *)file->new_fullpath.text; + + if (!gb_file_copy(old_fullpath, old_fullpath_backup, false)) { + gb_printf_err("failed to copy '%s' to '%s'\n", old_fullpath, old_fullpath_backup); + failed = true; + break; + } + + if (!gb_file_copy(new_fullpath, old_fullpath, false)) { + gb_printf_err("failed to move '%s' to '%s' %d\n", old_fullpath, new_fullpath, GetLastError()); + if (!gb_file_copy(old_fullpath_backup, old_fullpath, false)) { + gb_printf_err("failed to restore '%s' from '%s'\n", old_fullpath, old_fullpath_backup); + } + failed = true; + break; + } + + if (!gb_file_remove(old_fullpath_backup)) { + gb_printf_err("failed to remove '%s'\n", old_fullpath_backup); + } + + overwritten_files++; + } + + if (!build_context.keep_temp_files) { + for_array(i, generated_files) { + auto *file = &generated_files[i]; + char const *filename = nullptr; + filename = cast(char const *)file->new_fullpath.text; + GB_ASSERT_MSG(gb_file_remove(filename), "unable to delete file %s", filename); + + filename = cast(char const *)file->old_fullpath_backup.text; + if (gb_file_exists(filename) && !gb_file_remove(filename)) { + if (i < overwritten_files) { + gb_printf_err("unable to delete file %s", filename); + failed = true; + } + } + } + } + + + return cast(int)failed; } diff --git a/src/parser.cpp b/src/parser.cpp index ec1a5776b..2428f7507 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1240,6 +1240,7 @@ Token advance_token(AstFile *f) { f->lead_comment = nullptr; f->line_comment = nullptr; + f->prev_token_index = f->curr_token_index; Token prev = f->prev_token = f->curr_token; bool ok = next_token0(f); @@ -1517,10 +1518,15 @@ void expect_semicolon_newline_error(AstFile *f, Token const &token, Ast *s) { #endif } -void assign_removal_flag_to_semicolon(Token *token) { +void assign_removal_flag_to_semicolon(AstFile *f) { // NOTE(bill): this is used for rewriting files to strip unneeded semicolons - if (token->kind == Token_Semicolon && token->string == ";") { - token->flags |= TokenFlag_Remove; + Token *prev_token = &f->tokens[f->prev_token_index]; + Token *curr_token = &f->tokens[f->curr_token_index]; + GB_ASSERT(prev_token->kind == Token_Semicolon); + if (prev_token->string == ";") { + if (curr_token->pos.line > prev_token->pos.line) { + prev_token->flags |= TokenFlag_Remove; + } } } @@ -1528,8 +1534,8 @@ void expect_semicolon(AstFile *f, Ast *s) { Token prev_token = {}; if (allow_token(f, Token_Semicolon)) { + assign_removal_flag_to_semicolon(f); expect_semicolon_newline_error(f, f->prev_token, s); - assign_removal_flag_to_semicolon(&f->prev_token); return; } switch (f->curr_token.kind) { @@ -1543,8 +1549,8 @@ void expect_semicolon(AstFile *f, Ast *s) { prev_token = f->prev_token; if (prev_token.kind == Token_Semicolon) { + assign_removal_flag_to_semicolon(f); expect_semicolon_newline_error(f, f->prev_token, s); - assign_removal_flag_to_semicolon(&f->prev_token); return; } @@ -4707,8 +4713,9 @@ ParseFileError init_ast_file(AstFile *f, String fullpath, TokenPos *err_pos) { u64 end = time_stamp_time_now(); f->time_to_tokenize = cast(f64)(end-start)/cast(f64)time_stamp__freq(); + f->prev_token_index = 0; f->curr_token_index = 0; - f->prev_token = f->tokens[f->curr_token_index]; + f->prev_token = f->tokens[f->prev_token_index]; f->curr_token = f->tokens[f->curr_token_index]; isize const page_size = 4*1024; diff --git a/src/parser.hpp b/src/parser.hpp index ddcb27322..6ce877a6c 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -102,6 +102,7 @@ struct AstFile { Tokenizer tokenizer; Array tokens; isize curr_token_index; + isize prev_token_index; Token curr_token; Token prev_token; // previous non-comment Token package_token; -- cgit v1.2.3 From 2db6fea6655215452d445f327cadda815d65afd9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 31 Aug 2021 22:31:43 +0100 Subject: Remove dead code and add debug messages --- src/main.cpp | 12 +++++++++++- src/parser.cpp | 33 ++++++++------------------------- 2 files changed, 19 insertions(+), 26 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index ba09f7b31..b4e6c08af 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2054,7 +2054,7 @@ int strip_semicolons(Parser *parser) { AstPackage *pkg = parser->packages[i]; file_count += pkg->files.count; } - gb_printf_err("File count: %td\n", file_count); + gb_printf_err("File count to be stripped of unneeded tokens: %td\n", file_count); auto generated_files = array_make(permanent_allocator(), 0, file_count); @@ -2096,6 +2096,7 @@ int strip_semicolons(Parser *parser) { i64 written = 0; defer (gb_file_truncate(&f, written)); + debugf("Write file with stripped tokens: %s\n", filename); err = write_file_with_stripped_tokens(&f, file->file, &written); if (err) { break; @@ -2122,14 +2123,17 @@ int strip_semicolons(Parser *parser) { char const *old_fullpath_backup = cast(char const *)file->old_fullpath_backup.text; char const *new_fullpath = cast(char const *)file->new_fullpath.text; + debugf("Copy '%s' to '%s'\n", old_fullpath, old_fullpath_backup); if (!gb_file_copy(old_fullpath, old_fullpath_backup, false)) { gb_printf_err("failed to copy '%s' to '%s'\n", old_fullpath, old_fullpath_backup); failed = true; break; } + debugf("Copy '%s' to '%s'\n", new_fullpath, old_fullpath); if (!gb_file_copy(new_fullpath, old_fullpath, false)) { gb_printf_err("failed to move '%s' to '%s' %d\n", old_fullpath, new_fullpath, GetLastError()); + debugf("Copy '%s' to '%s'\n", old_fullpath_backup, old_fullpath); if (!gb_file_copy(old_fullpath_backup, old_fullpath, false)) { gb_printf_err("failed to restore '%s' from '%s'\n", old_fullpath, old_fullpath_backup); } @@ -2137,6 +2141,7 @@ int strip_semicolons(Parser *parser) { break; } + debugf("Remove '%s'\n", old_fullpath_backup); if (!gb_file_remove(old_fullpath_backup)) { gb_printf_err("failed to remove '%s'\n", old_fullpath_backup); } @@ -2149,9 +2154,12 @@ int strip_semicolons(Parser *parser) { auto *file = &generated_files[i]; char const *filename = nullptr; filename = cast(char const *)file->new_fullpath.text; + + debugf("Remove '%s'\n", filename); GB_ASSERT_MSG(gb_file_remove(filename), "unable to delete file %s", filename); filename = cast(char const *)file->old_fullpath_backup.text; + debugf("Remove '%s'\n", filename); if (gb_file_exists(filename) && !gb_file_remove(filename)) { if (i < overwritten_files) { gb_printf_err("unable to delete file %s", filename); @@ -2161,6 +2169,8 @@ int strip_semicolons(Parser *parser) { } } + gb_printf_err("Files stripped of unneeded token: %td\n", file_count); + return cast(int)failed; } diff --git a/src/parser.cpp b/src/parser.cpp index 2428f7507..fe334e4c7 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1559,31 +1559,14 @@ void expect_semicolon(AstFile *f, Ast *s) { } if (s != nullptr) { - switch (f->curr_token.kind) { - case Token_CloseBrace: - case Token_CloseParen: - case Token_else: - case Token_EOF: - return; - - default: - if (is_semicolon_optional_for_node(f, s)) { - return; - } - break; - } - String node_string = ast_strings[s->kind]; - String p = token_to_string(f->curr_token); - syntax_error(prev_token, "Expected ';' after %.*s, got %.*s", - LIT(node_string), LIT(p)); - } else { - switch (f->curr_token.kind) { - case Token_EOF: - return; - } - String p = token_to_string(f->curr_token); - syntax_error(prev_token, "Expected ';', got %.*s", LIT(p)); + return; + } + switch (f->curr_token.kind) { + case Token_EOF: + return; } + String p = token_to_string(f->curr_token); + syntax_error(prev_token, "Expected ';', got %.*s", LIT(p)); fix_advance_to_next_stmt(f); } @@ -4590,7 +4573,7 @@ Ast *parse_stmt(AstFile *f) { case Token_Semicolon: s = ast_empty_stmt(f, token); - advance_token(f); + expect_semicolon(f, nullptr); return s; } -- cgit v1.2.3 From cd09068e335c8446a9d48dd1bd9868989e531c5e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 31 Aug 2021 22:45:08 +0100 Subject: Correct parsing rules for `#assert` directives for semicolons --- src/build_settings.cpp | 2 ++ src/main.cpp | 8 ++++---- src/parser.cpp | 44 ++++++++++++-------------------------------- 3 files changed, 18 insertions(+), 36 deletions(-) (limited to 'src/main.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index c614a6dc8..728cf5077 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -205,6 +205,8 @@ struct BuildContext { bool keep_object_files; bool disallow_do; + bool strict_style; + bool ignore_warnings; bool warnings_as_errors; diff --git a/src/main.cpp b/src/main.cpp index b4e6c08af..6a0bf381c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1308,11 +1308,11 @@ bool parse_build_flags(Array args) { case BuildFlag_InsertSemicolon: gb_printf_err("-insert-semicolon flag is not required any more\n"); + bad_flags = true; break; case BuildFlag_StrictStyle: - gb_printf_err("-strict-style flag is not required any more\n"); - bad_flags = true; + build_context.strict_style = true; break; @@ -1870,8 +1870,8 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(2, "Sets the default allocator to be the nil_allocator, an allocator which does nothing"); print_usage_line(0, ""); - print_usage_line(1, "-insert-semicolon"); - print_usage_line(2, "Inserts semicolons on newlines during tokenization using a basic rule"); + print_usage_line(1, "-strict-style"); + print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons"); print_usage_line(0, ""); print_usage_line(1, "-ignore-warnings"); diff --git a/src/parser.cpp b/src/parser.cpp index fe334e4c7..fe421f7db 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1491,33 +1491,6 @@ Token expect_closing(AstFile *f, TokenKind kind, String context) { return expect_token(f, kind); } -bool is_semicolon_optional_for_node(AstFile *f, Ast *s) { - if (s == nullptr) { - return false; - } - return true; -} - -void expect_semicolon_newline_error(AstFile *f, Token const &token, Ast *s) { - #if 0 - if (!build_context.insert_semicolon && token.string == "\n") { - switch (token.kind) { - case Token_CloseBrace: - case Token_CloseParen: - case Token_else: - return; - } - if (is_semicolon_optional_for_node(f, s)) { - return; - } - - Token tok = token; - tok.pos.column -= 1; - syntax_error(tok, "Expected ';', got newline"); - } - #endif -} - void assign_removal_flag_to_semicolon(AstFile *f) { // NOTE(bill): this is used for rewriting files to strip unneeded semicolons Token *prev_token = &f->tokens[f->prev_token_index]; @@ -1525,6 +1498,9 @@ void assign_removal_flag_to_semicolon(AstFile *f) { GB_ASSERT(prev_token->kind == Token_Semicolon); if (prev_token->string == ";") { if (curr_token->pos.line > prev_token->pos.line) { + if (build_context.strict_style) { + syntax_error(*prev_token, "Found unneeded semicolon"); + } prev_token->flags |= TokenFlag_Remove; } } @@ -1535,7 +1511,6 @@ void expect_semicolon(AstFile *f, Ast *s) { if (allow_token(f, Token_Semicolon)) { assign_removal_flag_to_semicolon(f); - expect_semicolon_newline_error(f, f->prev_token, s); return; } switch (f->curr_token.kind) { @@ -1550,7 +1525,6 @@ void expect_semicolon(AstFile *f, Ast *s) { prev_token = f->prev_token; if (prev_token.kind == Token_Semicolon) { assign_removal_flag_to_semicolon(f); - expect_semicolon_newline_error(f, f->prev_token, s); return; } @@ -4545,14 +4519,20 @@ Ast *parse_stmt(AstFile *f) { return s; } else if (tag == "assert") { Ast *t = ast_basic_directive(f, hash_token, name); - return ast_expr_stmt(f, parse_call_expr(f, t)); + Ast *stmt = ast_expr_stmt(f, parse_call_expr(f, t)); + expect_semicolon(f, stmt); + return stmt; } else if (tag == "panic") { Ast *t = ast_basic_directive(f, hash_token, name); - return ast_expr_stmt(f, parse_call_expr(f, t)); + Ast *stmt = ast_expr_stmt(f, parse_call_expr(f, t)); + expect_semicolon(f, stmt); + return stmt; } else if (name.string == "force_inline" || name.string == "force_no_inline") { Ast *expr = parse_force_inlining_operand(f, name); - return ast_expr_stmt(f, expr); + Ast *stmt = ast_expr_stmt(f, expr); + expect_semicolon(f, stmt); + return stmt; } else if (tag == "unroll") { return parse_unrolled_for_loop(f, name); } else if (tag == "include") { -- cgit v1.2.3 From 169e717021d6b0f1016360c820bc81e801bf1e53 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 31 Aug 2021 22:49:18 +0100 Subject: Remove debug message --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 6a0bf381c..8df722437 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2132,7 +2132,7 @@ int strip_semicolons(Parser *parser) { debugf("Copy '%s' to '%s'\n", new_fullpath, old_fullpath); if (!gb_file_copy(new_fullpath, old_fullpath, false)) { - gb_printf_err("failed to move '%s' to '%s' %d\n", old_fullpath, new_fullpath, GetLastError()); + gb_printf_err("failed to copy '%s' to '%s'\n", old_fullpath, new_fullpath); debugf("Copy '%s' to '%s'\n", old_fullpath_backup, old_fullpath); if (!gb_file_copy(old_fullpath_backup, old_fullpath, false)) { gb_printf_err("failed to restore '%s' from '%s'\n", old_fullpath, old_fullpath_backup); -- cgit v1.2.3