From 4a161b36a7e2251e50c052988c409222dc1af7f7 Mon Sep 17 00:00:00 2001 From: Jonathan Campbell Date: Sun, 11 Aug 2024 01:49:39 +0000 Subject: [PATCH] SET /FIRST DOSBox-X extension for demos like Out of Control with lazy ass environment block parsing --- CHANGELOG | 3 ++ include/programs.h | 1 + src/misc/programs.cpp | 73 ++++++++++++++++++++++++++++++++++++++++ src/shell/shell_cmds.cpp | 16 +++++++-- 4 files changed, 91 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index e9f8037411..d1842a867b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,7 @@ NEXT + - Add SET /FIRST, a DOSBox-X extension, that takes the specified + variable if it exists and moves it to the front of the environment + block. (joncampbell123). - SET command processing has been cleaned up and streamlined. Added /ERASE command line switch to clear the environment block (DOSBox-X extension). (joncampbell123). diff --git a/include/programs.h b/include/programs.h index 77668519c2..c7ea67ff00 100644 --- a/include/programs.h +++ b/include/programs.h @@ -102,6 +102,7 @@ class Program { bool GetEnvNum(Bitu want_num,std::string & result); //! Return an environment variable by index Bitu GetEnvCount(void); //! Return the number of environmental variables bool SetEnv(const char * entry,const char * new_string); //! Set environment variable + bool FirstEnv(const char * entry); bool EraseEnv(void); virtual void WriteOut(const char *format, const char * arguments); void WriteOut(const char * format,...); //! Write to standard output diff --git a/src/misc/programs.cpp b/src/misc/programs.cpp index 9c7c4c29a3..ee27e63ae9 100644 --- a/src/misc/programs.cpp +++ b/src/misc/programs.cpp @@ -497,6 +497,79 @@ void Program::DebugDumpEnv() { } } +bool Program::FirstEnv(const char * entry) { + PhysPt env_base,env_fence,env_scan,env_first,env_last; + bool found = false; + + if (dos_kernel_disabled) { + LOG_MSG("BUG: Program::FirstEnv() called with DOS kernel disabled (such as OS boot).\n"); + return false; + } + + if (!LocateEnvironmentBlock(env_base,env_fence,psp->GetEnvironment())) { + LOG_MSG("Warning: GetEnvCount() was not able to locate the program's environment block\n"); + return false; + } + + std::string bigentry(entry); + for (std::string::iterator it = bigentry.begin(); it != bigentry.end(); ++it) *it = toupper(*it); + + env_scan = env_base; + while (env_scan < env_fence) { + /* "NAME" + "=" + "VALUE" + "\0" */ + /* end of the block is a NULL string meaning a \0 follows the last string's \0 */ + if (mem_readb(env_scan) == 0) break; /* normal end of block */ + + if (EnvPhys_StrCmp(env_scan,env_fence,bigentry.c_str()) == 0) { + found = true; + break; + } + + if (!EnvPhys_ScanUntilNextString(env_scan,env_fence)) break; + } + + if (found) { + env_first = env_scan; + if (!EnvPhys_ScanUntilNextString(env_scan,env_fence)) return false; + env_last = env_scan; + +#if 0//DEBUG + fprintf(stderr,"Env base=%x fence=%x first=%x last=%x\n", + (unsigned int)env_base, (unsigned int)env_fence, + (unsigned int)env_first, (unsigned int)env_last); +#endif + + assert(env_first <= env_last); + + /* if the variable is already at the beginning, do nothing */ + if (env_first == env_base) return true; + + { + std::vector tmp; + tmp.resize(size_t(env_last-env_first)); + + /* save variable */ + for (size_t i=0;i < tmp.size();i++) + tmp[i] = mem_readb(env_first+(PhysPt)i); + + /* shift all variables prior to it forward over the variable, BACKWARDS */ + const size_t pl = size_t(env_first - env_base); + assert((env_first-pl) == env_base); + assert((env_last-pl) >= env_base); + assert(env_first < env_last); + assert(pl != 0); + for (size_t i=0;i < pl;i++) mem_writeb(env_last-(i+1), mem_readb(env_first-(i+1))); + + /* put the variable in at the beginning */ + assert((env_base+tmp.size()) == (env_last-pl)); + for (size_t i=0;i < tmp.size();i++) + mem_writeb(env_base+(PhysPt)i,tmp[i]); + } + } + + return true; +} + bool Program::EraseEnv(void) { PhysPt env_base,env_fence; size_t nsl = 0,el = 0,needs; diff --git a/src/shell/shell_cmds.cpp b/src/shell/shell_cmds.cpp index 2ec439e1a2..17fc4c9501 100644 --- a/src/shell/shell_cmds.cpp +++ b/src/shell/shell_cmds.cpp @@ -2733,7 +2733,8 @@ void DOS_Shell::CMD_SET(char * args) { show_all_env, set_env, show_env, - erase_env + erase_env, + first_env }; op_mode_t op_mode = show_all_env; @@ -2757,6 +2758,9 @@ void DOS_Shell::CMD_SET(char * args) { else if (sw == "ERASE") { /* DOSBox-X extension: Completely erase the environment block */ op_mode = erase_env; } + else if (sw == "FIRST") { /* DOSBox-X extension: Move the specified variable to the front of the environment block */ + op_mode = first_env; + } else { WriteOut("Unknown switch /"); WriteOut(sw.c_str()); @@ -2768,7 +2772,11 @@ void DOS_Shell::CMD_SET(char * args) { } } - if (op_mode == show_all_env) { + if (op_mode == first_env) { + if (*args == 0) return; + readnonspc(env_name,args); + } + else if (op_mode == show_all_env) { if (*args != 0) { /* Most SET commands take the form NAME=VALUE */ char *p = strchr(args,'='); @@ -2814,6 +2822,10 @@ void DOS_Shell::CMD_SET(char * args) { if (!EraseEnv()) WriteOut("Unable to erase environment block\n"); break; + case first_env: + if (!FirstEnv(env_name.c_str())) + WriteOut("Unable to move environment variable\n"); + break; default: abort(); break;