MSDOS 6.22 batch file help: save and restore current drive and dir

Hi there,

I am asking for help on MSDOS 6.22 batch files.
I have a wee suspicion that this forum may not be appropriate to ask this, so please ignore or delete if this is the case.

I need to create a batch file which saves current drive and dir, switches to a new drive and dir, unzips some files and then switches back to initial drive and dir.

for example:
@rem save here current dir AND drive
d:
cd \abc\xyz
pkunzip c:\123.zip
@rem restore initial current dir AND drive

I have looked for information but I keep stumbling on new (windows) batch file manuals. Any pointers to old MSDOS batch file manual?

Many thanks

Something which may come handy is this guide on how to set environment variables inside a bat-file: Getting Data into Environment Variables under MS-DOS

1 Like

Sorry, I can’t help. Maybe stackoverflow might be a good place to ask.

I did find some manuals by searching on archive.org:

and I did find this article:

Thank you. That site (Pement’s) is a treasure trove!

Thank you. The 2 manuals will be useful. The 3rd link I tried before but setting or using % variables does not seem to work for my msdos (6.22) which is on a VirtualBox (an emulator).

set %a%=%CD%
echo %a%

just prints literal “%a%”

actually that was going to be my 2nd question, why checking on ERRORLEVEL does not work.

I am posting below how I solved it eventually.

I failed to achieve this via a batch file but I succeeded using C and ol’ int86. (assembly is the way to go but I am ignorant as to how to structure and compile an assembly program).

So, here is a sample which compiled with open-watcom (cross-compiled on Linux and run both on dosbox and VirtualBox emulators, MSDOS 6.22). It succeeds in changing drive

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <process.h>
#include <i86.h>
#include <errno.h>

extern int errno;

/* program to emulate Unix's pushd command by
   saving current dir AND drive to environment
   and then change to new, user specified, dir AND drive.
   Similarly a popd command would read environment variable
   and change back to original dir.
   It uses a hack to save environment variables to calling "shell"
   It re-spawns a shell (command.com) which will inherit all environment
   from calling process, that's us. A neat hack and that's how you
   turn a pragmatically useless (ok, limited) MSDOS to magic Unix.
   I did not make any effort to free allocated memory.
   Compiled using open-watcom (THANK YOU (open-)Watcom!) on Linux for MSDOS
     wcc pushd.c  -oh -ok -ot -s -oa -ei -ob -ol+
     wlink system dos option stack=4096 option map option eliminate name pushd.exe file pushd.o
   (based on a Makefile by Mike Brutman)

   Author: Andreas Hadjiprocopis (https://github.com/hadjiprocopis)
   Date: 31/01/2020
   Licence: free to copy, modify and share

*/

#define ISALPHALOWER(c) ((c) >= 'a' && (c) <= 'z')
#define ISALPHAUPPER(c) ((c) >= 'A' && (c) <= 'Z')
#define ISALPHA(c) (ISALPHALOWER(c) || ISALPHAUPPER(c))
#define TOUPPER(c) (ISALPHALOWER(c) ? ((c)+'A'-'a') : (c))
#define PATHSEPARATOR '\\'

