'===========================================================================
' Subject: KELVINATOR - REDUCE SOURCE SIZE    Date: 10-13-99 (21:59)       
'  Author: Antoni Gual                        Code: QB, PDS                
'  Origin: agual@eic.ictnet.es              Packet: MISC.ABC
'===========================================================================
DECLARE SUB Intro ()
DECLARE FUNCTION BInstr% (FROM%, a$, c$)
DECLARE FUNCTION Kelvinate$ (a$)
DECLARE FUNCTION filesel$ (prompt$, match%)
DECLARE FUNCTION IsKeyword% (w$)
DECLARE FUNCTION IsLinenum% (a$)
DECLARE SUB LoadKwds ()
DECLARE FUNCTION Firstword$ (w$)
DECLARE FUNCTION Cleanout$ (a$)
'............................................................................
'KELVINATOR, by Antoni Gual  agual@eic.ictnet.es
'............................................................................
'This program could help yo if you are short of space in your IDE
' for a particular program.
'
'It will remove all that's unnecesary to run your program
'It has two levels:
' -First level removes indentation, tabs,blank lines, comments
'   giving an important gain of place in the IDE
'
' -Second level chains lines in one wherever it's possible, making a program
'   very difficult to read by 'reverse engineers', but perfectly runable.
'   (Well...this is what I tried to do.. No warranties!)
'   This second level does'nt give any valuable gain of space, it's only fun!
'
'No risk of destroying the original source as program outputs to a new file
'with the original's name and '.bak' extension.
'
'It's exactly the reverse of 'pretty printers', or a good way to prepare test
' programs for one of them :-)
'
'NOTE: The funny name comes from an equivalent program for LISP
'
'Check the self-contained file selection function!
'............................................................................
'Use it in your programs as you want, only give me credit.
'CONTACT ME WITH YOUR OPINIONS !!
'.............................................................................

DEFINT A-Z

'$INCLUDE: 'qb.bi'

CONST false = 0, true = NOT false
CONST nnum = "0123456789"
CONST inum = "0123456789-"
CONST fpnum = "0123456789-ED."



DIM SHARED metacom AS INTEGER
DIM SHARED estring$
DIM SHARED errata, words
REDIM SHARED qword$(0)
DIM SHARED inreg AS RegType
DIM SHARED crlf$: crlf$ = CHR$(13) + CHR$(10)
LoadKwds
Intro
filename$ = filesel$("Select file to Kelvinate", 0)
filename$ = LEFT$(filename$, INSTR(filename$, ".") - 1)

ON ERROR GOTO 0
OPEN filename$ + ".bas" FOR BINARY AS #1
a$ = " "
GET #1, , a$
CLOSE
IF a$ = CHR$(&HFC) THEN PRINT "The file is in QB format, please resave it in ASCII": END

INPUT "Clean rems only? [y/n]", k$: IF UCASE$(k$) = "Y" THEN klv = false ELSE klv = true
OPEN filename$ + ".bas" FOR INPUT AS #1
OPEN filename$ + ".bak" FOR OUTPUT AS #2

CLS : PRINT "Kelvinating file "; filename$; ".bas"; : LOCATE 2, 1: PRINT "Progress: ";
siz1& = LOF(1)
'main loop
DO
	ptr& = SEEK(1)
	LINE INPUT #1, a$
	LOCATE 2, 12: PRINT SPACE$(5); : LOCATE , 12: PRINT ptr& * 100 \ siz1&; " %";
	a$ = Cleanout$(a$)
	IF klv AND LEN(a$) AND NOT metacom THEN a$ = Kelvinate$(a$)
	IF LEN(a$) THEN PRINT #2, a$
LOOP UNTIL EOF(1)
IF klv THEN PRINT #2, Kelvinate$("")
LOCATE 3, 1: PRINT "Done!"
CLOSE
ERASE qword$
END

errorhandler: errata = ERR: RESUME NEXT



