'===========================================================================
' Subject: VIEW DIRECTORIES/FILES ZIP/LZH     Date: 10-25-99 (18:05)       
'  Author: Randall L. Glass                   Code: PB                     
'  Origin: rlglass@yahoo.com                Packet: PB.ABC
'===========================================================================
'-------------------------------------------------------------------------
'
'                   Shell BY Randall L Glass
'                      CopyRight Aug3,1999
'
'---------------------------------------------------------------------------
'
' Email rlglass@yahoo.com
'
'---------------------------------------------------------------------------
'
' This Program is Freeware
' You may do anything with this program AS long AS you give me credit
' somewhere in the documentation.
'
'===========================================================================
'
' Program to view directoryies and files ,Including ZIP and Lzh
'
' Credit to Author: Jamshid Khoshrangi for his BOYER-MOORE INSTR              Code: PB
' Thanks to William Yu FOR his ZipView & LzhView progam.
'
'
' You must SET the value of TEMP in your autoexec.bat file.
' Example SET TEMP = D:\TMP
'
' OR Rewrite the code in the program to set TempDir to your tempory directory.
' And Rewrite the code to your editor or file viewer.
'
' This includes an text viewer to view huge files up to 32700 lines
' Possible size 2.6 Gig
'
' List File Viewer is Faster
' You Can always replace my file viewer with an shell to List.com
'
' Look for SUB viewer
'
'----------------------------------------------------------------------------
$DYNAMIC
$HUGE
$STACK 10000

DEFINT A-Z

TYPE ZipType                 ' Zip File Header
    Signature      AS DWORD
    Version        AS WORD
    GPBFlag        AS WORD          ' Not sure
    Compress       AS WORD          ' Compression type
    FileTime       AS WORD
    Filedate       AS WORD         ' Took a while to figure out
    CRC32          AS DWORD
    CompressSize   AS DWORD
    UnCompressSize AS DWORD
    FileNameLen    AS WORD
    ExtraField     AS WORD
END TYPE

TYPE LZHHead
     HeadSize       AS BYTE
     CheckSum       AS BYTE
     Id1            AS STRING * 3
     Method         AS BYTE
     Id2            AS BYTE
     CompressSize   AS DWORD
     UnCompressSize AS DWORD
     FileTime       AS WORD
     FileDate       AS WORD
     Attrs          AS BYTE
     HeaderLevel    AS BYTE
     FileNameLen    AS BYTE
     FileName       AS STRING * 12
END TYPE

DECLARE Function GetKey%() AS INTEGER
DECLARE SUB GetStrLoc()
DECLARE SUB BoxBorder ()
DECLARE SUB Scroll(BYVAL TopRow%,BYVAL TopLeftCol%,BYVAL BottomRow%,BYVAL BottomRightCol%,BYVAL TotalLines%,BYVAL Attr%,BYVAL WhichWay%)


DIM ScreenSegment AS shared WORD
DIM DirStack$(10),ShellDir$(10)
DIM Directory$(1000), DirInfo$(1000)

SHARED Directory$(), DirInfo$()

SHARED Viewer$,TempDir$,StackPos%,DirStack$()
SHARED StartLine$,EndLine$,FileList$,Response$,ExtractCmd$,ExtractReponse$
SHARED DosError??,ShellDir$(),ShellPos%,ExitShell%
SHARED NextHandle%,LineSeg??,LinePtr??

IF(PBVScreenCARD AND 1) = 0 THEN
      ScreenSegment?? = 47104       '&HB800          'color monitor
ELSE
      ScreenSegment?? = 45056       '&HB000          'mono monitor
END IF


%SIG = &H04034B50             ' Signature
%UpArrow = -72:%DownArrow = -80:%Enter = 13:%Escape = 27
%Home = -71:%PageDown = -81:%PageUp = -73:%EndKey = -79
%Up = 6:%Down = 7:%LeftArrow = -75:%RightArrow = -77
%AltHome = -151
%U = 85
%ReadOnly = 1:%Hidden = 2:%System = 4:%Volume = 8:%SubDir = 16:%Archive = 32
%AllFiles = 33

CLS
'Viewer$ = "listr.com"
LastDir$ = CURDRIVE$ + ":\"
CHDIR LastDir$
IF(PBVScreenCARD AND 1) = 0 THEN
      ScreenSegment?? = 47104       '&HB800          'color monitor
ELSE
      ScreenSegment?? = 45056       '&HB000          'mono monitor
END IF

EXIT FAR AT BackHome
DO
	ShellPos% = 0
	DosShell LastDir$
BackHome:
LOOP WHILE ShellPos%
END

SUB DosShell(ShellDirectory$)

Choice% = 1:DisplayPos% = 1:LowFIle% = 1:HiFile% = 18:Ky% = 0
InfoLine$ = "   FileName"+"       Size"+"     Date" + "       Time"
PushDir2 ShellDirectory$,ShellDir$(),ShellPos%

DO

        TotalFiles% = 0
        RealDir$ = AddSlash$(TrueDir2$(ShellDir$(), ShellPos%))
        GetDirAndFiles RealDir$ + "*.*", %SubDir  , Directory$(), DirInfo$(), FilesInfo$, TotalFiles%
        DirNum% = TotalFiles%
        IF DirNum% > 1 THEN ARRAY SORT Directory$(1) FOR TotalFiles%,TagArray DirInfo$()
        GetDirAndFiles RealDir$ + "*.*", %AllFiles, Directory$(), DirInfo$(), FilesInfo$, TotalFiles%
        SortNumber% = TotalFiles - DirNum%
        IF SortNumber% > 1 THEN
            FileStart% = DirNum% + 1
        	ARRAY SORT Directory$(FileStart%) FOR SortNumber%,TagArray DirInfo$()
        END IF
        Pick% = ChooseProgram%( DirInfo$(),Directory$(),FilesInfo$,InfoLine$,Choice%,DisplayPos%,LowFile%,HiFile%,Ky%,DirNum%,"Directory")
        SELECT CASE Ky%
           CASE %RightArrow,%Enter
                IF Attrib(RealDir$ + Directory$(Pick%)) = 16 THEN
                		DosShell(Directory$(Pick%))
                        COLOR 7,1
                        CLS
                ELSEIF Attrib(RealDir$ + Directory$(Pick%)) <> 16 THEN
				Ext$ = Extension$(Directory$(Pick%))
                        SELECT CASE Ext$
                  		CASE ".ZIP",".LZH"
						ShowZip(RealDir$ + Directory$(Pick%))
                        	CASE ELSE        '".TXT",".DOC"
                                Viewer RealDir$ + Directory$(Pick%)
                              	CLS
                              	DELAY .2
                              	COLOR 7,0
                  	END SELECT
                END IF
        	CASE %LeftArrow,%Escape
			DECR ShellPos%
                  EXIT LOOP

        	CASE %AltHome
                  EXIT FAR
        END SELECT
LOOP
END SUB

