Editor.mod 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616
  1. IMPLEMENTATION MODULE Editor ;
  2. FROM Term IMPORT
  3. GotoXY, PutCh, PutStr, PutCard, ScrnWide, Marked, Normal,
  4. GetCh, Avail, GetKey, Beep ;
  5. FROM TextBuf IMPORT
  6. TextLimit, CharAt, InsertCh, OverwriteCh, DeleteFromTo, Length ;
  7. FROM Posix IMPORT
  8. open, close, read, write ;
  9. FROM SYSTEM IMPORT ADR, BYTE ;
  10. IMPORT Keys ;
  11. CONST
  12. Broad = 80 ; (* columns on the screen *)
  13. TopRow = 1 ;
  14. NBufRows = 23 ; (* text rows: screen rows 2..24 *)
  15. FirstRow = 2 ;
  16. MaxCol = 125 ; (* max chars per line before CR *)
  17. NoMark = 0FFFFFFFFH ; (* not-found / not-set value *)
  18. (* control keys *)
  19. C_A = CHR (1) ; C_C = CHR (3) ; C_D = CHR (4) ; C_E = CHR (5) ;
  20. C_F = CHR (6) ; C_G = CHR (7) ; C_H = CHR (8) ; C_I = CHR (9) ;
  21. C_J = CHR (10) ; C_K = CHR (11) ; C_L = CHR (12) ; C_M = CHR (13) ;
  22. C_N = CHR (14) ; C_P = CHR (16) ; C_Q = CHR (17) ; C_R = CHR (18) ;
  23. C_S = CHR (19) ; C_T = CHR (20) ; C_U = CHR (21) ; C_V = CHR (22) ;
  24. C_W = CHR (23) ; C_X = CHR (24) ; C_Y = CHR (25) ; C_Z = CHR (26) ;
  25. Esc = CHR (27) ; Del = CHR (127) ;
  26. VAR
  27. drive : CHAR ;
  28. fname : ARRAY [0..255] OF CHAR ;
  29. modified : BOOLEAN ;
  30. keys : Keys.Map ; (* the key map, from TP3.KEY *)
  31. curLine, curCol : CARDINAL ; (* 0-based cursor *)
  32. topLine : CARDINAL ; (* first visible line *)
  33. colOff : CARDINAL ; (* horizontal scroll *)
  34. insertMode : BOOLEAN ;
  35. autoIndent : BOOLEAN ;
  36. haveLast : BOOLEAN ; (* last cursor position remembered *)
  37. lastLine, lastCol : CARDINAL ;
  38. blkDef : BOOLEAN ; (* a block has been marked *)
  39. blkShown : BOOLEAN ; (* block is displayed *)
  40. blkB, blkE : CARDINAL ; (* block byte offsets [blkB, blkE) *)
  41. restoreOn : BOOLEAN ; (* current line snapshot exists *)
  42. restoreLine : CARDINAL ;
  43. restoreText : ARRAY [0..MaxCol + 2] OF CHAR ;
  44. restoreLen : CARDINAL ;
  45. lastFind : BOOLEAN ; (* a search was issued *)
  46. fnStr : ARRAY [0..31] OF CHAR ;
  47. rpStr : ARRAY [0..31] OF CHAR ;
  48. optB, optG, optU, optW, optN : BOOLEAN ;
  49. optN2 : CARDINAL ;
  50. lastIsReplace : BOOLEAN ;
  51. endEdit : BOOLEAN ; (* Ctrl-K-D pressed *)
  52. gotoPend : BOOLEAN ; (* enter editor at gotoOff (errexit jump) *)
  53. gotoOff : CARDINAL ;
  54. trash : LONGINT ; (* discarded syscall result *)
  55. PROCEDURE StrClear (VAR s : ARRAY OF CHAR) ;
  56. BEGIN
  57. s [0] := 0C
  58. END StrClear ;
  59. PROCEDURE PatLen (s : ARRAY OF CHAR) : CARDINAL ;
  60. VAR p : CARDINAL ;
  61. BEGIN
  62. p := 0 ;
  63. WHILE (p <= HIGH (s)) AND (s [p] # 0C) DO
  64. INC (p)
  65. END ;
  66. RETURN p
  67. END PatLen ;
  68. (* ------------------------------------------------------------------ *)
  69. (* buffer geometry (lines are CR-terminated) *)
  70. (* ------------------------------------------------------------------ *)
  71. PROCEDURE LineCount () : CARDINAL ;
  72. VAR n, i : CARDINAL ;
  73. BEGIN
  74. n := 1 ;
  75. i := 0 ;
  76. WHILE i < Length () DO
  77. IF CharAt (i) = C_M THEN
  78. INC (n)
  79. END ;
  80. INC (i)
  81. END ;
  82. RETURN n
  83. END LineCount ;
  84. PROCEDURE LineStart (l : CARDINAL) : CARDINAL ;
  85. VAR i, n : CARDINAL ;
  86. BEGIN
  87. i := 0 ;
  88. n := 0 ;
  89. WHILE (n < l) AND (i < Length ()) DO
  90. IF CharAt (i) = C_M THEN
  91. INC (n)
  92. END ;
  93. INC (i)
  94. END ;
  95. RETURN i
  96. END LineStart ;
  97. PROCEDURE LineEnd (l : CARDINAL) : CARDINAL ;
  98. (* byte offset of the CR terminating line l, or Length for the last *)
  99. VAR i : CARDINAL ;
  100. BEGIN
  101. i := LineStart (l) ;
  102. WHILE (i < Length ()) AND (CharAt (i) # C_M) DO
  103. INC (i)
  104. END ;
  105. RETURN i
  106. END LineEnd ;
  107. PROCEDURE LineLen (l : CARDINAL) : CARDINAL ;
  108. BEGIN
  109. RETURN LineEnd (l) - LineStart (l)
  110. END LineLen ;
  111. PROCEDURE LastLine () : CARDINAL ;
  112. VAR c : CARDINAL ;
  113. BEGIN
  114. c := LineCount () ;
  115. IF c = 0 THEN
  116. RETURN 0
  117. END ;
  118. RETURN c - 1
  119. END LastLine ;
  120. PROCEDURE OffToPos (off : CARDINAL ; VAR ln, cl : CARDINAL) ;
  121. VAR l, ls, le : CARDINAL ;
  122. BEGIN
  123. IF off > Length () THEN
  124. off := Length ()
  125. END ;
  126. l := 0 ;
  127. LOOP
  128. ls := LineStart (l) ;
  129. le := LineEnd (l) ;
  130. IF off <= le THEN
  131. ln := l ;
  132. cl := off - ls ;
  133. RETURN
  134. END ;
  135. INC (l)
  136. END
  137. END OffToPos ;
  138. PROCEDURE GotoOffset (off : CARDINAL) ;
  139. (* Arm a jump: the next Run enters the editor with the cursor on this
  140. 0-based TextBuf offset. Mirrors TP3's errexit path, where the shell
  141. does waitesc ; BX:=txerrpos ; DEC BX ; JMP editor2 and editor2
  142. lands on txbeg+txerrpos. PositionCursor scrolls the view to it. *)
  143. VAR ln, cl : CARDINAL ;
  144. BEGIN
  145. gotoPend := TRUE ;
  146. gotoOff := off
  147. END GotoOffset ;
  148. PROCEDURE ClampCursor ;
  149. VAR last : CARDINAL ;
  150. BEGIN
  151. last := LastLine () ;
  152. IF curLine > last THEN
  153. curLine := last
  154. END ;
  155. IF curCol > LineLen (curLine) THEN
  156. curCol := LineLen (curLine)
  157. END
  158. END ClampCursor ;
  159. PROCEDURE CursorOff () : CARDINAL ;
  160. BEGIN
  161. RETURN LineStart (curLine) + curCol
  162. END CursorOff ;
  163. (* ------------------------------------------------------------------ *)
  164. (* block marker maintenance *)
  165. (* ------------------------------------------------------------------ *)
  166. PROCEDURE Inserted (at, n : CARDINAL) ;
  167. BEGIN
  168. IF NOT blkDef THEN RETURN END ;
  169. IF at <= blkB THEN blkB := blkB + n END ;
  170. IF at <= blkE THEN blkE := blkE + n END
  171. END Inserted ;
  172. PROCEDURE Deleted (at, n : CARDINAL) ;
  173. VAR end2 : CARDINAL ;
  174. BEGIN
  175. IF NOT blkDef THEN RETURN END ;
  176. end2 := at + n ;
  177. IF at < blkB THEN
  178. IF end2 < blkB THEN
  179. blkB := blkB - n
  180. ELSE
  181. blkB := at
  182. END
  183. END ;
  184. IF at < blkE THEN
  185. IF end2 < blkE THEN
  186. blkE := blkE - n
  187. ELSE
  188. blkE := at
  189. END
  190. END
  191. END Deleted ;
  192. PROCEDURE BufInsert (at : CARDINAL ; ch : CHAR) ;
  193. BEGIN
  194. InsertCh (at, ch) ;
  195. Inserted (at, 1)
  196. END BufInsert ;
  197. PROCEDURE BufDeleteN (at, n : CARDINAL) ;
  198. BEGIN
  199. Deleted (at, n) ;
  200. DeleteFromTo (at, n)
  201. END BufDeleteN ;
  202. (* ------------------------------------------------------------------ *)
  203. (* screen *)
  204. (* ------------------------------------------------------------------ *)
  205. PROCEDURE StatusLine ;
  206. BEGIN
  207. GotoXY (TopRow, 1) ;
  208. PutStr ("Line ") ;
  209. PutCard (curLine + 1) ;
  210. PutStr (" Col ") ;
  211. PutCard (curCol + 1) ;
  212. IF insertMode THEN
  213. PutStr (" Insert ")
  214. ELSE
  215. PutStr (" Overwrite ")
  216. END ;
  217. IF autoIndent THEN
  218. PutStr (" Indent ")
  219. ELSE
  220. PutStr (" ")
  221. END ;
  222. PutCh (drive) ;
  223. PutCh (":") ;
  224. PutStr (fname)
  225. END StatusLine ;
  226. PROCEDURE DrawRow (row, ln : CARDINAL) ;
  227. VAR start, i, x, off : CARDINAL ;
  228. shown : BOOLEAN ;
  229. BEGIN
  230. GotoXY (row, 1) ;
  231. start := LineStart (ln) ;
  232. shown := blkDef AND blkShown ;
  233. i := colOff ;
  234. x := 0 ;
  235. WHILE (i < LineLen (ln)) AND (x < Broad) DO
  236. off := start + i ;
  237. IF shown AND (off >= blkB) AND (off < blkE) THEN
  238. Marked
  239. ELSE
  240. Normal
  241. END ;
  242. PutCh (CharAt (off)) ;
  243. INC (i) ;
  244. INC (x)
  245. END ;
  246. Normal ;
  247. IF x < Broad THEN
  248. ScrnWide (Broad - x)
  249. END
  250. END DrawRow ;
  251. PROCEDURE DrawScreen ;
  252. VAR r : CARDINAL ;
  253. BEGIN
  254. StatusLine ;
  255. r := 0 ;
  256. WHILE (r < NBufRows) AND (topLine + r <= LastLine ()) DO
  257. DrawRow (FirstRow + r, topLine + r) ;
  258. INC (r)
  259. END ;
  260. WHILE r < NBufRows DO
  261. GotoXY (FirstRow + r, 1) ;
  262. ScrnWide (Broad) ;
  263. INC (r)
  264. END
  265. END DrawScreen ;
  266. PROCEDURE PositionCursor ;
  267. VAR row, col : CARDINAL ;
  268. BEGIN
  269. IF curLine < topLine THEN topLine := curLine END ;
  270. IF curLine >= topLine + NBufRows THEN
  271. topLine := curLine - NBufRows + 1
  272. END ;
  273. IF curCol < colOff THEN colOff := curCol END ;
  274. IF curCol >= colOff + Broad THEN colOff := curCol - Broad + 1 END ;
  275. row := FirstRow + (curLine - topLine) ;
  276. col := curCol - colOff + 1 ;
  277. GotoXY (row, col)
  278. END PositionCursor ;
  279. PROCEDURE MsgWait (msg : ARRAY OF CHAR) ;
  280. VAR ch : CHAR ;
  281. BEGIN
  282. GotoXY (TopRow, 1) ;
  283. ScrnWide (Broad) ;
  284. GotoXY (TopRow, 1) ;
  285. PutStr (msg) ;
  286. GetCh (ch) ;
  287. StatusLine
  288. END MsgWait ;
  289. (* ------------------------------------------------------------------ *)
  290. (* status line input *)
  291. (* ------------------------------------------------------------------ *)
  292. PROCEDURE ReadStatus (prompt : ARRAY OF CHAR ; prefix : BOOLEAN ;
  293. VAR s : ARRAY OF CHAR) : BOOLEAN ;
  294. VAR len : CARDINAL ;
  295. ch : CHAR ;
  296. BEGIN
  297. GotoXY (TopRow, 1) ;
  298. ScrnWide (Broad) ;
  299. GotoXY (TopRow, 1) ;
  300. PutStr (prompt) ;
  301. len := 0 ;
  302. LOOP
  303. GetCh (ch) ;
  304. IF (ch = C_M) OR (ch = C_J) THEN
  305. EXIT
  306. ELSIF (ch = C_U) OR (ch = Esc) THEN
  307. RETURN FALSE
  308. ELSIF (ch = C_H) OR (ch = Del) THEN
  309. IF len > 0 THEN
  310. DEC (len) ;
  311. PutCh (CHR (8)) ;
  312. PutCh (" ") ;
  313. PutCh (CHR (8))
  314. END
  315. ELSIF ch = C_P THEN
  316. IF prefix THEN
  317. GetCh (ch) ;
  318. IF (ch = Esc) OR (ch = C_U) THEN
  319. RETURN FALSE
  320. END ;
  321. IF len < HIGH (s) THEN
  322. s [len] := ch ;
  323. INC (len) ;
  324. PutCh (ch)
  325. END
  326. END
  327. ELSIF (ch >= " ") AND (ch <= "~") AND (len < HIGH (s)) THEN
  328. s [len] := ch ;
  329. INC (len) ;
  330. PutCh (ch)
  331. END
  332. END ;
  333. s [len] := 0C ;
  334. StatusLine ;
  335. RETURN TRUE
  336. END ReadStatus ;
  337. (* ------------------------------------------------------------------ *)
  338. (* word boundaries *)
  339. (* ------------------------------------------------------------------ *)
  340. PROCEDURE IsSep (ch : CHAR) : BOOLEAN ;
  341. BEGIN
  342. IF (ch = " ") OR (ch = C_M) OR (ch = C_J) THEN RETURN TRUE END ;
  343. IF (ch = "<") OR (ch = ">") OR (ch = ",") OR (ch = ";") OR
  344. (ch = ".") OR (ch = "(") OR (ch = ")") OR (ch = "[") OR
  345. (ch = "]") OR (ch = "*") OR (ch = "+") OR (ch = "-") OR
  346. (ch = "/") OR (ch = "$") OR (ch = "=") OR (ch = ":") OR
  347. (ch = "{") OR (ch = "}") OR (ch = "^") OR (ch = "#") OR
  348. (ch = "&") OR (ch = "'") THEN RETURN TRUE END ;
  349. RETURN FALSE
  350. END IsSep ;
  351. PROCEDURE NextWord (from : CARDINAL) : CARDINAL ;
  352. (* offset of the first non-separator at or after "from" *)
  353. VAR i : CARDINAL ;
  354. BEGIN
  355. i := from ;
  356. WHILE (i < Length ()) AND IsSep (CharAt (i)) DO INC (i) END ;
  357. RETURN i
  358. END NextWord ;
  359. PROCEDURE EndWord (from : CARDINAL) : CARDINAL ;
  360. (* offset past the last non-separator starting at "from" *)
  361. VAR i : CARDINAL ;
  362. BEGIN
  363. i := from ;
  364. WHILE (i < Length ()) AND NOT IsSep (CharAt (i)) DO INC (i) END ;
  365. RETURN i
  366. END EndWord ;
  367. PROCEDURE PrevWord (from : CARDINAL) : CARDINAL ;
  368. (* start of the word to the left of "from" *)
  369. VAR i : CARDINAL ;
  370. BEGIN
  371. i := from ;
  372. WHILE (i > 0) AND IsSep (CharAt (i - 1)) DO DEC (i) END ;
  373. WHILE (i > 0) AND NOT IsSep (CharAt (i - 1)) DO DEC (i) END ;
  374. RETURN i
  375. END PrevWord ;
  376. (* ------------------------------------------------------------------ *)
  377. (* cursor movement *)
  378. (* ------------------------------------------------------------------ *)
  379. PROCEDURE SavePos ;
  380. BEGIN
  381. haveLast := TRUE ;
  382. lastLine := curLine ;
  383. lastCol := curCol
  384. END SavePos ;
  385. PROCEDURE TakeSnapshot ;
  386. VAR i : CARDINAL ;
  387. BEGIN
  388. restoreOn := TRUE ;
  389. restoreLine := curLine ;
  390. restoreLen := LineLen (curLine) ;
  391. i := 0 ;
  392. WHILE i < restoreLen DO
  393. restoreText [i] := CharAt (LineStart (curLine) + i) ;
  394. INC (i)
  395. END
  396. END TakeSnapshot ;
  397. PROCEDURE ToPos (ln, cl : CARDINAL) ;
  398. BEGIN
  399. IF ln # curLine THEN TakeSnapshot END ;
  400. curLine := ln ;
  401. curCol := cl ;
  402. ClampCursor
  403. END ToPos ;
  404. PROCEDURE CharLeft ;
  405. BEGIN
  406. IF curCol > 0 THEN DEC (curCol) ELSE Beep END
  407. END CharLeft ;
  408. PROCEDURE CharRight ;
  409. BEGIN
  410. IF curCol < LineLen (curLine) THEN INC (curCol) ELSE Beep END
  411. END CharRight ;
  412. PROCEDURE WordLeft ;
  413. VAR off, ln, cl : CARDINAL ;
  414. BEGIN
  415. off := PrevWord (CursorOff ()) ;
  416. OffToPos (off, ln, cl) ;
  417. ToPos (ln, cl)
  418. END WordLeft ;
  419. PROCEDURE WordRight ;
  420. VAR off, ln, cl : CARDINAL ;
  421. BEGIN
  422. off := NextWord (CursorOff ()) ;
  423. OffToPos (off, ln, cl) ;
  424. ToPos (ln, cl)
  425. END WordRight ;
  426. PROCEDURE LineUp ;
  427. BEGIN
  428. IF curLine > 0 THEN ToPos (curLine - 1, curCol) ELSE Beep END
  429. END LineUp ;
  430. PROCEDURE LineDown ;
  431. BEGIN
  432. IF curLine < LastLine () THEN ToPos (curLine + 1, curCol) ELSE Beep END
  433. END LineDown ;
  434. PROCEDURE ScrollUp ;
  435. BEGIN
  436. IF topLine > 0 THEN
  437. DEC (topLine) ;
  438. IF curLine = topLine + NBufRows THEN DEC (curLine) END
  439. ELSE
  440. Beep
  441. END
  442. END ScrollUp ;
  443. PROCEDURE ScrollDown ;
  444. BEGIN
  445. IF topLine < LastLine () THEN
  446. INC (topLine) ;
  447. IF curLine < topLine THEN INC (curLine) END
  448. ELSE
  449. Beep
  450. END
  451. END ScrollDown ;
  452. PROCEDURE PageUp ;
  453. VAR target : CARDINAL ;
  454. BEGIN
  455. SavePos ;
  456. target := NBufRows - 1 ;
  457. IF curLine > target THEN
  458. ToPos (curLine - target, curCol)
  459. ELSE
  460. ToPos (0, curCol)
  461. END ;
  462. IF curLine < topLine THEN topLine := curLine END
  463. END PageUp ;
  464. PROCEDURE PageDown ;
  465. VAR target, last : CARDINAL ;
  466. BEGIN
  467. SavePos ;
  468. target := NBufRows - 1 ;
  469. last := LastLine () ;
  470. IF curLine + target <= last THEN
  471. ToPos (curLine + target, curCol)
  472. ELSE
  473. ToPos (last, curCol)
  474. END ;
  475. IF curLine >= topLine + NBufRows THEN
  476. topLine := curLine - NBufRows + 1
  477. END
  478. END PageDown ;
  479. PROCEDURE HomeLine ;
  480. BEGIN
  481. curCol := 0
  482. END HomeLine ;
  483. PROCEDURE EndLine ;
  484. BEGIN
  485. curCol := LineLen (curLine)
  486. END EndLine ;
  487. PROCEDURE HomeScreen ;
  488. BEGIN
  489. ToPos (topLine, 0)
  490. END HomeScreen ;
  491. PROCEDURE EndScreen ;
  492. BEGIN
  493. ToPos (topLine + NBufRows - 1, 0)
  494. END EndScreen ;
  495. PROCEDURE HomeFile ;
  496. BEGIN
  497. SavePos ;
  498. ToPos (0, 0) ;
  499. topLine := 0
  500. END HomeFile ;
  501. PROCEDURE EndFile ;
  502. BEGIN
  503. SavePos ;
  504. ToPos (LastLine (), LineLen (LastLine ())) ;
  505. IF curLine >= topLine + NBufRows THEN
  506. topLine := curLine - NBufRows + 1
  507. END
  508. END EndFile ;
  509. PROCEDURE ToBlockB ;
  510. VAR ln, cl : CARDINAL ;
  511. BEGIN
  512. IF blkDef THEN
  513. SavePos ;
  514. OffToPos (blkB, ln, cl) ;
  515. ToPos (ln, cl)
  516. END
  517. END ToBlockB ;
  518. PROCEDURE ToBlockE ;
  519. VAR ln, cl : CARDINAL ;
  520. BEGIN
  521. IF blkDef THEN
  522. SavePos ;
  523. OffToPos (blkE, ln, cl) ;
  524. ToPos (ln, cl)
  525. END
  526. END ToBlockE ;
  527. PROCEDURE ToLastPos ;
  528. BEGIN
  529. IF haveLast THEN ToPos (lastLine, lastCol) END
  530. END ToLastPos ;
  531. (* ------------------------------------------------------------------ *)
  532. (* editing *)
  533. (* ------------------------------------------------------------------ *)
  534. PROCEDURE NewLine ;
  535. VAR indentCol, i, at : CARDINAL ;
  536. BEGIN
  537. at := CursorOff () ;
  538. InsertCh (at, C_M) ;
  539. Inserted (at, 1) ;
  540. IF autoIndent THEN
  541. indentCol := 0 ;
  542. i := 0 ;
  543. WHILE (i < curCol) AND (CharAt (LineStart (curLine) + i) = " ") DO
  544. INC (i)
  545. END ;
  546. indentCol := i
  547. ELSE
  548. indentCol := 0
  549. END ;
  550. INC (curLine) ;
  551. curCol := indentCol ;
  552. IF indentCol > 0 THEN
  553. at := LineStart (curLine) ;
  554. i := 0 ;
  555. WHILE i < indentCol DO
  556. InsertCh (at + i, " ") ;
  557. Inserted (at + i, 1) ;
  558. INC (i)
  559. END
  560. END ;
  561. restoreOn := FALSE ;
  562. modified := TRUE
  563. END NewLine ;
  564. PROCEDURE InsertBreak ;
  565. VAR at, ln, cl : CARDINAL ;
  566. BEGIN
  567. at := CursorOff () ;
  568. InsertCh (at, C_M) ;
  569. Inserted (at, 1) ;
  570. OffToPos (at, ln, cl) ;
  571. ToPos (ln, cl) ;
  572. restoreOn := FALSE ;
  573. modified := TRUE
  574. END InsertBreak ;
  575. PROCEDURE TypeChar (ch : CHAR) ;
  576. VAR at : CARDINAL ;
  577. BEGIN
  578. IF insertMode THEN
  579. IF LineLen (curLine) >= MaxCol THEN
  580. MsgWait ("Line too long - CR inserted") ;
  581. NewLine ;
  582. at := CursorOff () ;
  583. InsertCh (at, ch) ;
  584. Inserted (at, 1) ;
  585. INC (curCol)
  586. ELSE
  587. at := CursorOff () ;
  588. InsertCh (at, ch) ;
  589. Inserted (at, 1) ;
  590. INC (curCol)
  591. END
  592. ELSE
  593. IF curCol < LineLen (curLine) THEN
  594. at := CursorOff () ;
  595. OverwriteCh (at, ch) ;
  596. INC (curCol)
  597. ELSE
  598. at := CursorOff () ;
  599. InsertCh (at, ch) ;
  600. Inserted (at, 1) ;
  601. INC (curCol)
  602. END
  603. END ;
  604. restoreOn := FALSE ;
  605. modified := TRUE
  606. END TypeChar ;
  607. PROCEDURE DeleteLeft ;
  608. VAR off : CARDINAL ;
  609. BEGIN
  610. IF CursorOff () = 0 THEN Beep ; RETURN END ;
  611. IF curCol > 0 THEN
  612. DEC (curCol) ;
  613. off := CursorOff () ;
  614. BufDeleteN (off, 1)
  615. ELSE
  616. off := CursorOff () - 1 ; (* the CR ending the previous line *)
  617. BufDeleteN (off, 1) ;
  618. DEC (curLine) ;
  619. curCol := LineLen (curLine)
  620. END ;
  621. restoreOn := FALSE ;
  622. modified := TRUE
  623. END DeleteLeft ;
  624. PROCEDURE DeleteChar ;
  625. VAR off : CARDINAL ;
  626. BEGIN
  627. IF curCol >= LineLen (curLine) THEN Beep ; RETURN END ;
  628. off := CursorOff () ;
  629. BufDeleteN (off, 1) ;
  630. restoreOn := FALSE ;
  631. modified := TRUE
  632. END DeleteChar ;
  633. PROCEDURE DeleteWord ;
  634. VAR from, ew : CARDINAL ;
  635. BEGIN
  636. from := CursorOff () ;
  637. ew := EndWord (from) ;
  638. IF ew = from THEN ew := NextWord (from) END ;
  639. IF ew > from THEN
  640. BufDeleteN (from, ew - from) ;
  641. restoreOn := FALSE ;
  642. modified := TRUE
  643. END
  644. END DeleteWord ;
  645. PROCEDURE DeleteLine ;
  646. VAR s, e, last : CARDINAL ;
  647. BEGIN
  648. s := LineStart (curLine) ;
  649. e := LineEnd (curLine) ;
  650. IF e < Length () THEN INC (e) END ; (* include the CR *)
  651. BufDeleteN (s, e - s) ;
  652. last := LastLine () ;
  653. IF curLine > last THEN curLine := last END ;
  654. curCol := 0 ;
  655. restoreOn := FALSE ;
  656. modified := TRUE
  657. END DeleteLine ;
  658. PROCEDURE DeleteToEOL ;
  659. VAR to, at : CARDINAL ;
  660. BEGIN
  661. at := CursorOff () ;
  662. to := LineEnd (curLine) ;
  663. IF to > at THEN
  664. BufDeleteN (at, to - at) ;
  665. restoreOn := FALSE ;
  666. modified := TRUE
  667. END
  668. END DeleteToEOL ;
  669. PROCEDURE RestoreLine ;
  670. VAR s, i : CARDINAL ;
  671. BEGIN
  672. IF NOT (restoreOn AND (restoreLine = curLine)) THEN Beep ; RETURN END ;
  673. s := LineStart (curLine) ;
  674. BufDeleteN (s, LineLen (curLine)) ;
  675. i := 0 ;
  676. WHILE i < restoreLen DO
  677. InsertCh (s + i, restoreText [i]) ;
  678. Inserted (s + i, 1) ;
  679. INC (i)
  680. END ;
  681. IF curCol > restoreLen THEN curCol := restoreLen END
  682. END RestoreLine ;
  683. PROCEDURE MarkBlockB ;
  684. BEGIN
  685. blkDef := TRUE ;
  686. blkShown := TRUE ;
  687. blkB := CursorOff () ;
  688. blkE := CursorOff ()
  689. END MarkBlockB ;
  690. PROCEDURE MarkBlockE ;
  691. BEGIN
  692. blkDef := TRUE ;
  693. blkShown := TRUE ;
  694. blkE := CursorOff () ;
  695. IF blkE < blkB THEN blkE := blkB END
  696. END MarkBlockE ;
  697. PROCEDURE MarkWord ;
  698. VAR b, e : CARDINAL ;
  699. BEGIN
  700. b := CursorOff () ;
  701. e := EndWord (b) ;
  702. IF e = b THEN
  703. b := PrevWord (b) ;
  704. e := EndWord (b)
  705. END ;
  706. blkDef := TRUE ;
  707. blkShown := TRUE ;
  708. blkB := b ;
  709. blkE := e
  710. END MarkWord ;
  711. PROCEDURE ToggleBlock ;
  712. BEGIN
  713. IF blkDef THEN blkShown := NOT blkShown END
  714. END ToggleBlock ;
  715. PROCEDURE CopyBlock (VAR dst : ARRAY OF CHAR) : CARDINAL ;
  716. VAR i : CARDINAL ;
  717. BEGIN
  718. i := 0 ;
  719. WHILE (blkB + i < blkE) AND (i < HIGH (dst)) DO
  720. dst [i] := CharAt (blkB + i) ;
  721. INC (i)
  722. END ;
  723. RETURN i
  724. END CopyBlock ;
  725. PROCEDURE CopyBlockCmd ;
  726. VAR buf : ARRAY [0..MaxCol * 4 + 8] OF CHAR ;
  727. n, at, i : CARDINAL ;
  728. BEGIN
  729. IF NOT blkDef THEN RETURN END ;
  730. n := CopyBlock (buf) ;
  731. IF n = 0 THEN RETURN END ;
  732. at := CursorOff () ;
  733. i := 0 ;
  734. WHILE i < n DO
  735. InsertCh (at + i, buf [i]) ;
  736. INC (i)
  737. END ;
  738. Inserted (at, n) ;
  739. blkB := at ;
  740. blkE := at + n ;
  741. restoreOn := FALSE ;
  742. modified := TRUE
  743. END CopyBlockCmd ;
  744. PROCEDURE MoveBlockCmd ;
  745. VAR buf : ARRAY [0..MaxCol * 4 + 8] OF CHAR ;
  746. n, at, del : CARDINAL ;
  747. i : CARDINAL ;
  748. BEGIN
  749. IF NOT blkDef THEN RETURN END ;
  750. n := CopyBlock (buf) ;
  751. IF n = 0 THEN RETURN END ;
  752. at := CursorOff () ;
  753. IF (at >= blkB) AND (at <= blkE) THEN RETURN END ;
  754. del := blkE - blkB ;
  755. BufDeleteN (blkB, del) ;
  756. IF at > blkB THEN at := at - del END ;
  757. blkDef := FALSE ;
  758. i := 0 ;
  759. WHILE i < n DO
  760. InsertCh (at + i, buf [i]) ;
  761. INC (i)
  762. END ;
  763. Inserted (at, n) ;
  764. blkDef := TRUE ;
  765. blkB := at ;
  766. blkE := at + n ;
  767. restoreOn := FALSE ;
  768. modified := TRUE
  769. END MoveBlockCmd ;
  770. PROCEDURE DeleteBlockCmd ;
  771. BEGIN
  772. IF blkDef AND (blkE > blkB) THEN
  773. BufDeleteN (blkB, blkE - blkB) ;
  774. blkDef := FALSE ;
  775. restoreOn := FALSE ;
  776. modified := TRUE
  777. END
  778. END DeleteBlockCmd ;
  779. (* ------------------------------------------------------------------ *)
  780. (* find / replace *)
  781. (* ------------------------------------------------------------------ *)
  782. PROCEDURE MatchAt (at : CARDINAL ; pa : ARRAY OF CHAR ;
  783. ignoreCase : BOOLEAN) : BOOLEAN ;
  784. VAR b, q, len2 : CARDINAL ;
  785. c1, c2 : CHAR ;
  786. f, l : CARDINAL ;
  787. BEGIN
  788. len2 := PatLen (pa) ;
  789. IF len2 = 0 THEN RETURN FALSE END ;
  790. q := 0 ;
  791. b := at ;
  792. WHILE q < len2 DO
  793. IF (pa [q] = C_M) AND (q + 1 < len2) AND (pa [q + 1] = C_J) THEN
  794. (* pattern CR LF matches a single CR in the buffer *)
  795. IF b >= Length () THEN RETURN FALSE END ;
  796. IF CharAt (b) # C_M THEN RETURN FALSE END ;
  797. INC (b) ;
  798. INC (q, 2)
  799. ELSIF pa [q] = C_A THEN
  800. (* Ctrl-A wildcard: any character *)
  801. IF b >= Length () THEN RETURN FALSE END ;
  802. INC (b) ;
  803. INC (q)
  804. ELSE
  805. IF b >= Length () THEN RETURN FALSE END ;
  806. c1 := CharAt (b) ;
  807. c2 := pa [q] ;
  808. IF ignoreCase THEN
  809. f := ORD (c1) ;
  810. l := ORD (c2) ;
  811. IF (f >= ORD ("a")) AND (f <= ORD ("z")) THEN f := f - 32 END ;
  812. IF (l >= ORD ("a")) AND (l <= ORD ("z")) THEN l := l - 32 END ;
  813. IF f # l THEN RETURN FALSE END
  814. ELSIF c1 # c2 THEN
  815. RETURN FALSE
  816. END ;
  817. INC (b) ;
  818. INC (q)
  819. END
  820. END ;
  821. RETURN TRUE
  822. END MatchAt ;
  823. PROCEDURE WordBounded (at : CARDINAL) : BOOLEAN ;
  824. VAR len2 : CARDINAL ;
  825. c : CHAR ;
  826. BEGIN
  827. len2 := PatLen (fnStr) ;
  828. IF at = 0 THEN
  829. (* ok at start of buffer *)
  830. ELSE
  831. c := CharAt (at - 1) ;
  832. IF NOT IsSep (c) THEN RETURN FALSE END
  833. END ;
  834. IF at + len2 >= Length () THEN
  835. (* ok at end of buffer *)
  836. ELSE
  837. c := CharAt (at + len2) ;
  838. IF NOT IsSep (c) THEN RETURN FALSE END
  839. END ;
  840. RETURN TRUE
  841. END WordBounded ;
  842. PROCEDURE FindFwd (from : CARDINAL) : CARDINAL ;
  843. VAR i, len2 : CARDINAL ;
  844. found : BOOLEAN ;
  845. BEGIN
  846. len2 := PatLen (fnStr) ;
  847. IF len2 = 0 THEN RETURN NoMark END ;
  848. found := FALSE ;
  849. i := from ;
  850. WHILE (i + len2 <= Length ()) AND NOT found DO
  851. IF MatchAt (i, fnStr, optU) AND
  852. ((NOT optW) OR WordBounded (i)) THEN
  853. found := TRUE
  854. ELSE
  855. INC (i)
  856. END
  857. END ;
  858. IF found THEN RETURN i END ;
  859. RETURN NoMark
  860. END FindFwd ;
  861. PROCEDURE FindBwd (from : CARDINAL) : CARDINAL ;
  862. VAR i, len2 : CARDINAL ;
  863. found : CARDINAL ;
  864. BEGIN
  865. len2 := PatLen (fnStr) ;
  866. IF len2 = 0 THEN RETURN NoMark END ;
  867. IF from > Length () THEN from := Length () END ;
  868. found := NoMark ;
  869. i := 0 ;
  870. WHILE i + len2 <= from DO
  871. IF MatchAt (i, fnStr, optU) AND
  872. ((NOT optW) OR WordBounded (i)) THEN
  873. found := i
  874. END ;
  875. INC (i)
  876. END ;
  877. RETURN found
  878. END FindBwd ;
  879. PROCEDURE PutCursorAfter (m : CARDINAL) ;
  880. VAR len2, ln, cl : CARDINAL ;
  881. BEGIN
  882. len2 := PatLen (fnStr) ;
  883. OffToPos (m + len2, ln, cl) ;
  884. ToPos (ln, cl)
  885. END PutCursorAfter ;
  886. PROCEDURE DoSeek (global : BOOLEAN) : BOOLEAN ;
  887. VAR from, m : CARDINAL ;
  888. BEGIN
  889. IF PatLen (fnStr) = 0 THEN RETURN FALSE END ;
  890. IF global THEN
  891. IF optB THEN from := Length () ELSE from := 0 END
  892. ELSIF optB THEN
  893. from := CursorOff ()
  894. ELSE
  895. from := CursorOff () + 1 ;
  896. IF from > Length () THEN from := Length () END
  897. END ;
  898. IF optB THEN m := FindBwd (from) ELSE m := FindFwd (from) END ;
  899. IF m = NoMark THEN
  900. MsgWait ("Target not found") ;
  901. RETURN FALSE
  902. END ;
  903. PutCursorAfter (m) ;
  904. RETURN TRUE
  905. END DoSeek ;
  906. PROCEDURE ReplAt (m : CARDINAL) : CARDINAL ;
  907. VAR len2, rl, i, q : CARDINAL ;
  908. buf : ARRAY [0..64] OF CHAR ;
  909. BEGIN
  910. len2 := PatLen (fnStr) ;
  911. rl := PatLen (rpStr) ;
  912. i := 0 ;
  913. WHILE i < rl DO
  914. buf [i] := rpStr [i] ;
  915. INC (i)
  916. END ;
  917. BufDeleteN (m, len2) ;
  918. q := 0 ;
  919. i := 0 ;
  920. WHILE i < rl DO
  921. IF (buf [i] = C_M) AND (i + 1 < rl) AND (buf [i + 1] = C_J) THEN
  922. InsertCh (m + q, C_M) ;
  923. Inserted (m + q, 1) ;
  924. INC (q) ;
  925. INC (i, 2)
  926. ELSE
  927. InsertCh (m + q, buf [i]) ;
  928. Inserted (m + q, 1) ;
  929. INC (q) ;
  930. INC (i)
  931. END
  932. END ;
  933. modified := TRUE ;
  934. RETURN m + q
  935. END ReplAt ;
  936. PROCEDURE ParseOptions (s : ARRAY OF CHAR) ;
  937. VAR i, d, num : CARDINAL ;
  938. ch : CHAR ;
  939. BEGIN
  940. optB := FALSE ; optG := FALSE ; optU := FALSE ; optW := FALSE ;
  941. optN := FALSE ; optN2 := 0 ;
  942. i := 0 ;
  943. WHILE (i <= HIGH (s)) AND (s [i] # 0C) DO
  944. ch := s [i] ;
  945. IF (ch = "B") OR (ch = "b") THEN
  946. optB := TRUE
  947. ELSIF (ch = "G") OR (ch = "g") THEN
  948. optG := TRUE
  949. ELSIF (ch = "U") OR (ch = "u") THEN
  950. optU := TRUE
  951. ELSIF (ch = "W") OR (ch = "w") THEN
  952. optW := TRUE
  953. ELSIF (ch = "N") OR (ch = "n") THEN
  954. optN := TRUE
  955. ELSIF (ch >= "0") AND (ch <= "9") THEN
  956. num := 0 ;
  957. WHILE (i <= HIGH (s)) AND (s [i] >= "0") AND (s [i] <= "9") DO
  958. d := ORD (s [i]) - ORD ("0") ;
  959. num := num * 10 + d ;
  960. INC (i)
  961. END ;
  962. IF num > 0 THEN optN2 := num END ;
  963. DEC (i)
  964. END ;
  965. INC (i)
  966. END
  967. END ParseOptions ;
  968. PROCEDURE DoFind ;
  969. VAR s : ARRAY [0..255] OF CHAR ;
  970. i, n : CARDINAL ;
  971. BEGIN
  972. IF NOT ReadStatus ("Find: ", TRUE, s) THEN StatusLine ; RETURN END ;
  973. StrClear (fnStr) ;
  974. i := 0 ;
  975. LOOP
  976. IF (i <= HIGH (s)) AND (i <= 30) AND (s [i] # 0C) THEN
  977. fnStr [i] := s [i] ;
  978. INC (i)
  979. ELSE
  980. EXIT
  981. END
  982. END ;
  983. IF NOT ReadStatus ("Options: ", FALSE, s) THEN StatusLine ; RETURN END ;
  984. ParseOptions (s) ;
  985. lastFind := TRUE ;
  986. lastIsReplace := FALSE ;
  987. IF DoSeek (optG) THEN
  988. n := optN2 ;
  989. WHILE n > 1 DO
  990. IF NOT DoSeek (FALSE) THEN n := 1 END ;
  991. DEC (n)
  992. END
  993. END ;
  994. StatusLine
  995. END DoFind ;
  996. PROCEDURE DoReplace ;
  997. VAR s : ARRAY [0..255] OF CHAR ;
  998. i, from, m, after, len2, cnt : CARDINAL ;
  999. cont, ask, yes : BOOLEAN ;
  1000. ch : CHAR ;
  1001. BEGIN
  1002. IF NOT ReadStatus ("Find: ", TRUE, s) THEN StatusLine ; RETURN END ;
  1003. StrClear (fnStr) ;
  1004. i := 0 ;
  1005. LOOP
  1006. IF (i <= HIGH (s)) AND (i <= 30) AND (s [i] # 0C) THEN
  1007. fnStr [i] := s [i] ;
  1008. INC (i)
  1009. ELSE
  1010. EXIT
  1011. END
  1012. END ;
  1013. IF NOT ReadStatus ("Replace with: ", TRUE, s) THEN StatusLine ; RETURN END ;
  1014. StrClear (rpStr) ;
  1015. i := 0 ;
  1016. LOOP
  1017. IF (i <= HIGH (s)) AND (i <= 30) AND (s [i] # 0C) THEN
  1018. rpStr [i] := s [i] ;
  1019. INC (i)
  1020. ELSE
  1021. EXIT
  1022. END
  1023. END ;
  1024. IF NOT ReadStatus ("Options: ", FALSE, s) THEN StatusLine ; RETURN END ;
  1025. ParseOptions (s) ;
  1026. lastFind := TRUE ;
  1027. lastIsReplace := TRUE ;
  1028. IF optG THEN
  1029. IF optB THEN from := Length () ELSE from := 0 END
  1030. ELSIF optB THEN
  1031. from := CursorOff ()
  1032. ELSE
  1033. from := CursorOff () + 1 ;
  1034. IF from > Length () THEN from := Length () END
  1035. END ;
  1036. len2 := PatLen (fnStr) ;
  1037. IF len2 = 0 THEN RETURN END ;
  1038. cont := TRUE ;
  1039. cnt := 0 ;
  1040. LOOP
  1041. IF NOT cont THEN EXIT END ;
  1042. IF optB THEN m := FindBwd (from) ELSE m := FindFwd (from) END ;
  1043. IF m = NoMark THEN EXIT END ;
  1044. ask := NOT optN ;
  1045. yes := optN ;
  1046. IF ask THEN
  1047. GotoXY (TopRow, 1) ;
  1048. ScrnWide (Broad) ;
  1049. GotoXY (TopRow, 1) ;
  1050. PutStr ("Replace (Y/N)?") ;
  1051. GetCh (ch) ;
  1052. StatusLine ;
  1053. IF ch = C_U THEN EXIT END ;
  1054. yes := (ch = "Y") OR (ch = "y")
  1055. END ;
  1056. IF yes THEN
  1057. INC (cnt) ;
  1058. after := ReplAt (m) ;
  1059. IF optB THEN
  1060. from := m ;
  1061. IF m = 0 THEN cont := FALSE END
  1062. ELSE
  1063. from := after
  1064. END
  1065. ELSE
  1066. IF optB THEN
  1067. from := m ;
  1068. IF m = 0 THEN cont := FALSE END
  1069. ELSE
  1070. from := m + len2
  1071. END
  1072. END ;
  1073. IF (optN2 > 0) AND (cnt >= optN2) THEN cont := FALSE END
  1074. END ;
  1075. StatusLine
  1076. END DoReplace ;
  1077. PROCEDURE RepeatLast ;
  1078. BEGIN
  1079. IF NOT lastFind THEN RETURN END ;
  1080. IF lastIsReplace THEN DoReplace ELSE DoFind END
  1081. END RepeatLast ;
  1082. (* ------------------------------------------------------------------ *)
  1083. (* block file I/O *)
  1084. (* ------------------------------------------------------------------ *)
  1085. PROCEDURE HasDot (s : ARRAY OF CHAR) : BOOLEAN ;
  1086. VAR i : CARDINAL ;
  1087. BEGIN
  1088. i := 0 ;
  1089. WHILE (i <= HIGH (s)) AND (s [i] # 0C) DO
  1090. IF s [i] = "." THEN RETURN TRUE END ;
  1091. INC (i)
  1092. END ;
  1093. RETURN FALSE
  1094. END HasDot ;
  1095. PROCEDURE ToFileName (VAR s : ARRAY OF CHAR) ;
  1096. VAR len2 : CARDINAL ;
  1097. BEGIN
  1098. len2 := PatLen (s) ;
  1099. IF len2 = 0 THEN RETURN END ;
  1100. IF s [len2 - 1] = "." THEN
  1101. s [len2 - 1] := 0C ;
  1102. RETURN
  1103. END ;
  1104. IF NOT HasDot (s) THEN
  1105. IF len2 + 4 <= HIGH (s) THEN
  1106. s [len2] := "." ;
  1107. s [len2 + 1] := "P" ;
  1108. s [len2 + 2] := "A" ;
  1109. s [len2 + 3] := "S" ;
  1110. s [len2 + 4] := 0C
  1111. END
  1112. END
  1113. END ToFileName ;
  1114. PROCEDURE FileExists (s : ARRAY OF CHAR) : BOOLEAN ;
  1115. VAR fd : INTEGER ;
  1116. BEGIN
  1117. fd := open (ADR (s), 0, 0) ;
  1118. IF fd < 0 THEN RETURN FALSE END ;
  1119. trash := close (fd) ;
  1120. RETURN TRUE
  1121. END FileExists ;
  1122. PROCEDURE ReadFileAt (s : ARRAY OF CHAR ; at : CARDINAL) : BOOLEAN ;
  1123. VAR fd : INTEGER ;
  1124. buf : ARRAY [0..1023] OF BYTE ;
  1125. got, i : LONGINT ;
  1126. n : CARDINAL ;
  1127. pos : CARDINAL ;
  1128. b : BYTE ;
  1129. prevCR, huge, eof : BOOLEAN ;
  1130. BEGIN
  1131. fd := open (ADR (s), 0, 0) ;
  1132. IF fd < 0 THEN
  1133. MsgWait ("File not found") ;
  1134. RETURN FALSE
  1135. END ;
  1136. huge := FALSE ;
  1137. pos := at ;
  1138. prevCR := FALSE ;
  1139. eof := FALSE ;
  1140. LOOP
  1141. IF eof THEN EXIT END ;
  1142. got := read (fd, ADR (buf), 1024) ;
  1143. IF got <= 0 THEN EXIT END ;
  1144. n := VAL (CARDINAL, got) ;
  1145. i := 0 ;
  1146. WHILE i < VAL (LONGINT, n) DO
  1147. b := buf [i] ;
  1148. IF b = CHR (26) THEN
  1149. eof := TRUE ;
  1150. i := VAL (LONGINT, n)
  1151. ELSE
  1152. IF Length () >= TextLimit THEN
  1153. huge := TRUE
  1154. ELSE
  1155. IF b = CHR (10) THEN
  1156. IF NOT prevCR THEN
  1157. InsertCh (pos, C_M) ;
  1158. Inserted (pos, 1) ;
  1159. INC (pos)
  1160. END ;
  1161. prevCR := FALSE
  1162. ELSIF b = CHR (13) THEN
  1163. InsertCh (pos, C_M) ;
  1164. Inserted (pos, 1) ;
  1165. INC (pos) ;
  1166. prevCR := TRUE
  1167. ELSE
  1168. InsertCh (pos, CHR (b)) ;
  1169. Inserted (pos, 1) ;
  1170. INC (pos) ;
  1171. prevCR := FALSE
  1172. END
  1173. END ;
  1174. INC (i)
  1175. END
  1176. END
  1177. END ;
  1178. trash := close (fd) ;
  1179. IF huge THEN MsgWait ("WARNING: Out of space") END ;
  1180. RETURN TRUE
  1181. END ReadFileAt ;
  1182. PROCEDURE WriteBlockTo (s : ARRAY OF CHAR) ;
  1183. VAR fd : INTEGER ;
  1184. i : CARDINAL ;
  1185. b : BYTE ;
  1186. ch : CHAR ;
  1187. BEGIN
  1188. IF NOT (blkDef AND (blkE > blkB)) THEN RETURN END ;
  1189. IF FileExists (s) THEN
  1190. GotoXY (TopRow, 1) ;
  1191. ScrnWide (Broad) ;
  1192. GotoXY (TopRow, 1) ;
  1193. PutStr ("Overwrite old ") ;
  1194. PutStr (s) ;
  1195. PutStr (" (Y/N)?") ;
  1196. GetCh (ch) ;
  1197. StatusLine ;
  1198. IF NOT ((ch = "Y") OR (ch = "y")) THEN RETURN END
  1199. END ;
  1200. fd := open (ADR (s), 1 + 512 + 64, 420) ;
  1201. IF fd < 0 THEN
  1202. MsgWait ("Unable to create ") ;
  1203. RETURN
  1204. END ;
  1205. i := blkB ;
  1206. WHILE i < blkE DO
  1207. IF CharAt (i) = C_M THEN
  1208. b := CHR (13) ;
  1209. trash := write (fd, ADR (b), 1) ;
  1210. b := CHR (10) ;
  1211. trash := write (fd, ADR (b), 1)
  1212. ELSE
  1213. b := VAL (BYTE, ORD (CharAt (i))) ;
  1214. trash := write (fd, ADR (b), 1)
  1215. END ;
  1216. INC (i)
  1217. END ;
  1218. trash := close (fd)
  1219. END WriteBlockTo ;
  1220. PROCEDURE ReadBlockToCursor ;
  1221. VAR s : ARRAY [0..255] OF CHAR ;
  1222. at, startLen : CARDINAL ;
  1223. BEGIN
  1224. IF NOT ReadStatus ("Read block from file ", FALSE, s) THEN
  1225. StatusLine ;
  1226. RETURN
  1227. END ;
  1228. ToFileName (s) ;
  1229. startLen := Length () ;
  1230. at := CursorOff () ;
  1231. IF ReadFileAt (s, at) THEN
  1232. blkDef := TRUE ;
  1233. blkShown := TRUE ;
  1234. blkB := at ;
  1235. blkE := at + (Length () - startLen) ;
  1236. modified := TRUE
  1237. END ;
  1238. StatusLine
  1239. END ReadBlockToCursor ;
  1240. PROCEDURE WriteBlockCmd ;
  1241. VAR s : ARRAY [0..255] OF CHAR ;
  1242. BEGIN
  1243. IF NOT (blkDef AND (blkE > blkB)) THEN RETURN END ;
  1244. IF NOT ReadStatus ("Write block to file ", FALSE, s) THEN
  1245. StatusLine ;
  1246. RETURN
  1247. END ;
  1248. ToFileName (s) ;
  1249. WriteBlockTo (s) ;
  1250. StatusLine
  1251. END WriteBlockCmd ;
  1252. (* ------------------------------------------------------------------ *)
  1253. (* key handling *)
  1254. (* ------------------------------------------------------------------ *)
  1255. PROCEDURE AutoTab ;
  1256. VAR ln, i, nextCol : CARDINAL ;
  1257. started : BOOLEAN ;
  1258. BEGIN
  1259. IF curLine = 0 THEN Beep ; RETURN END ;
  1260. ln := curLine - 1 ;
  1261. started := FALSE ;
  1262. nextCol := 0 ;
  1263. i := curCol ;
  1264. LOOP
  1265. IF i >= LineLen (ln) THEN EXIT END ;
  1266. IF NOT IsSep (CharAt (LineStart (ln) + i)) THEN
  1267. nextCol := i ;
  1268. started := TRUE ;
  1269. EXIT
  1270. END ;
  1271. INC (i)
  1272. END ;
  1273. IF NOT started THEN Beep ; RETURN END ;
  1274. curCol := nextCol ;
  1275. IF curCol > LineLen (curLine) THEN curCol := LineLen (curLine) END
  1276. END AutoTab ;
  1277. PROCEDURE HandleEsc ;
  1278. VAR k : CHAR ;
  1279. BEGIN
  1280. GetKey (k) ;
  1281. IF k = "[" THEN
  1282. GetCh (k) ;
  1283. IF k = "A" THEN
  1284. LineUp
  1285. ELSIF k = "B" THEN
  1286. LineDown
  1287. ELSIF k = "C" THEN
  1288. CharRight
  1289. ELSIF k = "D" THEN
  1290. CharLeft
  1291. ELSIF k = "H" THEN
  1292. HomeLine
  1293. ELSIF k = "F" THEN
  1294. EndLine
  1295. ELSIF k = "3" THEN
  1296. GetCh (k) ;
  1297. DeleteChar
  1298. ELSIF k = "5" THEN
  1299. GetCh (k) ;
  1300. PageUp
  1301. ELSIF k = "6" THEN
  1302. GetCh (k) ;
  1303. PageDown
  1304. ELSIF k = "Z" THEN
  1305. AutoTab
  1306. END
  1307. END
  1308. END HandleEsc ;
  1309. PROCEDURE Up (ch : CHAR) : CHAR ;
  1310. BEGIN
  1311. IF (ch >= "a") AND (ch <= "z") THEN
  1312. RETURN CHR (ORD (ch) - 32)
  1313. END ;
  1314. RETURN ch
  1315. END Up ;
  1316. PROCEDURE KCommand ;
  1317. VAR ch : CHAR ;
  1318. BEGIN
  1319. GetCh (ch) ;
  1320. IF ch = Esc THEN RETURN END ;
  1321. ch := Up (ch) ;
  1322. IF ch = C_D THEN
  1323. ch := "D" (* tolerate the held-Ctrl chord Ctrl-K-Ctrl-D *)
  1324. ELSIF ch = C_X THEN
  1325. ch := "X" (* tolerate Ctrl-K-Ctrl-X as well *)
  1326. END ;
  1327. IF ch = "B" THEN
  1328. MarkBlockB
  1329. ELSIF ch = "K" THEN
  1330. MarkBlockE
  1331. ELSIF ch = "T" THEN
  1332. MarkWord
  1333. ELSIF ch = "H" THEN
  1334. ToggleBlock
  1335. ELSIF ch = "C" THEN
  1336. CopyBlockCmd
  1337. ELSIF ch = "V" THEN
  1338. MoveBlockCmd
  1339. ELSIF ch = "Y" THEN
  1340. DeleteBlockCmd
  1341. ELSIF ch = "R" THEN
  1342. ReadBlockToCursor
  1343. ELSIF ch = "W" THEN
  1344. WriteBlockCmd
  1345. ELSIF ch = "D" THEN
  1346. endEdit := TRUE
  1347. ELSE
  1348. Beep
  1349. END
  1350. END KCommand ;
  1351. PROCEDURE QCommand ;
  1352. VAR ch : CHAR ;
  1353. BEGIN
  1354. GetCh (ch) ;
  1355. IF ch = Esc THEN RETURN END ;
  1356. ch := Up (ch) ;
  1357. IF ch = "S" THEN
  1358. HomeLine
  1359. ELSIF ch = "D" THEN
  1360. EndLine
  1361. ELSIF ch = "E" THEN
  1362. HomeScreen
  1363. ELSIF ch = "X" THEN
  1364. EndScreen
  1365. ELSIF ch = "R" THEN
  1366. HomeFile
  1367. ELSIF ch = "C" THEN
  1368. EndFile
  1369. ELSIF ch = "B" THEN
  1370. ToBlockB
  1371. ELSIF ch = "K" THEN
  1372. ToBlockE
  1373. ELSIF ch = "P" THEN
  1374. ToLastPos
  1375. ELSIF ch = "Y" THEN
  1376. DeleteToEOL
  1377. ELSIF ch = "L" THEN
  1378. RestoreLine
  1379. ELSIF ch = "I" THEN
  1380. autoIndent := NOT autoIndent
  1381. ELSIF ch = "F" THEN
  1382. DoFind
  1383. ELSIF ch = "A" THEN
  1384. DoReplace
  1385. ELSE
  1386. Beep
  1387. END
  1388. END QCommand ;
  1389. PROCEDURE MainLoop ;
  1390. VAR ch : CHAR ;
  1391. BEGIN
  1392. endEdit := FALSE ;
  1393. LOOP
  1394. DrawScreen ;
  1395. PositionCursor ;
  1396. GetCh (ch) ;
  1397. IF ch = Esc THEN
  1398. HandleEsc
  1399. ELSIF ch = keys.quit THEN
  1400. QCommand
  1401. ELSIF ch = keys.prefix THEN
  1402. KCommand
  1403. ELSIF ch = keys.literal THEN
  1404. GetCh (ch) ;
  1405. IF NOT ((ch = Esc) OR (ch = keys.charLeft)) THEN
  1406. BufInsert (CursorOff (), ch) ;
  1407. INC (curCol) ;
  1408. restoreOn := FALSE ;
  1409. modified := TRUE
  1410. END
  1411. ELSIF ch = keys.wordLeft THEN
  1412. WordLeft
  1413. ELSIF ch = keys.charLeft THEN
  1414. CharLeft
  1415. ELSIF ch = keys.charRight THEN
  1416. CharRight
  1417. ELSIF ch = keys.wordRight THEN
  1418. WordRight
  1419. ELSIF ch = keys.lineUp THEN
  1420. LineUp
  1421. ELSIF ch = keys.lineDown THEN
  1422. LineDown
  1423. ELSIF ch = keys.scrollUp THEN
  1424. ScrollUp
  1425. ELSIF ch = keys.scrollDown THEN
  1426. ScrollDown
  1427. ELSIF ch = keys.pageUp THEN
  1428. PageUp
  1429. ELSIF ch = keys.pageDown THEN
  1430. PageDown
  1431. ELSIF ch = keys.insertToggle THEN
  1432. insertMode := NOT insertMode
  1433. ELSIF ch = keys.deleteChar THEN
  1434. DeleteChar
  1435. ELSIF (ch = Del) OR (ch = keys.deleteLeft) THEN
  1436. DeleteLeft
  1437. ELSIF ch = keys.deleteWord THEN
  1438. DeleteWord
  1439. ELSIF ch = keys.insertBreak THEN
  1440. InsertBreak
  1441. ELSIF ch = keys.deleteLine THEN
  1442. DeleteLine
  1443. ELSIF ch = keys.repeatLast THEN
  1444. RepeatLast
  1445. ELSIF ch = keys.cancel THEN
  1446. Beep
  1447. ELSIF ch = keys.autoTab THEN
  1448. AutoTab
  1449. ELSIF (ch = keys.newLine) OR (ch = C_J) THEN
  1450. NewLine
  1451. ELSIF (ch >= " ") AND (ch <= "~") THEN
  1452. TypeChar (ch)
  1453. END ;
  1454. IF endEdit THEN EXIT END
  1455. END
  1456. END MainLoop ;
  1457. (* ------------------------------------------------------------------ *)
  1458. PROCEDURE Run (drv : CHAR ; VAR fileName : ARRAY OF CHAR ;
  1459. VAR changed : BOOLEAN) ;
  1460. VAR i : CARDINAL ;
  1461. kok : BOOLEAN ;
  1462. BEGIN
  1463. (* The key map: TP3.KEY beside the shell, or the built-in defaults. Read
  1464. once per Run (the file can change between edits without restarting). *)
  1465. Keys.Load ("TP3.KEY", keys, kok) ;
  1466. drive := drv ;
  1467. i := 0 ;
  1468. LOOP
  1469. fname [i] := fileName [i] ;
  1470. IF (i >= HIGH (fname)) OR (i >= HIGH (fileName))
  1471. OR (fileName [i] = 0C) THEN EXIT END ;
  1472. INC (i)
  1473. END ;
  1474. IF gotoPend THEN
  1475. gotoPend := FALSE ;
  1476. OffToPos (gotoOff, curLine, curCol) ;
  1477. ClampCursor
  1478. ELSE
  1479. curLine := 0 ;
  1480. curCol := 0
  1481. END ;
  1482. topLine := 0 ;
  1483. colOff := 0 ;
  1484. insertMode := TRUE ;
  1485. autoIndent := TRUE ;
  1486. haveLast := FALSE ;
  1487. blkDef := FALSE ;
  1488. blkShown := FALSE ;
  1489. restoreOn := FALSE ;
  1490. lastFind := FALSE ;
  1491. modified := FALSE ;
  1492. MainLoop ;
  1493. changed := modified
  1494. END Run ;
  1495. END Editor.