'keywords
QData:
DATA ABS,ABSOLUTE,APPEND,ACCESS,AS,ALIAS,ASC,ANY,ATN

DATA BASE,BLOAD,BSAVE,BEEP,BYVAL,BINARY

DATA CALL,CLS,CALLS,COLOR,CASE,COMMAND$,COMMON,COM,CDBL
DATA CONST,CDECL,COS,CHAIN,CSNG,CHDIR,CSRLIN,CHR$,CVD
DATA CINT,CVDMBF,CIRCLE,CVI,CLEAR,CVL, CLNG,CVS,CLOSE,CVSMBF

DATA DATA,DEF,DEFINT,DEFLNG,DATE$,DEFSNG,DEFSTR,DECLARE,DIM
DATA DO,DOUBLE,DRAW, DEFDBL,$DYNAMIC

DATA ELSE, ERDEV,ELSEIF,ERDEV$,END,ERROR,ERL,ERR,ENVIRON
DATA ENVIRON$,EXIT,EOF,EXP,EQV,ERASE

DATA FIELD,FOR,FILEATTR,FRE,FILES,FREEFILE,FIX,FUNCTION

DATA GOTO,GOSUB,GET

DATA HEX$

DATA IF,INTERRUPT,INPUT$,IMP,INSTR,$INCLUDE,INT,INKEY$,INTEGER
DATA INP,IOCTL,IOCTL$,IS,INPUT

DATA KILL,KEY

DATA LBOUND,LOCATE,LCASE$,LOCK,LEFT$,LOF,LEN,LOG,LET,LONG
DATA LINE,LOOP,LPOS,LIST,LPRINT,LOC,LSET,LOCAL,LTRIM$

DATA MID$,MKL$,MKS$,MKD$,MKSMBF$,MKDIR,MOD,MKDMBF$,MKI$

DATA NAME,NEXT,NOT

DATA OCT$,OFF,ON,OPEN,OPTION,OR,OUT,OUTPUT

DATA PLAY,PAINT,POINT,PALETTE,POKE,PCOPY,POS,PEEK,PRESET,PEN
DATA PRINT,PSET,PMAP,PUT,RANDOM,RETURN,RANDOMIZE, RIGHT$,READ
DATA RMDIR,REDIM,RND,REM,RSET,RESET,RTRIM$,RESTORE,RUN,RESUME

DATA SADD,STRIG,SPC,SCREEN,SQR,STATIC,SEEK,$STATIC,STEP,SELECT
DATA STICK,SETMEM,STOP,SEG,SGN,STR$,SHARED,SHELL,SIGNAL,STRING
DATA SIN,STRING$,SINGLE,SUB,SLEEP,SWAP,SOUND,SYSTEM,SPACE$

DATA TAB,TAN,TO,THEN,TROFF,TRON,TIME$,TYPE,TIMER

DATA UBOUND,UNLOCK,UCASE$,UNTIL,USING,UEVENT

DATA VAL,VARSEG,VIEW,VARPTR,VARPTR$

DATA WAIT,WINDOW,WEND,WRITE,WHILE,WIDTH

DATA @@@

FUNCTION BInstr% (FROM, a$, c$)
	BInstr% = 0
	IF FROM > LEN(a$) THEN EXIT FUNCTION
	IF LEN(c$) = 0 THEN EXIT FUNCTION
	IF LEN(a$) = 0 THEN EXIT FUNCTION
	IF FROM <= 0 THEN FROM = LEN(a$)
	P1 = LEN(c$)
	car = ASC(MID$(c$, P1, 1))
	T2 = FROM
	DO WHILE T2
		IF ASC(MID$(a$, T2, 1)) = car THEN
			DO
				P1 = P1 - 1
				IF P1 = 0 THEN
					BInstr% = T2: EXIT FUNCTION
				ELSEIF T2 = 1 THEN
					BInstr% = 0: EXIT FUNCTION
				ELSE
					car = ASC(MID$(c$, P1, 1))
					T2 = T2 - 1
					IF ASC(MID$(a$, T2, 1)) <> car THEN
						P1 = LEN(c$): car = ASC(MID$(c$, P1, 1)): EXIT DO
					END IF
				END IF
			LOOP
		END IF
		T2 = T2 - 1
	LOOP
