This content is archived and may be deprecated or obsolete and is presented in the hope that it may still be useful.
REM ReadLine.bbc Version 2.2 29-Mar-2006REM (C) 2006 Jon Ripley
ERROR 0,"This is a library."
REM Read a line of text
DEF FN_ReadLine
LOCAL temp%, buffer%
length% = 65536
DIM buffer% LOCAL length%
temp% = FN_ReadLineF(buffer%, length%, "~", 1)
= $buffer%
REM Read a number
DEF FN_ReadNumber
LOCAL temp%, buffer%
length% = 65536
DIM buffer% LOCAL length%
temp% = FN_ReadLineF(buffer%, length%, "0-9E.+\-", 1)
= VAL $buffer%
REM Read a password
DEF FN_ReadPassword
LOCAL temp%, buffer%
length% = 65536
DIM buffer% LOCAL length%
temp% = FN_ReadLineF(buffer%, length%, "~", 512+2)
= $buffer%
REM Equivalent to FN_ReadLineF without requiring a buffer
DEF FN_ReadLineS(length%, valid$, flags%)
IF length% <= 0 THEN =""
LOCAL temp%, buffer%
DIM buffer% LOCAL length%
temp% = FN_ReadLineF(buffer%, length%, "~", 1)
= $buffer%
REM Master line input routine
DEF FN_ReadLineF(buffer%, length%, valid$, flags%)
PRIVATE insert%, init%, history{}, line{()}, valid%
IF init% = FALSE THEN
init% = TRUE
insert% = TRUE
DIM history{cur%, top%, max%}
history.cur% = 0
history.top% = 0
IF ((flags% AND &FF000000) >> 24) = 7 THEN
history.max% = (flags% AND &00FFFFFF)
ELSE
history.max% = 260
ENDIF
DIM line{(history.max%)text$, pos%}
DIM valid% 255
ENDIF
REM Read validation string
LOCAL mode%, c%, d%, e%, i%, j%, err%, esc%
REM Invalidate all characters
FOR i% = 0 TO 255
valid%?i% = 0
NEXT i%
IF valid$ = "" THEN valid$ = "~"
IF LEFT$(valid$,1) = "~" THEN
REM Default: Allow all characters
FOR i% = 32 TO 126:REM ***
i% = i% - (i% = 127)
valid%?i% = 1
NEXT i%
ENDIF
REM Parse validation string
mode% = 1 :REM Allow
FOR i% = 1 TO LEN valid$
err%=FALSE:esc%=FALSE
c% = ASCMID$(valid$,i%)
IF c%=ASC"\" THEN
i% += 1
c% = ASCMID$(valid$,i%)
esc% = TRUE:REM Escape character
IF c% = -1 THEN err% = -1:REM Escape string too short
ENDIF
d% = ASCMID$(valid$,i%+1)
IF c% = ASC"-" AND NOT esc% THEN err% = -2:REM '-' outside range
IF c% = ASC"~" AND NOT esc% THEN mode% = 0:err% = 1:REM Disallow
IF d% = ASC"-" THEN
e% = ASCMID$(valid$,i%+2)
IF e%=ASC"\" THEN
i% += 1
e% = ASCMID$(valid$,i%+2)
esc% = TRUE:REM Escape character
IF e% = -1 THEN err% = -1:REM Escape string too short
ENDIF
IF e% = -1 THEN
err% = -3:REM Validation missing a range end mark
i% += 1
ELSE
IF c% > e% THEN
err% = -4:REM Invalid range, start character is greater than end character
ELSE
FOR j% = c% TO e%
valid%?j% = mode%
NEXT j%
ENDIF
i% += 2
ENDIF
ELSE
IF err% = 0 THEN valid%?c% = mode%
ENDIF
NEXT i%
REM Pop history (Must be here for reason code 8)
IF history.top% = history.max% - 1 THEN
FOR i% = 0 TO history.top% - 1
line{(i%)}.text$ = line{(i% + 1)}.text$
NEXT i%
history.top% -= 1
ENDIF
history.cur% = history.top%
REM Handle internal calls
LOCAL hfile%, temp%, ret%
CASE (flags% AND &FF000000) >> 24 OF
WHEN 1:REM Load history
hfile% = OPENIN$buffer%
IF hfile% = 0 THEN
ret% = 1
ELSE
PROC_ClearHistory
i% = 0
WHILE NOT EOF#hfile%
line{(i%)}.text$ = GET$#hfile%
temp% = BGET#hfile%
i% += 1
ENDWHILE
history.top% = i%
CLOSE#hfile%
hfile% = 0
ENDIF
WHEN 2:REM Save history
hfile% = OPENOUT$buffer%
IF hfile% = 0 THEN
ret% = 1
ELSE
FOR i% = 0 TO history.top% - 1
BPUT#hfile%, line{(i%)}.text$ + CHR$13 + CHR$10;
NEXT i%
CLOSE#hfile%
hfile% = 0
ENDIF
WHEN 3:REM Clear history
FOR i% = 0 TO history.top%
line{(i%)}.text$ = ""
line{(i%)}.pos% = 0
NEXT i%
history.top% = 0
history.cur% =0
WHEN 4:REM Dump history
IF history.top% > 0 THEN
FOR i% = history.top% TO 0 STEP -1
PRINT RIGHT$("000"+STR$i%,3)":";line{(i%)}.text$
NEXT i%
ELSE
PRINT "Line history is disabled."
ENDIF
WHEN 5:REM Enumerate lines
ret% = history.top%
WHEN 6:REM Return specific line
temp% = flags% AND &FFFFFF
IF temp% >= 0 AND temp% <= history.max% THEN
=line{(temp%)}.text$
ELSE
=""
ENDIF
WHEN 7:REM Set line history buffer
REM Must be the first call to ReadLineF
IF history.max% = flags% AND &00FFFFFF THEN
ret% = 0:REM Ok
ELSE
ret% = 1:REM Failed
ENDIF
WHEN 8:REM Append a line to the history buffer, as if it was the last line typed
line{(history.top%)}.text$ = $buffer%
history.top% += 1
IF history.top% > history.max% THEN history.top% = history.max%
WHEN 9:REM Remove history item specified by its position from the history
line% = flags% AND &FFFFFF
FOR i% = line% + 1 TO history.top%
IF i% <= history.max% + 1 THEN line{(i%-1)}.text$ = line{(i%)}.text$
NEXT i%
WHEN &A:REM Replace history item specified by its position with the given line
temp% = flags% AND &FFFFFF
IF temp% >= 0 AND temp% <= history.max% THEN
line{(temp%)}.text$ = $buffer%
ret% = 0
ELSE
ret% = 1
ENDIF
ENDCASE
IF (flags% AND &FF000000) >> 24 THEN =ret%
IF length% <= 0 THEN =0
LOCAL ptr%, len%, x%, y%, cursor_enabled%, escape_enabled%, history_enabled%
cursor_enabled% = FN_CursorIsEnabled
escape_enabled% = FN_EscapeIsEnabled
history_enabled% = ((flags% AND &200) >> 9) = 0
REM Disable ESCape
*ESC OFF
REM Set cursor to insert/overwrite
IF insert% THEN
VDU 23,0,10,13,0;0;0;
ELSE
VDU 23,0,10,0,0;0;0;
ENDIF
REM Clear buffer
FOR i%=0 TO length%
buffer%?i% = 13
NEXT
REM Main input loop
REPEAT
ON:temp% = GET:OFF
REM Recognise ALT+KeyPad combinations as characters
IF INKEY-3 = -1 AND temp% >= 32 AND temp% <> 127 THEN temp% += 256
REM Insert - toggle edit mode
IF temp% = 134 THEN
insert% = NOT insert%
REM Set cursor to insert/overwrite
IF insert% THEN
VDU 23,0,10,13,0;0;0;
ELSE
VDU 23,0,10,0,0;0;0;
ENDIF
temp% = 0
ENDIF
REM CTRL-U - clear input buffer
IF temp% = 21 THEN
PROC_MoveCursor(-ptr%, flags%)
PROC_WRSTR(STRING$(len%, CHR$31), flags%)
PROC_MoveCursor(-len%, flags%)
buffer%?0 = 13
ptr% = 0
len% = 0
temp% = 0
ENDIF
REM Delete character to the left
IF (temp% = 8 OR temp% = 127) AND ptr% > 0 THEN
IF ptr% = len% THEN
ptr% -= 1
len% -= 1
PROC_MoveCursor(-1, flags%)
PROC_WRCH(31, flags%)
PROC_MoveCursor(-1, flags%)
ELSE
buffer%?len% = 13
temp% = ptr%
FOR temp% = ptr% - 1 TO len%-1
buffer%?temp% = ?(buffer%+temp%+1)
NEXT
ptr% -= 1
len% -= 1
PROC_MoveCursor(-1, flags%)
PROC_WRSTR($(buffer%+ptr%)+CHR$31, flags%)
PROC_MoveCursor(-LEN$(buffer%+ptr%)-1, flags%)
ENDIF
temp% = 0
ENDIF
REM Delete character to the right
IF (temp% = 135 AND ptr% < len%) THEN
IF ptr% = len%-1 THEN
len% -= 1
PROC_WRCH(31, flags%)
PROC_MoveCursor(-1, flags%)
ELSE
buffer%?len% = 13
temp% = ptr%
FOR temp% = ptr% TO len%
buffer%?temp% = ?(buffer%+temp%+1)
NEXT
len% -= 1
PROC_WRSTR($(buffer%+ptr%)+CHR$31, flags%)
PROC_MoveCursor(-LEN$(buffer%+ptr%), flags%)
ENDIF
temp% = 0
ENDIF
REM User has finished inputting
IF temp% = 27 OR temp% = 10 OR temp% = 13 THEN
buffer%?len% = 13
REM Move pointer to end of input
IF ptr% < len% THEN
PROC_MoveCursor(len% - ptr%, flags%)
ptr% = len%
ENDIF
IF temp% = 27 THEN buffer%?0 = 13
IF temp% = 10 THEN history_enabled% = FALSE
temp% = -1: REM Signifies end of input
ENDIF
REM Left - Move left one char
IF temp% = 136 AND ptr% > 0 THEN
ptr% -= 1
PROC_MoveCursor(-1, flags%)
temp% = 0
ENDIF
REM Right - Move right one char
IF temp% = 137 AND ptr% < len% THEN
PROC_MoveCursor(1, flags%)
ptr% += 1
temp% = 0
ENDIF
REM Ctrl Left - Move one word left
IF temp% = 128 AND ptr% > 0 THEN
REPEAT
ptr% -= 1
PROC_MoveCursor(-1, flags%)
UNTIL ptr% = 0 OR (?(buffer% + ptr% - 1) = 32 AND buffer%?ptr% <> 32)
temp% = 0
ENDIF
REM Ctrl Right - Move one word right
IF temp% = 129 AND ptr% < len% THEN
REPEAT
PROC_MoveCursor(1, flags%)
ptr% += 1
UNTIL ptr% = len% OR (?(buffer% + ptr% - 1) = 32 AND buffer%?ptr% <> 32)
temp% = 0
ENDIF
REM Home - Move to start
IF temp% = 130 AND ptr% > 0 THEN
PROC_MoveCursor(-ptr%, flags%)
ptr% = 0
temp% = 0
ENDIF
REM End - Move to end
IF temp% = 131 AND ptr% < len% THEN
PROC_MoveCursor(len% - ptr%, flags%)
ptr% = len%
temp% = 0
ENDIF
REM Ctrl Home - Delete to start
IF temp% = 156 AND ptr% > 0 THEN
PROC_MoveCursor(-ptr%, flags%)
buffer%?len% = 13
PROC_WRSTR($(buffer% + ptr%) + STRING$(ptr%, CHR$31), flags%)
PROC_MoveCursor(-len%, flags%)
FOR i% = 0 TO len% - ptr%
buffer%?i% = ?(buffer% + ptr%+i%)
NEXT
len% -= ptr%
ptr% = 0
temp% = 0
ENDIF
REM Ctrl End - Delete to end
IF temp% = 157 THEN
buffer%?ptr% = 13
PROC_WRSTR(STRING$(len% - ptr%, CHR$31), flags%)
PROC_MoveCursor(-(len% - ptr%), flags%)
len% = ptr%
temp% = 0
ENDIF
REM Ctrl+S - Switch case
IF temp% = 19 AND ptr% < len% THEN
temp% = buffer%?ptr%
CASE TRUE OF
WHEN temp% >= ASC"A" AND temp% <= ASC"Z" : temp% += 32
WHEN temp% >= ASC"a" AND temp% <= ASC"z" : temp% -= 32
REM TODO: Switch case of extended characters
ENDCASE
IF valid%?temp% THEN
buffer%?ptr% = temp%
PROC_WRCH(buffer%?ptr%, flags%)
ELSE
PROC_MoveCursor(1, flags%)
ENDIF
ptr% += 1
temp% = 0
ENDIF
REM Ctrl+T - Transpose adjacent characters
IF temp% = 20 AND ptr% + 1 < len% THEN
SWAP buffer%?ptr%, buffer%?(ptr% + 1)
PROC_WRCH(buffer%?ptr%, flags%)
PROC_WRCH(buffer%?(ptr% + 1), flags%)
PROC_MoveCursor(-2, flags%)
temp% = 0
ENDIF
REM Sound system bell
IF temp% = 7 AND (flags% AND 1024) = 0 THEN VDU 7
REM Ctrl+C / Ctrl+X - Copy to clipboard
IF temp% = 3 OR temp% = 24 THEN
SYS "GlobalAlloc", &2000, LEN($buffer%)+3 TO i%
SYS "GlobalLock", i% TO j%
$j% = $buffer% + CHR$13 + CHR$10
j%?(LEN($buffer%)+2) = 0
SYS "GlobalUnlock", i%
SYS "OpenClipboard", @hwnd%
SYS "EmptyClipboard"
SYS "SetClipboardData", 1, i%
SYS "CloseClipboard"
IF temp% = 24 THEN
PROC_MoveCursor(-ptr%, flags%)
PROC_WRSTR(STRING$(len%, CHR$31), flags%)
PROC_MoveCursor(-len%, flags%)
buffer%?0 = 13
ptr% = 0
len% = 0
ENDIF
temp% = 0
ENDIF
REM Ctrl+V - Paste text from clipboard
IF temp% = 22 THEN
SYS "IsClipboardFormatAvailable", 1 TO i%
IF i% = 1 THEN
SYS "OpenClipboard", @hwnd%
SYS "GetClipboardData", 1 TO j%
SYS "GlobalLock", j% TO temp%
c% = 0
i% = temp%
temp$ = ""
WHILE ?i% <> 0 AND (len% + c% + 1) < length%
IF valid%?(?i%) THEN
c% += 1
temp$ += CHR$?i%
ENDIF
i% += 1
ENDWHILE
$(buffer% + ptr%) = temp$ + $(buffer% + ptr%)
SYS "GlobalUnlock", j%
SYS "CloseClipboard"
PROC_WRSTR($(buffer% + ptr%), flags%)
len% += c%
ptr% += c%
IF len% <> ptr% THEN PROC_MoveCursor(-len% + ptr%, flags%)
ENDIF
temp% = 0
ENDIF
REM Up / Down - Navigate history
IF temp% = 139 OR temp% = 138 THEN
dir% = NOT((temp% - 138) + (temp% = 138)) + 1
IF (history.cur% = history.top% AND dir% = -1) OR (history.cur% = 0 AND dir% = 1) OR (history.cur% > 0 AND history.cur% < history.top%) THEN
IF history.cur% = history.top% THEN
IF dir%=-1 THEN line{(history.top%)}.text$ = $buffer%
ENDIF
IF INKEY-1 THEN dir% *= 16
history.cur% += dir%
IF history.cur% > history.top% OR (INKEY-2 AND dir% > 0) THEN history.cur% = history.top%
IF history.cur% < 0 OR (INKEY-2 AND dir% < 0) THEN history.cur% = 0
REM Clear current buffer
PROC_MoveCursor(-ptr%, flags%)
PROC_WRSTR(STRING$(len%, CHR$31), flags%)
PROC_MoveCursor(-len%, flags%)
ptr% = 0
len% = 0
REM Copy string observing allowed characters
FOR i% = 1 TO LENline{(history.cur%)}.text$
temp% = ASCMID$(line{(history.cur%)}.text$, i%, 1)
IF valid%?temp% = 1 AND ptr% < length% THEN
buffer%?ptr% = temp%
ptr% += 1
ENDIF
NEXT i%
REM Finish and display
len% = ptr%
buffer%?len% = 13
PROC_WRSTR($buffer%, flags%)
ENDIF
temp% = 0
ENDIF
REM Insert character into buffer
IF temp% >= 256 THEN temp% -= 256
IF valid%?temp% = 1 THEN
IF (ptr% + 1) < length% AND (insert% AND len% + 1 < length%) THEN
REM Using insert mode
IF insert% AND ptr% <> len% THEN
buffer%?len% = 13
FOR i% = len% TO ptr% - 1 STEP -1
?(buffer% + i% + 1) = buffer%?i%
NEXT
PROC_MoveCursor(1, flags%)
PROC_WRSTR($(buffer% + ptr% + 1), flags%)
PROC_MoveCursor(-LEN$(buffer% + ptr%), flags%)
len% += 1
ENDIF
buffer%?ptr%=temp%
ptr% += 1
IF ptr% > len% THEN len% = ptr%
PROC_WRCH(temp%, flags%)
ELSE
REM Using overwrite mode
IF NOT insert% THEN
buffer%?ptr% = temp%
ptr% += 1
IF ptr% > len% THEN len% = ptr%
PROC_WRCH(temp%, flags%)
ELSE
REM Buffer is full
IF (flags% AND 1024) = 0 THEN VDU 7
ENDIF
ENDIF
ENDIF
UNTIL temp% = -1
REM Store line in history
line{(history.top%)}.text$ = ""
IF history_enabled% AND LEN$buffer% > 0 THEN
line{(history.top%)}.text$ = $buffer%
history.top% += 1
IF history.top% > history.max% THEN history.top% = history.max%
ENDIF
REM Restore cursor shape, cursor and *ESC status
VDU 23,0,10,13,0;0;0;
IF cursor_enabled% THEN ON
IF escape_enabled% THEN
*ESC ON
ELSE
*ESC OFF
ENDIF
=LEN$buffer%
REM Load line history from file$
DEF PROC_LoadHistory(file$)
LOCAL temp%, buffer%
DIM buffer% LOCAL LENfile$+1
$buffer% = file$
temp% = FN_ReadLineF(buffer%, LENfile$+1, "~", &01000000)
ENDPROC
REM Save line history to file$
DEF PROC_SaveHistory(file$)
LOCAL temp%, buffer%
DIM buffer% LOCAL LENfile$+1
$buffer% = file$
temp% = FN_ReadLineF(buffer%, LENfile$+1, "~", &02000000)
ENDPROC
REM Clear line history
DEF PROC_ClearHistory
LOCAL temp%
temp% = FN_ReadLineF(0, 0, "~", &03000000)
ENDPROC
REM Dump line history
DEF PROC_DumpHistory
LOCAL temp%
temp% = FN_ReadLineF(0, 0, "~", &04000000)
ENDPROC
REM Return number of items in history
DEF FN_EnumerateHistory
=FN_ReadLineF(0, 0, "~", &05000000)
REM Return specified line from history
DEF FN_GetHistoryItem(line%)
=FN_ReadLineF(0, 0, "~", &06000000 + line%)
REM Set number of lines remembered in the history
DEF PROC_SetHistory(lines%)
LOCAL temp%
temp% = FN_ReadLineF(0, 0, "~", &07000000 + lines%)
ENDPROC
REM Append a line to the history buffer, as if it was the last line typed
DEF PROC_AddHistory(line$)
LOCAL temp%, buffer%
DIM buffer% LOCAL LENline$+1
$buffer% = line$
temp% = FN_ReadLineF(buffer%, LENline$+1, "~", &08000000)
ENDPROC
REM Remove history item specified by its position from the history
DEF PROC_RemoveHistoryItem(line%)
LOCAL temp%
temp% = FN_ReadLineF(0, 0, "~", &09000000 + line%)
ENDPROC
REM Replace history item specified by its position with the given line
DEF PROC_ReplaceHistoryItem(line%, line$)
LOCAL temp%, buffer%
DIM buffer% LOCAL LENline$+1
$buffer% = line$
temp% = FN_ReadLineF(buffer%, LENline$+1, "~", &0A000000 + line%)
ENDPROC
REM Move cursor
DEF PROC_MoveCursor(offset%, flag%)
IF SGNoffset% = -1 THEN
PROC_WRSTR(STRING$(ABSoffset%, CHR$8), flag%)
ELSE
PROC_WRSTR(STRING$(offset%, CHR$9), flag%)
ENDIF
ENDPROC
REM Switch foreground and background text colours
DEF PROC_InvertVideo
LOCAL fgc%,bgc%
fgc% = @vdu%?70
bgc% = @vdu%?71
COLOUR bgc%
COLOUR 128+fgc%
ENDPROC
REM Return TRUE if text cursor is enabled
DEF FN_CursorIsEnabled
=((@vdu%?68 AND 32) >> 5) = 0
REM Return TRUE if *ESCape is enabled
DEF FN_EscapeIsEnabled
=((@flags% AND &40000000) >> 30) = 0
REM Write character observing flags
DEF PROC_WRCH(char%, flag%)
IF (flag% AND &1FF) = 0 THEN ENDPROC
IF (flag% AND &1FF) = 2 THEN flag% = 256 + 42
IF char% > 31 AND (flag% AND &100) THEN
char% = flag% AND &FF
IF char% < 31 OR char% > 126 THEN char% = 42
ENDIF
IF char% = 31 THEN char% = 32
VDU char%
ENDPROC
REM Write multiple characters observing flags
DEF PROC_WRSTR(string$, flag%)
IF LENstring$ = 0 THEN ENDPROC
LOCAL i%
FOR i% = 1 TO LEN string$
PROC_WRCH(ASCMID$(string$, i%), flag%)
NEXT i%
ENDPROC