int main(int argc, char *argv[])
{
	char *newdir = argv[1], *apath, *cwd,
		*initialDir, initialDrive, *envs, *envstr;
	unsigned char newdrive = 255;
	union REGS inr;
	union REGS outr;
	struct SREGS s;

	if( argc != 2 ){ fprintf(stderr, "Usage : %s newdir\n", argv[0]); exit(1); }
	if(  ISALPHA(newdir[0]) &&
	    (newdir[1] == ':') &&
	    (newdir[2] == PATHSEPARATOR)
	){
		newdrive = TOUPPER(newdir[0]);
		//newdir += 3;
		printf("%s : newdir contains a drive spec '%c'.\n", argv[0], newdrive);
	}

	// find current drive and path using int86x
	// same thing can be achieved using getcwd()

	inr.h.ah = 0x19;  /* get current drive, A=0x0, B=0x1 etc. */
	int86x( 0x21, &inr, &outr, &s ); // 0x21, inregs, outregs, insegregs
	printf( "current drive : '%c'\n", outr.h.al+'A');

	apath = (char *)malloc(64*sizeof(char));
	strcpy(apath, "<empty>");
	/* get current dir without drive letters or preceding slash, it is an absolute path */
	inr.h.ah = 0x47; 
	inr.h.dl = 0x03; // for drive
	inr.w.si = FP_OFF(apath);
	s.ds = FP_SEG(apath);
	int86x( 0x21, &inr, &outr, &s ); // 0x21, inregs, outregs, insegregs
	printf( "current path (if you are on \ this will be empty string): '%s'\n", apath);
	if( outr.x.cflag != 0 ){
		printf("got error, doserrno=%d, %d\n", _doserrno, outr.w.ax);
	}

	/* same thing with getcwd() */
	initialDir = getcwd(NULL, 0); // must free cwd
	if( initialDir == NULL ){ fprintf(stderr, "%s : call to getcwd() has failed: (%d), %s\n", argv[0], errno, strerror(errno)); exit(1); }
	initialDrive = initialDir[0];
	printf("using getcwd() : dir '%s' and drive '%c'\n", initialDir, initialDrive);

	/* now save to an env variable */
	// sets env var CWD to "c:\\abc\xyz"
	envstr = (char *)malloc(100*sizeof(char));
	sprintf(envstr, "CWDIR=%s", initialDir);
	printf("setting env '%s'\n", envstr);
	if( 0 != putenv(envstr) ){ fprintf(stderr, "%s : putenv() failed for '%s' : (%d) %s\n", argv[0], envstr, errno, strerror(errno)); exit(1); }

	envstr = (char *)malloc(100*sizeof(char));
	sprintf(envstr, "CWDRIVE=%c", initialDrive);
	printf("setting env '%s'\n", envstr);
	if( 0 != putenv(envstr) ){ fprintf(stderr, "%s : putenv() failed for '%s' : (%d) %s\n", argv[0], envstr, errno, strerror(errno)); exit(1); }

	/* and change dir to what the user asked */
	// set drive
	if( (newdrive >= 'A') && (newdrive <= 'Z') ){
		inr.h.ah = 0x0E; // change drive
		inr.h.dl = TOUPPER(newdrive)-'A';
		int86x( 0x21, &inr, &outr, &s);
		printf("drive set to %c\n", newdrive);
	}

	// chdir
	inr.h.ah = 0x3B; // change dir (not drive!)
	s.ds = FP_SEG(newdir);
	inr.w.dx = FP_OFF(newdir);
	int86x( 0x21, &inr, &outr, &s ); // 0x21, inregs, outregs, insegregs
	if( outr.x.cflag != 0 ){
		printf("got error chdir to '%s', doserrno=%d, code=%d\n", newdir, _doserrno, outr.w.ax);
	}

	cwd = getcwd(NULL, 0);
	fprintf(stderr, "My working directory is now '%s'\n", cwd);
	free(cwd);
	envs = getenv("CWDRIVE");
	if( envs == NULL ){ fprintf(stderr, "%s : call to getenv() has failed for 'CWDRIVE'.\n", argv[0]); }
	fprintf(stdout, "%s : CWDRIVE='%s'\n", argv[0], envs);
	//free(envs);
	envs = getenv("CWDIR");
	if( envs == NULL ){ fprintf(stderr, "%s : call to getenv() has failed for 'CWDIR'.\n", argv[0]); }
	fprintf(stdout, "%s : CWD='%s'\n", argv[0], envs);

	/* now these env vars were set only for our own process and not for the surrounding command.com
	   process. What we can do is to spawn a new command.com and this new process will get a 
	   copy of our environment, neat trick.
	*/
	if( -1 == execl("c:\\command.com", "c:\\command.com", NULL) ){
		fprintf(stderr, "%s : execl failed : (%d) %s\n", argv[0], errno, strerror(errno));
		exit(1);
	}
	// if execl was successful nothing beyond this point will be executed
}

bw, Andreas

Glad you found a way, and thanks for sharing your code! (I hope you stick around and become a regular here too.)

Fundamentally, this is rooted in the fact that the MS-DOS CD command can not change the drive.

This does not work:

CD A:\DIR

CD only changes the directory.

Otherwise, it would certainly be more straightforward to capture the current drive and directory, and then simply supply the argument to CD.

1 Like

Isn’t cd /D <drive>:\<folder> meant to do this?
As in cd /D E: to change to drive E?

Also %cd% should contain the current directory, but I don’t know starting from which version of DOS/Windows.