END FUNCTION

FUNCTION Cleanout$ (a$)
	metacom = false
	'trim tabs and spaces at end of line
	DO WHILE LEFT$(a$, 1) = CHR$(9) OR LEFT$(a$, 1) = CHR$(32): a$ = MID$(a$, 2): LOOP
	 

	'skip null lines
	IF a$ = "" THEN GOTO nooutput
 
	'comments with' skipped if not metacommands
	IF LEFT$(a$, 1) = "'" THEN
		IF MID$(a$, 2, 1) = "$" THEN metacom = true: GOTO trim ELSE GOTO nooutput
	END IF
  
	'skip REMS
	IF LEFT$(a$, 4) = "REM " THEN
		IF MID$(a$, 6, 1) = "$" THEN metacom = true: GOTO trim ELSE GOTO nooutput
	END IF
  
	'delete double spaces and ' rems not at the beggining of line
	'create a string mask, to skip them in next parses
  
	estring$ = SPACE$(LEN(a$))  'shared
	strng = false: spa = false
	SWAP t$, a$: a$ = ""
	FOR i = 1 TO LEN(t$)
		car = ASC(MID$(t$, i))
		SELECT CASE car:
		CASE 32:
			IF NOT strng THEN
				IF spa THEN GOTO skipcar ELSE spa = true
			END IF
		CASE 9:
			IF NOT strng THEN
			  car = 32: IF spa THEN GOTO skipcar ELSE spa = true
			END IF
		CASE 34: spa = false: strng = NOT strng
		CASE 39:
			IF NOT strng THEN EXIT FOR
		CASE ELSE
			spa = false
		END SELECT
		a$ = a$ + CHR$(car)
		IF strng THEN MID$(estring$, LEN(a$), 1) = "X"
skipcar:
	NEXT
   
	'clear unneeded " from data
	IF LEFT$(a$, 5) = "DATA " THEN
		FOR i = LEN(a$) TO 6 STEP -1
			IF MID$(a$, i, 1) = " " AND MID$(estring$, i, 1) = " " THEN
				a$ = LEFT$(a$, i - 1) + MID$(a$, i + 1)
				estring$ = LEFT$(estring$, i - 1) + MID$(estring$, i + 1)
			END IF
		NEXT
		strg = 0
		FOR i = LEN(a$) TO 5 STEP -1
			IF strg = 0 THEN IF MID$(estring$, i, 1) = "X" THEN strg = i
			IF strg AND MID$(estring$, i, 1) = " " THEN
				IF espa = 0 THEN
					t$ = a$
					a$ = LEFT$(t$, i) + MID$(t$, i + 2, strg - i - 1) + MID$(t$, strg + 2)
					estring$ = LEFT$(estring$, i) + SPACE$(strg - i - 1) + MID$(estring$, strg + 2)
				END IF
				strg = 0: espa = 0
			END IF
			IF strg THEN
				b = ASC(MID$(a$, i, 1))
				IF b = 32 OR b = 44 OR b = 58 THEN espa = 1
			END IF
		NEXT
	END IF
trim:

	'trim REM rems
	rm = INSTR(a$, "REM")
	IF rm THEN IF ASC(MID$(estring$, rm)) = 32 THEN a$ = LEFT$(a$, rm - 1)


	'trim spaces,tabs and : at the line end
	b = ASC(RIGHT$(a$, 1))
	DO WHILE b = 9 OR b = 32
	a$ = LEFT$(a$, LEN(a$) - 1): b = ASC(RIGHT$(a$, 1))
	LOOP

	'skip null lines
	IF a$ = "" THEN GOTO nooutput

	Cleanout$ = a$
