Author Topic: qXed 2021  (Read 3042 times)

0 Members and 1 Guest are viewing this topic.

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
qXed 2021
« on: December 26, 2021, 02:00:08 am »
Yello all,

Some are aware of this project, some aren't. So playing to both audiences, qXed is a hackable text editor made in qb64. It doesn't behave exactly like notepad or gedit, but its probably a bit more friendly than vim. I won't discuss the controls or address the learning curve here, sorry about that. Today is only for new stuff... Using the word "hackable", I mean qXed does more than let you write text. It can also take commands through Pipecom and receive the results back. For instance, I could be typing along, and then for whatever reason need to see the contents of the directory I'm in, so I can type:

Code: [Select]
>dir
and press Ctrl+Enter. (This would be "ls" in Linux of course.) Then, boom, right in the middle of my document, I get the contents of DIR:

Code: [Select]
>dir
( Volume in drive D is New Volume
 Volume Serial Number is 9E67-C1F1

 Directory of D:\qb64

12/26/2021  01:46 AM    <DIR>          .
12/26/2021  01:46 AM    <DIR>          ..
12/26/2021  01:16 AM                37 animals.txt
12/25/2021  09:53 PM         2,170,368 BinaryAnalyzer-Game.exe
12/25/2021  09:56 PM         2,172,416 BinaryAnalyzer.exe
12/23/2021  06:18 PM         1,611,264 bubblesort.exe



Since Pipecom is so handy, you can also send commands to external programs and get the answer back, supposing you have what I'll call "qXed-compatible" programs. These are defined as "pure text in, pure text out". Watch this example session (try to puzzle out what happens):

Code: [Select]
]save animals.txt
dog
cat
poodle
zebra
waifu
turtle

(animals.txt)
]

>quicksort animals.txt zoo.txt
()

>list zoo.txt
(cat
dog
poodle
turtle
waifu
zebra)
>

For this example to work, a program called quicksort(.exe) needs to already exist that reads a list from file, sorts it, and spits out a new sorted list. We read the sorted list back with another program called list(.exe).



Alright, so you see what I'm trying to make here. It's a text editor colliding with a terminal. This thing might behave in ways you might not expect, maybe rough ways, but I'm calling everything you see a "feature". I reserve the word "bug" for... well, you'll have to really convince me there's a problem. The grander point here is this aims to be one of those kinds of programs you can always have open. With the right supporting programs, it can do all kinds of work for you that the internet already does 1,000,000 times better, but what the hell right?

This is how it's landing in 2021. We'll see how the future treats this code but I bid you all to mess with it and try to dream up whatever console-friendly qXed-compatible programs can run with it. Full code here, all one file:

