Greetings,

	You have requested a copy of the PUSHDIR/POPDIR utilities.
Here they are!  I have not put them into shar format, so to take them
apart, seach for 10 dashs at the beginning of a line.  This is my
file-break.  Three files are included:

	PPDIR.USE	Basic instructions
	PUSHDIR.ASM	PUSHDIR source
	POPDIR.ASM	POPDIR source

I make these routines available on an as-is basis, without any
support.  (I MIGHT be able to answer a few simple questions about
them, though.)

			- BRENT W. BACCALA -
			Aerospace Engineering Department
			U.S. Naval Academy
			Annapolis, MD

			<baccala@usna.arpa>

	"I do graphics work on an SGI Iris, fun work on a VAX 11/780,
		grunge work on an IBM XT"

---------- PPDIR.USE ----------

PUSHDIR and POPDIR, by John Friend, PC Magazine, Vol.5 Num.10, p.243

USING PUSHDIR AND POPDIR

Compile each utility with an assembler of your choice.  Linking each
file will give you a `NO STACK SEGMENT' error, which should be ignored
because these are .COM files.  Use EXE2BIN to convert them into .COM
format.

PUSHDIR will push the current drive and working directory onto a stack
capable of holding 6 directories (this can be expanded by changing the
source and re-compiling).  POPDIR will pop the last directory pushed
by PUSHDIR.  It checks first to see if PUSHDIR has been run, and if
not, gives an error message.

BUG: These utilities use a circular stack.  POPDIR will check to see
if PUSHDIR has been run, but does not check to see if the next directory
is in fact a valid one. Ex: Running PUSHDIR once and POPDIR twice will
NOT generate an error from POPDIR and WILL cause unpredictable results.

NOTE: PUSHDIR will save the current drive and the working directory on
the current drive.  So if you use multiple drives (as I do), there is a
"hole" in the program that you have to be careful of.  If your shell
script runs PUSHDIR, changes drives, changes directories, does <your
application here>, and then run POPDIR, you will wind up back on your
original disk, in your original directory.  But the working directory
on the other disk may not necessarily be the same!  To get around this,
you have to run the programs several time, once to save the drive
letter, and then once on each drive to save the working directory.  My
feeling is that this problem is not so much with the PUSHDIR/POPDIR
programs then with DOS for not allowing one drive to be mounted on
another (similiar to UNIX mountable file systems).

					-bwb
---------- PUSHDIR ----------
main	group	code
code	segment	public	para	'code'
assume	cs:main

org	100h				;.COM file

BEGIN:	jmp	START			;program starts here
		db	"Copyright 1986 Ziff-Davis Publishing Co.",1Ah
signature	db	'PUSHDIR VERSION 1.0'
lengthsignature = $ - signature

savedint16	dd	?		;old int 16h vector

nextpush	dw	offset main:push1dir	;next place to save a dir
push1dir	db	67 dup (0)		;storage for a saved dir
push2dir	db	67 dup (0)		;more storage
push3dir	db	67 dup (0)		;more storage
push4dir	db	67 dup (0)		;more storage
push5dir	db	67 dup (0)		;more storage
push6dir	db	67 dup (0)		;last storage

;up to here must be EXACTLY identical in both PUSHDIR and POPDIR so that
;popdir can know how to access the memory space reserved by the first
;pushdir.

;myint16 in an interrupt handler chained onto the existing interrupt handler.
;it is used to find out if PUSHDIR is already installed and if it is, where
;is it located?  It works by adding another function to int 16h.  To use it
;ax = 7788h, bx=7789h, and ds:si points to the signature string.  If any one
;of these conditions is not true, then the int 16h call is passed onto the
;old routine without doing anything.  If they are all true, then we switch
;ax and bx and return ds = code segment (cs) of the interrupt handler.

myint16	proc	far

	pushf				;save flags
	cmp	ax,7788h			;possible signature request ?
	je	CHECKSIG			;yes
NOTSIG:
	popf				;no - recover flags
	jmp	cs:[savedint16]		;go to old routine as normal

CHECKSIG:
	cmp	bx,7789h		;possible signature request ?
	jne	NOTSIG			;no

	;ax and bx were both correct for a signature request
	;now see if ds:si was pointing to the signature string
	;the whole idea of the signature is that is has to be
	;totally unique so no other program could possible use the same one.

	push	es			;save the registers we will use
	push	di
	push	cx
	mov	di,offset main:signature	;address of the signature
	mov	cx,lengthsignature		;length of the signature
	repe	cmpsb			;does string at ds:si match es:di?
	pop	cx			;recover all registers we used
	pop	di
	pop	es
	jne	NOTSIG			;no, not correct signature

;yes, it was a signature request so return ds equal to the current code
;segment so subsequent pushdir's and popdir's know where the original
;is located.

	push	cs
	pop	ds			;set ds = cs
	xchg	ax,bx			;flip these two so we know that
					;ds is being returned
	popf				;recover original flags
	iret				;return back to the program
					;that called int int 16h

myint16	endp

endresident	label	byte		;label marking the end of the
					;code to remain resident

;code after here will not remain resident

install		db	1	;0 = already installed, 1 = not installed

abortmsg	db	'Error reading the current directory.$'

START:
	sti			;turn interrupts on

	;first check to see if PUSHDIR is already installed

	mov	ax,7788h			;signature request
	mov	bx,7789h			;signature request
	mov	si,offset main:signature	;point to signature
	int	16h			;is it installed ?

assume	ds:nothing

	cmp	bx,7788h			;were ax and bx switched ?
	jne	NOTINSTALLED		;no
	cmp	ax,7789h			;were ax and bx switched ?
	jne	NOTINSTALLED		;no

	;yes it is installed already

	mov	cs:[install],0		;don't install it again
NOTINSTALLED:

	;ds = segment of the installation
	;store the current directory, including disk drive letter

	mov	si,ds:[nextpush]	;get storage address for next push
	add	si,3			;make room for d:\
	mov	dl,0			;default drive
	mov	ah,47h			;dos function number
	int	21h			;get current directory
	jc	ABORTERR		;error message if carry set
	mov	ah,19h			;dos function number
	int	21h			;get the current drive
	add	al,'A'			;convert to ascii
	mov	byte ptr ds:[si-3],al	;add the "D:\" in front of path
	mov	byte ptr ds:[si-2],':'
	mov	byte ptr ds:[si-1],'\'

	;now update [nextpush] for the next PUSHDIR

	cmp	ds:[nextpush],offset main:push6dir	;time to wrap around ?
	je	WRAPPUSH				;yes
	add	ds:[nextpush],67			;no, point to next one
	jmp	short GOTNEXTPUSH
WRAPPUSH:
	mov	ds:[nextpush],offset main:push1dir	;wrap back to beginning
GOTNEXTPUSH:
	cmp	cs:[install],1			;should we install it ?
	je	DOINSTALL				;yes
	int	20h				;no, we are done

ABORTERR:
	mov	dx,offset main:abortmsg		;address of error message
	mov	ah,9				;dos function number
	int	21h				;show error message
	int	20h				;end program on error

	;if we got to here, then pushdir is not already installed,
	;so we need to install it by making part of it resident.

DOINSTALL:
	push	cs
	pop	ds			;set ds = cs

assume	ds:main

	;save the current int 16h vector

	push	es			;save es
	mov	ax,3516h			;dos function 35h, vector 16h
	int	21h			;get the existing vector into es:bx
	mov	word ptr [savedint16],bx	;save es:bx
	mov	word ptr [savedint16+2],es	;save es:bx
	pop	es			;recover es

	;now set the new int 16h vector to point to my routine
	mov	dx,offset main:myint16	;point to my new routine
	mov	ax,2516h			;dos function 25h, vector 16h
	int	21h			;set new vector to ds:dx

	;now free up the memory occupied by the environment so it is not
	;permantently wasted

	mov	ax,ds:[2ch]		;get segment of environment
	mov	es,ax			;load environment segment into es
	mov	ah,49h			;dos function number
	int	21h			;free the environment memory

	;now terminate resident protecting only the first part of this program

	mov	dx,offset main:endresident	;point to end of resident code
	add	dx,0fh			;round up
	mov	cl,4
	shr	dx,cl			;convert to paragraphs (divide by 16)
	mov	ax,3100h		;dos function 31h, error code=0
	int	21h			;terminate and remain resident

code	ends
end	begin				;start execution at BEGIN

---------- POPDIR ----------
main	group	code
code	segment	public	para	'code'
assume	cs:main

org	100h				;.COM file

BEGIN:	jmp	START			;prorgam starts here
		db	"Copyright 1986 Ziff-Davis Publishing Co.",1Ah
signature	db	'PUSHDIR VERSION 1.0'
lengthsignature = $ - signature

savedint16	dd	?		;used to be identical to pushdir

nextpush	dw	offset main:push1dir	;next place to save a dir
push1dir	db	67 dup (0)
push2dir	db	67 dup (0)
push3dir	db	67 dup (0)
push4dir	db	67 dup (0)
push5dir	db	67 dup (0)
push6dir	db	67 dup (0)

;up to here must be EXACTLY identical in both PUSHDIR and POPDIR.

notinstalled1	db	'Must run PUSHDIR.COM before POPDIR.COM'
		db	' will do anything.',13,10,10,'$'
errpop1		db	'Error popping the current directory',13,10,10,'$'

START:
	sti				;interrupts on

	;is PUSHDIR already installed ?

	mov	ax,7788h			;signature request
	mov	bx,7789h			;signature request
	mov	si,offset main:signature	;point ds:si to signature
	int	16h			;is it installed?

assume	ds:nothing

	cmp	bx,7788h			;were ax and bx switched ?
	jne	NOTINSTALLED		;no
	cmp	ax,7789h			;were ax and bx switched ?
	jne	NOTINSTALLED		;no
	jmp	short ISINSTALLED		;yes - continue, no error
NOTINSTALLED:

	;here PUSHDIR was not previously installed so POPDIR can't do anything
	;useful so we just terminate with an error message.

	mov	dx,offset main:notinstalled1	;error message
	mov	ah,9
	int	21h
	int	20h			;exit
ISINSTALLED:

	;get the address of the directory previously saved by pushdir

	mov	bp,ds:[nextpush]		;get the next push location
	sub	bp,67				;back up one to the last push
	cmp	ds:[nextpush],offset main:push1dir	;need to wrap back ?
	jne	NOWRAPBACK			;no
	mov	bp,offset main:push6dir		;yes, wrap back
NOWRAPBACK:

	;set the current directory

	mov	dx,bp			;load ds:dx with directory to set
	mov	ah,3bh			;dos function number
	int	21h			;set current dir back
	jc	ERRPOP			;branch on error
	mov	ds:[nextpush],bp	;update [nextpush] if successful

	;set the current drive also

	mov	dl,ds:[bp]			;get drive letter from path
	sub	dl,'A'				;convert to binary (0=A, 1=B)
	mov	ah,0eh				;dos function number
	int	21h				;set drive

	;exit successfully with no message

	int	20h				;exit

ERRPOP:
	push	cs
	pop	ds				;set ds = cs
	mov	dx,offset main:errpop1		;error message
	mov	ah,9				;dos function number
	int	21h				;show the message
	int	20h				;terminate

code	ends
end	BEGIN				;start execution at BEGIN
---------- END ----------
