You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
395 lines
12 KiB
395 lines
12 KiB
'
|
|
' Microsoft RemLine - Line Number Removal Utility
|
|
' Copyright (C) Microsoft Corporation 1985-1990
|
|
'
|
|
' REMLINE.BAS is a program to remove line numbers from Microsoft Basic
|
|
' Programs. It removes only those line numbers that are not the object
|
|
' of one of the following statements: GOSUB, RETURN, GOTO, THEN, ELSE,
|
|
' RESUME, RESTORE, or RUN.
|
|
'
|
|
' When REMLINE is run, it will ask for the name of the file to be
|
|
' processed and the name of the file or device to receive the
|
|
' reformatted output. If no extension is given, .BAS is assumed (except
|
|
' for output devices). If filenames are not given, REMLINE prompts for
|
|
' file names. If both filenames are the same, REMLINE saves the original
|
|
' file with the extension .BAK.
|
|
'
|
|
' REMLINE makes several assumptions about the program:
|
|
'
|
|
' 1. It must be correct syntactically, and must run in BASICA or
|
|
' GW-BASIC interpreter.
|
|
' 2. There is a 400 line limit. To process larger files, change
|
|
' MaxLines constant.
|
|
' 3. The first number encountered on a line is considered a line
|
|
' number; thus some continuation lines (in a compiler-specific
|
|
' construction) may not be handled correctly.
|
|
' 4. REMLINE can handle simple statements that test the ERL function
|
|
' using relational operators such as =, <, and >. For example,
|
|
' the following statement is handled correctly:
|
|
'
|
|
' IF ERL = 100 THEN END
|
|
'
|
|
' Line 100 is not removed from the source code. However, more
|
|
' complex expressions that contain the +, -, AND, OR, XOR, EQV,
|
|
' MOD, or IMP operators may not be handled correctly. For example,
|
|
' in the following statement REMLINE does not recognize line 105
|
|
' as a referenced line number and removes it from the source code:
|
|
'
|
|
' IF ERL + 5 = 105 THEN END
|
|
'
|
|
' If you do not like the way REMLINE formats its output, you can modify
|
|
' the output lines in SUB GenOutFile. An example is shown in comments.
|
|
DEFINT A-Z
|
|
|
|
' Function and Subprocedure declarations
|
|
DECLARE FUNCTION GetToken$ (Search$, Delim$)
|
|
DECLARE FUNCTION StrSpn% (InString$, Separator$)
|
|
DECLARE FUNCTION StrBrk% (InString$, Separator$)
|
|
DECLARE FUNCTION IsDigit% (Char$)
|
|
DECLARE SUB GetFileNames ()
|
|
DECLARE SUB BuildTable ()
|
|
DECLARE SUB GenOutFile ()
|
|
DECLARE SUB InitKeyTable ()
|
|
|
|
' Global and constant data
|
|
CONST TRUE = -1
|
|
CONST false = 0
|
|
CONST MaxLines = 400
|
|
|
|
DIM SHARED LineTable!(MaxLines)
|
|
DIM SHARED LineCount
|
|
DIM SHARED Seps$, InputFile$, OutputFile$, TmpFile$
|
|
|
|
' Keyword search data
|
|
CONST KeyWordCount = 9
|
|
DIM SHARED KeyWordTable$(KeyWordCount)
|
|
|
|
KeyData:
|
|
DATA THEN, ELSE, GOSUB, GOTO, RESUME, RETURN, RESTORE, RUN, ERL, ""
|
|
|
|
' Start of module-level program code
|
|
Seps$ = " ,:=<>()" + CHR$(9)
|
|
InitKeyTable
|
|
GetFileNames
|
|
ON ERROR GOTO FileErr1
|
|
OPEN InputFile$ FOR INPUT AS 1
|
|
ON ERROR GOTO 0
|
|
COLOR 7: PRINT "Working"; : COLOR 23: PRINT " . . .": COLOR 7: PRINT
|
|
BuildTable
|
|
CLOSE #1
|
|
OPEN InputFile$ FOR INPUT AS 1
|
|
ON ERROR GOTO FileErr2
|
|
OPEN OutputFile$ FOR OUTPUT AS 2
|
|
ON ERROR GOTO 0
|
|
GenOutFile
|
|
CLOSE #1, #2
|
|
IF OutputFile$ <> "CON" THEN CLS
|
|
|
|
END
|
|
|
|
FileErr1:
|
|
CLS
|
|
PRINT " Invalid file name": PRINT
|
|
INPUT " New input file name (ENTER to terminate): ", InputFile$
|
|
IF InputFile$ = "" THEN END
|
|
FileErr2:
|
|
INPUT " Output file name (ENTER to print to screen) :", OutputFile$
|
|
PRINT
|
|
IF (OutputFile$ = "") THEN OutputFile$ = "CON"
|
|
IF TmpFile$ = "" THEN
|
|
RESUME
|
|
ELSE
|
|
TmpFile$ = ""
|
|
RESUME NEXT
|
|
END IF
|
|
|
|
'
|
|
' BuildTable:
|
|
' Examines the entire text file looking for line numbers that are
|
|
' the object of GOTO, GOSUB, etc. As each is found, it is entered
|
|
' into a table of line numbers. The table is used during a second
|
|
' pass (see GenOutFile), when all line numbers not in the list
|
|
' are removed.
|
|
' Input:
|
|
' Uses globals KeyWordTable$, KeyWordCount, and Seps$
|
|
' Output:
|
|
' Modifies LineTable! and LineCount
|
|
'
|
|
SUB BuildTable STATIC
|
|
|
|
DO WHILE NOT EOF(1)
|
|
' Get line and first token
|
|
LINE INPUT #1, InLin$
|
|
Token$ = GetToken$(InLin$, Seps$)
|
|
DO WHILE (Token$ <> "")
|
|
FOR KeyIndex = 1 TO KeyWordCount
|
|
' See if token is keyword
|
|
IF (KeyWordTable$(KeyIndex) = UCASE$(Token$)) THEN
|
|
' Get possible line number after keyword
|
|
Token$ = GetToken$("", Seps$)
|
|
' Check each token to see if it is a line number
|
|
' (the LOOP is necessary for the multiple numbers
|
|
' of ON GOSUB or ON GOTO). A non-numeric token will
|
|
' terminate search.
|
|
DO WHILE (IsDigit(LEFT$(Token$, 1)))
|
|
LineCount = LineCount + 1
|
|
LineTable!(LineCount) = VAL(Token$)
|
|
Token$ = GetToken$("", Seps$)
|
|
IF Token$ <> "" THEN KeyIndex = 0
|
|
LOOP
|
|
END IF
|
|
NEXT KeyIndex
|
|
' Get next token
|
|
Token$ = GetToken$("", Seps$)
|
|
LOOP
|
|
LOOP
|
|
|
|
END SUB
|
|
|
|
'
|
|
' GenOutFile:
|
|
' Generates an output file with unreferenced line numbers removed.
|
|
' Input:
|
|
' Uses globals LineTable!, LineCount, and Seps$
|
|
' Output:
|
|
' Processed file
|
|
'
|
|
SUB GenOutFile STATIC
|
|
|
|
' Speed up by eliminating comma and colon (can't separate first token)
|
|
Sep$ = " " + CHR$(9)
|
|
DO WHILE NOT EOF(1)
|
|
LINE INPUT #1, InLin$
|
|
IF (InLin$ <> "") THEN
|
|
' Get first token and process if it is a line number
|
|
Token$ = GetToken$(InLin$, Sep$)
|
|
IF IsDigit(LEFT$(Token$, 1)) THEN
|
|
LineNumber! = VAL(Token$)
|
|
FoundNumber = false
|
|
' See if line number is in table of referenced line numbers
|
|
FOR index = 1 TO LineCount
|
|
IF (LineNumber! = LineTable!(index)) THEN
|
|
FoundNumber = TRUE
|
|
END IF
|
|
NEXT index
|
|
' Modify line strings
|
|
IF (NOT FoundNumber) THEN
|
|
Token$ = SPACE$(LEN(Token$))
|
|
MID$(InLin$, StrSpn(InLin$, Sep$), LEN(Token$)) = Token$
|
|
END IF
|
|
|
|
' You can replace the previous lines with your own
|
|
' code to reformat output. For example, try these lines:
|
|
|
|
'TmpPos1 = StrSpn(InLin$, Sep$) + LEN(Token$)
|
|
'TmpPos2 = TmpPos1 + StrSpn(MID$(InLin$, TmpPos1), Sep$)
|
|
'
|
|
'IF FoundNumber THEN
|
|
' InLin$ = LEFT$(InLin$, TmpPos1 - 1) + CHR$(9) + MID$(InLin$, TmpPos2)
|
|
'ELSE
|
|
' InLin$ = CHR$(9) + MID$(InLin$, TmpPos2)
|
|
'END IF
|
|
|
|
END IF
|
|
END IF
|
|
' Print line to file or console (PRINT is faster than console device)
|
|
IF OutputFile$ = "CON" THEN
|
|
PRINT InLin$
|
|
ELSE
|
|
PRINT #2, InLin$
|
|
END IF
|
|
LOOP
|
|
|
|
END SUB
|
|
|
|
'
|
|
' GetFileNames:
|
|
' Gets a file name by prompting the user.
|
|
' Input:
|
|
' User input
|
|
' Output:
|
|
' Defines InputFiles$ and OutputFiles$
|
|
'
|
|
SUB GetFileNames STATIC
|
|
|
|
CLS
|
|
PRINT " Microsoft RemLine: Line Number Removal Utility"
|
|
PRINT " (.BAS assumed if no extension given)"
|
|
PRINT
|
|
INPUT " Input file name (ENTER to terminate): ", InputFile$
|
|
IF InputFile$ = "" THEN END
|
|
INPUT " Output file name (ENTER to print to screen): ", OutputFile$
|
|
PRINT
|
|
IF (OutputFile$ = "") THEN OutputFile$ = "CON"
|
|
|
|
IF INSTR(InputFile$, ".") = 0 THEN
|
|
InputFile$ = InputFile$ + ".BAS"
|
|
END IF
|
|
|
|
IF INSTR(OutputFile$, ".") = 0 THEN
|
|
SELECT CASE OutputFile$
|
|
CASE "CON", "SCRN", "PRN", "COM1", "COM2", "LPT1", "LPT2", "LPT3"
|
|
EXIT SUB
|
|
CASE ELSE
|
|
OutputFile$ = OutputFile$ + ".BAS"
|
|
END SELECT
|
|
END IF
|
|
|
|
DO WHILE InputFile$ = OutputFile$
|
|
TmpFile$ = LEFT$(InputFile$, INSTR(InputFile$, ".")) + "BAK"
|
|
ON ERROR GOTO FileErr1
|
|
NAME InputFile$ AS TmpFile$
|
|
ON ERROR GOTO 0
|
|
IF TmpFile$ <> "" THEN InputFile$ = TmpFile$
|
|
LOOP
|
|
|
|
END SUB
|
|
|
|
'
|
|
' GetToken$:
|
|
' Extracts tokens from a string. A token is a word that is surrounded
|
|
' by separators, such as spaces or commas. Tokens are extracted and
|
|
' analyzed when parsing sentences or commands. To use the GetToken$
|
|
' function, pass the string to be parsed on the first call, then pass
|
|
' a null string on subsequent calls until the function returns a null
|
|
' to indicate that the entire string has been parsed.
|
|
' Input:
|
|
' Search$ = string to search
|
|
' Delim$ = String of separators
|
|
' Output:
|
|
' GetToken$ = next token
|
|
'
|
|
FUNCTION GetToken$ (Search$, Delim$) STATIC
|
|
|
|
' Note that SaveStr$ and BegPos must be static from call to call
|
|
' (other variables are only static for efficiency).
|
|
' If first call, make a copy of the string
|
|
IF (Search$ <> "") THEN
|
|
BegPos = 1
|
|
SaveStr$ = Search$
|
|
END IF
|
|
|
|
' Find the start of the next token
|
|
NewPos = StrSpn(MID$(SaveStr$, BegPos, LEN(SaveStr$)), Delim$)
|
|
IF NewPos THEN
|
|
' Set position to start of token
|
|
BegPos = NewPos + BegPos - 1
|
|
ELSE
|
|
' If no new token, quit and return null
|
|
GetToken$ = ""
|
|
EXIT FUNCTION
|
|
END IF
|
|
|
|
' Find end of token
|
|
NewPos = StrBrk(MID$(SaveStr$, BegPos, LEN(SaveStr$)), Delim$)
|
|
IF NewPos THEN
|
|
' Set position to end of token
|
|
NewPos = BegPos + NewPos - 1
|
|
ELSE
|
|
' If no end of token, return set to end a value
|
|
NewPos = LEN(SaveStr$) + 1
|
|
END IF
|
|
' Cut token out of search string
|
|
GetToken$ = MID$(SaveStr$, BegPos, NewPos - BegPos)
|
|
' Set new starting position
|
|
BegPos = NewPos
|
|
|
|
END FUNCTION
|
|
|
|
'
|
|
' InitKeyTable:
|
|
' Initializes a keyword table. Keywords must be recognized so that
|
|
' line numbers can be distinguished from numeric constants.
|
|
' Input:
|
|
' Uses KeyData
|
|
' Output:
|
|
' Modifies global array KeyWordTable$
|
|
'
|
|
SUB InitKeyTable STATIC
|
|
|
|
RESTORE KeyData
|
|
FOR Count = 1 TO KeyWordCount
|
|
READ KeyWord$
|
|
KeyWordTable$(Count) = KeyWord$
|
|
NEXT
|
|
|
|
END SUB
|
|
|
|
'
|
|
' IsDigit:
|
|
' Returns true if character passed is a decimal digit. Since any
|
|
' Basic token starting with a digit is a number, the function only
|
|
' needs to check the first digit. Doesn't check for negative numbers,
|
|
' but that's not needed here.
|
|
' Input:
|
|
' Char$ - initial character of string to check
|
|
' Output:
|
|
' IsDigit - true if within 0 - 9
|
|
'
|
|
FUNCTION IsDigit (Char$) STATIC
|
|
|
|
IF (Char$ = "") THEN
|
|
IsDigit = false
|
|
ELSE
|
|
CharAsc = ASC(Char$)
|
|
IsDigit = (CharAsc >= ASC("0")) AND (CharAsc <= ASC("9"))
|
|
END IF
|
|
|
|
END FUNCTION
|
|
|
|
'
|
|
' StrBrk:
|
|
' Searches InString$ to find the first character from among those in
|
|
' Separator$. Returns the index of that character. This function can
|
|
' be used to find the end of a token.
|
|
' Input:
|
|
' InString$ = string to search
|
|
' Separator$ = characters to search for
|
|
' Output:
|
|
' StrBrk = index to first match in InString$ or 0 if none match
|
|
'
|
|
FUNCTION StrBrk (InString$, Separator$) STATIC
|
|
|
|
Ln = LEN(InString$)
|
|
BegPos = 1
|
|
' Look for end of token (first character that is a delimiter).
|
|
DO WHILE INSTR(Separator$, MID$(InString$, BegPos, 1)) = 0
|
|
IF BegPos > Ln THEN
|
|
StrBrk = 0
|
|
EXIT FUNCTION
|
|
ELSE
|
|
BegPos = BegPos + 1
|
|
END IF
|
|
LOOP
|
|
StrBrk = BegPos
|
|
|
|
END FUNCTION
|
|
|
|
'
|
|
' StrSpn:
|
|
' Searches InString$ to find the first character that is not one of
|
|
' those in Separator$. Returns the index of that character. This
|
|
' function can be used to find the start of a token.
|
|
' Input:
|
|
' InString$ = string to search
|
|
' Separator$ = characters to search for
|
|
' Output:
|
|
' StrSpn = index to first nonmatch in InString$ or 0 if all match
|
|
'
|
|
FUNCTION StrSpn% (InString$, Separator$) STATIC
|
|
|
|
Ln = LEN(InString$)
|
|
BegPos = 1
|
|
' Look for start of a token (character that isn't a delimiter).
|
|
DO WHILE INSTR(Separator$, MID$(InString$, BegPos, 1))
|
|
IF BegPos > Ln THEN
|
|
StrSpn = 0
|
|
EXIT FUNCTION
|
|
ELSE
|
|
BegPos = BegPos + 1
|
|
END IF
|
|
LOOP
|
|
StrSpn = BegPos
|
|
|
|
END FUNCTION
|
|
|