EXIT FUNCTION
nooutput:
	Cleanout$ = ""
END FUNCTION

FUNCTION filesel$ (prompt$, match%) 'Allows user to select a file
'............................................................................
'SELF-CONTAINED FILE SELECTOR FUNCTION by Antoni Gual (agual@eic.ictnet.es)
'----------------------------------------------------------------------------
'match% on input  0= must match 1=must not match 2=indiferent
'match% on output 1=found       0=not found
'----------------------------------------------------------------------------
'This single function will add to your programs an 'user friendly' file
'  selection interface.
'To use it follow the steps:
'
'    -Enable the program for interrupt handling.
'       /lqb and '$INCLUDE: 'QB.BI'  for QuickBasic
'       use one of the ABC interrupt interface routines in QBasic
'            NOTE: If you don't want to add interrupt access to your program
'            simply uncomment the indicated line, the function will work
'            and will loose only the ability of changing drive.
'
'    -Add three lines to the main part of the module. Uncomment them! ;)
'       In the DIM part:
'
'       DIM shared inreg AS REGTYPE   'only if you have interrupt access
'       DIM SHARED errata AS INTEGER
'
'       Past the END of the program:
'
'       errorhandler:errata=ERR:RESUME NEXT
'
'    -Last but not Least: Add this function to your program!
'
'and thatïs all! Call filesel$, it will return with the filename you selected.
'It detects and handles all disk errors I'm able to imaginate.
'
'Only a WARNING: It changes the default drive and path to where the file is!
'.............................................................................
'Use it in your programs as you want, only give me credit.
'CONTACT ME WITH YOUR OPINIONS !!
'.............................................................................
'Version History:
'        detecta error de dar como nombre un path
'8/10/99 gesti¢n de match
'8/10/99 comprobaci¢n de letra de unidad
'8/10/99 usa freefile para test de fichero existente
'8/10/99 color segun drive/path/file
'pdte    a¤adir gesti¢n de extensi¢n
'pdte    no modificar dir actual, devolver path completo a fichero
'
'(Just in case you read spanish: 'pdte' is my shorthand for TO DO...)
'............................................................................
'The real thing:
'............................................................................
'error messages
CONST pak$ = ". Press a key.."
CONST pnf$ = "Path not Found"
CONST ddne$ = "Drive does not Exist"
CONST dnr$ = "Drive not ready. [Retry/Abort]"
CONST fdne$ = "File not found"
CONST ifn$ = "Invalid File Name"
CONST idn$ = "Invalid Drive Name"
CONST pfe$ = "Path-file error"
ff$ = ""
filnum = FREEFILE
'screen reset, start error handler
VIEW PRINT: COLOR 7, 0: CLS
ON ERROR GOTO errorhandler

DO
	GOSUB VIEWFILES
'
'UNCOMMENT NEXT LINE IF YOU DON'T HAVE INTERRUPT ACCESS!!
'GOTO ASKPATH
'
askdrive:
	COLOR 15, 4: CLS
	GOSUB question: INPUT ; "Drive [Enter:Current]>", drive$
'if no input, go ask path
	IF LEN(drive$) THEN
R3:
		IF LEN(drive$) > 2 THEN ERROR 64: GOTO BADDRIVE
		DRIV% = ASC(UCASE$(drive$)) - 65
		IF DRIV% < 0 OR DRIV% > 23 THEN ERROR 64: GOTO BADDRIVE
		inreg.ax = &HE00: inreg.dx = DRIV%
		CALL INTERRUPT(&H21, inreg, inreg)
'probably the drive we asked for does'nt exist
		inreg.ax = &H1900
		CALL INTERRUPT(&H21, inreg, inreg)
		IF (inreg.ax AND &HFF) <> DRIV% THEN ERROR 68
