;;; terminal.el --- terminal emulator for GNU Emacs.;; Copyright (C) 1986,87,88,89,93,94 Free Software Foundation, Inc.;; Author: Richard Mlynarik <mly@eddie.mit.edu>;; Maintainer: FSF;; Keywords: comm, terminals;; This file is part of GNU Emacs.;; GNU Emacs is free software; you can redistribute it and/or modify;; it under the terms of the GNU General Public License as published by;; the Free Software Foundation; either version 2, or (at your option);; any later version.;; GNU Emacs is distributed in the hope that it will be useful,;; but WITHOUT ANY WARRANTY; without even the implied warranty of;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the;; GNU General Public License for more details.;; You should have received a copy of the GNU General Public License;; along with GNU Emacs; see the file COPYING. If not, write to the;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,;; Boston, MA 02111-1307, USA.;;; Commentary:;;; This file has been censored by the Communications Decency Act.;;; That law was passed under the guise of a ban on pornography, but;;; it bans far more than that. This file did not contain pornography,;;; but it was censored nonetheless.;;; For information on US government censorship of the Internet, and;;; what you can do to bring back freedom of the press, see the web;;; site http://www.vtw.org/;;; Code:;;>>TODO;;>> ** Nothing can be done about emacs' meta-lossage **;;>> (without redoing keymaps `sanely' -- ask Mly for details);;>> One probably wants to do setenv MORE -c when running with;;>> more-processing enabled.(require'ehelp)(defgroupterminalnil"Terminal emulator for Emacs.":group'terminals)(defcustomterminal-escape-char?\C-^"*All characters except for this are passed verbatim through theterminal-emulator. This character acts as a prefix for commandsto the emulator program itself. Type this character twice to sendit through the emulator. Type ? after typing it for a list ofpossible commands.This variable is local to each terminal-emulator buffer.":type'character:group'terminal)(defcustomterminal-scrollingt;;>> Setting this to T sort-of defeats my whole aim in writing this package..."*If non-nil, the terminal-emulator will losingly `scroll' when output occurspast the bottom of the screen. If nil, output will win and `wrap' to the topof the screen.This variable is local to each terminal-emulator buffer.":type'boolean:group'terminal)(defcustomterminal-more-processingt"*If non-nil, do more-processing.This variable is local to each terminal-emulator buffer.":type'boolean:group'terminal);; If you are the sort of loser who uses scrolling without more breaks;; and expects to actually see anything, you should probably set this to;; around 400(defcustomterminal-redisplay-interval5000"*Maximum number of characters which will be processed by theterminal-emulator before a screen redisplay is forced.Set this to a large value for greater throughput,set it smaller for more frequent updates but overall slowerperformance.":type'integer:group'terminal)(defvarterminal-more-break-insertion"*** More break -- Press space to continue ***")(defvarterminal-meta-mapnil)(ifterminal-meta-mapnil(let((map(make-sparse-keymap)))(define-keymap[t]'te-pass-through)(setqterminal-meta-mapmap)))(defvarterminal-mapnil)(ifterminal-mapnil(let((map(make-sparse-keymap)))(define-keymap[t]'te-pass-through)(define-keymap[switch-frame]'handle-switch-frame)(define-keymap"\e"terminal-meta-map);(define-key map "\C-l"; '(lambda () (interactive) (te-pass-through) (redraw-display)))(setqterminal-mapmap)))(defvarterminal-escape-mapnil)(ifterminal-escape-mapnil(let((map(make-sparse-keymap)))(define-keymap[t]'undefined)(let((s"0"))(while(<=(arefs0)?9)(define-keymaps'digit-argument)(asets0(1+(arefs0)))))(define-keymap"b"'switch-to-buffer)(define-keymap"o"'other-window)(define-keymap"e"'te-set-escape-char)(define-keymap"\C-l"'redraw-display)(define-keymap"\C-o"'te-flush-pending-output)(define-keymap"m"'te-toggle-more-processing)(define-keymap"x"'te-escape-extended-command);;>> What use is this? Why is it in the default terminal-emulator map?(define-keymap"w"'te-edit)(define-keymap"?"'te-escape-help)(define-keymap(char-to-stringhelp-char)'te-escape-help)(setqterminal-escape-mapmap)))(defvarte-escape-command-alistnil)(ifte-escape-command-alistnil(setqte-escape-command-alist'(("Set Escape Character".te-set-escape-char);;>> What use is this? Why is it in the default terminal-emulator map?("Edit".te-edit)("Refresh".redraw-display)("Record Output".te-set-output-log)("Photo".te-set-output-log)("Tofu".te-tofu);; confuse the uninitiated("Stuff Input".te-stuff-string)("Flush Pending Output".te-flush-pending-output)("Enable More Processing".te-enable-more-processing)("Disable More Processing".te-disable-more-processing)("Scroll at end of page".te-do-scrolling)("Wrap at end of page".te-do-wrapping)("Switch To Buffer".switch-to-buffer)("Other Window".other-window)("Kill Buffer".kill-buffer)("Help".te-escape-help)("Set Redisplay Interval".te-set-redisplay-interval))))(defvarterminal-more-break-mapnil)(ifterminal-more-break-mapnil(let((map(make-sparse-keymap)))(define-keymap[t]'te-more-break-unread)(define-keymap(char-to-stringhelp-char)'te-more-break-help)(define-keymap" "'te-more-break-resume)(define-keymap"\C-l"'redraw-display)(define-keymap"\C-o"'te-more-break-flush-pending-output);;>>> this isn't right;(define-key map "\^?" 'te-more-break-flush-pending-output) ;DEL(define-keymap"\r"'te-more-break-advance-one-line)(setqterminal-more-break-mapmap)));;; Pacify the byte compiler(defvarte-processnil)(defvarte-log-buffernil)(defvarte-heightnil)(defvarte-widthnil)(defvarte-more-countnil)(defvarte-redisplay-countnil)(defvarte-pending-outputnil)(defvarte-saved-point)(defvarte-more-old-pointnil)(defvarte-more-old-local-mapnil)(defvarte-more-old-filternil)(defvarte-more-old-mode-line-formatnil)(defvarte-pending-output-infonil);; Required to support terminfo systems(defconstte-terminal-name-prefix"emacs-em""Prefix used for terminal type names for Terminfo.")(defconstte-terminfo-directory"/tmp/emacs-terminfo/""Directory used for run-time terminal definition files for Terminfo.")(defvarte-terminal-namenil);;;; escape map(defunte-escape()(interactive)(let(s(local(current-local-map))(global(current-global-map)))(unwind-protect(progn(use-global-mapterminal-escape-map)(use-local-mapterminal-escape-map)(setqs(read-key-sequence(ifcurrent-prefix-arg(format"Emacs Terminal escape> %d "(prefix-numeric-valuecurrent-prefix-arg))"Emacs Terminal escape> "))))(use-global-mapglobal)(use-local-maplocal))(message"")(cond;; Certain keys give vector notation, like [escape] when;; you hit esc key...((and(stringps)(string=s(make-string1terminal-escape-char)))(setqlast-command-charterminal-escape-char)(let((terminal-escape-char-259))(te-pass-through)))((setqs(lookup-keyterminal-escape-maps))(call-interactivelys)))))(defunte-escape-help()"Provide help on commands available after terminal-escape-char is typed."(interactive)(message"Terminal emulator escape help...")(let((char(single-key-descriptionterminal-escape-char)))(with-electric-help(function(lambda()(princ(format"Terminal-emulator escape, invoked by \"%s\"Type \"%s\" twice to send a single \"%s\" through.Other chars following \"%s\" are interpreted as follows:\n"charcharcharchar))(princ(substitute-command-keys"\\{terminal-escape-map}\n"))(princ(format"\nSubcommands of \"%s\" (%s)\n"(where-is-internal'te-escape-extended-commandterminal-escape-mapt)'te-escape-extended-command))(let((l(if(fboundp'sortcar)(sortcar(copy-sequencete-escape-command-alist)'string<)(sort(copy-sequencete-escape-command-alist)(function(lambda(ab)(string<(cara)(carb))))))))(whilel(let((doc(or(documentation(cdr(carl)))"Not documented")))(if(string-match"\n"doc);; just use first line of documentation(setqdoc(substringdoc0(match-beginning0))))(princ" \"")(princ(car(carl)))(princ"\":\n ")(princdoc)(write-char?\n))(setql(cdrl))))nil)))))(defunte-escape-extended-command()(interactive)(let((c(let((completion-ignore-caset))(completing-read"terminal command: "te-escape-command-alistnilt))))(ifc(catch'foo(setqc(downcasec))(let((lte-escape-command-alist))(whilel(if(string=c(downcase(car(carl))))(throw'foo(call-interactively(cdr(carl))))(setql(cdrl)))))))));; not used.(defunte-escape-extended-command-unread()(interactive)(setqunread-command-events(listify-key-sequence(this-command-keys)))(te-escape-extended-command))(defunte-set-escape-char(c)"Change the terminal-emulator escape character."(interactive"cSet escape character to: ")(let((oterminal-escape-char))(message(if(=oc)"\"%s\" is the escape char""\"%s\" is now the escape; \"%s\" passes through")(single-key-descriptionc)(single-key-descriptiono))(setqterminal-escape-charc)))(defunte-stuff-string(string)"Read a string to send to through the terminal emulatoras though that string had been typed on the keyboard.Very poor man's file transfer protocol."(interactive"sStuff string: ")(process-send-stringte-processstring))(defunte-set-output-log(name)"Record output from the terminal emulator in a buffer."(interactive(list(ifte-log-buffernil(read-buffer"Record output in buffer: "(format"%s output-log"(buffer-name(current-buffer)))nil))))(if(or(nullname)(equalname""))(progn(setqte-log-buffernil)(message"Output logging off."))(if(get-buffername)nil(save-excursion(set-buffer(get-buffer-createname))(fundamental-mode)(buffer-disable-undo(current-buffer))(erase-buffer)))(setqte-log-buffer(get-buffername))(message"Recording terminal emulator output into buffer \"%s\""(buffer-namete-log-buffer))))(defunte-tofu()"Discontinue output log."(interactive)(te-set-output-lognil))(defunte-toggle(symarg)(setsym(cond((not(numberparg))arg)((=arg1)(not(symbol-valuesym)))((<arg0)nil)(tt))))(defunte-toggle-more-processing(arg)(interactive"p")(message(if(te-toggle'terminal-more-processingarg)"More processing on""More processing off"))(ifterminal-more-processing(setqte-more-count-1)))(defunte-toggle-scrolling(arg)(interactive"p")(message(if(te-toggle'terminal-scrollingarg)"Scroll at end of page""Wrap at end of page")))(defunte-enable-more-processing()"Enable ** MORE ** processing"(interactive)(te-toggle-more-processingt))(defunte-disable-more-processing()"Disable ** MORE ** processing"(interactive)(te-toggle-more-processingnil))(defunte-do-scrolling()"Scroll at end of page (yuck)"(interactive)(te-toggle-scrollingt))(defunte-do-wrapping()"Wrap to top of window at end of page"(interactive)(te-toggle-scrollingnil))(defunte-set-redisplay-interval(arg)"Set the maximum interval (in output characters) between screen updates.Set this number to large value for greater throughput,set it smaller for more frequent updates (but overall slower performance."(interactive"NMax number of output chars between redisplay updates: ")(setqarg(maxarg1))(setqterminal-redisplay-intervalargte-redisplay-count0));;;; more map;; every command -must- call te-more-break-unwind;; or grave lossage will result(put'te-more-break-unread'suppress-keymapt)(defunte-more-break-unread()(interactive)(if(eqlast-input-charterminal-escape-char)(call-interactively'te-escape)(message"Continuing from more break (\"%s\" typed, %d chars output pending...)"(single-key-descriptionlast-input-char)(te-pending-output-length))(setqte-more-count259259)(te-more-break-unwind)(let((terminal-more-processingnil))(te-pass-through))))(defunte-more-break-resume()"Proceed past the **MORE** break,allowing the next page of output to appear"(interactive)(message"Continuing from more break")(te-more-break-unwind))(defunte-more-break-help()"Provide help on commands available in a terminal-emulator **MORE** break"(interactive)(message"Terminal-emulator more break help...")(sit-for0)(with-electric-help(function(lambda()(princ"Terminal-emulator more break.\n\n")(princ(format"Type \"%s\" (te-more-break-resume)\n%s\n"(where-is-internal'te-more-break-resumeterminal-more-break-mapt)(documentation'te-more-break-resume)))(princ(substitute-command-keys"\\{terminal-more-break-map}\n"))(princ"Any other key is passed through to the programrunning under the terminal emulator and disables more processing untilall pending output has been dealt with.")nil))))(defunte-more-break-advance-one-line()"Allow one more line of text to be output before doing another more break."(interactive)(setqte-more-count1)(te-more-break-unwind))(defunte-more-break-flush-pending-output()"Discard any output which has been received by the terminal emulator butnot yet processed and then proceed from the more break."(interactive)(te-more-break-unwind)(te-flush-pending-output))(defunte-flush-pending-output()"Discard any as-yet-unprocessed output which has been received bythe terminal emulator."(interactive);; this could conceivably be confusing in the presence of;; escape-sequences spanning process-output chunks(if(null(cdrte-pending-output))(message"(There is no output pending)")(let((length(te-pending-output-length)))(message"Flushing %d chars of pending output"length)(setqte-pending-output(list0(format"\n*** %d chars of pending output flushed ***\n"length)))(te-update-pending-output-display)(te-process-outputnil)(sit-for0))))(defunte-pass-through()"Character is passed to the program running under the terminal emulator.One characters is treated specially:the terminal escape character (normally C-^)lets you type a terminal emulator command."(interactive)(cond((eqlast-input-charterminal-escape-char)(call-interactively'te-escape))(t;; Convert `return' to C-m, etc.(if(and(symbolplast-input-char)(getlast-input-char'ascii-character))(setqlast-input-char(getlast-input-char'ascii-character)));; Convert meta characters to 8-bit form for transmission.(if(and(integerplast-input-char)(not(zerop(logandlast-input-char?\M-\^@))))(setqlast-input-char(+128(logandlast-input-char127))));; Now ignore all but actual characters.;; (It ought to be possible to send through function;; keys as character sequences if we add a description;; to our termcap entry of what they should look like.)(if(integerplast-input-char)(progn(andterminal-more-processing(null(cdrte-pending-output))(te-set-more-countnil))(send-stringte-process(make-string1last-input-char))(te-process-outputt))(message"Function key `%s' ignored"(single-key-descriptionlast-input-char))))))(defunte-set-window-start()(let*((w(get-buffer-window(current-buffer)))(h(ifw(window-heightw))))(cond((notw)); buffer not displayed((>=h(/(-(point)(point-min))(1+te-width)));; this is the normal case(set-window-startw(point-min)));; this happens if some vandal shrinks our window.((>=h(/(-(point-max)(point))(1+te-width)))(set-window-startw(-(point-max)(*h(1+te-width))-1)));; I give up.(tnil))))(defunte-pending-output-length()(let((length(carte-pending-output))(tem(cdrte-pending-output)))(whiletem(setqlength(+length(length(cartem)))tem(cdrtem)))length));;>> What use is this terminal-edit stuff anyway?;;>> If nothing else, it was written by somebody who didn't;;>> competently understand the terminal-emulator...(defvarterminal-edit-mapnil)(ifterminal-edit-mapnil(setqterminal-edit-map(make-sparse-keymap))(define-keyterminal-edit-map"\C-c\C-c"'terminal-cease-edit));; Terminal Edit mode is suitable only for specially formatted data.(put'terminal-edit-mode'mode-class'special)(defunterminal-edit-mode()"Major mode for editing the contents of a terminal-emulator buffer.The editing commands are the same as in Fundamental mode,together with a command \\<terminal-edit-map>to return to terminal emulation: \\[terminal-cease-edit]."(use-local-mapterminal-edit-map)(setqmajor-mode'terminal-edit-mode)(setqmode-name"Terminal Edit")(setqmode-line-modified(default-value'mode-line-modified))(setqmode-line-processnil)(run-hooks'terminal-edit-mode-hook))(defunte-edit()"Start editing the terminal emulator buffer with ordinary Emacs commands."(interactive)(terminal-edit-mode)(force-mode-line-update);; Make mode line update.(if(eq(key-binding"\C-c\C-c")'terminal-cease-edit)(message"Editing: Type C-c C-c to return to Terminal")(message"%s"(substitute-command-keys"Editing: Type \\[terminal-cease-edit] to return to Terminal"))))(defunterminal-cease-edit()"Finish editing message; switch back to Terminal proper."(interactive);;>> emulator will blow out if buffer isn't exactly te-width x te-height(let((buffer-read-onlynil))(widen)(let((opoint(point-marker))(widthte-width)(h(1-te-height)))(goto-char(point-min))(while(>=h0)(let((p(point)))(cond((search-forward"\n"(+pwidth)'move)(forward-char-1)(insert-char?\ (-width(-(point)p)))(forward-char1))((eobp)(insert-char?\ (-width(-(point)p))))((=(following-char)?\n)(forward-char1))(t(setqp(point))(if(search-forward"\n"nilt)(delete-regionp(1-(point)))(delete-regionp(point-max))))))(if(=h0)(if(not(eobp))(delete-region(point)(point-max)))(if(eobp)(insert?\n)))(setqh(1-h)))(goto-charopoint)(set-markeropointnilnil)(setqte-saved-point(point))(setqte-redisplay-count0)(setqte-more-count-1)))(setqmode-line-modified(default-value'mode-line-modified))(use-local-mapterminal-map)(setqmajor-mode'terminal-mode)(setqmode-name"terminal")(setqmode-line-process'(":%s")));;;; more break hair(defunte-more-break()(te-set-more-countt)(make-local-variable'te-more-old-point)(setqte-more-old-point(point))(make-local-variable'te-more-old-local-map)(setqte-more-old-local-map(current-local-map))(use-local-mapterminal-more-break-map)(make-local-variable'te-more-old-filter)(setqte-more-old-filter(process-filterte-process))(make-local-variable'te-more-old-mode-line-format)(setqte-more-old-mode-line-formatmode-line-formatmode-line-format(list"-- **MORE** "mode-line-buffer-identification"%-"))(set-process-filterte-process(function(lambda(processstring)(save-excursion(set-buffer(process-bufferprocess))(setqte-pending-output(nconcte-pending-output(liststring))))(te-update-pending-output-display))))(te-update-pending-output-display)(if(eq(window-buffer(selected-window))(current-buffer))(message"More break "))(or(eobp)(nullterminal-more-break-insertion)(save-excursion(forward-char1)(delete-region(point)(+(point)te-width))(insertterminal-more-break-insertion)))(run-hooks'terminal-more-break-hook)(sit-for0);get display to update(throw'te-process-outputt))(defunte-more-break-unwind()(use-local-mapte-more-old-local-map)(set-process-filterte-processte-more-old-filter)(goto-charte-more-old-point)(setqmode-line-formatte-more-old-mode-line-format)(force-mode-line-update)(let((buffer-read-onlynil))(cond((eobp))(terminal-more-break-insertion(forward-char1)(delete-region(point)(+(point)(lengthterminal-more-break-insertion)))(insert-char?\ te-width)(goto-charte-more-old-point)))(setqte-more-old-pointnil)(let((te-more-count259259))(te-newline)));(sit-for 0)(te-process-outputt))(defunte-set-more-count(newline)(let((line(/(-(point)(point-min))(1+te-width))))(ifnewline(setqline(1+line)))(cond((=linete-height)(setqte-more-countte-height));>>>> something is strange. Investigate this!((=line(1-te-height))(setqte-more-countte-height))((or(<line(/te-height2))(>(-te-heightline)10));; break at end of this page(setqte-more-count(-te-heightline)))(t;; migrate back towards top (ie bottom) of screen.(setqte-more-count(-te-height(if(>te-height10)21)))))));;;; More or less straight-forward terminal escapes;; ^j, meaning `newline' to non-display programs.;; (Who would think of ever writing a system which doesn't understand;; display terminals natively? Un*x: The Operating System of the Future.)(defunte-newline()"Move down a line, optionally do more processing, perhaps wrap/scroll,move to start of new line, clear to end of line."(end-of-line)(cond((notterminal-more-processing))((<(setqte-more-count(1-te-more-count))0)(te-set-more-countt))((eqte-more-count0);; this doesn't return(te-more-break)))(if(eobp)(progn(delete-region(point-min)(+(point-min)te-width))(goto-char(point-min))(ifterminal-scrolling(progn(delete-char1)(goto-char(point-max))(insert?\n))))(forward-char1)(delete-region(point)(+(point)te-width)))(insert-char?\ te-width)(beginning-of-line)(te-set-window-start)); ^p = x+32 y+32(defunte-move-to-position();; must offset by #o40 since cretinous unix won't send a 004 char through(let((y(-(te-get-char)32))(x(-(te-get-char)32)))(if(or(>xte-width)(>yte-height))()(goto-char(+(point-min)x(*y(1+te-width))));(te-set-window-start?)))(setqte-more-count-1));; ^p c(defunte-clear-rest-of-line()(save-excursion(let((n(-(point)(progn(end-of-line)(point)))))(delete-region(point)(+(point)n))(insert-char?\ (-n)))));; ^p C(defunte-clear-rest-of-screen()(save-excursion(te-clear-rest-of-line)(while(progn(end-of-line)(not(eobp)))(forward-char1)(end-of-line)(delete-region(-(point)te-width)(point))(insert-char?\ te-width))));; ^p ^l(defunte-clear-screen();; regenerate buffer to compensate for (nonexistent!!) bugs.(erase-buffer)(let((i0))(while(<ite-height)(setqi(1+i))(insert-char?\ te-width)(insert?\n)))(delete-region(1-(point-max))(point-max))(goto-char(point-min))(setqte-more-count-1));; ^p ^o count+32(defunte-insert-lines()(if(not(bolp))();(error "fooI")(save-excursion(let*((line(-te-height(/(-(point)(point-min))(1+te-width))-1))(n(min(-(te-get-char)?\ )line))(i0))(delete-region(-(point-max)(*n(1+te-width)))(point-max))(if(eq(point)(point-max))(insert?\n))(while(<in)(setqi(1+i))(insert-char?\ te-width)(or(eqiline)(insert?\n))))))(setqte-more-count-1));; ^p ^k count+32(defunte-delete-lines()(if(not(bolp))();(error "fooD")(let*((line(-te-height(/(-(point)(point-min))(1+te-width))-1))(n(min(-(te-get-char)?\ )line))(i0))(delete-region(point)(min(+(point)(*n(1+te-width)))(point-max)))(save-excursion(goto-char(point-max))(while(<in)(setqi(1+i))(insert-char?\ te-width)(or(eqiline)(insert?\n))))))(setqte-more-count-1));; ^p ^a(defunte-beginning-of-line()(beginning-of-line));; ^p ^b(defunte-backward-char()(if(not(bolp))(backward-char1)));; ^p ^f(defunte-forward-char()(if(not(eolp))(forward-char1)));; 0177(defunte-delete()(if(bolp)()(delete-region(1-(point))(point))(insert?\ )(forward-char-1)));; ^p ^g(defunte-beep()(beep));; ^p _ count+32(defunte-insert-spaces()(let*((p(point))(n(min(-(te-get-char)32)(-(progn(end-of-line)(point))p))))(if(<=n0)nil(delete-char(-n))(goto-charp)(insert-char?\ n))(goto-charp)));; ^p d count+32 (should be ^p ^d but cretinous un*x won't send ^d chars!!!)(defunte-delete-char()(let*((p(point))(n(min(-(te-get-char)32)(-(progn(end-of-line)(point))p))))(if(<=n0)nil(insert-char?\ n)(goto-charp)(delete-charn))(goto-charp)));; disgusting unix-required excrement;; Are we living twenty years in the past yet?(defunte-losing-unix()nil);; ^i(defunte-output-tab()(let*((p(point))(x(-p(progn(beginning-of-line)(point))))(l(min(-8(logandx7))(progn(end-of-line)(-(point)p)))))(goto-char(+pl))));; ^p ^j;; Handle the `do' or `nl' termcap capability.;;>> I am not sure why this broken, obsolete, capability is here.;;>> Perhaps it is for VIle. No comment was made about why it;;>> was added (in "Sun Dec 6 01:22:27 1987 Richard Stallman")(defunte-down-vertically-or-scroll()"Move down a line vertically, or scroll at bottom."(let((column(current-column)))(end-of-line)(if(eobp)(progn(delete-region(point-min)(+(point-min)te-width))(goto-char(point-min))(delete-char1)(goto-char(point-max))(insert?\n)(insert-char?\ te-width)(beginning-of-line))(forward-line1))(move-to-columncolumn))(te-set-window-start));; Also:;; ^m => beginning-of-line (for which it -should- be using ^p ^a, right?!!);; ^g => te-beep (for which it should use ^p ^g);; ^h => te-backward-char (for which it should use ^p ^b)(defunte-filter(processstring)(let*((obuf(current-buffer)));; can't use save-excursion, as that preserves point, which we don't want(unwind-protect(progn(set-buffer(process-bufferprocess))(goto-charte-saved-point)(and(bufferpte-log-buffer)(if(null(buffer-namete-log-buffer));; killed(setqte-log-buffernil)(set-bufferte-log-buffer)(goto-char(point-max))(insert-before-markersstring)(set-buffer(process-bufferprocess))))(setqte-pending-output(nconcte-pending-output(liststring)))(te-update-pending-output-display)(te-process-output(eq(current-buffer)(window-buffer(selected-window))))(set-buffer(process-bufferprocess))(setqte-saved-point(point)))(set-bufferobuf))));; (A version of the following comment which might be distractingly offensive;; to some readers has been moved to term-nasty.el.);; unix lacks ITS-style tty control...(defunte-process-output(preemptible);;>> There seems no good reason to ever disallow preemption(setqpreemptiblet)(catch'te-process-output(let((buffer-read-onlynil)(stringnil)ostringstartchar(matchposnil))(while(cdrte-pending-output)(setqostringstringstart(carte-pending-output)string(car(cdrte-pending-output))char(arefstringstart))(if(eq(setqstart(1+start))(lengthstring))(progn(setqte-pending-output(cons0(cdr(cdrte-pending-output)))start0string(car(cdrte-pending-output)))(te-update-pending-output-display))(setcarte-pending-outputstart))(if(and(>char?\037)(<char?\377))(cond((eolp);; unread char(if(eqstart0)(setqte-pending-output(cons0(cons(make-string1char)(cdrte-pending-output))))(setcarte-pending-output(1-start)))(te-newline))((nullstring)(delete-char1)(insertchar)(te-redisplay-if-necessary1))(t(let((end(or(and(eqostringstring)matchpos)(setqmatchpos(string-match"[\000-\037\177-\377]"stringstart))(lengthstring))))(delete-char1)(insertchar)(setqchar(point))(end-of-line)(setqend(minend(+start(-(point)char))))(goto-charchar)(if(eqendmatchpos)(setqmatchposnil))(delete-region(point)(+(point)(-endstart)))(insert(if(and(eqstart0)(eqend(lengthstring)))string(substringstringstartend)))(if(eqend(lengthstring))(setqte-pending-output(cons0(cdr(cdrte-pending-output))))(setcarte-pending-outputend))(te-redisplay-if-necessary(1+(-endstart))))));; I suppose if I split the guts of this out into a separate;; function we could trivially emulate different terminals;; Who cares in any case? (Apart from stupid losers using rlogin)(funcall(if(eqchar?\^p)(or(cdr(assq(te-get-char)'((?=.te-move-to-position)(?c.te-clear-rest-of-line)(?C.te-clear-rest-of-screen)(?\C-o.te-insert-lines)(?\C-k.te-delete-lines);; not necessary, but help sometimes.(?\C-a.te-beginning-of-line)(?\C-b.te-backward-char);; should be C-d, but un*x;; pty's won't send \004 through!;; Can you believe this?(?d.te-delete-char)(?_.te-insert-spaces);; random(?\C-f.te-forward-char)(?\C-g.te-beep)(?\C-j.te-down-vertically-or-scroll)(?\C-l.te-clear-screen))))'te-losing-unix)(or(cdr(assqchar'((?\C-j.te-newline)(?\177.te-delete);; Did I ask to be sent these characters?;; I don't remember doing so, either.;; (Perhaps some operating system or;; other is completely incompetent...)(?\C-m.te-beginning-of-line)(?\C-g.te-beep)(?\C-h.te-backward-char)(?\C-i.te-output-tab))))'te-losing-unix)))(te-redisplay-if-necessary1))(andpreemptible(input-pending-p);; preemptible output! Oh my!!(throw'te-process-outputt)))));; We must update window-point in every window displaying our buffer(let*((s(selected-window))(ws))(while(not(eqs(setqw(next-windoww))))(if(eq(window-bufferw)(current-buffer))(set-window-pointw(point))))))(defunte-get-char()(if(cdrte-pending-output)(let((start(carte-pending-output))(string(car(cdrte-pending-output))))(prog1(arefstringstart)(if(eq(setqstart(1+start))(lengthstring))(setqte-pending-output(cons0(cdr(cdrte-pending-output))))(setcarte-pending-outputstart))))(catch'char(let((filter(process-filterte-process)))(unwind-protect(progn(set-process-filterte-process(function(lambda(ps)(or(eq(lengths)1)(setqte-pending-output(list1s)))(throw'char(arefs0)))))(accept-process-outputte-process))(set-process-filterte-processfilter))))))(defunte-redisplay-if-necessary(length)(and(<=(setqte-redisplay-count(-te-redisplay-countlength))0)(eq(current-buffer)(window-buffer(selected-window)))(waiting-for-user-input-p)(progn(te-update-pending-output-display)(sit-for0)(setqte-redisplay-countterminal-redisplay-interval))))(defunte-update-pending-output-display()(if(null(cdrte-pending-output))(setqte-pending-output-info"")(let((length(te-pending-output-length)))(if(<length1500)(setqte-pending-output-info"")(setqte-pending-output-info(format"(%dK chars output pending) "(/(+length512)1024))))))(force-mode-line-update))(defunte-sentinel(processmessage)(cond((eq(process-statusprocess)'run))((null(buffer-name(process-bufferprocess))));deleted(t(let((b(current-buffer)))(save-excursion(set-buffer(process-bufferprocess))(setqbuffer-read-onlynil)(fundamental-mode)(goto-char(point-max))(delete-blank-lines)(delete-horizontal-space)(insert"\n*******\n"message"*******\n"))(if(and(eqb(process-bufferprocess))(waiting-for-user-input-p))(progn(goto-char(point-max))(recenter-1)))))))(defvarte-stty-string"stty -nl erase '^?' kill '^u' intr '^c' echo pass8""Shell command to set terminal modes for terminal emulator.");; This used to have `new' in it, but that loses outside BSD;; and it's apparently not needed in BSD.(defcustomexplicit-shell-file-namenil"*If non-nil, is file name to use for explicitly requested inferior shell.":type'(choice(const:tag"None"nil)file):group'terminal);;;###autoload(defunterminal-emulator(bufferprogramargs&optionalwidthheight)"Under a display-terminal emulator in BUFFER, run PROGRAM on arguments ARGS.ARGS is a list of argument-strings. Remaining arguments are WIDTH and HEIGHT.BUFFER's contents are made an image of the display generated by that program,and any input typed when BUFFER is the current Emacs buffer is sent to thatprogram an keyboard input.Interactively, BUFFER defaults to \"*terminal*\" and PROGRAM and ARGSare parsed from an input-string using your usual shell.WIDTH and HEIGHT are determined from the size of the current window-- WIDTH will be one less than the window's width, HEIGHT will be its height.To switch buffers and leave the emulator, or to give commandsto the emulator itself (as opposed to the program running under it),type Control-^. The following character is an emulator command.Type Control-^ twice to send it to the subprogram.This escape character may be changed using the variable `terminal-escape-char'.`Meta' characters may not currently be sent through the terminal emulator.Here is a list of some of the variables which control the behaviourof the emulator -- see their documentation for more information:terminal-escape-char, terminal-scrolling, terminal-more-processing,terminal-redisplay-interval.This function calls the value of terminal-mode-hook if that existsand is non-nil after the terminal buffer has been set up and thesubprocess started."(interactive(cons(save-excursion(set-buffer(get-buffer-create"*terminal*"))(buffer-name(if(or(not(boundp'te-process))(nullte-process)(not(eq(process-statuste-process)'run)))(current-buffer)(generate-new-buffer"*terminal*"))))(append(let*((default-s;; Default shell is same thing M-x shell uses.(orexplicit-shell-file-name(getenv"ESHELL")(getenv"SHELL")"/bin/sh"))(s(read-string(format"Run program in emulator: (default %s) "default-s))))(if(equals"")(listdefault-s'())(te-parse-program-and-argss))))))(switch-to-bufferbuffer)(if(nullwidth)(setqwidth(-(window-width(selected-window))1)))(if(nullheight)(setqheight(-(window-height(selected-window))1)))(terminal-mode)(setqte-widthwidthte-heightheight)(setqte-terminal-name(concatte-terminal-name-prefixte-widthte-height))(setqmode-line-buffer-identification(list(format"Emacs terminal %dx%d: %%b "te-widthte-height)'te-pending-output-info))(let((buffer-read-onlynil))(te-clear-screen))(let(process)(while(setqprocess(get-buffer-process(current-buffer)))(if(y-or-n-p(format"Kill process %s? "(process-nameprocess)))(delete-processprocess)(error"Process %s not killed"(process-nameprocess)))))(condition-caseerr(let((process-environment(cons(concat"TERM="te-terminal-name)(cons(concat"TERMCAP="(te-create-termcap))(cons(concat"TERMINFO="(te-create-terminfo))process-environment)))))(setqte-process(start-process"terminal-emulator"(current-buffer)"/bin/sh""-c";; Yuck!!! Start a shell to set some terminal;; control characteristics. Then start the;; "env" program to setup the terminal type;; Then finally start the program we wanted.(format"%s; exec %s"te-stty-string(mapconcat'te-quote-arg-for-sh(consprogramargs)" "))))(set-process-filterte-process'te-filter)(set-process-sentinelte-process'te-sentinel))(error(fundamental-mode)(signal(carerr)(cdrerr))))(setqinhibit-quitt);sport death(use-local-mapterminal-map)(run-hooks'terminal-mode-hook)(message"Entering emacs terminal-emulator... Type %s %s for help"(single-key-descriptionterminal-escape-char)(mapconcat'single-key-description(where-is-internal'te-escape-helpterminal-escape-mapt)" ")))(defunte-parse-program-and-args(s)(cond((string-match"\\`\\([-a-zA-Z0-9+=_.@/:]+[ \t]*\\)+\\'"s)(let((l())(p0))(whilep(setql(cons(if(string-match"\\([-a-zA-Z0-9+=_.@/:]+\\)\\([ \t]+\\)*"sp)(prog1(substringsp(match-end1))(setqp(match-end0))(if(eqp(lengths))(setqpnil)))(prog1(substringsp)(setqpnil)))l)))(setql(nreversel))(list(carl)(cdrl))))((and(string-match"[ \t]"s)(not(file-exists-ps)))(listshell-file-name(list"-c"(concat"exec "s))))(t(lists()))))(put'terminal-mode'mode-class'special);; This is only separated out from function terminal-emulator;; to keep the latter a little more manageable.(defunterminal-mode()"Set up variables for use with the terminal-emulator.One should not call this -- it is an internal functionof the terminal-emulator"(kill-all-local-variables)(buffer-disable-undo(current-buffer))(setqmajor-mode'terminal-mode)(setqmode-name"terminal"); (make-local-variable 'Helper-return-blurb); (setq Helper-return-blurb "return to terminal simulator")(setqmode-line-process'(":%s"))(setqbuffer-read-onlyt)(setqtruncate-linest)(make-local-variable'terminal-escape-char)(setqterminal-escape-char(default-value'terminal-escape-char))(make-local-variable'terminal-scrolling)(setqterminal-scrolling(default-value'terminal-scrolling))(make-local-variable'terminal-more-processing)(setqterminal-more-processing(default-value'terminal-more-processing))(make-local-variable'terminal-redisplay-interval)(setqterminal-redisplay-interval(default-value'terminal-redisplay-interval))(make-local-variable'te-width)(make-local-variable'te-height)(make-local-variable'te-process)(make-local-variable'te-pending-output)(setqte-pending-output(list0))(make-local-variable'te-saved-point)(setqte-saved-point(point-min))(make-local-variable'te-pending-output-info);for the mode line(setqte-pending-output-info"")(make-local-variable'inhibit-quit);(setq inhibit-quit t)(make-local-variable'te-log-buffer)(setqte-log-buffernil)(make-local-variable'te-more-count)(setqte-more-count-1)(make-local-variable'te-redisplay-count)(setqte-redisplay-countterminal-redisplay-interval);(use-local-map terminal-mode-map);; terminal-mode-hook is called above in function terminal-emulator);;;; what a complete loss(defunte-quote-arg-for-sh(string)(cond((string-match"\\`[-a-zA-Z0-9+=_.@/:]+\\'"string)string)((not(string-match"[$]"string));; "[\"\\]" are special to sh and the lisp reader in the same way(prin1-to-stringstring))(t(let((harder"")(start0)(end0))(while(cond((>=start(lengthstring))nil);; this is the set of chars magic with "..." in `sh'((setqend(string-match"[\"\\$]"stringstart))t)(t(setqharder(concatharder(substringstringstart)))nil))(setqharder(concatharder(substringstringstartend);; Can't use ?\\ since `concat';; unfortunately does prin1-to-string;; on fixna. Amazing."\\"(substringstringend(1+end)))start(1+end)))(concat"\""harder"\"")))))(defunte-create-terminfo()"Create and compile a terminfo entry for the virtual terminal. This is keptin the directory specified by `te-terminfo-directory'."(if(andsystem-uses-terminfo(not(file-exists-p(concatte-terminfo-directory(substringte-terminal-name-prefix01)"/"te-terminal-name))))(let((terminfo(concat;; The first newline avoids trouble with ncurses.(format"%s,\n\tmir, xon,cols#%d, lines#%d,"te-terminal-namete-widthte-height)"bel=^P^G, clear=^P\\f, cr=^P^A, cub1=^P^B, cud1=^P\\n,""cuf1=^P^F, cup=^P=%p1%'\\s'%+%c%p2%'\\s'%+%c,""dch=^Pd%p1%'\\s'%+%c, dch1=^Pd!, dl=^P^K%p1%'\\s'%+%c,""dl1=^P^K!, ed=^PC, el=^Pc, home=^P=\\s\\s,""ich=^P_%p1%'\\s'%+%c, ich1=^P_!, il=^P^O%p1%'\\s'%+%c,""il1=^P^O!, ind=^P\\n, nel=\\n,\n"));; The last newline avoids trouble with ncurses.(file-name(concatte-terminfo-directoryte-terminal-name".tif")))(make-directoryte-terminfo-directoryt)(save-excursion(set-buffer(create-file-bufferfile-name))(insertterminfo)(write-filefile-name)(kill-buffernil))(let((process-environment(cons(concat"TERMINFO="(directory-file-namete-terminfo-directory))process-environment)))(set-process-sentinel(start-process"tic"nil"tic"file-name)'te-tic-sentinel))))(directory-file-namete-terminfo-directory))(defunte-create-termcap()"Create a termcap entry for the virtual terminal";; Because of Unix Brain Death(tm), we can't change;; the terminal type of a running process, and so;; terminal size and scrollability are wired-down;; at this point. ("Detach? What's that?")(concat(format"%s:co#%d:li#%d:%s";; Sigh. These can't be dynamically changed.te-terminal-namete-widthte-height(ifterminal-scrolling"""ns:"));;-- Basic things;; cursor-motion, bol, forward/backward char"cm=^p=%+ %+ :cr=^p^a:le=^p^b:nd=^p^f:";; newline, clear eof/eof, audible bell"nw=^j:ce=^pc:cd=^pC:cl=^p^l:bl=^p^g:";; insert/delete char/line"IC=^p_%+ :DC=^pd%+ :AL=^p^o%+ :DL=^p^k%+ :";;-- Not-widely-known (ie nonstandard) flags, which mean;; o writing in the last column of the last line;; doesn't cause idiotic scrolling, and;; o don't use idiotische c-s/c-q sogenannte;; ``flow control'' auf keinen Fall."LP:NF:";;-- For stupid or obsolete programs"ic=^p_!:dc=^pd!:al=^p^o!:dl=^p^k!:ho=^p= :";;-- For disgusting programs.;; (VI? What losers need these, I wonder?)"im=:ei=:dm=:ed=:mi:do=^p^j:nl=^p^j:bs:"))(defunte-tic-sentinel(procstate-change)"If tic has finished, delete the .tif file"(if(equalstate-change"finished")(delete-file(concatte-terminfo-directoryte-terminal-name".tif"))))(provide'terminal);;; terminal.el ends here