SSHClient.Mod 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163
  1. MODULE SSHClient; (** AUTHOR "G.F."; PURPOSE "Secure Shell"; *)
  2. (* derived from WMVT100.Mod by ejz, modified to use an SSH connection instead of telnet *)
  3. IMPORT
  4. WMWindowManager, WMComponents, WMStandardComponents, WMG := WMGraphics,
  5. WMPopups, WMMessages, WMEditors, WMRectangles, Commands, Files,
  6. Strings, Texts, Inputs, Streams, Out := KernelLog, SSHAuthorize, SSH, Beep;
  7. CONST
  8. TerminalWidth = 80;
  9. TerminalHeight = 24;
  10. Border = 2; BoxW = 8; BoxH = 18;
  11. Left = 0; Right = 2;
  12. Underscore = 0; Blink = 1;
  13. CursorKeyMode = 0; AppKeypadMode = 1; AutoWrapMode = 2;
  14. ESC = 1BX; DEL = 07FX; CR = 0DX; NL = 0AX;
  15. VAR
  16. hexd: ARRAY 17 OF CHAR;
  17. TYPE
  18. WindowCloser = PROCEDURE {DELEGATE};
  19. Attribute = POINTER TO RECORD
  20. fnt: WMG.Font;
  21. bg, fg: WMG.Color;
  22. special: SET (* 0: underscore *)
  23. END;
  24. Char = RECORD
  25. attr: Attribute;
  26. char: LONGINT
  27. END;
  28. Data = POINTER TO ARRAY OF Char;
  29. Line = POINTER TO RECORD
  30. data: Data;
  31. t, b: LONGINT;
  32. next: Line
  33. END;
  34. Position = RECORD
  35. line: Line; ofs: LONGINT
  36. END;
  37. Frame = OBJECT (WMComponents.VisualComponent)
  38. VAR
  39. rows, cols, boxW, boxH, dX, dY: LONGINT;
  40. chan: SSH.Channel;
  41. r: Streams.Reader; w: Streams.Writer;
  42. mode: SET;
  43. closeWindow: WindowCloser;
  44. first, top: Line; bg: WMG.Color;
  45. scrollTop, scrollBottom: Line;
  46. scrollBegin, scrollEnd: LONGINT;
  47. tabs: POINTER TO ARRAY OF BOOLEAN;
  48. attr: Attribute;
  49. cursor: Position;
  50. old: RECORD
  51. attr: Attribute;
  52. offs: LONGINT;
  53. row: LONGINT
  54. END;
  55. sel: RECORD
  56. beg, end: Position
  57. END;
  58. popup: WMPopups.Popup;
  59. PROCEDURE EFill;
  60. (* VAR i, j, w: INTEGER; line: Line; char: Char; *)
  61. BEGIN
  62. (*
  63. i := 1; w := cols; char.ch := "E"; char.attr := none;
  64. WHILE i <= rows DO j := 1; line := t.line[i]; line.len := w;
  65. WHILE j <= w DO line.ch[j] := char; INC(j) END;
  66. INC(i)
  67. END;
  68. t.notify(t, update, 1, 1, t.height, t.width, t.cursor)
  69. *)
  70. END EFill;
  71. PROCEDURE GetCol(): LONGINT;
  72. BEGIN {EXCLUSIVE}
  73. RETURN cursor.ofs
  74. END GetCol;
  75. PROCEDURE GetRow(): LONGINT;
  76. VAR l: Line; row: LONGINT;
  77. BEGIN {EXCLUSIVE}
  78. l := top; row := 0;
  79. WHILE l # cursor.line DO
  80. l := l.next; INC( row )
  81. END;
  82. RETURN row
  83. END GetRow;
  84. PROCEDURE GetNewLine(): Line;
  85. VAR line: Line; i: LONGINT; ch: Char;
  86. BEGIN
  87. NEW( line ); line.next := NIL;
  88. NEW( line.data, cols );
  89. ch.attr := attr; ch.char := 0;
  90. FOR i := 0 TO cols - 1 DO line.data[i] := ch END;
  91. RETURN line
  92. END GetNewLine;
  93. PROCEDURE AppendLine( pred: Line ): Line;
  94. VAR line: Line;
  95. BEGIN
  96. line := GetNewLine();
  97. IF pred # NIL THEN
  98. line.next := pred.next;
  99. pred.next := line;
  100. IF pred.b >= dY THEN line.t := pred.b ELSE line.t := dY END
  101. ELSE
  102. line.t := dY;
  103. END;
  104. line.b := line.t + boxH;
  105. RETURN line
  106. END AppendLine;
  107. PROCEDURE UpdateBox(line: Line; ofs: LONGINT);
  108. VAR update: WMG.Rectangle;
  109. BEGIN
  110. update.l := dX + ofs*boxW; update.r := update.l + boxW;
  111. update.t := line.t; update.b := line.b;
  112. InvalidateRect(update)
  113. END UpdateBox;
  114. PROCEDURE UpdateRect(al, bl: Line; aofs, bofs: LONGINT; cur: SET);
  115. VAR tl: Line; tofs: LONGINT; update: WMG.Rectangle; swapl, swapo: BOOLEAN;
  116. BEGIN
  117. swapl := FALSE; swapo := FALSE;
  118. IF al # bl THEN
  119. tl := al;
  120. WHILE (tl # NIL) & (tl # bl) DO
  121. tl := tl.next
  122. END;
  123. IF tl = NIL THEN swapl := TRUE; tl := al; al := bl; bl := tl END
  124. END;
  125. IF aofs > bofs THEN swapo := TRUE; tofs := aofs; aofs := bofs; bofs := tofs END;
  126. update.l := dX + aofs*boxW; update.r := dX + bofs*boxW + boxW;
  127. update.t := al.t; update.b := bl.b;
  128. IF cur # {} THEN
  129. IF 1 IN cur THEN
  130. IF swapl THEN cursor.line := bl ELSE cursor.line := al END
  131. ELSIF 2 IN cur THEN
  132. IF swapl THEN cursor.line := al ELSE cursor.line := bl END
  133. END;
  134. IF 3 IN cur THEN
  135. IF swapo THEN cursor.ofs := bofs ELSE cursor.ofs := aofs END
  136. ELSIF 4 IN cur THEN
  137. IF swapo THEN cursor.ofs := aofs ELSE cursor.ofs := bofs END
  138. END
  139. END;
  140. InvalidateRect(update)
  141. END UpdateRect;
  142. PROCEDURE UpdateAll;
  143. VAR update: WMG.Rectangle;
  144. BEGIN
  145. update.l := 0; update.r := bounds.GetWidth();
  146. update.t := 0; update.b := bounds.GetHeight();
  147. InvalidateRect(update)
  148. END UpdateAll;
  149. PROCEDURE WriteChars( CONST buf: ARRAY OF CHAR; n: LONGINT);
  150. VAR prev, l: Line; i, ofs: LONGINT; wrap: BOOLEAN;
  151. BEGIN {EXCLUSIVE}
  152. wrap := FALSE;
  153. l := cursor.line; ofs := cursor.ofs; i := 0;
  154. LOOP
  155. WHILE (i < n) & (ofs < cols) DO
  156. l.data[ofs].attr := attr;
  157. l.data[ofs].char := ORD( buf[i] );
  158. INC( ofs ); INC( i )
  159. END;
  160. IF (i < n) & (AutoWrapMode IN mode) THEN
  161. prev := l; l := l.next; ofs := 0; wrap := TRUE;
  162. IF l = NIL THEN
  163. l := AppendLine( prev )
  164. END
  165. ELSE
  166. EXIT
  167. END
  168. END;
  169. IF wrap THEN
  170. cursor.ofs := ofs;
  171. UpdateRect( cursor.line, l, 0, cols-1, {2} )
  172. ELSE
  173. UpdateRect( cursor.line, l, cursor.ofs, ofs, {4} )
  174. END
  175. END WriteChars;
  176. PROCEDURE Delete;
  177. VAR l: Line; ofs: LONGINT;
  178. BEGIN {EXCLUSIVE}
  179. l := cursor.line; ofs := cursor.ofs;
  180. IF ofs > 0 THEN
  181. DEC( ofs );
  182. l.data[ofs].attr := attr;
  183. l.data[ofs].char := 0;
  184. UpdateRect( l, l, ofs, cursor.ofs, {3} )
  185. END
  186. END Delete;
  187. PROCEDURE GetLine( n: LONGINT ): Line;
  188. VAR line: Line;
  189. BEGIN
  190. line := top;
  191. WHILE (n > 0) & (line # NIL) DO line := line.next; DEC( n ) END;
  192. RETURN line
  193. END GetLine;
  194. PROCEDURE GetLastLine( ): Line;
  195. VAR line: Line;
  196. BEGIN
  197. line := top;
  198. WHILE line.next # NIL DO line := line.next END;
  199. RETURN line
  200. END GetLastLine;
  201. PROCEDURE SetScrollRegion;
  202. BEGIN
  203. scrollTop := GetLine( scrollBegin );
  204. scrollBottom := GetLine( scrollEnd );
  205. END SetScrollRegion;
  206. PROCEDURE Goto( row, col: LONGINT );
  207. VAR l: Line; hl, lines: LONGINT;
  208. BEGIN {EXCLUSIVE}
  209. IF col < 0 THEN col := 0 ELSIF col >= cols THEN col := cols - 1 END;
  210. l := first; hl := 1;
  211. WHILE l # top DO INC( hl ); l := l.next END;
  212. WHILE hl > 512 DO first := first.next; DEC( hl ) END; (* limit history *)
  213. lines := 1;
  214. WHILE row > 0 DO
  215. IF l.next = NIL THEN
  216. l := AppendLine( l );
  217. ELSE
  218. l := l.next
  219. END;
  220. DEC( row ); INC( lines )
  221. END;
  222. IF lines > rows THEN
  223. top := top.next;
  224. cursor.line := l; cursor.ofs := 0;
  225. UpdateAll()
  226. ELSE
  227. UpdateRect( cursor.line, l, cursor.ofs, col, {2, 4} )
  228. END;
  229. SetScrollRegion;
  230. SetOffsets;
  231. END Goto;
  232. PROCEDURE SetOffsets;
  233. VAR l: Line; y: LONGINT;
  234. BEGIN
  235. l := top; y := dY;
  236. REPEAT
  237. l.t := y; INC( y, BoxH ); l.b := y;
  238. l := l.next
  239. UNTIL l = NIL
  240. END SetOffsets;
  241. PROCEDURE MoveLines( down: BOOLEAN );
  242. VAR prev, l, newtop: Line;
  243. BEGIN
  244. l := first; prev := NIL;
  245. WHILE l # scrollTop DO prev := l; l := l.next END;
  246. IF down THEN
  247. l := GetNewLine( );
  248. l.next := scrollTop;
  249. IF prev # NIL THEN
  250. prev.next := l;
  251. IF top = scrollTop THEN top := l END
  252. ELSE first := l; top := l
  253. END;
  254. WHILE (l # scrollBottom) & (l.next # NIL) DO prev := l; l := l.next END;
  255. prev.next := l.next; (* unlink bottom line *)
  256. ELSE (* up *)
  257. WHILE (l # scrollBottom) & (l.next # NIL) DO l := l.next END;
  258. l := AppendLine( l );
  259. newtop := scrollTop.next;
  260. prev.next := newtop; (* unlink top line *)
  261. IF top = scrollTop THEN top := newtop END;
  262. IF first = scrollTop THEN first := newtop END;
  263. END;
  264. SetScrollRegion;
  265. SetOffsets
  266. END MoveLines;
  267. PROCEDURE Scroll( down: BOOLEAN );
  268. BEGIN {EXCLUSIVE}
  269. MoveLines( down );
  270. IF down THEN
  271. cursor.line := scrollTop; cursor.ofs := 0;
  272. UpdateAll
  273. ELSE
  274. cursor.line := scrollBottom; cursor.ofs := 0;
  275. UpdateAll
  276. END
  277. END Scroll;
  278. PROCEDURE SetMargins( beg, end: LONGINT );
  279. BEGIN {EXCLUSIVE}
  280. scrollBegin := beg - 1;
  281. scrollEnd := end - 1 ;
  282. SetScrollRegion
  283. END SetMargins;
  284. PROCEDURE RightTab;
  285. VAR l: Line; ofs: LONGINT; char: Char;
  286. BEGIN {EXCLUSIVE}
  287. char.attr := attr; char.char := 020H;
  288. l := cursor.line; ofs := cursor.ofs + 1;
  289. WHILE (ofs < cols) & ~tabs[ofs] DO
  290. l.data[ofs] := char; INC( ofs )
  291. END;
  292. IF ofs = cursor.ofs THEN RETURN END;
  293. UpdateRect( l, l, cursor.ofs, ofs, {4} )
  294. END RightTab;
  295. PROCEDURE EraseLine( l: Line; from, to: LONGINT );
  296. VAR i: LONGINT;
  297. BEGIN
  298. i := from;
  299. WHILE i <= to DO
  300. l.data[i].attr := attr; l.data[i].char := 0;
  301. INC( i )
  302. END
  303. END EraseLine;
  304. PROCEDURE Erase( mode: CHAR; CONST par: ARRAY OF LONGINT; n: LONGINT );
  305. BEGIN {EXCLUSIVE}
  306. CASE mode OF
  307. |"J":
  308. sel.beg.line := NIL;
  309. top := GetLastLine();
  310. cursor.line := top; cursor.ofs := 0;
  311. EraseLine( top, 0, cols-1 );
  312. UpdateAll();
  313. SetScrollRegion;
  314. |"K":
  315. IF n = 0 THEN
  316. EraseLine( cursor.line, cursor.ofs, cols-1 );
  317. UpdateRect( cursor.line, cursor.line, cursor.ofs, cols-1, {} )
  318. ELSIF (n = 1) & (par[0] = 1) THEN
  319. EraseLine( cursor.line, 0, cursor.ofs );
  320. UpdateRect( cursor.line, cursor.line, 0, cursor.ofs, {} )
  321. ELSIF (n = 1) & (par[0] = 2) THEN
  322. EraseLine( cursor.line, 0, cols-1 );
  323. UpdateRect( cursor.line, cursor.line, 0, cols-1, {} )
  324. END
  325. END
  326. END Erase;
  327. PROCEDURE NewAttr;
  328. VAR f: Files.File;
  329. BEGIN
  330. NEW(attr); attr.special := {};
  331. f := Files.Old( "Veramono_bd.ttf" );
  332. IF f # NIL THEN
  333. (* attr.fnt := WMG.GetFont( "Dejavumono", 12, {} ) *)
  334. attr.fnt := WMG.GetFont( "Veramono", 12, {0} )
  335. ELSE
  336. attr.fnt := WMG.GetFont( "Courier", 12, {} )
  337. END;
  338. attr.bg := WMG.RGBAToColor( 255, 255, 255, 255 );
  339. attr.fg := WMG.RGBAToColor( 0, 0, 0, 255 )
  340. END NewAttr;
  341. PROCEDURE Bright;
  342. VAR style: SET;
  343. BEGIN
  344. style := attr.fnt.style;
  345. IF ~(WMG.FontBold IN style) THEN
  346. INCL( style, WMG.FontBold );
  347. attr.fnt := WMG.GetFont( attr.fnt.name, attr.fnt.size, style )
  348. ELSE
  349. Out.String("Bright"); Out.Ln()
  350. END
  351. END Bright;
  352. PROCEDURE Dim;
  353. VAR style: SET;
  354. BEGIN
  355. style := attr.fnt.style;
  356. IF WMG.FontBold IN style THEN
  357. EXCL( style, WMG.FontBold );
  358. attr.fnt := WMG.GetFont( attr.fnt.name, attr.fnt.size, style )
  359. ELSE
  360. Out.String("Dim"); Out.Ln()
  361. END
  362. END Dim;
  363. PROCEDURE SetAttributes( CONST attrs: ARRAY OF LONGINT; n: LONGINT );
  364. VAR c: WMG.Color; i: LONGINT;
  365. BEGIN {EXCLUSIVE}
  366. NewAttr();
  367. i := 0;
  368. WHILE i < n DO
  369. CASE attrs[i] OF
  370. |0: (* Reset *) NewAttr()
  371. |1: (* Bright *) Bright()
  372. |2: (* Dim *) Dim()
  373. |4: (* Underscore *) INCL( attr.special, Underscore )
  374. |5: (* Blink *) INCL( attr.special, Blink )
  375. |7: (* Reverse *) c := attr.bg; attr.bg := attr.fg; attr.fg := c
  376. |8: (* Hidden *) attr.fg := attr.bg
  377. ELSE
  378. Out.String("attr "); Out.Int(attrs[i], 0); Out.Ln()
  379. END;
  380. INC(i)
  381. END
  382. END SetAttributes;
  383. PROCEDURE Draw*( canvas: WMG.Canvas );
  384. VAR
  385. l: Line; i, j, dy, bottom: LONGINT; attr: Attribute; char: Char;
  386. box: WMG.Rectangle;
  387. BEGIN {EXCLUSIVE}
  388. canvas.Fill( canvas.clipRect, bg, WMG.ModeCopy );
  389. l := first;
  390. WHILE l # top DO
  391. l.t := MIN(INTEGER); l.b := MIN(INTEGER); l := l.next
  392. END;
  393. attr := NIL; bottom := dY + rows*boxH;
  394. box.t := dY; box.b := dY + boxH; j := 0;
  395. WHILE (l # NIL) & (j < rows) & (box.b <= bottom) DO
  396. l.t := box.t; l.b := box.b;
  397. box.l := dX; box.r := dX + boxW; i := 0;
  398. WHILE i < cols DO
  399. char := l.data[i];
  400. IF char.attr # attr THEN
  401. attr := char.attr;
  402. canvas.SetColor( attr.fg );
  403. canvas.SetFont( attr.fnt );
  404. dy := attr.fnt.GetDescent()
  405. END;
  406. IF attr.bg # bg THEN
  407. canvas.Fill( box, attr.bg, WMG.ModeCopy )
  408. END;
  409. IF char.char # 0 THEN
  410. attr.fnt.RenderChar( canvas, box.l, box.b-dy, char.char )
  411. END;
  412. IF Underscore IN attr.special THEN
  413. canvas.Line( box.l, box.b-dy+1, box.r-1, box.b-dy+1, attr.fg, WMG.ModeCopy )
  414. END;
  415. INC( i ); INC( box.l, boxW ); INC( box.r, boxW )
  416. END;
  417. INC( j ); l := l.next;
  418. INC( box.t, boxH ); INC( box.b, boxH )
  419. END;
  420. WHILE l # NIL DO
  421. l.t := MAX(INTEGER); l.b := MAX(INTEGER); l := l.next
  422. END;
  423. IF hasFocus & (cursor.ofs >= 0) & (cursor.ofs < cols) THEN
  424. l := cursor.line; box.t := l.t; box.b := l.b;
  425. IF box.t < box.b THEN
  426. box.l := dX + cursor.ofs*boxW; box.r := box.l + boxW;
  427. canvas.Fill( box, WMG.RGBAToColor( 255, 0, 0, 192 ), WMG.ModeSrcOverDst )
  428. ELSE
  429. FocusLost
  430. END
  431. END;
  432. IF sel.beg.line # NIL THEN
  433. IF sel.beg.line = sel.end.line THEN
  434. box.l := dX + sel.beg.ofs * boxW; box.r := dX + sel.end.ofs * boxW + boxW;
  435. box.t := sel.beg.line.t; box.b := sel.end.line.b;
  436. canvas.Fill( box, WMG.RGBAToColor( 0, 0, 255, 32 ), WMG.ModeSrcOverDst )
  437. ELSE
  438. box.l := dX + sel.beg.ofs * boxW; box.r := dX + cols * boxW;
  439. box.t := sel.beg.line.t; box.b := sel.beg.line.b;
  440. canvas.Fill( box, WMG.RGBAToColor( 0, 0, 255, 32 ), WMG.ModeSrcOverDst );
  441. l := sel.beg.line.next;
  442. WHILE l # sel.end.line DO
  443. box.l := dX; box.r := dX + cols * boxW;
  444. box.t := l.t; box.b := l.b;
  445. canvas.Fill( box, WMG.RGBAToColor( 0, 0, 255, 32 ), WMG.ModeSrcOverDst );
  446. l := l.next
  447. END;
  448. box.l := dX; box.r := dX + sel.end.ofs * boxW + boxW;
  449. box.t := sel.end.line.t; box.b := sel.end.line.b;
  450. canvas.Fill( box, WMG.RGBAToColor( 0, 0, 255, 32 ), WMG.ModeSrcOverDst )
  451. END
  452. END
  453. END Draw;
  454. PROCEDURE MoveCursor( dr, dc: LONGINT );
  455. VAR col, currrow: LONGINT;
  456. BEGIN
  457. col := GetCol() + dc;
  458. IF col < 0 THEN col := 0 END;
  459. currrow := GetRow();
  460. IF (currrow = scrollEnd) & (dr > 0) THEN
  461. IF currrow < rows - 1 THEN Scroll( FALSE ); Goto( currrow, col )
  462. ELSE Goto( currrow + 1, col )
  463. END
  464. ELSIF (currrow = scrollBegin) & (dr < 0) THEN Scroll( TRUE ); Goto( currrow, col )
  465. ELSE Goto( currrow + dr, col )
  466. END
  467. END MoveCursor;
  468. PROCEDURE ESCSequence(ch: CHAR);
  469. VAR
  470. par: ARRAY 4 OF LONGINT; i, n: LONGINT;
  471. BEGIN
  472. r.Char( ch );
  473. IF ch = "[" THEN
  474. ch := r.Peek(); n := 0;
  475. IF ch = "?" THEN
  476. r.Char( ch ); ch := r.Peek();
  477. IF (ch >= "0") & (ch <= "9") THEN
  478. REPEAT
  479. r.Int( par[n], FALSE ); INC( n );
  480. r.Char( ch )
  481. UNTIL (n >= 4) OR (ch # " ")
  482. END
  483. ELSIF (ch >= "0") & (ch <= "9") THEN
  484. REPEAT
  485. r.Int( par[n], FALSE ); INC( n );
  486. r.Char( ch )
  487. UNTIL (n >= 4) OR (ch # ";")
  488. ELSE
  489. ASSERT( ch < DEL );
  490. r.Char( ch )
  491. END;
  492. CASE ch OF
  493. |"A":
  494. IF n = 1 THEN MoveCursor( -par[0], 0 ) ELSE MoveCursor( -1, 0 ) END
  495. |"B":
  496. IF n = 1 THEN MoveCursor( par[0], 0 ) ELSE MoveCursor( 1, 0 ) END
  497. |"C":
  498. IF n = 1 THEN MoveCursor( 0, par[0] ) ELSE MoveCursor( 0, 1 ) END
  499. |"D":
  500. IF n = 1 THEN MoveCursor( 0, -par[0] ) ELSE MoveCursor( 0, -1 ) END
  501. |"H":
  502. IF n = 2 THEN Goto( par[0] - 1, par[1] - 1 ) ELSE Goto( 0, 0 ) END
  503. |"J", "K":
  504. Erase( ch, par, n )
  505. |"h":
  506. IF n = 1 THEN
  507. IF par[0] = 1 THEN INCL( mode, CursorKeyMode )
  508. ELSIF par[0] = 7 THEN INCL( mode, AutoWrapMode )
  509. END
  510. END
  511. |"l":
  512. IF n = 1 THEN
  513. IF par[0] = 1 THEN EXCL( mode, CursorKeyMode )
  514. ELSIF par[0] = 7 THEN EXCL( mode, AutoWrapMode )
  515. END
  516. END
  517. |"m":
  518. SetAttributes( par, n )
  519. | "r":
  520. SetMargins( par[0], par[1] )
  521. ELSE
  522. Out.Ln; Out.String( "got unknown sequence ESC [ " );
  523. i := 0;
  524. WHILE i < n DO
  525. Out.Int( par[i], 0 ); INC( i );
  526. IF i < n THEN Out.String( " ; " ) END
  527. END;
  528. Out.Char( ch ); Out.Ln;
  529. END
  530. ELSE
  531. CASE ch OF
  532. |"7":
  533. old.attr := attr;
  534. old.offs := GetCol();
  535. old.row := GetRow()
  536. |"8":
  537. IF r.Peek( ) = '#' THEN r.Char( ch ); EFill
  538. ELSE attr := old.attr; Goto( old.row, old.offs )
  539. END
  540. |"=":
  541. INCL( mode, AppKeypadMode )
  542. |">":
  543. EXCL( mode, AppKeypadMode )
  544. |"D":
  545. IF GetRow() = scrollEnd THEN Scroll( FALSE )
  546. ELSE Goto( GetRow() + 1, GetCol() )
  547. END
  548. |"M":
  549. IF GetRow() = scrollBegin THEN Scroll( TRUE )
  550. ELSE Goto( GetRow() - 1, GetCol() )
  551. END
  552. ELSE
  553. Out.String("got unknown sequence ESC ");
  554. IF (ch >= ' ') & (ch <= '~') THEN Out.Char( "'" ); Out.Char( ch ); Out.Char( "'" )
  555. ELSE Out.Hex( ORD( ch ), 2 ); Out.Char( 'X' )
  556. END;
  557. Out.Ln;
  558. END
  559. END
  560. END ESCSequence;
  561. PROCEDURE Consume( ch: CHAR );
  562. VAR buf: ARRAY 256 OF CHAR; i, n: LONGINT;
  563. BEGIN
  564. CASE ch OF
  565. | 0X: (* NUL *)
  566. |07X: Beep.Beep( 1000 )
  567. |08X: MoveCursor( 0, -1 )
  568. |09X: RightTab()
  569. |NL, 0BX, 0CX:
  570. MoveCursor( 1, -1000 )
  571. |CR:
  572. IF r.Peek() = NL THEN
  573. r.Char( ch );
  574. MoveCursor( 1, -1000 )
  575. ELSE
  576. MoveCursor( 0, -1000 )
  577. END
  578. |ESC: ESCSequence(ch)
  579. |DEL: Delete()
  580. ELSE (* iso-8859-1 *)
  581. buf[0] := ch; i := 1; n := r.Available();
  582. IF n > 0 THEN
  583. IF n > 127 THEN n := 127 END;
  584. ch := r.Peek();
  585. WHILE (n > 0) & (ch >= ' ') & (ch <= '~') DO
  586. r.Char( ch ); DEC( n );
  587. buf[i] := ch; INC( i );
  588. IF n > 0 THEN ch := r.Peek() END
  589. END
  590. END;
  591. WriteChars( buf, i )
  592. END
  593. END Consume;
  594. PROCEDURE FocusReceived*;
  595. BEGIN
  596. FocusReceived^();
  597. UpdateBox( cursor.line, cursor.ofs )
  598. END FocusReceived;
  599. PROCEDURE FocusLost*;
  600. BEGIN
  601. FocusLost^();
  602. UpdateBox( cursor.line, cursor.ofs )
  603. END FocusLost;
  604. PROCEDURE LocateBox( x, y: LONGINT; VAR pos: Position );
  605. VAR l: Line; ofs, i: LONGINT;
  606. BEGIN
  607. IF x < dX THEN x := dX ELSIF x >= (dX + cols*boxW) THEN x := dX + cols*boxW-1 END;
  608. IF y < dY THEN y := dY ELSIF y >= (dY + rows*boxH) THEN y := dY + rows*boxH-1 END;
  609. pos.line := NIL; pos.ofs := -1;
  610. l := top;
  611. WHILE (l # NIL) & ~((l.t <= y) & (l.b > y)) DO
  612. l := l.next
  613. END;
  614. IF l # NIL THEN
  615. ofs := 0; i := dX;
  616. WHILE (ofs < cols) & ~((i <= x) & ((i+boxW) > x)) DO
  617. INC(ofs); INC(i, boxW)
  618. END;
  619. IF ofs < cols THEN
  620. pos.line := l; pos.ofs := ofs
  621. END
  622. END
  623. END LocateBox;
  624. PROCEDURE Copy;
  625. VAR
  626. l: Line; apos, pos, ofs, end: LONGINT; buf: ARRAY 2 OF LONGINT;
  627. attr: Attribute; tattr: Texts.Attributes;
  628. BEGIN {EXCLUSIVE}
  629. IF sel.beg.line = NIL THEN RETURN END;
  630. Texts.clipboard.AcquireRead();
  631. end := Texts.clipboard.GetLength();
  632. Texts.clipboard.ReleaseRead();
  633. Texts.clipboard.AcquireWrite();
  634. Texts.clipboard.Delete( 0, end );
  635. pos := 0; buf[1] := 0; l := sel.beg.line;
  636. attr := NIL; tattr := NIL; apos := -1;
  637. LOOP
  638. IF l = sel.beg.line THEN ofs := sel.beg.ofs ELSE ofs := 0 END;
  639. IF l = sel.end.line THEN end := sel.end.ofs + 1 ELSE end := cols END;
  640. WHILE ofs < end DO
  641. IF l.data[ofs].char # 0 THEN
  642. buf[0] := l.data[ofs].char;
  643. IF attr # l.data[ofs].attr THEN
  644. IF tattr # NIL THEN
  645. Texts.clipboard.SetAttributes( apos, pos - apos, tattr )
  646. END;
  647. apos := pos; attr := l.data[ofs].attr;
  648. NEW( tattr ); NEW( tattr.fontInfo );
  649. tattr.color := attr.fg; tattr.bgcolor := attr.bg;
  650. COPY( attr.fnt.name, tattr.fontInfo.name );
  651. tattr.fontInfo.size := attr.fnt.size;
  652. tattr.fontInfo.style := attr.fnt.style
  653. END;
  654. Texts.clipboard.InsertUCS32( pos, buf ); INC( pos )
  655. END;
  656. INC( ofs )
  657. END;
  658. IF l = sel.end.line THEN
  659. EXIT
  660. ELSE
  661. l := l.next;
  662. buf[0] := 0AH;
  663. Texts.clipboard.InsertUCS32( pos, buf ); INC( pos )
  664. END
  665. END;
  666. IF tattr # NIL THEN
  667. Texts.clipboard.SetAttributes( apos, pos - apos, tattr )
  668. END;
  669. Texts.clipboard.ReleaseWrite()
  670. END Copy;
  671. PROCEDURE Paste;
  672. VAR R: Texts.TextReader; ch: LONGINT;
  673. BEGIN {EXCLUSIVE}
  674. Texts.clipboard.AcquireRead();
  675. NEW( R, Texts.clipboard );
  676. R.SetPosition( 0 );
  677. R.SetDirection( 1 );
  678. R.ReadCh( ch );
  679. WHILE ~R.eot DO
  680. IF (ch DIV 256) = 0 THEN w.Char( CHR( ch ) ) END;
  681. R.ReadCh( ch )
  682. END;
  683. Texts.clipboard.ReleaseRead();
  684. w.Update()
  685. END Paste;
  686. PROCEDURE ClickHandler( sender, par: ANY );
  687. VAR b: WMStandardComponents.Button; str: Strings.String;
  688. BEGIN
  689. popup.Close();
  690. b := sender( WMStandardComponents.Button );
  691. str := b.caption.Get();
  692. IF str^ = "Copy" THEN
  693. Copy()
  694. ELSIF str^ = "Paste" THEN
  695. Paste()
  696. END
  697. END ClickHandler;
  698. PROCEDURE PointerDown*( x, y: LONGINT; keys: SET );
  699. BEGIN
  700. IF (Left IN keys) & hasFocus THEN
  701. LocateBox( x, y, sel.beg ); sel.end := sel.beg
  702. ELSIF Right IN keys THEN
  703. ToWMCoordinates(x, y, x, y);
  704. popup.Popup( x, y )
  705. ELSE
  706. sel.beg.line := NIL; sel.beg.ofs := -1;
  707. sel.end := sel.beg
  708. END;
  709. UpdateAll()
  710. END PointerDown;
  711. PROCEDURE PointerMove*( x, y: LONGINT; keys: SET );
  712. VAR pos: Position;
  713. BEGIN
  714. IF (Left IN keys) & (sel.beg.line # NIL) THEN
  715. LocateBox(x, y, pos);
  716. IF pos.line # NIL THEN
  717. IF pos.line.t > sel.beg.line.t THEN
  718. sel.end := pos
  719. ELSIF (pos.line = sel.beg.line) & (pos.ofs >= sel.beg.ofs) THEN
  720. sel.end := pos
  721. END;
  722. UpdateAll()
  723. END
  724. END
  725. END PointerMove;
  726. PROCEDURE PointerUp*( x, y: LONGINT; keys: SET );
  727. END PointerUp;
  728. PROCEDURE CursorKey( keySym: LONGINT );
  729. BEGIN
  730. w.Char( ESC );
  731. IF CursorKeyMode IN mode THEN w.Char( "O" )
  732. ELSE w.Char( "[" )
  733. END;
  734. CASE keySym OF
  735. |0FF51H: w.Char( "D" )
  736. |0FF52H: w.Char( "A" )
  737. |0FF53H: w.Char( "C" )
  738. |0FF54H: w.Char( "B" )
  739. ELSE
  740. END;
  741. w.Update()
  742. END CursorKey;
  743. PROCEDURE KeyEvent*( ucs: LONGINT; flags: SET; VAR keySym: LONGINT );
  744. BEGIN
  745. IF chan = NIL THEN RETURN END;
  746. IF ~(Inputs.Release IN flags) & hasFocus THEN
  747. IF (keySym DIV 256) = 0 THEN
  748. w.Char( CHR( keySym ) ); w.Update()
  749. ELSIF (keySym DIV 256) = 0FFH THEN
  750. CASE keySym OF
  751. |0FF51H .. 0FF54H:
  752. CursorKey(keySym)
  753. |0FF50H: (* Home *)
  754. |0FF55H: (* PgUp *)
  755. |0FF56H: (* PgDown *)
  756. |0FF57H: (* End *)
  757. |0FF63H: (* Insert *)
  758. |0FFFFH: (* Delete *)
  759. |0FF08H:
  760. w.Char( DEL ); w.Update()
  761. |0FF09H:
  762. w.Char( 9X ); w.Update()
  763. |0FF0DH:
  764. w.Char( CR ); w.Update()
  765. |0FF1BH:
  766. w.Char( ESC ); w.Update()
  767. |0FF8DH:
  768. IF AppKeypadMode IN mode THEN
  769. w.Char( ESC ); w.Char( "O" ); w.Char( "M" )
  770. ELSE
  771. w.Char( CR )
  772. END;
  773. w.Update()
  774. ELSE
  775. END
  776. END
  777. END
  778. END KeyEvent;
  779. PROCEDURE Handle*( VAR m : WMMessages.Message );
  780. BEGIN
  781. IF m.msgType = WMMessages.MsgKey THEN
  782. IF m.y MOD 256 = 9 THEN KeyEvent( m.x, m.flags, m.y )
  783. ELSE Handle^( m )
  784. END;
  785. ELSE Handle^( m )
  786. END
  787. END Handle;
  788. PROCEDURE resized;
  789. VAR l: Line; W, H, c, r, i: LONGINT; d: Data; ch: Char;
  790. BEGIN {EXCLUSIVE}
  791. W := bounds.GetWidth() - 2*Border;
  792. H := bounds.GetHeight() - 2*Border;
  793. c := W DIV BoxW; r := H DIV BoxH;
  794. boxW := W DIV c; boxH := H DIV r;
  795. dX := Border + (W - c*boxW) DIV 2;
  796. dY := Border + (H - r*boxH) DIV 2;
  797. SetOffsets;
  798. IF c # cols THEN
  799. ch.attr := attr; ch.char := 0;
  800. l := first;
  801. WHILE l # NIL DO
  802. NEW( d, c ); i := 0;
  803. WHILE (i < c) & (i < cols) DO d[i] := l.data[i]; INC( i ) END;
  804. WHILE i < c DO d[i] := ch; INC( i ) END;
  805. l.data := d; l := l.next
  806. END
  807. END;
  808. IF (c # cols) OR (r # rows) THEN
  809. IF cursor.ofs >= c THEN cursor.ofs := c - 1 END;
  810. l := cursor.line;
  811. IF l.b > (dY + r*boxH) THEN
  812. i := (l.b - (dY + r*boxH)) DIV boxH;
  813. l := top.next;
  814. WHILE (l # NIL) & (i > 0) DO top := l; l := l.next; DEC( i ) END
  815. END;
  816. IF (rows # r) & (scrollEnd = rows - 1) THEN
  817. scrollEnd := r - 1; scrollBottom := GetLine( r )
  818. END;
  819. sel.beg.line := NIL; cols := c; rows := r;
  820. END;
  821. END resized;
  822. PROCEDURE Resized*;
  823. BEGIN
  824. Resized^();
  825. resized();
  826. IF chan # NIL THEN chan.WindowChange( cols, rows ) END
  827. END Resized;
  828. PROCEDURE Initialize*;
  829. BEGIN
  830. Initialize^();
  831. Resized;
  832. takesFocus.Set( TRUE );
  833. Invalidate()
  834. END Initialize;
  835. PROCEDURE SetChannel( c: SSH.Channel );
  836. BEGIN{EXCLUSIVE}
  837. chan := c;
  838. Streams.OpenReader( r, chan.Receive );
  839. Streams.OpenWriter( w, chan.Send );
  840. mode := {};
  841. chan.WindowChange( cols, rows )
  842. END SetChannel;
  843. PROCEDURE &New*( cols, rows: LONGINT; close: WindowCloser );
  844. VAR i: LONGINT;
  845. BEGIN
  846. Init();
  847. closeWindow := close;
  848. SELF.rows := rows; SELF.cols := cols;
  849. NewAttr();
  850. bg := WMG.RGBAToColor( 255, 255, 255, 255 );
  851. first := AppendLine( NIL );
  852. top := first;
  853. scrollBegin := 0; scrollEnd := rows - 1;
  854. SetScrollRegion;
  855. cursor.line := top; cursor.ofs := 0;
  856. boxW := 0; boxH := 0; dX := 0; dY := 0;
  857. NEW( tabs, cols + 1 );
  858. tabs[0] := FALSE; i := 1;
  859. WHILE i <= cols DO tabs[i] := (i MOD 8) = 0; INC( i ) END;
  860. NEW( popup );
  861. popup.Add( "Copy", ClickHandler );
  862. popup.Add( "Paste", ClickHandler )
  863. END New;
  864. PROCEDURE Setup;
  865. BEGIN {EXCLUSIVE}
  866. AWAIT( chan # NIL );
  867. END Setup;
  868. PROCEDURE Dispatch;
  869. VAR ch: CHAR;
  870. BEGIN
  871. r.Char( ch );
  872. WHILE (chan.state = SSH.ChanOpen) & (r.res = Streams.Ok) DO
  873. Consume( ch );
  874. r.Char( ch )
  875. END
  876. END Dispatch;
  877. BEGIN {ACTIVE}
  878. Setup();
  879. Dispatch();
  880. IF closeWindow # NIL THEN closeWindow END;
  881. END Frame;
  882. Window = OBJECT( WMComponents.FormWindow )
  883. VAR
  884. toolbar: WMStandardComponents.Panel;
  885. address, user: WMEditors.Editor;
  886. connect, help : WMStandardComponents.Button;
  887. sshConn: SSHAuthorize.Connection;
  888. sshChan: SSH.Channel;
  889. frame: Frame;
  890. PROCEDURE &New;
  891. VAR vc: WMComponents.VisualComponent;
  892. BEGIN
  893. vc := CreateForm();
  894. Init( vc.bounds.GetWidth(), vc.bounds.GetHeight(), FALSE );
  895. SetContent( vc );
  896. SetTitle( WMWindowManager.NewString( "SSH Client" ) );
  897. WMWindowManager.DefaultAddWindow( SELF )
  898. END New;
  899. PROCEDURE CreateForm( ): WMComponents.VisualComponent;
  900. VAR
  901. panel: WMStandardComponents.Panel;
  902. label : WMStandardComponents.Label;
  903. BEGIN
  904. NEW( panel );
  905. panel.bounds.SetWidth( 2*Border + TerminalWidth*BoxW );
  906. panel.bounds.SetHeight( 2*Border + TerminalHeight*BoxH + 20 );
  907. panel.fillColor.Set( LONGINT( 0FFFFFFFFH ) );
  908. NEW( toolbar );
  909. toolbar.bounds.SetHeight( 20 );
  910. toolbar.alignment.Set( WMComponents.AlignTop );
  911. toolbar.fillColor.Set( LONGINT( 0CCCCCCFFH ) );
  912. NEW( label );
  913. label.bounds.SetWidth( 40 );
  914. label.alignment.Set( WMComponents.AlignLeft );
  915. label.caption.SetAOC( " Host: " );
  916. label.textColor.Set( 0000000FFH );
  917. toolbar.AddContent(label);
  918. NEW( address );
  919. address.bounds.SetWidth( 250 );
  920. address.alignment.Set( WMComponents.AlignLeft );
  921. address.tv.textAlignV.Set(WMG.AlignCenter);
  922. address.multiLine.Set( FALSE );
  923. address.fillColor.Set( LONGINT( 0FFFFFFFFH ) );
  924. address.tv.showBorder.Set( TRUE );
  925. address.tv.borders.Set( WMRectangles.MakeRect( 3,3,1,1 ) );
  926. address.onEnter.Add( ConnectHandler );
  927. address.SetAsString( "einstein.math.uni-bremen.de" );
  928. toolbar.AddContent( address );
  929. NEW( label );
  930. label.bounds.SetWidth( 40 );
  931. label.alignment.Set( WMComponents.AlignLeft );
  932. label.caption.SetAOC( " User: " );
  933. label.textColor.Set( 0000000FFH );
  934. toolbar.AddContent( label );
  935. NEW( user );
  936. user.bounds.SetWidth( 100 );
  937. user.alignment.Set( WMComponents.AlignLeft );
  938. user.tv.textAlignV.Set(WMG.AlignCenter);
  939. user.multiLine.Set( FALSE );
  940. user.fillColor.Set( LONGINT( 0FFFFFFFFH ) );
  941. user.tv.showBorder.Set( TRUE );
  942. user.tv.borders.Set( WMRectangles.MakeRect( 3,3,1,1 ) );
  943. user.onEnter.Add( ConnectHandler );
  944. user.SetAsString( "fld" );
  945. toolbar.AddContent( user );
  946. NEW( connect );
  947. connect.bounds.SetWidth( 100 );
  948. connect.alignment.Set( WMComponents.AlignLeft );
  949. connect.caption.SetAOC( "Connect" );
  950. connect.onClick.Add( ConnectHandler );
  951. toolbar.AddContent( connect );
  952. NEW( help );
  953. help.bounds.SetWidth( 100 );
  954. help.alignment.Set( WMComponents.AlignRight );
  955. help.caption.SetAOC( " Help " );
  956. help.onClick.Add( HelpHandler );
  957. toolbar.AddContent( help );
  958. panel.AddContent( toolbar );
  959. NEW( frame, TerminalWidth, TerminalHeight, Close );
  960. frame.alignment.Set( WMComponents.AlignClient );
  961. panel.AddContent( frame );
  962. Init( panel.bounds.GetWidth(), panel.bounds.GetHeight(), FALSE );
  963. RETURN panel
  964. END CreateForm;
  965. PROCEDURE Connected( ): BOOLEAN;
  966. BEGIN
  967. RETURN (sshChan # NIL) & (sshChan.state = SSH.ChanOpen)
  968. END Connected;
  969. PROCEDURE ConnectHandler( sender, data: ANY );
  970. VAR host, uid: ARRAY 64 OF CHAR;
  971. BEGIN
  972. address.GetAsString( host );
  973. IF host = "" THEN
  974. Beep.Beep( 1000 );
  975. Out.String( "no hostname specified" ); Out.Ln; RETURN
  976. END;
  977. user.GetAsString( uid );
  978. IF uid = "" THEN
  979. Beep.Beep( 1000 );
  980. Out.String( "user name missing" ); Out.Ln; RETURN
  981. END;
  982. IF Connected() THEN
  983. Beep.Beep( 1000 );
  984. Out.String( "still connected" ); Out.Ln; RETURN
  985. END;
  986. sshConn := SSHAuthorize.OpenConnection( host, uid );
  987. IF sshConn # NIL THEN
  988. sshChan := SSH.OpenSession( sshConn, TRUE (*interactive *) );
  989. frame.SetChannel( sshChan )
  990. END
  991. END ConnectHandler;
  992. PROCEDURE HelpHandler( sender, data: ANY );
  993. VAR res: WORD; msg: ARRAY 128 OF CHAR;
  994. BEGIN
  995. Commands.Call( "PAR Notepad.Open SSH.Tool ~", {}, res, msg );
  996. IF res # Commands.Ok THEN Out.String( msg ); Out.Ln END;
  997. END HelpHandler;
  998. PROCEDURE Close*;
  999. BEGIN
  1000. IF Connected( ) THEN
  1001. sshConn.Disconnect( 11, "" );
  1002. END;
  1003. Close^
  1004. END Close;
  1005. END Window;
  1006. PROCEDURE Open*;
  1007. VAR inst: Window;
  1008. BEGIN
  1009. NEW( inst );
  1010. END Open;
  1011. BEGIN
  1012. hexd := "0123456789ABCDEF"
  1013. END SSHClient.
  1014. SSHGlobals.SetDebug 15 ~
  1015. SSHClient.Open ~
  1016. System.Free SSHClient SSH ~
  1017. home, end, delete, insert, pageup, pagedown
  1018. emacs
  1019. pine
  1020. pico
  1021. lynx