BADDRIVE:
		SELECT CASE errata
		CASE 64:
		msgerr$ = idn$ + pak$: GOSUB errmsg: GOTO askdrive
		CASE 68: msgerr$ = ddne$ + pak$: GOSUB errmsg: GOTO askdrive
		CASE 71: msgerr$ = dnr$: GOSUB errmsg: IF a$ = "R" THEN GOTO R3 ELSE GOTO askdrive
		END SELECT
		GOSUB VIEWFILES
	END IF

askpath:
'what is in?  Here we trap the no disk error
	COLOR 14, 4: CLS
R4:
	IF errata = 71 THEN msgerr$ = dnr$: GOSUB errmsg: IF a$ = "R" THEN GOTO R4 ELSE GOTO askdrive
	GOSUB question: INPUT ; "Path [Enter:Current]>", path$
'if no input go ask filename
	IF LEN(path$) THEN
R1:
		CHDIR path$
		IF errata = 53 OR errata = 76 THEN msgerr$ = pnf$ + pak$: GOSUB errmsg: GOTO askpath
		IF errata = 71 THEN msgerr$ = dnr$: GOSUB errmsg: IF a$ = "R" THEN GOTO R1 ELSE GOTO askdrive
		GOSUB VIEWFILES
	END IF
askname:
	COLOR 7, 4: CLS
	GOSUB question: INPUT ; "File [Enter:New path]>", name$
'does it exist?
R2:
	OPEN name$ FOR INPUT AS #filnum
	SELECT CASE errata
	CASE 76: msgerr$ = pnf$ + pak$: GOSUB errmsg: GOTO askname
	CASE 53:
		IF match = 0 THEN
			LOCATE 2, 1: msgerr$ = fdne + pak$: GOSUB errmsg: GOTO askname
		ELSE
			ff$ = " NOT ": GOTO outofthere
		END IF
	CASE 64: LOCATE 2, 1: msgerr$ = ifn$ + pak$: GOSUB errmsg: GOTO askname
	CASE 71: msgerr$ = dnr$: GOSUB errmsg: IF a$ = "R" THEN GOTO R2 ELSE GOTO askdrive
	CASE 75: msgerr$ = pfe$ + pak$: GOSUB errmsg: GOTO askname
	END SELECT
r5:
CLOSE #filnum
LOOP UNTIL LEN(name$)

outofthere:
'file exists: return
msgerr$ = "File " + UCASE$(name$) + ff$ + " found.." + pak$: GOSUB errmsg
VIEW PRINT: COLOR 7, 0: CLS

IF LEN(ff$) THEN match% = 0 ELSE match = 1
filesel$ = name$
ON ERROR GOTO 0
EXIT FUNCTION

VIEWFILES:
	VIEW PRINT 3 TO 25: COLOR 7, 0: CLS : FILES:
	VIEW PRINT 1 TO 2:
RETURN

question:
LOCATE 1, 1: PRINT SPACE$(80); : LOCATE 1, 1: PRINT prompt$ + "? -->Select ";
RETURN

errmsg:
LOCATE 2, 1: PRINT msgerr$; : a$ = UCASE$(INPUT$(1)): LOCATE 2, 1: PRINT SPACE$(80)
errata = 0
RETURN

END FUNCTION

FUNCTION Firstword$ (w$)
   STATIC a$, ptr
   IF LEN(w$) = 0 AND ptr = 0 THEN Firstword$ = "": EXIT FUNCTION
   IF LEN(w$) THEN a$ = w$: ptr = 1
   s = INSTR(ptr, a$, " ")
   IF s = 0 THEN
	  Firstword$ = MID$(a$, ptr)
	  ptr = 0
   ELSE
	  Firstword$ = MID$(a$, ptr, s - ptr)
	  ptr = s + 1
   END IF
END FUNCTION

SUB IDEAS
'      Los programas que saca no siempre funcionan
'      No reconoce IF ... GOTO ni IF... GOSUB
'      Podrian empaquetarse los DATA
'      tambi‚n los if-then-else internos
END SUB

