editor.mod 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. MODULE editor;
  2. (* based on the kilo editor building course*)
  3. (* Step 69 *)
  4. (*** IMPORTS ***)
  5. IMPORT IO, termios, FIO, libc, CharClass, NumberIO, ASCII ;
  6. IMPORT STextIO, Strings, SYSTEM, SplitV1, Storage, MemUtils, ProgramArgs;
  7. FROM SeqFile IMPORT OpenRead, OpenWrite, OpenResults, Close, ChanId, read, write;
  8. FROM IOResult IMPORT ReadResults, ReadResult;
  9. FROM TextIO IMPORT ReadString, ReadRestLine, SkipLine, ReadToken;
  10. FROM Storage IMPORT ALLOCATE;
  11. (*** data ***)
  12. TYPE
  13. (* display buffer *)
  14. abufType = RECORD
  15. b : SYSTEM.ADDRESS;
  16. len : CARDINAL;
  17. END;
  18. abufTypePtr = POINTER TO abufType;
  19. (* line structure : linked list *)
  20. erowPtr = POINTER TO erow;
  21. erow = RECORD
  22. size : CARDINAL;
  23. chars : SYSTEM.ADDRESS;
  24. next, previous : erowPtr;
  25. END;
  26. CONST
  27. KILO_VERSION = "0.0.1";
  28. (* escape sequences length *)
  29. clearScreenStrLen = 4;
  30. cursorHomeStrLen = 3;
  31. tildeStrLen = 3;
  32. requestCursorPosStrLen = 4;
  33. cursorOnStrLen = 6;
  34. cursorOffStrLen = 6;
  35. eraseLineStrLen = 3;
  36. nextLineDownPos1Len = 4;
  37. (* fct key mapping*)
  38. ARROW_LEFT = 1000;
  39. ARROW_RIGHT = 1001;
  40. ARROW_UP = 1002;
  41. ARROW_DOWN = 1003;
  42. HOME_KEY = 1004;
  43. END_KEY = 1005;
  44. PAGE_UP = 1006;
  45. PAGE_DOWN = 1007;
  46. DEL_KEY = 1008;
  47. VAR
  48. editorConfig : RECORD
  49. screenrows : CARDINAL;
  50. screencols : CARDINAL;
  51. posX : CARDINAL;
  52. posY : CARDINAL;
  53. theTermios : termios.TERMIOS;
  54. rows : erowPtr;
  55. lastRow : erowPtr;
  56. numrows : CARDINAL;
  57. rowoff : CARDINAL;
  58. END;
  59. theRow : erow;
  60. theRowPtr : erowPtr;
  61. (* termios*)
  62. c : CHAR;
  63. TCSAFLUSH : INTEGER;
  64. (* Split structure *)
  65. theStructure : SplitV1.Structure;
  66. (* utility *)
  67. i : CARDINAL;
  68. (* escape sequences *)
  69. clearScreenStr : ARRAY [0..3] OF CHAR;
  70. cursorHomeStr : ARRAY [0..2] OF CHAR;
  71. tildeStr : ARRAY [0..2] OF CHAR;
  72. requestCursorPosStr : ARRAY [0..3] OF CHAR;
  73. cursorposStr : ARRAY [0..15] OF CHAR;
  74. cursorOnStr : ARRAY [0..5] OF CHAR;
  75. cursorOffStr : ARRAY [0..5] OF CHAR;
  76. eraseLineStr : ARRAY [0..2] OF CHAR;
  77. nextLineDownPos1 : ARRAY [0..3] OF CHAR;
  78. (* temporary variables *)
  79. tmpString : ARRAY [0..15] OF CHAR;
  80. tmpString1 : ARRAY [0..15] OF CHAR;
  81. tmpString2 : Strings.String1;
  82. cc : Strings.String1;
  83. (* error and information message string *)
  84. message : ARRAY [0..79] OF CHAR;
  85. (* cli arguments *)
  86. theArgChanId : ChanId;
  87. theArgument : ARRAY [0..20] OF CHAR;
  88. theFileName : ARRAY [0..20] OF CHAR;
  89. argumentNumber : CARDINAL;
  90. (******************** Utilities *****************)
  91. PROCEDURE Card2Str ( x : CARDINAL; VAR str : ARRAY OF CHAR);
  92. BEGIN
  93. IF x < 10 THEN
  94. NumberIO.CardToStr(x,1,str);
  95. ELSIF x < 100 THEN
  96. NumberIO.CardToStr(x,2,str);
  97. ELSE
  98. NumberIO.CardToStr(x,3,str);
  99. END;
  100. END Card2Str;
  101. (******************** Buffer ******************)
  102. PROCEDURE abufInit( VAR ptr : abufTypePtr);
  103. BEGIN
  104. Storage.ALLOCATE(ptr,SYSTEM.TSIZE(abufType) );
  105. ptr^.b := NIL;
  106. ptr^.len := 0;
  107. END abufInit;
  108. PROCEDURE abAppend (VAR ptr : abufTypePtr; theSourceAdress: SYSTEM.ADDRESS; theSourceLen : CARDINAL );
  109. BEGIN
  110. IF ptr <> NIL THEN
  111. IF ptr^.b = NIL THEN
  112. Storage.ALLOCATE(ptr^.b,theSourceLen);
  113. ELSE
  114. Storage.REALLOCATE( ptr^.b,ptr^.len + theSourceLen);
  115. END;
  116. MemUtils.MemCopy(theSourceAdress,theSourceLen, SYSTEM.ADDADR(ptr^.b,ptr^.len));
  117. ptr^.len := ptr^.len + theSourceLen;
  118. END;
  119. END abAppend;
  120. PROCEDURE abFree (VAR ptr : abufTypePtr);
  121. BEGIN
  122. Storage.DEALLOCATE(ptr^.b, ptr^.len);
  123. Storage.DEALLOCATE(ptr,SYSTEM.TSIZE(abufType));
  124. END abFree;
  125. (*** Terminal ***)
  126. PROCEDURE CtrlKey(c: CHAR) : CARDINAL;
  127. VAR
  128. n : BITSET;
  129. BEGIN
  130. n := BITSET(ORD(c));
  131. n := n * BITSET(1FH);
  132. RETURN CARDINAL(n)
  133. END CtrlKey;
  134. PROCEDURE die (s : ARRAY OF CHAR);
  135. BEGIN
  136. libc.write(FIO.StdOut,SYSTEM.ADR(cursorHomeStr), cursorHomeStrLen);
  137. libc.write(FIO.StdOut,SYSTEM.ADR(clearScreenStr), clearScreenStrLen);
  138. libc.perror(s);
  139. FOR i := 0 TO Strings.Length(s) DO
  140. IO.Write(s[i])
  141. END;
  142. HALT
  143. END die;
  144. PROCEDURE disablerawMode() : INTEGER;
  145. VAR
  146. result : INTEGER;
  147. BEGIN
  148. IO.BufferedMode(0,TRUE);
  149. IO.BufferedMode(1,TRUE);
  150. result := termios.tcsetattr(FIO.StdIn, TCSAFLUSH, editorConfig.theTermios);
  151. IF result = -1 THEN
  152. editorConfig.theTermios := termios.KillTermios(editorConfig.theTermios);
  153. die("tcsetattr")
  154. ELSE
  155. RETURN result
  156. END;
  157. END disablerawMode;
  158. PROCEDURE enablerawMode;
  159. BEGIN
  160. libc.atexit(disablerawMode);
  161. editorConfig.theTermios := termios.InitTermios();
  162. IF termios.tcgetattr(FIO.StdIn, editorConfig.theTermios) = -1 THEN
  163. die("tcgetattr")
  164. END;
  165. IO.UnBufferedMode(0,TRUE);
  166. IO.UnBufferedMode(1,TRUE);
  167. END enablerawMode;
  168. PROCEDURE editorReadKey() : CARDINAL;
  169. VAR
  170. c : CHAR;
  171. d : CHAR;
  172. num : CARDINAL;
  173. (*
  174. The Home key could be sent as <esc>[1~, <esc>[7~, <esc>[H, or
  175. <esc>OH (this is the letter O followed by H).
  176. Similarly, the End key could be sent as <esc>[4~, <esc>[8~,
  177. <esc>[F, or <esc>OF (this is the letter O followed by F).
  178. *)
  179. BEGIN
  180. IO.Read(c);
  181. IF c = ASCII.esc THEN
  182. IO.Read(c);
  183. (* mode := command; *)
  184. IF c = "[" THEN
  185. IO.Read(c);
  186. IF (c > "0") AND (c <"9") THEN
  187. IO.Read(d);
  188. IF d = "~" THEN
  189. CASE c OF
  190. "4", "8" : num := END_KEY; |
  191. "1", "7" : num := HOME_KEY; |
  192. "5" : num := PAGE_UP; |
  193. "6" : num := PAGE_DOWN;|
  194. "3" : num := DEL_KEY
  195. END;
  196. END;
  197. ELSIF (c = "A") OR (c = "B") OR (c = "C") OR (c = "D") OR (c = "F") OR (c = "H") THEN
  198. CASE c OF
  199. "A" : num := ARROW_UP; |
  200. "B" : num := ARROW_DOWN ; |
  201. "C" : num := ARROW_RIGHT; |
  202. "D" : num := ARROW_LEFT; |
  203. "F" : num := END_KEY; |
  204. "H" : num := HOME_KEY;
  205. END;
  206. ELSIF c = "O" THEN
  207. IO.Read(c);
  208. CASE c OF
  209. "F" : num := END_KEY; |
  210. "H" : num := HOME_KEY;
  211. END;
  212. ELSE
  213. message := ("key not recognized");
  214. END;
  215. (* mode := move; *)
  216. (* <esc>OF <esc>OH à voir !*)
  217. END;
  218. RETURN num;
  219. ELSE
  220. (* mode := edit; *)
  221. RETURN ORD(c)
  222. END;
  223. END editorReadKey;
  224. (*** row operations ***)
  225. PROCEDURE editorAppendRow(VAR line : ARRAY OF CHAR);
  226. VAR
  227. size : CARDINAL;
  228. BEGIN
  229. NEW(theRowPtr);
  230. size := Strings.Length(line);
  231. theRowPtr^.size := size;
  232. Storage.ALLOCATE(theRowPtr^.chars, size);
  233. MemUtils.MemCopy(SYSTEM.ADR(line),size,theRowPtr^.chars);
  234. theRowPtr^.next := NIL;
  235. (* if no lines were allocated *)
  236. IF editorConfig.rows = NIL THEN
  237. editorConfig.rows := theRowPtr;
  238. theRowPtr^.previous := editorConfig.rows;
  239. ELSE (* there are already some lines in the structure *)
  240. theRowPtr^.previous := editorConfig.lastRow;
  241. editorConfig.lastRow^.next := theRowPtr;
  242. END;
  243. editorConfig.lastRow := theRowPtr;
  244. INC(editorConfig.numrows);
  245. END editorAppendRow;
  246. (******************** file I/O ***************)
  247. PROCEDURE editorOpen( fileName : ARRAY OF CHAR);
  248. VAR
  249. theChanId : ChanId;
  250. theResult : OpenResults;
  251. line : ARRAY[0..255] OF CHAR;
  252. BEGIN
  253. (* PROCEDURE OpenRead (VAR cid: ChanId; name: ARRAY OF CHAR; flags: FlagSet; VAR res: OpenResults); *)
  254. OpenRead(theChanId, fileName, read, theResult);
  255. (* test du résultat*)
  256. IF theResult = noSuchFile THEN
  257. die("No such file")
  258. ELSIF theResult=opened THEN (* reading the lines ... *)
  259. (* first line *)
  260. line := "";
  261. ReadString(theChanId, line);
  262. (* testing the result *)
  263. WHILE ReadResult(theChanId) <> endOfInput DO
  264. (* testing end of line *)
  265. IF ReadResult(theChanId) = endOfLine THEN
  266. (* we have to 'skip' the endOfLine ... so strange *)
  267. SkipLine(theChanId);
  268. END;
  269. editorAppendRow(line);
  270. (* reading next line *)
  271. ReadString(theChanId, line);
  272. END;
  273. ELSE
  274. die("Error opening the file");
  275. END;
  276. Close(theChanId);
  277. END editorOpen;
  278. PROCEDURE editorMoveCursor(c : CARDINAL);
  279. BEGIN
  280. WITH editorConfig DO
  281. CASE c OF
  282. ARROW_UP, ORD("e") : IF posY > 1 THEN DEC(posY) END; |
  283. ARROW_DOWN, ORD("x") : IF posY < numrows -1 THEN INC(posY) END; |
  284. ARROW_LEFT, ORD("s") : IF posX > 1 THEN DEC(posX) END; |
  285. ARROW_RIGHT, ORD("d") : IF posX < (screencols - 1) THEN INC(posX) END; |
  286. PAGE_UP : posY := 1; |
  287. PAGE_DOWN : posY := screenrows; |
  288. HOME_KEY : posX := 1; |
  289. END_KEY : posX := screencols;
  290. END;
  291. END
  292. END editorMoveCursor;
  293. PROCEDURE editorProcessKeypress;
  294. VAR
  295. c : CARDINAL;
  296. BEGIN
  297. c := editorReadKey();
  298. CASE c OF
  299. 17 : libc.write(FIO.StdOut,SYSTEM.ADR(cursorHomeStr), cursorHomeStrLen);
  300. libc.write(FIO.StdOut,SYSTEM.ADR(clearScreenStr), clearScreenStrLen);
  301. HALT; |
  302. ORD("e"),
  303. ORD("x"),
  304. ORD("s"),
  305. ORD("d"),
  306. HOME_KEY,
  307. END_KEY,
  308. PAGE_UP,
  309. PAGE_DOWN,
  310. ARROW_UP,
  311. ARROW_DOWN,
  312. ARROW_LEFT,
  313. ARROW_RIGHT : editorMoveCursor(c);
  314. END
  315. END editorProcessKeypress;
  316. (*** Output ***)
  317. PROCEDURE editorScroll;
  318. BEGIN
  319. WITH editorConfig DO
  320. IF posY < rowoff THEN
  321. rowoff := posY;
  322. END;
  323. IF posY >= (rowoff + screenrows) THEN
  324. rowoff := posY - screenrows +1;
  325. END;
  326. END;
  327. END editorScroll;
  328. PROCEDURE editorDrawRows(VAR ptr : abufTypePtr);
  329. CONST
  330. tilde = "~";
  331. VAR
  332. y : CARDINAL;
  333. welcome : ARRAY [0..79] OF CHAR;
  334. welcomeLen : CARDINAL;
  335. blanks : CARDINAL;
  336. blanksStr : ARRAY[0..79] OF CHAR;
  337. blankStrLength : CARDINAL;
  338. len : CARDINAL;
  339. fileRow : CARDINAL;
  340. BEGIN
  341. (* Creation of the about text *)
  342. welcome := "Kilo editor -- version ";
  343. Strings.Concat(welcome,KILO_VERSION,welcome);
  344. welcomeLen := Strings.Length(welcome);
  345. Strings.Concat(welcome,nextLineDownPos1,welcome);
  346. (* centering the welcome text *)
  347. blanks := (editorConfig.screencols - welcomeLen) /2;
  348. cc[0] := " ";
  349. blanksStr := "";
  350. FOR i := 0 TO blanks DO
  351. Strings.Concat(blanksStr,cc,blanksStr);
  352. END;
  353. blankStrLength := Strings.Length(blanksStr);
  354. (* display of the page *)
  355. (* setting the pointer to the beginning of the part being displayed *)
  356. (* this is the start of the text ... *)
  357. theRowPtr := editorConfig.rows; (* this is the start of the text ... *)
  358. FOR y := 1 TO editorConfig.screenrows DO
  359. fileRow := y + editorConfig.rowoff;
  360. IF fileRow >= editorConfig.numrows THEN
  361. IF (y = (editorConfig.screenrows / 3)) AND (editorConfig.numrows = 0) THEN
  362. abAppend(ptr,SYSTEM.ADR(blanksStr), blankStrLength);
  363. IF welcomeLen > editorConfig.screencols THEN welcomeLen := editorConfig.screencols END;
  364. abAppend(ptr,SYSTEM.ADR(welcome),welcomeLen );
  365. ELSE
  366. abAppend(ptr,SYSTEM.ADR(tilde), 1);
  367. END;
  368. ELSE
  369. (* transfert des données dans le buffer *)
  370. IF y < editorConfig.rowoff THEN
  371. theRowPtr := theRowPtr^.next;
  372. ELSE
  373. len := theRowPtr^.size;
  374. IF len > editorConfig.screencols THEN
  375. len := editorConfig.screencols
  376. END;
  377. (* displaying the text lines begining at fileRow*)
  378. abAppend(ptr,theRowPtr^.chars,len);
  379. theRowPtr := theRowPtr^.next;
  380. END;
  381. END;
  382. abAppend(ptr,SYSTEM.ADR(eraseLineStr),eraseLineStrLen );
  383. IF y < editorConfig.screenrows THEN
  384. abAppend(ptr,SYSTEM.ADR(nextLineDownPos1),nextLineDownPos1Len );
  385. END;
  386. END;
  387. END editorDrawRows;
  388. PROCEDURE editorRefreshScreen;
  389. VAR
  390. ptr : abufTypePtr;
  391. BEGIN
  392. editorScroll;
  393. abufInit(ptr);
  394. abAppend(ptr,SYSTEM.ADR(cursorOffStr), cursorOffStrLen);
  395. abAppend(ptr,SYSTEM.ADR(cursorHomeStr), cursorHomeStrLen);
  396. editorDrawRows(ptr);
  397. (* cursorpos(1,1,FALSE); *)
  398. cursorpos(editorConfig.posX,editorConfig.posY,FALSE);
  399. abAppend(ptr,SYSTEM.ADR(cursorposStr), Strings.Length(cursorposStr));
  400. abAppend(ptr,SYSTEM.ADR(cursorOnStr), cursorOnStrLen);
  401. libc.write(FIO.StdOut,ptr^.b,ptr^.len);
  402. abFree(ptr);
  403. END editorRefreshScreen;
  404. PROCEDURE requestCursorPosition(VAR x,y : CARDINAL);
  405. VAR
  406. c : CHAR;
  407. str : ARRAY [0..15] OF CHAR;
  408. BEGIN
  409. str := "";
  410. SplitV1.InitStructure(theStructure);
  411. libc.write(FIO.StdOut,SYSTEM.ADR(requestCursorPosStr),requestCursorPosStrLen);
  412. (* getting answer *)
  413. str := "";
  414. REPEAT
  415. IO.Read(c);
  416. cc[0] := c;
  417. Strings.Concat(str,cc,str);
  418. UNTIL c = "R";
  419. Strings.Delete(str,0,2);
  420. Strings.Delete(str,Strings.Length(str) -1 ,1);
  421. SplitV1.SplitStr(str, ";", theStructure);
  422. NumberIO.StrToCard(theStructure[0].element,y);
  423. NumberIO.StrToCard(theStructure[1].element,x);
  424. END requestCursorPosition;
  425. PROCEDURE cursorpos(x,y : CARDINAL; emit : BOOLEAN);
  426. BEGIN
  427. cursorposStr := "";
  428. Strings.Concat(cursorposStr, ASCII.esc,cursorposStr);
  429. Strings.Concat(cursorposStr, "[",cursorposStr);
  430. tmpString := "";
  431. Strings.Concat(tmpString,cursorposStr, tmpString);
  432. tmpString1 := "";
  433. Card2Str(y,tmpString1);
  434. Strings.Concat(tmpString,tmpString1, tmpString);
  435. tmpString2[0] := ";";
  436. Strings.Concat(tmpString, tmpString2,tmpString);
  437. tmpString1 := "";
  438. Card2Str(x,tmpString1);
  439. Strings.Concat(tmpString,tmpString1, tmpString);
  440. tmpString2[0] := "H";
  441. Strings.Concat(tmpString, tmpString2,tmpString);
  442. cursorposStr := tmpString;
  443. editorConfig.posX := x;
  444. editorConfig.posY := y;
  445. IF emit THEN
  446. libc.write(FIO.StdOut,SYSTEM.ADR(tmpString),Strings.Length(tmpString));
  447. END;
  448. END cursorpos;
  449. PROCEDURE getWindowSize(VAR y : CARDINAL; VAR x: CARDINAL);
  450. BEGIN
  451. cursorpos(999,999, TRUE);
  452. requestCursorPosition(x,y);
  453. END getWindowSize;
  454. (*** Input ***)
  455. (*** Init ***)
  456. PROCEDURE InitScreenEscapes;
  457. VAR temp : ARRAY[0..10] OF CHAR;
  458. BEGIN
  459. cursorHomeStr := "";
  460. Strings.Concat(cursorHomeStr,ASCII.esc,cursorHomeStr);
  461. Strings.Concat(cursorHomeStr,"[H",cursorHomeStr);
  462. clearScreenStr := "";
  463. Strings.Concat(clearScreenStr,ASCII.esc,clearScreenStr);
  464. Strings.Concat(clearScreenStr,"[2J",clearScreenStr);
  465. tildeStr[0] := "~";
  466. tildeStr[1] := CHR(10);
  467. tildeStr[2] := CHR(13);
  468. cursorposStr := "";
  469. Strings.Concat(cursorposStr,ASCII.esc,cursorposStr);
  470. Strings.Concat(cursorposStr,"[",cursorposStr);
  471. requestCursorPosStr := "";
  472. Strings.Concat(requestCursorPosStr,ASCII.esc,requestCursorPosStr);
  473. Strings.Concat(requestCursorPosStr,"[6n",requestCursorPosStr);
  474. cursorOnStr := "";
  475. Strings.Concat(cursorOnStr,ASCII.esc,cursorOnStr);
  476. Strings.Concat(cursorOnStr,"[?25h",cursorOnStr);
  477. cursorOffStr := "";
  478. Strings.Concat(cursorOffStr,ASCII.esc,cursorOffStr);
  479. Strings.Concat(cursorOffStr,"[?25l",cursorOffStr);
  480. eraseLineStr := "";
  481. Strings.Concat(eraseLineStr,ASCII.esc,eraseLineStr);
  482. Strings.Concat(eraseLineStr,"[K",eraseLineStr);
  483. (* ESC[#E moves cursor to beginning of next line, # lines down *)
  484. nextLineDownPos1 := "";
  485. Strings.Concat(nextLineDownPos1,ASCII.esc,nextLineDownPos1);
  486. Strings.Concat(nextLineDownPos1,"[1E",nextLineDownPos1);
  487. END InitScreenEscapes;
  488. PROCEDURE InitEditor;
  489. BEGIN
  490. TCSAFLUSH := termios.tcsflush ();
  491. InitScreenEscapes;
  492. editorConfig.posX := 1;
  493. editorConfig.posY := 1;
  494. getWindowSize(editorConfig.screenrows, editorConfig.screencols);
  495. editorConfig.posX := 1;
  496. editorConfig.posY := 1;
  497. editorConfig.numrows := 0;
  498. editorConfig.rows := NIL;
  499. editorConfig.rowoff := 1;
  500. libc.write(FIO.StdOut,SYSTEM.ADR(clearScreenStr),4);
  501. END InitEditor;
  502. BEGIN
  503. enablerawMode;
  504. InitEditor;
  505. editorRefreshScreen;
  506. (* fetching the 2nd argument (fileName) *)
  507. theFileName := "";
  508. theArgChanId := ProgramArgs.ArgChan ();
  509. argumentNumber := 0;
  510. WHILE ProgramArgs.IsArgPresent() DO
  511. INC(argumentNumber);
  512. ReadToken(ProgramArgs.ArgChan(), theArgument);
  513. IF argumentNumber = 2 THEN
  514. theFileName := theArgument;
  515. END;
  516. ProgramArgs.NextArg ();
  517. END;
  518. IF argumentNumber >= 2 THEN
  519. editorOpen(theFileName);
  520. END;
  521. LOOP
  522. editorRefreshScreen;
  523. editorProcessKeypress;
  524. END;
  525. END editor.