;************************** POPD.ASM ************************************ ; ; POPD.ASM version 1.2 ; ; Usage: ; POPD ; Will change to the most recently stored directory on ; the stack. ; POPD +n ; Will delete the directory stored in position n from ; the bottom of the stack, starting at one on the bottom. ; Purpose: This program will either change to the topmost directory ; on the stack created by PUSHD v. 1.1 (first usage), OR ; will delete from the stack the directory stored at position ; n, where 1 is the bottom of the stack. In either case, ; the program will compress the stack, to remove any empty ; storage space, and will update the position for the next ; storage accordingly. ; Preconditions: ; 1) PUSHD must be hooked to interrupt 13. ; 2) PUSHD must have the same commands from beginning ; to after definition of push6d, so that it knows how ; to access the resident code of PUSHD. (This includes ; the fact that there are 6 locations for directory storage ; which are 67 bytes in size.) ; 3) PUSHD must have the signature "PUSHD VERSION 1.1". ; 4) PUSHD must look for 7788 in ax and 7789 in bx for ; a call to int 13. ; 5) PUSHD must put the value of 7789 into ax and 7788 ; into bx upon calling int 13 by DIRS, if PUSHD is installed. ; 6) PUSHD will set the data segment to be equal to the data ; segment that PUSHD used to install itself. ; 7) DS:[nextpush] (ds of PUSHD installation) must hold ; the location of the next empty directory stack location. ; Postconditions: ; 1) An appropriate error message will be printed out on ; standard error, OR ; 2) DS:[nextpush] will be decremented by one directory ; storage unit (67 bytes) AND EITHER: ; 2a) the current directory will become the directory on ; top of the stack (usage #1) OR ; 2b) the current directory will remain the same, but the ; targeted directory (1-6) will be deleted with the ; directories on the stack above shifted down one storage ; unit. ; 3) An error code will be generated. ; Error Codes: ; 0) Successful completion. ; 249) Error popping directory. ; 250) Directory Stack is empty. ; 251) Command Line parameter exceeds Directory Stack depth. ; 252) Illegal Characters on the Command Line. ; 253) Command Line parameter not in valid directory range (1-6). ; 254) Invalid Command Line delimiter. ; 255) PUSHD is not installed. ; Version changes: ; POPD v. 1.2: July 23, 1990. ; enhanced code using suggestions from David ; Kirschbaum (Toad Hall). ; "cleaned out" directory stack characters that ; were left after a POPD, which might not be over- ; written on next PUSHD. ; POPD v. 1.1: July 14, 1990. ; added "+n" option. ; changed hooked interrupt to 13 from 16. ; added error codes for abnormal termination. ; changed writes from vector 9 to vector 40h of ; int 21h. ; changed program termination from int 20h to vector ; 4Cxxh int 21h, where xx represents the error code ; generated. ; removed stack wrap around. ; POPDIR v. 1.0: Copyright 1986, Ziff-Davis Publishing Co. ; From PC Magazine, May 27, 1986 ; No changes. ; Future Enhancements: ; 1) Change the "n" parameters so that it refers to the ; top of the stack, not the bottom (RELATIVE from the top ; NOT ABSOLUTE from the bottom). ; 2) Make program an .EXE file. ; 3) Correct error caused by 3 or more blanks on the ; end of the command line tail. ; 4) Add a help option, ?, from the command line. ; 5) Add -c option to clear ALL directories on stack. ; 6) Add -n option to clear n topmost directories on stack. ; ; Created by: ; (from ideas based in UNIX and from PC Magazine's POPDIR.COM) ; William P. Sarra ; CIS:71041,347 ; arpa: sarra@TOPAZ.RUTGERS.EDU ; uucp: ...{ames,cbosgd, harvard, moss}!rutgers!topaz.rutgers.edu!sarra ; ;***************************************************************************** main group CSEG CSEG segment public para 'code' ASSUME CS:MAIN,DS:MAIN,ES:MAIN,SS:MAIN org 80h cmd_line label byte org 100h ;.COM file BEGIN: jmp Start ;program starts here signature db 'PUSHD VERSION 1.1' SIG_LEN equ $-signature savedint13 dd ? ;used to be identical to pushd nextpush dw offset MAIN:push1d ;next place to save a dir push1d db 67 dup (0) push2d db 67 dup (0) push3d db 67 dup (0) push4d db 67 dup (0) push5d db 67 dup (0) push6d db 67 dup (0) ;up to here must be EXACTLY identical in PUSHD, POPD and DIRS. ;**************************************************************** ; ; Data Section. ; ;**************************************************************** not_inst db 'Must run PUSHD.COM before POPD.COM' db ' will do anything.',13,10,10 NOT_INST_LN equ $-not_inst errpop1 db 'Error popping the current directory',13,10,10 ERRPOP1_LN equ $-errpop1 empty_stack db 'No directories on the stack.',13,10 EMPTY_STACK_LN equ $-empty_stack BLANK equ 20h ;' ' DELIMITER equ 2Bh ;'+' LOW_LIMIT equ 31h ;'1' UP_LIMIT equ 36h ;'6' bad_delim db 'Invalid Delimiter.',13,10 BAD_DELIM_LN equ $-bad_delim in_dir_msg db 'Valid directories range between 1 and 6.',13,10 IN_DIR_MSG_LN equ $-in_dir_msg bs_online db 'Invalid Command Line Parameters.',13,10 BS_ONLINE_LN equ $-bs_online not_pushed db 'Parameter exceeds Directory Stack depth.',13,10 NOT_PUSHED_LN equ $-not_pushed params db ? ;storage for command line tail targ_dir dw offset main:push1d ;address storage ;******************************************************************* ; ; Code Starts. ; ;******************************************************************* START: ;get and store command line tail as it may get destroyed cld ;v1.2 I entirely rewrote this section. What a kludge! mov si,offset cmd_line ;command line tail lodsb ;snarf length byte, bump SI ;SI -> first char (probably a space!) xor ah,ah ;clear msb mov cx,ax ;into CX push ax ;save length on stack for later jcxz INST_CHK ;CX is 0, branch ;We have a non-null command line mov di,offset MAIN:params ;store since it may get rep movsb ;destroyed INST_CHK: ;is PUSHD already installed ? mov ax,7788h ;signature request mov bx,7789h ;signature request mov si,offset MAIN:signature ;point ds:si to signature int 13h ;is it installed ? ASSUME DS:CSEG ;assume TSR's DS v1.2 cmp bx,7788h ;were ax and bx switched ? jne NOTINSTALLED ;no cmp ax,7789h ;were ax and bx switched ? je ISINSTALLED ;v1.2 NOTINSTALLED: ;PUSHD is not installed, terminate with an error message. mov dx,offset MAIN:not_inst ;error message mov cx,NOT_INST_LN mov al,0FFH ;ERRORLEVEL v1.2 jmp Msg_Term ;display, terminate v1.2 ISINSTALLED: ; check for command line parameters pop cx ;get saved cx push DS ;store current ds mov ax,CS ;v1.2 mov DS,ax ;v1.2 ASSUME DS:MAIN ;reminder v1.2 or cx,cx ;check for no parameters jnz ERR1 jmp CHK_STACK ERR1: mov di,offset MAIN:params mov al,BLANK ;remove leading blanks repe scasb dec di inc cx mov si,di ;save position lodsb ;snarf first char, bump SI v1.2 cmp al,BLANK ;space? v1.2 jne ERR2 jmp CHK_STACK ;only blanks on tail ERR2: cmp al,DELIMITER ;is it '+' delimiter? v1.2 je CHK_VALUE ;Current character is not valid -- Print error message and exit mov dx,offset MAIN:bad_delim ;bad delimiter message mov cx,BAD_DELIM_LN mov al,0FEH ;ERRORLEVEL v1.2 jmp Msg_Term ;display, terminate v1.2 CHK_VALUE: ;check that parameter values are within specified limits dec cx ;v1.2 Recoded lodsb ;snarf digit after '+', bump SI cmp al,UP_LIMIT ;above '6'? ja INVALID_DIR ;yep, bogus cmp al,LOW_LIMIT ;below '1'?' jnb FINISH_CHK ;nope, in range ['1'..'6'] INVALID_DIR: ;invalid directory number -- print message and exit mov dx,offset MAIN:in_dir_msg ;error message mov cx,IN_DIR_MSG_LN mov al,0FDH ;ERRORLEVEL v1.2 jmp Msg_Term ;display, terminate v1.2 FINISH_CHK: ;continue checking for invalid characters on the command line ; after getting valid characters. dec cx ;decr cmdline count for digit v1.2 jcxz VALID_DIR mov ah,al ;save digit v1.2 mov al,BLANK ;check for following nonblank repne scasb ;characters mov al,ah ;replace digit v1.2 jcxz VALID_DIR ;Invalid characters on the command line -- print message and ; exit mov dx,offset MAIN:bs_online ;error message mov cx,BS_ONLINE_LN mov al,0FCH ;ERRORLEVEL v1.2 jmp Msg_Term ;display, terminate v1.2 VALID_DIR: ;get stack location of targeted directory xor ah,ah ;clear ah v1.2 mov cx,67 ;multiplier v1.2 sub al,'1' ;'0'..'5' range mul cx ;directory offset v1.2 add ax,offset MAIN:push1d ;base directory buffer v1.2 mov targ_dir,ax ;save it v1.2 CHK_DIR: ;check that the directory stack depth is at least to ; the targeted directory location pop DS ;restore ds ASSUME DS:CSEG ;reminder v1.2 mov bp,DS:[nextpush] ;get location of last sub bp,67 ;pushed directory cmp bp,CS:[targ_dir] ;make sure directory stack jge PROCESS_DIRS ;is deep enough for params ;the parameter number "n" exceeds the depth of the directory stack ; print a message and exit with an error mov dx,offset MAIN:not_pushed ;error message mov cx,NOT_PUSHED_LN mov al,0FBH ;ERRORLEVEL v1.2 jmp Msg_Term ;display, terminate v1.2 PROCESS_DIRS: ;stack is deep enough, if the targeted directory location is ; at the top of the stack, simply decrement ds:[nextpush], ; effectively erasing the top directory and exit successfully. jne NOT_TOP ;check on popping top of stack, set mov DS:[nextpush],bp ;nextpush to be top of current stack jmp SHORT EXITING NOT_TOP: ;targeted directory is in the middle of stack, so decrement ; DS:[nextpush] to reflect stack deletion, set the current location ; to be the targeted stack location, and... mov DS:[nextpush],bp ;set nextpush to top of stack mov bp,CS:[targ_dir] ;set bp to be targeted directory mov ax,DS ;v1.2 mov ES,ax ;v1.2 ASSUME ES:CSEG ;reminder v1.2 ;handy constants v1.2 mov dx,DS:[nextpush] ;v1.2 mov ax,67 ;v1.2 mov bx,bp ;v1.2 DIR_LOOP: ;...cycle through the stack copying the contents of the directory ; above the current location into the current location... add bp,ax ;go to next pushed directory v1.2 cmp bp,dx ;DS:[nextpush] v1.2 je LAST_DIR ;found the last directory mov si,bp ;copy contents to mov di,bx ;targ_dir v1.2 mov cx,ax ;67 v1.2 rep movsb add bx,ax ;targ_dir,67 v1.2 jmp SHORT DIR_LOOP ;continue LAST_DIR: ;...until the last directory is copied into the nearest lower ; storage location mov si,bp ;one more copy mov di,bx ;targ_dir v1.2 mov cx,ax ;67 v1.2 rep movsb jmp SHORT EXITING ;exit successfully CHK_STACK: ;handle parameterless command ;get the location of the directory most recently saved by pushd ; (top of stack) pop DS ;restore ds ASSUME DS:CSEG ;reminder v1.2 mov bp,DS:[nextpush] ;get the next push location sub bp,67 ;back up one to the last push cmp DS:[nextpush],offset MAIN:push1d ;empty stack? jne NOTEMPTY ;no ;directory stack is empty (ds:[nextpush] = location of first ; storage location) -- print message and exit with error mov dx,offset MAIN:empty_stack ;error message mov cx,EMPTY_STACK_LN mov al,0FAH ;ERRORLEVEL v1.2 jmp Msg_Term ;display terminate v1.2 NOTEMPTY: ;directory stack is not empty -- change to directory on top of ; stack ;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 ;set the current drive if previous commands are successful mov al,DS:[bp] ;get drive letter from path sub al,'A' ;convert to binary (0=A, 1=B) mov dl,al mov ah,0eh ;dos function number int 21h ;set drive jc ERRPOP mov DS:[nextpush],bp ;update [nextpush] if successful EXITING: ;clear up previous directory storage v1.2 mov ax,DS ;v1.2 mov ES,ax ;v1.2 ASSUME ES:CSEG ;reminder v1.2 mov cx,67 ;v1.2 mov di,DS:[nextpush] ;v1.2 mov al,BLANK ;v1.2 rep stosb ;v1.2 ;exit successfully with no message mov ax,4c00h int 21h ;exit ERRPOP: ;An error occurred while attempting to change to targeted ; directory -- print error message and exit with error mov dx,offset MAIN:errpop1 ;error message mov cx,ERRPOP1_LN mov al,0F9H ;ERRORLEVEL v1.2 ; jmp Msg_Term ;display, terminate v1.2 ;********************************************************************* ; ; PROCEDURE: Msg_Term ; ; Purpose: This procedure will handle the printing of error messages ; and termination with the correct errorlevel. ; Preconditions: ; DX = error message address. ; CX = error message length. ; AL = ERRORLEVEL. ; Postconditions: ; The program will be terminated with the appropriate ; error message printed on standard error and with the ; the errorlevel stored in al. ; ;*************************************************************************** Msg_Term PROC NEAR ;v1.2 Common routine to display error messages and terminate. push CS ;insure this is so pop DS ASSUME DS:MAIN push ax ;save ERRORLEVEL in AL mov bx,2 ;write to stderr mov ah,40h ;dos function number int 21h ;show error message pop ax ;restore ERRORLEVEL in AL mov ah,4CH ;terminate process int 21h Msg_Term ENDP CSEG ends end BEGIN ;start execution at BEGIN