SUB Intro
COLOR 7, 0
CLS
COLOR 13
PRINT "............................................................................"
PRINT "                 KELVINATOR, by Antoni Gual  agual@eic.ictnet.es"
PRINT "............................................................................"
COLOR 7
PRINT "This program could help yo if you are short of place in your IDE to run a"
PRINT " particular program."
PRINT "It will remove all that's unnecesary to run your program"
PRINT "It has two levels:"
PRINT " -First level removes indentation, tabs, blank lines, comments, unneeded"
PRINT "   quotes in data, giving an important gain of place in IDE .This part is safe."
PRINT " -Second level chains lines in one wherever it's; possible, making a program"
PRINT "   very difficult to read by ' reverse engineers', but runable in theory."
PRINT "   At the moment, the resultant program can run in a 90% of cases and still does"
PRINT "   what itïs suposed to do in 80% of the cases. There's a place for improvement."
PRINT "No risk of destroying the original source as program outputs to a new file"
PRINT "with the original's name and '.bak' extension."
PRINT "It's exactly the reverse of 'pretty printers', or a good way to prepare test"
PRINT " programs for one of them :-)"
PRINT "NOTE: The funny name comes from an equivalent program for LISP"
PRINT "Check the self-contained file selection function!"
COLOR 14
PRINT "............................................................................"
PRINT "Use it in your programs as you want, only give me credit."
PRINT "CONTACT ME WITH YOUR OPINIONS !!";
LOCATE 25, 1: COLOR 12: PRINT "Press any key.."; : a$ = INPUT$(1)
END SUB

FUNCTION IsKeyword (w$)
	IsKeyword = false
	IF w$ <> UCASE$(w$) THEN EXIT FUNCTION
	IF w$ < qword$(1) THEN EXIT FUNCTION
	IF w$ > qword$(words) THEN EXIT FUNCTION
	P1 = 1: P2 = words
	DO
		p3 = (P1 + P2) \ 2: a$ = qword$(p3)
		IF w$ = a$ THEN IsKeyword = true: EXIT FUNCTION
		IF w$ < a$ THEN P2 = p3 - 1:  ELSE P1 = p3 + 1
	LOOP UNTIL P1 > P2
END FUNCTION

FUNCTION IsLinenum (a$)
	IsLinenum = false
	FOR i = 1 TO LEN(a$) - 1
		IF NOT INSTR(nnum, MID$(a$, i, 1)) THEN EXIT FUNCTION
	NEXT
	IsLinenum = true
END FUNCTION

FUNCTION Kelvinate$ (a$)

STATIC w$
	IF a$ = "" THEN Kelvinate$ = w$: w$ = "": EXIT FUNCTION
	fw$ = Firstword(a$)
   
	' with line numbers we start a new line
	IF IsLinenum(fw$) THEN PRINT #2, w$: w$ = a$: GOTO startline
   
	'check for kwd
	IF NOT IsKeyword(fw$) THEN
	   
		' with labels we start a new line
		IF RIGHT$(fw$, 1) = ":" THEN
			GOTO startline
		'sub names
		ELSE
			'sub names without parameters at beggining of line require CALL
			IF LEN(w$) = 0 THEN IF LEN(a$) = LEN(fw$) THEN a$ = "CALL " + a$
			GOTO addtoline
		END IF

	'treat line depending on starting kwd
	ELSE
		SELECT CASE fw$
		'sub and function headers must be in a line of their own
		CASE "SUB", "FUNCTION":     GOTO ownline
		'CASE "SELECT", "CASE":      GOTO STARTLINE
	   
		'pack as many data fit in a single line
		CASE "DATA":
			 GOTO startline
		CASE "ELSE", "ELSEIF"
			GOTO ownline
		CASE "END"
			dummy$ = Firstword(a$): swd$ = Firstword$("")
			SELECT CASE swd$
			CASE "SELECT", "IF": GOTO startline
			CASE ELSE: GOTO addtoline
			END SELECT
		CASE ELSE
			'if it has a then and it's not the last thing in line must end line
			b = BInstr%(0, a$, " THEN")
			IF b = 0 THEN GOTO addtoline
			IF b < LEN(a$) - 5 THEN
				GOTO finishline
			ELSE
				GOTO ownline
			END IF
		END SELECT
	END IF
