2 ' Microsoft RemLine - Line Number Removal Utility
3 ' Copyright (C) Microsoft Corporation 1985-1990
5 ' REMLINE.BAS is a program to remove line numbers from Microsoft Basic
6 ' Programs. It removes only those line numbers that are not the object
7 ' of one of the following statements: GOSUB, RETURN, GOTO, THEN, ELSE,
8 ' RESUME, RESTORE, or RUN.
10 ' When REMLINE is run, it will ask for the name of the file to be
11 ' processed and the name of the file or device to receive the
12 ' reformatted output. If no extension is given, .BAS is assumed (except
13 ' for output devices). If filenames are not given, REMLINE prompts for
14 ' file names. If both filenames are the same, REMLINE saves the original
15 ' file with the extension .BAK.
17 ' REMLINE makes several assumptions about the program:
19 ' 1. It must be correct syntactically, and must run in BASICA or
20 ' GW-BASIC interpreter.
21 ' 2. There is a 400 line limit. To process larger files, change
23 ' 3. The first number encountered on a line is considered a line
24 ' number; thus some continuation lines (in a compiler-specific
25 ' construction) may not be handled correctly.
26 ' 4. REMLINE can handle simple statements that test the ERL function
27 ' using relational operators such as =, <, and >. For example,
28 ' the following statement is handled correctly:
30 ' IF ERL = 100 THEN END
32 ' Line 100 is not removed from the source code. However, more
33 ' complex expressions that contain the +, -, AND, OR, XOR, EQV,
34 ' MOD, or IMP operators may not be handled correctly. For example,
35 ' in the following statement REMLINE does not recognize line 105
36 ' as a referenced line number and removes it from the source code:
38 ' IF ERL + 5 = 105 THEN END
40 ' If you do not like the way REMLINE formats its output, you can modify
41 ' the output lines in SUB GenOutFile. An example is shown in comments.
44 ' Function and Subprocedure declarations
45 DECLARE FUNCTION GetToken$ (Search$, Delim$)
46 DECLARE FUNCTION StrSpn% (InString$, Separator$)
47 DECLARE FUNCTION StrBrk% (InString$, Separator$)
48 DECLARE FUNCTION IsDigit% (Char$)
49 DECLARE SUB GetFileNames ()
50 DECLARE SUB BuildTable ()
51 DECLARE SUB GenOutFile ()
52 DECLARE SUB InitKeyTable ()
54 ' Global and constant data
59 DIM SHARED LineTable!(MaxLines)
61 DIM SHARED Seps$, InputFile$, OutputFile$, TmpFile$
64 CONST KeyWordCount = 9
65 DIM SHARED KeyWordTable$(KeyWordCount)
68 DATA THEN, ELSE, GOSUB, GOTO, RESUME, RETURN, RESTORE, RUN, ERL, ""
70 ' Start of module-level program code
71 Seps$ = " ,:=<>()" + CHR$(9)
74 ON ERROR GOTO FileErr1
75 OPEN InputFile$ FOR INPUT AS 1
77 COLOR 7: PRINT "Working"; : COLOR 23: PRINT " . . .": COLOR 7: PRINT
80 OPEN InputFile$ FOR INPUT AS 1
81 ON ERROR GOTO FileErr2
82 OPEN OutputFile$ FOR OUTPUT AS 2
86 IF OutputFile$ <> "CON" THEN CLS
92 PRINT " Invalid file name": PRINT
93 INPUT " New input file name (ENTER to terminate): ", InputFile$
94 IF InputFile$ = "" THEN END
96 INPUT " Output file name (ENTER to print to screen) :", OutputFile$
98 IF (OutputFile$ = "") THEN OutputFile$ = "CON"
108 ' Examines the entire text file looking for line numbers that are
109 ' the object of GOTO, GOSUB, etc. As each is found, it is entered
110 ' into a table of line numbers. The table is used during a second
111 ' pass (see GenOutFile), when all line numbers not in the list
114 ' Uses globals KeyWordTable$, KeyWordCount, and Seps$
116 ' Modifies LineTable! and LineCount
118 SUB BuildTable STATIC
121 ' Get line and first token
122 LINE INPUT #1, InLin$
123 Token$ = GetToken$(InLin$, Seps$)
124 DO WHILE (Token$ <> "")
125 FOR KeyIndex = 1 TO KeyWordCount
126 ' See if token is keyword
127 IF (KeyWordTable$(KeyIndex) = UCASE$(Token$)) THEN
128 ' Get possible line number after keyword
129 Token$ = GetToken$("", Seps$)
130 ' Check each token to see if it is a line number
131 ' (the LOOP is necessary for the multiple numbers
132 ' of ON GOSUB or ON GOTO). A non-numeric token will
134 DO WHILE (IsDigit(LEFT$(Token$, 1)))
135 LineCount = LineCount + 1
136 LineTable!(LineCount) = VAL(Token$)
137 Token$ = GetToken$("", Seps$)
138 IF Token$ <> "" THEN KeyIndex = 0
143 Token$ = GetToken$("", Seps$)
151 ' Generates an output file with unreferenced line numbers removed.
153 ' Uses globals LineTable!, LineCount, and Seps$
157 SUB GenOutFile STATIC
159 ' Speed up by eliminating comma and colon (can't separate first token)
162 LINE INPUT #1, InLin$
163 IF (InLin$ <> "") THEN
164 ' Get first token and process if it is a line number
165 Token$ = GetToken$(InLin$, Sep$)
166 IF IsDigit(LEFT$(Token$, 1)) THEN
167 LineNumber! = VAL(Token$)
169 ' See if line number is in table of referenced line numbers
170 FOR index = 1 TO LineCount
171 IF (LineNumber! = LineTable!(index)) THEN
175 ' Modify line strings
176 IF (NOT FoundNumber) THEN
177 Token$ = SPACE$(LEN(Token$))
178 MID$(InLin$, StrSpn(InLin$, Sep$), LEN(Token$)) = Token$
181 ' You can replace the previous lines with your own
182 ' code to reformat output. For example, try these lines:
184 'TmpPos1 = StrSpn(InLin$, Sep$) + LEN(Token$)
185 'TmpPos2 = TmpPos1 + StrSpn(MID$(InLin$, TmpPos1), Sep$)
188 ' InLin$ = LEFT$(InLin$, TmpPos1 - 1) + CHR$(9) + MID$(InLin$, TmpPos2)
190 ' InLin$ = CHR$(9) + MID$(InLin$, TmpPos2)
195 ' Print line to file or console (PRINT is faster than console device)
196 IF OutputFile$ = "CON" THEN
207 ' Gets a file name by prompting the user.
211 ' Defines InputFiles$ and OutputFiles$
213 SUB GetFileNames STATIC
216 PRINT " Microsoft RemLine: Line Number Removal Utility"
217 PRINT " (.BAS assumed if no extension given)"
219 INPUT " Input file name (ENTER to terminate): ", InputFile$
220 IF InputFile$ = "" THEN END
221 INPUT " Output file name (ENTER to print to screen): ", OutputFile$
223 IF (OutputFile$ = "") THEN OutputFile$ = "CON"
225 IF INSTR(InputFile$, ".") = 0 THEN
226 InputFile$ = InputFile$ + ".BAS"
229 IF INSTR(OutputFile$, ".") = 0 THEN
230 SELECT CASE OutputFile$
231 CASE "CON", "SCRN", "PRN", "COM1", "COM2", "LPT1", "LPT2", "LPT3"
234 OutputFile$ = OutputFile$ + ".BAS"
238 DO WHILE InputFile$ = OutputFile$
239 TmpFile$ = LEFT$(InputFile$, INSTR(InputFile$, ".")) + "BAK"
240 ON ERROR GOTO FileErr1
241 NAME InputFile$ AS TmpFile$
243 IF TmpFile$ <> "" THEN InputFile$ = TmpFile$
250 ' Extracts tokens from a string. A token is a word that is surrounded
251 ' by separators, such as spaces or commas. Tokens are extracted and
252 ' analyzed when parsing sentences or commands. To use the GetToken$
253 ' function, pass the string to be parsed on the first call, then pass
254 ' a null string on subsequent calls until the function returns a null
255 ' to indicate that the entire string has been parsed.
257 ' Search$ = string to search
258 ' Delim$ = String of separators
260 ' GetToken$ = next token
262 FUNCTION GetToken$ (Search$, Delim$) STATIC
264 ' Note that SaveStr$ and BegPos must be static from call to call
265 ' (other variables are only static for efficiency).
266 ' If first call, make a copy of the string
267 IF (Search$ <> "") THEN
272 ' Find the start of the next token
273 NewPos = StrSpn(MID$(SaveStr$, BegPos, LEN(SaveStr$)), Delim$)
275 ' Set position to start of token
276 BegPos = NewPos + BegPos - 1
278 ' If no new token, quit and return null
284 NewPos = StrBrk(MID$(SaveStr$, BegPos, LEN(SaveStr$)), Delim$)
286 ' Set position to end of token
287 NewPos = BegPos + NewPos - 1
289 ' If no end of token, return set to end a value
290 NewPos = LEN(SaveStr$) + 1
292 ' Cut token out of search string
293 GetToken$ = MID$(SaveStr$, BegPos, NewPos - BegPos)
294 ' Set new starting position
301 ' Initializes a keyword table. Keywords must be recognized so that
302 ' line numbers can be distinguished from numeric constants.
306 ' Modifies global array KeyWordTable$
308 SUB InitKeyTable STATIC
311 FOR Count = 1 TO KeyWordCount
313 KeyWordTable$(Count) = KeyWord$
320 ' Returns true if character passed is a decimal digit. Since any
321 ' Basic token starting with a digit is a number, the function only
322 ' needs to check the first digit. Doesn't check for negative numbers,
323 ' but that's not needed here.
325 ' Char$ - initial character of string to check
327 ' IsDigit - true if within 0 - 9
329 FUNCTION IsDigit (Char$) STATIC
335 IsDigit = (CharAsc >= ASC("0")) AND (CharAsc <= ASC("9"))
342 ' Searches InString$ to find the first character from among those in
343 ' Separator$. Returns the index of that character. This function can
344 ' be used to find the end of a token.
346 ' InString$ = string to search
347 ' Separator$ = characters to search for
349 ' StrBrk = index to first match in InString$ or 0 if none match
351 FUNCTION StrBrk (InString$, Separator$) STATIC
355 ' Look for end of token (first character that is a delimiter).
356 DO WHILE INSTR(Separator$, MID$(InString$, BegPos, 1)) = 0
370 ' Searches InString$ to find the first character that is not one of
371 ' those in Separator$. Returns the index of that character. This
372 ' function can be used to find the start of a token.
374 ' InString$ = string to search
375 ' Separator$ = characters to search for
377 ' StrSpn = index to first nonmatch in InString$ or 0 if all match
379 FUNCTION StrSpn% (InString$, Separator$) STATIC
383 ' Look for start of a token (character that isn't a delimiter).
384 DO WHILE INSTR(Separator$, MID$(InString$, BegPos, 1))