; TITLE 'ENTAB - REPLACE SPACES WITH TABS V1.6 ' ; ; A SOFTWARE TOOL AS DESCRIBED BY KERNIGAN AND PLAUGHER ; WHICH COMPRESSES SPACES INTO APPROPRIATE TABS AS PER ; CP/M CONVENTIONS. IT IS USEFUL FOR COMPACTING A PROGRAM ; AFTER A MODEM TRANSFER WHICH EXPANDS THE TABS. ; ; ; REVISION HISTORY ; ;11/11/84 ; REVISED V 1.5 FROM SIG/M VOL. 65 ; ADDED HELP MESSAGE. OUTPUT DRIVE DEFAULTS TO CURRENT DRIVE ; RATHER THAN INPUT DRIVE. MODIFIED EOFILE ROUTINE. IF V 1.5 ; FOUND A SINGLE SPACE AT A TAB POSITION IT WOULD SUBSTITUTE ; A TAB FOR THE SPACE. WE NOW OUTPUT ONLY THE SPACE. ; ;10/10/81. ; ADDED EQU FOR MODIFIED CP/M AND QUOTE AS DEFAULT DELIM. T. SHAPIN ; ;09/05/81 ; 1. REWROTE COMPRESSION ROUTINE TO WORK PROPERLY ; ; 2. ADDED FEATURE TO DISABLE COMPRESSION INSIDE ; QUOTED STRINGS, SINCE SOME LANGUAGES AND ; ASSEMBLERS DON'T LIKE TABS IN THEIR STRINGS. ; FOR EXAMPLE, AN ASSEMBLY FILE MAY HAVE A LINE: ; ; PASSWORD: DB ' ' ; ; IS THAT THE SAME THING AS THIS LINE? ; ; PASSWORD: DB 09H ; ; MOST DEFINITELY NOT, BUT THAT IS WHAT WE WOULD ; GET IF ENTAB COMPRESSED THOSE BLANKS INTO 1 TAB ; CHARACTER. ENTAB WILL PROMPT FOR THE STRING ; FIELD DELIMITING CHARACTER EACH TIME IT IS RUN ; (NORMALLY ' FOR ASSEMBLERS, AND " FOR BASICS). ; ; 3. MODIFIED OUTPUT FILE CODE TO SAVE THE ORIGINAL ; FILE AS A "BAK" FILE IF THE OUTPUT FILE IS GOING ; TO THE SAME DRIVE AS THE INPUT FILE. IF THE ; OUTPUT FILE IS GOING TO A DIFFERENT DRIVE, THEN ; THE ORIGINAL FILE WILL BE LEFT UNCHANGED. ; PREVIOUSLY, THE ORIGINAL FILE WAS ALWAYS ERASED. ; BIAS EQU 0H ; 0 FOR STANDARD CP/M, 4200H FOR MODIFIED BDOS EQU 5+BIAS ;CPM ENTRY FCB EQU 5CH+BIAS ;DEFAULT FCB TBUF EQU 80H+BIAS ;COMMAND LINE BUFFER ; PRINT EQU 9 BUFFIN EQU 10 OPEN EQU 15 CLOSE EQU 16 DELETE EQU 19 READ EQU 20 WRITE EQU 21 CREATE EQU 22 RENAME EQU 23 GETDSK EQU 25 SETDMA EQU 26 BLANK EQU ' ' TAB EQU 9 CR EQU 0DH LF EQU 0AH EOF EQU 1AH ;CONTROL Z DOLLAR EQU '$' ; REC EQU 16 ;NUMBER OF SECTORS PER TRANSFER ; ORG 100H+BIAS LXI H,0 ;SET UP OUR OWN STACK DAD SP ;SAVE THEIRS SHLD STACK LXI SP,STACK ; LXI D,SGNMSG ;TELL THE FOLKS OUR NAME MVI C,PRINT CALL BDOS JMP GETFCB ; SGNMSG: DB CR,LF,'ENTAB - VERS 1.6',CR,LF,LF,'$' DB ' - 11/11/84' ;DOESN'T PRINT ; GETFCB: LDA TBUF ;GET COMMAND LINE LENGTH CPI 4 ;TO SHORT TO BE VALID? JC HELP ;YES, GIVE HELP LXI H,FCB ;GET THE TYPED IN FILE NAME LXI D,FCB1 MVI C,16 CALL MOVE ;AND MOVE IT TO OUR OWN LXI H,FCB LXI D,FCB2 MVI C,16 CALL MOVE ;AND AGAIN ; LXI D,FCB1 MVI C,OPEN CALL BDOS ;OPEN IT INR A JNZ FILSEC ;NO ERROR LXI D,FNFMSG ;'FILE NOT FOUND' MVI C,PRINT CALL BDOS JMP EXIT ; FNFMSG: DB 'File Not Found',CR,LF,DOLLAR ; FILSEC: LXI H,FCB2+9 ;SEC NAME MVI M,DOLLAR ;MAKE IT '.$$$' INX H MVI M,DOLLAR INX H MVI M,DOLLAR LDA FCB+16 ORA A ;ANY OUTPUT DRIVE SPECIFIED? JNZ DEFALT ;YES, USE IT MVI C,GETDSK CALL BDOS ;ELSE GET CURRENT DRIVE INR A ;MAKE IT 1 RELATIVE AND .. DEFALT: STA FCB2 ;.. STASH IT IN OUTPUT FILE FCB ; DEL: MVI A,0 ;MAKE NEXT RECORD 0 STA FCB1+32 STA FCB2+32 LXI D,FCB2 ;DELETE ANY EXISTING TEMPORARY FILES MVI C,DELETE CALL BDOS ; LXI D,FCB2 MVI C,CREATE CALL BDOS ;NOW CREATE TEMPORARY OUTPUT FILE INR A JNZ GTDEL ;FILE CREATED LXI D,DSKMSG ;'NO DIRECTORY SPACE' MVI C,PRINT CALL BDOS JMP EXIT ;CAN'T KEEP GOING ; PROMPT: DB 'Enter the String Delimiter ( = '') ? $' BEGMSG: DB CR,LF,'Beginning Blanks Compression',CR,LF,'$' ; GTDEL: LXI D,PROMPT ;ASK FOR DELIMITER CHARACTER MVI C,PRINT CALL BDOS ; ;WE INPUT THE DELIMITER CHARACTER BY USING THE BUFFER INPUT ;ROUTINE TO ALLOW THE USER TO CORRECT AN INPUT ERROR, OR TO ;ABORT TO CP/M. A SINGLE CONIN CALL, ALTHOUGH REQUIRING LESS ;CODE, WOULD NOT ALLOW ANY MARGIN FOR ERROR IN SPECIFYING THE ;DELIMITER CHARACTER. ; LXI D,CIBUF ;SETUP CONSOLE INPUT BUFFER MVI A,2 STAX D ;ALLOW 2 CHARACTERS MAXIMUM MVI C,BUFFIN ;GET THE DELIMITER CHARACTER CALL BDOS LDA CIBUF+1 ;HOW MANY CHARACTERS ENTERED? ORA A MVI A,'''' ;DEFAULT TO QUOTE JZ SAVDEL ;USE DELIMITER OF 0 IF NO INPUT LDA CIBUF+2 ;ELSE, EXTRACT 1ST CHARACTER SAVDEL: STA DELIM+1 ;SAVE IT IN-LINE FOR COMPARISON LXI D,BEGMSG ;TELL USER WE'VE BEGUN MVI C,PRINT CALL BDOS ; ;BEGIN SCANNING INPUT BUFFER AND PACK THE SPACES INTO TABS ; ENTAB: XRA A STA COLUMN ;INITIALIZE TAB POSITION STA BLANKS ;CLEAR BLANK ACCUMULATOR STA SFLG ;CLEAR STRING FLAG NEXCHR: CALL GETC ;GET NEXT CHARACTER FROM FILE STA CHAR ;SAVE INPUT CHARACTER CPI TAB JNZ NOTAB CALL DUMBLK ;ON TAB, DUMP PENDING BLANKS TABIT: MVI A,TAB ;PASS THE TAB ALONG CALL PUTC XRA A STA COLUMN ;CLEAR TAB POSITION JMP NEXCHR ;GET THE NEXT CHARACTER ; NOTAB: CPI LF ;END OF LINE MARKERS? JZ EOL CPI CR JNZ NOEOL EOL: CALL DUMBLK ;DUMP TRAILING BLANKS IF SO LDA CHAR ;THEN DUMP THE CR OR LF CALL PUTC JMP ENTAB ;AND START NEXT LINE ; NOEOL: CPI BLANK ;SPACE CHARACTER? JNZ NOBLK LXI H,BLANKS ;INCREMENT BLANK COUNTER INR M LDA SFLG ;ARE WE IN A QUOTED STRING? ORA A JNZ STRING ;BLANK NOT SIGNIFICANT IF SO CALL BUMCOL ;BUMP TAB POSITION JNZ NEXCHR ;CONTINUE SCANNING IF NOT AT TAB STOP MOV A,M ;GET CURRENT BLANK COUNT MVI M,0 ;CLEAR COUNT TO ZERO CPI 2 ;WAS IT LESS THAN 2? JNC TABIT ;NO, OUTPUT A TAB JMP NOBLK1 ;YES, JUST OUTPUT A SINGLE SPACE ; STRING: CALL BUMCOL ;INSIDE QUOTED STRING, BUMP TAB POSITION JMP NEXCHR ;CONTINUE SCANNING TIL END OF STRING ; NOBLK: CPI EOF ;CP/M FILE EOF? JZ EOFILE CPI 0FFH ;GETBUF EOF? JZ EOFILE CALL DUMBLK ;CHARACTER NONE OF ABOVE - FLUSH BLANKS CALL BUMCOL ;BUMP TAB POSITION NOBLK1: LDA CHAR PUSH PSW CALL PUTC ;SEND LITERAL CHARACTER POP PSW DELIM: CPI 0 ;IS CHARACTER STRING DELIMITER? JNZ NEXCHR LDA SFLG ;TOGGLE STRING FLAG IF SO CMA STA SFLG JMP NEXCHR ;SCAN FOR NEXT CHARACTER ; DUMBLK: LDA BLANKS ;FLUSH THE ACCUMULATED BLANKS ORA A RZ DCR A STA BLANKS MVI A,' ' CALL PUTC JMP DUMBLK ; BUMCOL: LDA COLUMN ;INCREMENT TAB POSITION INR A ANI 7 STA COLUMN RET ; EOFILE: MVI A,EOF CALL PUTC ;OUTPUT EOF CHARACTER ; LHLD COCNT XCHG LHLD COPNT EOFLOP: MVI M,EOF ;FILL OUT LAST SECTOR WITH EOF MOV A,E ANI 7FH ; IS LOW BYTE OF COUNT 0 OR 128? JZ EOF1 ;YES, DONE. INX H INX D JMP EOFLOP ;NO, LOOP BACK ; EOF1: SHLD COPNT ;REPLACE POINTER .. XCHG SHLD COCNT ;.. AND BYTE COUNT CALL PUTBUF ;WRITE UNFINISHED BUFFER LXI D,FCB2 MVI C,CLOSE CALL BDOS ;CLOSE THE NEW FILE ; LDA FCB2 ;GET DESTINATION DR CODE TO INPUT FCB STA FCB1 LXI H,FCB1 ;SETUP RENAME FCB FOR ORIGINAL FILENAME LXI D,FCB1+16 PUSH D ;SAVE POINTER TO 2ND RENAME FIELD PUSH D MVI C,9 CALL MOVE ;MOVE FILENAME TO 2ND RENAME FIELD XCHG MVI M,'B' ;MAKE 2ND RENAME FILETYPE="BAK" INX H MVI M,'A' INX H MVI M,'K' POP D ;RECALL POINTER TO 2ND RENAME FIELD MVI C,DELETE ;ERASE ANY EXISTING BAK FILE ON OUTPUT DRIVE CALL BDOS POP H ;RECALL RENAME FIELD POINTER AGAIN MVI M,0 ;CLEAR DR IN 2ND RENAME FIELD ; LXI D,FCB1 ;RENAME ANY FILE ON OUTPUT DRIVE WITH INPUT MVI C,RENAME ;..FILENAME TO TYPE "BAK" CALL BDOS ; KEEPIT: LXI H,FCB1+1 ;MOVE THE NEW FILE NAME LXI D,FCB2+16 ;TO FCB2 XRA A STAX D INX D MVI C,11 CALL MOVE ; LXI D,FCB2 MVI C,RENAME CALL BDOS ;RENAME .$$$ TO ORIGINAL ; LXI D,DNMSG ;TELL THEM WE'RE DONE MVI C,PRINT CALL BDOS JMP EXIT ; DNMSG: DB 'Blanks Compression Completed',CR,LF,DOLLAR ; EXIT: LXI D,80H+BIAS ;RESET DEFAULT DMA MVI C,SETDMA CALL BDOS ; LHLD STACK SPHL RET ;BACK TO CP/M ; ; ; MOVE -MOVE (HL -> (DE) FOR C BYTES ; MOVE: MOV A,M STAX D INX H INX D DCR C JNZ MOVE RET ; ; GETC: LHLD CICNT ;ANY CHAR IN BUFFER? MOV A,H ORA L CZ GETBUF ;GET SOME IF NOT LHLD CIPNT ;POINTER TO CHAR MOV A,M ;GET THE CHARACTER INX H ;INCREMENT THE POINTER SHLD CIPNT LHLD CICNT ;REDUCE THE COUNT DCX H SHLD CICNT RET ; GETBUF: MVI A,REC ;RECORDS PER BUFFER LXI H,0 SHLD CICNT ;INIT FOR READ LXI H,CIBUF SHLD CIPNT ;INIT THE POINTER GBUF: SHLD DMAADD ;IPDATE DMA ADDRESS XCHG ;FOR BDOS CALL STA SECCNT ;UPDATE SECTORS TO GO ORA A RZ ;RETURN IF NONE MVI C,SETDMA CALL BDOS ;SET LOAD ADDRESS LXI D,FCB1 MVI C,READ CALL BDOS ;GET A SECTOR PUSH PSW ;SAVE DISK STATUS LHLD CICNT LXI D,128 ;# OF BYTES READ DAD D SHLD CICNT ;UPDATE IT LHLD DMAADD ;UPDATE POINTER POP PSW ;GET DISK STATUS ORA A JZ ROK ;READ WAS OK MVI M,0FFH ;OUR OWN END OF FILE RET ; ROK: DAD D ;THIS TO LDA SECCNT DCR A ;ONE LESS LEFT JMP GBUF ; PUTC: LHLD COPNT ;POINTER MOV M,A ;PUT CHAR IN BUFFER INX H SHLD COPNT ;INCREMENT POINTER LHLD COCNT INX H ;BUMP IT SHLD COCNT MOV A,H ;SEE IF END OF BUFFER CPI REC*128/256 RNZ CALL PUTBUF ;WRITE BUFFER TO DISK LXI H,0 SHLD COCNT ;RE-INIT FOR NEXT TIME RET ; PUTBUF: LXI H,COBUF SHLD COPNT ;RE-INIT FOR NEXT TIME PBUF: SHLD DMAADD ;UPDATE WRITE ADDRESS XCHG ;TO DE FOR CPM CALL LHLD COCNT ;ANY LEFT TO WRITE MOV A,L ORA H RZ ;IF NOT MVI C,SETDMA CALL BDOS LXI D,FCB2 ;WRITE FILE BLOCK MVI C,WRITE CALL BDOS ORA A ;CHECK FOR ERROR JNZ WRERR ;.AND TAKE CARE OF IT LHLD COCNT ;BYTE COUNT LXI D,-128 ;PER SECTOR DAD D ;THIS MANY LEFT SHLD COCNT MOV A,H ;GET SIGN BIT ORA A ;TEST IT RM ;ALL DATA WRITTEN (NOT A FULL BUFFER) LHLD DMAADD ;UPDATE BUFFER LXI D,128 DAD D JMP PBUF ; WRERR: LXI D,DSKMSG ;'WRITE ERROR' MVI C,PRINT CALL BDOS JMP EXIT ;THIS WILL FIX STACK ; DSKMSG: DB 'Disk or Directory Full',CR,LF,DOLLAR ; HELP: LXI D,USAGE ;SHOW HOW TO USE MVI C,PRINT CALL BDOS JMP EXIT ; DS 48 ;SOME STACK SPACE STACK: DW 0 COLUMN: DB 0 BLANKS: DB 0 SFLG: DB 0 CHAR: DB 0 ;INPUT CHAR SECCNT: DB 0 ;SECTORS TO READ CICNT: DW 0 COCNT: DW 0 DMAADD: DW 0 FCB1: DS 33 FCB2: DS 33 CIPNT: DW CIBUF COPNT: DW COBUF ; USAGE: DB CR,LF DB 'ENTAB.COM reads an input text file and compresses spaces' DB CR,LF DB 'into appropriate TABs as the text is written to an output' DB CR,LF DB 'file. You will be prompted for a string delimiter character' DB CR,LF DB '(either '' or " ). Any spaces within a string enclosed' DB CR,LF DB 'by the delimiter will NOT be compressed to TABs.',CR,LF,LF DB 'USAGE:',CR,LF,' ENTAB [d1:]INFILE.TYP [d2:]',CR,LF,LF DB 'd1: is an optional drive for the input file.',CR,LF DB 'INFILE.TYP is the name of the input file and will also' DB CR,LF DB 'be the name of the output file.',CR,LF DB 'd2: is an optional drive for the output file.',CR,LF DB 'Both d1 and d2 will default to the current drive.',CR,LF DB 'If d1 = d2 the filetype of the input file will be',CR,LF DB 'changed to .BAK',CR,LF DB CR,LF,'$' ; CIBUF: DS 128*REC COBUF: DS 128*REC END