EXIT FUNCTION


startline:
IF LEN(w$) THEN Kelvinate$ = w$
w$ = a$: EXIT FUNCTION


finishline:
  IF LEN(w$) + LEN(a$) <= 253 THEN
	  IF LEN(w$) THEN
		  IF RIGHT$(w$, 1) <> ":" THEN col$ = ": " ELSE col$ = " "
		  w$ = w$ + col$ + a$: Kelvinate$ = w$: w$ = "": EXIT FUNCTION
	  END IF
  END IF

ownline:
IF LEN(w$) THEN w$ = w$ + crlf$
Kelvinate$ = w$ + a$: w$ = "": EXIT FUNCTION


addtoline:
	IF LEN(w$) + LEN(a$) <= 253 THEN
		IF LEN(w$) THEN
			IF RIGHT$(w$, 1) <> ":" THEN col$ = ": " ELSE col$ = " "
			w$ = w$ + col$ + a$: Kelvinate$ = "": EXIT FUNCTION
		ELSE
			w$ = a$: Kelvinate$ = "": EXIT FUNCTION
		END IF
	ELSE
	   lima = 253 - LEN(w$)
	   DO
			colon = BInstr%(lima, a$, ":")
			IF colon THEN IF MID$(estring$, colon, 1) = " " THEN EXIT DO
			lima = colon - 1
	   LOOP UNTIL colon = 0
	   IF colon THEN
		w$ = w$ + ": " + LEFT$(a$, colon - 1): Kelvinate$ = w$
		w$ = MID$(a$, colon + 2): EXIT FUNCTION
	   ELSE
		Kelvinate$ = w$: w$ = a$: EXIT FUNCTION
	   END IF
	END IF


chaindata:
	IF LEN(w$) + LEN(a$) <= 256 THEN
		w$ = w$ + "," + MID$(a$, 6): RETURN
	ELSE
	   lima = 252 - LEN(w$)
	   DO
		   comma = BInstr%(lima, a$, ",")
		   IF comma THEN IF MID$(estring$, comma, 1) = " " THEN EXIT DO
		   lima = comma - 1
	   LOOP UNTIL comma = 0
	   IF comma THEN
		w$ = w$ + "," + MID$(a$, 6, comma - 7): PRINT #2, w$: w$ = "DATA " + MID$(a$, comma + 1)
	   ELSE
		PRINT #2, w$: w$ = a$: RETURN
	   END IF
	END IF
RETURN

END FUNCTION

SUB LoadKwds 'load kwds from DATA, sorts, counts and search for duplicates

'LOAD KWD DATA

'SNIPPET READ STRINGS FROM DATA WITH SENTINEL

RESTORE QData
words = 0
DO WHILE a$ <> "@@@"
	 words = words + 1: READ a$
LOOP
words = words - 1
REDIM qword$(1 TO words)
RESTORE QData
FOR i = 1 TO words
  READ qword$(i)
NEXT

'END SNIPPET

'SNIPPET BUBBLESORT

FOR i = 2 TO words
   J = i
   DO WHILE qword$(J) < qword$(J - 1)
	  SWAP qword$(J), qword$(J - 1)
	  J = J - 1
	  IF J < 2 THEN EXIT DO
   LOOP
NEXT

'END SNIPPET

CLS : COLOR 15

'snippet find equal
FOR i = 2 TO words
 IF qword$(i) = qword$(i - 1) THEN
	PRINT "ERROR Repeated Kwd "; i; ": "; qword$(i); "= "; qword$(i - 1)
 END IF
NEXT
'PRINT i; " Keywords in list": PRINT
'end snippet

'FOR i = 1 TO words: PRINT qword$(i); " "; : NEXT
'A$ = INPUT$(1)


END SUB
