SET /FIRST DOSBox-X extension for demos like Out of Control with lazy ass environment block parsing

This commit is contained in:
Jonathan Campbell 2024-08-11 01:49:39 +00:00
parent b3e0e4c6f9
commit 4a161b36a7
4 changed files with 91 additions and 2 deletions

View File

@ -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).

View File

@ -102,6 +102,7 @@ public:
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

View File

@ -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<uint8_t> 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;

View File

@ -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;