Editor.mod 38 KB

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