SSHClient.Mod 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162
  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( "PTMono_bd.ttf" );
  332. IF f # NIL THEN
  333. attr.fnt := WMG.GetFont( "PTMono", 13, {0} )
  334. ELSE
  335. attr.fnt := WMG.GetFont( "Courier", 12, {} )
  336. END;
  337. attr.bg := WMG.RGBAToColor( 255, 255, 255, 255 );
  338. attr.fg := WMG.RGBAToColor( 0, 0, 0, 255 )
  339. END NewAttr;
  340. PROCEDURE Bright;
  341. VAR style: SET;
  342. BEGIN
  343. style := attr.fnt.style;
  344. IF ~(WMG.FontBold IN style) THEN
  345. INCL( style, WMG.FontBold );
  346. attr.fnt := WMG.GetFont( attr.fnt.name, attr.fnt.size, style )
  347. ELSE
  348. Out.String("Bright"); Out.Ln()
  349. END
  350. END Bright;
  351. PROCEDURE Dim;
  352. VAR style: SET;
  353. BEGIN
  354. style := attr.fnt.style;
  355. IF WMG.FontBold IN style THEN
  356. EXCL( style, WMG.FontBold );
  357. attr.fnt := WMG.GetFont( attr.fnt.name, attr.fnt.size, style )
  358. ELSE
  359. Out.String("Dim"); Out.Ln()
  360. END
  361. END Dim;
  362. PROCEDURE SetAttributes( CONST attrs: ARRAY OF LONGINT; n: LONGINT );
  363. VAR c: WMG.Color; i: LONGINT;
  364. BEGIN {EXCLUSIVE}
  365. NewAttr();
  366. i := 0;
  367. WHILE i < n DO
  368. CASE attrs[i] OF
  369. |0: (* Reset *) NewAttr()
  370. |1: (* Bright *) Bright()
  371. |2: (* Dim *) Dim()
  372. |4: (* Underscore *) INCL( attr.special, Underscore )
  373. |5: (* Blink *) INCL( attr.special, Blink )
  374. |7: (* Reverse *) c := attr.bg; attr.bg := attr.fg; attr.fg := c
  375. |8: (* Hidden *) attr.fg := attr.bg
  376. ELSE
  377. Out.String("attr "); Out.Int(attrs[i], 0); Out.Ln()
  378. END;
  379. INC(i)
  380. END
  381. END SetAttributes;
  382. PROCEDURE Draw*( canvas: WMG.Canvas );
  383. VAR
  384. l: Line; i, j, dy, bottom: LONGINT; attr: Attribute; char: Char;
  385. box: WMG.Rectangle;
  386. BEGIN {EXCLUSIVE}
  387. canvas.Fill( canvas.clipRect, bg, WMG.ModeCopy );
  388. l := first;
  389. WHILE l # top DO
  390. l.t := MIN(INTEGER); l.b := MIN(INTEGER); l := l.next
  391. END;
  392. attr := NIL; bottom := dY + rows*boxH;
  393. box.t := dY; box.b := dY + boxH; j := 0;
  394. WHILE (l # NIL) & (j < rows) & (box.b <= bottom) DO
  395. l.t := box.t; l.b := box.b;
  396. box.l := dX; box.r := dX + boxW; i := 0;
  397. WHILE i < cols DO
  398. char := l.data[i];
  399. IF char.attr # attr THEN
  400. attr := char.attr;
  401. canvas.SetColor( attr.fg );
  402. canvas.SetFont( attr.fnt );
  403. dy := attr.fnt.GetDescent()
  404. END;
  405. IF attr.bg # bg THEN
  406. canvas.Fill( box, attr.bg, WMG.ModeCopy )
  407. END;
  408. IF char.char # 0 THEN
  409. attr.fnt.RenderChar( canvas, box.l, box.b-dy, char.char )
  410. END;
  411. IF Underscore IN attr.special THEN
  412. canvas.Line( box.l, box.b-dy+1, box.r-1, box.b-dy+1, attr.fg, WMG.ModeCopy )
  413. END;
  414. INC( i ); INC( box.l, boxW ); INC( box.r, boxW )
  415. END;
  416. INC( j ); l := l.next;
  417. INC( box.t, boxH ); INC( box.b, boxH )
  418. END;
  419. WHILE l # NIL DO
  420. l.t := MAX(INTEGER); l.b := MAX(INTEGER); l := l.next
  421. END;
  422. IF hasFocus & (cursor.ofs >= 0) & (cursor.ofs < cols) THEN
  423. l := cursor.line; box.t := l.t; box.b := l.b;
  424. IF box.t < box.b THEN
  425. box.l := dX + cursor.ofs*boxW; box.r := box.l + boxW;
  426. canvas.Fill( box, WMG.RGBAToColor( 255, 0, 0, 192 ), WMG.ModeSrcOverDst )
  427. ELSE
  428. FocusLost
  429. END
  430. END;
  431. IF sel.beg.line # NIL THEN
  432. IF sel.beg.line = sel.end.line THEN
  433. box.l := dX + sel.beg.ofs * boxW; box.r := dX + sel.end.ofs * boxW + boxW;
  434. box.t := sel.beg.line.t; box.b := sel.end.line.b;
  435. canvas.Fill( box, WMG.RGBAToColor( 0, 0, 255, 32 ), WMG.ModeSrcOverDst )
  436. ELSE
  437. box.l := dX + sel.beg.ofs * boxW; box.r := dX + cols * boxW;
  438. box.t := sel.beg.line.t; box.b := sel.beg.line.b;
  439. canvas.Fill( box, WMG.RGBAToColor( 0, 0, 255, 32 ), WMG.ModeSrcOverDst );
  440. l := sel.beg.line.next;
  441. WHILE l # sel.end.line DO
  442. box.l := dX; box.r := dX + cols * boxW;
  443. box.t := l.t; box.b := l.b;
  444. canvas.Fill( box, WMG.RGBAToColor( 0, 0, 255, 32 ), WMG.ModeSrcOverDst );
  445. l := l.next
  446. END;
  447. box.l := dX; box.r := dX + sel.end.ofs * boxW + boxW;
  448. box.t := sel.end.line.t; box.b := sel.end.line.b;
  449. canvas.Fill( box, WMG.RGBAToColor( 0, 0, 255, 32 ), WMG.ModeSrcOverDst )
  450. END
  451. END
  452. END Draw;
  453. PROCEDURE MoveCursor( dr, dc: LONGINT );
  454. VAR col, currrow: LONGINT;
  455. BEGIN
  456. col := GetCol() + dc;
  457. IF col < 0 THEN col := 0 END;
  458. currrow := GetRow();
  459. IF (currrow = scrollEnd) & (dr > 0) THEN
  460. IF currrow < rows - 1 THEN Scroll( FALSE ); Goto( currrow, col )
  461. ELSE Goto( currrow + 1, col )
  462. END
  463. ELSIF (currrow = scrollBegin) & (dr < 0) THEN Scroll( TRUE ); Goto( currrow, col )
  464. ELSE Goto( currrow + dr, col )
  465. END
  466. END MoveCursor;
  467. PROCEDURE ESCSequence(ch: CHAR);
  468. VAR
  469. par: ARRAY 4 OF LONGINT; i, n: LONGINT;
  470. BEGIN
  471. r.Char( ch );
  472. IF ch = "[" THEN
  473. ch := r.Peek(); n := 0;
  474. IF ch = "?" THEN
  475. r.Char( ch ); ch := r.Peek();
  476. IF (ch >= "0") & (ch <= "9") THEN
  477. REPEAT
  478. r.Int( par[n], FALSE ); INC( n );
  479. r.Char( ch )
  480. UNTIL (n >= 4) OR (ch # " ")
  481. END
  482. ELSIF (ch >= "0") & (ch <= "9") THEN
  483. REPEAT
  484. r.Int( par[n], FALSE ); INC( n );
  485. r.Char( ch )
  486. UNTIL (n >= 4) OR (ch # ";")
  487. ELSE
  488. ASSERT( ch < DEL );
  489. r.Char( ch )
  490. END;
  491. CASE ch OF
  492. |"A":
  493. IF n = 1 THEN MoveCursor( -par[0], 0 ) ELSE MoveCursor( -1, 0 ) END
  494. |"B":
  495. IF n = 1 THEN MoveCursor( par[0], 0 ) ELSE MoveCursor( 1, 0 ) END
  496. |"C":
  497. IF n = 1 THEN MoveCursor( 0, par[0] ) ELSE MoveCursor( 0, 1 ) END
  498. |"D":
  499. IF n = 1 THEN MoveCursor( 0, -par[0] ) ELSE MoveCursor( 0, -1 ) END
  500. |"H":
  501. IF n = 2 THEN Goto( par[0] - 1, par[1] - 1 ) ELSE Goto( 0, 0 ) END
  502. |"J", "K":
  503. Erase( ch, par, n )
  504. |"h":
  505. IF n = 1 THEN
  506. IF par[0] = 1 THEN INCL( mode, CursorKeyMode )
  507. ELSIF par[0] = 7 THEN INCL( mode, AutoWrapMode )
  508. END
  509. END
  510. |"l":
  511. IF n = 1 THEN
  512. IF par[0] = 1 THEN EXCL( mode, CursorKeyMode )
  513. ELSIF par[0] = 7 THEN EXCL( mode, AutoWrapMode )
  514. END
  515. END
  516. |"m":
  517. SetAttributes( par, n )
  518. | "r":
  519. SetMargins( par[0], par[1] )
  520. ELSE
  521. Out.Ln; Out.String( "got unknown sequence ESC [ " );
  522. i := 0;
  523. WHILE i < n DO
  524. Out.Int( par[i], 0 ); INC( i );
  525. IF i < n THEN Out.String( " ; " ) END
  526. END;
  527. Out.Char( ch ); Out.Ln;
  528. END
  529. ELSE
  530. CASE ch OF
  531. |"7":
  532. old.attr := attr;
  533. old.offs := GetCol();
  534. old.row := GetRow()
  535. |"8":
  536. IF r.Peek( ) = '#' THEN r.Char( ch ); EFill
  537. ELSE attr := old.attr; Goto( old.row, old.offs )
  538. END
  539. |"=":
  540. INCL( mode, AppKeypadMode )
  541. |">":
  542. EXCL( mode, AppKeypadMode )
  543. |"D":
  544. IF GetRow() = scrollEnd THEN Scroll( FALSE )
  545. ELSE Goto( GetRow() + 1, GetCol() )
  546. END
  547. |"M":
  548. IF GetRow() = scrollBegin THEN Scroll( TRUE )
  549. ELSE Goto( GetRow() - 1, GetCol() )
  550. END
  551. ELSE
  552. Out.String("got unknown sequence ESC ");
  553. IF (ch >= ' ') & (ch <= '~') THEN Out.Char( "'" ); Out.Char( ch ); Out.Char( "'" )
  554. ELSE Out.Hex( ORD( ch ), 2 ); Out.Char( 'X' )
  555. END;
  556. Out.Ln;
  557. END
  558. END
  559. END ESCSequence;
  560. PROCEDURE Consume( ch: CHAR );
  561. VAR buf: ARRAY 256 OF CHAR; i, n: LONGINT;
  562. BEGIN
  563. CASE ch OF
  564. | 0X: (* NUL *)
  565. |07X: Beep.Beep( 1000 )
  566. |08X: MoveCursor( 0, -1 )
  567. |09X: RightTab()
  568. |NL, 0BX, 0CX:
  569. MoveCursor( 1, -1000 )
  570. |CR:
  571. IF r.Peek() = NL THEN
  572. r.Char( ch );
  573. MoveCursor( 1, -1000 )
  574. ELSE
  575. MoveCursor( 0, -1000 )
  576. END
  577. |ESC: ESCSequence(ch)
  578. |DEL: Delete()
  579. ELSE (* iso-8859-1 *)
  580. buf[0] := ch; i := 1; n := r.Available();
  581. IF n > 0 THEN
  582. IF n > 127 THEN n := 127 END;
  583. ch := r.Peek();
  584. WHILE (n > 0) & (ch >= ' ') & (ch <= '~') DO
  585. r.Char( ch ); DEC( n );
  586. buf[i] := ch; INC( i );
  587. IF n > 0 THEN ch := r.Peek() END
  588. END
  589. END;
  590. WriteChars( buf, i )
  591. END
  592. END Consume;
  593. PROCEDURE FocusReceived*;
  594. BEGIN
  595. FocusReceived^();
  596. UpdateBox( cursor.line, cursor.ofs )
  597. END FocusReceived;
  598. PROCEDURE FocusLost*;
  599. BEGIN
  600. FocusLost^();
  601. UpdateBox( cursor.line, cursor.ofs )
  602. END FocusLost;
  603. PROCEDURE LocateBox( x, y: LONGINT; VAR pos: Position );
  604. VAR l: Line; ofs, i: LONGINT;
  605. BEGIN
  606. IF x < dX THEN x := dX ELSIF x >= (dX + cols*boxW) THEN x := dX + cols*boxW-1 END;
  607. IF y < dY THEN y := dY ELSIF y >= (dY + rows*boxH) THEN y := dY + rows*boxH-1 END;
  608. pos.line := NIL; pos.ofs := -1;
  609. l := top;
  610. WHILE (l # NIL) & ~((l.t <= y) & (l.b > y)) DO
  611. l := l.next
  612. END;
  613. IF l # NIL THEN
  614. ofs := 0; i := dX;
  615. WHILE (ofs < cols) & ~((i <= x) & ((i+boxW) > x)) DO
  616. INC(ofs); INC(i, boxW)
  617. END;
  618. IF ofs < cols THEN
  619. pos.line := l; pos.ofs := ofs
  620. END
  621. END
  622. END LocateBox;
  623. PROCEDURE Copy;
  624. VAR
  625. l: Line; apos, pos, ofs, end: LONGINT; buf: ARRAY 2 OF LONGINT;
  626. attr: Attribute; tattr: Texts.Attributes;
  627. BEGIN {EXCLUSIVE}
  628. IF sel.beg.line = NIL THEN RETURN END;
  629. Texts.clipboard.AcquireRead();
  630. end := Texts.clipboard.GetLength();
  631. Texts.clipboard.ReleaseRead();
  632. Texts.clipboard.AcquireWrite();
  633. Texts.clipboard.Delete( 0, end );
  634. pos := 0; buf[1] := 0; l := sel.beg.line;
  635. attr := NIL; tattr := NIL; apos := -1;
  636. LOOP
  637. IF l = sel.beg.line THEN ofs := sel.beg.ofs ELSE ofs := 0 END;
  638. IF l = sel.end.line THEN end := sel.end.ofs + 1 ELSE end := cols END;
  639. WHILE ofs < end DO
  640. IF l.data[ofs].char # 0 THEN
  641. buf[0] := l.data[ofs].char;
  642. IF attr # l.data[ofs].attr THEN
  643. IF tattr # NIL THEN
  644. Texts.clipboard.SetAttributes( apos, pos - apos, tattr )
  645. END;
  646. apos := pos; attr := l.data[ofs].attr;
  647. NEW( tattr ); NEW( tattr.fontInfo );
  648. tattr.color := attr.fg; tattr.bgcolor := attr.bg;
  649. COPY( attr.fnt.name, tattr.fontInfo.name );
  650. tattr.fontInfo.size := attr.fnt.size;
  651. tattr.fontInfo.style := attr.fnt.style
  652. END;
  653. Texts.clipboard.InsertUCS32( pos, buf ); INC( pos )
  654. END;
  655. INC( ofs )
  656. END;
  657. IF l = sel.end.line THEN
  658. EXIT
  659. ELSE
  660. l := l.next;
  661. buf[0] := 0AH;
  662. Texts.clipboard.InsertUCS32( pos, buf ); INC( pos )
  663. END
  664. END;
  665. IF tattr # NIL THEN
  666. Texts.clipboard.SetAttributes( apos, pos - apos, tattr )
  667. END;
  668. Texts.clipboard.ReleaseWrite()
  669. END Copy;
  670. PROCEDURE Paste;
  671. VAR R: Texts.TextReader; ch: LONGINT;
  672. BEGIN {EXCLUSIVE}
  673. Texts.clipboard.AcquireRead();
  674. NEW( R, Texts.clipboard );
  675. R.SetPosition( 0 );
  676. R.SetDirection( 1 );
  677. R.ReadCh( ch );
  678. WHILE ~R.eot DO
  679. IF (ch DIV 256) = 0 THEN w.Char( CHR( ch ) ) END;
  680. R.ReadCh( ch )
  681. END;
  682. Texts.clipboard.ReleaseRead();
  683. w.Update()
  684. END Paste;
  685. PROCEDURE ClickHandler( sender, par: ANY );
  686. VAR b: WMStandardComponents.Button; str: Strings.String;
  687. BEGIN
  688. popup.Close();
  689. b := sender( WMStandardComponents.Button );
  690. str := b.caption.Get();
  691. IF str^ = "Copy" THEN
  692. Copy()
  693. ELSIF str^ = "Paste" THEN
  694. Paste()
  695. END
  696. END ClickHandler;
  697. PROCEDURE PointerDown*( x, y: LONGINT; keys: SET );
  698. BEGIN
  699. IF (Left IN keys) & hasFocus THEN
  700. LocateBox( x, y, sel.beg ); sel.end := sel.beg
  701. ELSIF Right IN keys THEN
  702. ToWMCoordinates(x, y, x, y);
  703. popup.Popup( x, y )
  704. ELSE
  705. sel.beg.line := NIL; sel.beg.ofs := -1;
  706. sel.end := sel.beg
  707. END;
  708. UpdateAll()
  709. END PointerDown;
  710. PROCEDURE PointerMove*( x, y: LONGINT; keys: SET );
  711. VAR pos: Position;
  712. BEGIN
  713. IF (Left IN keys) & (sel.beg.line # NIL) THEN
  714. LocateBox(x, y, pos);
  715. IF pos.line # NIL THEN
  716. IF pos.line.t > sel.beg.line.t THEN
  717. sel.end := pos
  718. ELSIF (pos.line = sel.beg.line) & (pos.ofs >= sel.beg.ofs) THEN
  719. sel.end := pos
  720. END;
  721. UpdateAll()
  722. END
  723. END
  724. END PointerMove;
  725. PROCEDURE PointerUp*( x, y: LONGINT; keys: SET );
  726. END PointerUp;
  727. PROCEDURE CursorKey( keySym: LONGINT );
  728. BEGIN
  729. w.Char( ESC );
  730. IF CursorKeyMode IN mode THEN w.Char( "O" )
  731. ELSE w.Char( "[" )
  732. END;
  733. CASE keySym OF
  734. |0FF51H: w.Char( "D" )
  735. |0FF52H: w.Char( "A" )
  736. |0FF53H: w.Char( "C" )
  737. |0FF54H: w.Char( "B" )
  738. ELSE
  739. END;
  740. w.Update()
  741. END CursorKey;
  742. PROCEDURE KeyEvent*( ucs: LONGINT; flags: SET; VAR keySym: LONGINT );
  743. BEGIN
  744. IF chan = NIL THEN RETURN END;
  745. IF ~(Inputs.Release IN flags) & hasFocus THEN
  746. IF (keySym DIV 256) = 0 THEN
  747. w.Char( CHR( keySym ) ); w.Update()
  748. ELSIF (keySym DIV 256) = 0FFH THEN
  749. CASE keySym OF
  750. |0FF51H .. 0FF54H:
  751. CursorKey(keySym)
  752. |0FF50H: (* Home *)
  753. |0FF55H: (* PgUp *)
  754. |0FF56H: (* PgDown *)
  755. |0FF57H: (* End *)
  756. |0FF63H: (* Insert *)
  757. |0FFFFH: (* Delete *)
  758. |0FF08H:
  759. w.Char( DEL ); w.Update()
  760. |0FF09H:
  761. w.Char( 9X ); w.Update()
  762. |0FF0DH:
  763. w.Char( CR ); w.Update()
  764. |0FF1BH:
  765. w.Char( ESC ); w.Update()
  766. |0FF8DH:
  767. IF AppKeypadMode IN mode THEN
  768. w.Char( ESC ); w.Char( "O" ); w.Char( "M" )
  769. ELSE
  770. w.Char( CR )
  771. END;
  772. w.Update()
  773. ELSE
  774. END
  775. END
  776. END
  777. END KeyEvent;
  778. PROCEDURE Handle*( VAR m : WMMessages.Message );
  779. BEGIN
  780. IF m.msgType = WMMessages.MsgKey THEN
  781. IF m.y MOD 256 = 9 THEN KeyEvent( m.x, m.flags, m.y )
  782. ELSE Handle^( m )
  783. END;
  784. ELSE Handle^( m )
  785. END
  786. END Handle;
  787. PROCEDURE resized;
  788. VAR l: Line; W, H, c, r, i: LONGINT; d: Data; ch: Char;
  789. BEGIN {EXCLUSIVE}
  790. W := bounds.GetWidth() - 2*Border;
  791. H := bounds.GetHeight() - 2*Border;
  792. c := W DIV BoxW; r := H DIV BoxH;
  793. boxW := W DIV c; boxH := H DIV r;
  794. dX := Border + (W - c*boxW) DIV 2;
  795. dY := Border + (H - r*boxH) DIV 2;
  796. SetOffsets;
  797. IF c # cols THEN
  798. ch.attr := attr; ch.char := 0;
  799. l := first;
  800. WHILE l # NIL DO
  801. NEW( d, c ); i := 0;
  802. WHILE (i < c) & (i < cols) DO d[i] := l.data[i]; INC( i ) END;
  803. WHILE i < c DO d[i] := ch; INC( i ) END;
  804. l.data := d; l := l.next
  805. END
  806. END;
  807. IF (c # cols) OR (r # rows) THEN
  808. IF cursor.ofs >= c THEN cursor.ofs := c - 1 END;
  809. l := cursor.line;
  810. IF l.b > (dY + r*boxH) THEN
  811. i := (l.b - (dY + r*boxH)) DIV boxH;
  812. l := top.next;
  813. WHILE (l # NIL) & (i > 0) DO top := l; l := l.next; DEC( i ) END
  814. END;
  815. IF (rows # r) & (scrollEnd = rows - 1) THEN
  816. scrollEnd := r - 1; scrollBottom := GetLine( r )
  817. END;
  818. sel.beg.line := NIL; cols := c; rows := r;
  819. END;
  820. END resized;
  821. PROCEDURE Resized*;
  822. BEGIN
  823. Resized^();
  824. resized();
  825. IF chan # NIL THEN chan.WindowChange( cols, rows ) END
  826. END Resized;
  827. PROCEDURE Initialize*;
  828. BEGIN
  829. Initialize^();
  830. Resized;
  831. takesFocus.Set( TRUE );
  832. Invalidate()
  833. END Initialize;
  834. PROCEDURE SetChannel( c: SSH.Channel );
  835. BEGIN{EXCLUSIVE}
  836. chan := c;
  837. Streams.OpenReader( r, chan.Receive );
  838. Streams.OpenWriter( w, chan.Send );
  839. mode := {};
  840. chan.WindowChange( cols, rows )
  841. END SetChannel;
  842. PROCEDURE &New*( cols, rows: LONGINT; close: WindowCloser );
  843. VAR i: LONGINT;
  844. BEGIN
  845. Init();
  846. closeWindow := close;
  847. SELF.rows := rows; SELF.cols := cols;
  848. NewAttr();
  849. bg := WMG.RGBAToColor( 255, 255, 255, 255 );
  850. first := AppendLine( NIL );
  851. top := first;
  852. scrollBegin := 0; scrollEnd := rows - 1;
  853. SetScrollRegion;
  854. cursor.line := top; cursor.ofs := 0;
  855. boxW := 0; boxH := 0; dX := 0; dY := 0;
  856. NEW( tabs, cols + 1 );
  857. tabs[0] := FALSE; i := 1;
  858. WHILE i <= cols DO tabs[i] := (i MOD 8) = 0; INC( i ) END;
  859. NEW( popup );
  860. popup.Add( "Copy", ClickHandler );
  861. popup.Add( "Paste", ClickHandler )
  862. END New;
  863. PROCEDURE Setup;
  864. BEGIN {EXCLUSIVE}
  865. AWAIT( chan # NIL );
  866. END Setup;
  867. PROCEDURE Dispatch;
  868. VAR ch: CHAR;
  869. BEGIN
  870. r.Char( ch );
  871. WHILE (chan.state = SSH.ChanOpen) & (r.res = Streams.Ok) DO
  872. Consume( ch );
  873. r.Char( ch )
  874. END
  875. END Dispatch;
  876. BEGIN {ACTIVE}
  877. Setup();
  878. Dispatch();
  879. IF closeWindow # NIL THEN closeWindow END;
  880. END Frame;
  881. Window = OBJECT( WMComponents.FormWindow )
  882. VAR
  883. toolbar: WMStandardComponents.Panel;
  884. address, user: WMEditors.Editor;
  885. connect, help : WMStandardComponents.Button;
  886. sshConn: SSHAuthorize.Connection;
  887. sshChan: SSH.Channel;
  888. frame: Frame;
  889. PROCEDURE &New;
  890. VAR vc: WMComponents.VisualComponent;
  891. BEGIN
  892. vc := CreateForm();
  893. Init( vc.bounds.GetWidth(), vc.bounds.GetHeight(), FALSE );
  894. SetContent( vc );
  895. SetTitle( WMWindowManager.NewString( "SSH Client" ) );
  896. WMWindowManager.DefaultAddWindow( SELF )
  897. END New;
  898. PROCEDURE CreateForm( ): WMComponents.VisualComponent;
  899. VAR
  900. panel: WMStandardComponents.Panel;
  901. label : WMStandardComponents.Label;
  902. BEGIN
  903. NEW( panel );
  904. panel.bounds.SetWidth( 2*Border + TerminalWidth*BoxW );
  905. panel.bounds.SetHeight( 2*Border + TerminalHeight*BoxH + 20 );
  906. panel.fillColor.Set( LONGINT( 0FFFFFFFFH ) );
  907. NEW( toolbar );
  908. toolbar.bounds.SetHeight( 20 );
  909. toolbar.alignment.Set( WMComponents.AlignTop );
  910. toolbar.fillColor.Set( LONGINT( 0CCCCCCFFH ) );
  911. NEW( label );
  912. label.bounds.SetWidth( 40 );
  913. label.alignment.Set( WMComponents.AlignLeft );
  914. label.caption.SetAOC( " Host: " );
  915. label.textColor.Set( 0000000FFH );
  916. toolbar.AddContent(label);
  917. NEW( address );
  918. address.bounds.SetWidth( 250 );
  919. address.alignment.Set( WMComponents.AlignLeft );
  920. address.tv.textAlignV.Set(WMG.AlignCenter);
  921. address.multiLine.Set( FALSE );
  922. address.fillColor.Set( LONGINT( 0FFFFFFFFH ) );
  923. address.tv.showBorder.Set( TRUE );
  924. address.tv.borders.Set( WMRectangles.MakeRect( 3,3,1,1 ) );
  925. address.onEnter.Add( ConnectHandler );
  926. address.SetAsString( "einstein.math.uni-bremen.de" );
  927. toolbar.AddContent( address );
  928. NEW( label );
  929. label.bounds.SetWidth( 40 );
  930. label.alignment.Set( WMComponents.AlignLeft );
  931. label.caption.SetAOC( " User: " );
  932. label.textColor.Set( 0000000FFH );
  933. toolbar.AddContent( label );
  934. NEW( user );
  935. user.bounds.SetWidth( 100 );
  936. user.alignment.Set( WMComponents.AlignLeft );
  937. user.tv.textAlignV.Set(WMG.AlignCenter);
  938. user.multiLine.Set( FALSE );
  939. user.fillColor.Set( LONGINT( 0FFFFFFFFH ) );
  940. user.tv.showBorder.Set( TRUE );
  941. user.tv.borders.Set( WMRectangles.MakeRect( 3,3,1,1 ) );
  942. user.onEnter.Add( ConnectHandler );
  943. user.SetAsString( "fld" );
  944. toolbar.AddContent( user );
  945. NEW( connect );
  946. connect.bounds.SetWidth( 100 );
  947. connect.alignment.Set( WMComponents.AlignLeft );
  948. connect.caption.SetAOC( "Connect" );
  949. connect.onClick.Add( ConnectHandler );
  950. toolbar.AddContent( connect );
  951. NEW( help );
  952. help.bounds.SetWidth( 100 );
  953. help.alignment.Set( WMComponents.AlignRight );
  954. help.caption.SetAOC( " Help " );
  955. help.onClick.Add( HelpHandler );
  956. toolbar.AddContent( help );
  957. panel.AddContent( toolbar );
  958. NEW( frame, TerminalWidth, TerminalHeight, Close );
  959. frame.alignment.Set( WMComponents.AlignClient );
  960. panel.AddContent( frame );
  961. Init( panel.bounds.GetWidth(), panel.bounds.GetHeight(), FALSE );
  962. RETURN panel
  963. END CreateForm;
  964. PROCEDURE Connected( ): BOOLEAN;
  965. BEGIN
  966. RETURN (sshChan # NIL) & (sshChan.state = SSH.ChanOpen)
  967. END Connected;
  968. PROCEDURE ConnectHandler( sender, data: ANY );
  969. VAR host, uid: ARRAY 64 OF CHAR;
  970. BEGIN
  971. address.GetAsString( host );
  972. IF host = "" THEN
  973. Beep.Beep( 1000 );
  974. Out.String( "no hostname specified" ); Out.Ln; RETURN
  975. END;
  976. user.GetAsString( uid );
  977. IF uid = "" THEN
  978. Beep.Beep( 1000 );
  979. Out.String( "user name missing" ); Out.Ln; RETURN
  980. END;
  981. IF Connected() THEN
  982. Beep.Beep( 1000 );
  983. Out.String( "still connected" ); Out.Ln; RETURN
  984. END;
  985. sshConn := SSHAuthorize.OpenConnection( host, uid );
  986. IF sshConn # NIL THEN
  987. sshChan := SSH.OpenSession( sshConn, TRUE (*interactive *) );
  988. frame.SetChannel( sshChan )
  989. END
  990. END ConnectHandler;
  991. PROCEDURE HelpHandler( sender, data: ANY );
  992. VAR res: WORD; msg: ARRAY 128 OF CHAR;
  993. BEGIN
  994. Commands.Call( "PAR Notepad.Open SSH.Tool ~", {}, res, msg );
  995. IF res # Commands.Ok THEN Out.String( msg ); Out.Ln END;
  996. END HelpHandler;
  997. PROCEDURE Close*;
  998. BEGIN
  999. IF Connected( ) THEN
  1000. sshConn.Disconnect( 11, "" );
  1001. END;
  1002. Close^
  1003. END Close;
  1004. END Window;
  1005. PROCEDURE Open*;
  1006. VAR inst: Window;
  1007. BEGIN
  1008. NEW( inst );
  1009. END Open;
  1010. BEGIN
  1011. hexd := "0123456789ABCDEF"
  1012. END SSHClient.
  1013. SSHGlobals.SetDebug 15 ~
  1014. SSHClient.Open ~
  1015. System.Free SSHClient SSH ~
  1016. home, end, delete, insert, pageup, pagedown
  1017. emacs
  1018. pine
  1019. pico
  1020. lynx