SUB ShowZip(File$)
CLS
LOCATE 1,1,0
Ext$ = Extension$(File$)
File$ = TrueName$(File$)
IF ISFALSE FileExist%(File$) THEN EXIT SUB
TempDir$ = ENVIRON$("TEMP") + "\dosview"
PushDir TempDir$
ViewZip File$
PopDir
SHELL "DELTREE /Y " + Rtrim$(RealDir$,"\") + " >NUL"
COLOR 7,0
CLS
END SUB

SUB ViewZip(File$)
IF ISFALSE FileExist%(File$) THEN
	LOCATE 5,15:PRINT File$;" doesn't exist !";
      EXIT SUB
END IF
DIM ArchiveFiles$(1000),ArchiveInfo$(1000),ArchiveDir$(1000)
  Choice% = 1:DisplayPos% = 1:LowFIle% = 1:HiFile% = 18
  Ky% = 0
  InfoLine$ = "   FileName"+"       Size"+"     Date" + "       Time"
  CLS
  Ext$ = Extension$(File$)
   IF Ext$ = ".ZIP" OR Ext$ = ".LZH" THEN
      PresentDir$ = CURDIR$
      PresentDrive$ = CURDRIVE$
      CHDRIVE Left$(File$,1)
      CHDIR GetDirectory$(File$)
      SELECT CASE Ext$
          CASE ".ZIP"
                ZipView GetFileName$(File$), ArchiveFiles$(), ArchiveInfo$(), FilesInfo$, Success%
          CASE ".LZH"
                LzhView GetFileName$(File$), ArchiveFiles$(), ArchiveInfo$(), FilesInfo$, Success%
      END SELECT
      CHDRIVE PresentDrive$
      CHDIR PresentDir$
      IF ISFALSE Success% THEN EXIT SUB
      TotalFiles?? = CVWRD(ArchiveFiles$(0))
      'ARRAY SORT ArchiveFiles$(1) FOR TotalFiles??,TAGARRAY ArchiveInfo$()
      DO
               Pick% = ChooseProgram%( ArchiveInfo$(),ArchiveFiles$(),FilesInfo$,InfoLine$,Choice%,DisplayPos%,LowFile%,HiFile%,Ky%,0,"")
               Choice% = Pick%
                SELECT CASE Ky%
                        CASE %Escape,%LeftArrow
                                EXIT SUB
                        CASE %RightArrow
                                IF Pick% > 0 THEN
                                		Pick$ = UCASE$(ArchiveFiles$(Pick%))
                                          RealName$ = GetFileName$(Pick$)
                                        	PickExt$ = Extension$(Pick$)
                                        	IF PickExt$ =".ZIP" OR PickExt$ = ".LZH" THEN
                                                PushDir "TEMP"
                                                RealDir$ = TrueDir$ + "\"
                                                IF ISFALSE FileExist%(RealDir$+Pick$) THEN
                                                        CLS
                                                        SELECT CASE Ext$
                                                        CASE ".ZIP"
                                                                SHELL "PKUNZIP -e -o " + File$ + " " + Pick$ + " "  + RealDir$ + " >NUL"
                                                                'SHELL "UNZIP " + File$ + " " + Pick$ + " " + "-d" + RealDir$ '+ " >NUL"
                                                        CASE ".LZH"
                                                                SHELL "lha.exe e /pn2 " + File$ + " " + RealDir$ + " "+ Pick$
                                                  END SELECT
                                                END IF
                                                fe% = FetchErrorLevel%
                                                IF FetchErrorLevel% > 0 THEN
                                                        SHELL "DELTREE /Y " + Rtrim$(RealDir$,"\") + " >NUL"
                                                        EXIT SUB
                                                END IF
                                                ViewZip RealDir$ + RealName$
                                                SHELL "DELTREE /Y " + Rtrim$(RealDir$,"\") + " >NUL"
                                                PopDir
                                                CLS
                                                DELAY .2
                                                ITERATE LOOP
                                        	ELSE
                                                CLS
                                                RealDir$ = TrueDir$ + "\"
                                                SELECT CASE Ext$
                                                        CASE ".ZIP"
                                                                SHELL "PKUNZIP -e -o " + File$ + " " + Pick$ + " "  + RealDir$ + " >NUL"
                                                                'SHELL "UNZIP " + File$ + " " + Pick$ + " " + "-d" + RealDir$ ' + " >NUL"
                                                        CASE ".LZH"
                                                                SHELL "lha.exe e /pn2 " + File$ + " " + RealDir$ + " "+ Pick$
                                                END SELECT
                                        	END IF
                                        'CLS
                                        fe% = FetchErrorLevel%
                                        IF FetchErrorLevel% > 0 THEN
                                                SHELL "DELTREE /Y " + Rtrim$(RealDir$,"\") + " >NUL"
                                                EXIT SUB
                                        END IF

                                        PresentDir$ = CURDIR$
                                        PresentDrive$ = CURDRIVE$
                                        CHDRIVE LEFT$(TrueDir$,1)
                                        CHDIR TrueDir$
                                        Viewer RealName$
                                        CLS
                                        KILL RealName$
                                        CHDRIVE PresentDrive$
                                        CHDIR PresentDir$
                                        DELAY .2
                                        COLOR 7,0
                                    END IF
                        'CASE %U
                END SELECT
      LOOP
   END IF
END SUB

FUNCTION ChooseProgram%(ArchiveInfo$(),ArchiveFile$(),InfoLine1$,InfoLine2$ ,Choice%, DisplayPos%,LowFile%, HiFile%,Ky%,DirNum%,ChooseType$)
   DIM PrintLine AS STRING * 47
   DIM DirLine AS STRING * 80
   DisplayLine% = 18

   TotalFiles% = CVWRD(ArchiveInfo$(0))
   IF TotalFiles% < DisplayLine% THEN HiFile% = TotalFiles%

     BottomDisplayLine% = DisplayLine%
     COLOR 7,1
     CLS
     COLOR 0,7
     LOCATE 1,1:PRINT SPACE$(80);
     COLOR 7,1
     LOCATE 2,2:PRINT Ltrim$(InfoLine1$)
     LOCATE 4,2:PRINT InfoLine2$

   DO
      COLOR 15, 1
      LinePos% = 0
      IF TotalFiles% < 15 THEN BottomDisplayLine% = TotalFiles%
      IF ScrollIt% = 0 THEN
	      'POKE$ 0, SaveScreen$ : LOCATE y%, x%       'restore old screen
	      FOR ArchiveFile% = LowFile% TO HiFile%
	         INCR LinePos%
               LSET PrintLine$ = ArchiveInfo$(ArchiveFile%)
	         IF LinePos% = DisplayPos% THEN
	            QPrint 5 + LinePos%,1, PrintLine$ ,71
	         ELSE
	            QPRINT 5 + LinePos%,1,PrintLine$,23
	         END IF
	      NEXT ArchiveFile%
		LSET PrintLine$ = ""
            FOR I% = LinePos% + 1 To DisplayLine%
	            QPRINT 5 + I%,1,PrintLine$,23
            NEXT I%
      ELSE
		LSET PrintLine$ = ArchiveInfo$(Choice%)
            QPRINT 5 + DisplayPos%,1,PrintLine$,71
      END IF

      DirLine$ = SPACE$(80)
      MID$(DirLine$,2) = ArchiveFile$(Choice%)
      COLOR 0,7:LOCATE 25,1:PRINT DirLine$;

      ScrollIt% = 0
      ClearBuffer
      DO: ky$ = UCASE$(INKEY$): LOOP WHILE ky$ = ""
      Ky% = ASC(ky$): IF Ky% = 0 THEN Ky% = -ASC(MID$(ky$, 2))
       SELECT CASE Ky%
         CASE %Enter,%RightArrow
         	ChooseProgram% = choice%: EXIT DO
         CASE %Escape
         	ChooseProgram% = 0: EXIT DO
         CASE %Home
         	choice% = 1:LowFile% = 1
            HiFile% = BottomDisplayLine%
            DisplayPos% = 1
         CASE %EndKey
            Choice% = TotalFiles%
            DisplayPos% = MODTYPE(Choice%, BottomDisplayLine%)
            LowFile% = Choice% - DisplayPos% + 1
            HiFile% = TotalFiles%
         CASE %PageDown
            INCR Choice%, DisplayLine%
            INCR LowFile%,DisplayLine%
            HiFile% = LowFile% + BottomDisplayLine% -1
            IF HiFile% > TotalFiles% THEN HiFile% = TotalFiles%
		IF choice% > TotalFiles% THEN
			Choice% = TotalFiles%
                        DisplayPos% = ModType(Choice%, BottomDisplayLine%)
			LowFile% = Choice% - DisplayPos% + 1
			HiFile% = TotalFiles%
	      END IF

         CASE %PageUp
            DECR Choice%,DisplayLine%
            DECR LowFile%,DisplayLine%
            HiFile% = LowFile% + DisplayLine% -1
            IF LowFile% < 1 THEN LowFile% = 1:HiFile% = BottomDisplayLine%
	      IF Choice% <=1 THEN
              Choice% = 1
              LowFile% = 1
              HiFile% = BottomDisplayLine%
              DisplayPos% = 1
      	END IF
         CASE %DownArrow
            INCR Choice%
            IF Choice% <= TotalFiles% THEN
                  INCR DisplayPos%
            ELSE
            	Choice% = TotalFiles%
            END IF
	      IF DisplayPos% > BottomDisplayLine% THEN
                DisplayPos% = BottomDisplayLine%
                LowFile% = Choice% -BottomDisplayLine% + 1
                IF HiFile%  >= TotalFiles% THEN
                   HiFile% = TotalFiles%
                ELSE
                   HiFile% = LowFile% + BottomDisplayLine% -1
                END IF
                PrintAtr BottomDisplayLine%+5, 1, 47, 23
                Scroll 5,1, BottomDisplayLine%+4,47, 1,23,%Up
                ScrollIt% = 1
      	END IF
         CASE %UpArrow
             DECR choice%
             DECR DisplayPos%
             IF choice% <=1 THEN
             	choice% = 1
             	LowFile% = 1
             	HiFile% = BottomDisplayLine%
             	DisplayPos% = 1
             ELSE
	            IF DisplayPos% < 1 THEN
		             DisplayPos% = 1
		             LowFile% = Choice%
		             HiFile% = Choice% + BottomDisplayLine% -1
                         IF HiFile%  >= TotalFiles% THEN
            		       HiFile% = TotalFiles%
                         ELSE
            		       HiFile% = LowFile% + BottomDisplayLine% -1
                         END IF
                         PrintAtr 6, 1, 47, 23
                         Scroll 5,1, BottomDisplayLine%+4,47, 1,23,%Down
                         ScrollIt% = 1
                  END IF
             END IF
        CASE 48 TO 57,65 TO 90
            IF ChooseType$ = "Directory" THEN
                  ScanFiles% = TotalFiles% - Choice%
			ARRAY SCAN ArchiveInfo$(Choice% + 1) FOR ScanFiles% ,FROM 2 TO 2,=Ky$,To PossibleChoice%
                  IF PossibleChoice% > 0 THEN
                  	Choice% = Choice% + PossibleChoice%
                  ELSEIF PossibleChoice% = 0 THEN
                  	ARRAY SCAN ArchiveInfo$(1) FOR Choice% - 1 ,FROM 2 TO 2,=Ky$,To PossibleChoice%
                        IF PossibleChoice% > 0 THEN Choice% = PossibleChoice%
                  END IF
                  IF PossibleChoice% > 0 THEN
	                  LowFile% = Choice% - DisplayPos% + 1
	                  HiFile% = LowFile% + BottomDisplayLine% -1
	                  IF LowFile% < 1 THEN LowFile% = 1:HiFile% = BottomDisplayLine%:DisplayPos% = Choice%
	                  IF HiFile% > TotalFiles% THEN HiFile% = TotalFiles%
			END IF
        	END IF
        CASE ELSE
            	EXIT DO
      END SELECT
   LOOP

   COLOR 7,0
END FUNCTION


SUB Scroll(BYVAL TopRow%,BYVAL TopLeftCol%,BYVAL BottomRow%,BYVAL BottomRightCol%,BYVAL TotalLines%,BYVAL Attr%,BYVAL WhichWay%)
!       PUSH DS
!       PUSH BP
!       MOV AH,WhichWay%
!       MOV AL, TotalLines%
!       MOV BH, Attr%
!       MOV CH, TopRow%
!       MOV CL, TopLeftCol%
!       MOV DH, BottomRow%
!       MOV DL, BottomRightCol%
!       INT &H10
!       POP BP
!       POP DS
END SUB

SUB QPrint(BYVAL Row, BYVAL Col, BYVAL Text$, BYVAL attr) PUBLIC
ScreenSeg??  = ScreenSegment??

   ! push DS                 ; save DS for PowerBASIC
   ! mov             ax,Row%         ; get the row
   ! dec             al              ; convert row to zero base
   ! mov             bl,80           ; get the number of cols
   ! mul             bl              ; multiply # of cols * rows
   ! add             ax,Col%         ; add the column
   ! dec             al              ; convert column to zero base
   ! mov             di,ax           ; offset within video page
   ! shl             di,1            ; account for color/attribute

   ! push WORD PTR Text$     ; push string handle on the stack
   ! call GetStrLoc          ; find the string
   ! jcxz QPExit             ; if it's Null, exit
   ! mov  DS, DX             ; put segment in DS
   ! mov  SI, AX             ; put offset in SI
   ! mov  ES, ScreenSeg??    ; move to ES
   ! mov  AH, attr           ; put attribute in AH
   WriteChar:
   ! lodsb                   ; get Char from string
   ! stosw                   ; write Char and attribute to screen
   ! loop WriteChar          ; do it all CX times
   QPExit:
   ! pop  DS                 ; restore DS for PowerBASIC
END SUB

SUB PrintAtr(byval Row%, byval Col%,ByVal LineWidth%, byval Attr%)
ScreenSeg??  = ScreenSegment??
!           push    ds              ;save the good stuff
!           mov     ax,Row%         ; get the row
!           dec     al              ; convert row to zero base
!           mov     bl,80           ; get the number of cols
!           mul     bl              ; multiply # of cols * rows
!           add     ax,Col%         ; add the column
!           dec     al              ; convert column to zero base
!           mov     di,ax           ; offset within video page
!           shl     di,1            ; account for color/attribute
!           mov     es,ScreenSeg??  ;put video segment into ax
!           mov     ah,Attr%        ;the color attribute will be needed
!           mov     cx,LineWidth%
forloop:
!           inc     di              ;increment to the next vidram position
!           mov     es:[di],ah      ;poke the attribute into vidram
!           inc     di
!           loop    forloop         ;loop until cx = 0
!           pop     ds
END SUB

FUNCTION GetKey%() Public
While ISFALSE INSTAT:Wend
ky$ = Inkey$
IF len(ky$) = 2 then
	GetKey% = -ascii(right$(ky$,1))
ELSE
	GetKey% = ascii(ky$)
END IF
END FUNCTION

SUB ClearBuffer
DO:LOOP UNTIL INKEY$ = ""
END SUB

SUB GetDirAndFiles(Spec$, Attr%, Directory$(), DirInfo$(), FilesInfo$, TotalFiles%)
 IF Attr% <> %SubDir THEN Attr% = 33
 DIM FileName AS STRING * 8
 DIM Ext AS STRING * 4
 DirFile$ = DIR$(Spec$,Attr%)
 IF DirFile$ <> "" THEN
	GetDirInfo DirDate$,DirTime$,DirSize???,DirAttr?
      IF Attr% = 16 AND DirAttr? = 16 THEN
		INCR TotalFiles%
		Directory$(TotalFiles%) = DirFile$
	      LSET FileName$ = Extract$(DirFile$,".")
	      LSET Ext$ = Extension$(DirFile$)
      	IsDir$ = "<DIR>"
      	DirInfo$(TotalFiles%)  =  " " + FileName$ + Ext$ + "  " + IsDir$ + "        " + "  "+DirDate$ + "  " + DirTime$
      ELSEIF Attr% <> 16 AND DirAttr? <> 16 THEN
		INCR TotalFiles%
		Directory$(TotalFiles%) = DirFile$
	      LSET FileName$ = Extract$(DirFile$,".")
	      LSET Ext$ = Extension$(DirFile$)
      	IsDir$ = "     "
      	DirInfo$(TotalFiles%)  =  " " + FileName$ + Ext$ + "  " + IsDir$ + USING$("########",DirSize???) + "  "+DirDate$ + "  " + DirTime$
      END IF
 END IF

 DO
	DirFile$ = DIR$
	IF DirFile$ <> "" THEN
      	GetDirInfo DirDate$,DirTime$,DirSize???,DirAttr?
            IF Attr% = 16 AND DirAttr? = 16 THEN
            	INCR TotalFiles%
            	Directory$(TotalFiles%) = DirFile$
            	LSET FileName$ = Extract$(DirFile$,".")
            	LSET Ext$ = Extension$(DirFile$)
            	IsDir$ = "<DIR>"
            	DirInfo$(TotalFiles%)  =  " " + FileName$ + Ext$ + "  " + IsDir$ + "        " + "  "+DirDate$ + "  " + DirTime$
	      ELSEIF Attr% <> 16 AND DirAttr? <> 16 THEN
      		INCR TotalFiles%
			Directory$(TotalFiles%) = DirFile$
	      	LSET FileName$ = Extract$(DirFile$,".")
	      	LSET Ext$ = Extension$(DirFile$)
      		IsDir$ = "     "
      		DirInfo$(TotalFiles%)  =  " " + FileName$ + Ext$ + "  " + IsDir$ + USING$("########",DirSize???) + "  "+DirDate$ + "  " + DirTime$
      	END IF
	ELSE
		EXIT LOOP
	END IF
 LOOP
 FilesInfo$ =  Spec$ + ":" + STR$(TotalFiles%)+" Directories And Files"
 DirInfo$(0)   = MKWRD$(TotalFiles%)
 Directory$(0) = MKWRD$(TotalFiles%)

END SUB

SUB GetRegDir(Spec$,Attr%,Directory$(),TotalFiles%)
DirFile$ = DIR$(Spec$,Attr%)
IF DirFile$ <> "" THEN
	INCR FileNum%
	Directory$(FileNum%) = DirFile$

ELSE
      TotalFiles% = 0
	EXIT SUB
END IF

DO
	DirFile$ = DIR$
	IF DirFile$ <> "" THEN
      	INCR FileNum%
      	Directory$(FileNum%) = DirFile$
	ELSE
		EXIT SUB
	END IF
LOOP
END SUB

FUNCTION Extension$(File$)
      FOR I% = LEN(File$) TO 1 STEP -1
		Period$ = MID$(File$,I%,1)
            IF Period$ = "." THEN Extension$ = MID$(File$,I%):EXIT FUNCTION
	NEXT I%
END FUNCTION

FUNCTION Trim$(S$)
Trim$ = Ltrim$(Rtrim$(S$))
END FUNCTION

FUNCTION GetFileName$(F$)
FOR I% = LEN(F$) TO 1 STEP -1
	Char$ = MID$(F$,I%,1)
	IF Char$ = "\" OR CHAR$ = "/" THEN
      	GetFileName$ = File$
            EXIT FUNCTION
	ELSE
      	File$ = Char$ + File$
      END IF
NEXT I%
GetFileName$ = File$
END FUNCTION

FUNCTION GetDirectory$(D$)
Ln% = LEN(D$)
FOR I% = Ln% TO 1 STEP -1
      Char$ = MID$(D$,I%,1)
	IF Char$ = "\" OR CHAR$ = "/" THEN EXIT FOR
NEXT I%
IF I% <> 3 THEN
	TheDir$ = LEFT$(D$,I%-1)
ELSE
	TheDir$ = LEFT$(D$,I%)
END IF
GetDirectory$ = TheDir$
END FUNCTION

FUNCTION GetLastDir$(D$)
Ln% = LEN(D$)
Slash$ = RIGHT$(D$,1)
IF Slash$ = "\" OR Slash$ = "/" THEN DECR Ln%

FOR I% = Ln% TO 1 STEP -1
      Char$ = MID$(TheDir$,I%,1)
	IF Char$ = "\" OR CHAR$ = "/" THEN EXIT FOR
NEXT I%
IF I% <> 3 THEN
	TheDir$ = INDEX$(D$,I%,Ln%)
ELSE
	TheDir$ = LEFT$(D$,I%)
END IF
GetLastDir$ = TheDir$
END FUNCTION

FUNCTION INDEX$(M$,StartPos%,LastPos%)
INDEX$ = MID$(M$,StartPos%,LastPos% - StartPos% + 1)
END FUNCTION

FUNCTION GetDirAndSlash$(D$)
Ln% = LEN(D$)
FOR I% = Ln% TO 1 STEP -1
      Char$ = MID$(D$,I%,1)
	IF Char$ = "\" OR CHAR$ = "/" THEN EXIT FOR
NEXT I%
IF I% <> 3 THEN
	TheDir$ = LEFT$(D$,I%)
ELSE
	TheDir$ = LEFT$(D$,I%)
END IF
GetDirAndSlash$ = TheDir$
END FUNCTION

FUNCTION AddSlash$(D$)
TheDir$ = TrueName$(D$)
IF LEN(TheDir$) > 3 AND RIGHT$(TheDir$,1) <> "\" THEN
	AddSlash$ = TheDir$ + "\"
ELSE
	AddSlash$ = TheDir$
END IF
END FUNCTION

FUNCTION FileDirExist%(d$)
Ln% = LEN(D$)
FOR I% = Ln% TO 1 STEP -1
      Char$ = MID$(D$,I%,1)
	IF Char$ = "\" OR CHAR$ = "/" THEN EXIT FOR
NEXT I%
FileDirExist% = I%
END FUNCTION

SUB PushDir(Directory$)
IF Directory$ = "" THEN EXIT SUB
INCR StackPos%
DirStack$(StackPos%) = Directory$
IF  DirExist%(TrueDir$) = 0 THEN MKDIR TrueDir$
END SUB

SUB PopDir()
DECR StackPos%
END SUB

FUNCTION TrueDir$()
D$ = DirStack$(1)
FOR I% = 2 to StackPos%
IF RIGHT$(DirStack$(I% -1),1) <> "\" THEN
	D$ = D$ + "\" + DirStack$(I%)
ELSE
	D$ = D$ + DirStack$(I%)
END IF
NEXT I%
TrueDir$ = D$
END FUNCTION

SUB PushDir2(Directory$,Stack$(),StackPos2%) PUBLIC
IF Directory$ = "" THEN EXIT SUB
INCR StackPos2%
Stack$(StackPos2%) = Directory$
END SUB

SUB PopDir2(StackPos2%) PUBLIC
DECR StackPos2%
END SUB

FUNCTION TrueDir2$(Stack$(), StackPos2%) PUBLIC
D$ = Stack$(1)
FOR I% = 2 to StackPos2%
IF RIGHT$(Stack$(I% -1),1) <> "\" THEN
	D$ = D$ + "\" + Stack$(I%)
ELSE
	D$ = D$ + Stack$(I%)
END IF
NEXT I%
TrueDir2$ = D$
END FUNCTION

FUNCTION DirExist%(Directory$)
DirName$ = DIR$(Directory$,16)
IF DirName$ = "" THEN EXIT FUNCTION
Exists% = ATTRIB(Directory$)
Exists% = BIT(Exists%,4)
IF Exists% THEN DirExist% = 1
END FUNCTION

FUNCTION FileExist%(Directory$)
D$ = DIR$(Directory$)
IF D$ <> "" THEN FileExist% = 1
END FUNCTION

FUNCTION TrueName$(Directory$)
DIM DirName2 AS STRING * 128
DirName$ = Directory$ +CHR$(0)
DirNamePtr??? = STRPTR32(DirName$)
DirNamePtr2??? = VARPTR32(DirName2$)
DosError?? = 0

!PUSH DS
!LDS SI,DirNamePtr???
!LES DI,DirNamePtr2???
!MOV AH,&H60
!INT &H21
!POP DS

!JNC NoError
!MOV DosError??,AX
NoError:

TrueName$ = EXTRACT$(DirName2$,CHR$(0))
END FUNCTION

FUNCTION CURDRIVE$
Curdrive$ = LEFT$(CURDIR$,1)
END FUNCTION

FUNCTION MODTYPE??(Number%,ModNumber%)
 M% = Number% MOD ModNumber%
 IF M% = 0 THEN
 	ModType% = ModNumber%
 ELSE
 	ModType% = M%
 END IF
END FUNCTION

SUB LzhView(LzhFile$,ArchiveFiles$(),ArchiveInfo$(), FilesInfo$, Success%)
 DIM Lzh AS LZHHead
 DIM FileName AS STRING * 8
 DIM Ext AS STRING * 4

  FilePos??? = 0: QUIT% = 0

  Handle% = FREEFILE

  OPEN LzhFile$ FOR BINARY AS Handle%
  RestOfFileSize??? = LOF(Handle%)
  IF RestOfFileSize??? = 0 THEN
    CLOSE Handle%
    PRINT LzhFile$+" has an FileSize of Zero !"
    Success% = 0
    EXIT SUB
  END IF


  DO
          GET  Handle%,FilePos???,Lzh
          IF Lzh.Id1 = "-lh" Or Lzh.Id1 = "-lz" Or Lzh.Id1 = "-lZ" THEN
            IF Lzh.HeadSize AND (Lzh.HeadSize < RestOfFileSize???) THEN
                Get Handle%,FilePos??? + Lzh.HeadSize,DirLen??
                SEEK Handle%,FilePos??? + Lzh.HeadSize+3

                IF DirLen?? > 0 AND DirLen?? < 256 THEN
                              GET$ Handle%,DirLen??-3,Directory$
                              REPLACE  CHR$(255) WITH "\" IN Directory$
                ELSE
                              Directory$ = ""
                END IF

                INCR FilePos???, Lzh.CompressSize + Lzh.HeadSize + 2
                DECR RestOfFileSize???, FilePos???

                INCR FileNumber??

                File$ = LEFT$(Lzh.FileName,Lzh.FileNameLen)
                LSET FileName$ = Extract$(File$,".")
                LSET Ext$ = Extension$(File$)
                GetDateAndTime Lzh.Filedate,Lzh.FileTime,Filedate$,FileTime$
                IF DirLen?? THEN
                	DirSign$ = " + "
                ELSE
                	DirSign$ = "   "
                END IF
                ArchiveFiles$(FileNumber??) = Directory$ + File$
                ArchiveInfo$(FileNumber??)  = DirSign$ + FileName$ + Ext$ + USING$("########",Lzh.UnCompressSize) + " " + Filedate$ + "  "+FileTime$
            END IF
          ELSE
                INCR FilePos???, 2
           	    DECR RestOfFileSize???, FilePos???
          END IF

  Loop UNTIL Lzh.HeadSize = 0 Or Lzh.HeadSize > RestOfFileSize??? OR FilePos??? = RestOfFileSize???

  Close Handle%
  FilesInfo$ =  LzhFile$ + ":" + STR$(FileNumber??)+" Files"
  ArchiveFiles$(0) = MKWRD$(FileNumber??)
  ArchiveInfo$(0)  = MKWRD$(FileNumber??)
  IF FileNumber?? > 1 THEN ARRAY SORT ArchiveFiles$(1) FOR FileNumber??,TAGARRAY ArchiveInfo$()
  Success% = 1
END SUB

SUB ZipView(ZipFile$,ArchiveFiles$(),ArchiveInfo$(),FilesInfo$,Success%)  ' View the ZIP File
 DIM Zip AS ZipType
 DIM FileName AS STRING * 8
 DIM Ext AS STRING * 4
 FileNumber?? = 0

  Handle% = FREEFILE
  OPEN ZipFile$ FOR BINARY AS Handle%
  FileSize??? = LOF(Handle%)
  IF FileSize??? = 0 THEN               ' Check if files exists
    ?ZipFile;" has an FileSize of Zeor !"    ' if not, exit
    CLOSE Handle%
    Success% = 0
    EXIT SUB
  END IF

    GET$ Handle%,2,PK$                   ' Read File Header
    IF PK$ = "PK" THEN
    	FilePos??? = 0
    ELSE
    	SEEK Handle%,2
      GET$ Handle%,2,PK$                   ' Read File Header
	IF PK$ = "PK" THEN
      	FilePos??? = 2
	ELSE
		Success% = 0
            EXIT SUB
      END IF
    END IF

  DO
    GET Handle%,FilePos???,Zip                   ' Read File Header

    IF (Zip.Signature = %Sig) THEN
      INCR FilePos???, Zip.CompressSize + 30 + Zip.FileNameLen + ZIP.ExtraField
      INCR FileNumber??
	GET$ Handle%,Zip.FileNameLen,File$
      TheFile$ = GetFileName$(File$)
      IF TheFile$ = "" THEN
      	DECR FileNumber??
            ITERATE DO
      END IF

      LSET FileName$ = Extract$(TheFile$,".")
      LSET Ext$ = Extension$(File$)
      GetDateAndTime Zip.Filedate,Zip.FileTime,Filedate$,FileTime$
      ArchiveFiles$(FileNumber??) = File$

	IF FileDirExist%(File$) THEN
		DirSign$ = " + "
	ELSE
		DirSign$ = "   "
	END IF

      ArchiveInfo$(FileNumber??)  = DirSign$ + FileName$ + Ext$ + USING$("########",Zip.UnCompressSize) + "  "+Filedate$+"  "+FileTime$
  END IF

  LOOP WHILE Zip.Signature = %Sig
  CLOSE Handle%

  FilesInfo$ =  ZipFile$ + ":" + STR$(FileNumber??)+" Files"
  ArchiveInfo$(0) = MKWRD$(FileNumber??) 'Total Number of FIles
  ArchiveFiles$(0) = MKWRD$(FileNumber??)
  IF FileNumber?? > 1 THEN ARRAY SORT ArchiveFiles$(1) FOR FileNumber??,TAGARRAY ArchiveInfo$()
  Success% = 1

END SUB

SUB GetDirInfo(DirDate$,DirTime$,DirSize???,DirAttrib?) PUBLIC
  LOCAL DirDate??,DirTime??,DirAttr?,Dsize???
  ! mov  AX, &H2F00              ; function 2Fh, get DTA location
  ! int  &H21                    ; call DOS
  ! MOV  AX,ES:[BX + 24]
  ! MOV  DirDate??,AX
  ! MOV  AX,ES:[BX + 22]
  ! MOV  DirTime??,AX
  ! MOV  AX,ES:[BX +26]
  ! MOV  DSize???[0],AX
  ! MOV  AX,ES:[BX +28]
  ! MOV  DSize???[2],AX
  ! MOV  AL,ES:[BX +21]
  ! MOV  DirAttr?,AL
  DirSize??? = DSize???
  DirAttrib? = DirAttr?
    GetDateAndTime DirDate??,DirTime??,DirDate$,DirTime$
END SUB

SUB GetDateAndTime(Filedate??,FileTime??,Filedate$,FileTime$) PUBLIC
       Year?? = (Filedate?? AND &B1111111000000000):SHIFT RIGHT Year??,9
       INCR Year??,1980
       Month?? = Filedate?? AND &B0000000111100000:SHIFT RIGHT Month??,5
       Day?? = Filedate?? AND &B0000000000011111
       Hours?? = FileTime?? AND &B1111100000000000:SHIFT RIGHT Hours??,11
       Minutes?? = FileTime?? AND &B0000011111100000:SHIFT RIGHT Minutes??,5
       Seconds?? = (FileTime?? AND &B0000000000011111) * 2

       Month$ = LTRIM$(STR$(Month??))
       Day$ = LTRIM$(STR$(Day??))
       IF LEN(Month$) = 1 THEN Month$="0"+Month$
       IF LEN(Day$) = 1 THEN Day$ = "0"+Day$
       'IF Hours?? > 12 THEN
       '	Hours?? = Hours?? -12:AmPm$ = "p & "
       'ELSE
       '	AmPm$ = "a & "
       'END IF
       Hours$ = LTRIM$(STR$(Hours??))
       Minutes$ = LTRIM$(STR$(Minutes??))
       Seconds$ = LTRIM$(STR$(Seconds??))
       IF LEN(Hours$) = 1 THEN Hours$="0"+Hours$
       IF LEN(Minutes$) = 1 THEN Minutes$="0"+Minutes$
       IF LEN(Seconds$) = 1 THEN Seconds$="0"+Seconds$

       Filedate$ = Month$ + "-" + Day$ + "-" + LTRIM$(STR$(Year??))
       FileTime$ = Hours$ + ":" + Minutes$ + ":" + Seconds$
       'FileTime$ = Hours$ + ":" + Minutes$ + AmPm$ + Seconds$ +"s"

END SUB

SUB Viewer(ViewFileName$)
DIM LinePt???(32700)

NextHandle% = FREEFILE
OPEN ViewFIleName$ FOR INPUT AS NextHandle%
DO
        INCR I???
        LinePt???(I???) = SEEK(NextHandle%)
        LINE INPUT #NextHandle%,Dummy$
LOOP UNTIL EOF(NextHandle%)
LinePt???(0) = I???
ViewFile LinePt???()
CLOSE NextHandle%
END SUB

SUB ViewFile(FileLines???())

   DIM PrintLine AS STRING * 80,Lines AS STRING * 1000
   SHARED LineSeg??,LinePtr??
   LineSeg?? = VARSEG(Lines$)
   LinePtr?? = VARPTR(Lines$)
   DisplayLine% = 23
   PrintLine$ = SPACE$(80)
   LowFile& = 1:HiFile& = 23
   TotalLines& = FileLines???(0)
   DisPlayPos% = 1
   HorzPos% = 1
   Choice% = 1
   IF TotalLines& < DisplayLine% THEN HiFile& = TotalLines&

     BottomDisplayLine% = DisplayLine%
     COLOR 7,1
     CLS
     COLOR 0,7
     LOCATE 1,1:PRINT SPACE$(80);
     LOCATE 25,1:PRINT SPACE$(80);
     COLOR 7,1


   DO
      COLOR 15, 1
      LinePos% = 0
      IF TotalLines& < 23 THEN BottomDisplayLine% = TotalLines&
      IF ScrollIt% = 0 THEN
      	WAIT &H3DA,8
              FOR ViewLines??? = LowFile& TO HiFile&
	         INCR LinePos%
               SEEK NextHandle%,FileLines???(ViewLines???)
               Line Input #NextHandle%, LineInput$
               REPLACE CHR$(9) WITH "      " IN LineInput$
               Lines$ = LineInput$
               IF LinePos% = DisplayPos% THEN
                        QuickPrintLine 1 + LinePos%,HorzPos%,71
                        IF Find% THEN PrintAtr 1 + LinePos%, Find%-HorzPos% + 1,SearchLen%, 48
	         ELSE
                        QuickPrintLine 1 + LinePos%, HorzPos%,23
	         END IF
              NEXT ViewLines???
            FOR I% = LinePos% + 1 To DisplayLine%
	            QPRINT 1 + I%, 1 ,PrintLine$,23
            NEXT I%
      ELSE
               SEEK NextHandle%,FileLines???(Choice&)
               Line Input #NextHandle%, LineInput$
               REPLACE CHR$(9) WITH "      " IN LineInput$
               Lines$ = LineInput$
               QuickPrintLine 1 + DisplayPos%, HorzPos% ,71
      END IF
      COLOR 0,7
      LOCATE 1,1
      PRINT "ViewFile";
      LOCATE 1,10
      PRINT ;Choice&;
      LOCATE 1,18
      PRINT ;TotalLines&;
      LOCATE 1,27:PRINT HorzPos%;
      LOCATE 1,34:PRINT "1000";
      COLOR 7,1
      ScrollIt% = 0
      ClearBuffer
      FindLine% = 0:Find% = 0:SearchLen% = 0
      DO: ky$ = UCASE$(INKEY$): LOOP WHILE ky$ = ""
      Ky% = ASC(Ky$)
      IF Ky%= 0 THEN Ky% = -ASC(Right$(ky$,1))
       SELECT CASE Ky%
         CASE %RightArrow
                IF HorzPos% < 1000 THEN INCR HorzPos%
         CASE %LeftArrow
                IF HorzPos% > 1 THEN DECR HorzPos%
         CASE %Escape
         	    EXIT DO
         CASE %Home
         	Choice& = 1:LowFile& = 1
            HiFile& = BottomDisplayLine%
            DisplayPos% = 1
         CASE %EndKey
            Choice& = TotalLines&
            DisplayPos% = ModType2??(Choice&, BottomDisplayLine%)
            LowFile& = Choice& - DisplayPos% + 1
            HiFile& = TotalLines&
         CASE %PageDown
            INCR Choice&, DisplayLine%
            INCR LowFile&,DisplayLine%
            HiFile& = LowFile& + BottomDisplayLine% -1
            IF HiFile& > TotalLines& THEN HiFile& = TotalLines&
		IF Choice& > TotalLines& THEN
			Choice& = TotalLines&
                        DisplayPos% = ModType2??(Choice&, BottomDisplayLine%)
			LowFile& = Choice& - DisplayPos% + 1
			HiFile& = TotalLines&
	      END IF

         CASE %PageUp
            DECR Choice&,DisplayLine%
            DECR LowFile&,DisplayLine%
            HiFile& = LowFile& + DisplayLine% -1
            IF LowFile& < 1 THEN LowFile& = 1:HiFile& = BottomDisplayLine%
	      IF Choice& <=1 THEN
              Choice& = 1
              LowFile& = 1
              HiFile& = BottomDisplayLine%
              DisplayPos% = 1
      	END IF
         CASE %DownArrow
            INCR Choice&
            IF Choice& <= TotalLines& THEN
                  INCR DisplayPos%
            ELSE
            	Choice& = TotalLines&
            END IF
	      IF DisplayPos% > BottomDisplayLine% THEN
                DisplayPos% = BottomDisplayLine%
                LowFile& = Choice& -BottomDisplayLine% + 1
                IF HiFile&  >= TotalLines& THEN
                   HiFile& = TotalLines&
                ELSE
                   HiFile& = LowFile& + BottomDisplayLine% -1
                END IF
                PrintAtr BottomDisplayLine%+1, 1, 80, 23
                Scroll 1, 0, BottomDisplayLine%, 79, 1, 23, %Up
                ScrollIt% = 1
      	END IF
         CASE %UpArrow
             DECR Choice&
             DECR DisplayPos%
             IF Choice& <=1 THEN
             	Choice& = 1
             	LowFile& = 1
             	HiFile& = BottomDisplayLine%
             	DisplayPos% = 1
             ELSE
	            IF DisplayPos% < 1 THEN
		             DisplayPos% = 1
		             LowFile& = Choice&
		             HiFile& = Choice& + BottomDisplayLine% -1
                         IF HiFile&  >= TotalLines& THEN
            		       HiFile& = TotalLines&
                         ELSE
            		       HiFile& = LowFile& + BottomDisplayLine% -1
                         END IF
                         PrintAtr 2, 1, 80, 23
                         Scroll 1,0, BottomDisplayLine%,79, 1,23,%Down
                         ScrollIt% = 1
                  END IF
             END IF
        CASE 70 'F
            LOCATE 25,1:COLOR 0,7
            PRINT Space$(50);
            LOCATE 25,1
            LINE INPUT; "FIND >";Search$
            COLOR 7,1
            Search$ = UCASE$(Search$)
            FOR I??? = Choice& + 1 TO TotalLines&
                  SEEK NextHandle%,FileLines???(I???)
                  Line Input #NextHandle%, LineInput$
                  REPLACE Chr$(9) WITH "      " IN LineInput$
                  Find% = BINSTR% (1,UCASE$(LineInput$),Search$)
                  IF Find% THEN EXIT FOR
            NEXT I???
            IF Find% = 0 THEN
                  FOR I??? =  1 TO Choice& -1
                          SEEK NextHandle%,FileLines???(I???)
                          Line Input #NextHandle%, LineInput$
                          REPLACE Chr$(9) WITH "      " IN LineInput$
                          Find% = BINSTR% (1,UCASE$(LineInput$),Search$)
                          IF Find% THEN EXIT FOR
                  NEXT I???
            END IF
            IF Find% THEN
			GOSUB ResetValues
            END IF
            EXIT SELECT
        CASE 65 'N
            FOR I??? = Choice& + 1 TO TotalLines&
                  SEEK NextHandle%,FileLines???(I???)
                  Line Input #NextHandle%, LineInput$
                  REPLACE Chr$(9) WITH "      " IN LineInput$
                  Find% = BINSTR% (1,UCASE$(LineInput$),Search$)
                  IF Find% THEN EXIT FOR
            NEXT I???
            IF Find% = 0 THEN
                  FOR I??? =  1 TO Choice& -1
                  	SEEK NextHandle%,FileLines???(I???)
	                  SEEK NextHandle%,FileLines???(I???)
	                  Line Input #NextHandle%, LineInput$
	                  REPLACE Chr$(9) WITH "      " IN LineInput$
	                  Find% = BINSTR% (1,UCASE$(LineInput$),Search$)
                        IF Find% THEN EXIT FOR
                  NEXT I???
            END IF
            IF Find% THEN
			GOSUB ResetValues
            END IF
            EXIT SELECT
        CASE ELSE
            	EXIT DO
      END SELECT
   LOOP

   COLOR 7,0
   EXIT SUB

ResetValues:
               SearchLen% = LEN(Search$)
		   Choice& = I???
               FindLine% = I???
               DisplayPos% = ModType2??(Choice&, BottomDisplayLine%)
               LowFile& = Choice& - DisplayPos% + 1
               HiFile& = LowFile& + BottomDisplayLine% -1
               IF LowFile& < 1 THEN LowFile& = 1:HiFile& = BottomDisplayLine%:DisplayPos% = Choice&
               IF HiFile& > TotalLines& THEN HiFile& = TotalLines&
               IF HorzPos% > Find% THEN HorzPos% = 1
               IF Find% > HorzPos% + 80 -1  THEN HorzPos% = Find% -80 + SearchLen% + 80\SearchLen% -1
RETURN
END SUB

FUNCTION ModType2??(Number&,ModNumber%)
 M?? = Number& MOD ModNumber%
 IF M?? = 0 THEN
 	ModType2?? = ModNumber%
 ELSE
 	ModType2?? = M??
 END IF
END FUNCTION

SUB QuickPrintLine(BYVAL Row , BYVAL HorzPos%,BYVAL attr) PUBLIC

   ! push DS                 ; save DS for PowerBASIC
   ! mov             ax,Row%         ; get the row
   ! dec             al              ; convert row to zero base
   ! mov             bl,80           ; get the number of cols
   ! mul             bl              ; multiply # of cols * rows
   ! mov             di,ax           ; offset within video page
   ! shl             di,1            ; account for color/attribute
   ! mov  CX,1000
   ! mov  BX, 80
   ! sub  CX, HorzPos%
   ! CMP  CX, 80
   ! Jg   GoAhead
   ! SUB BX, CX
   ! JMP DoIt
   GoAhead:
   ! MOV CX,80
   ! MOV BX,0
   DoIt:
   ! MOV  SI, LinePtr??      ; put segment in DS
   ! MOV  DS, LineSeg??
   ! add  SI, HorzPos%
   ! DEC  SI
   ! mov  ES, ScreenSegment??    ; move to ES
   ! mov  AH, attr           ; put attribute in AH
   ! jcxz RestOfLine         ; if it's Null, exit
   WriteChar2:
   ! lodsb                   ; get Char from string
   ! stosw                   ; write Char and attribute to screen
   ! loop WriteChar2         ; do it all CX times
   RestOfLine:
    ! MOV  AH, attr          ; put attribute in AH
    ! MOV AL,32
    ! MOV CX,BX
    ! jcxz Quit3              ; if it's Null, Do Rest of Line
    ! REP stosw
    Quit3:
    ! pop  DS                ; restore DS for PowerBASIC
END SUB


%SMALLEST.PATTERN = 2
%SMALLEST.BUFFER  = 4

FUNCTION BINSTR%(TheStart%, Target$, Rule$) PUBLIC

    ' ES:DI Target$
    ' DS:SI Rule$

    PatLen%      = LEN(Rule$)
    BuffLen%     = LEN(Target$)

    IF BuffLen% < %SMALLEST.BUFFER THEN
        FUNCTION = INSTR(TheStart%, Target$, Rule$)
        EXIT FUNCTION
    ELSE
        IF PatLen% < %SMALLEST.PATTERN THEN
            FUNCTION = INSTR(TheStart%, Target$, Rule$)
            EXIT FUNCTION
        END IF
    END IF

    IF TheStart% > BuffLen% THEN      ' we might as well not even bother
        EXIT FUNCTION
    END IF

    StackSeg??    = 0                 ' We do this for later inline ASM

    PatSeg??      = STRSEG(Rule$)
    PatPtr??      = STRPTR(Rule$)
    BuffSeg??     = STRSEG(Target$)
    BuffPtr??     = STRPTR(Target$) + TheStart%
    TrueBuffPtr?? = BuffPtr??

    !   cld
    !   push    sp
    !   push    si             ;preserve caller's register variables
    !   push    di
    !   push    es
    !   push    ds
    !   sub     sp, 256*2      ;allocate space for SkipTable on our
    !                          ; stack


    !   mov     ax,PatLen%
    !   and     ax,ax          ;return an instant match if the
    !   jz      InstantMatch   ; pattern 0-length

    !   mov     StackSeg??,ss
    !   mov     es,StackSeg??
    !   mov     di,sp          ;point to SkipBuffer

    !   mov     cx,256
    !   rep     stosw

    !   mov     es,BuffSeg??
    !   mov     ds,PatSeg??

    !   dec     ax             ;from now on, we only need
    !   mov     PatLen%,ax      ; PatLen% - 1

    '   Point to last (rightmost) byte of first potential pattern match
    '   location in buffer.

    !   add     BuffPtr??, ax


    !   sub     BuffLen%,ax
    !   jbe     NoMatch


    !   mov     si,PatPtr??      ;point to start of pattern
    !   and     ax,ax          ;are there any skips to set?
    !   jz      SetSkipDone    ;no
    !   mov     di,sp          ;point to SkipBuffer

SetSkipLoop:
    !   sub     bx,bx          ;prepare for word addressing off byte value
    !   mov     bl,ds:[si]     ;get the next pattern byte
    !   inc     si             ;advance the pattern pointer
    !   shl     bx,1           ;prepare for word look-up
    !   mov     ss:[di+bx],ax  ;set the skip value when this byte value
    !                          ;is the mismatch value in the buffer
    !   dec     ax
    !   jnz     SetSkipLoop

SetSkipDone:
    !   mov     dl,ds:[si]     ;DL=rightmost pattern byte from now on
    !   dec     si             ;point to next-to-rightmost byte of pattern
    !   mov     PatPtr??,si      ; from now on

    '   Search the buffer.

    !   std                    ;for backward REPZ CMPSB
    !   mov     di,BuffPtr??     ;point to the first search location
    !   mov     cx,BuffLen%     ;# of match locations to check

SearchLoop:
    !   mov     si,sp          ;point SI to SkipTable

    '   Skip through until there's a match for the rightmost pattern
    '   byte.

    '   We jump this way to avoid a FIXUP OVERFLOW under PB... <sigh>
    !   jmp     QuickSearchLoop

InstantMatch:
    !   mov     ax,BuffPtr??
    !   jmp     short Done

    '   Compare the pattern and the buffer location, searching from high
    '   memory toward low (right to left).

FullCompare:
    !   mov     BuffPtr??,di     ;save the current state of
    !   mov     BuffLen%,cx     ; the search
    !   mov     cx,PatLen%      ;# of bytes yet to compare
    !   jcxz    Matched        ;done if there was only one character
    !   mov     si,PatPtr??      ;point to next-to-rightmost bytes
    !   dec     di             ; of buffer location and pattern

    !   repz    cmpsb          ;compare the rest of the pattern

    !   jz      Matched        ;that's it; we've found a match

    '   It's a mismatch; let's see what we can learn from it.

    !   inc     di             ;compensate for 1-byte overrun of REPZ CMPSB;
    !                          ; point to mismatch location in buffer
    !                          ; # of bytes that did match.
    !   mov     si,BuffPtr??
    !   sub     si,di


    !   sub     bx,bx          ;prepare for word addressing off byte value
    !   mov     bl,es:[di]     ;get the value of the mismatch byte in buffer
    !   add     bx,bx          ;prepare for word look-up
    !   add     bx,sp          ;SP points to SkipTable
    !   mov     cx,ss:[bx]     ;get the skip value for this mismatch
    !   mov     ax,1           ;assume we'll just advance to the next
    !                          ; potential match location
    !   sub     cx,si          ;is the skip far enough to be worth taking?
    !   jna     MoveAhead      ;no, go with the default advance of 1
    !   mov     ax,cx          ;yes; this is the distance to skip ahead from
    !                          ; the last potential match location checked

MoveAhead:
    '   Skip ahead and perform the next comparison, if there's any buffer
    '   left to check.

    !   mov     di,BuffPtr??
    !   add     di,ax          ;BuffPtr?? += Skip
    !   mov     cx,BuffLen%
    !   sub     cx,ax          ;BuffLen% -= Skip;
    !   ja      SearchLoop     ;continue if any buffer left

    '   Return a NULL pointer for no match.

NoMatch:
    !   sub     ax,ax
    !   jmp     short Done

    '   Return start of match in buffer (BuffPtr?? - (PatLen% - 1)).

Matched:
    !   mov ax, BuffPtr??
    !   sub ax, TrueBuffPtr??
    !   sub ax, PatLen%

    !   les bx, [bp+&HE]
    !   add ax, es:[bx]         ; Parameter: TheStart%

    !   inc ax
    !   mov FUNCTION, ax

Done:
    !   cld
    !   add     sp,256*2       ;deallocate space for SkipTable
    !   pop     ds
    !   pop     es
    !   pop     di             ;restore caller's register variables
    !   pop     si
    !   pop     sp

EXIT FUNCTION

QuickSearchLoop:
    !   mov     bl,es:[di]     ;rightmost buffer byte at this location
    !   cmp     dl,bl          ;does it match the rightmost pattern byte?
    !   jz      FullCompare    ;yes, so keep going
    !   sub     bh,bh          ;convert to a word
    !   add     bx,bx          ;prepare for look-up in SkipTable

    !   mov     ax,ss:[si+bx]  ;get skip value from skip table for this
    !                          ; mismatch value

    !   add     di,ax          ;BuffPtr?? += Skip;
    !   sub     cx,ax          ;BuffLen% -= Skip;
    !   ja      QuickSearchLoop;continue if any buffer left
    !   jmp     short NoMatch

    '   Return a pointer to the start of the buffer (for 0-length
    '   pattern).

END FUNCTION
