This is a module of EuroTool program EuroText for Linux. How does it work:
input.txtmay be in any UTF|ANSI|OEM encoding.
[InputPtr]..[InputEnd]./tmp/input.txt.lstto the output file, convert the input to UTF-8 in the output file, reopened output file and map it to
[OutputPtr]..[OutputEnd]./tmp/input.txt.indexto the index file, create one LST_INDEX record for each physical line in output file and store the record to index file. Indexes will be mapped to
[IndexPtr]..[IndexEnd].
EUROASM CPU=X64, Unicode=No, DumpWidth=36, Debug=no
textling PROGRAM Format=COFF, Width=64
%DROPMACRO *
INCLUDEHEAD argument.htm
INCLUDE1 linabi.htm, cpuext64.htm, cpuext.htm, linsfile.htm, linf64.htm, \
ansi.htm,string64.htm, memory64.htm, time.htm, status32.htm, debug.htm
[.rodata] segment.
[CodePoint] (WORD) and [Relevance] (BYTE).
[Translit] (DWORD) and [Entity] (QWORD) are not used in EuroText program.
[.rodata] ; Declare global symbol at the beginning of each section:
[CodePoint] ; Switch to section [CodePoint].
CodePoint: ; Label indicating the start of the section.
[Relevance] ; Switch to section [Relevance].
Relevance: ; Label indicating the start of the section.
; Declare macroinstruction UCP which interprets the lines of UnicodePoints and populates sections [CodePoint] and [Relevance].
UCP %MACRO CodePoint, Relevance, Translit, Entity
[CodePoint] ; Switch to section [CodePoint].
DW 0x%CodePoint ; Define code point (unsigned word), e. g. 0x0000, 0x0001,..0xFFFF.
[Relevance] ; Switch to section [Relevance].
DB %Relevance ; Define relevance of each code point (signed byte), e. g. Cc (-32), Nd (+16) etc.
%ENDMACRO UCP
INCLUDE unicode.htm ; Expand the macro UCP with each line of the table UnicodePoints.
[CodePoint]
CodePointEnd: ; Label indicating the end of the section.
"ISO-8859-2";
"Latin 2 (Central European)";
https://en.wikipedia.org/wiki/Windows-1250 (not used in this program);
[CPid].
[CPname] one after another..
[CPtable] one after another. The first six tables are omitted.
[.rodata] ; Declare symbol at the beginning of each section of segment [.rodata]
[CPid]
CPid:
[CPname]
CPname:
[CPtable]
CPtable:
; Declare macroinstruction CP which interprets the lines of CodePages and populates sections [CPid], [CPname] and [CPtable].
CP %MACRO CPid, CPname, CPaltName, CPurl, CPtable
[CPid] ; Switch to section [CPid].
DW %CPid ; Define encoding identifier, e. g. 437, 667,,,28606.
[CPname] ; Switch to section [CPname].
DB %CPname,0,%CPaltName,0 ; Join name and alternative name.
[CPtable] ; Switch to section [CPtable].
%IF "%CPtable" !=== "" ; The first six encodings don't have translation table, omit them.
i %FOR 1..128 ; Define 128 words of translation table for each encoding.
DW 0x%5 ; Member of CPtable, e. g. 00C7, 00FC etc.
%SHIFT 1 ; Take the next member of the table.
%ENDFOR i ; Repeat the definition 128 times.
%ENDIF
%ENDMACRO CP
INCLUDE codepage.htm ; Expand the macro CP with each line of the table CodePages.
[CPid]
CPidEnd: ; Declare symbol at the end of section [CPid]. Size of the section is CPidEnd-CPid.
LST_INDEX STRUC ; Strucure of the index records. .FA D DWORD ; Offset of the start of line from OutputPtr (file address). .Size D DWORD ; Size of the displayable line in bytes, plus Flags. ENDSTRUC LST_INDEX ; .Size does not exceed TermWidth. If FlagWrap is set, it may be less by 1 then TermWidth, ; because the last character may be a french quote signalizing the wrap. FlagWrap = 0x8000_0000 ; This line is word-wrapped; put french quote at TermWidth-1. FlagFound = 0x4000_0000 ; Searched string was found on this line. FlagLAfound = 0x3FF0_0000 ; Line address (0..765 characters) of searched string occurence in bytes which was found on this line. NoFlags = 0x000F_FFFF ; Bits 0..19 contain the actual line size in bytes. TabSpaces = 8 ; Number of spaces per one tabelator. MinTermHeight = 8 MinTermWidth = 16 MaxTermWidth = 800
[.data] ; Initialized data. TimeSpec1 DS TIMESPEC, .tv_sec=1, .tv_nsec=200_000_000 EuroText DB 'EuroText',0 Version DB " %^DATE",0 Help: DB 10 DB "EuroText is one of the EuroTool from https://euroassembler.eu/eurotool/",10 DB " which can be used to read any text file written in",10 DB " UTF-8, UTF-16, UTF-32 or in one of 75 OEM|ANSI encodings.",10 DB " Control characters are replaced with space.",10 DB "Specify ./eurotext.x -InputEncoding=? for the list of supported encodings.",10,10 DB "Program parameters may be specified on the command line, for instance",10 DB " ./eurotext.x InputFile.txt",10 DB "or they may be specified as command-line arguments:",10 DB " -InputFile=InputFile.txt",10 DB " -InputEncoding=UTF-8",10 DB " -WrapLines=no",10,10 DB "Program keys in pseudographic window:",10 DB " W",9,9,"wrap | unwrap",10 DB " F",9,9,"find a text in the document",10 DB " N",9,9,"find the next occurence",10 DB " P",9,9,"find the previous occurence",10 DB " Home",9,9,"beginning of the document",10 DB " End",9,9,"end of the document",10 DB " PgUp | PgDn",9,"page up | down",10 DB " Up|Dn|Mouse",9,"line up | down",10 DB " Esc",9,9,"close window, terminate program",10 DB 0 BOM_UTF32BE DB 0x00,0x00,0xFE,0xFF BOM_UTF32LE DB 0xFF,0xFE,0x00,0x00 BOM_UTF16BE DB 0xFE,0xFF BOM_UTF16LE DB 0xFF,0xFE BOM_UTF8 DB 0xEF,0xBB,0xBF MediumShadeX2 DB 0xE2,0x96,0x92,0xE2,0x96,0x92,0 FullBlockX2 DB 0xE2,0x96,0x88,0xE2,0x96,0x88,0 WrapSymbol DB 27,'[38;2;192;0;0m',0xC2,0xBB,27,'[38;2;0;0;0m',0 AnsiResize DB 27,'[8;' ValResize DB 'xxxxxxxxx' ; To be replaced with '8;16t',0 Spaces DB MaxTermWidth * BYTE ' '
[.bss] FileIni DS FILE64 FileInput DS FILE64 FileOutput DS FILE64 FileIndex DS FILE64 TtySize DS TTYSIZE TermIO DS TERMIO InputPtr D QWORD InputSize D QWORD InputEnd D QWORD OutputPtr D QWORD OutputSize D QWORD OutputEnd D QWORD IndexPtr D QWORD IndexEnd D QWORD CPnamePtr D QWORD ; Pointer to encoding name, e.g. "UTF-8",0. CPtablePtr D QWORD ; Pointer to 8-bit encoding table. TermWidth D DWORD ; Width of displayable window in characters. TermHeight D DWORD ; Height of displayable window in characters. CodePagesLength D DWORD ; Number of code pages (1+5+75). CodePointLength D DWORD ; Number of characters inunicode.htm(1227). ListPos D DWORD ; Ordinal number of index whose row is displayed on the top of terminal. ListMax D DWORD ; Number of index rows. It may be changed by switching wrap|unwrap. DocRow D DWORD ; Which row was clicked on by right mouse button. DocCol D DWORD ; Which column was clicked on by right mouse button. ThisRowWrap D DWORD ; 0 when nowrap, 1 when wrap. FindCursor D DWORD ; Offset of cursor from the beginning of edit field 0,1,2.... Find$Size D DWORD ; Size of the string to find in bytes. FindEditLen D DWORD ; Size of find-edit field in characters. FindEditWidth D DWORD ; Maximal size of visible editor field. FoundCnt D DWORD ; How many times was the string found. FindEdit D 256* BYTE ; Searched string being edited. Find$ D 256* BYTE ; Searched string final version, approved by Enter in Find dialogue. UTF-8. KeyBuffer D 32*BYTE WorkMemory D 800*BYTE ArgNr D DWORD
[.text]
Main:: PROC
StdOutput EuroText, Version, Eol=yes
; Prepare database sections.
LEA RSI,[CPid:]
LEA RCX,[CPidEnd:]
SUB RCX,RSI
SHR ECX,1
MOV [CodePagesLength],ECX
LEA RSI,[CodePoint:]
LEA RCX,[CodePointEnd:]
SUB RCX,RSI
SHR ECX,1
MOV [CodePointLength],ECX
; Try to load arguments from the configuration file "/etc/eurotool/eurotext.ini".
FileAssign FileIni,=B'/etc/eurotool/eurotext.ini'
FileExists? FileIni
JNC .10:
CALL ReadFileIni
.10:MOV EAX,[ArgNr] ; Read arguments from the command-line.
INC EAX
MOV [ArgNr],EAX
GetArg RAX, Frame=RSP ; Returns the line in RSI,RCX with one argument, e. g. -IF="~/file.obj"
JC .20: ; If there're no more arguments.
StripQuotes RSI,RCX
CALL ArgParse:: ; Use ArgParse to translate it to a public symbol Arg***.
JNC .10:
StdOutput ErrorMessage::
JMP ErrorHelp:
.20:LEA RSI,[ArgInputFile::]
CMPB [RSI],0
JZ ErrorHelp:
LEA RBX,[FileInput]
FileAssign RBX,RSI
FileExists? RBX
JNC .30:
LEA RSI,[RBX+FILE64.Name]
StdOutput =B"File """,RSI,=B""" was not found.",Eol=yes
TerminateProgram 4
.30:; Input file was specified and exists.
FileLoad RBX
JC ErrorFileInput:
MOV [InputPtr],RSI
MOV [InputSize],RAX
ADD RAX,RSI
MOV [InputEnd],RAX
MOV EAX,[ArgInputEncoding::]
TEST EAX
JNZ .40:
CALL AutodetectEncoding
MOV [ArgInputEncoding::],EAX
.40:LEA RDI,[CPid] ; Array of WORD encoding numbers: 20127, 65001, 1200, 1201 etc.
MOV RSI,RDI
MOV ECX,[CodePagesLength]
REPNE SCASW
SUB RDI,RSI
SAR EDI,1
MOV EDX,EDI ; EDX=1 (ASCII), 2 (UTF-8), 3 (UTF-16LE), ,,
DEC EDI
SAL EDI,8
LEA RSI,[CPtable-6*256+RDI] ; RSI=address of 8-bit translation table.
MOV [CPtablePtr],RSI
LEA RDI,[CPname]
LEA RCX,[CPtable]
SUB RCX,RDI
XOR EAX,EAX
.50:DEC EDX
JZ .60:
REPNE SCASB ; Skip CPname.
REPNE SCASB ; Skip CPaltName.
JMP .50:
.60:MOV [CPnamePtr],RDI
CALL CreateOutputFile
CALL WindowResize
CALL MakeIndex
CALL WindowPaint
CALL WindowProc
FileClose FileInput
JSt [Status::],ArgLeaveTemporary,.90:
FileClose FileOutput,FileIndex
.90:TerminateProgram
RET
ErrorHelp:
StdOutput Help:,Eol=yes
TerminateProgram 8
ErrorFileOutput:
LEA RSI,[FileOutput.Name]
JMP ErrorFile:
ErrorFileInput:
LEA RSI,[FileInput.Name]
JMP ErrorFile:
ErrorFileIndex:
LEA RSI,[FileIndex.Name]
ErrorFile:
StdOutput =B'Error in file "',RSI,=B'"',Eol=yes
JMP .Terminate:
ErrorAlloc::
StdOutput =B"Error on memory allocation.",Eol=yes
.Terminate: TerminateProgram 8
ENDP Main
FileIniin UTF-8 and parses its each line into configuration variables Arg*** in UTF-8, too.
ReadFileIni PROC
SetSt [Status::],ArgFromFile ; Tell ArgParse that arguments may not begin with / or -.
LEA RDI,[WorkMemory]
Concat$ RDI, RDI,=B'Configuration "',FileIni.Name,=B'"'
.10:FileStreamOpen FileIni,BufSize=4K
JNC .20:
Concat$ RDI,RDI,=B'" was not found.',=B(10)
JMP .90:
.20:FileStreamReadLn FileIni
JBE .80:
MOV ECX,EAX
; The first line may begin with BOM.
MOV AX,[RSI]
CMPW AX,0xBBEF ; UTF-8 BOM?
JNE .50:
ADD RSI,3 ; Skip the BOM.
SUB ECX,3
JB .80:
JMP .50:
.30:FileStreamReadLn FileIni
JBE .80:
MOV ECX,EAX
.50:CALL ArgParse::
JNC .30:
StdOutput ErrorMessage::, Help
TerminateProgram 8
.80:FileClose FileIni
LEA RDI,[WorkMemory]
Concat$ RDI,RDI,=B'" was accepted.',=B(10)
.90:RstSt [Status::],ArgFromFile
RET
ENDP ReadFileIni
AutodetectEncoding PROC
MOV R8,[InputPtr]
MOV R9,[InputSize]
MOV R12,65001 ; UTF-8.
CMP R9D,16
JB .90: ; Shorter files cannot be autodetected.
CMP R9D,256K
JB .10:
MOV R9D,256K
.10:MOV R10,0x8000_0000_0000_0000 ; R10 is the best relevance saldo so far. R12 is its code page.
XOR R13,R13 ; R13 is 0,1,2 corresponding with character size 1,2,4.
; Try encoding 20127 ASCII.
SUB R11,R11 ; R11=relevance saldo.
MOV RSI,R8 ; Restore pointer to text from R8.
MOV RCX,R9 ; Restore size of text from R9.
XOR EAX,EAX
.15:LODSB
CMP AL,0x7F
JA .20:
CALL .StoreRelevance:
JMPS .22:
.20:ADD R11,?? ; Any byte above 0x7F deteriorates the relevance by ??=-128.
.22:LOOP .15:
CMP R11,R10 ; Compare with the best saldo so far in R10.
JLE .30:
MOV R12,20127
MOV R10,R11
; Try encoding 12000 UTF-32LE.
MOV R13B,2 ; Character size=4.
SUB R11,R11 ; R11=relevance saldo.
MOV RSI,R8 ; Restore pointer to text from R8.
MOV RCX,R9 ; Restore size of text from R9.
SHR ECX,2
MOV EAX,[BOM_UTF32LE]
CMP EAX,[RSI]
JNE .25:
ADD R11,Bm
ADD RSI,4
DEC ECX
.25:LODSD
CALL .StoreRelevance:
LOOP .25:
CMP R11,R10 ; Compare with the best saldo so far in R10.
JLE .30:
MOV R12,12000
MOV R10,R11
.30: ; Try encoding 12001 UTF-32BE.
SUB R11,R11 ; R11=relevance saldo.
MOV RSI,R8 ; Restore pointer to text from R8.
MOV RCX,R9 ; Restore size of text from R9.
SHR ECX,2
MOV EAX,[BOM_UTF32BE]
CMP EAX,[RSI]
JNE .35:
ADD R11,Bm
ADD RSI,4
DEC ECX
.35:LODSD
BSWAP EAX
CALL .StoreRelevance:
LOOP .35:
CMP R11,R10 ; Compare with the best saldo so far in R10.
JLE .40:
MOV R12,12001
MOV R10,R11
.40: ; Try encoding 1200 UTF-16LE.
MOV R13B,1 ; Character size=2.
SUB R11,R11 ; R11=relevance saldo.
MOV RSI,R8 ; Restore pointer to text from R8.
MOV RCX,R9 ; Restore size of text from R9.
SHR ECX,1
XOR EAX,EAX
MOV AX,[BOM_UTF16LE]
CMP AX,[RSI]
JNE .45:
ADD R11,Bm
ADD RSI,2
DEC ECX
.45:LODSW
CALL .StoreRelevance:
LOOP .45:
CMP R11,R10 ; Compare with the best saldo so far in R10.
JLE .50:
MOV R12,1200
MOV R10,R11
.50: ; Try encoding 1201 UTF-16BE.
SUB R11,R11 ; R11=relevance saldo.
MOV RSI,R8 ; Restore pointer to text from R8.
MOV RCX,R9 ; Restore size of text from R9.
SHR ECX,1
XOR EAX,EAX
MOV AX,[BOM_UTF16BE]
CMP AX,[RSI]
JNE .55:
ADD R11,Bm
ADD RSI,2
DEC ECX
.55:LODSW
XCHG AL,AH
CALL .StoreRelevance:
LOOP .55:
CMP R11,R10 ; Compare with the best saldo so far in R10.
JLE .60:
MOV R12,1201
MOV R10,R11
.60: ; Try encoding 65001 UTF-8.
SUB R11,R11 ; R11=relevance saldo.
MOV RSI,R8 ; Restore pointer to text from R8.
MOV RCX,R9 ; Restore size of text from R9.
MOV EAX,[BOM_UTF8]
MOV EDX,[RSI]
AND EAX,0x00FFFFFF
AND EDX,0x00FFFFFF
CMP EAX,EDX
JNE .65:
ADD R11,Bm
ADD RSI,3
SUB RCX,3
.65:DecodeUTF8 RSI,.StoreRelevanceUTF8,Size=RCX,Width=32 ; Use macro from string64.htm.
CMP R11,R10 ; Compare with the best saldo so far in R10.
JLE .70:
MOV R12,65001
MOV R10,R11
.70: ; Try 8bit encoding OEM or WIDE according to CodePages 437..28606.
XOR EDX,EDX ; RDX is index to [CPtable]. 0x0000="IBM437",0x0100="Mazovia", 0x0200="IBM737", 0x0300="IBM737",,,0x7400="ISO-8859-16"
XOR R13,R13 ; Character size=1.
.72:LEA RBX,[RDX+CPtable:]
SUB R11,R11 ; R11=relevance saldo.
; ; A hack to prefer Kamenicky over KOI8-CS.
; CMP DH,0x1C ; "KOI8-CS"?
; JNE .74:
; SUB R11,9999
;.74:CMP DH,0x1D ; "Kamenicky"
; JNE .76:
; ADD R11,9999
;.76:
MOV RSI,R8 ; Restore pointer to text from R8.
MOV RCX,R9 ; Restore size of text from R9.
.78:XOR EAX,EAX
LODSB
CMP AL,0x7F
JBE .80:
MOV AX,[RBX+2*RAX-256] ; Translate AL (128..255) to unicode point in AX by this table.
.80:CALL .StoreRelevance: ; Add relevance of unicode point EAX to R11.
LOOP .78: ; The next character from the sample.
CMP R11,R10 ; Compare with the best saldo so far in R10.
JLE .85: ; Skip when poor.
MOV R10,R11 ; R10 is the best saldo so far.
LEA RCX,[CPid:]
MOVZX EAX,DH
MOVZXW R12,[RCX+2*RAX+2*6]; R12 is the best encoding so far.
.85:INC DH ; Try the next encoding.
LEA EAX,[EDX+256*6]
CMP AH,[CodePagesLength] ; Each OEM/ANSI table is 2*128 bytes long.
JB .72:
MOV EAX,20127 ; ASCII.
TEST R10 ; Best saldo.
JS .90: ; If negative, it is not a text file. Simulate ASCII.
MOV RAX,R12 ; Autodetected encoding is returned in RAX.
.90:RET
.StoreRelevanceUTF8: PROC ; This subprocedure will add relevance of a character decoded from UTF-8
XOR R13,R13 ; with codepoint RAX to the saldo in R11. Clobbers: RAX,RDI,R13.
CMP EAX,80h
JB .4:
INC R13
CMP EAX,800h
JB .4:
INC R13
.4: ; Continue with .StoreRelevance:. R13=0,1,2.
ENDP .StoreRelevanceUTF8:
.StoreRelevance: PROC ; This subprocedure will add relevance of a character
PUSH RCX,RSI ; with codepoint EAX to the saldo in R11. Clobbers: RAX,RDI.
XOR ESI,ESI
CMP EAX,0x0000_FFFF
JA .9: ; Characters above BMP (asian, emojis) do not influence the relevance.
LEA RDI,[CodePoint]
LEA RSI,[RDI+2]
MOV ECX,[CodePointLength]
REPNE SCASW
JE .6:
MOV RSI,?? ; Deteriorate the relevance when this character is not in our Unicode table.
JMP .8:
.6: SUB RDI,RSI ; Otherwise find the corresponding relevance.
LEA RSI,[Relevance:]
SHR RDI,1
MOVSXB RSI,[RSI+RDI]
.8: MOV RCX,R13
SAL RSI,CL ; Double (UTF-16) or quadruple (UTF-32) the relevance.
ADD R11,RSI ; Add it to the saldo R11.
.9: POP RSI,RCX
CLC
RET
ENDP .StoreRelevance:
ENDP AutodetectEncoding
/tmp/InputFile.lst.
CreateOutputFile PROC
LEA RSI,[ArgOutputFile::]
LEA RBX,[FileOutput]
CMPB [RSI],0
JZ .10:
FileAssign RBX,RSI
JMP .20:
.10:MOV EAX,[FileInput.NameOffs]
LEA RSI,[FileInput.Name]
ADD RSI,RAX
FileAssign RBX,=B'/tmp/',RSI,=B'.lst'
.20:FileMkDir RBX
JC ErrorFileOutput:
FileStreamCreate RBX
JC ErrorFileOutput:
; Convert the input to UTF-8 and store it to FileOutput.
MOV RSI,[InputPtr]
MOV RCX,[InputSize]
LEA RDX,[WorkMemory]
MOV EAX,[ArgInputEncoding::]
Dispatch AX, 65001, 20127, 1200, 1201, 12000, 12001 ; Input encodings UTF or ASCII.
MOV RBX,[CPtablePtr]
LEA RDX,[WorkMemory]
XOR EAX,EAX
MOV RSI,[InputPtr]
.50:CMP RSI,[InputEnd]
JNB .90:
LODSB
CMP AL,0x80
JAE .60:
FileStreamWriteByte FileOutput
JC ErrorFileOutput
JMP .50:
.60:MOV AX,[RBX+2*RAX-256]
MOV RDI,RDX
.70:EncodeUTF8 ; Encode EAX to UTF-8 and store it to RDI.
SUB RDI,RDX
FileStreamWrite FileOutput,RDX,RDI
JC ErrorFileOutput
JMP .50:
.EncodeEAX: ; Subprocedure to encode codepoint EAX to UTF-8 and store the character to RDI.
MOV RDI,RDX ; Input: RDX point to an ampty string in memory.
EncodeUTF8 ; Encode EAX to UTF-8 and store it to RDI.
SUB RDI,RDX ; Get size of UTF-8 character (1..4 bytes).
FileStreamWrite FileOutput,RDX,RDI
JC ErrorFileOutput
RET
.20127: ; ASCII.
.65001: ; UTF-8.
FileStreamWrite FileOutput,RSI,RCX ; Store the entire file UTF-8 or ASCII as is.
JC ErrorFileOutput
JMP .90
.12001: ; UTF-32BE.
SAR ECX,2
JZ .90:
.12001L:
LODSD
BSWAP EAX
CALL .EncodeEAX:
LOOP .12001L:
JMP .90:
.12000: ; UTF-32LE.
SAR ECX,2
JZ .90:
.12000L:
LODSD
CALL .EncodeEAX:
LOOP .12000L:
JMP .90:
.1201: ; UTF-16BE.
SAR ECX,1
JZ .90:
.1201L:
LODSW
XCHG AH,AL
CMP AX,0xD800
JB .1201X:
CMP AX,0xDBFF
JA .1201X:
SUB EAX,0xD800 ; EAX is the high surrogate.
SHL EAX,10
MOV EDI,EAX
LODSW ; The low surrogate expected.
XCHG AH,AL
CMP AX,0xDC00
JB .1201X:
CMP AX,0xDFFF
JA .1201X:
SUB EAX,0xDC00
LEA EAX,[RDI+RAX+0x1_0000]
.1201X:
CALL .EncodeEAX:
LOOP .1201L:
JMP .90:
.1200: ; UTF-16LE.
SAR ECX,1
JZ .90:
.1200L:
LODSW
CMP AX,0xD800
JB .1200X:
CMP AX,0xDBFF
JA .1200X:
SUB EAX,0xD800 ; EAX is the high surrogate.
SHL EAX,10
MOV EDI,EAX
LODSW ; The low surrogate expected.
CMP AX,0xDC00
JB .1200X:
CMP AX,0xDFFF
JA .1200X:
SUB EAX,0xDC00
LEA EAX,[RDI+RAX+0x1_0000]
.1200X:
CALL .EncodeEAX:
LOOP .1200L:
; JMP .90:
.90:FileClose FileInput, FileOutput
FileLoad FileOutput
JC ErrorFileOutput
MOV [OutputPtr],RSI
MOV [OutputSize],RAX
ADD RAX,RSI
MOV [OutputEnd],RAX
RET
ENDP CreateOutputFile
MakeIndex PROC
LEA RBX,[FileIndex]
FileClose RBX ; If it was created before.
LEA RSI,[FileOutput.Name]
FileAssign RBX,RSI,=B'.index'
FileStreamCreate RBX
JC ErrorFileIndex: ; Abort on error.
MOV R14,[OutputPtr] ; R14 points at the start of output file in memory.
MOV R15,[OutputEnd] ; R15 points at the end of output file in memory.
MOV R13D,[TermWidth] ; Current terminal window width in bytes.
MOV EAX,3
JSt [Status::],ArgWrapLines,.10:
DEC EAX
.10:SUB R13,RAX ; R13 is TermWidth-2 or TermWidth-3.
; Virtualy copy the index of source (FileOutput) to destination (FileIndex).
XOR EAX,EAX
; FileStreamWriteDword RBX ; Store 0 DWORD from EAX.
; FileStreamWriteDword RBX ; Store 0-th index as an empty line first.
MOV RSI,R14
.30:CMP RSI,R15 ; OutputEnd reached?
JNB .70:
MOV RAX,RSI
SUB RAX,R14 ; RAX is now FA of this line.
FileStreamWriteDword RBX ; Write LST_INDEX.FA
JC ErrorFileIndex
XOR EDX,EDX ; Counter of displayed characters in destination (0..TermWidth-2).
MOV RDI,RSI ; Pointer to the start of this source line.
.40:LODSB ; Read one character after another.
Dispatch AL,0x0A, 0x08, 0x09 ; Other controls will be replaced with space (1 byte).
INC EDX ; Count one character (ASCII or UTF-8).
CMP AL,0x80
JB .50: ; Jump when it's ASCII character 1 byte width.
OR EAX,0xFFFF_FF00 ; Otherwise it's UTF-8 character 2-4 bytes width.
NEG EAX
BSR ECX,EAX ; ECX=5,4,3 for 2,3,4 bytes of UTF-8 character.
CMP ECX,5
JA .40: ; Wrong UTF-8 character, ignore.
MOV EAX,6
SUB EAX,ECX
ADD RSI,RAX ; RAX are remaining UTF-8 bytes (1,2,3).
.50:CMP RDX,R13 ; Out of terminal columns?
JB .40:
JNSt [Status::],ArgWrapLines, .0x0A:
; This line will be marked FlagWrap in wrap mode because on display it's longer than R13.
MOV RAX,RSI
SUB RAX,RDI ; Subtract the start of this line (RDI).
OR EAX,FlagWrap
FileStreamWriteDword RBX ; LST_INDEX.Size
JC ErrorFileIndex
JMP .30:
.0x0A: ; Skip the rest of the line in nonwrap mode.
PUSH RAX
MOV RAX,RSI
SUB RAX,RDI ; Subtract the start of this line (RDI).
FileStreamWriteDword RBX ; LST_INDEX.Size
JC ErrorFileIndex
POP RAX
.60:CMP RSI,R15 ; OutputEnd?
JNB .70:
CMP AL,0x0A
JE .30:
LODSB
JMP .60: ; Skip until EOL.
.0x08:
CMP EDX,1
JNA .40:
DEC EDX
JMP .40:
.0x09:
ADD EDX,TabSpaces
JMP .50:
.70:XOR EAX,EAX
FileStreamWriteDword RBX ; Store the last index as empty.
FileStreamWriteDword RBX
.80 FileClose RBX
FileLoad RBX ; Reopen and load the index.
JC .90:
ADD RAX,RSI
MOV [IndexPtr],RSI
MOV [IndexEnd],RAX
SUB RAX,RSI
SHR EAX,3 ; SIZE# LST_INDEX=8
MOV [ListMax],EAX
.90:RET
ENDP MakeIndex
WindowResize PROC
LinABI ioctl,0,TIOCGWINSZ,TtySize
MOVZXW EDX,[TtySize.ts_lines]
MOVZXW ECX,[TtySize.ts_cols]
MOV EAX,MinTermHeight ; Minimal acceptable lines.
CMP EDX,EAX
JB .10:
MOV EAX,EDX
.10:LEA RDI,[ValResize]
StoD RDI
MOV AL,';'
STOSB
MOV EAX,MinTermWidth ; Minimal acceptable columns.
CMP ECX,EAX
JB .20:
MOV EAX,ECX
.20:MOV EDX,MaxTermWidth
CMP EAX,EDX
JB .30:
MOV EAX,EDX
.30:StoD RDI
MOV AX,'t'
STOSW
StdOutput AnsiResize
LinABI ioctl,0,TIOCGWINSZ,TtySize
MOVZXW EDX,[TtySize.ts_lines]
MOV [TermHeight],EDX
MOVZXW ECX,[TtySize.ts_cols]
MOV [TermWidth],ECX
RET
ENDP WindowResize
EuroText "input.txt"{0}[0], UTF-8, nowrap, found 0× "searched".
WindowTitle PROC
LEA RDI,[WorkMemory::] ; The title is constructed in WorkMemory.
MOV RDX,RDI
MOV AX,' '
STOSW
LEA RSI,[EuroText]
MOV ECX,8
REP MOVSB
MOV AX,' "'
STOSW
LEA RSI,[ArgInputFile::] ; Input file name in UTF-8.
GetLengthUTF8 RSI
REP MOVSB
MOV AX,'"{'
STOSW
MOV EAX,[DocRow]
StoD RDI
MOV AX,'}['
STOSW
MOV EAX,[DocCol]
StoD RDI
MOV EAX,'], '
STOSD
DEC RDI
MOV RSI,[CPnamePtr]
TEST RSI
JZ .20:
GetLength$ RSI
REP MOVSB
MOV AX,', '
.20:JNSt [Status::],ArgWrapLines,.30:
STOSW
JMPS .40:
.30:MOV EAX,', no'
STOSD
.40:MOV EAX,'wrap'
STOSD
MOV EAX,', fo'
STOSD
MOV EAX,'und '
STOSD
MOV EAX,[FoundCnt]
StoD RDI
MOV EAX,0x222097C3
STOSD
LEA RSI,[Find$]
GetLength$ RSI
MOV [Find$Size],ECX
REP MOVSB
MOV AX,'"'
STOSW
StdOutput AnsiClrBgFFFFFF, AnsiClrFg800000, AnsiKeyHome
GetLength$ RDX
MOV ESI,[TermWidth]
CMP ECX,ESI
JBE .60:
StdOutput RDX,Size=RSI
JMP .90:
.60:StdOutput RDX,Size=RCX
SUB ESI,ECX
StdOutput Spaces,Size=RSI
.90:RET
ENDP WindowTitle
ListMax rows of the listing file (converted to UTF-8 in FileOutput).
The following example shows the listing with ListMax=22 rows,
viewed by the terminal window with TermHeight=11 rows.
First line in terminal (title row) is fixed, it shows the program name, file name,
file coordinates {row}[column], encoding, wrap status, searched string.
Vertical position of the displayed part of listing in the terminal window is specified by
ListPos, that is the ordinal number of this listing row, which is displayed
in terminal row 1 (just below the title row).
Cursor keys and mouse wheel manipulate only with this variable ListPos, it is saturated to
0..ListMax.
The last two columns in terminal window are dedicated to scroll box. The height of its slider is
ScrollHeight=(TermHeight-1)*(TermHeight-1)/ListMax=10*10/22=4 saturated to 1..TermHeight-1=1..10.
Position of the topmost character of the slider is
ScrollPos=(TermHeight-1)*(ListPos)/(ListMax)+1=10*7/22+1=4, saturated to
1..TermHeight-1=1..10.
WindowPaint PROC
MOV R15D,[TermWidth]
DEC R15 ; R15=TermWidth-1 character for the scroll column.
MOV R12D,1 ; R12=row number: 2,3,4,,,TermHeight
CALL WindowTitle ; Display the topmost line with title.
StdOutput AnsiClrFg000000,AnsiClrBgFFFFD0 ; Standard color (black on yellowish).
MOV EAX,[ListPos] ; Ordinal number of ListRow which is displayed in TermRow 1 (0,1,2,3,,,ListMax)
CMP EAX,[ListMax] ; Check its saturation.
JBE .15:
MOV EAX,[ListMax]
MOV [ListPos],EAX ; Saturate ListPos.
.15:INC R12 ; Current row in terminal (2..TermHeight).
CMP R12,[TermHeight]
JA .45: ; Jump when TermHeight lines have been displayed.
LEA RDI,[AnsiAtRowCol+2]
MOV RAX,R12 ; R12=TermRow number: 2,3,4,,,TermHeight
StoD RDI ; Store row number 2,3,4,,,TermHeight to ANSI code.
MOV EAX,';1H'
STOSD
StdOutput AnsiAtRowCol ; Place the cursor at TermRow R12, column 1.
LEA RDI,[WorkMemory] ; The line is constructed here.
MOV R8,RDI ; R8=Second cache of RDI.
MOV R9,RDI ; R9=First cache of RDI.
MOV EAX,[ListPos] ; 0=empty row; 1=first row, 2=second row .. ListMax.
LEA RAX,[RAX+R12-2] ; R12=TermRow number: 2,3,4,,,TermHeight
CMP EAX,[ListMax]
JA .0x0A:
MOV RBX,[IndexPtr]
LEA RBX,[RBX+8*RAX]
CMP RBX,[IndexEnd]
JAE .0x0A:
MOV ESI,[RBX+LST_INDEX.FA] ; RBX is index of current line.
MOV ECX,[RBX+LST_INDEX.Size]
ADD RSI,[OutputPtr] ; Let RSI be pointer to the first character of current line in output text.
MOV EAX,ECX
SHR EAX,31
MOV [ThisRowWrap],EAX ; 0 or 1.
AND ECX,NoFlags
LEA R13,[RSI+RCX] ; R13=pointer to the end of (wrapped) line.
XOR EDX,EDX ; Counter of characters in this row.
.20:StdOutput AnsiClrFg000000,AnsiClrBgFFFFD0 ; Restore standard color.
CMP RSI,R13 ; Loop which will display one row from RSI..R13.
JAE .0x0A:
LODSB ; RSI points at the UTF-8 characters mapped in memory.
MOV R9,R8 ; R9 is previous cache.
MOV R8,RDI ; R8 is current cache (for the case of backspace).
Dispatch AL,0x0A,0x08,0x09 ; Controls except for those three will be replaced with space.
CMP AL,0x20
JAE .25:
MOV AL,0x20 ; Replace other controls with space.
.25:INC EDX ; Count one character.
STOSB ; The first byte of UTF-8 character.
CMP AL,0x80
JB .30: ; Jump if it was ASCII character.
OR EAX,0xFFFF_FF00
NEG EAX
BSR ECX,EAX ; ECX=5,4,3 for 2,3,4 bytes of UTF-8 character.
CMP ECX,5
JA .20:
MOV EAX,6
SUB EAX,ECX ; EAX=1,2,3 (remaining bytes of UTF-8 character).
MOV ECX,EAX
REP MOVSB ; Copy the remaining bytes.
.30:CMP RDX,R15 ; Out of terminal width?
JBE .20:
.0x0A: ; End of line. Complete the line at RDI with spaces, if necessary.
JNSt [ThisRowWrap],1, .35:
PUSH RSI
LEA RSI,[WrapSymbol]
MOV ECX,SIZE# WrapSymbol
REP MOVSB ; Store RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK alias french quote.
POP RSI
.35:LEA RCX,[R15-1] ; TermWidth-2 - the last displayed column.
SUB ECX,EDX ; Subtract the displayed characters EDX.
JBE .40:
MOV AL,' '
REP STOSB
.40:LEA RAX,[WorkMemory]
SUB RDI,RAX
StdOutput RAX,Size=RDI ; Display the current line.
MOV ECX,[RBX+LST_INDEX.Size]
JNSt ECX,FlagFound,.44:
AND ECX,FlagLAfound ; Searched string was found on this line.
SHR ECX,20 ; ECX is line address of found string as byte-offset (0..TermWidth) from the start of line.
LEA RSI,[WorkMemory]
ADD RCX,RSI ; Count how many characters are in front of the searched string.
XOR EDX,EDX ; Character counter.
.41:CMP RSI,RCX
JAE .43:
INC EDX
LODSB
CMP AL,0x80
JB .41:
PUSH RCX
OR EAX,0xFFFF_FF00 ; AL is UTF-8 character 2,3,4 bytes width.
NEG EAX
BSR ECX,EAX ; ECX=5,4,3 for 2,3,4 bytes of UTF-8 character.
MOV EAX,6
SUB EAX,ECX
ADD RSI,RAX ; RAX are remaining UTF-8 bytes (1,2,3).
POP RCX
JMP .41:
.43:LEA RDI,[AnsiAtCol+2]
LEA RAX,[RDX+1] ; RCX is the size in front of displayed string in bytes, RDX in characters.
StoD RDI
MOV AX,0x0047 ; G
STOSW
StdOutput AnsiAtCol,AnsiClrBgFFFFFF,AnsiClrFgFF00FF ; Magenta on white.
MOV EAX,[Find$Size]
StdOutput RSI,Size=RAX ; Rewrite the found string with emphasised color.
StdOutput AnsiClrFg000000,AnsiClrBgFFFFD0 ; Restore standard color.
.44:CMP R12D,[TermHeight]
JB .15:
.45:; Scroll box.
MOV EDI,[TermHeight] ; Calculate ScrollHeight.
DEC EDI
MOV EAX,EDI
MUL RAX
MOV ECX,[ListMax]
TEST ECX
JZ .90:
DIV RCX
CMP EAX,0 ; Saturate ScrollHeight.
JA .50:
MOV EAX,1
.50:CMP EAX,EDI
JNA .55:
MOV EAX,EDI
.55:MOV ESI,EAX ; Let ESI=ScrollHeight.
MOV EAX,[ListPos] ; Calculate ScrollPos.
MUL RDI ; RDI=TermHeight-1.
MOV ECX,[ListMax]
DIV RCX
INC EAX
INC EDI
CMP EAX,EDI ; Saturate ScrollPos.
JNA .60:
MOV EAX,EDI
.60:MOV EDX,EAX ; EDX=ScrollPos, ESI=ScrollHeight.
LEA RSI,[RSI+RDX+1] ; ESI=position below the slider.
StdOutput AnsiClrBgFEFEFF, AnsiClrFg000080
MOV R12D,1 ; R12=row number: 2,3,4,,,TermHeight
.70:INC R12
MOV RAX,R12 ; R12=row number: 2,3,4,,,TermHeight
CMP EAX,[TermHeight]
JA .90:
LEA RDI,[AnsiAtRowCol+2]
StoD RDI ; Store row number 2,3,4,,,TermHeight
MOV AX,';'
STOSB
MOV RAX,R15 ; TermWidth-1.
StoD RDI
MOV AX,'H'
STOSW
StdOutput AnsiAtRowCol ; Place the cursor at row R12, col=TermWidth-2.
CMP R12,RDX
JB .80:
CMP R12,RSI
JAE .80:
StdOutput FullBlockX2
JMP .70:
.80:StdOutput MediumShadeX2
JMP .70:
.0x08: ; Backspace.
SUB EDX,2
MOV RDI,R9 ; Restore position from previous cache R9.
JMP .20:
.0x09: ; Tabelator.
MOV ECX,TabSpaces
ADD EDX,ECX
MOV AL,' '
REP STOSB
JMP .20:
.90:RET
ENDP WindowPaint
ListPos.
WindowProc PROC
LEA RBX,[TermIO]
LinABI ioctl,0,TCGETS,RBX ; Load current local terminal status.
RstSt [RBX+TERMIO.c_lflag],ICANON+ECHO
LinABI ioctl,0,TCSETS,RBX ; Disable terminal echo.
StdOutput AnsiMouseEnable
JMP .ReadKeyboard:
.NoMore:
; Paint double frame around NoMore box:
StdOutput AnsiClrBgFFFFFF,AnsiClrFgFF00FF,AnsiRow4,AnsiCol4,AnsiBox2DR
MOV ECX,36
.M2:StdOutput AnsiBox2H
LOOP .M2:
StdOutput AnsiBox2DL,AnsiRow5,AnsiCol4,AnsiBox2V
StdOutput =B' Next|Previous occurence not found. ' ; NoMore text.
StdOutput AnsiBox2V,AnsiRow6,AnsiCol4,AnsiBox2UR
MOV ECX,36
.M3:StdOutput AnsiBox2H
LOOP .M3:
StdOutput AnsiBox2UL
LinABI nanosleep, TimeSpec1,0
JMP .Saturate:
.Resized:
CALL WindowResize
.Paint:
CALL MakeIndex
CALL WindowPaint
MOV EBX,[ListPos]
JMP .Saturate:
.AnsiKeyCtrlF:
.AnsiKeyf:
.AnsiKeyF: ; Find a text string in the output file. Position it at the 1st occurence, if found.
CALL FindWindowPaint
CALL FindEditor ; Get string Find$.
JC .Paint: ; CF=cancel. Repaint screen; do not search.
CALL Search ; Mark LST_INDEX.Size with FlagFound and FlagLAfound.
CALL WindowTitle ; Write FoundCnt number to window title.
MOV RSI,[IndexPtr]
MOV EBX,[ListPos]
SUB RSI,SIZE# LST_INDEX
DEC EBX
.FF:INC EBX
ADD RSI,SIZE# LST_INDEX
CMP RSI,[IndexEnd]
JNB .NoMore: ; No more found.
TEST [RSI+LST_INDEX.Size],FlagFound
JZ .FF:
JMP .Saturate:
.AnsiKeyCtrlN:
.AnsiKeyn:
.AnsiKeyN: ; Find the next occurence.
MOV RSI,[IndexPtr]
MOV RDX,[IndexEnd] ; End of the array
MOV EBX,[ListPos]
LEA RSI,[RSI+8*RBX]
.NF:INC EBX
ADD RSI,SIZE#LST_INDEX
CMP RSI,RDX
JNB .NoMore: ; No more found.
JNSt [RSI+LST_INDEX.Size],FlagFound,.NF:
JMP .Saturate:
.AnsiKeyCtrlP:
.AnsiKeyp:
.AnsiKeyP: ; Find the previous occurence.
MOV RSI,[IndexPtr]
MOV RDX,RSI ; Begining of the array.
MOV EBX,[ListPos]
LEA RSI,[RSI+8*RBX]
.PF:DEC EBX
JZ .NoMore:
SUB RSI,SIZE#LST_INDEX
CMP RSI,RDX
JB .NoMore:
JNSt [RSI+LST_INDEX.Size],FlagFound,.PF:
JMP .Saturate:
.ReadKeyboard:
LinABI ioctl,0,TIOCGWINSZ,TtySize ; Check if terminal dimension changed.
MOVZXW EDX,[TtySize.ts_lines]
MOVZXW ECX,[TtySize.ts_cols]
CMP EDX,[TermHeight]
JNE .Resized:
CMP ECX,[TermWidth]
JNE .Resized:
XOR EDI,EDI ; Read the keyboard in the main window.
LEA RSI,[KeyBuffer]
MOV R8,RSI
MOV [RSI],RDI ; Pre-clear the keyboard buffer.
MOV EDX,SIZE# KeyBuffer
LinABI read,RDI,RSI,RDX
TEST EAX
JNA .ReadKeyboard:
XOR ECX,ECX
MOV EBX,[ListPos]
key %FOR AnsiKeyUp,AnsiKeyDn,AnsiKeyPgUp,AnsiKeyPgDn,AnsiKeyHome,AnsiKeyEnd, \
AnsiKeyCtrlHome,AnsiKeyCtrlEnd,AnsiKeyCtrlPgUp,AnsiKeyCtrlPgDn, \
AnsiKeyCtrlUp,AnsiKeyCtrlDn,AnsiKeyAltUp,AnsiKeyAltDn, \
AnsiKeyShiftUp,AnsiKeyShiftDn,AnsiKeyShiftTab,AnsiKeyTab, \
AnsiKeyW,AnsiKeyw,AnsiKeyAltW,AnsiKeyAltw,AnsiKeyCtrlW, \
AnsiKeyF,AnsiKeyf,AnsiKeyN,AnsiKeyn,AnsiKeyP,AnsiKeyp,AnsiKeyEscape
LEA RDI,[%key]
MOV CL,SIZE# %key
MOV RSI,R8
REPE CMPSB
JE .%key:
%ENDFOR key
MOV RSI,R8 ; Read the mouse.
LODSW
CMP AX,0x5B1B ; Esc [
JNE .ReadKeyboard:
LODSB
CMP AL,0x3C ; Less-than character?
JNE .ReadKeyboard:
LodD ; Mouse button.
JC .ReadKeyboard:
CMP AL,64 ; Scroll wheel up?
JE .AnsiKeyCtrlUp:
CMP AL,65 ; Scroll wheel down?
JE .AnsiKeyCtrlDn:
MOV ECX,EAX ; Not scrolled. CL=0 for left button; CL=2 for right button.
LODSB
CMP AL,';'
JNE .ReadKeyboard:
LodD
JC .ReadKeyboard:
MOV EBX,EAX ; X-coordinate of mouse cursor.
SHL EBX,16
LODSB
CMP AL,';'
JNE .ReadKeyboard:
LodD
JC .ReadKeyboard:
MOV BX,AX ; Y-coordinate of mouse cursor.
LODSB
CMP AL,'m'
JNE .ReadKeyboard:
; Mouse cursor coordinates are in EBX. Row number is in BX, button in CL.
MOV EDX,EBX
SHR EDX,16
MOV EAX,[TermWidth]
SUB EAX,3 ; AX is column of the scrollbox.
CMP DX,AX ; DX is the column where the mouse clicked.
JA .Scroll: ; Clicked at the scrollbox.
CMP CL,0
JE .MouseLeftButton:
CMP CL,2
JE .MouseRightButton:
JMP .ReadKeyboard:
.Scroll: ; Clicked at the scroll box, row BX.
MOVZX EAX,BX
TEST EAX
JZ .30:
DEC EAX
.30:MOV ECX,[ListMax]
MUL RCX
MOV ECX,[TermHeight]
DIV RCX
MOV [ListPos],EAX
CALL WindowPaint
JMP .ReadKeyboard:
.AnsiKeyCtrlUp:
.AnsiKeyAltUp:
.AnsiKeyShiftUp:
SUB EBX,3
.AnsiKeyUp:
DEC EBX
.Saturate: ; Saturate the proposed EBX=ListPos to the range 0..ListMax-2 (if ListMax>TermHeight),
; else to the range 0..1.
TEST EBX
JNS .50:
SUB EBX,EBX ; If EBX was lower than 0.
.50:MOV EAX,[ListMax]
CMP EAX,[TermHeight]
JA .60:
CMP EBX,1
JBE .70:
MOV EBX,1
JMP .70:
.60:SUB EAX,2
CMP EBX,EAX
JBE .70
MOV EBX,EAX
.70:MOV [ListPos],EBX
CALL WindowPaint
JMP .ReadKeyboard
.AnsiKeyCtrlDn:
.AnsiKeyAltDn:
.AnsiKeyShiftDn:
ADD EBX,3
.AnsiKeyDn:
INC EBX
JMP .Saturate:
.AnsiKeyPgUp:
SUB EBX,[TermHeight]
ADD EBX,2
JMP .Saturate:
.AnsiKeyPgDn:
ADD EBX,[TermHeight]
SUB EBX,2
JMP .Saturate:
.AnsiKeyCtrlHome:
.AnsiKeyCtrlPgUp:
.AnsiKeyHome:
MOV EBX,0
JMP .Saturate:
.AnsiKeyCtrlEnd:
.AnsiKeyCtrlPgDn:
.AnsiKeyEnd:
MOV EBX,[ListMax]
SUB EBX,[TermHeight]
ADD EBX,2
JMP .Saturate:
.MouseLeftButton:
.MouseRightButton:
MOVZXW EAX,BX
ADD EAX,[ListPos]
SUB EAX,2
MOV [DocRow],EAX
SHR EBX,16
MOV [DocCol],EBX
CALL WindowTitle
JMP .ReadKeyboard:
.AnsiKeyTab:
.AnsiKeyShiftTab:
jmp .ReadKeyboard:
.AnsiKeyW:
.AnsiKeyw:
.AnsiKeyAltW:
.AnsiKeyAltw:
.AnsiKeyCtrlW:
InvSt [Status::],ArgWrapLines
CALL WindowTitle
CALL MakeIndex
CALL WindowPaint
JMP .ReadKeyboard:
.AnsiKeyEscape:
StdOutput AnsiMouseDisable
LEA RBX,[TermIO]
LinABI ioctl,0,TCGETS,RBX ; Get current terminal echo.
SetSt [RBX+TERMIO.c_lflag],ICANON+ECHO
LinABI ioctl,0,TCSETS,RBX ; Restore terminal echo.
RET
ENDP WindowProc
FindEditor PROC
GetLengthUTF8 FindEdit
MOV [FindEditLen],ECX
MOV [FindCursor],ECX
.Read:
StdOutput AnsiRow6,AnsiCol6,FindEdit
CALL .ShowCursor:
XOR EDI,EDI ; Read the keyboard in the edit window.
LEA RSI,[KeyBuffer]
MOV R8,RSI
MOV [RSI],RDI
MOV EDX,SIZE# KeyBuffer
LinABI read,RDI,RSI,RDX
TEST EAX
JNA .Read:
XOR ECX,ECX ; Key is at RSI=R8, its size=RAX.
key %FOR AnsiKeyEnter,AnsiKeyLineFeed,AnsiKeyEscape,AnsiKeyLeft,AnsiKeyRight,AnsiKeyBackspace, \
AnsiKeyDelete,AnsiKeyDelete2,AnsiKeyHome,AnsiKeyEnd,AnsiKeyCtrlDelete
LEA RDI,[%key]
MOV CL,SIZE# %key
MOV RSI,R8
REPE CMPSB
JE .%key:
%ENDFOR key
MOV RSI,R8 ; Special keys were dispatched.
CMPB [RSI],27
JE .Read: ; Ignore other characters which start with Esc.
; RSI points to a pressed ordinary key (letter). RAX is its UTF-8 size 1..4.
; Make room for RAX bytes at [FindCursor] by right-shift the text between [FindCursor]..[FindCursorSize+RAX].
PUSH RAX,RSI
LEA RDX,[FindEdit]
MOV RSI,RDX
GetLength$ RDX ; Return RCX=size of text in FindEdit up to NUL in bytes.
ADD RSI,RCX
LEA RDI,[RSI+RAX]
CALL .Cursor2Pos: ; Return RCX=position in bytes in FindEdit corresponding with [FindCursor].
ADD RDX,RCX
LEA RCX,[RSI+1]
SUB RCX,RDX
STD
REP MOVSB
CLD
POP RSI,RAX
CALL .Cursor2Pos: ; Return RCX=FindEditSize in bytes corresponding to FindEditLen.
LEA RDI,[FindEdit+RCX]
MOV RCX,RAX
REP MOVSB
INC [FindCursor]
JMP .Read:
.AnsiKeyCtrlDelete: ; Erase all FileEdit field.
MOVB [FindEdit],0
StdOutput AnsiRow6,AnsiCol6
MOV ECX,TermWidth
SUB EDX,6+6
StdOutput Spaces,Size=RDX
MOV [FindCursor],0
CALL .ShowCursor
JMP .Read:
.AnsiKeyHome:
MOV [FindCursor],0
JMP .Read:
.AnsiKeyEnd:
GetLengthUTF8 FindEdit
MOV [FindCursor],ECX
JMP .Read:
.AnsiKeyLeft:
MOV EAX,[FindCursor]
DEC EAX
JS .Read:
MOV [FindCursor],EAX
CALL .ShowCursor:
JMP .Read:
.AnsiKeyRight:
CALL .Cursor2Pos:
LEA RSI,[FindEdit+RCX]
LODSB
CMP AL,0
JNE .R1:
MOVB [RSI-1],' '
.R1:MOV EAX,[FindCursor]
INC EAX
CMP EAX,[FindEditWidth]
JA .Read:
MOV [FindCursor],EAX
CALL .ShowCursor:
JMP .Read:
.AnsiKeyDelete:
INC [FindCursor]
CALL .Cursor2Pos: ; Return RCX=position in bytes in FindEdit corresponding with [FindCursor].
LEA RSI,[FindEdit+RCX]
DEC [FindCursor]
CALL .Cursor2Pos: ; Return RCX=position in bytes in FindEdit corresponding with [FindCursor].
LEA RDI,[FindEdit+RCX]
LEA RCX,[FindEdit+SIZE#FindEdit+1]
SUB RCX,RSI
REP MOVSB
CALL .ShowField:
JMP .Read:
.AnsiKeyDelete2:
.AnsiKeyBackspace:
CALL .Cursor2Pos: ; Return RCX=position in bytes in FindEdit corresponding with [FindCursor].
LEA RSI,[FindEdit+RCX]
MOV EAX,[FindCursor]
DEC EAX
JS .Read:
MOV [FindCursor],EAX
CALL .Cursor2Pos: ; Return RCX=position in bytes in FindEdit corresponding with [FindCursor].
LEA RDI,[FindEdit+RCX]
LEA RCX,[FindEdit+SIZE#FindEdit+1]
SUB RCX,RSI
REP MOVSB
CALL .ShowField:
JMP .Read:
.AnsiKeyLineFeed:
.AnsiKeyEnter: ; Save changes to Find$.
LEA RSI,[FindEdit]
LEA RDI,[Find$]
.E1:LODSB
STOSB
CMP AL,0
JNE .E1:
.90:GetLength$ Find$
MOV [Find$Size],ECX
RET ; Return CF=0.
.AnsiKeyEscape: ; Ignore changes.
STC
RET ; Return CF=1.
.ShowField: PROC ; Display FindEdit up to NUL and then spaces.
MOV EDX,[TermWidth]
SUB EDX,6+6
LEA RDI,[FindEdit]
StdOutput AnsiRow6,AnsiCol6,RDI
GetLength$ RDI
SUB EDX,ECX
JNA .9:
StdOutput Spaces,Size=RDX
.9: RET
ENDP .ShowField:
.Cursor2Pos: PROC ; Return RCX=position in bytes in FindEdit corresponding with [FindCursor].
PUSH RAX,RSI
LEA RSI,[FindEdit]
MOV ECX,[FindCursor] ; Length in characters 0..
JRCXZ .9:
.0: LODSB ; Load UTF-8 character.
OR AL,0000_0111b
CMP AL,1111_0111b
JE .4 ; Three more bytes follow.
OR AL,0000_1111b
CMP AL,1110_1111b
JE .3: ; Two more bytes follow.
OR AL,0001_1111b
CMP AL,1101_1111b
JE .2: ; One more byte follows.
JMP .1: ; Otherwise no extra byte follows.
.4: INC RSI
.3: INC RSI
.2: INC RSI
.1: LOOP .0:
.9: LEA RCX,[FindEdit]
XCHG RCX,RSI
SUB RCX,RSI
POP RSI,RAX
RET
ENDP .Cursor2Pos:
.ShowCursor: PROC ; Display cursor at [FindCursor]. Clobbers RAX,RDI.
LEA RDI,[KeyBuffer]
MOV AX,0x5B1B ; Esc [
STOSW
MOV EAX,[FindCursor] ; 0..
ADD EAX,6 ; Offset of 0-th character from left screen edge.
StoD RDI
MOV AX,0x0047 ; G
STOSW
StdOutput KeyBuffer
RET
ENDP .ShowCursor:
ENDP FindEditor
4, 4, 8, TermWidth-4
with the string FindEdit initialized from Find$.
FindWindowPaint PROC
MOV EAX,[TermWidth] ; 0x7F
SUB EAX,12
MOV [FindEditWidth],EAX ; 0x67
LEA RSI,[Find$] ; Copy Find$ to FindEdit.
LEA RDI,[FindEdit]
.05:LODSB
STOSB
CMP AL,0
JNE .05:
; Paint double frame around Find box:
StdOutput AnsiClrBgFFFFFF,AnsiClrFg000000,AnsiRow4, AnsiCol4,AnsiBox2DR
MOV ECX,[TermWidth]
MOVB [FindEdit+RCX-3],0
SUB ECX,9
MOV EDX,ECX
.10:StdOutput AnsiBox2H
LOOP .10:
StdOutput AnsiBox2DL,AnsiRow5,AnsiCol4,AnsiBox2V
StdOutput Spaces,Size=RDX
StdOutput AnsiBox2V,AnsiRow6,AnsiCol4,AnsiBox2V
StdOutput Spaces,Size=RDX
StdOutput AnsiBox2V,AnsiRow7,AnsiCol4,AnsiBox2UR
MOV ECX,EDX
.20:StdOutput AnsiBox2H
LOOP .20:
StdOutput AnsiBox2UL,AnsiRow5,AnsiCol6,AnsiClrFg808080,=B'Enter the text string to find. Then use keys N|P to find next|previous occurence'
StdOutput AnsiRow6,AnsiCol6,AnsiClrFgFF00FF,FindEdit
RET
ENDP FindWindowPaint
Search PROC
XOR EAX,EAX
MOV [FoundCnt],EAX
MOV R13,[OutputPtr::] ; R13 is the start of output file contents mapped to memory.
LEA RSI,[Find$]
MOV ECX,[Find$Size] ; Needle size in bytes.
TEST ECX
JZ .90: ; If no needle.
LODSB ; AL is 1st letter of the needle.
MOV R12,RSI ; R12 is pointer to the needle plus one.
LEA R11,[RCX-1] ; R11 is size of the needle minus one.
MOV RBX,[IndexPtr]
.10:CMP RBX,[IndexEnd]
JAE .90:
MOV EDI,[RBX+LST_INDEX.FA] ; EDI is FA of the line.
ADD RDI,R13 ; RDI is pointer to the start of the line indexed with RBX.
MOV ECX,[RBX+LST_INDEX.Size]
AND ECX,NoFlags ; RCX is the size of current line.
MOV [RBX+LST_INDEX.Size],ECX ; Remove FlagFound and FlagLAfound, if they were set from previous search.
.20:REPNE SCASB ; Search for the first byte.
JNE .70:
PUSH RCX,RDI
MOV RCX,R11
MOV RSI,R12
JRCXZ .40:
REPE CMPSB
.40:POP RDI,RCX
JNE .20:
INC [FoundCnt]
SUB RDI,R13
SUB EDI,[RBX+LST_INDEX.FA]
DEC EDI
SHL EDI,20
AND EDI,FlagLAfound
OR EDI,FlagFound
OR [RBX+LST_INDEX.Size],EDI
.70:ADD RBX,SIZE# LST_INDEX
JMP .10:
.90:RET
ENDP Search
%IF %^Debug
DebugOutput PROC
StdOutput RSI,Size=RCX
LinABI nanosleep, TimeSpec1,0
LEA RBX,[TermIO]
LinABI ioctl,0,TCGETS,RBX ; Get current terminal echo.
SetSt [RBX+TERMIO.c_lflag],ICANON+ECHO
LinABI ioctl,0,TCSETS,RBX ; Restore terminal echo.
RET
ENDP DebugOutput
%ENDIF
ENDPROGRAM textling