diff --git a/include/autoexec.h b/include/autoexec.h new file mode 100644 index 00000000000..53d697f14c9 --- /dev/null +++ b/include/autoexec.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020-2023 The DOSBox Staging Team + * Copyright (C) 2002-2021 The DOSBox Team + * + * This program is free software; 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef DOSBOX_AUTOEXEC_H +#define DOSBOX_AUTOEXEC_H + +#include "setup.h" + +#include + +// Registers the AUTOEXEC.BAT file on the emulated Z: drive. +// From now on, the only changes to the file content which became visible on the +// guest side, are DOS code page changes. +// TODO: change this, every environment variable modification or [autoexec] +// modification by CONFIG.COM command should be reflected; this will require +// careful checking of our COMMAND.COM iomplementattion in order not break +// anything when the change happens during AUTOEXEC.BAT execution. +void AUTOEXEC_RegisterFile(); + +// Notify, that DOS display code page has changed, and the AUTOEXEC.BAT content +// might need to be refreshed from the original (internal) UTF-8 version. +void AUTOEXEC_NotifyNewCodePage(); + +// Adds/updates environment variable to the AUTOEXEC.BAT file. Empty value +// removes the variable. If a shell is already running, it the environment is +// updated accordingly. +// The 'name' and 'value' have to be a printable, 7-bit ASCII variables. +void AUTOEXEC_SetVariable(const std::string& name, const std::string& value); + +void AUTOEXEC_Init(Section* sec); + +#endif diff --git a/include/dos_system.h b/include/dos_system.h index a995c55d858..db04a3ee19a 100644 --- a/include/dos_system.h +++ b/include/dos_system.h @@ -417,6 +417,9 @@ void VFILE_Register(const char *name, const uint8_t *data, const uint32_t size, const char *dir = ""); +void VFILE_Register(const char* name, const std::vector& blob, + const char* dir = ""); +void VFILE_Update(const char* name, std::vector blob, const char* dir = ""); +void VFILE_Remove(const char* name, const char* dir = ""); -void VFILE_Register(const char *name, const std::vector &blob, const char *dir); #endif diff --git a/include/programs.h b/include/programs.h index a4bd37d6416..84c5baa8e7a 100644 --- a/include/programs.h +++ b/include/programs.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2020-2023 The DOSBox Staging Team * Copyright (C) 2002-2021 The DOSBox Team * * This program is free software; you can redistribute it and/or modify @@ -33,8 +34,6 @@ #define WIKI_URL "https://github.com/dosbox-staging/dosbox-staging/wiki" #define WIKI_ADD_UTILITIES_ARTICLE WIKI_URL "/Add-Utilities" -constexpr int autoexec_maxsize = 4096; - class CommandLine { public: CommandLine(int argc, char const *const argv[]); diff --git a/include/shell.h b/include/shell.h index ba0a4eb1796..9b4a036d7ea 100644 --- a/include/shell.h +++ b/include/shell.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2020-2023 The DOSBox Staging Team * Copyright (C) 2002-2021 The DOSBox Team * * This program is free software; you can redistribute it and/or modify @@ -153,25 +154,8 @@ class DOS_Shell : public Program { bool call = false; }; -/* Object to manage lines in the autoexec.bat The lines get removed from - * the file if the object gets destroyed. The environment is updated - * as well if the line set a a variable */ -class AutoexecObject{ -private: - bool installed = false; - std::string buf = {}; - -public: - AutoexecObject() = default; - AutoexecObject(const std::string& line); - ~AutoexecObject(); - - void Install(const std::string& in); - void InstallBefore(const std::string& in); - -private: - void CreateAutoexec(); -}; +std::tuple parse_drive_conf( + std::string drive_letter, const std_fs::path& conf_path); // Localized output diff --git a/src/dos/dos_keyboard_layout.cpp b/src/dos/dos_keyboard_layout.cpp index b7a4b251b58..f2333faa5b4 100644 --- a/src/dos/dos_keyboard_layout.cpp +++ b/src/dos/dos_keyboard_layout.cpp @@ -25,6 +25,7 @@ using sv = std::string_view; #include "../ints/int10.h" +#include "autoexec.h" #include "bios.h" #include "bios_disk.h" #include "callback.h" @@ -1065,6 +1066,9 @@ KeyboardErrorCode KeyboardLayout::ReadCodePageFile(const char *requested_cp_file } INT10_SetupRomMemoryChecksum(); + // convert UTF-8 [autoexec] section to new code page + AUTOEXEC_NotifyNewCodePage(); + return KEYB_NOERROR; } @@ -1697,6 +1701,9 @@ class DOS_KeyboardLayout final : public Module_base { LOG_MSG("LAYOUT: DOS keyboard layout loaded with main language code %s for layout %s",lcode,layoutname); } } + + // Convert UTF-8 [autoexec] section to new code page + AUTOEXEC_NotifyNewCodePage(); } ~DOS_KeyboardLayout(){ diff --git a/src/dos/dos_programs.cpp b/src/dos/dos_programs.cpp index ff90a6251ba..dec784bf86c 100644 --- a/src/dos/dos_programs.cpp +++ b/src/dos/dos_programs.cpp @@ -21,6 +21,7 @@ #include "programs.h" +#include "autoexec.h" #include "program_attrib.h" #include "program_autotype.h" #include "program_boot.h" @@ -47,7 +48,6 @@ extern uint32_t floppytype; -extern char autoexec_data[autoexec_maxsize]; std::unique_ptr CONFIG_ProgramCreate(); std::unique_ptr MIXER_ProgramCreate(); std::unique_ptr SHELL_ProgramCreate(); @@ -91,8 +91,10 @@ void Add_VFiles(const bool add_autoexec) PROGRAMS_MakeFile("SERIAL.COM", ProgramCreate); PROGRAMS_MakeFile("TREE.COM", ProgramCreate); PROGRAMS_MakeFile("COMMAND.COM", SHELL_ProgramCreate); - if (add_autoexec) - VFILE_Register("AUTOEXEC.BAT", (uint8_t *)autoexec_data, (uint32_t)strlen(autoexec_data)); + + if (add_autoexec) { + AUTOEXEC_RegisterFile(); + } } void DOS_SetupPrograms(void) diff --git a/src/dos/drive_virtual.cpp b/src/dos/drive_virtual.cpp index 44d5085b21f..3e6fefba3f2 100644 --- a/src/dos/drive_virtual.cpp +++ b/src/dos/drive_virtual.cpp @@ -1,4 +1,5 @@ /* + * Copyright (C) 2021-2023 The DOSBox Staging Team * Copyright (C) 2002-2021 The DOSBox Team * * This program is free software; you can redistribute it and/or modify @@ -48,37 +49,28 @@ extern DOS_Shell *first_shell; class VFILE_Block; using vfile_block_t = std::shared_ptr; +using vfile_data_t = std::shared_ptr>; -class VFILE_Block { +class VFILE_Block final { public: std::string name = {}; - uint8_t* data = nullptr; + vfile_data_t data = nullptr; - uint32_t size = 0; uint16_t date = 0; uint16_t time = 0; - unsigned int onpos = 0; + unsigned int position = 0; bool isdir = false; vfile_block_t next = {}; - ~VFILE_Block(); + ~VFILE_Block() = default; }; static vfile_block_t first_file = {}; static vfile_block_t parent_dir = {}; -VFILE_Block::~VFILE_Block() -{ - // Release the vfile's data (allocated with new[]) - if (data) { - delete[] data; - data = nullptr; - } -} - // this gets replaced with std::find_if later template vfile_block_t find_vfile_by_predicate(vfile_block_t head_file, Predicate predicate_) @@ -93,19 +85,39 @@ vfile_block_t find_vfile_by_predicate(vfile_block_t head_file, Predicate predica return {}; } -vfile_block_t find_vfile_by_name_and_pos(const std::string& name, unsigned int onpos) +vfile_block_t find_vfile_by_name_and_pos(const std::string& name, unsigned int position) { - return find_vfile_by_predicate(first_file, [name, onpos](vfile_block_t vfile) { - return onpos == vfile->onpos && iequals(name, vfile->name); + return find_vfile_by_predicate(first_file, [name, position](vfile_block_t vfile) { + return position == vfile->position && iequals(name, vfile->name); }); } -bool vfile_name_and_pos_exists(const std::string& name, unsigned int onpos) +vfile_block_t find_vfile_by_name_and_dir(const char* name, const char* dir) { - return find_vfile_by_name_and_pos(name, onpos).get(); + assert(name); + assert(dir); + unsigned int position = 0; + if (*dir) { + for (unsigned int i = 1; i < vfile_pos; i++) { + if (!strcasecmp(vfilenames[i].shortname.c_str(), dir) || + !strcasecmp(vfilenames[i].fullname.c_str(), dir)) { + position = i; + break; + } + } + if (position == 0) { + return {}; + } + } + return find_vfile_by_name_and_pos(name, position); +} + +bool vfile_name_and_pos_exists(const std::string& name, unsigned int position) +{ + return find_vfile_by_name_and_pos(name, position).get(); } -char* VFILE_Generate_8x3(const char* name, const unsigned int onpos) +char* VFILE_Generate_8x3(const char* name, const unsigned int position) { if (!name || !*name) { reset_str(sfn); @@ -130,7 +142,7 @@ char* VFILE_Generate_8x3(const char* name, const unsigned int onpos) } // Return if 8.3 name does not already exist - if (!vfile_name_and_pos_exists(sfn, onpos)) { + if (!vfile_name_and_pos_exists(sfn, position)) { return sfn; } num++; @@ -150,28 +162,28 @@ void VFILE_Register(const char *name, const auto isdir = !strcmp(dir, "/") || !strcmp(name, ".") || !strcmp(name, ".."); const auto len = strlen(dir); - unsigned int onpos = 0; + unsigned int position = 0; if (len > 2 && dir[0] == '/' && dir[len - 1] == '/') { for (unsigned int i = 1; i < vfile_pos; i++) if (!strcasecmp((vfilenames[i].shortname + "/").c_str(), dir + 1) || !strcasecmp((vfilenames[i].fullname + "/").c_str(), dir + 1)) { - onpos = i; + position = i; break; } - if (onpos == 0) + if (position == 0) return; } - if (vfile_name_and_pos_exists(name, onpos)) { + if (vfile_name_and_pos_exists(name, position)) { return; } Filename filename; filename.fullname = name; filename.shortname = filename_not_strict_8x3(name) - ? VFILE_Generate_8x3(name, onpos) - : name; + ? VFILE_Generate_8x3(name, position) + : name; trim(filename.fullname); trim(filename.shortname); vfilenames.push_back(filename); @@ -181,14 +193,15 @@ void VFILE_Register(const char *name, auto new_file = std::make_shared(); new_file->name = vfilenames[vfile_pos].shortname; vfile_pos++; - new_file->data = data ? new (std::nothrow) uint8_t[size] : nullptr; - if (new_file->data) - memcpy(new_file->data, data, size); - new_file->size = size; - new_file->date = fztime || fzdate ? fzdate : default_date; - new_file->time = fztime || fzdate ? fztime : default_time; - new_file->onpos = onpos; - new_file->isdir = isdir; + if (data && size > 0) { + new_file->data = std::make_shared>(data, data + size); + } else { + new_file->data = std::make_shared>(); + } + new_file->date = fztime || fzdate ? fzdate : default_date; + new_file->time = fztime || fzdate ? fztime : default_time; + new_file->position = position; + new_file->isdir = isdir; new_file->next = first_file; first_file = new_file; } @@ -198,22 +211,19 @@ void VFILE_Register(const char *name, const std::vector &blob, const ch VFILE_Register(name, blob.data(), check_cast(blob.size()), dir); } -void VFILE_Remove(const char *name, const char *dir = "") +void VFILE_Update(const char* name, std::vector blob, const char* dir) { - assert(name); - assert(dir); - unsigned int onpos = 0; - if (*dir) { - for (unsigned int i = 1; i < vfile_pos; i++) - if (!strcasecmp(vfilenames[i].shortname.c_str(), dir) || - !strcasecmp(vfilenames[i].fullname.c_str(), dir)) { - onpos = i; - break; - } - if (onpos == 0) - return; + auto vfile = find_vfile_by_name_and_dir(name, dir); + + if (vfile) { + vfile->data = std::make_shared>(std::move(blob)); } - auto vfile = find_vfile_by_name_and_pos(name, onpos); +} + +void VFILE_Remove(const char* name, const char* dir) +{ + auto vfile = find_vfile_by_name_and_dir(name, dir); + if (vfile) { if (vfile.get() == first_file.get()) { first_file = vfile->next; @@ -311,7 +321,7 @@ void VFILE_RegisterZDrive(const std_fs::path &z_drive_path) class Virtual_File final : public DOS_File { public: - Virtual_File(uint8_t *in_data, uint32_t in_size); + Virtual_File(const vfile_data_t& in_data); Virtual_File(const Virtual_File &) = delete; // prevent copying Virtual_File &operator=(const Virtual_File &) = delete; // prevent assignment @@ -323,15 +333,13 @@ class Virtual_File final : public DOS_File { uint16_t GetInformation(); private: - uint32_t file_size; + const vfile_data_t file_data; uint32_t file_pos; - uint8_t *file_data; }; -Virtual_File::Virtual_File(uint8_t *in_data, uint32_t in_size) - : file_size(in_size), - file_pos(0), - file_data(in_data) +Virtual_File::Virtual_File(const vfile_data_t& in_data) + : file_data(in_data), + file_pos(0) { date = default_date; time = default_time; @@ -339,46 +347,57 @@ Virtual_File::Virtual_File(uint8_t *in_data, uint32_t in_size) } bool Virtual_File::Read(uint8_t * data,uint16_t * size) { - uint32_t left=file_size-file_pos; + uint32_t left = file_data->size() - file_pos; if (left <= *size) { - memcpy(data, &file_data[file_pos], left); + memcpy(data, &((*file_data)[file_pos]), left); *size = (uint16_t)left; } else { - memcpy(data, &file_data[file_pos], *size); + memcpy(data, &((*file_data)[file_pos]), *size); } file_pos += *size; return true; } -bool Virtual_File::Write(uint8_t * /*data*/,uint16_t * /*size*/){ +bool Virtual_File::Write(uint8_t* /*data*/, uint16_t* /*size*/) +{ /* Not really writable */ return false; } -bool Virtual_File::Seek(uint32_t * new_pos,uint32_t type){ +bool Virtual_File::Seek(uint32_t* new_pos, uint32_t type) +{ switch (type) { case DOS_SEEK_SET: - if (*new_pos <= file_size) file_pos = *new_pos; - else return false; + if (*new_pos <= file_data->size()) { + file_pos = *new_pos; + } else { + return false; + } break; case DOS_SEEK_CUR: - if ((*new_pos + file_pos) <= file_size) file_pos = *new_pos + file_pos; - else return false; + if ((*new_pos + file_pos) <= file_data->size()) { + file_pos = *new_pos + file_pos; + } else { + return false; + } break; case DOS_SEEK_END: - if (*new_pos <= file_size) file_pos = file_size - *new_pos; - else return false; + if (*new_pos <= file_data->size()) { + file_pos = file_data->size() - *new_pos; + } else { + return false; + } break; } *new_pos = file_pos; return true; } -bool Virtual_File::Close(){ +bool Virtual_File::Close() +{ return true; } - uint16_t Virtual_File::GetInformation() { return 0x40; // read-only drive } @@ -397,11 +416,11 @@ bool Virtual_Drive::FileOpen(DOS_File * * file,char * name,uint32_t flags) { DOS_SetError(DOSERR_ACCESS_DENIED); return false; } -/* Scan through the internal list of files */ + /* Scan through the internal list of files */ auto vfile = find_vfile_by_name(name); if (vfile) { /* We have a match */ - *file = new Virtual_File(vfile->data, vfile->size); + *file = new Virtual_File(vfile->data); (*file)->flags = flags; return true; } @@ -441,7 +460,7 @@ bool Virtual_Drive::FileStat(const char* name, FileStat_Block * const stat_block if (vfile) { stat_block->attr = (int)(vfile->isdir ? DOS_ATTR_DIRECTORY : DOS_ATTR_ARCHIVE); - stat_block->size = vfile->size; + stat_block->size = vfile->data->size(); stat_block->date = default_date; stat_block->time = default_time; return true; @@ -461,30 +480,30 @@ bool Virtual_Drive::FileExists(const char* name){ bool Virtual_Drive::FindFirst(char *_dir, DOS_DTA &dta, bool fcb_findfirst) { assert(_dir); - unsigned int onpos = 0; + unsigned int position = 0; if (*_dir) { if (FileExists(_dir)) { DOS_SetError(DOSERR_FILE_NOT_FOUND); return false; } for (unsigned int i = 1; i < vfile_pos; i++) { - if (!strcasecmp(vfilenames[i].shortname.c_str(), _dir) || - !strcasecmp(vfilenames[i].fullname.c_str(), _dir)) { - onpos = i; + if (iequals(vfilenames[i].shortname, _dir) || + iequals(vfilenames[i].fullname, _dir)) { + position = i; break; } } - if (!onpos) { + if (!position) { DOS_SetError(DOSERR_PATH_NOT_FOUND); return false; } } - dta.SetDirID(onpos); + dta.SetDirID(position); uint8_t attr; char pattern[DOS_NAMELENGTH_ASCII]; dta.GetSearchParams(attr, pattern); - search_file = (attr & DOS_ATTR_DIRECTORY) && onpos > 0 ? parent_dir - : first_file; + search_file = (attr & DOS_ATTR_DIRECTORY) && position > 0 ? parent_dir + : first_file; if (attr == DOS_ATTR_VOLUME) { dta.SetResult(GetLabel(), 0, 0, 0, DOS_ATTR_VOLUME); return true; @@ -493,7 +512,7 @@ bool Virtual_Drive::FindFirst(char *_dir, DOS_DTA &dta, bool fcb_findfirst) dta.SetResult(GetLabel(), 0, 0, 0, DOS_ATTR_VOLUME); return true; } - } else if ((attr & DOS_ATTR_DIRECTORY) && onpos > 0) { + } else if ((attr & DOS_ATTR_DIRECTORY) && position > 0) { if (WildFileCmp(".", pattern)) { dta.SetResult(".", 0, default_date, default_time, DOS_ATTR_DIRECTORY); @@ -508,7 +527,7 @@ vfile_block_t find_vfile_by_atribute_pattern_and_pos(vfile_block_t head_file, unsigned int pos) { return find_vfile_by_predicate(head_file, [pos, attr, pattern](vfile_block_t vfile) { - return pos == vfile->onpos && + return pos == vfile->position && ((attr & DOS_ATTR_DIRECTORY) || !vfile->isdir) && WildFileCmp(vfile->name.c_str(), pattern); }); @@ -535,7 +554,7 @@ bool Virtual_Drive::FindNext(DOS_DTA& dta) pos); if (search_file) { dta.SetResult(search_file->name.c_str(), - search_file->size, + search_file->data->size(), search_file->date, search_file->time, (int)(search_file->isdir ? DOS_ATTR_DIRECTORY @@ -610,10 +629,10 @@ const char* Virtual_Drive::GetLabel() bool Virtual_Drive::is_name_equal(const vfile_block_t vfile, const char* name) const { - unsigned int onpos = vfile->onpos; + unsigned int position = vfile->position; return iequals(name, - onpos ? vfilenames[onpos].shortname + "\\" + vfile->name - : vfile->name); + position ? vfilenames[position].shortname + "\\" + vfile->name + : vfile->name); } vfile_block_t Virtual_Drive::find_vfile_by_name(const char* name) const diff --git a/src/hardware/gus.cpp b/src/hardware/gus.cpp index 30ffa3a2809..02727894540 100644 --- a/src/hardware/gus.cpp +++ b/src/hardware/gus.cpp @@ -28,6 +28,7 @@ #include #include +#include "autoexec.h" #include "control.h" #include "dma.h" #include "hardware.h" @@ -114,7 +115,6 @@ struct VoiceCtrl { // Collection types involving constant quantities using address_array_t = std::array; -using autoexec_array_t = std::array, 2>; using pan_scalars_array_t = std::array; using ram_array_t = std::vector; using read_io_array_t = std::array; @@ -271,7 +271,6 @@ class Gus { read_io_array_t read_handlers = {}; write_io_array_t write_handlers = {}; voice_array_t voices = {{nullptr}}; - autoexec_array_t autoexec_lines = {}; const address_array_t dma_addresses = { {MIN_DMA_ADDRESS, 1, 3, 5, 6, MAX_IRQ_ADDRESS, 0, 0}}; @@ -937,34 +936,19 @@ void Gus::SetupEnvironment(uint16_t port, const char* ultradir_env_val) assert(dma1 < 10 && dma2 < 10); assert(irq1 <= 12 && irq2 <= 12); - const std::string at_set = "@SET"; - // ULTRASND variable char ultrasnd_env_val[] = "HHH,D,D,II,II"; safe_sprintf(ultrasnd_env_val, "%x,%u,%u,%u,%u", port, dma1, dma2, irq1, irq2); - const auto ultrasnd_line = at_set + " " + ultrasnd_env_name + "=" + - ultrasnd_env_val; - autoexec_lines.at(0) = std::make_unique(ultrasnd_line); + AUTOEXEC_SetVariable(ultrasnd_env_name, ultrasnd_env_val); // ULTRADIR variable - const auto ultradir_line = at_set + " " + ultradir_env_name + "=" + - ultradir_env_val; - autoexec_lines.at(1) = std::make_unique(ultradir_line); - - if (first_shell) { - first_shell->SetEnv(ultrasnd_env_name, ultrasnd_env_val); - first_shell->SetEnv(ultradir_env_name, ultradir_env_val); - } + AUTOEXEC_SetVariable(ultradir_env_name, ultradir_env_val); } void Gus::ClearEnvironment() { - autoexec_lines = {}; - - if (first_shell) { - first_shell->SetEnv(ultrasnd_env_name, ""); - first_shell->SetEnv(ultradir_env_name, ""); - } + AUTOEXEC_SetVariable(ultrasnd_env_name, ""); + AUTOEXEC_SetVariable(ultradir_env_name, ""); } // Generate logarithmic to linear volume conversion tables diff --git a/src/hardware/ipx.cpp b/src/hardware/ipx.cpp index bb01d66a4e8..a6d0796ce22 100644 --- a/src/hardware/ipx.cpp +++ b/src/hardware/ipx.cpp @@ -1088,7 +1088,6 @@ Bitu IPX_ESRHandler(void) { return CBRET_NONE; } -void VFILE_Remove(const char *name, const char *dir = ""); bool NetWrapper_InitializeSDLNet(); // from misc_util.cpp class IPX final : public Module_base { diff --git a/src/hardware/sblaster.cpp b/src/hardware/sblaster.cpp index 2bf1791e8f6..4a159559106 100644 --- a/src/hardware/sblaster.cpp +++ b/src/hardware/sblaster.cpp @@ -1,4 +1,5 @@ /* + * Copyright (C) 2019-2023 The DOSBox Staging Team * Copyright (C) 2002-2021 The DOSBox Team * * This program is free software; you can redistribute it and/or modify @@ -25,6 +26,7 @@ #include #include +#include "autoexec.h" #include "control.h" #include "dma.h" #include "hardware.h" @@ -2111,9 +2113,6 @@ class SBLASTER final { IO_WriteHandleObject write_handlers[0x10] = {}; static constexpr auto blaster_env_name = "BLASTER"; - - std::unique_ptr autoexec_line = {}; - OplMode oplmode = OplMode::None; void SetupEnvironment() @@ -2125,8 +2124,6 @@ class SBLASTER final { assert(sb.hw.irq <= 12); assert(sb.hw.dma8 < 10); - const std::string at_set = "@SET"; - char blaster_env_val[] = "AHHH II DD HH TT"; if (sb.type == SBT_16) { @@ -2147,22 +2144,14 @@ class SBLASTER final { static_cast(sb.type)); } + // Update AUTOEXEC.BAT line LOG_MSG("%s: %s=%s", CardType(), blaster_env_name, blaster_env_val); - autoexec_line = std::make_unique( - at_set + " " + blaster_env_name + "=" + blaster_env_val); - - if (first_shell) { - first_shell->SetEnv(blaster_env_name, blaster_env_val); - } + AUTOEXEC_SetVariable(blaster_env_name, blaster_env_val); } void ClearEnvironment() { - autoexec_line = {}; - - if (first_shell) { - first_shell->SetEnv(blaster_env_name, ""); - } + AUTOEXEC_SetVariable(blaster_env_name, ""); } public: diff --git a/src/shell/autoexec.cpp b/src/shell/autoexec.cpp new file mode 100644 index 00000000000..33b66d0c31a --- /dev/null +++ b/src/shell/autoexec.cpp @@ -0,0 +1,584 @@ +/* + * Copyright (C) 2020-2023 The DOSBox Staging Team + * Copyright (C) 2002-2021 The DOSBox Team + * + * This program is free software; 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "autoexec.h" + +#include "checks.h" +#include "control.h" +#include "dosbox.h" +#include "fs_utils.h" +#include "setup.h" +#include "shell.h" +#include "string_utils.h" + +#include +#include +#include + +CHECK_NARROWING(); + +// *************************************************************************** +// Constants +// *************************************************************************** + +static const std::string autoexec_file_name = "AUTOEXEC.BAT"; + +constexpr char char_lf = 0x0a; // line feed +constexpr char char_cr = 0x0d; // carriage return + +// *************************************************************************** +// AUTOEXEC.BAT data - both source and binary +// *************************************************************************** + +// Generated AUTOEXEC.BAT, un UTF-8 format +static std::string autoexec_bat_utf8 = {}; +// Whether AUTOEXEC.BAT is already registered on the Z: drive +static bool is_vfile_registered = false; +// Code page used to generate Z:\AUTOEXEC.BAT from the internal UTF-8 version +static uint16_t vfile_code_page = 0; + +// Data to be used to generate AUTOEXEC.BAT + +// If true, put ECHO OFF before the content of [autoexec] section +static bool autoexec_has_echo_off = false; +// Environment variables to be set in AUTOEXEC.BAT +static std::map autoexec_variables = {}; + +enum class Location { + // Autogenerated commands placed BEFORE content of the [autoexec] + GeneratedBeforeAutoexec, + // Content of [autoexec] section from the configuration file(s) + ConfigFileAutoexec, + // Autogenerated commands placed AFTER content of the [autoexec] + GeneratedAfterAutoexec, +}; + +// Lines to be placed in the generated AUTOEXEC.BAT, by section (location) +static std::map> autoexec_lines; + +// *************************************************************************** +// AUTOEXEC.BAT generation code +// *************************************************************************** + +std::string create_autoexec_bat_utf8() +{ + std::string out; + + // Helper lamdbas + + auto push_new_line = [&] { // DOS line ending is CR+LF + out.push_back(char_cr); + out.push_back(char_lf); + }; + + auto push_string = [&](const std::string& line) { + if (line.empty()) { + push_new_line(); + return; + } + + for (const auto& character : line) { + out.push_back(character); + } + push_new_line(); + }; + + // Generate AUTOEXEC.BAT, in UTF-8 format + + // If currently printed lines are autogenerated + bool prints_generated = false; + // If currently printed lines are from [autoexec] section of config file + bool prints_config_section = false; + + static const std::string comment = ":: "; + static const std::string comment_generated = + comment + MSG_Get("AUTOEXEC_BAT_AUTOGENERATED"); + static const std::string comment_config_section = + comment + MSG_Get("AUTOEXEC_BAT_CONFIG_SECTION"); + + // Put 'ECHO OFF' and 'SET variable=value' if needed + + if (autoexec_has_echo_off || !autoexec_variables.empty()) { + push_string(comment_generated); + prints_generated = true; + } + + if (autoexec_has_echo_off) { + push_new_line(); + push_string("@ECHO OFF"); + } + + if (!autoexec_variables.empty()) { + push_new_line(); + for (const auto& [name, value] : autoexec_variables) { + push_string("@SET " + name + "=" + value); + } + } + + if (prints_generated) { + push_new_line(); + } + + // Put remaining AUTOEXEC.BAT content + + for (const auto& [source, list_lines] : autoexec_lines) { + if (list_lines.empty()) { + continue; + } + + switch (source) { + case Location::GeneratedBeforeAutoexec: + case Location::GeneratedAfterAutoexec: + if (!prints_generated) { + if (!out.empty()) { + push_new_line(); + } + push_string(comment_generated); + push_new_line(); + prints_generated = true; + prints_config_section = false; + } + break; + case Location::ConfigFileAutoexec: + if (!prints_config_section) { + if (!out.empty()) { + push_new_line(); + } + push_string(comment_config_section); + push_new_line(); + prints_generated = false; + prints_config_section = true; + } + break; + default: assert(false); + } + + for (const auto& autoexec_line : list_lines) { + push_string(autoexec_line); + } + } + + return out; +} + +static void create_autoexec_bat_dos(const std::string& input_utf8, + const uint16_t code_page) +{ + // Convert UTF-8 AUTOEXEC.BAT to DOS code page + std::string autoexec_bat_dos = {}; + utf8_to_dos(input_utf8, autoexec_bat_dos, code_page); + + // Convert the result to a binary format + auto autoexec_bat_bin = std::vector(autoexec_bat_dos.begin(), + autoexec_bat_dos.end()); + + // Register/refresh Z:\AUTOEXEC.BAT file + if (is_vfile_registered) { + VFILE_Update(autoexec_file_name.c_str(), std::move(autoexec_bat_bin)); + } else { + VFILE_Register(autoexec_file_name.c_str(), + std::move(autoexec_bat_bin)); + is_vfile_registered = true; + } + + // Store current code page for caching purposes + vfile_code_page = code_page; +} + +// *************************************************************************** +// AUTOEXEC class declaration and implementation +// *************************************************************************** + +class AutoExecModule final : public Module_base { +public: + AutoExecModule(Section* configuration); + +private: + void ProcessConfigFile(const Section_line& section, + const std::string& source_name); + + void AutoMountDrive(const std::string& dir_letter); + + void AddCommandBefore(const std::string& line); + void AddCommandAfter(const std::string& line); + void AddAutoExecLine(const std::string& line); + + void AddMessages(); +}; + +AutoExecModule::AutoExecModule(Section* configuration) + : Module_base(configuration) +{ + AddMessages(); + + // Get the [dosbox] conf section + const auto sec = static_cast(control->GetSection("dosbox")); + assert(sec); + + // Auto-mount drives (except for DOSBox's Z:) prior to [autoexec] + if (sec->Get_bool("automount")) { + for (char letter = 'a'; letter <= 'z'; ++letter) { + AutoMountDrive({letter}); + } + } + + // Initialize configurable states that control misc behavior + + // Check -securemode switch to disable mount/imgmount/boot after + // running autoexec.bat + const auto cmdline = control->cmdline; // short-lived copy + const bool secure = cmdline->FindExist("-securemode", true); + + // Are autoexec sections permitted? + const bool autoexec_is_allowed = !cmdline->FindExist("-noautoexec", true); + + // Should autoexec sections be joined or overwritten? + const std::string_view section_pref = sec->Get_string("autoexec_section"); + const bool should_join_autoexecs = (section_pref == "join"); + + // Check to see for extra command line options to be added + // (before the command specified on commandline) + std::string argument = {}; + + bool exit_call_exists = false; + while (cmdline->FindString("-c", argument, true)) { +#if defined(WIN32) + // Replace single with double quotes so that mount commands + // can contain spaces + for (auto& character : argument) { + if (character == '\'') { + character = '\"'; + } + } +#endif // Linux users can simply use \" in their shell + + // If the user's added an exit call, simply store that + // fact but don't insert it because otherwise it can + // precede follow on [autoexec] calls. + if (argument == "exit" || argument == "\"exit\"") { + exit_call_exists = true; + continue; + } + AddCommandBefore(argument); + } + + // Check for the -exit switch, which indicates they want to quit + const bool exit_arg_exists = cmdline->FindExist("-exit"); + + // Check if instant-launch is active + const bool using_instant_launch_with_executable = + control->GetStartupVerbosity() == Verbosity::InstantLaunch && + cmdline->HasExecutableName(); + + // Should we add an 'exit' call to the end of autoexec.bat? + const bool should_add_exit = exit_call_exists || exit_arg_exists || + using_instant_launch_with_executable; + + auto maybe_add_command_secure = [&](const bool after = false) { + const std::string command = "@Z:\\CONFIG.COM -securemode"; + if (secure) { + if (after) { + AddCommandAfter(command); + } else { + AddCommandBefore(command); + } + } + }; + + auto maybe_add_command_mount_d_cdrom = [&](const std::string& targets) { + if (targets.empty()) { + return; + } + AddCommandBefore(std::string("@Z:\\IMGMOUNT.COM D ") + targets + + std::string(" -t iso")); + }; + + auto add_command_mount_c_directory = [&](const std::string& target) { + AddCommandBefore(std::string("@Z:\\MOUNT.COM C ") + target); + AddCommandBefore("@C:"); + }; + + // Check for first argument being a directory or file + + const std::string quote = "\""; + + unsigned int index = 1; + bool found_dir_or_command = false; + std::string cdrom_images = {}; + + while (cmdline->FindCommand(index++, argument)) { + // Check if argument is a file/directory + + std_fs::path path = argument; + bool is_directory = std_fs::is_directory(path); + if (!is_directory) { + path = std_fs::current_path() / path; + is_directory = std_fs::is_directory(path); + } + + if (is_directory) { + maybe_add_command_mount_d_cdrom(cdrom_images); + add_command_mount_c_directory(quote + argument + quote); + maybe_add_command_secure(); + + found_dir_or_command = true; + break; + } + + // Check if argument is a batch file + + auto argument_ucase = argument; + upcase(argument_ucase); + if (ends_with(argument_ucase, ".BAT")) { + maybe_add_command_mount_d_cdrom(cdrom_images); + maybe_add_command_secure(); + // BATch files are called else exit will not work + AddCommandBefore(std::string("CALL ") + argument); + + found_dir_or_command = true; + break; + } + + // Check if argument is a boot image file + + if (ends_with(argument_ucase, ".IMG") || + ends_with(argument_ucase, ".IMA")) { + maybe_add_command_mount_d_cdrom(cdrom_images); + // No secure mode here as boot is destructive and + // enabling securemode disables boot + AddCommandBefore(std::string("BOOT ") + quote + + argument + quote); + + found_dir_or_command = true; + break; + } + + // Check if argument is a CD image + + if (ends_with(argument_ucase, ".ISO") || + ends_with(argument_ucase, ".CUE")) { + if (!cdrom_images.empty()) { + cdrom_images += " "; + } + cdrom_images += quote + argument + quote; + continue; + } + + // Consider argument as a command + + maybe_add_command_mount_d_cdrom(cdrom_images); + maybe_add_command_secure(); + AddCommandBefore(argument); + + found_dir_or_command = true; + break; + } + + // Generate AUTOEXEC.BAT + + if (autoexec_is_allowed) { + if (should_join_autoexecs) { + ProcessConfigFile(*static_cast(configuration), + "one or more joined sections"); + } else if (found_dir_or_command) { + LOG_MSG("AUTOEXEC: Using commands provided on the command line"); + } else { + ProcessConfigFile(control->GetOverwrittenAutoexecSection(), + control->GetOverwrittenAutoexecConf()); + } + } + + if (!found_dir_or_command) { + maybe_add_command_mount_d_cdrom(cdrom_images); + // If we're in secure mode without command line + // executables, then seal off the configuration + constexpr bool after = true; + maybe_add_command_secure(after); + } + + if (should_add_exit) { + AddCommandAfter("@EXIT"); + } + + // Register the AUTOEXEC.BAT file if not already done + AUTOEXEC_RegisterFile(); +} + +void AutoExecModule::ProcessConfigFile(const Section_line& section, + const std::string& source_name) +{ + if (section.data.empty()) { + return; + } + + LOG_MSG("AUTOEXEC: Using autoexec from %s", source_name.c_str()); + + auto check_echo_off = [](const std::string& line) { + if (line.empty()) { + return false; + } + + std::string tmp = (line[0] == '@') ? line.substr(1) : line; + if (tmp.length() < 8) { + return false; + } + + lowcase(tmp); + if (tmp.substr(0, 4) != "echo" || !ends_with(tmp, "off")) { + return false; + } + + tmp = tmp.substr(4, tmp.length() - 7); + for (const auto character : tmp) { + if (!isspace(character)) { + return false; + } + } + + return true; + }; + + std::istringstream input; + input.str(section.data); + + std::string line = {}; + + bool is_first_line = true; + while (std::getline(input, line)) { + trim(line); + + // If the first line is 'echo off' command, skip it and replace + // with auto-generated one + + if (is_first_line) { + is_first_line = false; + if (check_echo_off(line)) { + autoexec_has_echo_off = true; + continue; + } + } + + AddAutoExecLine(line); + } +} + +// Takes in a drive letter (eg: 'c') and attempts to mount the 'drives/c', +// extends system path if needed +void AutoExecModule::AutoMountDrive(const std::string& dir_letter) +{ + // Does drives/[x] exist? + const auto drive_path = GetResourcePath("drives", dir_letter); + if (!path_exists(drive_path)) { + return; + } + + // Try parsing the [x].conf file + const auto conf_path = drive_path.string() + ".conf"; + const auto [drive_letter, mount_args, path_val] = + parse_drive_conf(dir_letter, conf_path); + + // Install mount as an autoexec command + AddCommandBefore(std::string("@Z:\\MOUNT.COM ") + drive_letter + " \"" + + simplify_path(drive_path).string() + "\"" + mount_args); + + // Install path as an autoexec command + if (path_val.length()) { + AddCommandBefore(std::string("@SET PATH=") + path_val); + } +} + +void AutoExecModule::AddCommandBefore(const std::string& line) +{ + autoexec_lines[Location::GeneratedBeforeAutoexec].push_back(line); +} + +void AutoExecModule::AddCommandAfter(const std::string& line) +{ + autoexec_lines[Location::GeneratedAfterAutoexec].push_back(line); +} + +void AutoExecModule::AddAutoExecLine(const std::string& line) +{ + autoexec_lines[Location::ConfigFileAutoexec].push_back(line); +} + +void AutoExecModule::AddMessages() +{ + MSG_Add("AUTOEXEC_BAT_AUTOGENERATED", "autogenerated"); + MSG_Add("AUTOEXEC_BAT_CONFIG_SECTION", "from [autoexec] section"); +} + +void AUTOEXEC_NotifyNewCodePage() +{ + // No need to do anything during the shutdown or if Z:\AUTOEXEC.BAT file + // does not exist yet + if (shutdown_requested || !is_vfile_registered) { + return; + } + + // No need to do anything if the code page used by UTF-8 engine is still + // the same as when Z:\AUTOEXEC.BAT was generated/refreshed + const auto code_page = get_utf8_code_page(); + if (code_page == vfile_code_page) { + return; + } + + // Recreate the AUTOEXEC.BAT file as visible on DOS side + create_autoexec_bat_dos(autoexec_bat_utf8, code_page); +} + +void AUTOEXEC_SetVariable(const std::string& name, const std::string& value) +{ +#if C_DEBUG + if (!std::all_of(name.cbegin(), name.cend(), is_printable_ascii)) { + E_Exit("AUTOEXEC: Variable name is not a printable ASCII"); + } + if (!std::all_of(value.cbegin(), value.cend(), is_printable_ascii)) { + E_Exit("AUTOEXEC: Variable value is not a printable ASCII"); + } +#endif + + auto name_upcase = name; + upcase(name_upcase); + + // If shell is already running, refresh variable content + if (first_shell) { + first_shell->SetEnv(name_upcase.c_str(), value.c_str()); + } + + // Update our internal list of variables to set in AUTOEXEC.BAT + if (value.empty()) { + autoexec_variables.erase(name_upcase); + } else { + autoexec_variables[name_upcase] = value; + } +} + +void AUTOEXEC_RegisterFile() +{ + autoexec_bat_utf8 = create_autoexec_bat_utf8(); + create_autoexec_bat_dos(autoexec_bat_utf8, get_utf8_code_page()); +} + +static std::unique_ptr autoexec_module{}; + +void AUTOEXEC_Init(Section* sec) +{ + autoexec_module = std::make_unique(sec); +} diff --git a/src/shell/meson.build b/src/shell/meson.build index 7a0b5f5ed68..73308357ef6 100644 --- a/src/shell/meson.build +++ b/src/shell/meson.build @@ -1,4 +1,5 @@ libshell_sources = files( + 'autoexec.cpp', 'shell.cpp', 'shell_batch.cpp', 'shell_cmds.cpp', diff --git a/src/shell/shell.cpp b/src/shell/shell.cpp index 6c7f33987df..a12195929cf 100644 --- a/src/shell/shell.cpp +++ b/src/shell/shell.cpp @@ -26,6 +26,7 @@ #include #include "../dos/program_more_output.h" +#include "autoexec.h" #include "callback.h" #include "control.h" #include "fs_utils.h" @@ -49,126 +50,6 @@ std::unique_ptr SHELL_ProgramCreate() { return ProgramCreate(); } -char autoexec_data[autoexec_maxsize] = { 0 }; -static std::list autoexec_strings; -typedef std::list::iterator auto_it; - -void VFILE_Remove(const char *name, const char *dir = ""); - -AutoexecObject::AutoexecObject(const std::string& line) -{ - Install(line); -} - -void AutoexecObject::Install(const std::string &in) { - if (GCC_UNLIKELY(installed)) - E_Exit("autoexec: already created %s", buf.c_str()); - installed = true; - buf = in; - autoexec_strings.push_back(buf); - this->CreateAutoexec(); - - //autoexec.bat is normally created AUTOEXEC_Init. - //But if we are already running (first_shell) - //we have to update the envirionment to display changes - - if(first_shell) { - //create a copy as the string will be modified - std::string::size_type n = buf.size(); - char* buf2 = new char[n + 1]; - safe_strncpy(buf2, buf.c_str(), n + 1); - if((strncasecmp(buf2,"set ",4) == 0) && (strlen(buf2) > 4)){ - char* after_set = buf2 + 4;//move to variable that is being set - char* test = strpbrk(after_set,"="); - if(!test) {first_shell->SetEnv(after_set,"");return;} - *test++ = 0; - //If the shell is running/exists update the environment - first_shell->SetEnv(after_set,test); - } - delete [] buf2; - } -} - -void AutoexecObject::InstallBefore(const std::string &in) { - if(GCC_UNLIKELY(installed)) E_Exit("autoexec: already created %s",buf.c_str()); - installed = true; - buf = in; - autoexec_strings.push_front(buf); - this->CreateAutoexec(); -} - -void AutoexecObject::CreateAutoexec() -{ - /* Remove old autoexec.bat if the shell exists */ - if(first_shell) VFILE_Remove("AUTOEXEC.BAT"); - - //Create a new autoexec.bat - autoexec_data[0] = 0; - size_t auto_len; - for (std::string linecopy : autoexec_strings) { - std::string::size_type offset = 0; - // Lets have \r\n as line ends in autoexec.bat. - while(offset < linecopy.length()) { - const auto n = linecopy.find('\n', offset); - if (n == std::string::npos) - break; - const auto rn = linecopy.find("\r\n", offset); - if (rn != std::string::npos && rn + 1 == n) { - offset = n + 1; - continue; - } - // \n found without matching \r - linecopy.replace(n,1,"\r\n"); - offset = n + 2; - } - - auto_len = safe_strlen(autoexec_data); - if ((auto_len+linecopy.length() + 3) > autoexec_maxsize) { - E_Exit("SYSTEM:Autoexec.bat file overflow"); - } - sprintf((autoexec_data + auto_len),"%s\r\n",linecopy.c_str()); - } - if (first_shell) VFILE_Register("AUTOEXEC.BAT",(uint8_t *)autoexec_data,(uint32_t)strlen(autoexec_data)); -} - -AutoexecObject::~AutoexecObject(){ - if(!installed) return; - - // Remove the line from the autoexecbuffer and update environment - for(auto_it it = autoexec_strings.begin(); it != autoexec_strings.end(); ) { - if ((*it) == buf) { - std::string::size_type n = buf.size(); - char* buf2 = new char[n + 1]; - safe_strncpy(buf2, buf.c_str(), n + 1); - bool stringset = false; - // If it's a environment variable remove it from there as well - if ((strncasecmp(buf2,"set ",4) == 0) && (strlen(buf2) > 4)){ - char* after_set = buf2 + 4;//move to variable that is being set - char* test = strpbrk(after_set,"="); - if (!test) { - delete [] buf2; - continue; - } - *test = 0; - stringset = true; - //If the shell is running/exists update the environment - if (first_shell) first_shell->SetEnv(after_set,""); - } - delete [] buf2; - if (stringset && first_shell && first_shell->bf && first_shell->bf->filename.find("AUTOEXEC.BAT") != std::string::npos) { - //Replace entry with spaces if it is a set and from autoexec.bat, as else the location counter will be off. - *it = buf.assign(buf.size(),' '); - ++it; - } else { - it = autoexec_strings.erase(it); - } - } else { - ++it; - } - } - this->CreateAutoexec(); -} - DOS_Shell::DOS_Shell() : Program(), l_history{}, @@ -570,200 +451,6 @@ void DOS_Shell::SyntaxError() extern int64_t ticks_at_program_launch; -class AUTOEXEC final : public Module_base { -private: - std::list autoexec_lines = {}; - - void AutomountDrive(const std::string &dir_letter); - - void ProcessConfigFileAutoexec(const Section_line §ion, - const std::string &source_name); - - void InstallLine(const std::string &line) - { - autoexec_lines.emplace_back().Install(line); - } - -public: - AUTOEXEC(Section *configuration) : Module_base(configuration) - { - // Get the [dosbox] conf section - const auto ds = static_cast( - control->GetSection("dosbox")); - assert(ds); - - // Auto-mount drives (except for DOSBox's Z:) prior to [autoexec] - if (ds->Get_bool("automount")) { - constexpr std::string_view drives = "abcdefghijklmnopqrstuvwxy"; - for (const auto letter : drives) { - AutomountDrive({letter}); - } - } - - // Initialize configurable states that control autoexec-related - // behavior - - /* Check -securemode switch to disable mount/imgmount/boot after - * running autoexec.bat */ - const auto cmdline = control->cmdline; // short-lived copy - const bool secure = cmdline->FindExist("-securemode", true); - - // Are autoexec sections permitted? - const bool autoexec_is_allowed = !secure && - !cmdline->FindExist("-noautoexec", - true); - - // Should autoexec sections be joined or overwritten? - const std::string_view section_pref = ds->Get_string("autoexec_section"); - const bool should_join_autoexecs = (section_pref == "join"); - - /* Check to see for extra command line options to be added - * (before the command specified on commandline) */ - std::string line = {}; - - bool exit_call_exists = false; - while (cmdline->FindString("-c", line, true)) { -#if defined(WIN32) - // replace single with double quotes so that mount - // commands can contain spaces - for (Bitu temp = 0; temp < line.size(); ++temp) - if (line[temp] == '\'') - line[temp] = '\"'; -#endif // Linux users can simply use \" in their shell - - // If the user's added an exit call, simply store that - // fact but don't insert it because otherwise it can - // precede follow on [autoexec] calls. - if (line == "exit" || line == "\"exit\"") { - exit_call_exists = true; - continue; - } - InstallLine(line); - } - - // Check for the -exit switch, which indicates they want to quit - const bool exit_arg_exists = cmdline->FindExist("-exit"); - - // Check if instant-launch is active - const bool using_instant_launch_with_executable = - control->GetStartupVerbosity() == Verbosity::InstantLaunch && - cmdline->HasExecutableName(); - - // Should we add an 'exit' call to the end of autoexec.bat? - const bool addexit = exit_call_exists || - exit_arg_exists || - using_instant_launch_with_executable; - - /* Check for first command being a directory or file */ - char buffer[CROSS_LEN + 1]; - char orig[CROSS_LEN + 1]; - char cross_filesplit[2] = {CROSS_FILESPLIT, 0}; - - unsigned int command_index = 1; - bool found_dir_or_command = false; - while (cmdline->FindCommand(command_index++, line) && - !found_dir_or_command) { - struct stat test; - if (line.length() > CROSS_LEN) - continue; - safe_strcpy(buffer, line.c_str()); - if (stat(buffer, &test)) { - if (getcwd(buffer, CROSS_LEN) == NULL) - continue; - if (safe_strlen(buffer) + line.length() + 1 > CROSS_LEN) - continue; - safe_strcat(buffer, cross_filesplit); - safe_strcat(buffer, line.c_str()); - if (stat(buffer, &test)) - continue; - } - if (test.st_mode & S_IFDIR) { - InstallLine(std::string("MOUNT C \"") + buffer + "\""); - InstallLine("C:"); - if (secure) - InstallLine("z:\\config.com -securemode"); - } else { - char *name = strrchr(buffer, CROSS_FILESPLIT); - if (!name) { // Only a filename - line = buffer; - if (getcwd(buffer, CROSS_LEN) == NULL) - continue; - if (safe_strlen(buffer) + line.length() + 1 > CROSS_LEN) - continue; - safe_strcat(buffer, cross_filesplit); - safe_strcat(buffer, line.c_str()); - if (stat(buffer, &test)) - continue; - name = strrchr(buffer, CROSS_FILESPLIT); - if (!name) - continue; - } - *name++ = 0; - if (!path_exists(buffer)) - continue; - InstallLine(std::string("MOUNT C \"") + buffer + "\""); - InstallLine("C:"); - /* Save the non-modified filename (so boot and - * imgmount can use it (long filenames, case - * sensivitive)) */ - safe_strcpy(orig, name); - upcase(name); - if (strstr(name, ".BAT") != 0) { - if (secure) - InstallLine("z:\\config.com -securemode"); - /* BATch files are called else exit will not work */ - InstallLine(std::string("CALL ") + name); - } else if ((strstr(name, ".IMG") != 0) || (strstr(name, ".IMA") != 0)) { - // No secure mode here as boot is destructive and enabling securemode disables boot - /* Boot image files */ - InstallLine(std::string("BOOT ") + orig); - } else if ((strstr(name, ".ISO") != 0) || (strstr(name, ".CUE") != 0)) { - /* imgmount CD image files */ - /* securemode gets a different number from the previous branches! */ - InstallLine(std::string("IMGMOUNT D \"") + - orig + std::string("\" -t iso")); - // autoexec[16].Install("D:"); - if (secure) - InstallLine("z:\\config.com -securemode"); - /* Makes no sense to exit here */ - } else { - if (secure) - InstallLine("z:\\config.com -securemode"); - InstallLine(name); - } - } - found_dir_or_command = true; - } - if (autoexec_is_allowed) { - if (should_join_autoexecs) { - ProcessConfigFileAutoexec(*static_cast(configuration), - "one or more joined sections"); - } else if (found_dir_or_command) { - LOG_MSG("AUTOEXEC: Using commands provided on the command line"); - } else { - ProcessConfigFileAutoexec( - control->GetOverwrittenAutoexecSection(), - control->GetOverwrittenAutoexecConf()); - } - } - if (secure && !found_dir_or_command) { - // If we're in secure mode without command line - // executables, then seal off the configuration - InstallLine("z:\\config.com -securemode"); - } - // The last slot is always reserved for the exit call, - // regardless if we're in secure-mode or not. - if (addexit) - InstallLine("exit"); - - // Print the entire autoexec content, if needed: - // for (const auto &autoexec_line : autoexec) - // LOG_INFO("AUTOEXEC-LINE: %s", autoexec_line.GetLine().c_str()); - - VFILE_Register("AUTOEXEC.BAT",(uint8_t *)autoexec_data,(uint32_t)strlen(autoexec_data)); - } -}; - // Specify a 'Drive' config object with allowed key and value types static std::unique_ptr specify_drive_config() { @@ -786,8 +473,8 @@ static std::unique_ptr specify_drive_config() } // Parse a 'Drive' config file and return object with allowed key and value types -static std::tuple parse_drive_conf( - std::string drive_letter, const std_fs::path &conf_path) +std::tuple parse_drive_conf( + std::string drive_letter, const std_fs::path& conf_path) { // Default return values constexpr auto default_args = ""; @@ -831,77 +518,6 @@ static std::tuple parse_drive_conf( return {drive_letter, mount_args, path_val}; } -// Takes in a drive letter (eg: 'c') and attempts to mount the 'drives/c' -// resource using an autoexec 'mount' command. -void AUTOEXEC::AutomountDrive(const std::string &dir_letter) -{ - // Does drives/[x] exist? - const auto drive_path = GetResourcePath("drives", dir_letter); - if (!path_exists(drive_path)) - return; - - // Try parsing the [x].conf file - const auto conf_path = drive_path.string() + ".conf"; - const auto [drive_letter, - mount_args, - path_val] = parse_drive_conf(dir_letter, conf_path); - - // Wrap the drive path inside quotes, plus a prefix space. - const auto quoted_path = " \"" + simplify_path(drive_path).string() + "\""; - - // Install mount as an autoexec command - InstallLine(std::string("@mount ") + drive_letter + quoted_path + mount_args); - - // Install path as an autoexec command - if (path_val.length()) - InstallLine(std::string("@set PATH=") + path_val); -} - -void AUTOEXEC::ProcessConfigFileAutoexec(const Section_line §ion, - const std::string &source_name) -{ - if (section.data.empty()) - return; - - auto extra = §ion.data[0]; - - /* detect if "echo off" is the first line */ - size_t firstline_length = strcspn(extra, "\r\n"); - bool echo_off = !strncasecmp(extra, "echo off", 8); - if (echo_off && firstline_length == 8) - extra += 8; - else { - echo_off = !strncasecmp(extra, "@echo off", 9); - if (echo_off && firstline_length == 9) - extra += 9; - else - echo_off = false; - } - - /* if "echo off" move it to the front of autoexec.bat */ - if (echo_off) { - autoexec_lines.emplace_back().InstallBefore("@echo off"); - if (*extra == '\r') - extra++; // It can point to \0 - if (*extra == '\n') - extra++; // same - } - - /* Install the stuff from the configfile if anything - * left after moving echo off */ - if (*extra) { - InstallLine(extra); - LOG_MSG("AUTOEXEC: Using autoexec from %s", source_name.c_str()); - } -} - -static std::unique_ptr autoexec_module{}; - -void AUTOEXEC_Init(Section *sec) -{ - autoexec_module = std::make_unique(sec); -} - static Bitu INT2E_Handler() { /* Save return address and current process */ diff --git a/vs/dosbox.vcxproj b/vs/dosbox.vcxproj index b04ba83d95c..cd3cad693a5 100644 --- a/vs/dosbox.vcxproj +++ b/vs/dosbox.vcxproj @@ -712,6 +712,7 @@ IF %ERRORLEVEL% LSS 8 SET ERRORLEVEL = 0 + @@ -732,6 +733,7 @@ IF %ERRORLEVEL% LSS 8 SET ERRORLEVEL = 0 + diff --git a/vs/dosbox.vcxproj.filters b/vs/dosbox.vcxproj.filters index 4b3f7a0a5d0..668dfd56000 100644 --- a/vs/dosbox.vcxproj.filters +++ b/vs/dosbox.vcxproj.filters @@ -460,6 +460,9 @@ src\misc + + src\shell + src\shell @@ -801,6 +804,9 @@ include + + src\shell + include