'===========================================================================
' Subject: TEXT COMPILER FOR ASCII TEXTS      Date: 03-29-99 (03:44)       
'  Author: Andrew S. Gibson                   Code: QB, PDS                
'  Origin: gibsons3@ix.netcom.com           Packet: TEXT.ABC
'===========================================================================
'---------------------------------------------------------------------------
' TEXE.BAS for Power Basic 3.2 ported to QuickBASIC by Zapf DingBat (Ver 0.3)
' Text compiler for ASCII texts
' Freeware (c) 1997 by Dieter Folger
' NOTE: Do not use in IDE - compile first!
'---------------------------------------------------------------------------
' Dieter's words:
'
' If you want to convert an ASCII text file, e.g. a readme file, to an
' executable program, i.e. a self displaying EXE file, you need a text
' compiler. Then the user can read this file without the help of an
' ASCII viewer or an editor. TEXE is such a compiler. It converts any
' text file up to 10000 lines.

' How TEXE works: When TEXE is started the file length is compared with
' the predefined value of FileEnd&. If they match, then there is no
' text file included yet and the user is prompted for two file names:
' the name of the text file and the compiled file name.
' This EXE file is written via SHELL using the DOS COPY command.

' When the EXE file is started, it reads its own file name (ProgName$
' is a very useful function!), opens this file and reads the text into
' an array. Variable FileEnd& tells the program the position from where
' to start reading (= length of EXE without text). You have to adjust
' this value if you make any changes to the code. Otherwise the program
' wouldn't work correctly. Then the text is shown and can be scrolled
' down, left, up and right. The Esc key gets the user back to the command
' line.
'
' Zapf's Words:
'
' This port to QuickBasic functions like the PB version, I have added
' the extra code to redraw both info line because some text files destroyed
' the display. You can save the text file to disk only once in a directory.
' To avoid having to change the FileEnd& variable (if you're
' using QuickBASIC) compile the program like this: BC TEXE/O/AH;
'
' and Link it like this (of course you'll probably need to change
' some stuff):
'
' C:\ABCRX>link TEXE/FAR/PACKC/EX
'
' Microsoft (R) Overlay Linker  Version 3.69
' Copyright (C) Microsoft Corp 1983-1988.  All rights reserved.
'
' RUN File(TEXE.EXE):
' List File [NUL.MAP]:
' Libraries [.LIB]: c:\QB45\BCOM45+
' Libraries [.LIB]: c:\QB45\QB
'
' I forgot Whom coded the Dir$ function, Ethan Winer did the ExeName$
' function (same as the Progname function), and I coded the EXTRACT$
' function. (SFX means Source For eXtraction). Remember you *can't*
' compress your resulting file, it won't work !!! You can save the
' document to disk, only once in a directory.
' Email me Zapf_DingBat@JUNO.COM or Zapf_DingBat@DepecheMode.COM
'---------------------------------------------------------------------------
'$DYNAMIC
DEFINT A-Z
DECLARE FUNCTION Dir$ (FileSpec$)
DECLARE FUNCTION ExeName$ ()
DECLARE FUNCTION Exist (Name$)
DECLARE FUNCTION EXTRACT$ (SFX$, Char$)

TYPE RegType
  AX    AS INTEGER
  BX    AS INTEGER
  CX    AS INTEGER
  DX    AS INTEGER
  BP    AS INTEGER
  SI    AS INTEGER
  DI    AS INTEGER
  flags AS INTEGER
  DS    AS INTEGER
  ES    AS INTEGER
END TYPE

CONST FALSE = 0: CONST TRUE = NOT FALSE, SSpc$ = " "
CONST DOS = &H21, SetDTA = &H1A00, FindFirst = &H4E00, FindNext = &H4F00
DIM SHARED Registers AS RegType

' You have to change FileEnd& if you make any changes in source:
FileEnd& = 46692              ' length of EXE file without text