Code: QB64: [Select]
  1.  
  2. 'On Error GoTo BadExit
  3.  
  4.  
  5. _Title "qXed"
  6.  
  7. '$ExeIcon:'qXedlogo.ico'
  8.  
  9. Const KeyboardBksp = 8
  10. Const KeyboardTab = 9
  11. Const KeyboardEnter = 13
  12. Const KeyboardEsc = 27
  13. Const KeyboardF1 = 15104
  14. Const KeyboardF2 = 15360
  15. Const KeyboardF3 = 15616
  16. Const KeyboardF4 = 15872
  17. Const KeyboardF5 = 16128
  18. Const KeyboardF6 = 16384
  19. Const KeyboardF7 = 16640
  20. Const KeyboardF8 = 16896
  21. Const KeyboardF9 = 17152
  22. Const KeyboardF10 = 17408
  23. Const KeyboardF11 = 34048
  24. Const KeyboardF12 = 34304
  25. Const KeyboardHome = 18176
  26. Const KeyboardUpArrow = 18432
  27. Const KeyboardPgUp = 18688
  28. Const KeyboardLeftArrow = 19200
  29. Const KeyboardRightArrow = 19712
  30. Const KeyboardEnd = 20224
  31. Const KeyboardDnArrow = 20480
  32. Const KeyboardPgDn = 20736
  33. Const KeyboardIns = 20992
  34. Const KeyboardDel = 21248
  35. Const KeyboardRightShift = 100304
  36. Const KeyboardLeftShift = 100304
  37. Const KeyboardRightCtrl = 100305
  38. Const KeyboardLeftCtrl = 100306
  39. Const KeyboardRightAlt = 100307
  40. Const KeyboardLeftAlt = 100308
  41.  
  42. Dim Shared Spacebar As String
  43. LF = Chr$(10)
  44. CR = Chr$(13)
  45. Spacebar = Chr$(32)
  46.  
  47. Dim Shared BackgroundColor
  48. Dim Shared Cursor1Back
  49. Dim Shared Cursor2Back
  50. Dim Shared CursorBlinkFace
  51. Dim Shared CursorMixedBack
  52. Dim Shared CursorStdFace
  53. Dim Shared ScrollbarBack
  54. Dim Shared ScrollbarBase
  55. Dim Shared ScrollbarFace
  56. Dim Shared StatusBarColor
  57. Dim Shared TextBackColor
  58. Dim Shared TextFaceColor
  59. Dim Shared TextHighBack
  60. Dim Shared TextHighFace
  61. Dim Shared TitleColor
  62. Dim Shared WireColor
  63.  
  64. Dim Shared ColorTheme
  65. ColorTheme = 1
  66.  
  67. Dim Shared TheERROR As String
  68. Dim Shared Debug$
  69.  
  70.  
  71. _Delay .25
  72. Dim Shared scrHand As Long
  73. Dim Shared oldHand As Long
  74. scrHand = _NewImage(1 * 80, 1 * 24, 0) ' 8, 16, 0
  75. Screen scrHand
  76. Call SetPalette
  77.  
  78. ' Define fundamental structures.
  79. Type Vector
  80.     X As Integer
  81.     Y As Integer
  82.  
  83. Type Cell
  84.     Identity As Long
  85.     Pointer As Long
  86.     Lagger As Long
  87.     Content As String * 1
  88.  
  89. Dim Shared ChainLimit As Long
  90. Dim Shared BOC As Long ' Beginning of chain.
  91. Dim Shared EOC As Long ' End of chain.
  92. ChainLimit = 1 * 10 ^ 6
  93. BOC = 0
  94. EOC = ChainLimit
  95.  
  96. ' Define text window properties.
  97. Dim Shared WindowHeight As Integer
  98. Dim Shared WindowWidth As Integer
  99. Dim Shared VisibleLines As Integer
  100. Dim Shared TopIndent As Integer
  101. Dim Shared LeftIndent As Integer
  102. Dim Shared TextHeight As Integer
  103. Dim Shared TextWidth As Integer
  104. Dim Shared TextWrapping As Integer
  105. Dim Shared TextFormatting As Integer
  106. Dim Shared InsertKey As Integer
  107.  
  108. ' Set window properties.
  109. Call InitTextWindow
  110.  
  111. ' Set display state.
  112. HScroll = 1
  113. TextWrapping = 1
  114. TextFormatting = -1
  115. InsertKey = -1
  116.  
  117. ' Initiate text inside window.
  118. Dim Shared StartIndex
  119. Dim Shared LineAsMapped(TextHeight + 1) As String
  120. Dim Shared Cursor1 As Vector
  121. Dim Shared Cursor2 As Vector
  122.  
  123. ' Auxiliary 2D text grid.
  124. Dim Shared GOLSwitch
  125. Dim Shared AuxGrid(TextWidth, TextHeight, 2) As String
  126. GOLSwitch = -1
  127.  
  128. ' Create memory space for string.
  129. Dim Shared TheChain(ChainLimit) As Cell
  130.  
  131. ' File I/O.
  132. Dim Shared WorkingFileName As String
  133.  
  134. Dim Shared Highlight As String
  135. Highlight = "qXed"
  136.  
  137. ' Load text file into memory if applicable, use example string if not.
  138. Call InitTextChain(Command$)
  139.  
  140. ' Prime main loop.
  141. Call MapText
  142. Call CalibrateCursor(ID1)
  143. Call CalibrateCursor(ID2)
  144. Call PrintEverything
  145.  
  146. ' Main loop.
  147.  
  148.     If (StateChange% = 1) Then
  149.         Call PrintEverything
  150.     End If
  151.  
  152.     If (GOLSwitch = 1) Then
  153.         Call ConvertToGrid
  154.         Call GOL
  155.         Call ConvertFromGrid
  156.         Call MapText
  157.         Call PrintEverything
  158.     End If
  159.  
  160.     Do While (_Resize)
  161.         If ((_ResizeWidth / 8 > 18) And (_ResizeHeight / 16 > 6)) Then
  162.             oldHand = scrHand
  163.             scrHand = _NewImage((_ResizeWidth / 8), (_ResizeHeight / 16), 0)
  164.             Screen scrHand
  165.             _FreeImage oldHand
  166.             Call SetPalette
  167.             Call InitTextWindow
  168.             ReDim LineAsMapped(TextHeight)
  169.             ReDim AuxGrid(TextWidth, TextHeight, 2)
  170.             Call MapText
  171.             Call CalibrateCursor(ID1)
  172.             Call CalibrateCursor(ID2)
  173.             Call PrintEverything
  174.         End If
  175.     Loop
  176.  
  177.         If (_FileExists(_DroppedFile$(1))) Then
  178.             Call InitTextChain(_DroppedFile$(1))
  179.             Call MapText
  180.             Call CalibrateCursor(ID1)
  181.             Call CalibrateCursor(ID2)
  182.             Call PrintEverything
  183.         End If
  184.         _FinishDrop
  185.     End If
  186.  
  187.     _Display
  188.     _Limit 240
  189.  
  190.  
  191. BadExit:
  192. _Echo (TheERROR)
  193.  
  194. Sub StringPrint (RequestCode As String, x As Integer, y As Integer, txt As String)
  195.     TheERROR = RequestCode + Str$(x) + Str$(y) + " " + txt
  196.     '_PrintString (x * 8, y * 16), txt
  197.     Locate y, x: Print txt;
  198.  
  199. Sub InitTextWindow
  200.     WindowWidth = _Width / 1 - 0 * 1 '8, 1
  201.     WindowHeight = _Height / 1 - 0 * 1 '16, 1
  202.     TopIndent = 1 '0
  203.     LeftIndent = 1 '0
  204.     TextHeight = WindowHeight - (1 + TopIndent)
  205.     TextWidth = WindowWidth - (1 + LeftIndent)
  206.  
  207. Sub SetPalette
  208.     BackgroundColor = 0
  209.     Cursor1Back = 3
  210.     Cursor2Back = 7
  211.     CursorBlinkFace = 16 + 6
  212.     CursorMixedBack = 7
  213.     CursorStdFace = 0
  214.     ScrollbarBack = 7
  215.     ScrollbarBase = 8
  216.     ScrollbarFace = 1
  217.     StatusBarColor = 3
  218.     TextBackColor = 1
  219.     TextFaceColor = 11
  220.     TextHighBack = 2
  221.     TextHighFace = 1
  222.     TitleColor = 3
  223.     WireColor = 8
  224.     If (ColorTheme = 1) Then
  225.         _PaletteColor 0, _RGB32(0, 0, 39)
  226.         _PaletteColor 1, _RGB32(0, 49, 78)
  227.         _PaletteColor 3, _RGB32(69, 118, 147)
  228.         _PaletteColor 4, _RGB32(216, 98, 78)
  229.         _PaletteColor 6, _RGB32(255, 167, 0) '''
  230.         _PaletteColor 7, _RGB32(98, 98, 98)
  231.         _PaletteColor 8, _RGB32(48, 48, 48) '''
  232.         _PaletteColor 9, _RGB32(0, 88, 108)
  233.         _PaletteColor 10, _RGB32(85, 206, 85)
  234.         _PaletteColor 11, _RGB32(0, 170, 170) '''
  235.         _PaletteColor 14, _RGB32(255, 167, 0)
  236.         _PaletteColor 15, _RGB32(216, 216, 216)
  237.     Else
  238.         oldHand = scrHand
  239.         scrHand = _NewImage(_Width, _Height, 0)
  240.         Screen scrHand
  241.         _FreeImage oldHand
  242.     End If
  243.  
  244. Sub InitTextChain (c As String)
  245.     Dim i As Integer
  246.     Dim j As Integer
  247.     Dim k As Long
  248.     Dim q As String
  249.     Dim r As String
  250.     If (c <> "") Then
  251.         q = ""
  252.         Open c For Input As #1
  253.         Do While Not EOF(1)
  254.             Line Input #1, r
  255.             q = q + r + CR
  256.         Loop
  257.         Close #1
  258.         i = InStr(c, ".")
  259.         If (i <> 0) Then
  260.             j = i - 1
  261.         Else
  262.             j = Len(c)
  263.         End If
  264.         WorkingFileName = Left$(c, j) + "-" + LTrim$(RTrim$(Str$(Int(Timer)))) + ".txt"
  265.     Else
  266.         WorkingFileName = "qXed_file" + "-" + Date$ + "-" + LTrim$(RTrim$(Str$(Int(Timer)))) + ".txt"
  267.         q = ""
  268.         'q = q + "Working file is:" + CR + Chr$(9) + WorkingFileName + CR + CR + "Press F6 to save." + CR
  269.         q = q + "Welcome to qXed" + CR
  270.         q = q + "... a hackable text editor" + CR
  271.         q = q + "... now boosted with Pipecom!" + CR
  272.         q = q + CR
  273.         q = q + "Use Pipecom by starting a line with a bracket '>'." + CR
  274.         q = q + "Press Ctrl+Enter to evaluate that line." + CR
  275.         q = q + CR
  276.         q = q + "For example, place the cursor after 'dir', and press Ctrl+Enter:" + CR
  277.         q = q + "(Or, replace 'dir' with 'ls' for Linux.)" + CR
  278.         q = q + CR
  279.         q = q + ">dir"
  280.         q = q + CR
  281.         q = q + CR
  282.         q = q + "This program has a steeper learning curve than Notepad, so beware." + CR
  283.         q = q + CR
  284.         q = q + "Press F3 to change search term." + CR
  285.         q = q + "Press F10 to toggle high contrast mode." + CR
  286.         q = q + "Press F11 to see break returns and whitespace." + CR
  287.         q = q + "Press F12 to cycle text wrapping modes." + CR
  288.     End If
  289.  
  290.     ' Create character list.
  291.     For k = 1 To ChainLimit
  292.         TheChain(k).Identity = 0
  293.     Next
  294.     ID2 = Assimilate&(q, BOC, EOC)
  295.     'StartIndex = 1
  296.     ID1 = StartIndex
  297.  
  298.     GOLSwitch = -1
  299.  
  300. Function Assimilate& (a As String, st As Long, en As Long)
  301.     ' Maps a raw string of text between a set of identities.
  302.     Dim b As String
  303.     Dim c As String
  304.     Dim j As Long
  305.     Dim previousID As Long
  306.     Dim nextID As Long
  307.     Dim n0 As Long
  308.  
  309.     If ((st = BOC) And (en = EOC)) Then
  310.         previousID = st
  311.         nextID = NextOpenIdentity&(1)
  312.     Else
  313.         previousID = TheChain(st).Lagger
  314.         nextID = NextOpenIdentity&(st)
  315.     End If
  316.     n0 = nextID
  317.  
  318.     b = a
  319.     Do
  320.         c = Left$(b, 1)
  321.         b = Right$(b, Len(b) - 1)
  322.         Select Case c
  323.             Case Chr$(9)
  324.                 c = " "
  325.                 b = "   " + b
  326.             Case CR
  327.                 If (Left$(b, 1) = LF) Then
  328.                     b = Right$(b, Len(b) - 1)
  329.                 End If
  330.             Case LF
  331.                 c = CR
  332.         End Select
  333.         j = nextID
  334.         TheChain(j).Identity = j
  335.         TheChain(j).Content = c
  336.         TheChain(j).Lagger = previousID
  337.         If (previousID <> BOC) Then TheChain(previousID).Pointer = j
  338.         If (Len(b) > 0) Then
  339.             previousID = j
  340.             nextID = NextOpenIdentity&(j)
  341.         Else
  342.             If ((st = BOC) And (en = EOC)) Then
  343.                 TheChain(j).Pointer = en
  344.                 TheChain(en).Lagger = j
  345.             Else
  346.                 TheChain(j).Pointer = st
  347.                 TheChain(st).Lagger = j
  348.             End If
  349.             Exit Do
  350.         End If
  351.     Loop
  352.  
  353.     If ((st = BOC) And (en = EOC)) Then
  354.         StartIndex = n0
  355.     End If
  356.  
  357.     If (st <> EOC) Then
  358.         If (st = StartIndex) Then
  359.             StartIndex = BackBreak&(StartIndex)
  360.         End If
  361.     End If
  362.  
  363.     Assimilate& = n0
  364.  
  365. Function InsertString& (a As String, st As Long)
  366.     InsertString& = Assimilate&(a, st, TheChain(st).Pointer)
  367.  
  368. Function NthP& (a As Long, b As Long)
  369.     ' Returns the address that is b jumps ahead of address a.
  370.     Dim i As Long
  371.     Dim j As Long
  372.     Dim k As Long
  373.     i = a
  374.     j = 0
  375.     If (i <> EOC) Then
  376.         k = 0
  377.         Do While (k < b)
  378.             k = k + 1
  379.             j = i
  380.             i = TheChain(j).Pointer
  381.             If (i = EOC) Then Exit Do
  382.         Loop
  383.     End If
  384.     '''
  385.     If (j = 0) Then
  386.         j = StartIndex
  387.         Sound 1000, 1
  388.     End If
  389.     '''
  390.     NthP& = j
  391.  
  392. Function NthL& (a As Long, b As Long)
  393.     ' Returns the address that is b jumps behind address a.
  394.     Dim i As Long
  395.     Dim j As Long
  396.     Dim k As Long
  397.     i = a
  398.     k = 0
  399.     Do While (k < b)
  400.         k = k + 1
  401.         j = i
  402.         i = TheChain(j).Lagger
  403.         If (i = BOC) Then Exit Do
  404.     Loop
  405.     NthL& = j
  406.  
  407. Function NextOpenIdentity& (a As Long)
  408.     Dim j As Long
  409.     For j = a To ChainLimit
  410.         If (TheChain(j).Identity = 0) Then Exit For
  411.     Next
  412.     If (j > ChainLimit) Then
  413.         Print "Chain limit exceeded."
  414.         Sleep
  415.         System
  416.     End If
  417.     NextOpenIdentity& = j
  418.  
  419. Function BackBreak& (a As Long)
  420.     ' Function for scrolling up.
  421.     Dim c As String
  422.     Dim d As String
  423.     Dim j As Long
  424.     Dim k As Long
  425.     Dim lastBreak As Long
  426.     j = a
  427.     lastBreak = 0
  428.     c = ""
  429.     Do
  430.         If (j = BOC) Then Exit Do
  431.         k = TheChain(j).Lagger
  432.         If (k = BOC) Then
  433.             lastBreak = j
  434.             Exit Do
  435.         End If
  436.         j = k
  437.         d = TheChain(j).Content
  438.         c = d + c
  439.         If (TextWrapping = 1) Then
  440.             If ((d = " ") Or (d = CR)) Then
  441.                 lastBreak = j
  442.             End If
  443.         End If
  444.         If (TextWrapping <> 2) And (Len(c) = TextWidth) Then Exit Do
  445.         If (d = CR) Then
  446.             Exit Do
  447.         End If
  448.     Loop
  449.     If (lastBreak <> 0) Then j = lastBreak
  450.     BackBreak& = j
  451.  
  452. Function BackBreak2& (a As Long)
  453.     Dim c As String
  454.     Dim d As String
  455.     Dim j As Long
  456.     Dim k As Long
  457.     Dim lastBreak As Long
  458.     j = a
  459.     lastBreak = 0
  460.     c = ""
  461.     Do
  462.         If (j = BOC) Then Exit Do
  463.         k = TheChain(j).Lagger
  464.         If (k = BOC) Then
  465.             lastBreak = j
  466.             Exit Do
  467.         End If
  468.         j = k
  469.         d = TheChain(j).Content
  470.         c = d + c
  471.         If (TextWrapping = 1) Then
  472.             If (d = CR) Then
  473.                 If (Mid$(c, 2, 1) = ">") Or (Mid$(c, 2, 1) = "]") Then
  474.                     'If ((d = " ") Or (d = CR)) Then
  475.                     lastBreak = TheChain(j).Pointer
  476.                     'lastBreak = j
  477.                 End If
  478.             End If
  479.         End If
  480.         If (TextWrapping <> 2) And (Len(c) = TextWidth) Then Exit Do
  481.         If (d = CR) Then
  482.             If (Mid$(c, 2, 1) = ">") Or (Mid$(c, 2, 1) = "]") Then
  483.                 'If (d = CR) Then
  484.                 Exit Do
  485.             End If
  486.         End If
  487.     Loop
  488.     If (lastBreak <> 0) Then j = lastBreak
  489.     BackBreak2& = j
  490.  
  491.  
  492. Sub InsertBefore (a As Long, b As String)
  493.     ' Inserts a single cell before address a in the chain.
  494.     Dim lg As Long
  495.     Dim j As Long
  496.     j = NextOpenIdentity&(a)
  497.     lg = TheChain(a).Lagger
  498.     TheChain(j).Identity = j
  499.     TheChain(j).Pointer = a
  500.     TheChain(j).Lagger = lg
  501.     TheChain(j).Content = b
  502.     TheChain(a).Lagger = j
  503.     If (lg = BOC) Then
  504.         StartIndex = j
  505.     Else
  506.         TheChain(lg).Pointer = j
  507.     End If
  508.  
  509. Sub InsertCharacter (k As Integer)
  510.     If (InsertKey = -1) Then
  511.         Call InsertBefore(ID1, LTrim$(RTrim$(Chr$(k))))
  512.     Else
  513.         TheChain(ID1).Content = LTrim$(RTrim$(Chr$(k)))
  514.         ID1 = NthP&(ID1, 2)
  515.     End If
  516.     If (TextWrapping = 2) Then
  517.         If (Cursor1.X - LeftIndent = TextWidth) Then
  518.             HScroll = HScroll + 1
  519.         End If
  520.     End If
  521.  
  522. Function InsertAfter& (a As Long, b As String)
  523.     ' Inserts a single cell after address a in the chain.
  524.     Dim j As Long
  525.     Dim p As Long
  526.     j = NextOpenIdentity&(a)
  527.     p = TheChain(a).Pointer
  528.     TheChain(j).Identity = j
  529.     TheChain(j).Pointer = p
  530.     TheChain(j).Lagger = a
  531.     TheChain(j).Content = b
  532.     TheChain(a).Pointer = j
  533.     If (p <> EOC) Then
  534.         TheChain(p).Lagger = j
  535.     End If
  536.     InsertAfter& = j
  537.  
  538. Function LinearCount& (a As Long, b As Long)
  539.     LinearCount& = LinearCountProto&(a, b, ChainLimit + 1)
  540.  
  541. Function LinearCountProto& (a As Long, b As Long, c As Long)
  542.     ' Returns number of links between two addresses, with exit condition.
  543.     Dim i As Long
  544.     Dim j As Long
  545.     Dim k As Long
  546.     i = a
  547.     k = 0
  548.     Do While (i <> b)
  549.         k = k + 1
  550.         j = i
  551.         i = TheChain(j).Pointer
  552.         If (i = EOC) Then Exit Do
  553.         If (k = c) Then Exit Do
  554.     Loop
  555.     LinearCountProto& = k
  556.  
  557. Function Projection$ (a As Long, b As Long)
  558.     ' Returns the linear content for all address between a and b, inclusive.
  559.     Dim TheReturn As String
  560.     Dim j As Long
  561.     Dim k As Long
  562.     Dim c As String
  563.     TheReturn = ""
  564.     If (a = b) Then
  565.         TheReturn = TheChain(a).Content
  566.     Else
  567.         j = a
  568.         Do
  569.             c = TheChain(j).Content
  570.             TheReturn = TheReturn + c
  571.             k = TheChain(j).Pointer
  572.             If (j = b) Then Exit Do
  573.             If (k = EOC) Then Exit Do
  574.             j = k
  575.         Loop
  576.     End If
  577.     Projection$ = TheReturn
  578.  
  579. Sub MapText
  580.     Dim c1 As Long
  581.     Dim c2 As Long
  582.     Dim i As Integer
  583.     Dim r As Integer
  584.     Dim m As Integer
  585.     Dim j As Long
  586.     Dim k As Long
  587.     Dim k1 As Long
  588.     Dim k2 As Long
  589.     Dim n As Long
  590.     Dim q As String
  591.     Dim d As String
  592.     Dim c As String
  593.     Dim brsymbol As String
  594.     If (TextFormatting = 1) Then
  595.         brsymbol = "~"
  596.     Else
  597.         brsymbol = " "
  598.     End If
  599.     j = StartIndex
  600.     i = 1
  601.     q = ""
  602.     d = ""
  603.     Do ' Begin with any left-over text from previous iteration.
  604.         q = d
  605.         d = ""
  606.         r = TextWidth - Len(q)
  607.  
  608.         '''
  609.         If (TextWrapping = 0) Then
  610.             k1 = NthP&(j, r)
  611.             If (TheChain(k1).Pointer = EOC) Then k1 = EOC
  612.         End If
  613.         If (TextWrapping = 1) Then
  614.             k1 = NthP&(j, r)
  615.             If (TheChain(k1).Pointer = EOC) Then k1 = EOC
  616.         End If
  617.         If (TextWrapping = 2) Then
  618.             k1 = EOC
  619.         End If
  620.         '''
  621.  
  622.         k2 = j
  623.         Do
  624.             If (TheChain(k2).Content = CR) Then Exit Do
  625.             If (TheChain(k2).Pointer = EOC) Then Exit Do
  626.             k2 = TheChain(k2).Pointer
  627.         Loop
  628.  
  629.         If (TextWrapping <> 2) Then
  630.             c1 = LinearCount&(j, k1)
  631.         Else
  632.             c1 = LinearCountProto&(j, k1, TextWidth * TextHeight)
  633.         End If
  634.  
  635.         c2 = LinearCount&(j, k2)
  636.  
  637.         If (c2 = 0) Then ' Line has one character (except in Fluid mode).
  638.             k = k2
  639.             If (TheChain(j).Content = CR) Then
  640.                 q = q + brsymbol
  641.             Else
  642.                 q = q + TheChain(j).Content
  643.             End If
  644.             j = NthP&(k, 2)
  645.  
  646.         Else
  647.  
  648.             If (c1 = c2) Then ' End of line. (Possible end of chain?)
  649.                 '''
  650.                 If (TextWrapping = 0) Then
  651.                     k = k1
  652.                     If (TheChain(k).Content = CR) Then
  653.                         q = q + Projection$(j, TheChain(k).Lagger) + brsymbol
  654.                     Else
  655.                         q = q + Projection$(j, k)
  656.                     End If
  657.                     j = NthP&(k, 2)
  658.                 End If
  659.                 If (TextWrapping = 1) Then
  660.                     k = TheChain(k1).Lagger
  661.                     If (TheChain(k).Content = CR) Then
  662.                         q = q + Projection$(j, TheChain(k).Lagger) + brsymbol
  663.                     Else
  664.                         q = q + Projection$(j, k)
  665.                     End If
  666.                     j = NthP&(k, 2)
  667.                 End If
  668.                 If (TextWrapping = 2) Then
  669.                     k = k1 ' == EOC
  670.                     q = q + Projection$(j, k)
  671.                     j = NthP&(k, 2)
  672.                 End If
  673.                 '''
  674.             End If
  675.             If (c1 < c2) Then ' Width limit case (not always maximum).
  676.                 k = k1
  677.                 q = q + Projection$(j, k)
  678.                 j = NthP&(k, 2)
  679.             End If
  680.             If (c1 > c2) Then ' Break return somewhere in line (not first).
  681.                 k = k2
  682.                 n = TheChain(k).Pointer
  683.                 ''' Clean this up if compelled.
  684.                 If (n <> EOC) Then
  685.                     q = q + Projection$(j, TheChain(k).Lagger) + brsymbol
  686.                     j = n
  687.                 Else ' End of chain.
  688.                     If (TheChain(k).Content = CR) Then
  689.                         q = q + Projection$(j, TheChain(k).Lagger) + brsymbol
  690.                     Else
  691.                         q = q + Projection$(j, k)
  692.                     End If
  693.                 End If
  694.                 '''
  695.             End If
  696.         End If
  697.  
  698.         If (TextWrapping = 1) Then ' Wrap text at first breaking character from right, send remainder to next line.
  699.             If (Len(q) >= TextWidth) Then
  700.                 For m = Len(q) To 1 Step -1
  701.                     c = Mid$(q, m, 1)
  702.                     If ((c = " ") Or (c = "-") Or (c = ".") Or (c = "_") Or (c = brsymbol)) Then
  703.                         q = Left$(q, m)
  704.                         Exit For
  705.                     End If
  706.                     d = c + d
  707.                 Next
  708.                 If (m = 0) Then ' Text is too long for line and contains no wrapping characters.
  709.                     q = Left$(q, TextWidth)
  710.                     d = ""
  711.                 End If
  712.             End If
  713.         End If
  714.  
  715.         LineAsMapped(i) = q
  716.         i = i + 1
  717.  
  718.         If (n = EOC) Then
  719.             If (d <> "") Then
  720.                 LineAsMapped(i) = d
  721.                 i = i + 1
  722.             End If
  723.             Exit Do
  724.         End If
  725.         If (i = TextHeight) Then
  726.             'IF (d <> "") THEN BEEP
  727.             Exit Do
  728.         End If
  729.         If (j = k) Then
  730.             'IF (d <> "") THEN BEEP
  731.             Exit Do
  732.         End If
  733.     Loop
  734.     VisibleLines = i - 1
  735.  
  736. Sub PasteClipboard (a As Long, b As String)
  737.     Dim z As Long
  738.     z = InsertString&(b, a)
  739.  
  740. Sub CalibrateCursor (a As Long)
  741.     ' Place Cursor under ID on rendered line.
  742.     Dim s As Long
  743.     Dim c As Long
  744.     Dim i As Integer
  745.     Dim j As Integer
  746.     Dim k As Integer
  747.     Dim n As Integer
  748.     s = StartIndex
  749.     If ((TextWrapping = 2) And (HScroll > 1)) Then s = NthP&(s, HScroll)
  750.     c = LinearCount&(s, a)
  751.     k = 0
  752.     i = -1
  753.     For j = 1 To VisibleLines
  754.         n = Len(LineAsMapped(j))
  755.         If (k + n < c) Then
  756.             k = k + n
  757.         Else
  758.             i = c - k + 1
  759.             Exit For
  760.         End If
  761.     Next
  762.     If (j < VisibleLines) Then
  763.         If (i >= 1 + Len(LineAsMapped(j))) Then ''' Clean this line up a little.
  764.             i = 1
  765.             j = j + 1
  766.         End If
  767.     End If
  768.     If (a = ID1) Then
  769.         Cursor1.X = LeftIndent + i
  770.         Cursor1.Y = TopIndent + j
  771.     End If
  772.     If (a = ID2) Then
  773.         Cursor2.X = LeftIndent + i
  774.         Cursor2.Y = TopIndent + j
  775.     End If
  776.  
  777. Function FindID% (a As Integer, b As Long)
  778.     ' Find identity under a mapped location.
  779.     Dim relx As Integer
  780.     Dim rely As Integer
  781.     Dim k As Integer
  782.     Dim t As Integer
  783.     relx = a - LeftIndent
  784.     rely = b - TopIndent
  785.     For k = 1 To rely - 1
  786.         t = t + Len(LineAsMapped(k))
  787.     Next
  788.     t = t + relx
  789.     FindID% = t
  790.  
  791. Sub ReassignID1
  792.     ID1 = NthP&(StartIndex, FindID%(Cursor1.X, Cursor1.Y) + (HScroll - 1))
  793.  
  794. Sub ReassignID2
  795.     ID2 = NthP&(StartIndex, FindID%(Cursor2.X, Cursor2.Y) + (HScroll - 1))
  796.  
  797. Sub PrintEverything
  798.     Color BackgroundColor, BackgroundColor
  799.     Cls
  800.     Call PrintWires
  801.     Call HorizontalScrollbar
  802.     Call PrintStatusBars(VerticalScrollbar#)
  803.     Call PrintCursorInfo
  804.     Call PrintMainText
  805.     Call PrintCursor2
  806.     Call PrintCursor1
  807.  
  808. Sub PrintWires
  809.     Dim i As Integer
  810.     Color WireColor, BackgroundColor
  811.     Call StringPrint("PrintWires", WindowWidth, WindowHeight - 1, Chr$(217))
  812.     Call StringPrint("PrintWires", LeftIndent, WindowHeight - 1, Chr$(192))
  813.     For i = 1 + TopIndent To WindowHeight - 2
  814.         Call StringPrint("PrintWires", LeftIndent, i, Chr$(179))
  815.     Next
  816.     Call StringPrint("PrintWires", LeftIndent, TopIndent, Chr$(218))
  817.     Call StringPrint("PrintWires", WindowWidth, TopIndent, Chr$(191))
  818.     For i = 1 + LeftIndent To WindowWidth - 1
  819.         Call StringPrint("PrintWires", i, TopIndent, Chr$(196))
  820.     Next
  821.  
  822. Sub HorizontalScrollbar
  823.     Dim i As Integer
  824.     Dim p As Long
  825.     Dim q As Long
  826.     Dim r As Double
  827.     Dim s As Double
  828.     Color ScrollbarBase, BackgroundColor
  829.     For i = (1 + LeftIndent) To (WindowWidth - 1)
  830.         Call StringPrint("HorizontalScrollbar1", i, WindowHeight - 1, Chr$(177))
  831.     Next
  832.     p = LinearCount&(NthP&(StartIndex, FindID%(LeftIndent + 1, Cursor1.Y)), ID1)
  833.     q = Len(LineAsMapped(Cursor1.Y - TopIndent))
  834.     If (q <> 1) Then
  835.         r = p / (q - 1)
  836.         If (r > 1) Then r = 1
  837.     Else
  838.         r = 0
  839.     End If
  840.     s = r * (WindowWidth - LeftIndent - 2)
  841.     i = (1 + LeftIndent) + Int(s)
  842.     Color ScrollbarFace, ScrollbarBack
  843.     Call StringPrint("HorizontalScrollbar2", i, WindowHeight - 1, "^")
  844.  
  845. Function VerticalScrollbar#
  846.     Dim i As Integer
  847.     Dim p As Long
  848.     Dim q As Long
  849.     Dim r As Double
  850.     Dim s As Double
  851.     Color ScrollbarBase, BackgroundColor
  852.     For i = (1 + TopIndent) To (WindowHeight - 2)
  853.         Call StringPrint("VerticalScrollbar#", WindowWidth, i, Chr$(177))
  854.     Next
  855.     p = LinearCount&(ID1, NthP&(ID1, ChainLimit + 1))
  856.     q = LinearCount&(NthL&(ID1, ChainLimit + 1), NthP&(ID1, ChainLimit + 1))
  857.     If (q = 0) Then
  858.         r = 1
  859.     Else
  860.         r = Abs(1 - p / q)
  861.     End If
  862.     s = r * (WindowHeight - TopIndent - 3)
  863.     i = (1 + TopIndent) + Int(s)
  864.     Color ScrollbarFace, ScrollbarBack
  865.     Call StringPrint("VerticalScrollbar#", WindowWidth, i, "<")
  866.     VerticalScrollbar# = r
  867.  
  868. Sub PrintStatusBars (r As Double)
  869.     Dim c As String
  870.     c = ""
  871.     If (TextFormatting = 1) Then c = "[Fmt] " + c
  872.     Select Case TextWrapping
  873.         Case 0: c = c + "[Square]" + " "
  874.         Case 1: c = c + "[Fluid]" + " "
  875.         Case 2: c = c + "[None]" + " "
  876.     End Select
  877.     c = c + "[" + LTrim$(RTrim$(Str$(Int(100 * r)))) + "%]"
  878.     If (Len(c) >= TextWidth) Then c = Left$(c, TextWidth)
  879.     Color StatusBarColor, BackgroundColor
  880.     Call StringPrint("PrintStatusBars", WindowWidth - Len(c), TopIndent, c)
  881.     c = ""
  882.     If (InsertKey = 1) Then c = "[Ins]" + c
  883.     If (Len(c) >= TextWidth) Then c = Left$(c, TextWidth)
  884.     Color StatusBarColor, BackgroundColor
  885.     Call StringPrint("PrintStatusBars", WindowWidth - Len(c), WindowHeight, c)
  886.  
  887. Sub PrintCursorInfo
  888.     Dim c As String
  889.     Dim d As String
  890.     Dim c0 As String
  891.     Dim d0 As String
  892.     c = TheChain(ID1).Content
  893.     d = TheChain(ID2).Content
  894.     c0 = c
  895.     d0 = d
  896.     If (c = LF) Then c = "@" ' Should never happen.
  897.     If (d = LF) Then d = "@" ' Should never happen.
  898.     If (c = CR) Then c = "~"
  899.     If (d = CR) Then d = "~"
  900.     If (c = Spacebar) Then c = "_"
  901.     If (d = Spacebar) Then d = "_"
  902.     c = "(" + LTrim$(RTrim$(Str$(Cursor1.X - LeftIndent))) + " " + LTrim$(RTrim$(Str$(Cursor1.Y - TopIndent))) + " " + c + Str$(ID1) + ")"
  903.     Color CursorStdFace, Cursor1Back
  904.     Call StringPrint("PrintCursorInfo", 1 + LeftIndent, WindowHeight, c)
  905.     'IF (LinearCount&(StartIndex, ID2) > LinearCount&(StartIndex, ID1)) THEN
  906.     d = "(" + LTrim$(RTrim$(Str$(Cursor2.X - LeftIndent))) + " " + LTrim$(RTrim$(Str$(Cursor2.Y - TopIndent))) + " " + d + Str$(ID2) + ")"
  907.     Color CursorStdFace, Cursor2Back
  908.     Call StringPrint("PrintCursorInfo", 2 + LeftIndent + Len(c), WindowHeight, d)
  909.     'END IF
  910.     Color TitleColor, BackgroundColor
  911.     Call StringPrint("PrintCursorInfo", 1 + LeftIndent, TopIndent, "[qXed]" + Debug$)
  912.  
  913. Sub PrintMainText
  914.     Dim i As Integer
  915.     Dim j As Integer
  916.     Dim k As Integer
  917.     Dim k0 As Integer
  918.     Dim k00 As Integer
  919.     Dim c As String
  920.     Dim d As String
  921.     For i = 1 To VisibleLines
  922.         c = LineAsMapped(i)
  923.         If (TextFormatting = 1) Then
  924.             For j = 1 To TextWidth - Len(c)
  925.                 c = c + "_"
  926.             Next
  927.         End If
  928.         d = Mid$(c, HScroll, TextWidth)
  929.  
  930.         If (Highlight <> "") Then
  931.             k = InStr(d, Highlight)
  932.         End If
  933.         If (k > 0) Then
  934.             k0 = 0
  935.             Do While (k > 0)
  936.                 k00 = k0 + k - 1
  937.                 Color TextFaceColor, TextBackColor
  938.                 Call StringPrint("PrintMainText", k0 + LeftIndent + 1, TopIndent + i, Left$(d, k - 1))
  939.                 Color TextHighFace, TextHighBack
  940.                 Call StringPrint("PrintMainText", k0 + k - 1 + LeftIndent + 1, TopIndent + i, Mid$(d, k, Len(Highlight)))
  941.                 d = Right$(d, Len(d) - k - Len(Highlight) + 1)
  942.                 k0 = k0 + k - 1 + Len(Highlight)
  943.                 k = InStr(d, Highlight)
  944.             Loop
  945.             Color TextFaceColor, TextBackColor
  946.             Call StringPrint("PrintMainText", k00 + Len(Highlight) + LeftIndent + 1, TopIndent + i, d)
  947.         Else
  948.             Color TextFaceColor, TextBackColor
  949.             Call StringPrint("PrintMainText", LeftIndent + 1, TopIndent + i, d)
  950.         End If
  951.     Next
  952.  
  953. Sub PrintCursor1
  954.     Dim c As String
  955.     If ((Cursor1.X > 0 And Cursor1.X < WindowWidth) And ((Cursor1.Y > 0) And (Cursor1.Y < WindowHeight))) Then
  956.         c = TheChain(ID1).Content
  957.         If (c = " ") Then c = "_"
  958.         If (c = CR) Then c = "~"
  959.         If ((Cursor1.X = Cursor2.X) And (Cursor1.Y = Cursor2.Y)) Then
  960.             Color CursorBlinkFace, CursorMixedBack
  961.             Call StringPrint("PrintCursor1", Cursor1.X, Cursor1.Y, c)
  962.         Else
  963.             Color CursorBlinkFace, Cursor1Back
  964.             Call StringPrint("PrintCursor1", Cursor1.X, Cursor1.Y, c)
  965.         End If
  966.     End If
  967.  
  968. Sub PrintCursor2
  969.     Dim c As String
  970.     Dim p1 As Long
  971.     Dim p2 As Long
  972.     Dim pe As Long
  973.     If ((Cursor2.X > 0 And Cursor2.X < WindowWidth) And ((Cursor2.Y > 0) And (Cursor2.Y < WindowHeight))) Then
  974.         p1 = LinearCount&(StartIndex, ID1)
  975.         p2 = LinearCount&(StartIndex, ID2)
  976.         pe = LinearCount&(StartIndex, EOC)
  977.         If p2 < pe Then 'IF ((p2 > p1) AND (p2 < pe)) THEN
  978.             c = TheChain(ID2).Content
  979.             If (c = " ") Then c = "_"
  980.             If (c = CR) Then c = "~"
  981.             Color CursorStdFace, Cursor2Back
  982.             Call StringPrint("PrintCursor2", Cursor2.X, Cursor2.Y, c)
  983.         End If
  984.     End If
  985.  
  986. Function StateChange%
  987.     Dim TheReturn As Integer
  988.     Dim MH As Integer
  989.     Dim MW As Integer
  990.     Dim MT As Integer
  991.     Dim MH1 As Integer
  992.     Dim MH2 As Integer
  993.     Dim MH3 As Integer
  994.     Dim KH As Long
  995.     MH = 0
  996.     MW = 0
  997.     MT = 0
  998.  
  999.     KH = _KeyHit
  1000.  
  1001.         MH1 = _MouseButton(1)
  1002.         MH2 = _MouseButton(2)
  1003.         MH3 = _MouseButton(3)
  1004.         MW = _MouseWheel
  1005.         If (MW <> 0) Then MT = MW
  1006.     Loop
  1007.     MW = MT
  1008.  
  1009.     If (MH1 = -1) Then MH = MouseButton1%
  1010.     If (MH2 = -1) Then MH = MouseButton2%
  1011.     If (MH3 = -1) Then MH = MouseButton3%
  1012.     If (MW = -1) Then MH = MouseWheelUp%
  1013.     If (MW = 1) Then MH = MouseWheelDown%
  1014.  
  1015.     If (KH = KeyboardBksp) Then Call KeyBksp
  1016.     If (KH = KeyboardTab) Then Call KeyTab
  1017.     If (KH = KeyboardEsc) Then Call KeyEsc
  1018.     If (KH = KeyboardEnter) Or ((KH >= 32) And (KH <= 126)) Then Call KeyEnterAlphaNumer(KH)
  1019.     If (KH = KeyboardF1) Then Call KeyF1
  1020.     If (KH = KeyboardF2) Then Call KeyF2
  1021.     If (KH = KeyboardF3) Then Call KeyF3
  1022.     If (KH = KeyboardF4) Then Call KeyF4
  1023.     If (KH = KeyboardF5) Then Call KeyF5
  1024.     If (KH = KeyboardF6) Then Call KeyF6
  1025.     'If (KH = KeyboardF7) Then Call KeyF7
  1026.     If (KH = KeyboardF10) Then Call KeyF10
  1027.     If (KH = KeyboardF11) Then Call KeyF11
  1028.     If (KH = KeyboardF12) Then Call KeyF12
  1029.     If (KH = KeyboardHome) Then Call KeyHome
  1030.     If (KH = KeyboardUpArrow) Then Call KeyUpArrow
  1031.     If (KH = KeyboardPgUp) Then Call KeyPgUp
  1032.     If (KH = KeyboardLeftArrow) Then Call KeyLeftArrow
  1033.     If (KH = KeyboardRightArrow) Then Call KeyRightArrow
  1034.     If (KH = KeyboardEnd) Then Call KeyEnd
  1035.     If (KH = KeyboardDnArrow) Then Call KeyDwnArrow
  1036.     If (KH = KeyboardPgDn) Then Call KeyPgDn
  1037.     If (KH = KeyboardIns) Then Call KeyIns
  1038.     If (KH = KeyboardDel) Then Call KeyDel
  1039.  
  1040.     ' Exit sequence
  1041.     TheReturn = 0
  1042.     If ((MH <> 0) Or (KH > 0)) Then
  1043.         TheReturn = 1
  1044.         Call MapText
  1045.         Call CalibrateCursor(ID1)
  1046.         Call CalibrateCursor(ID2)
  1047.  
  1048.         ' Cursor sync and autoscrolling.
  1049.         Do While (Cursor1.Y > TopIndent + TextHeight - 1)
  1050.             StartIndex = NthP&(StartIndex, Len(LineAsMapped(1)) + 1)
  1051.             Call MapText
  1052.             Call CalibrateCursor(ID1)
  1053.             Call CalibrateCursor(ID2)
  1054.         Loop
  1055.         If (TextWrapping = 2) Then '''
  1056.             Do While (Cursor1.X > LeftIndent + TextWidth - 0)
  1057.                 HScroll = HScroll + 1
  1058.                 Cursor1.X = Cursor1.X - 1
  1059.             Loop
  1060.         End If
  1061.  
  1062.     End If
  1063.     MH = 0
  1064.     KH = 0
  1065.     _KeyClear
  1066.     StateChange% = TheReturn
  1067.  
  1068. Function MouseButton1%
  1069.     Dim As Double mx, my
  1070.     mx = _MouseX ' Int(_MouseX / 8)
  1071.     my = _MouseY ' Int(_MouseY / 16)
  1072.     If ((mx > LeftIndent) And (mx < TextWidth + LeftIndent + 1) And (my > TopIndent) And (my < TopIndent + TextHeight)) Then
  1073.         Call MouseButton1Cursor
  1074.     End If
  1075.     If (mx = WindowWidth) Then
  1076.         Call MouseButton1VScroll
  1077.     End If
  1078.     If (my = WindowHeight - 1) Then
  1079.         Call MouseButton1Hscroll
  1080.     End If
  1081.     MouseButton1% = 1
  1082.  
  1083. Sub MouseButton1VScroll
  1084.     ' This sub does things wrong.
  1085.     Dim i As Long
  1086.     Dim j As Long
  1087.     Dim k As Long
  1088.     Dim t As Long
  1089.     Dim r As Double
  1090.     Dim f As Double
  1091.     Dim As Double mx, my
  1092.     mx = _MouseX ' Int(_MouseX / 8)
  1093.     my = _MouseY ' Int(_MouseY / 16)
  1094.     If ((my > TopIndent) And (my < (TopIndent + TextHeight))) Then
  1095.         i = NthL&(ID1, ChainLimit + 1)
  1096.         j = NthP&(ID1, ChainLimit + 1)
  1097.         If (my = 1 + TopIndent) Then
  1098.             i = i ' clicked at top
  1099.         ElseIf (my = (TopIndent + TextHeight - 1)) Then
  1100.             i = j ' clicked at bottom
  1101.         Else
  1102.             t = LinearCount&(i, j)
  1103.             f = (my - TopIndent + 1) / (WindowHeight - TopIndent)
  1104.             For k = 1 To t
  1105.                 r = k / t
  1106.                 If (r >= f) Then Exit For
  1107.                 i = TheChain(i).Pointer
  1108.             Next
  1109.             If (TextWrapping <> 2) Then
  1110.                 'i = BackBreak&(i)
  1111.             End If
  1112.         End If
  1113.         StartIndex = i
  1114.         ID1 = i
  1115.     End If
  1116.  
  1117. Sub MouseButton1Hscroll
  1118.     Dim i As Long
  1119.     Dim j As Long
  1120.     Dim k As Integer
  1121.     Dim t As Integer
  1122.     Dim r As Double
  1123.     Dim f As Double
  1124.     Dim As Double mx, my
  1125.     mx = _MouseX ' Int(_MouseX / 8)
  1126.     my = _MouseY ' Int(_MouseY / 16)
  1127.     If ((mx > LeftIndent) And (mx < (LeftIndent + 1 + TextWidth))) Then
  1128.         j = ID1
  1129.         i = NthP&(StartIndex, FindID%(LeftIndent + 1, Cursor1.Y))
  1130.         t = Len(LineAsMapped(Cursor1.Y - TopIndent))
  1131.         f = (mx - LeftIndent) / (WindowWidth - LeftIndent)
  1132.         For k = 1 To t
  1133.             r = k / t
  1134.             If (r >= f) Then Exit For
  1135.             i = TheChain(i).Pointer
  1136.         Next
  1137.         ID1 = i
  1138.         k = LinearCount&(StartIndex, i) - LinearCount&(StartIndex, j)
  1139.         If (TextWrapping = 2) Then HScroll = HScroll + k
  1140.         If (HScroll < 1) Then HScroll = 1
  1141.     End If
  1142.  
  1143. Sub MouseButton1Cursor
  1144.     Dim k As Integer
  1145.     Dim As Double mx, my
  1146.     mx = _MouseX ' Int(_MouseX / 8)
  1147.     my = _MouseY ' Int(_MouseY / 16)
  1148.     Cursor1.X = mx
  1149.     k = LeftIndent + Len(LineAsMapped(my - TopIndent)) - (HScroll - 1)
  1150.     If (Cursor1.X > k) Then
  1151.         If (k < 0) Then
  1152.             If (Len(LineAsMapped(my - TopIndent)) > TextWidth) Then
  1153.                 HScroll = 1 + Len(LineAsMapped(my - TopIndent)) - (Cursor1.X - LeftIndent)
  1154.             Else
  1155.                 HScroll = 1
  1156.                 Cursor1.X = LeftIndent + Len(LineAsMapped(my - TopIndent))
  1157.             End If
  1158.         Else
  1159.             Cursor1.X = k
  1160.         End If
  1161.     End If
  1162.     Cursor1.Y = my
  1163.     Call ReassignID1
  1164.  
  1165. Function MouseButton2%
  1166.     ' Move Cursor2.
  1167.     Dim k As Integer
  1168.     Dim As Double mx, my
  1169.     mx = _MouseX ' Int(_MouseX / 8)
  1170.     my = _MouseY ' Int(_MouseY / 16)
  1171.     If (mx > LeftIndent) And (mx < TextWidth + LeftIndent + 1) And (my > TopIndent) And (my < TopIndent + TextHeight + 1) Then
  1172.         Cursor2.X = mx
  1173.         k = LeftIndent + Len(LineAsMapped(my - TopIndent)) - (HScroll - 1)
  1174.         If (Cursor2.X > k) Then
  1175.             If (k < 0) Then
  1176.             Else
  1177.                 Cursor2.X = k
  1178.             End If
  1179.         End If
  1180.         Cursor2.Y = my
  1181.         Call ReassignID2
  1182.     End If
  1183.     MouseButton2% = 1
  1184.  
  1185. Function MouseButton3%
  1186.     Call PasteClipboard(ID1, _Clipboard$)
  1187.     MouseButton3% = 1
  1188.  
  1189. Function MouseWheelUp%
  1190.     Call KeyUpArrow
  1191.     MouseWheelUp% = 1
  1192.  
  1193. Function MouseWheelDown%
  1194.     Call KeyDwnArrow
  1195.     MouseWheelDown% = 1
  1196.  
  1197. Sub KeyBksp
  1198.     Dim qq As Long
  1199.     Dim q As Long
  1200.     Dim p As Long
  1201.     q = TheChain(ID1).Lagger
  1202.     p = TheChain(ID1).Pointer
  1203.     If (q = BOC) Then
  1204.         ' Do nothing.
  1205.     End If
  1206.     If (q <> BOC) Then
  1207.         If ((TextWrapping = 2) And (Cursor1.X - LeftIndent = 1)) Then
  1208.             If (HScroll > 1) Then
  1209.                 HScroll = HScroll - 1
  1210.                 Call CalibrateCursor(ID1)
  1211.             End If
  1212.         End If
  1213.         qq = TheChain(q).Lagger
  1214.         TheChain(ID1).Lagger = qq
  1215.         TheChain(q).Identity = 0
  1216.         If (qq <> BOC) Then
  1217.             If (StartIndex = q) Then StartIndex = BackBreak&(StartIndex)
  1218.             TheChain(qq).Pointer = ID1
  1219.         End If
  1220.         If (qq = BOC) Then
  1221.             StartIndex = ID1
  1222.         End If
  1223.     End If
  1224.     ID2 = StartIndex
  1225.  
  1226. Sub KeyDel
  1227.     Dim i As _Integer64
  1228.     Dim q As Long
  1229.     Dim p As Long
  1230.     Dim q2 As Long
  1231.     Dim p2 As Long
  1232.  
  1233.     If (LinearCount(StartIndex, ID2) = LinearCount&(StartIndex, EOC)) Then
  1234.         i = -1
  1235.     Else
  1236.         i = LinearCount&(StartIndex, ID2) - LinearCount&(StartIndex, ID1)
  1237.     End If
  1238.  
  1239.     If (i <= 0) Then
  1240.         q = TheChain(ID1).Lagger
  1241.         p = TheChain(ID1).Pointer
  1242.         If (q = BOC) And (p = EOC) Then
  1243.             ' Never delete the only character.
  1244.             TheChain(ID1).Content = " "
  1245.         End If
  1246.         If (q <> BOC) And (p <> EOC) Then
  1247.             If (StartIndex = ID1) Then StartIndex = p
  1248.             TheChain(p).Lagger = q
  1249.             TheChain(ID1).Identity = 0
  1250.             TheChain(q).Pointer = p
  1251.             ID1 = p
  1252.         End If
  1253.         If ((q = BOC) And (p <> EOC)) Then
  1254.             StartIndex = p
  1255.             TheChain(p).Lagger = q
  1256.             TheChain(ID1).Identity = 0
  1257.             ID1 = p
  1258.         End If
  1259.         If ((q <> BOC) And (p = EOC)) Then
  1260.             If (StartIndex = ID1) Then StartIndex = q
  1261.             TheChain(ID1).Identity = 0
  1262.             TheChain(q).Pointer = p
  1263.             ID1 = q
  1264.         End If
  1265.     End If
  1266.  
  1267.     If (i > 0) Then
  1268.         q = TheChain(ID1).Lagger
  1269.         p = TheChain(ID1).Pointer
  1270.         q2 = TheChain(ID2).Lagger
  1271.         p2 = TheChain(ID2).Pointer
  1272.  
  1273.         If ((q <> BOC) And (p2 <> EOC)) Then
  1274.             If (StartIndex = ID1) Then StartIndex = p2
  1275.             Call UnlinkRange(ID1, ID2)
  1276.             ID1 = TheChain(q).Pointer
  1277.         End If
  1278.         If ((q = BOC) And (p2 <> EOC)) Then
  1279.             StartIndex = p2
  1280.             Call UnlinkRange(p, ID2)
  1281.             TheChain(p2).Lagger = q
  1282.             ID1 = p2
  1283.         End If
  1284.         If ((q <> BOC) And (p2 = EOC)) Then
  1285.             If (StartIndex = ID1) Then StartIndex = q
  1286.             Call UnlinkRange(ID1, q2)
  1287.             TheChain(ID2).Identity = 0
  1288.             TheChain(q).Pointer = p2
  1289.             ID1 = q
  1290.         End If
  1291.         If ((q = BOC) And (p2 = EOC)) Then
  1292.             StartIndex = ID1 '''
  1293.             Call UnlinkRange(p, q2)
  1294.             TheChain(ID2).Identity = 0
  1295.             TheChain(ID1).Content = " "
  1296.             TheChain(ID1).Lagger = BOC
  1297.             TheChain(ID1).Pointer = EOC
  1298.         End If
  1299.     End If
  1300.  
  1301.     ID2 = StartIndex
  1302.  
  1303. Sub UnlinkRange (a As Long, b As Long)
  1304.     Dim q As Long
  1305.     Dim u As Long
  1306.     Dim p As Long
  1307.     q = TheChain(a).Lagger
  1308.     u = a
  1309.     Do
  1310.         TheChain(u).Identity = 0
  1311.         p = TheChain(u).Pointer
  1312.         If (u = b) Then Exit Do
  1313.         If (p <> EOC) Then u = p
  1314.     Loop
  1315.     TheChain(p).Lagger = q
  1316.     If (q <> BOC) Then TheChain(q).Pointer = p
  1317.  
  1318. Sub KeyEsc
  1319.     If (ID2 <> ID1) Then
  1320.         ID2 = ID1
  1321.     Else
  1322.         ID2 = StartIndex
  1323.     End If
  1324.  
  1325. Sub KeyEnterAlphaNumer (k As Integer)
  1326.     Dim z As Long
  1327.     Dim a As String
  1328.     Dim b As String
  1329.     Dim c As String
  1330.     Dim n As Integer
  1331.     If (_KeyDown(KeyboardLeftCtrl) Or _KeyDown(KeyboardRightCtrl)) Then
  1332.  
  1333.         If (k = KeyboardEnter) Then
  1334.             z = BackBreak2&(ID1)
  1335.             a = Projection$(z, TheChain(ID1).Lagger)
  1336.  
  1337.             If (Left$(a, 1) = ">") Then
  1338.                 a = Right$(a, Len(a) - 1)
  1339.                 a = _Trim$(a)
  1340.                 a = pipecom_lite$(a)
  1341.                 Do While ((Right$(a, 1) = CR) Or (Right$(a, 1) = LF))
  1342.                     a = Left$(a, Len(a) - 1)
  1343.                 Loop
  1344.                 z = InsertString&(CR + "(" + a + ")" + CR + ">", ID1)
  1345.             End If
  1346.  
  1347.             If (Left$(a, 6) = "]save ") Then
  1348.                 a = Right$(a, Len(a) - 6)
  1349.                 n = InStr(a, " ")
  1350.                 b = Left$(a, n - 1)
  1351.                 c = Right$(a, Len(a) - n)
  1352.                 If (b <> "") Then
  1353.                     Open b For Output As #1
  1354.                     Print #1, c
  1355.                     Close #1
  1356.                     z = InsertString&(CR + "(" + b + ")" + CR + "]", ID1)
  1357.                 Else
  1358.                     z = InsertString&(CR + "(" + "Error" + ")" + CR + "]", ID1)
  1359.                 End If
  1360.             End If
  1361.  
  1362.             ID2 = StartIndex
  1363.         End If
  1364.  
  1365.  
  1366.         If (k = Asc("c")) Or (k = Asc("C")) Then
  1367.             _Clipboard$ = Projection$(ID1, ID2)
  1368.             ID2 = StartIndex
  1369.         End If
  1370.  
  1371.         If (k = Asc("v")) Or (k = Asc("V")) Then
  1372.             Call PasteClipboard(ID1, _Clipboard$)
  1373.             ID2 = StartIndex
  1374.         End If
  1375.  
  1376.         '''
  1377.     Else
  1378.  
  1379.         Call InsertCharacter(k)
  1380.  
  1381.         If ((k = KeyboardEnter) And (TextWrapping = 2)) Then HScroll = 1
  1382.         If ((k = KeyboardEnter) And (Cursor1.Y = TextHeight)) Then
  1383.             Call KeyDwnArrow
  1384.             Call KeyHome
  1385.         End If
  1386.  
  1387.     End If
  1388.  
  1389.  
  1390. Sub KeyF1
  1391.     Dim h0 As Integer
  1392.     h0 = HScroll
  1393.     Call KeyLeftArrow
  1394.     If (h0 = HScroll) Then
  1395.         If (TextWrapping = 2) Then
  1396.             HScroll = HScroll - 1
  1397.             If (HScroll < 1) Then HScroll = 1
  1398.         End If
  1399.     End If
  1400.  
  1401. Sub KeyF2
  1402.     Dim h0 As Integer
  1403.     h0 = HScroll
  1404.     Call KeyRightArrow
  1405.     If ((HScroll = h0) And (HScroll <> 1)) Then
  1406.         If (TextWrapping = 2) Then
  1407.             HScroll = HScroll + 1
  1408.         End If
  1409.     End If
  1410.  
  1411. Sub KeyF3
  1412.     Dim j As Integer
  1413.     Dim k As Integer
  1414.     For k = 2 To _Width - 1
  1415.         For j = Int(_Height / 3) - 2 To Int(_Height / 3) + 2
  1416.             Color BackgroundColor, BackgroundColor
  1417.             Call StringPrint("", k, j, " ")
  1418.         Next
  1419.         Color Cursor1Back, Cursor1Back
  1420.         Call StringPrint("", k, Int(_Height / 3) - 2, " ")
  1421.         Color Cursor1Back, Cursor1Back
  1422.         Call StringPrint("", k, Int(_Height / 3) + 2, " ")
  1423.     Next
  1424.     Color TextFaceColor, BackgroundColor
  1425.     Cls
  1426.     Locate 1, 1: Line Input "Text to highlight: ", Highlight
  1427.  
  1428. Sub KeyF4
  1429.     Cursor1.X = LeftIndent + 1
  1430.     Call ReassignID1
  1431.     Cursor2.X = LeftIndent + Len(LineAsMapped(Cursor1.Y - TopIndent))
  1432.     Cursor2.Y = Cursor1.Y
  1433.     Call ReassignID2
  1434.  
  1435. Sub KeyF5
  1436.     Dim a As Long
  1437.     Dim b As Long
  1438.     Dim c As String
  1439.     Dim k As Long
  1440.     a = NthL&(ID1, ChainLimit + 1)
  1441.     b = NthP&(ID1, ChainLimit + 1)
  1442.     c = Projection$(a, b) + CR
  1443.     For k = 1 To ChainLimit
  1444.         TheChain(k).Identity = 0
  1445.     Next
  1446.     a = Assimilate&(c, BOC, EOC)
  1447.  
  1448. Sub KeyF6
  1449.     Dim c As String
  1450.     Open WorkingFileName For Output As #1
  1451.     c = Projection$(NthL&(ID1, ChainLimit + 1), NthP&(ID1, ChainLimit + 1))
  1452.     Print #1, c
  1453.     Close #1
  1454.  
  1455. Sub KeyF7
  1456.     GOLSwitch = -GOLSwitch
  1457.  
  1458. Sub KeyF10
  1459.     ColorTheme = -ColorTheme
  1460.     Call SetPalette
  1461.  
  1462. Sub KeyF11
  1463.     TextFormatting = -TextFormatting
  1464.  
  1465. Sub KeyF12
  1466.     TextWrapping = TextWrapping + 1
  1467.     If (TextWrapping > 2) Then
  1468.         TextWrapping = 0
  1469.     End If
  1470.     If (TextWrapping <> 2) Then
  1471.         HScroll = 1
  1472.     End If
  1473.  
  1474. Sub KeyHome
  1475.     If (TextWrapping = 2) Then HScroll = 1
  1476.     Cursor1.X = LeftIndent + 1
  1477.     Call ReassignID1
  1478.  
  1479. Sub KeyUpArrow
  1480.     Dim k As Integer
  1481.     If (Cursor1.Y > TopIndent + 1) Then
  1482.         Cursor1.Y = Cursor1.Y - 1
  1483.     Else
  1484.         StartIndex = BackBreak&(StartIndex)
  1485.     End If
  1486.     k = LeftIndent + Len(LineAsMapped(Cursor1.Y - TopIndent)) - (HScroll - 1)
  1487.     If (Cursor1.X > k) Then
  1488.         If (k < 0) Then
  1489.             If (Len(LineAsMapped(Cursor1.Y - TopIndent)) > TextWidth) Then
  1490.                 HScroll = 1 + Len(LineAsMapped(Cursor1.Y - TopIndent)) - (Cursor1.X - LeftIndent)
  1491.             Else
  1492.                 HScroll = 1
  1493.                 Cursor1.X = LeftIndent + Len(LineAsMapped(Cursor1.Y - TopIndent))
  1494.             End If
  1495.         Else
  1496.             Cursor1.X = k
  1497.         End If
  1498.     End If
  1499.     Call ReassignID1
  1500.  
  1501. Sub KeyPgUp
  1502.     Dim k As Integer
  1503.     For k = 1 To Int(TextHeight / 2)
  1504.         StartIndex = BackBreak&(StartIndex)
  1505.     Next
  1506.     Call ReassignID1
  1507.  
  1508. Sub KeyLeftArrow
  1509.     Dim j As Integer
  1510.     Dim k As Integer
  1511.     ID1 = NthL&(ID1, 2)
  1512.     If (TextWrapping = 2) Then
  1513.         If (Cursor1.X = LeftIndent + 1) Then
  1514.             If (HScroll > 1) Then
  1515.                 HScroll = HScroll - 1
  1516.             Else
  1517.                 j = Cursor1.Y - TopIndent - 1
  1518.                 If (j >= 1) Then
  1519.                     k = Len(LineAsMapped(j)) - TextWidth + 1
  1520.                     If (k >= 1) Then
  1521.                         HScroll = k
  1522.                     End If
  1523.                 End If
  1524.             End If
  1525.         End If
  1526.     Else
  1527.         If ((Cursor1.X - LeftIndent = 1) And Cursor1.Y - TopIndent = 1) Then
  1528.             StartIndex = BackBreak&(StartIndex)
  1529.         End If
  1530.     End If
  1531.  
  1532. Sub KeyRightArrow
  1533.     Dim i As Integer
  1534.     Dim j As Integer
  1535.     Dim k As Integer
  1536.     ID1 = NthP&(ID1, 2)
  1537.     i = Cursor1.X - LeftIndent
  1538.     j = Len(LineAsMapped(Cursor1.Y - TopIndent)) - HScroll + 1
  1539.     If (TextWrapping = 2) Then
  1540.         If (i >= TextWidth) Then
  1541.             HScroll = HScroll + 1
  1542.             Call ReassignID1
  1543.         End If
  1544.         If (i >= j) Then
  1545.             k = Cursor1.Y - TopIndent + 1
  1546.             If ((k <= TextHeight) And (VisibleLines > 1)) Then HScroll = 1
  1547.         End If
  1548.     Else
  1549.         If ((i >= j) And (Cursor1.Y - TopIndent = VisibleLines)) Then
  1550.             If (VisibleLines > 1) Then StartIndex = NthP&(StartIndex, Len(LineAsMapped(1)) + 1)
  1551.         End If
  1552.     End If
  1553.  
  1554. Sub KeyEnd
  1555.     Dim k As Integer
  1556.     If (TextWrapping <> 2) Then
  1557.         Cursor1.X = LeftIndent + Len(LineAsMapped(Cursor1.Y - TopIndent))
  1558.         Call ReassignID1
  1559.     End If
  1560.     If (TextWrapping = 2) Then
  1561.         Do
  1562.             If (TheChain(ID1).Content = CR) Then Exit Do
  1563.             If (TheChain(ID1).Pointer = EOC) Then Exit Do
  1564.             ID1 = TheChain(ID1).Pointer
  1565.         Loop
  1566.         k = Len(LineAsMapped(Cursor1.Y - TopIndent)) - TextWidth + 1
  1567.         If (k >= 1) Then
  1568.             HScroll = k
  1569.             Call CalibrateCursor(ID1)
  1570.         End If
  1571.     End If
  1572.  
  1573. Sub KeyTab
  1574.     Dim z As Long
  1575.     z = InsertString&(Chr$(KeyboardTab), ID1)
  1576.  
  1577. Sub KeyDwnArrow
  1578.     Dim k As Integer
  1579.     If (Cursor1.Y = TopIndent + VisibleLines) Then
  1580.         If (VisibleLines > 1) Then
  1581.             StartIndex = NthP&(StartIndex, Len(LineAsMapped(1)) + 1)
  1582.             Call MapText
  1583.         End If
  1584.     Else
  1585.         Cursor1.Y = Cursor1.Y + 1
  1586.     End If
  1587.     k = LeftIndent + Len(LineAsMapped(Cursor1.Y - TopIndent)) - (HScroll - 1)
  1588.     If (Cursor1.X > k) Then
  1589.         If (k < 0) Then
  1590.             If (Len(LineAsMapped(Cursor1.Y - TopIndent)) > TextWidth) Then
  1591.                 HScroll = 1 + Len(LineAsMapped(Cursor1.Y - TopIndent)) - (Cursor1.X - LeftIndent)
  1592.             Else
  1593.                 HScroll = 1
  1594.                 Cursor1.X = LeftIndent + Len(LineAsMapped(Cursor1.Y - TopIndent))
  1595.             End If
  1596.         Else
  1597.             Cursor1.X = k
  1598.         End If
  1599.     End If
  1600.     Call ReassignID1
  1601.  
  1602. Sub KeyPgDn
  1603.     Dim k As Integer
  1604.     For k = 1 To Int(TextHeight / 2)
  1605.         If (VisibleLines > 1) Then
  1606.             StartIndex = NthP&(StartIndex, Len(LineAsMapped(1)) + 1)
  1607.             Call MapText
  1608.         End If
  1609.     Next
  1610.     Call ReassignID1
  1611.  
  1612. Sub KeyIns
  1613.     InsertKey = -InsertKey
  1614.  
  1615. Sub ConvertToGrid
  1616.     Dim i As Integer
  1617.     Dim j As Integer
  1618.     Dim c As String
  1619.     For j = 1 To VisibleLines
  1620.         c = LineAsMapped(j)
  1621.         For i = 1 To Len(c) - 1 ' BR offset to exclude CR at line end.
  1622.             AuxGrid(i, j, 1) = Mid$(c, i, 1)
  1623.         Next
  1624.     Next
  1625.  
  1626. Sub ConvertFromGrid
  1627.     Dim i As Integer
  1628.     Dim j As Integer
  1629.     Dim k As Long
  1630.     Dim c As String
  1631.     c = ""
  1632.     For j = 1 To VisibleLines
  1633.         For i = 1 To Len(LineAsMapped(j)) - 1
  1634.             c = c + AuxGrid(i, j, 1)
  1635.         Next
  1636.         c = c + CR ' Undoes BR offset.
  1637.     Next
  1638.     For k = 1 To ChainLimit
  1639.         TheChain(k).Identity = 0
  1640.     Next
  1641.     k = Assimilate&(c, BOC, EOC)
  1642.  
  1643. Sub GOL
  1644.     Dim i As Integer
  1645.     Dim j As Integer
  1646.     Dim c As String
  1647.     Dim a1 As Integer
  1648.     Dim a2 As Integer
  1649.     Dim a3 As Integer
  1650.     Dim a4 As Integer
  1651.     Dim a6 As Integer
  1652.     Dim a7 As Integer
  1653.     Dim a8 As Integer
  1654.     Dim a9 As Integer
  1655.     Dim t As Integer
  1656.     For j = 1 To VisibleLines
  1657.         For i = 1 To Len(LineAsMapped(j)) - 1
  1658.             c = AuxGrid(i, j, 1)
  1659.             If (c = " ") Then c = "0" Else c = "1"
  1660.             AuxGrid(i, j, 1) = c
  1661.             AuxGrid(i, j, 2) = c
  1662.         Next
  1663.     Next
  1664.     For j = 2 To VisibleLines - 2 ' BR offset.
  1665.         For i = 2 To Len(LineAsMapped(j)) - 2 ' BR offset.
  1666.             c = AuxGrid(i, j, 1)
  1667.             a1 = Val(AuxGrid(i - 1, j + 1, 1))
  1668.             a2 = Val(AuxGrid(i, j + 1, 1))
  1669.             a3 = Val(AuxGrid(i + 1, j + 1, 1))
  1670.             a4 = Val(AuxGrid(i - 1, j, 1))
  1671.             a6 = Val(AuxGrid(i + 1, j, 1))
  1672.             a7 = Val(AuxGrid(i - 1, j - 1, 1))
  1673.             a8 = Val(AuxGrid(i, j - 1, 1))
  1674.             a9 = Val(AuxGrid(i + 1, j - 1, 1))
  1675.             t = a1 + a2 + a3 + a4 + a6 + a7 + a8 + a9
  1676.             If (c = "1") Then
  1677.                 Select Case t
  1678.                     Case Is < 2
  1679.                         AuxGrid(i, j, 2) = "0"
  1680.                     Case 2
  1681.                         AuxGrid(i, j, 2) = "1"
  1682.                     Case 3
  1683.                         AuxGrid(i, j, 2) = "1"
  1684.                     Case Is > 3
  1685.                         AuxGrid(i, j, 2) = "0"
  1686.                 End Select
  1687.             Else
  1688.                 If (t = 3) Then AuxGrid(i, j, 2) = "1"
  1689.             End If
  1690.         Next
  1691.     Next
  1692.     For j = 1 To VisibleLines
  1693.         For i = 1 To Len(LineAsMapped(j)) - 1
  1694.             c = AuxGrid(i, j, 2)
  1695.             If (c = "0") Then c = " " Else c = Chr$(219)
  1696.             AuxGrid(i, j, 1) = c
  1697.             AuxGrid(i, j, 2) = c
  1698.         Next
  1699.     Next
  1700.  
  1701. '''
  1702.  
  1703. $If PIPECOM = UNDEFINED Then
  1704.     $Let PIPECOM = TRUE
  1705.     Function pipecom& (cmd As String, stdout As String, stderr As String)
  1706.         stdout = "": stderr = ""
  1707.         $If WIN Then
  1708.             Type SECURITY_ATTRIBUTES
  1709.                 As _Unsigned Long nLength
  1710.                 $If 64BIT Then
  1711.                     As String * 4 padding
  1712.                 $End If
  1713.                 As _Offset lpSecurityDescriptor
  1714.                 As Long bInheritHandle
  1715.                 $If 64BIT Then
  1716.                     As String * 4 padding2
  1717.                 $End If
  1718.             End Type
  1719.  
  1720.             Type STARTUPINFO
  1721.                 As Long cb
  1722.                 $If 64BIT Then
  1723.                     As String * 4 padding
  1724.                 $End If
  1725.                 As _Offset lpReserved, lpDesktop, lpTitle
  1726.                 As _Unsigned Long dwX, dwY, dwXSize, dwYSize, dwXCountChars, dwYCountChars, dwFillAttribute, dwFlags
  1727.                 As _Unsigned Integer wShowWindow, cbReserved2
  1728.                 $If 64BIT Then
  1729.                     As String * 4 padding2
  1730.                 $End If
  1731.                 As _Offset lpReserved2, hStdInput, hStdOutput, hStdError
  1732.             End Type
  1733.  
  1734.             Type PROCESS_INFORMATION
  1735.                 As _Offset hProcess, hThread
  1736.                 As _Unsigned Long dwProcessId
  1737.                 $If 64BIT Then
  1738.                     As String * 4 padding
  1739.                 $End If
  1740.             End Type
  1741.  
  1742.             Const STARTF_USESTDHANDLES = &H00000100
  1743.             Const CREATE_NO_WINDOW = &H8000000
  1744.  
  1745.             Const INFINITE = 4294967295
  1746.             Const WAIT_FAILED = &HFFFFFFFF
  1747.  
  1748.             Declare CustomType Library
  1749.                 Function CreatePipe& (ByVal hReadPipe As _Offset, Byval hWritePipe As _Offset, Byval lpPipeAttributes As _Offset, Byval nSize As _Unsigned Long)
  1750.                 Function CreateProcess& (ByVal lpApplicationName As _Offset, Byval lpCommandLine As _Offset, Byval lpProcessAttributes As _Offset, Byval lpThreadAttributes As _Offset, Byval bInheritHandles As Long, Byval dwCreationFlags As _Unsigned Long, Byval lpEnvironment As _Offset, Byval lpCurrentDirectory As _Offset, Byval lpStartupInfo As _Offset, Byval lpProcessInformation As _Offset)
  1751.                 Function GetExitCodeProcess& (ByVal hProcess As _Offset, Byval lpExitCode As _Offset)
  1752.                 Sub HandleClose Alias "CloseHandle" (ByVal hObject As _Offset)
  1753.                 Function ReadFile& (ByVal hFile As _Offset, Byval lpBuffer As _Offset, Byval nNumberOfBytesToRead As _Unsigned Long, Byval lpNumberOfBytesRead As _Offset, Byval lpOverlapped As _Offset)
  1754.                 Function WaitForSingleObject~& (ByVal hHandle As _Offset, Byval dwMilliseconds As _Unsigned Long)
  1755.             End Declare
  1756.  
  1757.             Dim As Long ok: ok = 1
  1758.             Dim As _Offset hStdOutPipeRead, hStdOutPipeWrite, hStdReadPipeError, hStdOutPipeError
  1759.             Dim As SECURITY_ATTRIBUTES sa: sa.nLength = Len(sa): sa.lpSecurityDescriptor = 0: sa.bInheritHandle = 1
  1760.  
  1761.             If CreatePipe(_Offset(hStdOutPipeRead), _Offset(hStdOutPipeWrite), _Offset(sa), 0) = 0 Then
  1762.                 pipecom = -1
  1763.                 Exit Function
  1764.             End If
  1765.  
  1766.             If CreatePipe(_Offset(hStdReadPipeError), _Offset(hStdOutPipeError), _Offset(sa), 0) = 0 Then
  1767.                 pipecom = -1
  1768.                 Exit Function
  1769.             End If
  1770.  
  1771.             Dim As STARTUPINFO si
  1772.             si.cb = Len(si)
  1773.             si.dwFlags = STARTF_USESTDHANDLES
  1774.             si.hStdError = hStdOutPipeError
  1775.             si.hStdOutput = hStdOutPipeWrite
  1776.             si.hStdInput = 0
  1777.             Dim As PROCESS_INFORMATION procinfo
  1778.             Dim As _Offset lpApplicationName
  1779.             Dim As String lpCommandLine: lpCommandLine = "cmd /c " + cmd + Chr$(0)
  1780.             Dim As _Offset lpProcessAttributes, lpThreadAttributes
  1781.             Dim As Long bInheritHandles: bInheritHandles = 1
  1782.             Dim As _Unsigned Long dwCreationFlags: dwCreationFlags = CREATE_NO_WINDOW
  1783.             Dim As _Offset lpEnvironment, lpCurrentDirectory
  1784.             ok = CreateProcess(lpApplicationName, _Offset(lpCommandLine), lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, _Offset(si), _Offset(procinfo))
  1785.  
  1786.             If ok = 0 Then
  1787.                 pipecom = -1
  1788.                 Exit Function
  1789.             End If
  1790.  
  1791.             HandleClose hStdOutPipeWrite
  1792.             HandleClose hStdOutPipeError
  1793.  
  1794.             Dim As String buf: buf = Space$(4096 + 1)
  1795.             Dim As _Unsigned Long dwRead
  1796.             While ReadFile(hStdOutPipeRead, _Offset(buf), 4096, _Offset(dwRead), 0) <> 0 And dwRead > 0
  1797.                 buf = Mid$(buf, 1, dwRead)
  1798.                 GoSub RemoveChr13
  1799.                 stdout = stdout + buf
  1800.                 buf = Space$(4096 + 1)
  1801.             Wend
  1802.  
  1803.             While ReadFile(hStdReadPipeError, _Offset(buf), 4096, _Offset(dwRead), 0) <> 0 And dwRead > 0
  1804.                 buf = Mid$(buf, 1, dwRead)
  1805.                 GoSub RemoveChr13
  1806.                 stderr = stderr + buf
  1807.                 buf = Space$(4096 + 1)
  1808.             Wend
  1809.  
  1810.             Dim As Long exit_code, ex_stat
  1811.             If WaitForSingleObject(procinfo.hProcess, INFINITE) <> WAIT_FAILED Then
  1812.                 If GetExitCodeProcess(procinfo.hProcess, _Offset(exit_code)) Then
  1813.                     ex_stat = 1
  1814.                 End If
  1815.             End If
  1816.  
  1817.             HandleClose hStdOutPipeRead
  1818.             HandleClose hStdReadPipeError
  1819.             If ex_stat = 1 Then
  1820.                 pipecom = exit_code
  1821.             Else
  1822.                 pipecom = -1
  1823.             End If
  1824.  
  1825.             Exit Function
  1826.  
  1827.             RemoveChr13:
  1828.             Dim As Long j
  1829.             j = InStr(buf, Chr$(13))
  1830.             Do While j
  1831.                 buf = Left$(buf, j - 1) + Mid$(buf, j + 1)
  1832.                 j = InStr(buf, Chr$(13))
  1833.             Loop
  1834.             Return
  1835.         $Else
  1836.             Declare CustomType Library
  1837.             Function popen%& (cmd As String, readtype As String)
  1838.             Function feof& (ByVal stream As _Offset)
  1839.             Function fgets$ (str As String, Byval n As Long, Byval stream As _Offset)
  1840.             Function pclose& (ByVal stream As _Offset)
  1841.             End Declare
  1842.  
  1843.             Declare Library
  1844.             Function WEXITSTATUS& (ByVal stat_val As Long)
  1845.             End Declare
  1846.  
  1847.             Dim As _Offset stream
  1848.  
  1849.             Dim buffer As String * 4096
  1850.             If _FileExists("pipestderr") Then
  1851.             Kill "pipestderr"
  1852.             End If
  1853.             stream = popen(cmd + " 2>pipestderr", "r")
  1854.             If stream Then
  1855.             While feof(stream) = 0
  1856.             If fgets(buffer, 4096, stream) <> "" And feof(stream) = 0 Then
  1857.             stdout = stdout + Mid$(buffer, 1, InStr(buffer, Chr$(0)) - 1)
  1858.             End If
  1859.             Wend
  1860.             Dim As Long status, exit_code
  1861.             status = pclose(stream)
  1862.             exit_code = WEXITSTATUS(status)
  1863.             If _FileExists("pipestderr") Then
  1864.             Dim As Integer errfile
  1865.             errfile = FreeFile
  1866.             Open "pipestderr" For Binary As #errfile
  1867.             If LOF(errfile) > 0 Then
  1868.             stderr = Space$(LOF(errfile))
  1869.             Get #errfile, , stderr
  1870.             End If
  1871.             Close #errfile
  1872.             Kill "pipestderr"
  1873.             End If
  1874.             pipecom = exit_code
  1875.             Else
  1876.             pipecom = -1
  1877.             End If
  1878.         $End If
  1879.  
  1880.     Function pipecom_lite$ (cmd As String)
  1881.         Dim As Long a
  1882.         Dim As String stdout, stderr
  1883.         a = pipecom(cmd, stdout, stderr)
  1884.         If stderr <> "" Then
  1885.             pipecom_lite = stderr
  1886.         Else
  1887.             pipecom_lite = stdout
  1888.         End If
  1889.  
You're not done when it works, you're done when it's right.