Skip to content

Commit

Permalink
SET /FIRST DOSBox-X extension for demos like Out of Control with lazy…
Browse files Browse the repository at this point in the history
… ass environment block parsing
  • Loading branch information
joncampbell123 committed Aug 11, 2024
1 parent b3e0e4c commit 4a161b3
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 2 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -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).
Expand Down
1 change: 1 addition & 0 deletions include/programs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
73 changes: 73 additions & 0 deletions src/misc/programs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
16 changes: 14 additions & 2 deletions src/shell/shell_cmds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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());
Expand All @@ -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,'=');
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 4a161b3

Please sign in to comment.