Program$ = ExeName$         ' program reads its own name
'PRINT Program$
OPEN Program$ FOR INPUT AS #1 ' open this EXE file
'---------------------------------------------------------------------------
' This part writes the self displaying EXE file
'---------------------------------------------------------------------------
IF LOF(1) = FileEnd& THEN     ' EXE is without text
   INPUT "Textfile to compile: ", TextFile$
   IF Dir$(TextFile$) = "" THEN
      IF LEN(TextFile$) = 0 THEN
         PRINT "What you don't want to make a file ?"
      ELSE
         PRINT TextFile$; " not found."
      END IF
      END
   END IF
   INPUT "Name of EXE file: ", ExeFile$
   IF ExeFile$ = "" THEN END
   ' Force an *.EXE filename:
   IF INSTR(ExeFile$, ".") = 0 THEN ExeFile$ = ExeFile$ + ".EXE"
   IF INSTR(ExeFile$, ".EXE") = 0 THEN ExeFile$ = EXTRACT$(ExeFile$, ".") + ".EXE"
   'This does it:
   SHELL "COPY /B " + Program$ + " + " + TextFile$ + SSpc$ + ExeFile$ + " > nul"
   PRINT UCASE$(ExeFile$); " written"
   END
END IF
'---------------------------------------------------------------------------
' This part displays the text
'---------------------------------------------------------------------------
Extracted = FALSE 'Whether text has been extracted already, default NO
DocumentName$ = EXTRACT$(Program$, ".") + ".TXT" 'make document name

SEEK #1, FileEnd&             ' go to start of text
DO                            ' get number of text lines
  IF EOF(1) THEN EXIT DO
  LINE INPUT #1, Dummy$
  Lin = Lin + 1
LOOP UNTIL EOF(1)
IF Lin > 10000 THEN Lin = 10000 ' max lines, I don't know what the limit is.

' Bob Perkins' code really clever, works in IDE under other
' circumstances (other viewers, such as NeatView seen it before ?!)
' dim array for text lines
maxmem& = FRE(-1) - 65535
maxlines% = CINT(maxmem& \ 128)
REDIM L(1 TO maxlines%) AS STRING * 128

SEEK #1, FileEnd&             ' go to start of text again
FOR i = 1 TO Lin
    LINE INPUT #1, L(i)      ' read text lines in array
NEXT: CLOSE

'make screen and show text:
COLOR 14, 1
LOCATE 1, 1: PRINT SPACE$(80);
LOCATE 1, 2: PRINT Program$;
P = POS(0)
LOCATE 25, 1: PRINT SPACE$(80);
LOCATE 25, 2
PRINT "Action Keys:"; SSpc$; CHR$(25); SSpc$; CHR$(24); SSpc$; CHR$(27); SSpc$; CHR$(26); SSpc$; "Home End PageUp PageDown (S)ave ESCape";
Start = 1: Offset = 1
DO
    'make screen and show text:
    COLOR 14, 1
    LOCATE 1, 1: PRINT SPACE$(80);
    LOCATE 1, 2: PRINT Program$;
    P = POS(0)
    LOCATE 1, P: COLOR 14, 1
    PRINT " Line"; Start; "of"; Lin - 1; "       ";
    LOCATE 25, 1: PRINT SPACE$(80);
    LOCATE 25, 2
    PRINT "Action Keys:"; SSpc$; CHR$(25); SSpc$; CHR$(24); SSpc$; CHR$(27); SSpc$; CHR$(26); SSpc$; "Home End PageUp PageDown (S)ave ESCape";
    COLOR 0, 3
    FOR i = 2 TO 24
        LOCATE i, 1
        PRINT MID$(L(Start + i - 2) + SPACE$(80), Offset, 80);
    NEXT
    DO: K$ = INKEY$: LOOP UNTIL LEN(K$)
    SELECT CASE RIGHT$(K$, 1)
           CASE "P" 'Cursor down
                Start = Start + 1
           CASE "M" 'Cursor right
                Offset = Offset + 1
           CASE "K" 'Cursor left
                Offset = Offset - 1
           CASE "H" 'Cursor up
                Start = Start - 1
           CASE "G": 'Home
                Start = 1
           CASE "O": 'End
                Start = Lin - 22
           CASE "Q": 'PgDn
                Start = Start + 23
           CASE "I": 'PgUp
                Start = Start - 23
           CASE CHR$(83), CHR$(115) 'Save document to disk
                IF Exist(DocumentName$) = FALSE AND Extracted = FALSE THEN
                   OPEN Program$ FOR INPUT AS #1
                   OPEN DocumentName$ FOR OUTPUT AS #2
                    SEEK #1, FileEnd&             ' go to start of text again
                        FOR i = 1 TO Lin
                           LINE INPUT #1, XX$
                           PRINT #2, XX$
                        NEXT: CLOSE : XX$ = ""
                   Extracted = TRUE
                ELSE
                   OUT 97, INP(97) OR &H3: OUT 67, 182
                   OUT 66, 119: OUT 66, 2: FOR ZZZ! = 0 TO 400: NEXT ZZZ!
                   OUT 97, INP(97) AND &HFC
                END IF
           CASE CHR$(27): END 'Quit
           CASE ELSE
                OUT 97, INP(97) OR &H3: OUT 67, 182
                OUT 66, 41: OUT 66, 21: FOR ZZZ! = 0 TO 555: NEXT ZZZ!
                OUT 97, INP(97) AND &HFC
     END SELECT
     IF Start > Lin - 22 THEN Start = Lin - 22
     IF Start < 1 THEN Start = 1
     IF Offset < 1 THEN Offset = 1
