home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume19 / atty / part01 next >
Encoding:
Internet Message Format  |  1989-05-30  |  47.9 KB

  1. Subject:  v19i010:  A command-line editor (for BSD), Part01/04
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: ka@june.cs.washington.edu (Kenneth Almquist)
  7. Posting-number: Volume 19, Issue 10
  8. Archive-name: atty/part01
  9.  
  10. Atty is an alternative to tty(4).
  11. Atty provides a set of EMACS-like editing commands which allow
  12. you to move around the input line and make corrections where ever you want.
  13. Atty allows you to bring lines that you previously typed into the input
  14. buffer so that you can resend them, possible after modifying them.  Atty
  15. also handles line wrapping and does a better job than tty of keeping input
  16. and output from getting intermingled.  Not to mention its abbreviation
  17. feature...  Try it, you'll like it!
  18.  
  19. Atty runs entirely in user mode and requires no special privileges to run
  20. or install.  It's a hack, but it works.  I developed the code under Ultrix,
  21. a 4.2 BSD derivative.  It has also been tested on a couple of other 4.2 BSD
  22. derivatives.  The code does not run under System V, and would not be easy
  23. to port.
  24.  
  25. # This is part 1 of atty.  To unpack, feed it into the shell (not csh).
  26. # The atty distribution consists of four pieces.  After you unpack everyting,
  27. # read the file README.
  28.  
  29. echo extracting README
  30. cat > README <<\EOF
  31.                 Welcome to ATTY
  32.  
  33. Atty is an alternative to tty(4).  To correct a mistake using tty(4), you
  34. must erase all the characters between the mistake and the end of the line.
  35. In contrast, atty provides a set of EMACS-like editing commands which allow
  36. you to move around the input line and make corrections where ever you want.
  37. Atty allows you to bring lines that you previously typed into the input
  38. buffer so that you can resend them, possible after modifying them.  Atty
  39. also handles line wrapping and does a better job than tty of keeping input
  40. and output from getting intermingled.  Not to mention its abbreviation
  41. feature...  Try it, you'll like it!
  42.  
  43. Atty runs entirely in user mode and requires no special privileges to run
  44. or install.  It's a hack, but it works.  I developed the code under Ultrix,
  45. a 4.2 BSD derivative.  It has also been tested on a couple of other 4.2 BSD
  46. derivatives.  The code does not run under System V, and would not be easy
  47. to port.
  48.  
  49.  
  50.                  INSTALLATION
  51.  
  52. To compile atty, just type "make".  Three programs should be created:
  53.  
  54.     atty - the atty program
  55.     kbind - program to generate key binding files
  56.     fmatch - file name matching program
  57.  
  58. These should be installed in a bin directory somewhere.  Three additional
  59. files are generated:
  60.  
  61.     atty.bindc - default binding file
  62.     dftbind.c - name of default binding file
  63.     atty.1 - atty manual page
  64.  
  65. Atty.bindc is the default key binding file, which is created from the
  66. file atty.bind.  The latter two files include the absolution path name
  67. of atty.bindc.  The makefile will create these files correctly if you
  68. just leave atty.bindc in the source directory.  If you install atty.bindc
  69. somewhere else, you will have to edit these files.
  70.  
  71. The makefile used cc.  The Berkeley header files are incompatible with
  72. ANSI C, so if you compile with gcc or some other ANSI compiler, you will
  73. get incorrect code.  You can use an ANSI compatable version of the header
  74. files, or you can use the -traditional flag of gcc.
  75.  
  76. Having compiled atty, you are now ready to run it.  However, you may want
  77. to change some the bindings first.  The end of the "Getting Started"
  78. section of the atty manual page provides a tutorial introduction to
  79. doing this; if you really want to know what's going on read the kbind
  80. manual page.  Some users will probably want set up their bindings to act
  81. like vi rather than emacs.  The file vi.bind illustrates how to set up
  82. separate insert and command modes, like vi has.  However, I have not
  83. implemented the vi commands, and you can't have a real vi emulation
  84. without them.
  85.  
  86.  
  87. Copyright (c) 1989 by Kenneth Almquist
  88. EOF
  89. if test `wc -c < README` -ne 2593
  90. then    echo 'README is the wrong size'
  91. fi
  92. echo extracting LICENSE
  93. cat > LICENSE <<\EOF
  94.             ATTY GENERAL PUBLIC LICENSE
  95.  
  96.   Copyright 1989 by Kenneth Almquist.
  97.   Everyone is permitted to copy and distribute verbatim copies of this
  98.   license, but changing it is not allowed.  You can also use this wording
  99.   to make the terms for other programs.  This license agreement uses
  100.   wording taken from the GNU Emacs General Public License, which is
  101.   copyright 1985, 1987, 1988 by Richard M. Stallman.  Richard M. Stallman
  102.   is not responsible for the contents of this license.
  103.  
  104.  
  105. Atty is copyrighted.  To encourage maximum use of this software, I,
  106. Kenneth Almquist, hereby grant all recipients of atty the right to
  107. reproduce and modify atty under the terms specified in this license.
  108.  
  109.  
  110.             COPYING POLICIES
  111.  
  112.   1. You may copy and distribute verbatim copies of atty source code
  113. as you receive it, in any medium, provided that you conspicuously and
  114. appropriately publish on each copy a valid copyright notice "Copyright
  115. 1989 by Kenneth Almquist." (or with whatever year is appropriate); keep
  116. intact the notices on all files that refer to this License Agreement
  117. and to the absence of any warranty; and give any other recipients of
  118. the atty program a copy of this License Agreement along with the program.
  119. You may charge a distribution fee for the physical act of transferring
  120. a copy.
  121.  
  122.   2. You may modify your copy or copies of atty source code or
  123. any portion of it, and copy and distribute such modifications under
  124. the terms of Paragraph 1 above, provided that you also do the following:
  125.  
  126.     a) cause the modified files to carry prominent notices stating
  127.     that you changed the files and the date of any change; and
  128.  
  129.     b) cause the whole of any work that you distribute or publish,
  130.     that in whole or in part contains or is a derivative of atty
  131.     or any part thereof, to be licensed at no charge to all third
  132.     parties on terms identical to those contained in this License
  133.     Agreement (except that you may choose to grant more extensive
  134.     warranty protection to some or all third parties, at your option).
  135.  
  136.     c) You may charge a distribution fee for the physical act of
  137.     transferring a copy, and you may at your option offer warranty
  138.     protection in exchange for a fee.
  139.  
  140. Mere aggregation of another unrelated program with this program (or its
  141. derivative) on a volume of a storage or distribution medium does not bring
  142. the other program under the scope of these terms.
  143.  
  144.   3. You may copy and distribute atty (or a portion or derivative of it,
  145. under Paragraph 2) in object code or executable form under the terms of
  146. Paragraphs 1 and 2 above provided that you also do one of the following:
  147.  
  148.     a) accompany it with the complete corresponding machine-readable
  149.     source code, which must be distributed under the terms of
  150.     Paragraphs 1 and 2 above; or,
  151.  
  152.     b) accompany it with a written offer, valid for at least three
  153.     years, to give any third party free (except for a nominal
  154.     shipping charge) a complete machine-readable copy of the
  155.     corresponding source code, to be distributed under the terms of
  156.     Paragraphs 1 and 2 above; or,
  157.  
  158.     c) accompany it with the information you received as to where the
  159.     corresponding source code may be obtained.  (This alternative is
  160.     allowed only for noncommercial distribution and only if you
  161.     received the program in object code or executable form alone.)
  162.  
  163. For an executable file, complete source code means all the source code for
  164. all modules it contains; but, as a special exception, it need not include
  165. source code for modules which are standard libraries that accompany the
  166. operating system on which the executable file runs.
  167.  
  168.   4. The atty distribution contains two files, regex.h and regex.c,
  169. which are copyrighted by the Free Software Foundation, Inc.  These files
  170. are not covered by this license agreement, and may only be used or copied
  171. in accordance with the license agreements that appear at the top of those
  172. files.  The compilation process for the atty program links it with regex.c.
  173. Therefore binary versions of the atty program are covered by both this
  174. license and the license appearing at the top of regex.c.
  175.  
  176.   5. You may not copy, sublicense, distribute or transfer atty except as
  177. expressly provided under this License Agreement.  Any attempt otherwise
  178. to copy, sublicense, distribute or transfer atty is void and your rights
  179. to use atty under this License agreement shall be automatically terminated.
  180. However, parties who have received computer software programs from you
  181. with this License Agreement will not have their licenses terminated so
  182. long as such parties remain in full compliance.
  183.  
  184.  
  185.                NO WARRANTY
  186.  
  187.   Because atty is licensed free of charge, I provide absolutely no
  188. warranty, to the extent permitted by applicable state law.  Except
  189. when otherwise stated in writing, Kenneth Almquist and/or other
  190. parties provide atty "as is" without warranty of any kind, either
  191. expressed or implied, including, but not limited to, the implied
  192. warranties of merchantability and fitness for a particular purpose.
  193. The entire risk as to the quality and performance of the program is
  194. with you.  Should the atty program prove defective, you assume the cost
  195. of all necessary servicing, repair or correction.
  196.  
  197.  In no event unless required by applicable law will Kenneth Almquist
  198. and/or any other party who may modify and redistribute atty as permitted
  199. above, be liable to you for damages, including any lost profits, lost
  200. monies, or other special, incidental or consequential damages arising
  201. out of the use or inability to use (including but not limited to loss
  202. of data or data being rendered inaccurate or losses sustained by third
  203. parties or a failure of the program to operate with programs provided
  204. by other parties) the program, even if you have been advised of the
  205. possibility of such damages, or for any claim by any other party.
  206. EOF
  207. if test `wc -c < LICENSE` -ne 5831
  208. then    echo 'LICENSE is the wrong size'
  209. fi
  210. echo extracting atty.1.mk
  211. cat > atty.1.mk <<\EOF
  212. echo '.TH ATTY 1'
  213. echo ".ds d `pwd`/atty.bind"
  214. cat <<\!
  215. .SH NAME
  216. atty \- alternative tty driver
  217. .SH SYNOPSYS
  218. .B atty
  219. .B -ls
  220. .B -c
  221. .I command
  222. [
  223. .I bindings-file
  224. ]
  225. .SH COPYRIGHT
  226. .if n Copyright (C) 1989 by Kenneth Almquist.
  227. .if t Copyright \(co 1989 by Kenneth Almquist.  
  228. .SH DESCRIPTION
  229. .I Atty
  230. is an alternative to
  231. .IR tty (4).
  232. It puts the terminal in
  233. .I cbreak
  234. mode to suppress the line editing normally done by
  235. .IR tty (4)
  236. .PP
  237. Most of the standard
  238. .IR tty (4)
  239. features (except for the ones relating to input line editing) are supported.
  240. Skip to the the section titled
  241. .I "Getting Started"
  242. if you just want an introduction to
  243. .IR atty .
  244. .PP
  245. When
  246. .I atty
  247. starts up, it runs the shell specified by the environment variable
  248. SHELL.  It sets the environment variable ATTY to inform programs that
  249. they are running under \fIatty\fR.
  250. If the
  251. .B -l
  252. option is given, the shell is made a login shell (by prepending a minus
  253. sign to arg zero).
  254. Normally suspending the shell will cause
  255. .I atty
  256. to suspend itself.  The
  257. .B -s
  258. option causes
  259. .I atty
  260. to restart a suspended shell instead.
  261. The
  262. .B -c
  263. option causes the shell to run the specified command before reading commands
  264. from the standard input.  This only works with
  265. .IR ash (1).
  266. (I use it to make the prompt of my top level shell different from the
  267. default ``@\ '' \fIwithout\fR passing PS1 as an environment variable,
  268. which would change the prompt for subshells as well as the top level shell.)
  269. .PP
  270. The optional
  271. .I bindings-file
  272. controls the key bindings used by the editor (see
  273. .IR kbind (1)).
  274. If
  275. .I bindings-file
  276. is omitted,
  277. .I atty
  278. use the file "$HOME/.bindc" as the bindings file if it exists;
  279. otherwise it uses the system default binding file ``\*dc''.
  280. .PP
  281. Several escape sequences are handled specially by
  282. .IR atty .
  283. ``ESC ] P''
  284. introduces a prompt.  The remainder of the line consists of a sequence
  285. of digits giving the program ID, a semicolon, and the text of the
  286. prompt.
  287. ``ESC ] D newline'' deletes the current prompt (if any).
  288. These two escape sequences can be used by shells (such as
  289. .IR ash (1))
  290. that know about
  291. .IR atty .
  292. ``ESC ] I'' inserts text in the input buffer.  The remainder of the
  293. line contains the text to be inserted.  This is used by the
  294. .B -c
  295. option of
  296. .IR fmatch (1).
  297. .PP
  298. Clearing the ODDP bit in the sgtty structure (``stty -odd'') turns off
  299. the special input editing features of
  300. .I atty
  301. and causes the normal
  302. .IR tty (4)
  303. input editing to be done.
  304. .PP
  305. When the prompt is set (via ``ESC ] P''),
  306. .I atty
  307. assumes that it can run the
  308. .IR fmatch (1)
  309. program by inputing a command line preceded by an FS character (octal 34).
  310. .sp 2
  311. .B "Getting Started"
  312. .PP
  313. This section gives an overview of the editiing features of
  314. .I atty
  315. when the default binding file is used.  For detailed descriptions of the
  316. commands, see
  317. .IR kbind (1).
  318. .PP
  319. Each command can be given a numeric prefix argument.  One way to do this
  320. is to type ESC followed by the number.  Another is to type ^U.
  321. ^U multiplies the preceding prefix argument by 4 if one is given,
  322. so a single ^U sets the prefix argument to 4, ^U ^U sets the prefix
  323. argument to 16, and so on.  Typically, the prefix argument specifies the
  324. number of times that a command is to be repeated.  Most commands will do
  325. something sensible with a negative argument.
  326. .PP
  327. Printing characters are bound to the
  328. .I self-insert
  329. command, so to enter then you just type them.
  330. Control characters are used for commands, but you can enter them by
  331. preceding them with ^V.
  332. .PP
  333. Several commands correspond to functions normally provided by
  334. .IR tty (4).
  335. DEL (^?) sends an interrupt signal, FS (^\e) sends a quit signal, and
  336. ^Z sends a stop signal.  The first two of these normally flush
  337. .IR atty 's
  338. input buffer.  ^D generate an end of file when typed on an empty line.
  339. (Otherwise it deletes the following character.)  These keys are the ones
  340. that you are most likely to want to re-bind; see below.
  341. .PP
  342. There are several commands for moving around on a line.  ^F moves forward
  343. one character and ^B moves backward one character.  ESC f and ESC b move
  344. forward and backword by words rather than characters.  ESC a and ESC e
  345. move to the beginning and the end of the line, respectively.
  346. .PP
  347. The commands that delete text generally save the deleted text in the
  348. .IR "kill buffer" .
  349. You can then get the text back by typing ^Y.  This feature is often
  350. used to move text around.  Also, if you delete text by accident, you
  351. can get it back by typing ^Y.
  352. .PP
  353. The most important deletion command is BACKSPACE, which deletes the
  354. preceding character.  It only saves the deleted text to the kill buffer
  355. if you gave an explicit prefix argument.  ^D is similar to BACKSPACE,
  356. but deletes forwards rather than backwards.  (As a special case, ^D
  357. generates an end of file when typed on an empty line.)  There are also
  358. commands for deleting words:  ESC BACKSPACE deletes the preceding word and
  359. ESC d deletes the following word.  Finally, ^W deletes everything between
  360. the cursor and a location in the line known as the
  361. .IR mark .
  362. You can set the location of the mark by typing ^@.
  363. .PP
  364. One final text modification command worth mentioning is ^T, which transposes
  365. the two preceding characters.
  366. .PP
  367. .I Atty
  368. mantains a history of lines typed.  ^P moves to the preceding history line
  369. and ^N moves to the next history line.  Both ^P and ^N skip blank lines.
  370. ^R does a reverse search through the history file for a specified regular
  371. expression (which you type in, followed by a carriage return).  When
  372. you go to a line, it is loaded into the input buffer where you can then
  373. edit it.  (Editing it does not affect the contents of the actual history
  374. file.)
  375. .PP
  376. When you type carriage return to send a line,
  377. .I atty
  378. moves to the end of the history file.  You can also send a line using
  379. ^J (NEWLINE); in this case
  380. .I atty
  381. will advance to the next line of the history file rather than moving to
  382. the end.  ^J is useful for resending a series of lines.
  383. ESC . inserts the last word of the last history line.  This
  384. is useful for entering sequences like:
  385. .in +1i
  386. .nf
  387. mkdir directory
  388. cd directory
  389. .fi
  390. .in -1i
  391. Finally, ``ESC ,'' inserts the last line of output.
  392. .sp 2
  393. .B "Changing Bindings"
  394. .PP
  395. If you want to change a few bindings, the best thing to do is to copy
  396. the system binding file \*d to the file .bind in your home directory,
  397. and then edit it.  As an example, suppose you are a closet VMS fanatic
  398. and want to make CONTROL-C be your interrupt character.  You will find
  399. that two of the lines in the .bind file read
  400. .sp
  401. .nf
  402.     b    ^C    upcase-char
  403. and
  404.     b    \e177    tty-intr
  405. .fi
  406. .sp
  407. The first line binds ^C to upcase-char, a function that converts a
  408. character to upper case.  The second line binds DELETE (written here
  409. as \e177; it could also have been written as ^?) to tty-intr.  You
  410. want CONTROL C to do what DELETE does in the standard binding file,
  411. so change the ``upcase-char'' to ``tty-intr''.  Now both CONTROL C
  412. and DELETE will generate an interrupt, which is perfectly legal.  In
  413. practice you probably want to make DELETE delete the preceding character,
  414. which you can do by binding it to delete-backward-char.  The resulting
  415. lines look like:
  416. .sp
  417. .nf
  418.     b    ^C    tty-intr
  419. and
  420.     b    \e177    delete-backward-char
  421. .fi
  422. .PP
  423. After you have set up the .bind file to your satisfaction, type
  424. .sp
  425.     kbind .bind
  426. .sp
  427. This will create a file named .bindc which
  428. .I atty
  429. will read instead of the default binding file.
  430. \fIIf you omit this step, atty will ignore the \fR.bind\fI file and will
  431. continue to use the default binding file.\fR  Atty doesn't look for a
  432. \&.bind file, only a .bindc file.
  433. .SH AUTHORS
  434. Kenneth Almquist
  435. .SH FILES
  436. \*dc\h'0.5i'default binding file
  437. .SH "SEE ALSO"
  438. .ta 2i
  439. \fIkbind\fR(1)
  440. .SH BUGS
  441. Atty could be more intelligent about updating the screen in some cases.
  442. (The slowest terminals on the systems it was developed on were 9600 baud.)
  443. .PP
  444. The program does not create a /etc/utmp entry for the pseudo-terminal that
  445. it uses.  This confuses
  446. .IR talk (1).
  447. .PP
  448. Changing the line discipline will cause problems.  There are also a number
  449. of race conditions.  This program really needs more operating system support.
  450. (Stream pipes would help, but an acknowledgement should really be sent back
  451. when data is read to allow prompts and input lines to be associated correctly.)
  452. !
  453. EOF
  454. if test `wc -c < atty.1.mk` -ne 8197
  455. then    echo 'atty.1.mk is the wrong size'
  456. fi
  457. echo extracting atty.bind
  458. cat > atty.bind <<\EOF
  459. # This is the default key binding file for atty.
  460.  
  461. mode 0
  462.  
  463. default        self-insert
  464. b    ^@    set-mark
  465. b    ^A    beginning-of-line
  466. b    ^B    backward-char
  467. b    ^C    upcase-char
  468. #b    ^D    delete-char
  469. b    ^D    eof-or-delete-char
  470. b    ^E    end-of-line
  471. b    ^F    forward-char
  472. b    ^G    undefined
  473. b    ^H    delete-backward-char
  474. b    \t    self-insert
  475. b    ^J    newline-and-insert
  476. b    ^K    kill-line
  477. b    ^L    undefined
  478. b    \r    newline
  479. b    ^N    next-history
  480. b    ^O    undefined
  481. b    ^P    previous-history
  482. b    ^Q    quoted-insert
  483. b    ^R    re-search-backward
  484. b    ^S    re-search-forward
  485. b    ^T    gosling-transpose-chars
  486. b    ^U    universal-argument
  487. b    ^V    quoted-insert
  488. b    ^W    kill-region
  489. b    ^X    kill-input
  490. b    ^Y    yank
  491. b    ^Z    tty-susp
  492. b    \034    tty-quit
  493. b    \035    undefined
  494. b    \036    undefined
  495. #b    \037    end-of-file
  496. b    \177    tty-intr
  497.  
  498. b    \e^H    backward-kill-word
  499. b    \e\s    set-mark
  500. b    \e<    beginning-of-history
  501. b    \e>    end-of-history
  502. b    \e-    negative-argument
  503. b    \e0    digit-argument
  504. b    \e1    digit-argument
  505. b    \e2    digit-argument
  506. b    \e3    digit-argument
  507. b    \e4    digit-argument
  508. b    \e5    digit-argument
  509. b    \e6    digit-argument
  510. b    \e7    digit-argument
  511. b    \e8    digit-argument
  512. b    \e9    digit-argument
  513. b    \eb    backward-word
  514. b    \eB    backward-word
  515. b    \ed    kill-word
  516. b    \eD    kill-word
  517. b    \ef    forward-word
  518. b    \eF    forward-word
  519. b    \eh    backward-kill-word
  520. b    \eH    backward-kill-word
  521. #b    \em    insert "/usr/spool/mail/ka"
  522. b    \et    transpose-words
  523. b    \eT    transpose-words
  524. b    \eu    upcase-word
  525. b    \eU    upcase-region
  526. b    \ew    copy-region-as-kill
  527. b    \eW    copy-region-as-kill
  528. b    \ex    exchange-point-and-mark
  529. b    \eX    exchange-point-and-mark
  530. b    \e\e    file-complete
  531. b    \e=    list-file-completions
  532. b    \e,    last-output-line
  533. b    \e.    get-history-word
  534. b    \e\177    backward-kill-word
  535.  
  536. # arrow keys
  537. b    \e[C    forward-char
  538. b    \e[D    backward-char
  539. b    \e[A    previous-history
  540. b    \e[B    next-history
  541.  
  542. syntax word    "a-zA-Z0-9$%"
  543. syntax filename    "^ \t\n&();<>|"
  544. syntax abbrev    "^ \t\n&();<>|/"
  545.  
  546. #abbrev    "~"    "$HOME"
  547. EOF
  548. if test `wc -c < atty.bind` -ne 1737
  549. then    echo 'atty.bind is the wrong size'
  550. fi
  551. echo extracting atty.h
  552. cat > atty.h <<\EOF
  553. /*
  554.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  555.  * This file is part of atty, which is distributed under the terms specified
  556.  * by the Atty General Public License.  See the file named LICENSE.
  557.  */
  558.  
  559. struct tty {            /* structure containing terminal modes */
  560.       int ldisc;        /* line discipline */
  561.       struct sgttyb sgtty;    /* stty modes */
  562.       int lmode;        /* local mode */
  563.       struct tchars tchars;    /* special characters */
  564.       struct ltchars ltchars;    /* more special characters */
  565.       /* window size omitted */
  566. };
  567. EOF
  568. if test `wc -c < atty.h` -ne 542
  569. then    echo 'atty.h is the wrong size'
  570. fi
  571. echo extracting atty.c
  572. cat > atty.c <<\EOF
  573. /*
  574.  * Atty - tty(4) replacement.
  575.  *
  576.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  577.  * This file is part of atty, which is distributed under the terms specified
  578.  * by the Atty General Public License.  See the file named LICENSE.
  579.  */
  580.  
  581. #include <stdio.h>
  582. #include <sys/types.h>
  583. #include <sys/time.h>
  584. #include <signal.h>
  585. #include <sgtty.h>
  586. #include <errno.h>
  587. #include <fcntl.h>
  588. #include <setjmp.h>
  589. #include <sys/wait.h>
  590. #include <sys/resource.h>
  591. #include "atty.h"
  592. #include "attyed.h"
  593.  
  594.  
  595. #ifndef __STDC__
  596. #define const
  597. #define volatile
  598. #endif
  599.  
  600.  
  601. #define ECHO_NL 1        /* echo input line terminated by a newline */
  602. #define ECHO_EOF 2        /* echo input line terminated by EOF */
  603. #define ECHO_SIG 3        /* echo interrupt character */
  604.  
  605.  
  606. struct indata {
  607.       struct indata *next;    /* next structure on queue */
  608.       short nchars;        /* number of characters */
  609.       short nwrt;        /* number of characters already written */
  610.       char data[4];        /* characters; should be "char data[nchars]" */
  611. };
  612.  
  613.  
  614. struct tty origtty;        /* original tty modes */
  615. struct tty realtty;        /* tty modes of real tty */
  616. struct tty ptymodes;        /* modes of pty */
  617. int realfcntl;            /* flags for real tty file descriptor */
  618. int ptyfd;            /* file descriptor of pty */
  619. char ptydevice[12];        /* pty device */
  620. int childpid;            /* pid of child process */
  621. jmp_buf sigjmp;            /* where to jump on signal */
  622. volatile int canjump;        /* set if should do longjmp on signal */
  623. volatile int gotsigchild;    /* child is dead */
  624. volatile int gotsigwinch;    /* window size has changed */
  625. volatile int childstatus;    /* status of child */
  626. struct indata *inputq;        /* queue of data to be sent to pty */
  627. struct indata *inqlast;        /* last entry in inputq */
  628. struct mode ttymode;        /* tty characters for editor */
  629. int remoteon;            /* true if TIOCREMOTE mdoe is on */
  630. int needclearin;        /* true if we must clear the tty input */
  631. int echoflag;            /* set if output echoed */
  632. char sigchar;            /* signal character to be echoed */
  633. int noedit;            /* true if not using input editor */
  634. int logall;            /* save all output data in the trace file */
  635. int loginshell;            /* true if should start up login shell */
  636. int nosuspend;            /* don't permit atty to be suspended */
  637. char *shellcmd;            /* prompt to be used by shell */
  638. short ospeed;            /* output speed, for termcap */
  639. int gottty;            /* true if we have read the tty modes */
  640.  
  641. const struct ltchars real_ltchars = { -1, -1, -1, -1, -1, -1 };
  642.  
  643. /* C library routines */
  644. #ifdef __STDC__
  645. int ioctl(int, int, char *);
  646. int isatty(int);
  647. int getpid(void);
  648. char *getenv(char *);
  649. #else
  650. int ioctl();
  651. int isatty();
  652. int getpid();
  653. char *getenv();
  654. #endif
  655.  
  656.  
  657. #ifdef __STDC__
  658. int openpty(void);
  659. void spawnshell(void);
  660. void runtty(void);
  661. void ttyoutc(int);
  662. void outreal(char *, int);
  663. void writereal(void);
  664. void senddata(char *, int);
  665. void writepty(void);
  666. int stripecho(int, char *, int);
  667. void flushptyin(void);
  668. void onchild();
  669. void onwinch();
  670. void indata(char *, int);
  671. void newttymodes();
  672. void fillttymode(void);
  673. void sendsig(int);
  674. void flushoutput(void);
  675. void fatal(char *);
  676. void done(int);
  677. void cleanup(void);
  678. int getttymodes(int, struct tty *);
  679. int setttymodes(int, struct tty *);
  680. int copyttysize(void);
  681. #else
  682. int openpty();
  683. void spawnshell();
  684. void runtty();
  685. void ttyoutc();
  686. void outreal();
  687. void writereal();
  688. void senddata();
  689. void writepty();
  690. int stripecho();
  691. void flushptyin();
  692. void onchild();
  693. void onwinch();
  694. void indata();
  695. void newttymodes();
  696. void fillttymode();
  697. void sendsig();
  698. void flushoutput();
  699. void fatal();
  700. void done();
  701. void cleanup();
  702. int getttymodes();
  703. int setttymodes();
  704. int copyttysize();
  705. #endif
  706.  
  707. #define scopy(s1, s2)    strcpy(s2, s1)    /*TEMPORARY*/
  708.  
  709.  
  710. main(argc, argv)
  711.       char **argv;
  712.       {
  713.       int one = 1;
  714.       int i;
  715.       char **ap;
  716.       char *p;
  717.  
  718.       if (! isatty(2) || ! isatty(0)) {
  719.         fputs("Atty can only be run from a terminal\n", stderr);
  720.         exit(2);
  721.       }
  722.       ap = argv;
  723.       if (argc > 0) {
  724.         ap++;
  725.         for (;;) {
  726.           if ((p = *ap) == NULL || *p++ != '-')
  727.             break;
  728.           ap++;
  729.           if (p[0] == '-' && p[1] == '\0')    /* "--" */
  730.             break;
  731.           while (*p) {
  732.             switch (*p++) {
  733.             case 'l':
  734.                   loginshell++;
  735.                   break;
  736.             case 's':
  737.                   nosuspend++;
  738.                   break;
  739.             case 'c':
  740.                   if (*p == '\0' && (p = *ap++) == NULL) {
  741.                     fputs("atty:  no arg for -c option\n");
  742.                     exit(2);
  743.                   }
  744.                   shellcmd = p;
  745.                   p = "";
  746.                   break;
  747.             default:
  748.                   fprintf(stderr, "atty: Illegal option -%c\n", p[-1]);
  749.                   exit(2);
  750.             }
  751.           }
  752.         }
  753.       }
  754.       signal(SIGCHLD, onchild);
  755.       ptyfd = openpty();
  756.       if (getttymodes(2, &origtty) < 0)
  757.         fatal("getttymodes");
  758.       realfcntl = fcntl(2, F_GETFL, 0);
  759.       gottty = 1;
  760.       ospeed = origtty.sgtty.sg_ospeed;
  761.       edinit(*ap);
  762.       ptymodes = origtty;
  763.       ptymodes.ldisc = NTTYDISC;    /* we must have literal next char */
  764.       ptymodes.sgtty.sg_flags |= EVENP|ODDP;    /* no parity checking */
  765.       realtty = origtty;
  766.       realtty.tchars.t_intrc = -1;
  767.       realtty.tchars.t_quitc = -1;
  768.       realtty.ltchars = real_ltchars;
  769.       realtty.sgtty.sg_flags |= CBREAK;
  770.       realtty.sgtty.sg_flags &=~ (CRMOD|ECHO|LCASE);
  771.       if ((realtty.sgtty.sg_flags & TBDELAY) == XTABS)
  772.         realtty.sgtty.sg_flags &=~ TBDELAY;
  773.       if (setttymodes(2, &realtty) < 0) {
  774.         perror("setttymodes");
  775.         exit(2);
  776.       }
  777.       /* copyttysize(); */
  778.       realfcntl |= FNDELAY;
  779.       fcntl(2, F_SETFL, realfcntl);
  780.       i = fcntl(ptyfd, F_GETFL, 0);
  781.       i |= FNDELAY;
  782.       fcntl(ptyfd, F_SETFL, i);
  783. #ifdef REMOTE
  784.       if (ioctl(ptyfd, TIOCREMOTE, (char *)&one) < 0)
  785.         fatal("TIOCREMOTE");
  786.       remoteon = 1;
  787. #endif
  788.       if (ioctl(ptyfd, TIOCPKT, (char *)&one) < 0)
  789.         fatal("TIOCPKT");
  790.       if (setttymodes(ptyfd, &ptymodes) < 0)
  791.         fatal("set pty modes");
  792.       fillttymode();
  793.       copyttysize();
  794.       if ((childpid = fork()) == -1) {
  795.         fatal("fork");
  796.       }
  797.       if (childpid == 0)
  798.         spawnshell();
  799.       else {
  800.         signal(SIGTTOU, SIG_IGN);
  801.         signal(SIGWINCH, onwinch);
  802.         runtty();
  803.       }
  804.       /*NOTREACHED*/
  805. }
  806.       
  807.  
  808. /*
  809.  * Search for an unused pty.  It returns a file descriptor for the master
  810.  * and leaves the name of the slave in the global variable ptydevice.
  811.  * This code starts at the beginning; a more efficient version might start
  812.  * at a randomly chosen point in the middle.
  813.  */
  814.  
  815. int
  816. openpty() {
  817.       char c;
  818.       int i;
  819.       int fd;
  820.  
  821.       scopy("/dev/ptyXX", ptydevice);
  822.       for (c = 'p'; c <= 's'; c++) {
  823.             for (i = 0 ; i < 16 ; i++) {
  824.                   ptydevice[8] = c;
  825.                   ptydevice[9] = "0123456789abcdef"[i];
  826.                   fd = open(ptydevice, 2);
  827.                   if (fd >= 0) {
  828.             ptydevice[5] = 't';    /* change "pty" to "tty" */
  829.                         return fd;        /* success */
  830.                   }
  831.                   if (errno == ENOENT)
  832.                         goto failure;
  833.             }
  834.       }
  835. failure:
  836.       fprintf(stderr, "Out of pty's\n");
  837.       exit(2);
  838. }
  839.  
  840.  
  841. /*
  842.  * This routine exec's the shell.
  843.  */
  844.  
  845. void
  846. spawnshell() {
  847.       int fd;
  848.       int mypid = getpid();
  849.       char *shell;
  850.       char **env;
  851.       char **ep;
  852.       extern char **environ;
  853.       char arg0[16];
  854.       char *p;
  855.       char *basename;
  856.       char *argv[4];
  857.  
  858.       if ((shell = getenv("SHELL")) == NULL)
  859.         shell = "/bin/sh";
  860.       for (ep = environ ; *ep ; ep++);
  861.       if ((env = (char **)malloc((char *)(ep + 2) - (char *)environ)) == NULL)
  862.         fatal("Malloc failed");
  863.       for (ep = env ; *environ ; environ++) {
  864.         if (strncmp(*environ, "ATTY=", 5) != 0)
  865.           *ep++ = *environ;
  866.       }
  867.       *ep++ = "ATTY=";
  868.       *ep = NULL;
  869.       close(0);
  870.       close(1);
  871.       close(ptyfd);
  872.       fd = open("/dev/tty", O_RDWR);
  873.       if (fd >= 0) {
  874.         ioctl(fd, TIOCNOTTY, (char *)0);
  875.         close(fd);
  876.       }
  877.       setpgrp(mypid, 0);
  878.       if (open(ptydevice, O_RDWR) != 0) {
  879.         perror(ptydevice);
  880.         exit(2);
  881.       }
  882.       setpgrp(mypid, mypid);
  883.       close(2);
  884.       dup(0);                /* file descriptor 1 = dup of 0 */
  885.       dup(0);                /* file descriptor 2 = dup of 0 */
  886.       if (ioctl(2, TIOCSPGRP, (char *)&mypid) < 0)
  887.         printf("TIOCSPGRP failed, errno=%d\n", errno);
  888.       basename = shell;
  889.       for (p = shell ; *p ; p++) {
  890.         if (*p == '/')
  891.           basename = p + 1;
  892.       }
  893.       if (loginshell) {
  894.         arg0[0] = '-';
  895.         for (p = arg0 + 1 ; p < arg0 + 15 && *basename ; *p++ = *basename++);
  896.         *p = '\0';
  897.         basename = arg0;
  898.       }
  899.       argv[0] = basename;
  900.       argv[1] = NULL;
  901.       if (shellcmd) {
  902.         argv[1] = "-sc";
  903.         argv[2] = shellcmd;
  904.         argv[3] = NULL;
  905.       }
  906.       execve(shell, argv, env);
  907.       perror(shell);
  908.       exit(2);
  909. }
  910.  
  911.  
  912. #define OUTRSIZE 512
  913.  
  914. char outrbuf[OUTRSIZE];
  915. char *outrp = outrbuf;
  916. char *outrnext = outrbuf;
  917. int outrsize;
  918.  
  919.  
  920. /*
  921.  * This routine contains the main loop which is run after initialization.
  922.  */
  923.  
  924. void
  925. runtty() {
  926.       volatile int ptybit = 1 << ptyfd;
  927.       volatile int realbit = 1 << 2;
  928.       volatile int nfds = ptyfd + 1;
  929.       volatile int inbits, outbits, exceptbits;
  930.       volatile int sel;
  931.       int i, j;
  932.       char buf[260];
  933.  
  934.       if (setjmp(sigjmp))
  935.         goto gotsig;
  936.       for (;;) {
  937.         inbits = realbit;
  938.         outbits = 0;
  939.         if (outrsize == 0)
  940.           inbits |= ptybit;
  941.         else
  942.           outbits = realbit;
  943.         if (inputq != NULL)
  944.           outbits |= ptybit;
  945.         exceptbits = 0;
  946.         canjump = 1;
  947.         if (gotsigchild || gotsigwinch)
  948.           goto gotsig;
  949.         sel = select(nfds, &inbits, &outbits, &exceptbits, (struct timeval *)NULL);
  950.         canjump = 0;
  951.         if (0) {    /* if interrupted by signal */
  952. gotsig:
  953.           if (gotsigchild) {
  954.             gotsigchild = 0;
  955.             i = childstatus;
  956.             if ((i & 0xFF) == 0177
  957.              && (i >> 8 == SIGTSTP || i >> 8 == SIGSTOP)) {
  958.                   if (! nosuspend) {
  959.                     setttymodes(2, &origtty);
  960.                     fcntl(2, F_SETFL, realfcntl &~ FNDELAY);
  961.                     kill(getpid(), SIGTSTP);
  962.                     setttymodes(2, &realtty);
  963.                     fcntl(2, F_SETFL, realfcntl);
  964.                   }
  965.                   killpg(childpid, SIGCONT);
  966.             } else {
  967.                   if ((i & 0xFF) == 0)
  968.                     i >>= 8;
  969.                   else
  970.                     i = i & 0xFF | 0x80;
  971.                   done(i);
  972.             }
  973.           }
  974.           if (gotsigwinch) {
  975.             gotsigwinch = 0;
  976.             newscrnwidth(copyttysize());
  977.           }
  978.         } else if (sel < 0) {
  979.           if (errno != EINTR)
  980.             fatal("select");
  981.         } else {
  982.           if (inbits & realbit) {
  983.             if ((i = read(2, buf, sizeof buf)) < 0)
  984.                   fatal("tty read");
  985.             if (i == 0)
  986.                   fatal("tty eof");
  987.             indata(buf, i);
  988.           }
  989.           if (inbits & ptybit) {
  990.             if ((i = read(ptyfd, buf, sizeof buf)) < 0) {
  991.                   /*
  992.                    * Probably the problem is that the child
  993.                    * process has terminated, which causes the
  994.                    * ptc device to return EIO.
  995.                    */
  996.                   if (gotsigchild)
  997.                     goto gotsig;
  998.                   if (errno == EIO) {
  999.                     sleep(1);
  1000.                     errno = EIO;
  1001.                     if (gotsigchild)
  1002.                       goto gotsig;
  1003.                   }
  1004.                   fatal("pty read");
  1005.             }
  1006.             if (i == 0)
  1007.                   fatal("pty eof");
  1008. #ifndef TIOCPKT_IOCTL
  1009.             newttymodes();
  1010. #endif
  1011.             if (buf[0] == TIOCPKT_DATA) {
  1012.                   j = 0;
  1013.                   if (echoflag) {
  1014.                     j = stripecho(echoflag, buf + 1, i - 1);
  1015.                     echoflag = 0;
  1016.                   }
  1017.                   j++;
  1018. #ifdef notdef
  1019.                   if (logall)
  1020.                     fwrite(buf + 1, i - 1, 1, trace);
  1021. #endif
  1022.                   outchars(buf + j, i - j);
  1023.                   writereal();
  1024.                   if (noedit == 0)
  1025.                     refresh();
  1026.             } else {
  1027. #ifdef TIOCPKT_IOCTL
  1028.                   if (buf[0] & TIOCPKT_IOCTL)
  1029.                     newttymodes();
  1030. #endif
  1031.                   if (buf[0] & TIOCPKT_FLUSHREAD) {
  1032.                     clearinput();
  1033.                   }
  1034.             }
  1035.           }
  1036.           if (outbits & realbit) {
  1037.             writereal();
  1038.           }
  1039.           if (outbits & ptybit) {
  1040.             writepty();
  1041.           }
  1042.         }
  1043.       }
  1044. }
  1045.  
  1046.  
  1047. /*
  1048.  * Output a character to the real tty device.
  1049.  */
  1050.  
  1051. void
  1052. ttyoutc(c)
  1053.       char c;
  1054.       {
  1055.       outreal(&c, 1);
  1056. }
  1057.  
  1058.  
  1059. /*
  1060.  * Output a block of characters to the real tty device.
  1061.  */
  1062.  
  1063. void
  1064. outreal(p, size)
  1065.       char *p;
  1066.       {
  1067.       int n = &outrbuf[OUTRSIZE] - outrnext;
  1068.  
  1069.       if (size + outrsize > OUTRSIZE)
  1070.         fatal("output overflow");
  1071.       if (n > size)
  1072.         n = size;
  1073.       bcopy(p, outrnext, n);
  1074.       outrnext += n;
  1075.       if (outrnext == &outrbuf[OUTRSIZE])
  1076.         outrnext = outrbuf;
  1077.       if (n < size) {
  1078.         p += n;
  1079.         n = size - n;
  1080.         bcopy(p, outrnext, n);
  1081.         outrnext += n;
  1082.       }
  1083.       outrsize += size;
  1084. }
  1085.  
  1086.  
  1087. /*
  1088.  * Write as much data as possible to the real tty, until we block.
  1089.  */
  1090.  
  1091. void
  1092. writereal() {
  1093.       int i;
  1094.       int n;
  1095.  
  1096.       while (outrsize > 0) {
  1097.         n = &outrbuf[OUTRSIZE] - outrp;
  1098.         if (n > outrsize)
  1099.           n = outrsize;
  1100.         if ((i = write(2, outrp, n)) < 0) {
  1101.           if (errno == EWOULDBLOCK)
  1102.             break;
  1103.           else
  1104.             fatal("tty write");
  1105.         }
  1106.         outrsize -= i;
  1107.         outrp += i;
  1108.         if (outrp == &outrbuf[OUTRSIZE])
  1109.           outrp = outrbuf;
  1110.       }
  1111. }
  1112.  
  1113.  
  1114. /*
  1115.  * Send data to the pty.
  1116.  */
  1117.  
  1118. #define INDATAHDR ((char *)((struct indata *)0)->data - (char *)0)
  1119.  
  1120. void
  1121. senddata(p, size)
  1122.       char *p;
  1123.       {
  1124.       struct indata *ip;
  1125.  
  1126.       if ((ip = (struct indata *)malloc(INDATAHDR + size)) == NULL)
  1127.         fatal("malloc pty data");
  1128.       ip->nchars = size;
  1129.       ip->nwrt = 0;
  1130.       bcopy(p, ip->data, size);
  1131.       ip->next = NULL;
  1132.       if (inputq == NULL)
  1133.         inputq = ip;
  1134.       else
  1135.         inqlast->next = ip;
  1136.       inqlast = ip;
  1137. }
  1138.  
  1139.  
  1140.  
  1141. /*
  1142.  * Like writereal, but for pty.
  1143.  */
  1144.  
  1145. void
  1146. writepty() {
  1147. #ifdef REMOTE
  1148.       int i;
  1149.       struct indata *ip;
  1150.       int one = 1;
  1151.  
  1152.       if ((ip = inputq) == NULL)
  1153.         return;
  1154.       if (! remoteon) {
  1155.         if (ioctl(ptyfd, TIOCREMOTE, (char *)&one) < 0)
  1156.           fatal("TIOCREMOTE");
  1157.         remoteon = 1;
  1158.       }
  1159.       if ((i = write(ptyfd, ip->data, ip->nchars)) < 0) {
  1160.         if (errno != EWOULDBLOCK)
  1161.           fatal("pty write");
  1162.         return;
  1163.       } else if (i != ip->nchars) {
  1164.         fatal("partial pty write");
  1165.       }
  1166.       inputq = ip->next;
  1167.       free((char *)ip);
  1168. #else
  1169.       int i;
  1170.       struct indata *ip;
  1171.       char buf[512];
  1172.       int buflen;
  1173.       char c;
  1174.       char *p;
  1175.       int echotype;
  1176.  
  1177.       if ((ip = inputq) == NULL)
  1178.         return;
  1179.       if (noedit) {
  1180.         i = write(ptyfd, ip->data + ip->nwrt, ip->nchars - ip->nwrt);
  1181.         if (i < 0)
  1182.           fatal("pty write");
  1183.         if ((ip->nwrt += i) >= ip->nchars) {
  1184.           inputq = ip->next;
  1185.           free((char *)ip);
  1186.         }
  1187.       } else {      
  1188.         newttymodes();    /* just in case */
  1189.         p = buf;
  1190.         for (i = ip->nwrt ; i < ip->nchars ; i++) {
  1191.           c = ip->data[i];
  1192.           if (c == ptymodes.sgtty.sg_erase
  1193.            || c == ptymodes.sgtty.sg_kill
  1194.            || c == ptymodes.tchars.t_intrc
  1195.            || c == ptymodes.tchars.t_quitc
  1196.            || c == ptymodes.tchars.t_startc
  1197.            || c == ptymodes.tchars.t_stopc
  1198.            || c == ptymodes.tchars.t_eofc
  1199.            || c == ptymodes.tchars.t_brkc
  1200.            || c == ptymodes.ltchars.t_suspc
  1201.            || c == ptymodes.ltchars.t_dsuspc
  1202.            || c == ptymodes.ltchars.t_rprntc
  1203.            || c == ptymodes.ltchars.t_flushc
  1204.            || c == ptymodes.ltchars.t_werasc
  1205.            || c == ptymodes.ltchars.t_lnextc)
  1206.             *p++ = ptymodes.ltchars.t_lnextc;
  1207.           *p++ = c;
  1208.         }
  1209.         echotype = ECHO_NL;
  1210.         if (p == buf || p[-1] != '\n') {
  1211.           *p++ = ptymodes.tchars.t_eofc;
  1212.           echotype = ECHO_EOF;
  1213.         }
  1214.         buflen = p - buf;
  1215.         i = write(ptyfd, buf, buflen);
  1216.         if (i < 0) {
  1217.           if (errno != EWOULDBLOCK)
  1218.             fatal("pty write");
  1219.         } else if (i < buflen) {
  1220.           for (p = buf ; p < buf + buflen ; p++) {
  1221.             if (*p == ptymodes.ltchars.t_lnextc)
  1222.                   p++;
  1223.             ip->nwrt++;
  1224.           }
  1225.         } else {
  1226.           inputq = ip->next;
  1227.           free((char *)ip);
  1228.         }
  1229.         if (ptymodes.sgtty.sg_flags & ECHO) {
  1230.           echoflag = echotype;
  1231.         }
  1232.       }
  1233. #endif
  1234. }
  1235.  
  1236.  
  1237.  
  1238. /*
  1239.  * Locate the end of the echoed characters.
  1240.  */
  1241.  
  1242. int
  1243. stripecho(type, p, n)
  1244.       char *p;
  1245.       {
  1246.       register char *q;
  1247.  
  1248.       q = p;
  1249.       if (type == ECHO_NL) {
  1250.         do {
  1251.           if (--n < 0)
  1252.             return 0;
  1253.         } while (*q++ != '\n');
  1254.       } else if (type == ECHO_EOF) {
  1255.         do {
  1256.           if (--n <= 0)
  1257.             return 0;
  1258.         } while (*q++ != '\b' || *q != '\b');
  1259.         q++;
  1260.       } else {
  1261.         if (n < 2 || p[0] != '^' || p[1] != (sigchar ^ 0100))
  1262.           return 0;
  1263.         q += 2;
  1264.       }
  1265.       return q - p;
  1266. }
  1267.  
  1268.  
  1269.  
  1270. /*
  1271.  * Clear pty input queue.  The input queue is the one we write to.
  1272.  */
  1273.  
  1274. void
  1275. flushptyin() {
  1276.       struct indata *ip;
  1277.  
  1278.       while (inputq != NULL) {
  1279.         ip = inputq;
  1280.         inputq = ip->next;
  1281.         free((char *)ip);
  1282.       }
  1283.       /* ioctl(ptyfd, TIOCFLUSH, (char *)&mode); */
  1284. }
  1285.  
  1286.  
  1287. /*
  1288.  * Signal handler invoked when a child dies or is stopped.
  1289.  */
  1290.  
  1291. void
  1292. onchild() {
  1293.       int pid;
  1294.       int status;
  1295.       int e = errno;
  1296.  
  1297.       if ((pid = wait3(&status, WNOHANG|WUNTRACED, (struct rusage *)0)) == childpid) {
  1298.         childstatus = status;
  1299.         gotsigchild = 1;
  1300.         if (canjump)
  1301.           longjmp(sigjmp, 1);
  1302.       }
  1303.       errno = e;
  1304. }
  1305.  
  1306.  
  1307. /*
  1308.  * Signal handler called when the terminal changes size.
  1309.  */
  1310.  
  1311. void
  1312. onwinch() {
  1313.       gotsigwinch = 1;
  1314.       if (canjump)
  1315.         longjmp(sigjmp, 1);
  1316. }
  1317.  
  1318.  
  1319. /*
  1320.  * Called to process data received from the real tty.
  1321.  */
  1322.  
  1323. void
  1324. indata(p, n)
  1325.       char *p;
  1326.       {
  1327.       if (noedit) {
  1328.         senddata(p, n);
  1329. #ifdef REMOTE
  1330.         if (ptymodes.sgtty.sg_flags & ECHO) {
  1331.           outchars(p, n);
  1332.           writereal();
  1333.         }
  1334. #endif
  1335.       } else {
  1336.         while (--n >= 0)
  1337.           inchar(*p++);
  1338.         refresh();
  1339.         if (needclearin) {
  1340.           clearinput();
  1341.           needclearin = 0;
  1342.         }
  1343.         if (outrsize)
  1344.           writereal();
  1345.       }
  1346. }
  1347.  
  1348.  
  1349. clearinput() {
  1350.       edflush();
  1351.       flushptyin();
  1352. }
  1353.  
  1354.  
  1355. /*
  1356.  * Send a signal to the user process.
  1357.  */
  1358.  
  1359. void
  1360. sendsig(signo) {
  1361. #ifdef REMOTE
  1362.       int pgrp;
  1363.       int zero = 0;
  1364.  
  1365.       if (ioctl(ptyfd, TIOCFLUSH, (char *)&zero) < 0)
  1366.         fatal("TIOCFLUSH");
  1367.       needclearin++;
  1368.       if (ioctl(ptyfd, TIOCGPGRP, (char *)&pgrp) < 0)
  1369.         fatal("TIOCGPRGP");
  1370.       killpg(pgrp, signo);
  1371. #else
  1372.       char c;
  1373.  
  1374.       switch (signo) {
  1375.     case SIGINT:    c = ttymode.intr;  break;
  1376.     case SIGQUIT:    c = ttymode.quit;  break;
  1377.     case SIGTSTP:    c = ttymode.susp;  break;
  1378.     default:    fatal("sendsig arg");
  1379.       }      
  1380.       if ((ptymodes.lmode & LNOFLSH) == 0 && signo != SIGTSTP)
  1381.         flushoutput();
  1382.       echoflag = ECHO_SIG;
  1383.       sigchar = c;
  1384.       if (write(ptyfd, &sigchar, 1) != 1)
  1385.         fatal("sendsig write");
  1386. #endif
  1387. }
  1388.  
  1389.  
  1390. void
  1391. flushoutput() {
  1392.       int two = 2;
  1393.  
  1394.       outrp = outrnext = outrbuf;
  1395.       outrsize = 0;
  1396.       ioctl(2, TIOCFLUSH, (char *)&two);
  1397. }
  1398.  
  1399.  
  1400. puttty(c)
  1401.       char c;
  1402.       {
  1403.       write(2, &c, 1);
  1404. }
  1405.  
  1406.  
  1407. puttstr(s)
  1408.       char *s;
  1409.       {
  1410.       write(2, s, strlen(s));
  1411. }
  1412.  
  1413.  
  1414. /*
  1415.  * Called when the tty modes of the pty are changed by the user.
  1416.  */
  1417.  
  1418. #define SGCOPY    (ALLDELAY|RAW)    /* bits to copy from pty to real tty */
  1419.  
  1420. void
  1421. newttymodes() {
  1422.       char startc, stopc;
  1423.       int changereal;
  1424.       int flags;
  1425.  
  1426.       if (getttymodes(ptyfd, &ptymodes) < 0)
  1427.         fatal("get pty modes");
  1428.       noedit = 0;
  1429. #ifdef notdef
  1430.       if (ptymodes.sgtty.sg_flags & (RAW|CBREAK))
  1431. #else
  1432.       if ((ptymodes.sgtty.sg_flags & (RAW|CBREAK|ODDP)) != ODDP)
  1433. #endif
  1434.         noedit = 1;
  1435.       changereal = 0;
  1436.       startc = ptymodes.tchars.t_startc;
  1437.       stopc = ptymodes.tchars.t_stopc;
  1438. #ifdef notdef
  1439.       if (ptymodes.sgtty.sg_flags & RAW)
  1440.         startc = stopc = -1;
  1441. #endif
  1442.       if (realtty.tchars.t_startc != startc
  1443.        || realtty.tchars.t_stopc != stopc) {
  1444.         realtty.tchars.t_startc = startc;
  1445.         realtty.tchars.t_stopc = stopc;
  1446.         changereal = 1;
  1447.       }
  1448.       flags = ptymodes.sgtty.sg_flags & SGCOPY;
  1449.       if ((flags & TBDELAY) == XTABS)
  1450.         flags &=~ TBDELAY;
  1451.       if ((realtty.sgtty.sg_flags & SGCOPY) !=  flags) {
  1452.         realtty.sgtty.sg_flags &=~ SGCOPY;
  1453.         realtty.sgtty.sg_flags |= flags;
  1454.         changereal = 1;
  1455.       }
  1456.       if ((realtty.lmode & LDECCTQ) != (ptymodes.lmode & DECCTQ)) {
  1457.         realtty.lmode &=~ LDECCTQ;
  1458.         if (ptymodes.lmode & DECCTQ)
  1459.           realtty.lmode |= LDECCTQ;
  1460.         changereal = 1;
  1461.       }
  1462.       if (changereal) {
  1463.         if (setttymodes(2, &realtty) < 0)
  1464.           fatal("change real");
  1465.       }
  1466.       if (ttymode.intr != ptymodes.tchars.t_intrc
  1467.        || ttymode.quit != ptymodes.tchars.t_quitc
  1468.        || ttymode.erase!= ptymodes.sgtty.sg_erase
  1469.        || ttymode.kill != ptymodes.sgtty.sg_kill
  1470.        || ttymode.eof  != ptymodes.tchars.t_eofc
  1471.        || ttymode.susp != ptymodes.ltchars.t_suspc
  1472.        || ttymode.echo != (ptymodes.sgtty.sg_flags & ECHO)) {
  1473.         fillttymode();
  1474.         newttychars();
  1475.       }
  1476. }
  1477.  
  1478.  
  1479. void
  1480. fillttymode() {
  1481.       ttymode.intr = ptymodes.tchars.t_intrc;
  1482.       ttymode.quit = ptymodes.tchars.t_quitc;
  1483.       ttymode.erase= ptymodes.sgtty.sg_erase;
  1484.       ttymode.kill = ptymodes.sgtty.sg_kill;
  1485.       ttymode.eof  = ptymodes.tchars.t_eofc;
  1486.       ttymode.susp = ptymodes.ltchars.t_suspc;
  1487.       ttymode.echo = ptymodes.sgtty.sg_flags & ECHO;
  1488. }
  1489.  
  1490.  
  1491. /*
  1492.  * A unexpected error occurred.
  1493.  */
  1494.  
  1495. void
  1496. fatal(msg)
  1497.       char *msg;
  1498.       {
  1499.       int e = errno;
  1500.  
  1501.       cleanup();
  1502.       errno = e;
  1503.       perror(msg);
  1504.       fflush(stderr);
  1505.       abort();
  1506. }
  1507.  
  1508.  
  1509. /*
  1510.  * Called if initialization fails.
  1511.  */
  1512.  
  1513. void
  1514. badinit(msg)
  1515.       char *msg;
  1516.       {
  1517.       cleanup();
  1518.       fputs(msg, stderr);
  1519.       putc('\n', stderr);
  1520.       exit(2);
  1521. }
  1522.  
  1523.  
  1524. /*
  1525.  * Exit the program, resetting the terminal modes first.  Status is the
  1526.  * exit status.
  1527.  */
  1528.  
  1529. void
  1530. done(status) {
  1531.       cleanup();
  1532.       exit(status);
  1533. }
  1534.  
  1535.  
  1536. void
  1537. cleanup() {
  1538.       if (gottty) {
  1539.         setttymodes(2, &origtty);
  1540.         realfcntl &=~ FNDELAY;
  1541.         fcntl(2, F_SETFL, realfcntl);
  1542.       }
  1543. }
  1544.  
  1545.  
  1546.  
  1547. int
  1548. getttymodes(fd, tty)
  1549.       int fd;
  1550.       struct tty *tty;
  1551.       {
  1552.       if (ioctl(fd, TIOCGETD, (char *)&tty->ldisc) < 0
  1553.        || ioctl(fd, TIOCGETP, (char *)&tty->sgtty) < 0
  1554.        || ioctl(fd, TIOCGETC, (char *)&tty->tchars) < 0
  1555.        || ioctl(fd, TIOCLGET, (char *)&tty->lmode) < 0
  1556.        || ioctl(fd, TIOCGLTC, (char *)&tty->ltchars) < 0)
  1557.         return -1;
  1558.       return 0;
  1559. }
  1560.  
  1561.  
  1562.  
  1563. int
  1564. setttymodes(fd, tty)
  1565.       int fd;
  1566.       struct tty *tty;
  1567.       {
  1568.       if (ioctl(fd, TIOCSETD, (char *)&tty->ldisc) < 0
  1569.        || ioctl(fd, TIOCSETP, (char *)&tty->sgtty) < 0
  1570.        || ioctl(fd, TIOCSETC, (char *)&tty->tchars) < 0
  1571.        || ioctl(fd, TIOCLSET, (char *)&tty->lmode) < 0
  1572.        || ioctl(fd, TIOCSLTC, (char *)&tty->ltchars) < 0)
  1573.         return -1;
  1574.       return 0;
  1575. }
  1576.  
  1577.  
  1578.  
  1579. /*
  1580.  * Copy the size of the tty from the real device to the pty.
  1581.  * Returns the number of columns.
  1582.  */
  1583.  
  1584. int
  1585. copyttysize() {
  1586. #ifdef TIOCGWINSZ
  1587.       struct winsize size;
  1588.  
  1589.       if (ioctl(2, TIOCGWINSZ, (char *)&size) >= 0)
  1590.         ioctl(ptyfd, TIOCSWINSZ, (char *)&size);
  1591.       return size.ws_col;
  1592. #else
  1593.       return 0;
  1594. #endif
  1595. }
  1596. EOF
  1597. if test `wc -c < atty.c` -ne 22147
  1598. then    echo 'atty.c is the wrong size'
  1599. fi
  1600. echo extracting attyed.h
  1601. cat > attyed.h <<\EOF
  1602. /*
  1603.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1604.  * This file is part of atty, which is distributed under the terms specified
  1605.  * by the Atty General Public License.  See the file named LICENSE.
  1606.  *
  1607.  * This file defines the interface between the editor and the rest of
  1608.  * atty.  First come things defined in atty and exported to the editor.
  1609.  */
  1610.  
  1611. #define MAXLLEN 254        /* maximum line length (excluding newline) */
  1612. #define COLUMNS 132        /* maximum screen width we expect */
  1613.  
  1614.  
  1615. struct mode {
  1616.       int intr;            /* interrupt character */
  1617.       int quit;            /* quit character */
  1618.       int erase;        /* erase character */
  1619.       int kill;            /* kill character */
  1620.       int eof;            /* eof character */
  1621.       int susp;            /* suspend character */
  1622.       int echo;            /* set if echo turned on */
  1623. };
  1624.  
  1625.  
  1626. extern struct mode ttymode;    /* various info about the tty */
  1627. extern short ospeed;        /* output speed (as returned by gtty) */
  1628. extern int promptset;        /* true if prompt specified by the user */
  1629. extern char prompt[COLUMNS];    /* the prompt, if any */
  1630. extern int column;        /* column for writing output to (see below) */
  1631. extern char outline[COLUMNS];    /* current output line */
  1632. extern char lastoutline[COLUMNS]; /* last complete line of output */
  1633. extern int lastoutlinelen;    /* lenght of lastoutline */
  1634.  
  1635.  
  1636. #ifdef __STDC__
  1637. void ttyoutc(int);        /* write a character to the tty */
  1638. void outreal(char *, int);    /* write a block of characters to the tty */
  1639. void senddata(char *, int);    /* send a line to the process */
  1640. void badinit(char *);        /* edinit failed; print message and exit */
  1641. #else
  1642. void ttyoutc();
  1643. void outreal();
  1644. void senddata();
  1645. void badinit();
  1646. #endif
  1647.  
  1648.  
  1649.  
  1650. /*
  1651.  * Now routines provide by the editor.
  1652.  */
  1653.  
  1654. #ifdef __STDC__
  1655. void edinit(char *);        /* initialize the editor */
  1656. void newttychars(void);        /* called when ttymode changes */
  1657. void newscrnwidth(int);        /* called when the screen width changes */
  1658. void inchar(int);        /* called to input a character */
  1659. void edflush(void);        /* discard any input */
  1660. void refresh(void);        /* turn display on and update it */
  1661. void dispoff(int);        /* turn display off */
  1662. void freezedisp(int);        /* freeze the editor display */
  1663. void insertchars(char *, int);    /* insert characters into input buffer */
  1664. #else
  1665. void edinit();
  1666. void newttychars();
  1667. void newscrnwidth();
  1668. void inchar();
  1669. void edflush();
  1670. void refresh();
  1671. void dispoff();
  1672. void freezedisp();
  1673. void insertchars();
  1674. #endif
  1675. EOF
  1676. if test `wc -c < attyed.h` -ne 2378
  1677. then    echo 'attyed.h is the wrong size'
  1678. fi
  1679. echo extracting bind.h
  1680. cat > bind.h <<\EOF
  1681. /*
  1682.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1683.  * This file is part of atty, which is distributed under the terms specified
  1684.  * by the Atty General Public License.  See the file named LICENSE.
  1685.  */
  1686.  
  1687. #define BINDMAGIC 300        /* magic number at start of .bindc file */
  1688.  
  1689. #define C_FUNC 0        /* start of functions */
  1690. #define C_PFXTBL 1        /* prefix table */
  1691. #define C_INSERT 2        /* insert string */
  1692. #define C_UNDEF 3        /* not defined */
  1693.  
  1694. #define NSYNTAX 3        /* number of syntax classes */
  1695. EOF
  1696. if test `wc -c < bind.h` -ne 495
  1697. then    echo 'bind.h is the wrong size'
  1698. fi
  1699. echo extracting bindc.5
  1700. cat > bindc.5 <<\EOF
  1701. .TH BINDC 5
  1702. .SH NAME
  1703. bindc \- compiled bindings file
  1704. .SH COPYRIGHT
  1705. .if n Copyright (C) 1989 by Kenneth Almquist.
  1706. .if t Copyright \(co 1989 by Kenneth Almquist.  
  1707. .SH DESCRIPTION
  1708. The manual page describes the format of the compiled binding files
  1709. produced by
  1710. .IR kbind (1)
  1711. and used by
  1712. .IR atty (1).
  1713. The format assumes eight bit characters.  Short integers are stored in
  1714. two bytes, with the low order byte first.
  1715. .PP
  1716. The overall format of the file is:
  1717.  
  1718. .in +1i
  1719. .nf
  1720. Magic number (300) (2 bytes)
  1721. Version number of the bindc format (1 byte)
  1722. Number of keymaps (1 bytes)
  1723. Size of string area (2 bytes)
  1724. Size of abbreviations (2 bytes)
  1725. Keymap
  1726. ...
  1727. Word syntax bitmap (16 bytes)
  1728. File name syntax bitmap (16 bytes)
  1729. Abbrev syntax bitmap (16 bytes)
  1730. String area
  1731. Abbreviations area
  1732. .fi
  1733. .in -1i
  1734. .PP
  1735. A keymap consists of two 128 byte arrays, which are indexed by the
  1736. input character.  The first array specifies the type of operation
  1737. to be performed when that key is typed.  C_FUNC specifies that a
  1738. function is to be performed.  The number of the function is given
  1739. by the second array.  C_PFXTBL specifies that the next key is to
  1740. be interpreted using the keymap whose number is given by the
  1741. second array.  (This is used to implement multicharacter commands.)  \
  1742. C_INSERT causes a string in the string area to be inserted.  The
  1743. number of the string appears in the second array.  C_UNDEF indicates
  1744. that this character is undefined.
  1745. .PP
  1746. The string table consists of a series of strings laid end to end.
  1747. Each string is preceded by a single byte giving the length of the
  1748. string.  The length byte is used instead of a nul terminator to allow
  1749. nul characters to be included in strings.  The abbreviations area
  1750. contains pairs of strings laid end to end.  Each string is preceded by
  1751. a byte giving the length of the string.  The first string in each pair
  1752. is the name of an abbreviation; the second is the string that the
  1753. abbreviation expands to.
  1754. .SH AUTHORS
  1755. Kenneth Almquist
  1756. .SH "SEE ALSO"
  1757. .IR kbind (1),
  1758. .IR atty (1).
  1759. EOF
  1760. if test `wc -c < bindc.5` -ne 1987
  1761. then    echo 'bindc.5 is the wrong size'
  1762. fi
  1763. echo extracting ed.h
  1764. cat > ed.h <<\EOF
  1765. /*
  1766.  * Stuff shared between ed.c and update.c.
  1767.  *
  1768.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1769.  * This file is part of atty, which is distributed under the terms specified
  1770.  * by the Atty General Public License.  See the file named LICENSE.
  1771.  */
  1772.  
  1773. extern char curline[MAXLLEN+1];    /* line being edited */
  1774. extern char *point;        /* cursor location within line */
  1775. extern char *endcurline;    /* end of current line */
  1776. extern int needbeep;        /* set to request beep */
  1777. extern char editprompt;        /* prompt for recursive edit */
  1778.  
  1779.  
  1780. #ifdef __STDC__
  1781. void gettermcap(void);
  1782. void movetoend(void);
  1783. #else
  1784. void gettermcap();
  1785. void movetoend();
  1786. #endif
  1787. EOF
  1788. if test `wc -c < ed.h` -ne 644
  1789. then    echo 'ed.h is the wrong size'
  1790. fi
  1791. echo Archive 1 unpacked
  1792. exit
  1793.