LOOP
END
'==== eof ==================================================================

REM $STATIC
FUNCTION Dir$ (FileSpec$)
'
' Returns a file from current directory
'
DIM DTA AS STRING * 44
Null$ = CHR$(0)

'-----  Set up our own DTA so we don't destroy COMMAND$
Registers.AX = SetDTA                   'Set DTA function
Registers.DX = VARPTR(DTA)              'DS:DX points to our DTA
Registers.DS = -1                       'Use current value for DS
CALL InterruptX(DOS, Registers, Registers)          'Do the interrupt

'-----  Check to see if this is First or Next
IF LEN(FileSpec$) THEN             'FileSpec$ isn't null, so
                'FindFirst
  FileSpecZ$ = FileSpec$ + Null$   'Make FileSpec$ into an ASCIIZ
                'string
  Registers.AX = FindFirst              'Perform a FindFirst
  Registers.CX = 0                      'Only look for normal files
  Registers.DX = SADD(FileSpecZ$)       'DS:DX points to ASCIIZ file
  Registers.DS = -1                     'Use current DS
ELSE                               'We have a null FileSpec$,
  Registers.AX = FindNext               'so FindNext
END IF

CALL InterruptX(DOS, Registers, Registers)         'Do the interrupt

'-----  Return file name or null
IF Registers.flags AND 1 THEN           'No files found
  Dir$ = ""                        'Return null string
ELSE
  Null = INSTR(31, DTA, Null$)     'Get the filename found
  Dir$ = MID$(DTA, 31, Null - 30)  'It's an ASCIIZ string starting
END IF                              'at offset 30 of the DTA
END FUNCTION

FUNCTION ExeName$
  'Returns the name of the currently running program; requires DOS 3.0 +

  '---- DOS Interrupt &H21 service &H62 returns the PSP segment in BX
  Registers.AX = &H6200
  CALL InterruptX(DOS, Registers, Registers)

  '---- The environment segment is at address &H2C/&H2D in PSP segment
  DEF SEG = Registers.BX
  DEF SEG = PEEK(&H2C) + PEEK(&H2D) * 256

  '---- Search the environment segment for two zero bytes in a row.  A count
  '     word (which we skip over) follows that, and the program name follows
  '     the count word.
  Byte = 0
  DO
    IF PEEK(Byte) = 0 THEN              'this is zero
      IF PEEK(Byte + 1) = 0 THEN        'this is too
        Byte = Byte + 2                 'so skip both
        EXIT DO                         'all done
      END IF
    END IF                              'else,
    Byte = Byte + 1                     'keep looking
  LOOP
  IF PEEK(Byte) = 1 THEN                'if this count byte = 1
    Byte = Byte + 2                     'the name follows
    DO WHILE PEEK(Byte)                 'up to another zero
      Tmp$ = Tmp$ + CHR$(PEEK(Byte))    'this is a different Tmp$ on purpose
      Byte = Byte + 1
    LOOP
    ExeName$ = Tmp$                     'assign the function output
  END IF
END FUNCTION

FUNCTION Exist (Name$)
OPEN Name$ FOR BINARY AS #1
IF LOF(1) <> 0 THEN
   Exist = TRUE: CLOSE
ELSE
   Exist = FALSE: CLOSE
   KILL Name$
END IF
END FUNCTION

FUNCTION EXTRACT$ (SFX$, Char$)
'QB Version of PB's EXTRACT$
Temp$ = ""
 FOR X = 1 TO LEN(SFX$)
     IF MID$(SFX$, X, 1) = Char$ THEN EXIT FOR
     Temp$ = Temp$ + MID$(SFX$, X, 1)
 NEXT X
EXTRACT$ = Temp$
END FUNCTION
