Initial import to CVS. Basic functions are working, albeit with a couple of showstopper memory bugs and many missing features. Detaching, reattaching, creating new sessions, listing sessions work acceptably for using with shells. Simple curses programs (top, systat, tetris) and more complicated ones (mutt, emacs) that don't require scrolling regions (ESC[r) mostly work fine (including mutt, emacs). No status bar yet and no key remapping or other customisation.

pull/1/head
Nicholas Marriott 2007-07-09 19:04:12 +00:00
parent 2905e0ef10
commit a41ece5ff0
19 changed files with 6798 additions and 0 deletions

11
CHANGES Normal file
View File

@ -0,0 +1,11 @@
09 July 2007
* Initial import to CVS. Basic functions are working, albeit with a couple of
showstopper memory bugs and many missing features. Detaching, reattaching,
creating new sessions, listing sessions work acceptably for using with shells.
Simple curses programs (top, systat, tetris) and more complicated ones (mutt,
emacs) that don't require scrolling regions (ESC[r) mostly work fine
(including mutt, emacs). No status bar yet and no key remapping or other
customisation.

65
Makefile Normal file
View File

@ -0,0 +1,65 @@
# $Id: Makefile,v 1.1.1.1 2007-07-09 19:03:33 nicm Exp $
.SUFFIXES: .c .o .y .l .h
.PHONY: clean
PROG= tmux
VERSION= 0.1
OS!= uname
REL!= uname -r
DATE!= date +%Y%m%d-%H%M
SRCS= tmux.c server.c buffer.c buffer-poll.c xmalloc.c input.c screen.c \
window.c session.c local.c log.c command.c
HDRS= tmux.h
LEX= lex
YACC= yacc -d
CC= cc
INCDIRS+= -I. -I- -I/usr/local/include
CFLAGS+= -DBUILD="\"$(VERSION) ($(DATE))\""
CFLAGS+= -g -ggdb -DDEBUG
#CFLAGS+= -pedantic -std=c99
#CFLAGS+= -Wredundant-decls -Wdisabled-optimization -Wendif-labels
CFLAGS+= -Wno-long-long -Wall -W -Wnested-externs -Wformat=2
CFLAGS+= -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations
CFLAGS+= -Wwrite-strings -Wshadow -Wpointer-arith -Wcast-qual -Wsign-compare
CFLAGS+= -Wundef -Wshadow -Wbad-function-cast -Winline -Wcast-align
PREFIX?= /usr/local
INSTALLBIN= install -g bin -o root -m 555
INSTALLMAN= install -g bin -o root -m 444
LDFLAGS+= -L/usr/local/lib
LIBS+= -lutil -lncurses
OBJS= ${SRCS:S/.c/.o/:S/.y/.o/:S/.l/.o/}
CLEANFILES= ${PROG} *.o .depend *~ ${PROG}.core *.log
.c.o:
${CC} ${CFLAGS} ${INCDIRS} -c ${.IMPSRC} -o ${.TARGET}
.l.o:
${LEX} ${.IMPSRC}
${CC} ${CFLAGS} ${INCDIRS} -c lex.yy.c -o ${.TARGET}
.y.o:
${YACC} ${.IMPSRC}
${CC} ${CFLAGS} ${INCDIRS} -c y.tab.c -o ${.TARGET}
all: .depend ${PROG}
${PROG}: ${OBJS}
${CC} ${LDFLAGS} -o ${PROG} ${LIBS} ${OBJS}
.depend: ${HDRS}
-mkdep ${CFLAGS} ${INCDIRS} ${SRCS:M*.c}
depend:
mkdep ${CFLAGS} ${INCDIRS} ${SRCS:M*.c}
clean:
rm -f ${CLEANFILES}

31
NOTES Normal file
View File

@ -0,0 +1,31 @@
Command prefix is C-b.
Commands: d detach
c create new terminal
n next terminal
p previous terminal
r refresh screen
t set window name
0-9 select window
There is one default server process per user which puts its socket in
/tmp/tmux-UID. It is created the first time tmux is run and subsequent
invocations will connect to the same server. The server holds multiple
sessions, call tmux with "-n <session name>" to create a session or attach to
an existing session. All the sessions may be listed with -l, or the windows of
a single session with "-ln <session name>". Sessions are destroyed when no
windows remain attached to them.
Another server process can be used by specifying an alternative socket path with
"-s <path>" but it shouldn't normally be required.
You can set the window title (listed in -l), using the \e] escape sequence. For
example:
$ echo -n \\033]0;My Title\\007
There is currently no method for setting the window name (what will eventually
be shown in the status bar).
You might get message "couldn't find server" after a crash, in this case you
must remove the /tmp/tmux-`id -u` file manually.

13
TODO Normal file
View File

@ -0,0 +1,13 @@
- key remapping
- decide if TIOCPKT is necessary and either handle it or remove the code
- it would be nice if there wasn't so much copying buffers about, audit uses
- window names
- status bar
- useful env vars like WINDOW
- lots of scripting love: add, remove, move around windows, status bar
- sort out who controls the buffers in local.c a bit
- better checking/emulation for missing term requirements
- alt charset, borders etc (terminfo(5)/Line Graphics)
- use default shell rather than fixed
- wrap windows with forward/back
- new window command prompt

779
ansicode.txt Normal file
View File

@ -0,0 +1,779 @@
Summary of ANSI standards for ASCII terminals Joe Smith, 18-May-84
Contents:
1. Overview and Definitions
2. General rules for interpreting an ESCape Sequence
3. General rules for interpreting a Control Sequence
4. C0 and C1 control codes in numeric order
5. Two and three-character ESCape Sequences in numeric order
6. Control Sequences in numeric order
7. VT100 emulation requirements
The VT100 USER GUIDE and ANSI standard X3.64-1979 both list the ANSI ESCape
sequences in alphabetic order by mnemonic, but do not have a have a cross
reference in order by ASCII code. This paper lists the combination of all
definitions from the three ANSI standards in numeric order. For a description
of the advantages of using these standards, see the article "Toward
Standardized Video Terminals" in the April-1984 issue of BYTE magazine.
ANSI X3.4-1977 defines the 7-bit ASCII character set (C0 and G0). It was
written in 1968, revised in 1977, and explains the decisions made in laying out
the ASCII code. In particular, it explains why ANSI chose to make ASCII
incompatible with EBCDIC in order to make it self-consistant.
ANSI X3.41-1974 introduces the idea of an 8-bit ASCII character set (C1 and G1
in addition to the existing C0 and G0). It describes how to use the 8-bit
features in a 7-bit environment. X3.41 defines the format of all ESCape
sequences, but defines only the 3-character ones with a parameter character
in the middle. These instruct the terminal how to interpret the C0, G0, C1,
and G1 characters (such as by selecting different character-set ROMs).
Note: NAPLPS does videotex graphics by redefining the C1 set and
selecting alternate G0, G1, G2, and G3 sets.
See the February 1983 issue of BYTE magazine for details.
ANSI X3.64-1979 defines the remaining ESCape sequences. It defines all the C1
control characters, and specifies that certain two-character ESCape sequences
in the 7-bit environment are to act exactly like the 8-bit C1 control set.
X3.64 introduces the idea of a Control-Sequence, which starts with CSI
character, has an indefinite length, and is terminated by an alphabetic
character. The VT100 was one of the first terminals to implement this
standard.
Definitions:
Control Character - A single character with an ASCII code with the range
of 000 to 037 and 200 to 237 octal, 00 to 1F and 80 to 9F hex.
Escape Sequence - A two or three character string staring with ESCape.
(Four or more character strings are allowed but not defined.)
Control Sequence - A string starting with CSI (233 octal, 9B hex) or
with ESCape Left-Bracket, and terminated by an alphabetic character.
Any number of parameter characters (digits 0 to 9, semicolon, and
question mark) may appear within the Control Sequence. The terminating
character may be preceded by an intermediate character (such as space).
Character classifications:
C0 Control 000-037 octal, 00-1F hex (G0 is 041-176 octal, 21-7E hex)
SPACE 040+240 octal, 20+A0 hex Always and everywhere a blank space
Intermediate 040-057 octal, 20-2F hex !"#$%&'()*+,-./
Parameters 060-077 octal, 30-3F hex 0123456789:;<=>?
Uppercase 100-137 octal, 40-5F hex @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
Lowercase 140-176 octal, 60-7E hex `abcdefghijlkmnopqrstuvwxyz{|}~
Alphabetic 100-176 octal, 40-7E hex (all of upper and lower case)
Delete 177 octal, 7F hex Always and everywhere ignored
C1 Control 200-237 octal, 80-9F hex 32 additional control characters
G1 Displayable 241-376 octal, A1-FE hex 94 additional displayable characters
Special 240+377 octal, A0+FF hex Same as SPACE and DELETE
Note that in this paper, the terms uppercase, lowercase, and alphabetics
include more characters than just A to Z.
------------------------------------------------------------------------------
General rules for interpreting an ESCape Sequence:
An ESCape Sequence starts with the ESC character (033 octal, 1B hex).
The length of the ESCape Sequence depends on the character that immediately
follows the ESCape.
If the next character is
C0 control: Interpret it first, then resume processing ESCape sequence.
Example: CR, LF, XON, and XOFF work as normal within an ESCape sequence.
Intermediate: Expect zero or more intermediates, a parameter terminates
a private function, an alphabetic terminates a standard sequence.
Example: ESC ( A defines standard character set, ESC ( 0 a DEC set.
Parameter: End of a private 2-character escape sequence.
Example: ESC = sets special keypad mode, ESC > clears it.
Uppercase: Translate it into a C1 control character and act on it.
Example: ESC D does indexes down, ESC M indexes up. (CSI is special)
Lowercase: End of a standard 2-character escape sequence.
Example: ESC c resets the terminal.
Delete: Ignore it, and continue interpreting the ESCape sequence
C1 and G1: Treat the same as their 7-bit counterparts
Note that CSI is the two-character sequence ESCape left-bracket or the 8-bit
C1 code of 233 octal, 9B hex. CSI introduces a Control Sequence, which
continues until an alphabetic character is received.
General rules for interpreting a Control Sequence:
1) It starts with CSI, the Control Sequence Introducer.
2) It contains any number of parameter characters (0123456789:;<=>?).
3) It terminates with an alphabetic character.
4) Intermediate characters (if any) immediately precede the terminator.
If the first character after CSI is one of "<=>?" (074-077 octal, 3C-3F hex),
then Control Sequence is to be interpreted according to private standards (such
as setting and resetting modes not defined by ANSI). The terminal should
expect any number of numeric parameters, separated by semicolons (073 octal,
3B hex). Only after the terminating alphabetic character is received should
the terminal act on the Control Sequence.
=============================================================================
C0 set of 7-bit control characters (from ANSI X3.4-1977).
Oct Hex Name * (* marks function used in DEC VT series or LA series terminals)
--- -- - --- - --------------------------------------------------------------
000 00 @ NUL * Null filler, terminal should ignore this character
001 01 A SOH Start of Header
002 02 B STX Start of Text, implied end of header
003 03 C ETX End of Text, causes some terminal to respond with ACK or NAK
004 04 D EOT End of Transmission
005 05 E ENQ * Enquiry, causes terminal to send ANSWER-BACK ID
006 06 F ACK Acknowledge, usually sent by terminal in response to ETX
007 07 G BEL * Bell, triggers the bell, buzzer, or beeper on the terminal
010 08 H BS * Backspace, can be used to define overstruck characters
011 09 I HT * Horizontal Tabulation, move to next predetermined position
012 0A J LF * Linefeed, move to same position on next line (see also NL)
013 0B K VT * Vertical Tabulation, move to next predetermined line
014 0C L FF * Form Feed, move to next form or page
015 0D M CR * Carriage Return, move to first character of current line
016 0E N SO * Shift Out, switch to G1 (other half of character set)
017 0F O SI * Shift In, switch to G0 (normal half of character set)
020 10 P DLE Data Link Escape, interpret next control character specially
021 11 Q XON * (DC1) Terminal is allowed to resume transmitting
022 12 R DC2 Device Control 2, causes ASR-33 to activate paper-tape reader
023 13 S XOFF* (DC2) Terminal must pause and refrain from transmitting
024 14 T DC4 Device Control 4, causes ASR-33 to deactivate paper-tape reader
025 15 U NAK Negative Acknowledge, used sometimes with ETX and ACK
026 16 V SYN Synchronous Idle, used to maintain timing in Sync communication
027 17 W ETB End of Transmission block
030 18 X CAN * Cancel (makes VT100 abort current escape sequence if any)
031 19 Y EM End of Medium
032 1A Z SUB * Substitute (VT100 uses this to display parity errors)
033 1B [ ESC * Prefix to an ESCape sequence
034 1C \ FS File Separator
035 1D ] GS Group Separator
036 1E ^ RS * Record Separator (sent by VT132 in block-transfer mode)
037 1F _ US Unit Separator
040 20 SP * Space (should never be defined to be otherwise)
177 7F DEL * Delete, should be ignored by terminal
==============================================================================
C1 set of 8-bit control characters (from ANSI X3.64-1979)
Oct Hex Name * (* marks function used in DEC VT series or LA series terminals)
--- -- - --- - --------------------------------------------------------------
200 80 @ Reserved for future standardization
201 81 A Reserved
202 82 B Reserved
203 83 C Reserved
204 84 D IND * Index, moves down one line same column regardless of NL
205 85 E NEL * NEw Line, moves done one line and to first column (CR+LF)
206 86 F SSA Start of Selected Area to be sent to auxiliary output device
207 87 G ESA End of Selected Area to be sent to auxiliary output device
210 88 H HTS * Horizontal Tabulation Set at current position
211 89 I HTJ Hor Tab Justify, moves string to next tab position
212 8A J VTS Vertical Tabulation Set at current line
213 8B K PLD Partial Line Down (subscript)
214 8C L PLU Partial Line Up (superscript)
215 8D M RI * Reverse Index, go up one line, reverse scroll if necessary
216 8E N SS2 * Single Shift to G2
217 8F O SS3 * Single Shift to G3 (VT100 uses this for sending PF keys)
220 90 P DCS * Device Control String, terminated by ST (VT125 enters graphics)
221 91 Q PU1 Private Use 1
222 92 R PU2 Private Use 2
223 93 S STS Set Transmit State
224 94 T CCH Cancel CHaracter, ignore previous character
225 95 U MW Message Waiting, turns on an indicator on the terminal
226 96 V SPA Start of Protected Area
227 97 W EPA End of Protected Area
230 98 X Reserved for for future standard
231 99 Y Reserved
232 9A Z * Reserved, but causes DEC terminals to respond with DA codes
233 9B [ CSI * Control Sequence Introducer (described in a seperate table)
234 9C \ ST * String Terminator (VT125 exits graphics)
235 9D ] OSC Operating System Command (reprograms intelligent terminal)
236 9E ^ PM Privacy Message (password verification), terminated by ST
237 9F _ APC Application Program Command (to word processor), term by ST
==============================================================================
Character set selection sequences (from ANSI X3.41-1974)
All are 3 characters long (including the ESCape). Alphabetic characters
as 3rd character are defined by ANSI, parameter characters as 3rd character
may be interpreted differently by each terminal manufacturer.
Oct Hex * (* marks function used in DEC VT series or LA series terminals)
--- -- -- - ------------------------------------------------------------------
040 20 ANNOUNCER - Determines whether to use 7-bit or 8-bit ASCII
A G0 only will be used. Ignore SI, SO, and G1.
B G0 and G1 used internally. SI and SO affect G0, G1 is ignored.
C G0 and G1 in an 8-bit only environment. SI and SO are ignored.
D G0 and G1 are used, SI and SO affect G0.
E
F * 7-bit transmission, VT240/PRO350 sends CSI as two characters ESC [
G * 8-bit transmission, VT240/PRO350 sends CSI as single 8-bit character
041 21 ! Select C0 control set (choice of 63 standard, 16 private)
042 22 " Select C1 control set (choice of 63 standard, 16 private)
043 23 # Translate next character to a special single character
#3 * DECDHL1 - Double height line, top half
#4 * DECDHL2 - Double height line, bottom half
#5 * DECSWL - Single width line
#6 * DECDWL - Double width line
#7 * DECHCP - Make a hardcopy of the graphics screen (GIGI,VT125,VT241)
#8 * DECALN - Alignment display, fill screen with "E" to adjust focus
044 24 $ MULTIBYTE CHARACTERS - Displayable characters require 2-bytes each
045 25 % SPECIAL INTERPRETATION - Such as 9-bit data
046 26 & Reserved for future standardization
047 27 ' Reserved for future standardization
050 28 ( * SCS - Select G0 character set (choice of 63 standard, 16 private)
(0 * DEC VT100 line drawing set (affects lowercase characters)
(1 * DEC Alternate character ROM set (RAM set on GIGI and VT220)
(2 * DEC Alternate character ROM set with line drawing
(5 * DEC Finnish on LA100
(6 * DEC Norwegian/Danish on LA100
(7 * DEC Swedish on LA100
(9 * DEC French Canadian
(< * DEC supplemental graphics (everything not in USASCII)
(A * UKASCII (British pound sign)
(B * USASCII (American pound sign)
(C * ISO Finnish on LA120
(E * ISO Norwegian/Danish on LA120
(H * ISO Swedish on LA120
(K * ISO German on LA100,LA120
(R * ISO French on LA100,LA120
(Y * ISO Italian on LA100
(Z * ISO Spanish on LA100
051 29 ) * SCS - Select G1 character set (choice of 63 standard, 16 private)
* (same character sets as listed under G0)
052 2A * * SCS - Select G2 character set
* (same character sets as listed under G0)
053 2B + * SCS - Select G3 character set
* (same character sets as listed under G0)
054 2C , SCS - Select G0 character set (additional 63+16 sets)
055 2D - SCS - Select G1 character set (additional 63+16 sets)
056 2E . SCS - Select G2 character set
057 2F / SCS - Select G3 character set
==============================================================================
Private two-character escape sequences (allowed by ANSI X3.41-1974)
These can be defined differently by each terminal manufacturer.
Oct Hex * (* marks function used in DEC VT series or LA series terminals)
--- -- - - ------------------------------------------------------------------
060 30 0
061 31 1 DECGON graphics on for VT105, DECHTS horiz tab set for LA34/LA120
062 32 2 DECGOFF graphics off VT105, DECCAHT clear all horz tabs LA34/LA120
063 33 3 DECVTS - set vertical tab for LA34/LA120
064 34 4 DECCAVT - clear all vertical tabs for LA34/LA120
065 35 5 * DECXMT - Host requests that VT132 transmit as if ENTER were pressed
066 36 6
067 37 7 * DECSC - Save cursor position and character attributes
070 38 8 * DECRC - Restore cursor and attributes to previously saved position
071 39 9
072 3A :
073 3B ;
074 3C < * DECANSI - Switch from VT52 mode to VT100 mode
075 3D = * DECKPAM - Set keypad to applications mode (ESCape instead of digits)
076 3E > * DECKPNM - Set keypad to numeric mode (digits intead of ESCape seq)
077 3F ?
DCS Device Control Strings used by DEC terminals (ends with ST)
Pp = Start ReGIS graphics (VT125, GIGI, VT240, PRO350)
Pq = Start SIXEL graphics (screen dump to LA34, LA100, screen load to VT125)
Pr = SET-UP data for GIGI, $PrVC0$\ disables both visible cursors.
Ps = Reprogram keys on the GIGI, $P0sDIR<CR>$\ makes keypad 0 send "DIR<CR>"
0-9=digits on keypad, 10=ENTER, 11=minus, 12=comma, 13=period,
14-17=PF1-PF4, 18-21=cursor keys. Enabled by $[?23h (PK1).
Pt = Start VT105 graphics on a VT125
==============================================================================
Standard two-character escape sequences (defined by ANSI X3.64-1979)
100 40 @ See description of C1 control characters
An ESCape followed by one of these uppercase characters is translated
to an 8-bit C1 control character before being interpreted.
220 90 P DCS - Device Control String, terminated by ST - see table above.
133 5B [ CSI - Control Sequence Introducer - see table below.
137 5F _ See description of C1 control characters
==============================================================================
Indepenent control functions (from Appendix E of X3.64-1977).
These four controls have the same meaning regardless of the current
definition of the C0 and C1 control sets. Each control is a two-character
ESCape sequence, the 2nd character is lowercase.
Oct Hex * (* marks function used in DEC VT series or LA series terminals)
--- -- - - --------------------------------------------------------------------
140 60 ` DMI - Disable Manual Input
141 61 a INT - INTerrupt the terminal and do special action
142 62 b EMI - Enable Manual Input
143 63 c * RIS - Reset to Initial State (VT100 does a power-on reset)
... The remaining lowercase characters are reserved by ANSI.
153 6B k NAPLPS lock-shift G1 to GR
154 6C l NAPLPS lock-shift G2 to GR
155 6D m NAPLPS lock-shift G3 to GR
156 6E n * LS2 - Shift G2 to GL (extension of SI) VT240,NAPLPS
157 6F o * LS3 - Shift G3 to GL (extension of SO) VT240,NAPLPS
... The remaining lowercase characters are reserved by ANSI.
174 7C | * LS3R - VT240 lock-shift G3 to GR
175 7D } * LS2R - VT240 lock-shift G2 to GR
176 7E ~ * LS1R - VT240 lock-shift G1 to GR
==============================================================================
Control Sequences (defined by ANSI X3.64-1979)
Control Sequences are started by either ESC [ or CSI and are terminated by an
"alphabetic" character (100 to 176 octal, 40 to 7E hex). Intermediate
characters are space through slash (40 to 57 octal, 20 to 2F hex) and parameter
characters are zero through question mark (60 to 77 octal, 30 to 3F hex,
including digits and semicolon). Parameters consist of zero or more decimal
numbers separated by semicolons. Leading zeros are optional, leading blanks
are not allowed. If no digits precede the final character, the default
parameter is used. Many functions treat a parameter of 0 as if it were 1.
Oct Hex * (* marks function used in DEC VT series or LA series terminals)
--- -- - - --------------------------------------------------------------------
100 40 @ ICH - Insert CHaracter
[10@ = Make room for 10 characters at current position
101 41 A * CUU - CUrsor Up
* [A = Move up one line, stop at top of screen, [9A = move up 9
102 42 B * CUD - CUrsor Down
* [B = Move down one line, stop at bottom of screen
103 43 C * CUF - CUrsor Forward
* [C = Move forward one position, stop at right edge of screen
104 44 D * CUB - CUrsor Backward
* [D = Same as BackSpace, stop at left edge of screen
105 45 E CNL - Cursor to Next Line
[5E = Move to first position of 5th line down
106 46 F CPL - Cursor to Previous Line
[5F = Move to first position of 5th line previous
107 47 G CHA - Cursor Horizontal position Absolute
[40G = Move to column 40 of current line
110 48 H * CUP - CUrsor Position
* [H = Home, [24;80H = Row 24, Column 80
111 49 I CHT - Cursor Horizontal Tabulation
[I = Same as HT (Control-I), [3I = Go forward 3 tabs
112 4A J * ED - Erase in Display (cursor does not move)
* [J = [0J = Erase from current position to end (inclusive)
* [1J = Erase from beginning to current position (inclusive)
* [2J = Erase entire display
* [?0J = Selective erase in display ([?1J, [?2J similar)
113 4B K * EL - Erase in Line (cursor does not move)
* [K = [0K = Erase from current position to end (inclusive)
* [1K = Erase from beginning to current position
* [2K = Erase entire current line
* [?0K = Selective erase to end of line ([?1K, [?2K similar)
114 4C L * IL - Insert Line, current line moves down (VT102 series)
[3L = Insert 3 lines if currently in scrolling region
115 4D M * DL - Delete Line, lines below current move up (VT102 series)
[2M = Delete 2 lines if currently in scrolling region
116 4E N EF - Erase in Field (as bounded by protected fields)
[0N, [1N, [2N act like [L but within currend field
117 4F O EA - Erase in qualified Area (defined by DAQ)
[0O, [1O, [2O act like [J but within current area
120 50 P * DCH - Delete Character, from current position to end of field
[4P = Delete 4 characters, VT102 series
121 51 Q SEM - Set Editing extent Mode (limits ICH and DCH)
[0Q = [Q = Insert/delete character affects rest of display
[1Q = ICH/DCH affect the current line only
[2Q = ICH/DCH affect current field (between tab stops) only
[3Q = ICH/DCH affect qualified area (between protected fields)
122 52 R * CPR - Cursor Position Report (from terminal to host)
* [24;80R = Cursor is positioned at line 24 column 80
123 53 S SU - Scroll up, entire display is moved up, new lines at bottom
[3S = Move everything up 3 lines, bring in 3 new lines
124 54 T SD - Scroll down, new lines inserted at top of screen
[4T = Scroll down 4, bring previous lines back into view
125 55 U NP - Next Page (if terminal has more than 1 page of memory)
[2U = Scroll forward 2 pages
126 56 V PP - Previous Page (if terminal remembers lines scrolled off top)
[1V = Scroll backward 1 page
127 57 W CTC - Cursor Tabulation Control
[0W = Set horizontal tab for current line at current position
[1W = Set vertical tab stop for current line of current page
[2W = Clear horiz tab stop at current position of current line
[3W = Clear vert tab stop at current line of current page
[4W = Clear all horiz tab stops on current line only
[5W = Clear all horiz tab stops for the entire terminal
[6W = Clear all vert tabs stops for the entire terminal
130 58 X ECH - Erase CHaracter
[4X = Change next 4 characters to "erased" state
131 59 Y CVT - Cursor Vertical Tab
[2Y = Move forward to 2nd following vertical tab stop
132 5A Z CBT - Cursor Back Tab
[3Z = Move backwards to 3rd previous horizontal tab stop
133 5B [ Reserved for future standardization left bracket
134 5C \ Reserved reverse slant
135 5D ] Reserved right bracket
136 5E ^ Reserved circumflex
137 5F _ Reserved underscore
140 60 ` * HPA - Horizontal Position Absolute (depends on PUM)
[720` = Move to 720 decipoints (1 inch) from left margin
* [80` = Move to column 80 on LA120
141 61 a * HPR - Horizontal Position Relative (depends on PUM)
[360a = Move 360 decipoints (1/2 inch) from current position
* [40a = Move 40 columns to right of current position on LA120
142 62 b REP - REPeat previous displayable character
[80b = Repeat character 80 times
143 63 c * DA - Device Attributes
* [c = Terminal will identify itself
* [?1;2c = Terminal is saying it is a VT100 with AVO
* [>0c = Secondary DA request (distinguishes VT240 from VT220)
144 64 d * VPA - Vertical Position Absolute (depends on PUM)
[90d = Move to 90 decipoints (1/8 inch) from top margin
* [10d = Move to line 10 if before that else line 10 next page
145 65 e * VPR - Vertical Position Relative (depends on PUM)
[720e = Move 720 decipoints (1 inch) down from current position
* [6e = Advance 6 lines forward on LA120
146 66 f * HVP - Horizontal and Vertical Position (depends on PUM)
[720,1440f = Move to 1 inch down and 2 inches over (decipoints)
* [24;80f = Move to row 24 column 80 if PUM is set to character
147 67 g * TBC - Tabulation Clear
* [0g = Clear horizontal tab stop at current position
* [1g = Clear vertical tab stop at current line (LA120)
* [2g = Clear all horizontal tab stops on current line only LA120
* [3g = Clear all horizontal tab stops in the terminal
150 68 h * SM - Set Mode (. means permanently set on VT100)
[0h = Error, this command is ignored
* [1h = GATM - Guarded Area Transmit Mode, send all (VT132)
[2h = KAM - Keyboard Action Mode, disable keyboard input
[3h = CRM - Control Representation Mode, show all control chars
* [4h = IRM - Insertion/Replacement Mode, set insert mode (VT102)
[5h = SRTM - Status Report Transfer Mode, report after DCS
* [6h = ERM - ERasure Mode, erase protected and unprotected
[7h = VEM - Vertical Editing Mode, IL/DL affect previous lines
[8h, [9h are reserved
[10h = HEM - Horizontal Editing mode, ICH/DCH/IRM go backwards
[11h = PUM - Positioning Unit Mode, use decipoints for HVP/etc
. [12h = SRM - Send Receive Mode, transmit without local echo
[13h = FEAM - Format Effector Action Mode, FE's are stored
[14h = FETM - Format Effector Transfer Mode, send only if stored
[15h = MATM - Multiple Area Transfer Mode, send all areas
* [16h = TTM - Transmit Termination Mode, send scrolling region
[17h = SATM - Send Area Transmit Mode, send entire buffer
[18h = TSM - Tabulation Stop Mode, lines are independent
[19h = EBM - Editing Boundry Mode, all of memory affected
* [20h = LNM - Linefeed Newline Mode, LF interpreted as CR LF
* [?1h = DECCKM - Cursor Keys Mode, send ESC O A for cursor up
* [?2h = DECANM - ANSI Mode, use ESC < to switch VT52 to ANSI
* [?3h = DECCOLM - COLumn mode, 132 characters per line
* [?4h = DECSCLM - SCrolL Mode, smooth scrolling
* [?5h = DECSCNM - SCreeN Mode, black on white background
* [?6h = DECOM - Origin Mode, line 1 is relative to scroll region
* [?7h = DECAWM - AutoWrap Mode, start newline after column 80
* [?8h = DECARM - Auto Repeat Mode, key will autorepeat
* [?9h = DECINLM - INterLace Mode, interlaced for taking photos
* [?10h = DECEDM - EDit Mode, VT132 is in EDIT mode
* [?11h = DECLTM - Line Transmit Mode, ignore TTM, send line
[?12h = ?
* [?13h = DECSCFDM - Space Compression/Field Delimiting on,
* [?14h = DECTEM - Transmit Execution Mode, transmit on ENTER
[?15h = ?
* [?16h = DECEKEM - Edit Key Execution Mode, EDIT key is local
[?17h = ?
* [?18h = DECPFF - Print FormFeed mode, send FF after printscreen
* [?19h = DECPEXT - Print Extent mode, print entire screen
* [?20h = OV1 - Overstrike, overlay characters on GIGI
* [?21h = BA1 - Local BASIC, GIGI to keyboard and screen
* [?22h = BA2 - Host BASIC, GIGI to host computer
* [?23h = PK1 - GIGI numeric keypad sends reprogrammable sequences
* [?24h = AH1 - Autohardcopy before erasing or rolling GIGI screen
* [?29h = - Use only the proper pitch for the LA100 font
* [?38h = DECTEK - TEKtronix mode graphics
151 69 i * MC - Media Copy (printer port on VT102)
* [0i = Send contents of text screen to printer
[1i = Fill screen from auxiliary input (printer's keyboard)
[2i = Send screen to secondary output device
[3i = Fill screen from secondary input device
* [4i = Turn on copying received data to primary output (VT125)
* [4i = Received data goes to VT102 screen, not to its printer
* [5i = Turn off copying received data to primary output (VT125)
* [5i = Received data goes to VT102's printer, not its screen
* [6i = Turn off copying received data to secondary output (VT125)
* [7i = Turn on copying received data to secondary output (VT125)
* [?0i = Graphics screen dump goes to graphics printer VT125,VT240
* [?1i = Print cursor line, terminated by CR LF
* [?2i = Graphics screen dump goes to host computer VT125,VT240
* [?4i = Disable auto print
* [?5i = Auto print, send a line at a time when linefeed received
152 6A j Reserved for future standardization
153 6B k Reserved for future standardization
154 6C l * RM - Reset Mode (. means permanently reset on VT100)
* [1l = GATM - Transmit only unprotected characters (VT132)
. [2l = KAM - Enable input from keyboard
. [3l = CRM - Control characters are not displayable characters
* [4l = IRM - Reset to replacement mode (VT102)
. [5l = SRTM - Report only on command (DSR)
* [6l = ERM - Erase only unprotected fields
. [7l = VEM - IL/DL affect lines after current line
[8l, [9l are reserved
. [10l = HEM - ICH and IRM shove characters forward, DCH pulls
. [11l = PUM - Use character positions for HPA/HPR/VPA/VPR/HVP
[12l = SRM - Local echo - input from keyboard sent to screen
. [13l = FEAM - HPA/VPA/SGR/etc are acted upon when received
. [14l = FETM - Format Effectors are sent to the printer
[15l = MATM - Send only current area if SATM is reset
* [16l = TTM - Transmit partial page, up to cursor position
[17l = SATM - Transmit areas bounded by SSA/ESA/DAQ
. [18l = TSM - Setting a tab stop on one line affects all lines
. [19l = EBM - Insert does not overflow to next page
* [20l = LNM - Linefeed does not change horizontal position
* [?1l = DECCKM - Cursor keys send ANSI cursor position commands
* [?2l = DECANM - Use VT52 emulation instead of ANSI mode
* [?3l = DECCOLM - 80 characters per line (erases screen)
* [?4l = DECSCLM - Jump scrolling
* [?5l = DECSCNM - Normal screen (white on black background)
* [?6l = DECOM - Line numbers are independent of scrolling region
* [?7l = DECAWM - Cursor remains at end of line after column 80
* [?8l = DECARM - Keys do not repeat when held down
* [?9l = DECINLM - Display is not interlaced to avoid flicker
* [?10l = DECEDM - VT132 transmits all key presses
* [?11l = DECLTM - Send page or partial page depending on TTM
[?12l = ?
* [?13l = DECSCFDM - Don't suppress trailing spaces on transmit
* [?14l = DECTEM - ENTER sends ESC S (STS) a request to send
[?15l = ?
* [?16l = DECEKEM - EDIT key transmits either $[10h or $[10l
[?17l = ?
* [?18l = DECPFF - Don't send a formfeed after printing screen
* [?19l = DECPEXT - Print only the lines within the scroll region
* [?20l = OV0 - Space is destructive, replace not overstrike, GIGI
* [?21l = BA0 - No BASIC, GIGI is On-Line or Local
* [?22l = BA0 - No BASIC, GIGI is On-Line or Local
* [?23l = PK0 - Ignore reprogramming on GIGI keypad and cursors
* [?24l = AH0 - No auto-hardcopy when GIGI screen erased
* [?29l = Allow all character pitches on the LA100
* [?38l = DECTEK - Ignore TEKtronix graphics commands
155 6D m * SGR - Set Graphics Rendition (affects character attributes)
* [0m = Clear all special attributes
* [1m = Bold or increased intensity
* [2m = Dim or secondary color on GIGI (superscript on XXXXXX)
[3m = Italic (subscript on XXXXXX)
* [4m = Underscore, [0;4m = Clear, then set underline only
* [5m = Slow blink
[6m = Fast blink (overscore on XXXXXX)
* [7m = Negative image, [0;1;7m = Bold + Inverse
[8m = Concealed (do not display character echoed locally)
[9m = Reserved for future standardization
* [10m = Select primary font (LA100)
* [11m - [19m = Selete alternate font (LA100 has 11 thru 14)
[20m = FRAKTUR (whatever that means)
* [22m = Cancel bold or dim attribute only (VT220)
* [24m = Cancel underline attribute only (VT220)
* [25m = Cancel fast or slow blink attribute only (VT220)
* [27m = Cancel negative image attribute only (VT220)
* [30m = Write with black, [40m = Set background to black (GIGI)
* [31m = Write with red, [41m = Set background to red
* [32m = Write with green, [42m = Set background to green
* [33m = Write with yellow, [43m = Set background to yellow
* [34m = Write with blue, [44m = Set background to blue
* [35m = Write with magenta, [45m = Set background to magenta
* [36m = Write with cyan, [46m = Set background to cyan
* [37m = Write with white, [47m = Set background to white
[38m, [39m, [48m, [49m are reserved
156 6E n * DSR - Device Status Report
* [0n = Terminal is ready, no malfunctions detected
[1n = Terminal is busy, retry later
[2n = Terminal is busy, it will send DSR when ready
* [3n = Malfunction, please try again
[4n = Malfunction, terminal will send DSR when ready
* [5n = Command to terminal to report its status
* [6n = Command to terminal requesting cursor position (CPR)
* [?15n = Command to terminal requesting printer status, returns
[?10n = OK, [?11n = not OK, [?13n = no printer.
* [?25n = "Are User Defined Keys Locked?" (VT220)
157 6F o DAQ - Define Area Qualification starting at current position
[0o = Accept all input, transmit on request
[1o = Protected and guarded, accept no input, do not transmit
[2o = Accept any printing character in this field
[3o = Numeric only field
[4o = Alphabetic (A-Z and a-z) only
[5o = Right justify in area
[3;6o = Zero fill in area
[7o = Set horizontal tab stop, this is the start of the field
[8o = Protected and unguarded, accept no input, do transmit
[9o = Space fill in area
==============================================================================
Private Control Sequences (allowed by ANSI X3.41-1974).
These take parameter strings and terminate with the last half of lowercase.
Oct Hex * (* marks function used in DEC VT series or LA series terminals)
--- -- - - --------------------------------------------------------------------
160 70 p * DECSTR - Soft Terminal Reset
[!p = Soft Terminal Reset
161 71 q * DECLL - Load LEDs
[0q = Turn off all, [?1;4q turns on L1 and L4, etc
[154;155;157q = VT100 goes bonkers
[2;23!q = Partial screen dump from GIGI to graphics printer
[0"q = DECSCA Select Character Attributes off
[1"q = DECSCA - designate set as non-erasable
[2"q = DECSCA - designate set as erasable
162 72 r * DECSTBM - Set top and bottom margins (scroll region on VT100)
[4;20r = Set top margin at line 4 and bottom at line 20
163 73 s * DECSTRM - Set left and right margins on LA100,LA120
[5;130s = Set left margin at column 5 and right at column 130
164 74 t * DECSLPP - Set physical lines per page
[66t = Paper has 66 lines (11 inches at 6 per inch)
165 75 u * DECSHTS - Set many horizontal tab stops at once on LA100
[9;17;25;33;41;49;57;65;73;81u = Set standard tab stops
166 76 v * DECSVTS - Set many vertical tab stops at once on LA100
[1;16;31;45v = Set vert tabs every 15 lines
167 77 w * DECSHORP - Set horizontal pitch on LAxxx printers
[1w = 10 characters per inch, [2w = 12 characters per inch
[0w=10, [3w=13.2, [4w=16.5, [5w=5, [6w=6, [7w=6.6, [8w=8.25
170 78 x * DECREQTPARM - Request terminal parameters
[3;5;2;64;64;1;0x = Report, 7 bit Even, 1200 baud, 1200 baud
171 79 y * DECTST - Invoke confidence test
[2;1y = Power-up test on VT100 series (and VT100 part of VT125)
[3;1y = Power-up test on GIGI (VK100)
[4;1y = Power-up test on graphics portion of VT125
172 7A z * DECVERP - Set vertical pitch on LA100
[1z = 6 lines per inch, [2z = 8 lines per inch
[0z=6, [3z=12, [4z=3, [5z=3, [6z=4
173 7B { Private
174 7C | * DECTTC - Transmit Termination Character
[0| = No extra characters, [1| = terminate with FF
175 7D } * DECPRO - Define protected field on VT132
[0} = No protection, [1;4;5;7} = Any attribute is protected
[254} = Characters with no attributes are protected
176 7E ~ * DECKEYS - Sent by special function keys
[1~=FIND, [2~=INSERT, [3~=REMOVE, [4~=SELECT, [5~=PREV, [6~=NEXT
[17~=F6...[34~=F20 ([23~=ESC,[24~=BS,[25~=LF,[28~=HELP,[29~=DO)
177 7F DELETE is always ignored
==============================================================================
Control Sequences with intermediate characters (from ANSI X3.64-1979).
Note that there is a SPACE character before the terminating alphabetic.
Oct Hex * (* marks function used in DEC VT series or LA series terminals)
--- -- - - --------------------------------------------------------------------
100 40 @ SL - Scroll Left
[4 @ = Move everything over 4 columns, 4 new columns at right
101 41 A SR - Scroll Right
[2 A = Move everything over 2 columns, 2 new columns at left
102 42 B GSM - Graphic Size Modification
[110;50 B = Make 110% high, 50% wide
103 43 C GSS - Graphic Size Selection
[120 C = Make characters 120 decipoints (1/6 inch) high
104 44 D FNT - FoNT selection (used by SGR, [10m thru [19m)
[0;23 D = Make primary font be registered font #23
105 45 E TSS - Thin Space Specification
[36 E = Define a thin space to be 36 decipoints (1/20 inch)
106 46 F JFY - JustiFY, done by the terminal/printer
[0 E = No justification
[1 E = Fill, bringing words up from next line if necessary
[2 E = Interword spacing, adjust spaces between words
[3 E = Letter spacing, adjust width of each letter
[4 E = Use hyphenation
[5 E = Flush left margin
[6 E = Center following text between margins (until [0 E)
[7 E = Flush right margin
[8 E = Italian form (underscore instead of hyphen)
107 47 G SPI - SPacing Increment (in decipoints)
[120;72 G = 6 per inch vertical, 10 per inch horizontal
110 48 H QUAD- Do quadding on current line of text (typography)
[0 H = Flush left, [1 H = Flush left and fill with leader
[2 H = Center, [3 H = Center and fill with leader
[4 H = Flush right, [5 H = Flush right and fill with leader
111 49 I Reserved for future standardization
157 67 o Reserved for future standardization
160 70 p Private use
... May be defined by the printer manufacturer
176 7E ~ Private use
177 7F DELETE is always ignored
==============================================================================
Minimum requirements for VT100 emulation:
1) To act as a passive display, implement the 4 cursor commands, the 2 erase
commands, direct cursor addressing, and at least inverse characters.
The software should be capable of handling strings with 16 numeric parameters
with values in the range of 0 to 255.
[A Move cursor up one row, stop if a top of screen
[B Move cursor down one row, stop if at bottom of screen
[C Move cursor forward one column, stop if at right edge of screen
[D Move cursor backward one column, stop if at left edge of screen
[H Home to row 1 column 1 (also [1;1H)
[J Clear from current position to bottom of screen
[K Clear from current position to end of line
[24;80H Position to line 24 column 80 (any line 1 to 24, any column 1 to 132)
[0m Clear attributes to normal characters
[7m Add the inverse video attribute to succeeding characters
[0;7m Set character attributes to inverse video only
2) To enter data in VT100 mode, implement the 4 cursor keys and the 4 PF keys.
It must be possible to enter ESC, TAB, BS, DEL, and LF from the keyboard.
[A Sent by the up-cursor key (alternately ESC O A)
[B Sent by the down-cursor key (alternately ESC O B)
[C Sent by the right-cursor key (alternately ESC O C)
[D Sent by the left-cursor key (alternately ESC O D)
OP PF1 key sends ESC O P
OQ PF2 key sends ESC O Q
OR PF3 key sends ESC O R
OS PF3 key sends ESC O S
[c Request for the terminal to identify itself
[?1;0c VT100 with memory for 24 by 80, inverse video character attribute
[?1;2c VT100 capable of 132 column mode, with bold+blink+underline+inverse
3) When doing full-screen editing on a VT100, implement directed erase, the
numeric keypad in applications mode, and the limited scrolling region.
The latter is needed to do insert/delete line functions without rewriting
the screen.
[0J Erase from current position to bottom of screen inclusive
[1J Erase from top of screen to current position inclusive
[2J Erase entire screen (without moving the cursor)
[0K Erase from current position to end of line inclusive
[1K Erase from beginning of line to current position inclusive
[2K Erase entire line (without moving cursor)
[12;24r Set scrolling region to lines 12 thru 24. If a linefeed or an
INDex is received while on line 24, the former line 12 is deleted
and rows 13-24 move up. If a RI (reverse Index) is received while
on line 12, a blank line is inserted there as rows 12-13 move down.
All VT100 compatible terminals (except GIGI) have this feature.
ESC = Set numeric keypad to applications mode
ESC > Set numeric keypad to numbers mode
OA Up-cursor key sends ESC O A after ESC = ESC [ ? 1 h
OB Down-cursor key sends ESC O B " " "
OC Right-cursor key sends ESC O B " " "
OB Left-cursor key sends ESC O B " " "
OM ENTER key sends ESC O M after ESC =
Ol COMMA on keypad sends ESC O l " " (that's lowercase L)
Om MINUS on keypad sends ESC O m " "
Op ZERO on keypad sends ESC O p " "
Oq ONE on keypad sends ESC O q " "
Or TWO on keypad sends ESC O r " "
Os THREE on keypad sends ESC O s " "
Ot FOUR on keypad sends ESC O t " "
Ou FIVE on keypad sends ESC O u " "
Ov SIX on keypad sends ESC O v " "
Ow SEVEN on keypad sends ESC O w " "
Ox EIGHT on keypad sends ESC O x " "
Oy NINE on keypad sends ESC O y " "
4) If the hardware is capable of double width/double height:
#3 Top half of a double-width double-height line
#4 Bottom half of a double-width double-height line
#5 Make line single-width (lines are set this way when cleared by ESC [ J)
#6 Make line double-width normal height (40 or 66 characters)
5) If the terminal emulator is capable of insert/delete characters,
insert/delete lines, insert/replace mode, and can do a full-screen dump to
the printer (in text mode), then it should identify itself as a VT102
[c Request for the terminal to identify itself
[?6c VT102 (printer port, 132 column mode, and ins/del standard)
[1@ Insert a blank character position (shift line to the right)
[1P Delete a character position (shift line to the left)
[1L Insert blank line at current row (shift screen down)
[1M Delete the current line (shift screen up)
[4h Set insert mode, new characters shove existing ones to the right
[4l Reset insert mode, new characters replace existing ones
[0i Print screen (all 24 lines) to the printer
[4i All received data goes to the printer (nothing to the screen)
[5i All received data goes to the screen (nothing to the printer)
[End of ANSICODE.TXT]

91
array.h Normal file
View File

@ -0,0 +1,91 @@
/* $Id: array.h,v 1.1.1.1 2007-07-09 19:03:30 nicm Exp $ */
/*
* Copyright (c) 2006 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef ARRAY_H
#define ARRAY_H
#define ARRAY_DECL(n, c) \
struct n { \
c *list; \
u_int num; \
size_t space; \
}
#define ARRAY_ITEM(a, i) ((a)->list[i])
#define ARRAY_ITEMSIZE(a) (sizeof *(a)->list)
#define ARRAY_EMPTY(a) ((a) == NULL || (a)->num == 0)
#define ARRAY_LENGTH(a) ((a)->num)
#define ARRAY_FIRST(a) ARRAY_ITEM(a, 0)
#define ARRAY_LAST(a) ARRAY_ITEM(a, (a)->num - 1)
#define ARRAY_INIT(a) do { \
(a)->num = 0; \
(a)->list = NULL; \
(a)->space = 0; \
} while (0)
#define ARRAY_SET(a, i, s) do { \
(a)->list[i] = s; \
} while (0)
#define ARRAY_ADD(a, s) do { \
ENSURE_SIZE2((a)->list, (a)->space, (a)->num + 1, ARRAY_ITEMSIZE(a)); \
(a)->list[(a)->num] = s; \
(a)->num++; \
} while (0)
#define ARRAY_REMOVE(a, i) do { \
if (i < (a)->num - 1) { \
memmove((a)->list + (i), (a)->list + (i) + 1, \
ARRAY_ITEMSIZE(a) * ((a)->num - (i) - 1)); \
} \
(a)->num--; \
if ((a)->num == 0) \
ARRAY_FREE(a); \
} while (0)
#define ARRAY_EXPAND(a, n) do { \
ENSURE_SIZE2((a)->list, (a)->space, (a)->num + n, ARRAY_ITEMSIZE(a)); \
(a)->num += n; \
} while (0)
#define ARRAY_TRUNC(a, n) do { \
if ((a)->num > n) \
(a)->num -= n; \
else \
ARRAY_FREE(a); \
} while (0)
#define ARRAY_CONCAT(a, b) do { \
ENSURE_SIZE2((a)->list, (a)->space, (a)->num + (b)->num, \
ARRAY_ITEMSIZE(a)); \
memcpy((a)->list + (a)->num, (b)->list, (b)->num * ARRAY_ITEMSIZE(a)) \
(a)->num += (b)->num; \
} while (0)
#define ARRAY_FREE(a) do { \
if ((a)->list != NULL) \
xfree((a)->list); \
ARRAY_INIT(a); \
} while (0)
#define ARRAY_FREEALL(a) do { \
ARRAY_FREE(a); \
xfree(a); \
} while (0)
#endif

57
buffer-poll.c Normal file
View File

@ -0,0 +1,57 @@
/* $Id: buffer-poll.c,v 1.1.1.1 2007-07-09 19:04:12 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <errno.h>
#include <poll.h>
#include <unistd.h>
#include "tmux.h"
/* Fill buffers from socket based on poll results. */
int
buffer_poll(struct pollfd *pfd, struct buffer *in, struct buffer *out)
{
ssize_t n;
if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP))
return (-1);
if (pfd->revents & POLLIN) {
buffer_ensure(in, BUFSIZ);
n = read(pfd->fd, BUFFER_IN(in), BUFFER_FREE(in));
if (n == 0)
return (-1);
if (n == -1) {
if (errno != EINTR && errno != EAGAIN)
return (-1);
return (0);
}
buffer_add(in, n);
}
if (BUFFER_USED(out) > 0 && pfd->revents & POLLOUT) {
n = write(pfd->fd, BUFFER_OUT(out), BUFFER_USED(out));
if (n == -1) {
if (errno != EINTR && errno != EAGAIN)
return (-1);
return (0);
}
buffer_remove(out, n);
}
return (0);
}

181
buffer.c Normal file
View File

@ -0,0 +1,181 @@
/* $Id: buffer.c,v 1.1.1.1 2007-07-09 19:03:33 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <string.h>
#include "tmux.h"
/* Create a buffer. */
struct buffer *
buffer_create(size_t size)
{
struct buffer *b;
if (size == 0)
log_fatalx("buffer_create: zero size");
b = xcalloc(1, sizeof *b);
b->base = xmalloc(size);
b->space = size;
return (b);
}
/* Destroy a buffer. */
void
buffer_destroy(struct buffer *b)
{
xfree(b->base);
xfree(b);
}
/* Empty a buffer. */
void
buffer_clear(struct buffer *b)
{
b->size = 0;
b->off = 0;
}
/* Ensure free space for size in buffer. */
void
buffer_ensure(struct buffer *b, size_t size)
{
if (size == 0)
log_fatalx("buffer_ensure: zero size");
if (BUFFER_FREE(b) >= size)
return;
if (b->off > 0) {
if (b->size > 0)
memmove(b->base, b->base + b->off, b->size);
b->off = 0;
}
ENSURE_FOR(b->base, b->space, b->size, size);
}
/* Adjust buffer after data appended. */
void
buffer_add(struct buffer *b, size_t size)
{
if (size == 0)
log_fatalx("buffer_add: zero size");
if (size > b->space - b->size)
log_fatalx("buffer_add: overflow");
b->size += size;
}
/* Reverse buffer add. */
void
buffer_reverse_add(struct buffer *b, size_t size)
{
if (size == 0)
log_fatalx("buffer_reverse_add: zero size");
if (size > b->size)
log_fatalx("buffer_reverse_add: underflow");
b->size -= size;
}
/* Adjust buffer after data removed. */
void
buffer_remove(struct buffer *b, size_t size)
{
if (size == 0)
log_fatalx("buffer_remove: zero size");
if (size > b->size)
log_fatalx("buffer_remove: underflow");
b->size -= size;
b->off += size;
}
/* Reverse buffer remove. */
void
buffer_reverse_remove(struct buffer *b, size_t size)
{
if (size == 0)
log_fatalx("buffer_reverse_remove: zero size");
if (size > b->off)
log_fatalx("buffer_reverse_remove: overflow");
b->size += size;
b->off -= size;
}
/* Insert a section into the buffer. */
void
buffer_insert_range(struct buffer *b, size_t base, size_t size)
{
if (size == 0)
log_fatalx("buffer_insert_range: zero size");
if (base > b->size)
log_fatalx("buffer_insert_range: range overflows buffer");
buffer_ensure(b, size);
memmove(b->base + b->off + base + size,
b->base + b->off + base, b->size - base);
b->size += size;
}
/* Delete a section from the buffer. */
void
buffer_delete_range(struct buffer *b, size_t base, size_t size)
{
if (size == 0)
log_fatalx("buffer_delete_range: zero size");
if (size > b->size)
log_fatalx("buffer_delete_range: size too big");
if (base + size > b->size)
log_fatalx("buffer_delete_range: range overflows buffer");
memmove(b->base + b->off + base,
b->base + b->off + base + size, b->size - base - size);
b->size -= size;
}
/* Copy data into a buffer. */
void
buffer_write(struct buffer *b, const void *data, size_t size)
{
if (size == 0)
log_fatalx("buffer_write: zero size");
buffer_ensure(b, size);
memcpy(BUFFER_IN(b), data, size);
buffer_add(b, size);
}
/* Copy data out of a buffer. */
void
buffer_read(struct buffer *b, void *data, size_t size)
{
if (size == 0)
log_fatalx("buffer_read: zero size");
if (size > b->size)
log_fatalx("buffer_read: underflow");
memcpy(data, BUFFER_OUT(b), size);
buffer_remove(b, size);
}

165
command.c Normal file
View File

@ -0,0 +1,165 @@
/* $Id: command.c,v 1.1.1.1 2007-07-09 19:03:50 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include "tmux.h"
int cmd_prefix = '\002'; /* C-b */
int cmd_fn_select(struct buffer *, int);
int cmd_fn_create(struct buffer *, int);
int cmd_fn_detach(struct buffer *, int);
int cmd_fn_next(struct buffer *, int);
int cmd_fn_previous(struct buffer *, int);
int cmd_fn_refresh(struct buffer *, int);
int cmd_fn_rename(struct buffer *, int);
struct cmd {
int key;
int (*fn)(struct buffer *, int);
int arg;
};
struct cmd cmd_table[] = {
{ '0', cmd_fn_select, 0 },
{ '1', cmd_fn_select, 1 },
{ '2', cmd_fn_select, 2 },
{ '3', cmd_fn_select, 3 },
{ '4', cmd_fn_select, 4 },
{ '5', cmd_fn_select, 5 },
{ '6', cmd_fn_select, 6 },
{ '7', cmd_fn_select, 7 },
{ '8', cmd_fn_select, 8 },
{ '9', cmd_fn_select, 9 },
{ 'C', cmd_fn_create, 0 },
{ 'c', cmd_fn_create, 0 },
{ 'D', cmd_fn_detach, 0 },
{ 'd', cmd_fn_detach, 0 },
{ 'N', cmd_fn_next, 0 },
{ 'n', cmd_fn_next, 0 },
{ 'P', cmd_fn_previous, 0 },
{ 'p', cmd_fn_previous, 0 },
{ 'R', cmd_fn_refresh, 0 },
{ 'r', cmd_fn_refresh, 0 },
{ 'T', cmd_fn_rename, 0 },
{ 't', cmd_fn_rename, 0 }
};
/* Dispatch to a command. */
int
cmd_execute(int key, struct buffer *srv_out)
{
struct cmd *cmd;
u_int i;
for (i = 0; i < (sizeof cmd_table / sizeof cmd_table[0]); i++) {
cmd = cmd_table + i;
if (cmd->key == key)
return (cmd->fn(srv_out, cmd->arg));
}
return (0);
}
/* Handle select command. */
int
cmd_fn_select(unused struct buffer *srv_out, int arg)
{
struct hdr hdr;
struct select_data data;
hdr.code = MSG_SELECT;
hdr.size = sizeof data;
buffer_write(srv_out, &hdr, sizeof hdr);
data.idx = arg;
buffer_write(srv_out, &data, sizeof data);
return (0);
}
/* Handle create command. */
int
cmd_fn_create(unused struct buffer *srv_out, unused int arg)
{
struct hdr hdr;
hdr.code = MSG_CREATE;
hdr.size = 0;
buffer_write(srv_out, &hdr, sizeof hdr);
return (0);
}
/* Handle detach command. */
int
cmd_fn_detach(unused struct buffer *srv_out, unused int arg)
{
return (-1);
}
/* Handle next command. */
int
cmd_fn_next(unused struct buffer *srv_out, unused int arg)
{
struct hdr hdr;
hdr.code = MSG_NEXT;
hdr.size = 0;
buffer_write(srv_out, &hdr, sizeof hdr);
return (0);
}
/* Handle previous command. */
int
cmd_fn_previous(unused struct buffer *srv_out, unused int arg)
{
struct hdr hdr;
hdr.code = MSG_PREVIOUS;
hdr.size = 0;
buffer_write(srv_out, &hdr, sizeof hdr);
return (0);
}
/* Handle refresh command. */
int
cmd_fn_refresh(unused struct buffer *srv_out, unused int arg)
{
struct hdr hdr;
hdr.code = MSG_REFRESH;
hdr.size = 0;
buffer_write(srv_out, &hdr, sizeof hdr);
return (0);
}
/* Handle rename command. */
int
cmd_fn_rename(struct buffer *srv_out, unused int arg)
{
struct hdr hdr;
hdr.code = MSG_RENAME;
hdr.size = 0;
buffer_write(srv_out, &hdr, sizeof hdr);
return (0);
}

825
input.c Normal file
View File

@ -0,0 +1,825 @@
/* $Id: input.c,v 1.1.1.1 2007-07-09 19:03:45 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
size_t input_sequence(
u_char *, size_t, u_char *, u_char *, uint16_t **, u_int *);
int input_control(
u_char **, size_t *, struct buffer *, struct screen *, u_char);
int input_pair_private(
u_char **, size_t *, struct buffer *, struct screen *, u_char);
int input_pair_standard(
u_char **, size_t *, struct buffer *, struct screen *, u_char);
int input_pair_control(
u_char **, size_t *, struct buffer *, struct screen *, u_char);
int input_control_sequence(
u_char **, size_t *, struct buffer *, struct screen *);
int input_check_one(uint16_t *, u_int, uint16_t *, uint16_t);
int input_check_one2(
uint16_t *, u_int, uint16_t *, uint16_t, uint16_t, uint16_t);
int input_check_two(
uint16_t *, u_int, uint16_t *, uint16_t, uint16_t *, uint16_t);
struct input_key {
int key;
const char *string;
};
struct input_key input_keys[] = {
{ KEYC_BACKSPACE, "" },
{ KEYC_DC, "[3~" },
{ KEYC_DOWN, "OB" },
{ KEYC_F1, "OP" },
{ KEYC_F10, "[21~" },
{ KEYC_F11, "[23~" },
{ KEYC_F12, "[24~" },
{ KEYC_F2, "OQ" },
{ KEYC_F3, "OR" },
{ KEYC_F4, "OS" },
{ KEYC_F5, "[15~" },
{ KEYC_F6, "[17~" },
{ KEYC_F7, "[18~" },
{ KEYC_F8, "[19~" },
{ KEYC_F9, "[20~" },
{ KEYC_HOME, "[1~" },
{ KEYC_IC, "[2~" },
{ KEYC_LEFT, "OD" },
{ KEYC_LL, "[4~" },
{ KEYC_NPAGE, "[6~" },
{ KEYC_PPAGE, "[5~" },
{ KEYC_RIGHT, "OC" },
{ KEYC_UP, "OA" }
};
/*
* This parses CSI escape sequences into a code and a block of uint16_t
* arguments. buf must be the next byte after the \e[ and len the remaining
* data.
*/
size_t
input_sequence(u_char *buf, size_t len,
u_char *code, u_char *private, uint16_t **argv, u_int *argc)
{
char ch;
u_char *ptr, *saved;
const char *errstr;
*code = 0;
*argc = 0;
*argv = NULL;
if (len == 0)
return (0);
saved = buf;
/*
* 0x3c (<) to 0x3f (?) mark private sequences when appear as the first
* character.
*/
*private = '\0';
if (*buf >= '<' && *buf <= '?') {
*private = *buf;
buf++; len--;
} else if (*buf < '0' || *buf > ';')
goto complete;
while (len > 0) {
/*
* Every parameter substring is bytes from 0x30 (0) to 0x3a (:),
* terminated by 0x3b (;). 0x3a is an internal seperator.
*/
/* Find the end of the substring. */
ptr = buf;
while (len != 0 && *ptr >= '0' && *ptr <= '9') {
ptr++;
len--;
}
if (len == 0)
break;
/* An 0x3a is unsupported. */
if (*ptr == ':')
goto invalid;
/* Create a new argument. */
(*argc)++;
*argv = xrealloc(*argv, *argc, sizeof **argv);
/* Fill in argument value. */
errstr = NULL;
if (ptr == buf)
(*argv)[*argc - 1] = UINT16_MAX;
else {
ch = *ptr; *ptr = '\0';
(*argv)[*argc - 1] =
strtonum(buf, 0, UINT16_MAX - 1, &errstr);
*ptr = ch;
}
buf = ptr;
/* If the conversion had errors, abort now. */
if (errstr != NULL)
goto invalid;
/* Break for any non-terminator. */
if (*buf != ';')
goto complete;
buf++; len--;
}
if (len == 0)
goto incomplete;
complete:
/* Valid final characters are 0x40 (@) to 0x7e (~). */
if (*buf < '@' || *buf > '~')
goto invalid;
*code = *buf;
return (buf - saved + 1);
invalid:
if (*argv != NULL) {
xfree(*argv);
*argv = NULL;
}
*argc = 0;
/* Invalid. Consume until a valid terminator. */
while (len > 0) {
if (*buf >= '@' && *buf <= '~')
break;
buf++; len--;
}
if (len == 0)
goto incomplete;
*code = '\0';
return (buf - saved + 1);
incomplete:
if (*argv != NULL) {
xfree(*argv);
*argv = NULL;
}
*argc = 0;
*code = '\0';
return (0);
}
/* Translate a key code into an output key sequence. */
void
input_key(struct buffer *b, int key)
{
struct input_key *ak;
u_int i;
log_debug("writing key %d", key);
if (key != KEYC_NONE && key >= 0) {
input_store8(b, key);
return;
}
for (i = 0; i < (sizeof input_keys / sizeof input_keys[0]); i++) {
ak = input_keys + i;
if (ak->key == key) {
log_debug("found key %d: \"%s\"", key, ak->string);
buffer_write(b, ak->string, strlen(ak->string));
return;
}
}
}
/*
* Parse a block of data and normalise escape sequences into a \e, a single
* character code and the correct number of arguments. This includes adding
* missing arguments and default values, and enforcing limits. Returns the
* number of bytes consumed. The screen is updated with the data and used
* to fill in current cursor positions and sizes.
*/
size_t
input_parse(u_char *buf, size_t len, struct buffer *b, struct screen *s)
{
u_char *saved, ch;
size_t size;
FILE *f;
saved = buf;
if (debug_level > 1) {
f = fopen("tmux-in.log", "a+");
fwrite(buf, len, 1, f);
fclose(f);
}
while (len > 0) {
ch = *buf++; len--;
/* Handle control characters. */
if (ch != '\e') {
if (ch < ' ') {
if (input_control(&buf, &len, b, s, ch) == 1) {
*--buf = ch;
break;
}
} else {
log_debug("character: %c (%hhu)", ch, ch);
screen_character(s, ch);
input_store8(b, ch);
}
continue;
}
if (len == 0) {
*--buf = '\e';
break;
}
/* Read the first character. */
ch = *buf++; len--;
/* Ignore delete. */
if (ch == '\177') {
if (len == 0) {
*--buf = '\e';
break;
}
ch = *buf++; len--;
}
/* Interpret C0 immediately. */
if (ch < ' ') {
if (input_control(&buf, &len, b, s, ch) == 1) {
*--buf = ch;
break;
}
if (len == 0) {
*--buf = '\e';
break;
}
ch = *buf++; len--;
}
/*
* Save used size to work out how much to pass to
* screen_sequence later.
*/
size = BUFFER_USED(b);
/* Skip until the end of intermediate strings. */
if (ch >= ' ' && ch <= '/') {
while (len != 0) {
if (ch >= 0x30 && ch <= 0x3f)
break;
if (ch >= 0x40 && ch <= 0x5f)
break;
ch = *buf++; len--;
}
continue;
}
/* Handle two-character sequences. */
if (ch >= '0' && ch <= '?') {
if (input_pair_private(&buf, &len, b, s, ch) == 1)
goto incomplete;
goto next;
}
if (ch >= '`' && ch <= '~') {
if (input_pair_standard(&buf, &len, b, s, ch) == 1)
goto incomplete;
goto next;
}
if (ch >= '@' && ch <= '_' && ch != '[') {
if (input_pair_control(&buf, &len, b, s, ch) == 1)
goto incomplete;
goto next;
}
/* If not CSI at this point, invalid. */
if (ch != '[')
continue;
if (input_control_sequence(&buf, &len, b, s) == 1)
goto incomplete;
next:
size = BUFFER_USED(b) - size;
log_debug("output is %zu bytes", size);
if (size > 0) /* XXX only one command? */
screen_sequence(s, BUFFER_IN(b) - size);
log_debug("remaining data %zu bytes", len);
}
return (buf - saved);
incomplete:
*--buf = ch;
*--buf = '\e';
return (buf - saved);
}
/* Handle single control characters. */
int
input_control(unused u_char **buf, unused size_t *len,
struct buffer *b, struct screen *s, u_char ch)
{
switch (ch) {
case '\0': /* NUL */
break;
case '\n': /* LF */
case '\r': /* CR */
case '\010': /* BS */
log_debug("control: %hhu", ch);
screen_character(s, ch);
input_store8(b, ch);
break;
default:
log_debug("unknown control: %c (%hhu)", ch, ch);
break;
}
return (0);
}
/* Translate a private two-character sequence. */
int
input_pair_private(unused u_char **buf, unused size_t *len,
unused struct buffer *b, unused struct screen *s, unused u_char ch)
{
log_debug("private2: %c (%hhu)", ch, ch);
switch (ch) {
case '=': /* DECKPAM */
input_store_zero(b, CODE_KKEYPADON);
break;
case '>': /* DECKPNM*/
input_store_zero(b, CODE_KKEYPADOFF);
break;
default:
log_debug("unknown private2: %c (%hhu)", ch, ch);
break;
}
return (0);
}
/* Translate a standard two-character sequence. */
int
input_pair_standard(unused u_char **buf, unused size_t *len,
unused struct buffer *b, unused struct screen *s, u_char ch)
{
log_debug("unknown standard2: %c (%hhu)", ch, ch);
return (0);
}
/* Translate a control two-character sequence. */
int
input_pair_control(u_char **buf, size_t *len,
struct buffer *b, unused struct screen *s, u_char ch)
{
u_char *ptr;
size_t size;
log_debug("control2: %c (%hhu)", ch, ch);
switch (ch) {
case ']': /* window title */
if (*len < 3)
return (1);
ch = *(*buf)++; (*len)--;
/*
* Search MAXTITLELEN + 1 to allow space for the ;. The
* \007 is also included, but space is needed for a \0 so
* it doesn't need to be compensated for.
*/
size = *len > MAXTITLELEN + 1 ? MAXTITLELEN + 1 : *len;
if ((ptr = memchr(*buf, '\007', size)) == NULL) {
log_debug("title not found in %zu bytes", size);
if (*len >= MAXTITLELEN + 1)
break;
(*buf)--; (*len)++;
return (1);
}
size = ptr - *buf;
/* A zero size means no ;, just skip the \007 and return. */
if (size == 0) {
(*buf)++; (*len)--;
break;
}
/* Set the title if appropriate. */
if (**buf == ';' && (ch == '0' || ch == '1')) {
log_debug("title found, length %zu bytes: %.*s",
size - 1, (int) size - 1, *buf + 1);
input_store_one(b, CODE_TITLE, size - 1);
buffer_write(b, *buf + 1, size - 1);
}
/* Skip the title; add one for the \007. */
(*buf) += size + 1;
(*len) -= size + 1;
break;
case 'M': /* RI */
input_store_one(b, CODE_CURSORUPSCROLL, 1);
break;
default:
log_debug("unknown control2: %c (%hhu)", ch, ch);
break;
}
return (0);
}
/* Translate a control sequence. */
int
input_control_sequence(
u_char **buf, size_t *len, struct buffer *b, struct screen *s)
{
u_char code, private;
size_t used;
uint16_t *argv, ua, ub;
u_int argc, i;
used = input_sequence(*buf, *len, &code, &private, &argv, &argc);
if (used == 0) /* incomplete */
return (1);
(*buf) += used;
(*len) -= used;
if (code == '\0') /* invalid */
return (-1);
log_debug(
"sequence: %c (%hhu) [%c] [cx %u, cy %u, sx %u, sy %u]: %u", code,
code, private, s->cx + 1, s->cy + 1, s->sx + 1, s->sy + 1, argc);
for (i = 0; i < argc; i++)
log_debug("\targument %u: %u", i, argv[i]);
switch (code) {
case 'A': /* CUU */
if (private != '\0')
break;
if (input_check_one(argv, argc, &ua, 1) != 0)
break;
if (ua == 0)
break;
input_store_one(b, CODE_CURSORUP, ua);
break;
case 'B': /* CUD */
if (private != '\0')
break;
if (input_check_one(argv, argc, &ua, 1) != 0)
break;
if (ua == 0)
break;
input_store_one(b, CODE_CURSORDOWN, ua);
break;
case 'C': /* CUF */
if (private != '\0')
break;
if (input_check_one(argv, argc, &ua, 1) != 0)
break;
if (ua == 0)
break;
input_store_one(b, CODE_CURSORRIGHT, ua);
break;
case 'D': /* CUB */
if (private != '\0')
break;
if (input_check_one(argv, argc, &ua, 1) != 0)
break;
if (ua == 0)
break;
input_store_one(b, CODE_CURSORLEFT, ua);
break;
case 'P': /* DCH */
if (private != '\0')
break;
if (input_check_one(argv, argc, &ua, 1) != 0)
break;
if (ua == 0)
break;
input_store_one(b, CODE_DELETECHARACTER, ua);
break;
case 'M': /* DL */
if (private != '\0')
break;
if (input_check_one(argv, argc, &ua, 1) != 0)
break;
if (ua == 0)
break;
input_store_one(b, CODE_DELETELINE, ua);
break;
case '@': /* ICH */
if (private != '\0')
break;
if (input_check_one(argv, argc, &ua, 1) != 0)
break;
if (ua == 0)
break;
input_store_one(b, CODE_INSERTCHARACTER, ua);
break;
case 'L': /* IL */
if (private != '\0')
break;
if (input_check_one(argv, argc, &ua, 1) != 0)
break;
if (ua == 0)
break;
input_store_one(b, CODE_INSERTLINE, ua);
break;
case 'd': /* VPA */
if (private != '\0')
break;
if (input_check_one(argv, argc, &ua, 1) != 0)
break;
if (ua == 0)
break;
input_store_two(b, CODE_CURSORMOVE, ua, s->cx + 1);
break;
case 'G': /* HPA */
if (private != '\0')
break;
if (input_check_one(argv, argc, &ua, 1) != 0)
break;
if (ua == 0)
break;
input_store_two(b, CODE_CURSORMOVE, s->cy + 1, ua);
break;
case 'H': /* CUP */
case 'f': /* HVP */
if (private != '\0')
break;
if (input_check_two(argv, argc, &ua, 1, &ub, 1) != 0)
break;
if (ua == 0 || ub == 0)
break;
input_store_two(b, CODE_CURSORMOVE, ua, ub);
break;
case 'J': /* ED */
if (private != '\0')
break;
if (input_check_one2(argv, argc, &ua, 0, 0, 2) != 0)
break;
switch (ua) {
case 0:
input_store_zero(b, CODE_CLEARENDOFSCREEN);
break;
case 2:
input_store_zero(b, CODE_CLEARSCREEN);
break;
}
break;
case 'K': /* EL */
if (private != '\0')
break;
if (input_check_one2(argv, argc, &ua, 0, 0, 2) != 0)
break;
switch (ua) {
case 0:
input_store_zero(b, CODE_CLEARENDOFLINE);
break;
case 1:
input_store_zero(b, CODE_CLEARSTARTOFLINE);
break;
case 2:
input_store_zero(b, CODE_CLEARLINE);
break;
}
break;
case 'h': /* SM */
if (input_check_one(argv, argc, &ua, 0) != 0)
break;
switch (private) {
case '?':
switch (ua) {
case 1: /* GATM */
input_store_zero(b, CODE_KCURSORON);
break;
case 25: /* TCEM */
input_store_zero(b, CODE_CURSORON);
break;
default:
log_debug("unknown SM [%d]: %u", private, ua);
}
break;
case '\0':
switch (ua) {
case 4: /* IRM */
input_store_zero(b, CODE_INSERTON);
break;
case 34:
/* Cursor high visibility not supported. */
break;
default:
log_debug("unknown SM [%d]: %u", private, ua);
break;
}
break;
}
break;
case 'l': /* RM */
if (input_check_one(argv, argc, &ua, 0) != 0)
break;
switch (private) {
case '?':
switch (ua) {
case 1: /* GATM */
input_store_zero(b, CODE_KCURSOROFF);
break;
case 25: /* TCEM */
input_store_zero(b, CODE_CURSOROFF);
break;
default:
log_debug("unknown RM [%d]: %u", private, ua);
}
break;
case '\0':
switch (ua) {
case 4: /* IRM */
input_store_zero(b, CODE_INSERTOFF);
break;
case 34:
/* Cursor high visibility not supported. */
break;
default:
log_debug("unknown RM [%d]: %u", private, ua);
break;
}
break;
}
break;
case 'r': /* DECSTBM */
if (private != '\0')
break;
if (input_check_two(argv, argc,
&ua, s->ry_upper + 1, &ub, s->ry_lower + 1) != 0)
break;
if (ua == 0 || ub == 0 || ub < ua)
break;
input_store_two(b, CODE_SCROLLREGION, ua, ub);
break;
case 'm': /* SGR */
input_store_zero(b, CODE_ATTRIBUTES);
if (argc == 0) {
input_store16(b, 1);
input_store16(b, 0);
} else {
input_store16(b, argc);
for (i = 0; i < argc; i++) {
if (argv[i] == UINT16_MAX)
argv[i] = 0;
input_store16(b, argv[i]);
}
}
break;
default:
log_debug("unknown sequence: %c (%hhu)", code, code);
break;
}
if (argv != NULL) {
xfree(argv);
argv = NULL;
}
return (0);
}
/* Check for one argument. */
int
input_check_one(uint16_t *argv, u_int argc, uint16_t *a, uint16_t ad)
{
*a = ad;
if (argc == 1) {
if (argv[0] != UINT16_MAX)
*a = argv[0];
} else if (argc != 0)
return (-1);
return (0);
}
/* Check for one argument with limits. */
int
input_check_one2(uint16_t *argv, u_int argc,
uint16_t *a, uint16_t ad, uint16_t al, uint16_t au)
{
*a = ad;
if (argc == 1) {
if (argv[0] != UINT16_MAX)
*a = argv[0];
} else if (argc != 0)
return (-1);
if (*a < al || *a > au)
return (-1);
return (0);
}
/* Check for two arguments. */
int
input_check_two(uint16_t *argv, u_int argc,
uint16_t *a, uint16_t ad, uint16_t *b, uint16_t bd)
{
*a = ad;
*b = bd;
if (argc == 1) {
if (argv[0] != UINT16_MAX)
*a = argv[0];
} else if (argc == 2) {
if (argv[0] != UINT16_MAX)
*a = argv[0];
if (argv[1] != UINT16_MAX)
*b = argv[1];
} else if (argc != 0)
return (-1);
return (0);
}
/* Store a code without arguments. */
void
input_store_zero(struct buffer *b, u_char code)
{
input_store8(b, '\e');
input_store8(b, code);
}
/* Store a code with a single argument. */
void
input_store_one(struct buffer *b, u_char code, uint16_t ua)
{
input_store8(b, '\e');
input_store8(b, code);
input_store16(b, ua);
}
/* Store a code with two arguments. */
void
input_store_two(struct buffer *b, u_char code, uint16_t ua, uint16_t ub)
{
input_store8(b, '\e');
input_store8(b, code);
input_store16(b, ua);
input_store16(b, ub);
}
/* Write an 8-bit quantity to a buffer. */
void
input_store8(struct buffer *b, uint8_t n)
{
buffer_write(b, &n, sizeof n);
}
/* Write a 16-bit argument to a buffer. */
void
input_store16(struct buffer *b, uint16_t n)
{
buffer_write(b, &n, sizeof n);
}
/* Extract an 8-bit quantity from a buffer. */
uint8_t
input_extract8(struct buffer *b)
{
uint8_t n;
buffer_read(b, &n, sizeof n);
return (n);
}
/* Extract a 16-bit argument from a pointer. */
uint16_t
input_extract16(struct buffer *b)
{
uint16_t n;
buffer_read(b, &n, sizeof n);
return (n);
}

690
local.c Normal file
View File

@ -0,0 +1,690 @@
/* $Id: local.c,v 1.1.1.1 2007-07-09 19:03:30 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <curses.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <term.h>
#include <unistd.h>
#include "tmux.h"
/*
* Functions to translate input and write output to the local client terminal.
* This file should probably be called tty or terminal.c.
*/
int local_cmp(const void *, const void *);
int local_putc(int);
void local_putp(const char *);
/* Local key types and key codes. */
struct local_key {
const char *name;
char *string;
size_t size;
int code;
};
struct local_key local_keys[] = {
{ "ka1", NULL, 0, KEYC_A1 },
{ "ka3", NULL, 0, KEYC_A3 },
{ "kb2", NULL, 0, KEYC_B2 },
{ "kbs", NULL, 0, KEYC_BACKSPACE },
{ "kbeg", NULL, 0, KEYC_BEG },
{ "kcbt", NULL, 0, KEYC_BTAB },
{ "kc1", NULL, 0, KEYC_C1 },
{ "kc3", NULL, 0, KEYC_C3 },
{ "kcan", NULL, 0, KEYC_CANCEL },
{ "ktbc", NULL, 0, KEYC_CATAB },
{ "kclr", NULL, 0, KEYC_CLEAR },
{ "kclo", NULL, 0, KEYC_CLOSE },
{ "kcmd", NULL, 0, KEYC_COMMAND },
{ "kcpy", NULL, 0, KEYC_COPY },
{ "kcrt", NULL, 0, KEYC_CREATE },
{ "kctab", NULL, 0, KEYC_CTAB },
{ "kdch1", NULL, 0, KEYC_DC },
{ "kdl1", NULL, 0, KEYC_DL },
{ "kcud1", NULL, 0, KEYC_DOWN },
{ "krmir", NULL, 0, KEYC_EIC },
{ "kend", NULL, 0, KEYC_END },
{ "kent", NULL, 0, KEYC_ENTER },
{ "kel", NULL, 0, KEYC_EOL },
{ "ked", NULL, 0, KEYC_EOS },
{ "kext", NULL, 0, KEYC_EXIT },
{ "kf0", NULL, 0, KEYC_F0 },
{ "kf1", NULL, 0, KEYC_F1 },
{ "kf10", NULL, 0, KEYC_F10 },
{ "kf11", NULL, 0, KEYC_F11 },
{ "kf12", NULL, 0, KEYC_F12 },
{ "kf13", NULL, 0, KEYC_F13 },
{ "kf14", NULL, 0, KEYC_F14 },
{ "kf15", NULL, 0, KEYC_F15 },
{ "kf16", NULL, 0, KEYC_F16 },
{ "kf17", NULL, 0, KEYC_F17 },
{ "kf18", NULL, 0, KEYC_F18 },
{ "kf19", NULL, 0, KEYC_F19 },
{ "kf2", NULL, 0, KEYC_F2 },
{ "kf20", NULL, 0, KEYC_F20 },
{ "kf21", NULL, 0, KEYC_F21 },
{ "kf22", NULL, 0, KEYC_F22 },
{ "kf23", NULL, 0, KEYC_F23 },
{ "kf24", NULL, 0, KEYC_F24 },
{ "kf25", NULL, 0, KEYC_F25 },
{ "kf26", NULL, 0, KEYC_F26 },
{ "kf27", NULL, 0, KEYC_F27 },
{ "kf28", NULL, 0, KEYC_F28 },
{ "kf29", NULL, 0, KEYC_F29 },
{ "kf3", NULL, 0, KEYC_F3 },
{ "kf30", NULL, 0, KEYC_F30 },
{ "kf31", NULL, 0, KEYC_F31 },
{ "kf32", NULL, 0, KEYC_F32 },
{ "kf33", NULL, 0, KEYC_F33 },
{ "kf34", NULL, 0, KEYC_F34 },
{ "kf35", NULL, 0, KEYC_F35 },
{ "kf36", NULL, 0, KEYC_F36 },
{ "kf37", NULL, 0, KEYC_F37 },
{ "kf38", NULL, 0, KEYC_F38 },
{ "kf39", NULL, 0, KEYC_F39 },
{ "kf4", NULL, 0, KEYC_F4 },
{ "kf40", NULL, 0, KEYC_F40 },
{ "kf41", NULL, 0, KEYC_F41 },
{ "kf42", NULL, 0, KEYC_F42 },
{ "kf43", NULL, 0, KEYC_F43 },
{ "kf44", NULL, 0, KEYC_F44 },
{ "kf45", NULL, 0, KEYC_F45 },
{ "kf46", NULL, 0, KEYC_F46 },
{ "kf47", NULL, 0, KEYC_F47 },
{ "kf48", NULL, 0, KEYC_F48 },
{ "kf49", NULL, 0, KEYC_F49 },
{ "kf5", NULL, 0, KEYC_F5 },
{ "kf50", NULL, 0, KEYC_F50 },
{ "kf51", NULL, 0, KEYC_F51 },
{ "kf52", NULL, 0, KEYC_F52 },
{ "kf53", NULL, 0, KEYC_F53 },
{ "kf54", NULL, 0, KEYC_F54 },
{ "kf55", NULL, 0, KEYC_F55 },
{ "kf56", NULL, 0, KEYC_F56 },
{ "kf57", NULL, 0, KEYC_F57 },
{ "kf58", NULL, 0, KEYC_F58 },
{ "kf59", NULL, 0, KEYC_F59 },
{ "kf6", NULL, 0, KEYC_F6 },
{ "kf60", NULL, 0, KEYC_F60 },
{ "kf61", NULL, 0, KEYC_F61 },
{ "kf62", NULL, 0, KEYC_F62 },
{ "kf63", NULL, 0, KEYC_F63 },
{ "kf7", NULL, 0, KEYC_F7 },
{ "kf8", NULL, 0, KEYC_F8 },
{ "kf9", NULL, 0, KEYC_F9 },
{ "kfnd", NULL, 0, KEYC_FIND },
{ "khlp", NULL, 0, KEYC_HELP },
{ "khome", NULL, 0, KEYC_HOME },
{ "kich1", NULL, 0, KEYC_IC },
{ "kil1", NULL, 0, KEYC_IL },
{ "kcub1", NULL, 0, KEYC_LEFT },
{ "kll", NULL, 0, KEYC_LL },
{ "kmrk", NULL, 0, KEYC_MARK },
{ "kmsg", NULL, 0, KEYC_MESSAGE },
{ "kmov", NULL, 0, KEYC_MOVE },
{ "knxt", NULL, 0, KEYC_NEXT },
{ "knp", NULL, 0, KEYC_NPAGE },
{ "kopn", NULL, 0, KEYC_OPEN },
{ "kopt", NULL, 0, KEYC_OPTIONS },
{ "kpp", NULL, 0, KEYC_PPAGE },
{ "kprv", NULL, 0, KEYC_PREVIOUS },
{ "kprt", NULL, 0, KEYC_PRINT },
{ "krdo", NULL, 0, KEYC_REDO },
{ "kref", NULL, 0, KEYC_REFERENCE },
{ "krfr", NULL, 0, KEYC_REFRESH },
{ "krpl", NULL, 0, KEYC_REPLACE },
{ "krst", NULL, 0, KEYC_RESTART },
{ "kres", NULL, 0, KEYC_RESUME },
{ "kcuf1", NULL, 0, KEYC_RIGHT },
{ "ksav", NULL, 0, KEYC_SAVE },
{ "kBEG", NULL, 0, KEYC_SBEG },
{ "kCAN", NULL, 0, KEYC_SCANCEL },
{ "kCMD", NULL, 0, KEYC_SCOMMAND },
{ "kCPY", NULL, 0, KEYC_SCOPY },
{ "kCRT", NULL, 0, KEYC_SCREATE },
{ "kDC", NULL, 0, KEYC_SDC },
{ "kDL", NULL, 0, KEYC_SDL },
{ "kslt", NULL, 0, KEYC_SELECT },
{ "kEND", NULL, 0, KEYC_SEND },
{ "kEOL", NULL, 0, KEYC_SEOL },
{ "kEXT", NULL, 0, KEYC_SEXIT },
{ "kind", NULL, 0, KEYC_SF },
{ "kFND", NULL, 0, KEYC_SFIND },
{ "kHLP", NULL, 0, KEYC_SHELP },
{ "kHOM", NULL, 0, KEYC_SHOME },
{ "kIC", NULL, 0, KEYC_SIC },
{ "kLFT", NULL, 0, KEYC_SLEFT },
{ "kMSG", NULL, 0, KEYC_SMESSAGE },
{ "kMOV", NULL, 0, KEYC_SMOVE },
{ "kNXT", NULL, 0, KEYC_SNEXT },
{ "kOPT", NULL, 0, KEYC_SOPTIONS },
{ "kPRV", NULL, 0, KEYC_SPREVIOUS },
{ "kPRT", NULL, 0, KEYC_SPRINT },
{ "kri", NULL, 0, KEYC_SR },
{ "kRDO", NULL, 0, KEYC_SREDO },
{ "kRPL", NULL, 0, KEYC_SREPLACE },
{ "kRIT", NULL, 0, KEYC_SRIGHT },
{ "kRES", NULL, 0, KEYC_SRSUME },
{ "kSAV", NULL, 0, KEYC_SSAVE },
{ "kSPD", NULL, 0, KEYC_SSUSPEND },
{ "khts", NULL, 0, KEYC_STAB },
{ "kUND", NULL, 0, KEYC_SUNDO },
{ "kspd", NULL, 0, KEYC_SUSPEND },
{ "kund", NULL, 0, KEYC_UNDO },
{ "kcuu1", NULL, 0, KEYC_UP },
{ "pmous", NULL, 0, KEYC_MOUSE },
{ NULL, NULL, 0, KEYC_NONE }
};
/* tty file descriptor and local terminal buffers. */
int local_fd;
struct buffer *local_in;
struct buffer *local_out;
struct termios local_tio;
/* Initialise local terminal. */
int
local_init(struct buffer **in, struct buffer **out)
{
char *tty;
int mode;
struct termios tio;
struct local_key *lk;
if ((tty = ttyname(STDOUT_FILENO)) == NULL)
log_fatal("ttyname");
if ((local_fd = open(tty, O_RDWR)) == -1)
log_fatal("open(\"%s\")", tty);
if ((mode = fcntl(local_fd, F_GETFL)) == -1)
log_fatal("fcntl");
if (fcntl(local_fd, F_SETFL, mode|O_NONBLOCK) == -1)
log_fatal("fcntl");
*in = local_in = buffer_create(BUFSIZ);
*out = local_out = buffer_create(BUFSIZ);
setupterm(NULL, STDOUT_FILENO, NULL);
if (tcgetattr(local_fd, &local_tio) != 0)
log_fatal("tcgetattr");
memcpy(&tio, &local_tio, sizeof tio);
tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR);
tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET);
tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHOKE|ECHOCTL|ISIG);
if (tcsetattr(local_fd, TCSANOW, &tio) != 0)
log_fatal("tcsetattr");
if (enter_ca_mode)
local_putp(enter_ca_mode);
local_putp(keypad_xmit);
local_putp(clear_screen);
for (lk = local_keys; lk->name != NULL; lk++) {
lk->string = tigetstr(lk->name);
if (lk->string == (char *) -1 || lk->string == (char *) 0)
lk->string = NULL;
else {
lk->size = strlen(lk->string);
log_debug("string for %s (%d): \"%s\", length %zu",
lk->name, lk->code, lk->string, lk->size);
}
}
qsort(local_keys, sizeof local_keys /
sizeof local_keys[0], sizeof local_keys[0], local_cmp);
return (local_fd);
}
/* Compare keys. */
int
local_cmp(const void *ptr1, const void *ptr2)
{
const struct local_key *lk1 = ptr1, *lk2 = ptr2;
if (lk1->string == NULL && lk2->string == NULL)
return (0);
if (lk1->string == NULL)
return (1);
if (lk2->string == NULL)
return (-1);
return (lk2->size - lk1->size);
}
/* Tidy up and reset local terminal. */
void
local_done(void)
{
xfree(local_in);
xfree(local_out);
if (tcsetattr(local_fd, TCSANOW, &local_tio) != 0)
log_fatal("tcsetattr");
close(local_fd);
putp(keypad_local); /* not local_putp */
if (exit_ca_mode)
putp(exit_ca_mode);
putp(clear_screen);
putp(cursor_normal);
putp(exit_attribute_mode);
}
/* Put a character. Used as parameter to tputs. */
int
local_putc(int c)
{
FILE *f;
u_char ch = c;
if (c < 0 || c > (int) UCHAR_MAX)
log_fatalx("local_putc: invalid character");
if (debug_level > 1) {
f = fopen("tmux-out.log", "a+");
fprintf(f, "%c", ch);
fclose(f);
}
buffer_write(local_out, &ch, 1);
return (c);
}
/* Put terminfo string. */
void
local_putp(const char *s)
{
if (s == NULL)
log_fatalx("local_putp: null pointer");
tputs(s, 1, local_putc);
}
/* Return waiting keys if any. */
int
local_key(size_t *used)
{
struct local_key *lk;
u_int i;
size_t size;
size = BUFFER_USED(local_in);
if (size == 0)
return (KEYC_NONE);
i = 0;
lk = local_keys;
while (lk->string != NULL) {
if (strncmp(BUFFER_OUT(local_in), lk->string, size) == 0) {
if (size < lk->size)
return (KEYC_NONE);
log_debug("got key: %s %d", lk->name, lk->code);
buffer_remove(local_in, lk->size);
if (used != NULL)
*used = lk->size;
return (lk->code);
}
i++;
lk = local_keys + i;
}
if (used != NULL)
*used = 1;
return (input_extract8(local_in));
}
/* Display output data. */
void
local_output(struct buffer *b, size_t size)
{
u_char ch;
uint16_t ua, ub;
while (size != 0) {
if (size < 1)
break;
size--;
ch = input_extract8(b);
if (ch != '\e') {
switch (ch) {
case '\n': /* LF */
if (cursor_down) {
local_putp(cursor_down);
break;
}
log_fatalx("local_output: cursor_down");
case '\r': /* CR */
if (carriage_return) {
local_putp(carriage_return);
break;
}
log_fatalx("local_output: carriage_return");
case '\010': /* BS */
if (cursor_left) {
local_putp(cursor_left);
break;
}
log_fatalx("local_output: cursor_left");
break;
default:
local_putc(ch);
break;
}
continue;
}
if (size < 1)
log_fatalx("local_output: underflow");
size--;
ch = input_extract8(b);
log_debug("received code %hhu", ch);
switch (ch) {
case CODE_CURSORUP:
if (size < 2)
log_fatalx("local_output: underflow");
size -= 2;
ua = input_extract16(b);
if (!parm_up_cursor)
log_fatalx("local_output: parm_up_cursor");
local_putp(tparm(parm_up_cursor, ua));
break;
case CODE_CURSORDOWN:
if (size < 2)
log_fatalx("local_output: underflow");
size -= 2;
ua = input_extract16(b);
if (!parm_down_cursor)
log_fatalx("local_output: parm_down_cursor");
local_putp(tparm(parm_down_cursor, ua));
break;
case CODE_CURSORRIGHT:
if (size < 2)
log_fatalx("local_output: underflow");
size -= 2;
ua = input_extract16(b);
if (!parm_right_cursor)
log_fatalx("local_output: parm_right_cursor");
local_putp(tparm(parm_right_cursor, ua));
break;
case CODE_CURSORLEFT:
if (size < 2)
log_fatalx("local_output: underflow");
size -= 2;
ua = input_extract16(b);
if (!parm_left_cursor)
log_fatalx("local_output: parm_left_cursor");
local_putp(tparm(parm_left_cursor, ua));
break;
case CODE_CURSORMOVE:
if (size < 4)
log_fatalx("local_output: underflow");
size -= 4;
ua = input_extract16(b);
ub = input_extract16(b);
if (!cursor_address)
log_fatalx("local_output: cursor_address");
local_putp(tparm(cursor_address, ua - 1, ub - 1));
break;
case CODE_CLEARENDOFSCREEN:
if (!clr_eos)
log_fatalx("local_output: clr_eos");
local_putp(clr_eos);
break;
case CODE_CLEARSCREEN:
if (!clear_screen)
log_fatalx("local_output: clear_screen");
local_putp(clear_screen);
break;
case CODE_CLEARENDOFLINE:
if (!clr_eol)
log_fatalx("local_output: clr_eol");
local_putp(clr_eol);
break;
case CODE_CLEARSTARTOFLINE:
if (!clr_bol)
log_fatalx("local_output: clr_bol");
local_putp(clr_bol);
break;
case CODE_CLEARLINE:
if (!clr_eol)
log_fatalx("local_output: clr_eol");
local_putp(clr_eol); /* XXX */
break;
case CODE_INSERTLINE:
if (size < 2)
log_fatalx("local_output: underflow");
size -= 2;
ua = input_extract16(b);
if (!parm_insert_line)
log_fatalx("local_output: parm_insert_line");
local_putp(tparm(parm_insert_line, ua));
break;
case CODE_DELETELINE:
if (size < 2)
log_fatalx("local_output: underflow");
size -= 2;
ua = input_extract16(b);
if (!parm_delete_line)
log_fatalx("local_output: parm_delete");
local_putp(tparm(parm_delete_line, ua));
break;
case CODE_INSERTCHARACTER:
if (size < 2)
log_fatalx("local_output: underflow");
size -= 2;
ua = input_extract16(b);
if (!parm_ich)
log_fatalx("local_output: parm_ich");
local_putp(tparm(parm_ich, ua));
break;
case CODE_DELETECHARACTER:
if (size < 2)
log_fatalx("local_output: underflow");
size -= 2;
ua = input_extract16(b);
if (!parm_dch)
log_fatalx("local_output: parm_dch");
local_putp(tparm(parm_dch, ua));
break;
case CODE_CURSORON:
if (!cursor_normal)
log_fatalx("local_output: cursor_normal");
local_putp(cursor_normal);
break;
case CODE_CURSOROFF:
if (!cursor_invisible)
log_fatalx("local_output: cursor_invisible");
local_putp(cursor_invisible);
break;
case CODE_CURSORUPSCROLL:
if (!scroll_reverse)
log_fatalx("local_output: scroll_reverse");
local_putp(scroll_reverse);
break;
case CODE_CURSORDOWNSCROLL:
if (!scroll_forward)
log_fatalx("local_output: scroll_forward");
local_putp(scroll_forward);
break;
case CODE_SCROLLREGION:
if (size < 4)
log_fatalx("local_output: underflow");
size -= 4;
ua = input_extract16(b);
ub = input_extract16(b);
if (!change_scroll_region) {
log_fatalx(
"local_output: change_scroll_region");
}
local_putp(tparm(change_scroll_region, ua - 1, ub - 1));
break;
case CODE_INSERTON:
if (!enter_insert_mode)
log_fatalx("local_output: enter_insert_mode");
local_putp(enter_insert_mode);
break;
case CODE_INSERTOFF:
if (!exit_insert_mode)
log_fatalx("local_output: exit_insert_mode");
local_putp(exit_insert_mode);
break;
case CODE_KCURSOROFF:
/*t = tigetstr("CE");
if (t != (char *) 0 && t != (char *) -1)
local_putp(t);*/
break;
case CODE_KCURSORON:
/*t = tigetstr("CS");
if (t != (char *) 0 && t != (char *) -1)
local_putp(t);*/
break;
case CODE_KKEYPADOFF:/*
if (!keypad_local)
log_fatalx("local_output: keypad_local");
local_putp(keypad_local);*/
break;
case CODE_KKEYPADON:/*
if (!keypad_xmit)
log_fatalx("local_output: keypad_xmit");
local_putp(keypad_xmit);*/
break;
case CODE_TITLE:
if (size < 2)
log_fatalx("local_output: underflow");
size -= 2;
ua = input_extract16(b);
if (size < ua)
log_fatalx("local_output: underflow");
size -= ua;
buffer_remove(b, ua);
break;
case CODE_ATTRIBUTES:
if (size < 2)
log_fatalx("local_output: underflow");
size -= 2;
ua = input_extract16(b);
if (!exit_attribute_mode)
log_fatalx("local_output: exit_attribute_mode");
if (ua == 0) {
if (exit_attribute_mode)
local_putp(exit_attribute_mode);
break;
}
while (ua-- != 0) {
if (size < 2)
log_fatalx("local_output: underflow");
size -= 2;
ub = input_extract16(b);
switch (ub) {
case 0:
case 10:
if (exit_attribute_mode)
local_putp(exit_attribute_mode);
break;
case 1:
if (enter_bold_mode)
local_putp(enter_bold_mode);
break;
case 2:
if (enter_dim_mode)
local_putp(enter_dim_mode);
break;
case 3:
if (enter_standout_mode)
local_putp(enter_standout_mode);
break;
case 4:
if (enter_underline_mode)
local_putp(enter_underline_mode);
break;
case 5:
if (enter_blink_mode)
local_putp(enter_blink_mode);
break;
case 7:
if (enter_reverse_mode)
local_putp(enter_reverse_mode);
break;
case 8:
if (enter_secure_mode)
local_putp(enter_secure_mode);
break;
case 23:
if (exit_standout_mode)
local_putp(exit_standout_mode);
break;
case 24:
if (exit_underline_mode)
local_putp(exit_underline_mode);
break;
case 30:
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
local_putp(
tparm(set_a_foreground, ub - 30));
break;
case 39:
if (tigetflag("AX") == TRUE)
local_putp("\e[39m");
else {
local_putp(
tparm(set_a_background, 0));
}
break;
case 40:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
local_putp(
tparm(set_a_background, ub - 40));
break;
case 49:
if (tigetflag("AX") == TRUE)
local_putp("\e[49m");
else {
local_putp(
tparm(set_a_background, 0));
}
break;
}
}
break;
}
}
}

218
log.c Normal file
View File

@ -0,0 +1,218 @@
/* $Id: log.c,v 1.1.1.1 2007-07-09 19:03:33 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include "tmux.h"
/* Logging enabled. */
int log_enabled;
/* Log stream or NULL to use syslog. */
FILE *log_stream;
/* Debug level. */
int log_level;
/* Open log. */
void
log_open(FILE *f, int facility, int level)
{
log_stream = f;
log_level = level;
if (f == NULL)
openlog(__progname, LOG_PID|LOG_NDELAY, facility);
tzset();
log_enabled = 1;
}
/* Close logging. */
void
log_close(void)
{
if (log_stream != NULL)
fclose(log_stream);
log_enabled = 0;
}
/* Write a log message. */
void
log_write(FILE *f, int priority, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_vwrite(f, priority, fmt, ap);
va_end(ap);
}
/* Write a log message. */
void
log_vwrite(FILE *f, int priority, const char *fmt, va_list ap)
{
if (!log_enabled)
return;
if (f == NULL) {
vsyslog(priority, fmt, ap);
return;
}
if (vfprintf(f, fmt, ap) == -1)
exit(1);
if (fputc('\n', f) == EOF)
exit(1);
fflush(log_stream);
}
/* Log a warning with error string. */
void printflike1
log_warn(const char *msg, ...)
{
va_list ap;
char *fmt;
if (!log_enabled)
return;
va_start(ap, msg);
if (asprintf(&fmt, "%s: %s", msg, strerror(errno)) == -1)
exit(1);
log_vwrite(log_stream, LOG_CRIT, fmt, ap);
xfree(fmt);
va_end(ap);
}
/* Log a warning. */
void printflike1
log_warnx(const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
log_vwrite(log_stream, LOG_CRIT, msg, ap);
va_end(ap);
}
/* Log an informational message. */
void printflike1
log_info(const char *msg, ...)
{
va_list ap;
if (log_level > -1) {
va_start(ap, msg);
if (log_stream == stderr)
log_vwrite(stdout, LOG_INFO, msg, ap);
else
log_vwrite(log_stream, LOG_INFO, msg, ap);
va_end(ap);
}
}
/* Log a debug message. */
void printflike1
log_debug(const char *msg, ...)
{
va_list ap;
if (log_level > 0) {
va_start(ap, msg);
log_vwrite(log_stream, LOG_DEBUG, msg, ap);
va_end(ap);
}
}
/* Log a debug message at level 2. */
void printflike1
log_debug2(const char *msg, ...)
{
va_list ap;
if (log_level > 1) {
va_start(ap, msg);
log_vwrite(log_stream, LOG_DEBUG, msg, ap);
va_end(ap);
}
}
/* Log a debug message at level 3. */
void printflike1
log_debug3(const char *msg, ...)
{
va_list ap;
if (log_level > 2) {
va_start(ap, msg);
log_vwrite(log_stream, LOG_DEBUG, msg, ap);
va_end(ap);
}
}
/* Log a critical error, with error string if necessary, and die. */
__dead void
log_vfatal(const char *msg, va_list ap)
{
char *fmt;
if (!log_enabled)
exit(1);
if (errno != 0) {
if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1)
exit(1);
log_vwrite(log_stream, LOG_CRIT, fmt, ap);
} else {
if (asprintf(&fmt, "fatal: %s", msg) == -1)
exit(1);
log_vwrite(log_stream, LOG_CRIT, fmt, ap);
}
xfree(fmt);
exit(1);
}
/* Log a critical error, with error string, and die. */
__dead void
log_fatal(const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
log_vfatal(msg, ap);
}
/* Log a critical error and die. */
__dead void
log_fatalx(const char *msg, ...)
{
va_list ap;
errno = 0;
va_start(ap, msg);
log_vfatal(msg, ap);
}

827
screen.c Normal file
View File

@ -0,0 +1,827 @@
/* $Id: screen.c,v 1.1.1.1 2007-07-09 19:03:54 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <string.h>
#include "tmux.h"
/*
* Virtual screen and basic ANSI terminal emulator.
*/
size_t screen_store_attributes(struct buffer *, u_char);
size_t screen_store_colours(struct buffer *, u_char);
void screen_free_lines(struct screen *, u_int, u_int);
void screen_make_lines(struct screen *, u_int, u_int);
void screen_move_lines(struct screen *, u_int, u_int, u_int);
void screen_fill_lines(
struct screen *, u_int, u_int, u_char, u_char, u_char);
uint16_t screen_extract(u_char *);
void screen_write_character(struct screen *, u_char);
void screen_cursor_up_scroll(struct screen *, u_int);
void screen_cursor_down_scroll(struct screen *, u_int);
void screen_scroll_up(struct screen *, u_int);
void screen_scroll_down(struct screen *, u_int);
void screen_fill_screen(struct screen *, u_char, u_char, u_char);
void screen_fill_line(struct screen *, u_int, u_char, u_char, u_char);
void screen_fill_end_of_screen(
struct screen *, u_int, u_int, u_char, u_char, u_char);
void screen_fill_end_of_line(
struct screen *, u_int, u_int, u_char, u_char, u_char);
void screen_fill_start_of_line(
struct screen *, u_int, u_int, u_char, u_char, u_char);
void screen_insert_lines(struct screen *, u_int, u_int);
void screen_delete_lines(struct screen *, u_int, u_int);
void screen_insert_characters(struct screen *, u_int, u_int, u_int);
void screen_delete_characters(struct screen *, u_int, u_int, u_int);
#define SCREEN_DEFDATA ' '
#define SCREEN_DEFATTR 0
#define SCREEN_DEFCOLR 0x88
#define screen_last_y(s) ((s)->sy - 1)
#define screen_last_x(s) ((s)->sx - 1)
/* Create a new screen. */
void
screen_create(struct screen *s, u_int sx, u_int sy)
{
s->sx = sx;
s->sy = sy;
s->cx = 0;
s->cy = 0;
s->ry_upper = 0;
s->ry_lower = sy - 1;
s->attr = SCREEN_DEFATTR;
s->colr = SCREEN_DEFCOLR;
s->mode = MODE_CURSOR;
*s->title = '\0';
s->grid_data = xmalloc(sy * (sizeof *s->grid_data));
s->grid_attr = xmalloc(sy * (sizeof *s->grid_attr));
s->grid_colr = xmalloc(sy * (sizeof *s->grid_colr));
screen_make_lines(s, 0, screen_last_y(s));
screen_fill_screen(s, SCREEN_DEFDATA, 0, SCREEN_DEFCOLR);
}
/* Resize screen. */
void
screen_resize(struct screen *s, u_int sx, u_int sy)
{
u_int i, ox, oy, ny;
if (sx < 1)
sx = 1;
if (sy < 1)
sy = 1;
ox = s->sx;
oy = s->sy;
s->sx = sx;
s->sy = sy;
log_debug("resizing screen (%u, %u) -> (%u, %u)", ox, oy, sx, sy);
if (sy < oy) {
ny = oy - sy;
if (ny > s->cy)
ny = s->cy;
if (ny != 0) {
log_debug("removing %u lines from top", ny);
for (i = 0; i < ny; i++) {
log_debug("freeing line %u", i);
xfree(s->grid_data[i]);
xfree(s->grid_attr[i]);
xfree(s->grid_colr[i]);
}
memmove(s->grid_data, s->grid_data + ny,
(oy - ny) * (sizeof *s->grid_data));
memmove(s->grid_attr, s->grid_attr + ny,
(oy - ny) * (sizeof *s->grid_attr));
memmove(s->grid_colr, s->grid_colr + ny,
(oy - ny) * (sizeof *s->grid_colr));
s->cy -= ny;
}
if (ny < oy - sy) {
log_debug(
"removing %u lines from bottom", oy - sy - ny);
for (i = sy; i < oy - ny; i++) {
log_debug("freeing line %u", i);
xfree(s->grid_data[i]);
xfree(s->grid_attr[i]);
xfree(s->grid_colr[i]);
}
if (s->cy >= sy)
s->cy = sy - 1;
}
}
if (sy != oy) {
s->grid_data = xrealloc(s->grid_data, sy, sizeof *s->grid_data);
s->grid_attr = xrealloc(s->grid_attr, sy, sizeof *s->grid_attr);
s->grid_colr = xrealloc(s->grid_colr, sy, sizeof *s->grid_colr);
}
if (sy > oy) {
for (i = oy; i < sy; i++) {
log_debug("allocating line %u", i);
s->grid_data[i] = xmalloc(sx);
s->grid_attr[i] = xmalloc(sx);
s->grid_colr[i] = xmalloc(sx);
screen_fill_line(s, i,
SCREEN_DEFDATA, SCREEN_DEFATTR, SCREEN_DEFCOLR);
}
sy = oy;
}
if (sx != ox) {
for (i = 0; i < sy; i++) {
log_debug("adjusting line %u to %u", i, sx);
s->grid_data[i] = xrealloc(s->grid_data[i], sx, 1);
s->grid_attr[i] = xrealloc(s->grid_attr[i], sx, 1);
s->grid_colr[i] = xrealloc(s->grid_colr[i], sx, 1);
if (sx > ox) {
screen_fill_end_of_line(s, ox, i,
SCREEN_DEFDATA, SCREEN_DEFATTR,
SCREEN_DEFCOLR);
}
}
if (s->cx >= sx)
s->cx = sx - 1;
}
}
/* Draw a set of lines on the screen. */
void
screen_draw(struct screen *s, struct buffer *b, u_int py_upper, u_int py_lower)
{
u_char attr, colr;
size_t size;
u_int i, j;
uint16_t n;
if (py_upper > s->sy || py_lower > s->sy || py_upper > py_lower) {
log_fatalx(
"screen_draw_lines: invalid, %u to %u", py_upper, py_lower);
}
/* XXX. This is naive and rough right now. */
attr = 0;
colr = SCREEN_DEFCOLR;
input_store_zero(b, CODE_CURSOROFF);
input_store_one(b, CODE_ATTRIBUTES, 0);
for (j = py_upper; j <= py_lower; j++) {
input_store_two(b, CODE_CURSORMOVE, j + 1, 1);
for (i = 0; i < s->sx; i++) {
size = BUFFER_USED(b);
input_store_one(b, CODE_ATTRIBUTES, 0);
n = 0;
if (s->grid_attr[j][i] != attr) {
attr = s->grid_attr[j][i];
n += screen_store_attributes(b, attr);
if (attr == 0)
colr = SCREEN_DEFCOLR;
}
if (s->grid_colr[j][i] != colr) {
colr = s->grid_colr[j][i];
n += screen_store_colours(b, colr);
}
if (n == 0)
buffer_reverse_add(b, 4);
else {
size = BUFFER_USED(b) - size;
memcpy(BUFFER_IN(b) - size + 2, &n, 2);
}
input_store8(b, s->grid_data[j][i]);
}
}
size = BUFFER_USED(b);
input_store_one(b, CODE_ATTRIBUTES, 0);
n = screen_store_attributes(b, s->attr);
n += screen_store_colours(b, s->colr);
size = BUFFER_USED(b) - size;
memcpy(BUFFER_IN(b) - size + 2, &n, 2);
input_store_two(b, CODE_CURSORMOVE, s->cy + 1, s->cx + 1);
if (s->mode & MODE_CURSOR)
input_store_zero(b, CODE_CURSORON);
}
/* Store screen atttributes in buffer. */
size_t
screen_store_attributes(struct buffer *b, u_char attr)
{
size_t n;
if (attr == 0) {
input_store16(b, 0);
return (1);
}
n = 0;
if (attr & ATTR_BRIGHT) {
input_store16(b, 1);
n++;
}
if (attr & ATTR_DIM) {
input_store16(b, 2);
n++;
}
if (attr & ATTR_ITALICS) {
input_store16(b, 3);
n++;
}
if (attr & ATTR_UNDERSCORE) {
input_store16(b, 4);
n++;
}
if (attr & ATTR_BLINK) {
input_store16(b, 5);
n++;
}
if (attr & ATTR_REVERSE) {
input_store16(b, 7);
n++;
}
if (attr & ATTR_HIDDEN) {
input_store16(b, 8);
n++;
}
return (n);
}
/* Store screen colours in buffer. */
size_t
screen_store_colours(struct buffer *b, u_char colr)
{
uint16_t v;
v = colr >> 4;
if (v == 8)
v = 9;
input_store16(b, 30 + v);
v = colr & 0xf;
if (v == 8)
v = 9;
input_store16(b, 40 + v);
return (2);
}
/* Make a range of lines. */
void
screen_make_lines(struct screen *s, u_int uy, u_int ly)
{
u_int i;
log_debug("making lines %u:%u", uy, ly);
if (uy > screen_last_y(s) || ly > screen_last_y(s) || ly < uy)
log_fatalx("screen_make_lines: bad range");
for (i = uy; i <= ly; i++) {
s->grid_data[i] = xmalloc(s->sx);
s->grid_attr[i] = xmalloc(s->sx);
s->grid_colr[i] = xmalloc(s->sx);
}
}
/* Free a range of lines. */
void
screen_free_lines(struct screen *s, u_int uy, u_int ly)
{
u_int i;
log_debug("freeing lines %u:%u", uy, ly);
if (uy > screen_last_y(s) || ly > screen_last_y(s) || ly < uy)
log_fatalx("screen_free_lines: bad range");
for (i = uy; i <= ly; i++) {
xfree(s->grid_data[i]);
s->grid_data[i] = (u_char *) 0xabcdabcd;
xfree(s->grid_attr[i]);
s->grid_attr[i] = (u_char *) 0xabcdabcd;
xfree(s->grid_colr[i]);
s->grid_colr[i] = (u_char *) 0xabcdabcd;
}
}
/* Move a range of lines. */
void
screen_move_lines(struct screen *s, u_int dy, u_int uy, u_int ly)
{
u_int ny;
log_debug("moving lines %u:%u to %u", uy, ly, dy);
if (uy > screen_last_y(s) || ly > screen_last_y(s) || ly < uy)
log_fatalx("screen_move_lines: bad range");
if (dy > screen_last_y(s))
log_fatalx("screen_move_lines: bad destination");
ny = (ly - uy) + 1;
memmove(
s->grid_data + dy, s->grid_data + uy, ny * (sizeof *s->grid_data));
memmove(
s->grid_attr + dy, s->grid_attr + uy, ny * (sizeof *s->grid_attr));
memmove(
s->grid_colr + dy, s->grid_colr + uy, ny * (sizeof *s->grid_colr));
}
/* Fill a range of lines. */
void
screen_fill_lines(
struct screen *s, u_int uy, u_int ly, u_char data, u_char attr, u_char colr)
{
u_int i;
log_debug("filling lines %u:%u", uy, ly);
if (uy > screen_last_y(s) || ly > screen_last_y(s) || ly < uy)
log_fatalx("screen_fill_lines: bad range");
for (i = uy; i <= ly; i++)
screen_fill_line(s, i, data, attr, colr);
}
/* Update screen with character. */
void
screen_character(struct screen *s, u_char ch)
{
switch (ch) {
case '\n': /* LF */
screen_cursor_down_scroll(s, 1);
break;
case '\r': /* CR */
s->cx = 0;
break;
case '\010': /* BS */
if (s->cx > 0)
s->cx--;
break;
default:
if (ch < ' ')
log_fatalx("screen_character: bad control: %hhu", ch);
screen_write_character(s, ch);
break;
}
}
/* Extract 16-bit value from pointer. */
uint16_t
screen_extract(u_char *ptr)
{
uint16_t n;
memcpy(&n, ptr, sizeof n);
return (n);
}
/* Update screen with escape sequence. */
void
screen_sequence(struct screen *s, u_char *ptr)
{
uint16_t ua, ub;
ptr++;
log_debug("processing code %hhu", *ptr);
switch (*ptr++) {
case CODE_CURSORUP:
ua = screen_extract(ptr);
if (ua > s->cy)
ua = s->cy;
s->cy -= ua;
break;
case CODE_CURSORDOWN:
ua = screen_extract(ptr);
if (s->cy + ua >= s->sy)
ua = s->sy - 1 - s->cy;
s->cy += ua;
break;
case CODE_CURSORLEFT:
ua = screen_extract(ptr);
if (ua > s->cx)
ua = s->cx;
s->cx -= ua;
break;
case CODE_CURSORRIGHT:
ua = screen_extract(ptr);
if (s->cx + ua >= s->sx)
ua = s->sx - 1 - s->cx;
s->cx += ua;
break;
case CODE_CURSORMOVE:
ua = screen_extract(ptr);
ptr += 2;
ub = screen_extract(ptr);
if (ub > s->sx)
ub = s->sx;
s->cx = ub - 1;
if (ua > s->sy)
ua = s->sy;
s->cy = ua - 1;
break;
case CODE_CLEARENDOFSCREEN:
screen_fill_end_of_screen(
s, s->cx, s->cy, SCREEN_DEFDATA, s->attr, s->colr);
break;
case CODE_CLEARSCREEN:
screen_fill_screen(s, SCREEN_DEFDATA, s->attr, s->colr);
break;
case CODE_CLEARENDOFLINE:
screen_fill_end_of_line(
s, s->cx, s->cy, SCREEN_DEFDATA, s->attr, s->colr);
break;
case CODE_CLEARSTARTOFLINE:
screen_fill_start_of_line(
s, s->cx, s->cy, SCREEN_DEFDATA, s->attr, s->colr);
break;
case CODE_CLEARLINE:
screen_fill_line(s, s->cy, SCREEN_DEFDATA, s->attr, s->colr);
break;
case CODE_INSERTLINE:
ua = screen_extract(ptr);
screen_insert_lines(s, s->cy, ua);
break;
case CODE_DELETELINE:
ua = screen_extract(ptr);
screen_delete_lines(s, s->cy, ua);
break;
case CODE_INSERTCHARACTER:
ua = screen_extract(ptr);
screen_insert_characters(s, s->cx, s->cy, ua);
break;
case CODE_DELETECHARACTER:
ua = screen_extract(ptr);
screen_delete_characters(s, s->cx, s->cy, ua);
break;
case CODE_CURSORON:
s->mode |= MODE_CURSOR;
break;
case CODE_CURSOROFF:
s->mode &= ~MODE_CURSOR;
break;
case CODE_CURSORDOWNSCROLL:
ua = screen_extract(ptr);
screen_cursor_down_scroll(s, ua);
break;
case CODE_CURSORUPSCROLL:
ua = screen_extract(ptr);
screen_cursor_up_scroll(s, ua);
break;
case CODE_SCROLLREGION:
ua = screen_extract(ptr);
ptr += 2;
ub = screen_extract(ptr);
if (ua > s->sy)
ua = s->sy;
s->ry_upper = ua - 1;
if (ub > s->sy)
ub = s->sy;
s->ry_lower = ub - 1;
break;
case CODE_INSERTOFF:
s->mode &= ~MODE_INSERT;
break;
case CODE_INSERTON:
s->mode |= MODE_INSERT;
break;
case CODE_KCURSOROFF:
s->mode &= ~MODE_KCURSOR;
break;
case CODE_KCURSORON:
s->mode |= MODE_KCURSOR;
break;
case CODE_KKEYPADOFF:
s->mode &= ~MODE_KKEYPAD;
break;
case CODE_KKEYPADON:
s->mode |= MODE_KKEYPAD;
break;
case CODE_TITLE:
ua = screen_extract(ptr);
ptr += 2;
log_debug("new title: %u:%.*s", ua, (int) ua, ptr);
if (ua > MAXTITLELEN - 1)
ua = MAXTITLELEN - 1;
memcpy(s->title, ptr, ua);
s->title[ua] = '\0';
break;
case CODE_ATTRIBUTES:
ua = screen_extract(ptr);
if (ua == 0) {
s->attr = 0;
s->colr = SCREEN_DEFCOLR;
break;
}
while (ua-- > 0) {
ptr += 2;
ub = screen_extract(ptr);
switch (ub) {
case 0:
case 10:
s->attr = 0;
s->colr = SCREEN_DEFCOLR;
break;
case 1:
s->attr |= ATTR_BRIGHT;
break;
case 2:
s->attr |= ATTR_DIM;
break;
case 3:
s->attr |= ATTR_ITALICS;
break;
case 4:
s->attr |= ATTR_UNDERSCORE;
break;
case 5:
s->attr |= ATTR_BLINK;
break;
case 7:
s->attr |= ATTR_REVERSE;
break;
case 8:
s->attr |= ATTR_HIDDEN;
break;
case 23:
s->attr &= ~ATTR_ITALICS;
break;
case 24:
s->attr &= ~ATTR_UNDERSCORE;
break;
case 30:
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
s->colr &= 0x0f;
s->colr |= (ub - 30) << 4;
break;
case 39:
s->colr &= 0x0f;
s->colr |= 0x80;
break;
case 40:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
s->colr &= 0xf0;
s->colr |= ub - 40;
break;
case 49:
s->colr &= 0xf0;
s->colr |= 0x08;
break;
}
}
}
}
/* Write a single character to the screen at the cursor and move forward. */
void
screen_write_character(struct screen *s, u_char ch)
{
s->grid_data[s->cy][s->cx] = ch;
s->grid_attr[s->cy][s->cx] = s->attr;
s->grid_colr[s->cy][s->cx] = s->colr;
s->cx++;
if (s->cx >= s->sx) {
s->cx = 0;
screen_cursor_down_scroll(s, 1);
}
}
/* Move cursor up and scroll if necessary. */
void
screen_cursor_up_scroll(struct screen *s, u_int ny)
{
if (s->cy < ny) {
screen_scroll_down(s, ny - s->cy);
s->cy = 0;
} else
s->cy -= ny;
}
/* Move cursor down and scroll if necessary. */
void
screen_cursor_down_scroll(struct screen *s, u_int ny)
{
if (s->sy - 1 - s->cy < ny) {
screen_scroll_up(s, ny - (s->sy - 1 - s->cy));
s->cy = s->sy - 1;
} else
s->cy += ny;
}
/* Scroll screen up. */
void
screen_scroll_up(struct screen *s, u_int ny)
{
if (s->ry_upper == 0 && s->ry_lower == s->sy - 1) {
screen_delete_lines(s, 0, ny);
return;
}
log_fatalx("screen_scroll_up: %u %u", s->ry_upper, s->ry_lower);
}
/* Scroll screen down. */
void
screen_scroll_down(struct screen *s, u_int ny)
{
if (s->ry_upper == 0 && s->ry_lower == s->sy - 1) {
screen_insert_lines(s, 0, ny);
return;
}
log_fatalx("screen_scroll_down: %u %u", s->ry_upper, s->ry_lower);
}
/* Fill entire screen. */
void
screen_fill_screen(struct screen *s, u_char data, u_char attr, u_char colr)
{
screen_fill_end_of_screen(s, 0, 0, data, attr, colr);
}
/* Fill single line. */
void
screen_fill_line(
struct screen *s, u_int py, u_char data, u_char attr, u_char colr)
{
screen_fill_end_of_line(s, 0, py, data, attr, colr);
}
/* Fill to end of screen. */
void
screen_fill_end_of_screen(
struct screen *s, u_int px, u_int py, u_char data, u_char attr, u_char colr)
{
if (py >= s->sy)
return;
if (px != 0) {
screen_fill_end_of_line(s, px, py, data, attr, colr);
if (py++ >= s->sy)
return;
}
while (py++ < s->sy)
screen_fill_line(s, py - 1, data, attr, colr);
}
/* Fill to end of line. */
void
screen_fill_end_of_line(
struct screen *s, u_int px, u_int py, u_char data, u_char attr, u_char colr)
{
if (px >= s->sx)
return;
if (py >= s->sy)
return;
memset(s->grid_data[py] + px, data, s->sx - px);
memset(s->grid_attr[py] + px, attr, s->sx - px);
memset(s->grid_colr[py] + px, colr, s->sx - px);
}
/* Fill to start of line. */
void
screen_fill_start_of_line(
struct screen *s, u_int px, u_int py, u_char data, u_char attr, u_char colr)
{
if (px >= s->sx)
return;
if (py >= s->sy)
return;
memset(s->grid_data[py], data, px);
memset(s->grid_attr[py], attr, px);
memset(s->grid_colr[py], colr, px);
}
/* Insert lines. */
void
screen_insert_lines(struct screen *s, u_int py, u_int ny)
{
if (py > screen_last_y(s))
return;
if (py + ny > screen_last_y(s))
ny = screen_last_y(s) - py;
log_debug("inserting lines: %u,%u", py, ny);
screen_free_lines(s, (screen_last_y(s) - ny) + 1, screen_last_y(s));
if (py != screen_last_y(s))
screen_move_lines(s, py + ny, py, screen_last_y(s) - ny);
screen_fill_lines(
s, py, py + ny - 1, SCREEN_DEFDATA, SCREEN_DEFATTR, SCREEN_DEFCOLR);
}
/* Delete lines. */
void
screen_delete_lines(struct screen *s, u_int py, u_int ny)
{
if (py > screen_last_y(s))
return;
if (py + ny > screen_last_y(s))
ny = screen_last_y(s) - py;
log_debug("deleting lines: %u,%u", py, ny);
screen_free_lines(s, py, py + ny - 1);
if (py != screen_last_y(s))
screen_move_lines(s, py, py + ny, screen_last_y(s));
screen_make_lines(s, (screen_last_y(s) - ny) + 1, screen_last_y(s));
screen_fill_lines(s, (screen_last_y(s) - ny) + 1,
screen_last_y(s), SCREEN_DEFDATA, SCREEN_DEFATTR, SCREEN_DEFCOLR);
}
/* Insert characters. */
void
screen_insert_characters(struct screen *s, u_int px, u_int py, u_int nx)
{
if (px >= s->sx || py >= s->sy)
return;
if (px + nx > s->sx)
nx = s->sx - px;
if (px - nx != s->sx) {
memmove(s->grid_data[py] + px + nx,
s->grid_data[py] + px, s->sx - px - nx);
memmove(s->grid_attr[py] + px + nx,
s->grid_attr[py] + px, s->sx - px - nx);
memmove(s->grid_colr[py] + px + nx,
s->grid_colr[py] + px, s->sx - px - nx);
}
memset(s->grid_data[py] + px, SCREEN_DEFDATA, nx);
memset(s->grid_attr[py] + px, SCREEN_DEFATTR, nx);
memset(s->grid_colr[py] + px, SCREEN_DEFCOLR, nx);
}
/* Delete characters. */
void
screen_delete_characters(struct screen *s, u_int px, u_int py, u_int nx)
{
if (px >= s->sx || py >= s->sy)
return;
if (px + nx > s->sx)
nx = s->sx - px;
if (px - nx != s->sx) {
memmove(s->grid_data[py] + px,
s->grid_data[py] + px + nx, s->sx - px - nx);
memmove(s->grid_attr[py] + px,
s->grid_attr[py] + px + nx, s->sx - px - nx);
memmove(s->grid_colr[py] + px,
s->grid_colr[py] + px + nx, s->sx - px - nx);
}
memset(s->grid_data[py] + px + nx, SCREEN_DEFDATA, s->sx - px - nx);
memset(s->grid_attr[py] + px + nx, SCREEN_DEFATTR, s->sx - px - nx);
memset(s->grid_colr[py] + px + nx, SCREEN_DEFCOLR, s->sx - px - nx);
}

999
server.c Normal file
View File

@ -0,0 +1,999 @@
/* $Id: server.c,v 1.1.1.1 2007-07-09 19:03:42 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <termios.h>
#include <unistd.h>
#include <util.h>
#include "tmux.h"
/*
* Main server functions.
*/
/* Client list. */
struct clients clients;
int server_main(int);
void fill_windows(struct pollfd **);
void handle_windows(struct pollfd **);
void fill_clients(struct pollfd **);
void handle_clients(struct pollfd **);
struct client *accept_client(int);
void lost_client(struct client *);
void user_start(struct client *, const char *, const char *,
size_t, void (*)(struct client *, const char *));
void user_input(struct client *, size_t);
void write_message(struct client *, const char *, ...);
void write_client(struct client *, u_int, void *, size_t);
void write_client2(
struct client *, u_int, void *, size_t, void *, size_t);
void write_clients(struct window *, u_int, void *, size_t);
void new_window(struct window *);
void lost_window(struct window *);
void changed_window(struct client *);
void draw_client(struct client *, u_int, u_int);
void process_client(struct client *);
void process_identify_msg(struct client *, struct hdr *);
void process_create_msg(struct client *, struct hdr *);
void process_next_msg(struct client *, struct hdr *);
void process_previous_msg(struct client *, struct hdr *);
void process_select_msg(struct client *, struct hdr *);
void process_size_msg(struct client *, struct hdr *);
void process_input_msg(struct client *, struct hdr *);
void process_refresh_msg(struct client *, struct hdr *);
void process_sessions_msg(struct client *, struct hdr *);
void process_windows_msg(struct client *, struct hdr *);
void process_rename_msg(struct client *, struct hdr *);
void rename_callback(struct client *, const char *);
/* Fork and start server process. */
int
server_start(void)
{
mode_t mode;
int fd;
struct sockaddr_un sa;
size_t sz;
pid_t pid;
FILE *f;
char *path;
/* Fork the server process. */
switch (pid = fork()) {
case -1:
return (-1);
case 0:
break;
default:
return (0);
}
/* Start logging to file. */
if (debug_level > 0) {
xasprintf(&path,
"%s-server-%ld.log", __progname, (long) getpid());
f = fopen(path, "w");
log_open(f, LOG_DAEMON, debug_level);
xfree(path);
}
log_debug("server started, pid %ld", (long) getpid());
/* Create the socket. */
memset(&sa, 0, sizeof sa);
sa.sun_family = AF_UNIX;
sz = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
if (sz >= sizeof sa.sun_path) {
errno = ENAMETOOLONG;
log_fatal("socket");
}
unlink(sa.sun_path);
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
log_fatal("socket");
mode = umask(S_IXUSR|S_IRWXG|S_IRWXO);
if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1)
log_fatal("bind");
umask(mode);
if (listen(fd, 16) == -1)
log_fatal("listen");
/*
* Detach into the background. This means the PID changes which will
* have to be fixed in some way at some point... XXX
*/
if (daemon(1, 1) != 0)
log_fatal("daemon");
log_debug("server daemonised, pid now %ld", (long) getpid());
setproctitle("server (%s)", socket_path);
exit(server_main(fd));
}
/* Main server loop. */
int
server_main(int srv_fd)
{
struct pollfd *pfds, *pfd;
int nfds, mode;
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART;
act.sa_handler = SIG_IGN;
if (sigaction(SIGPIPE, &act, NULL) != 0)
log_fatal("sigaction");
if (sigaction(SIGUSR1, &act, NULL) != 0)
log_fatal("sigaction");
if (sigaction(SIGUSR2, &act, NULL) != 0)
log_fatal("sigaction");
if (sigaction(SIGINT, &act, NULL) != 0)
log_fatal("sigaction");
if (sigaction(SIGQUIT, &act, NULL) != 0)
log_fatal("sigaction");
ARRAY_INIT(&windows);
ARRAY_INIT(&clients);
ARRAY_INIT(&sessions);
if ((mode = fcntl(srv_fd, F_GETFL)) == -1)
log_fatal("fcntl");
if (fcntl(srv_fd, F_SETFL, mode|O_NONBLOCK) == -1)
log_fatal("fcntl");
pfds = NULL;
while (!sigterm) {
/* Initialise pollfd array. */
nfds = 1 + ARRAY_LENGTH(&windows) + ARRAY_LENGTH(&clients);
pfds = xrealloc(pfds, nfds, sizeof *pfds);
pfd = pfds;
/* Fill server socket. */
pfd->fd = srv_fd;
pfd->events = POLLIN;
pfd++;
/* Fill window and client sockets. */
fill_windows(&pfd);
fill_clients(&pfd);
/* Do the poll. */
if (poll(pfds, nfds, INFTIM) == -1) {
if (errno == EAGAIN || errno == EINTR)
continue;
log_fatal("poll");
}
pfd = pfds;
/* Handle server socket. */
if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP))
log_fatalx("lost server socket");
if (pfd->revents & POLLIN) {
accept_client(srv_fd);
continue;
}
pfd++;
/*
* Handle window and client sockets. Clients can create
* windows, so windows must come first to avoid messing up by
* increasing the array size.
*/
handle_windows(&pfd);
handle_clients(&pfd);
}
close(srv_fd);
unlink(socket_path);
return (0);
}
/* Fill window pollfds. */
void
fill_windows(struct pollfd **pfd)
{
struct window *w;
u_int i;
for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
if ((w = ARRAY_ITEM(&windows, i)) == NULL)
(*pfd)->fd = -1;
else {
(*pfd)->fd = w->fd;
(*pfd)->events = POLLIN;
if (BUFFER_USED(w->out) > 0)
(*pfd)->events |= POLLOUT;
}
(*pfd)++;
}
}
/* Handle window pollfds. */
void
handle_windows(struct pollfd **pfd)
{
struct window *w;
u_int i;
struct buffer *b;
for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
if ((w = ARRAY_ITEM(&windows, i)) != NULL) {
if (window_poll(w, *pfd) != 0)
lost_window(w);
else {
b = buffer_create(BUFSIZ);
window_output(w, b);
if (BUFFER_USED(b) != 0) {
write_clients(w, MSG_OUTPUT,
BUFFER_OUT(b), BUFFER_USED(b));
}
buffer_destroy(b);
}
}
(*pfd)++;
}
}
/* Fill client pollfds. */
void
fill_clients(struct pollfd **pfd)
{
struct client *c;
u_int i;
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
if ((c = ARRAY_ITEM(&clients, i)) == NULL)
(*pfd)->fd = -1;
else {
(*pfd)->fd = c->fd;
(*pfd)->events = POLLIN;
if (BUFFER_USED(c->out) > 0)
(*pfd)->events |= POLLOUT;
}
(*pfd)++;
}
}
/* Handle client pollfds. */
void
handle_clients(struct pollfd *(*pfd))
{
struct client *c;
u_int i;
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
if ((c = ARRAY_ITEM(&clients, i)) != NULL) {
if (buffer_poll((*pfd), c->in, c->out) != 0)
lost_client(c);
else
process_client(c);
}
(*pfd)++;
}
}
/* accept(2) and create new client. */
struct client *
accept_client(int srv_fd)
{
struct client *c;
struct sockaddr_storage sa;
socklen_t slen = sizeof sa;
int client_fd, mode;
u_int i;
client_fd = accept(srv_fd, (struct sockaddr *) &sa, &slen);
if (client_fd == -1) {
if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
return (NULL);
log_fatal("accept");
}
if ((mode = fcntl(client_fd, F_GETFL)) == -1)
log_fatal("fcntl");
if (fcntl(client_fd, F_SETFL, mode|O_NONBLOCK) == -1)
log_fatal("fcntl");
c = xmalloc(sizeof *c);
c->fd = client_fd;
c->in = buffer_create(BUFSIZ);
c->out = buffer_create(BUFSIZ);
c->session = NULL;
c->prompt = NULL;
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
if (ARRAY_ITEM(&clients, i) == NULL) {
ARRAY_SET(&clients, i, c);
return (c);
}
}
ARRAY_ADD(&clients, c);
return (c);
}
/* Lost a client. */
void
lost_client(struct client *c)
{
u_int i;
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
if (ARRAY_ITEM(&clients, i) == c)
ARRAY_SET(&clients, i, NULL);
}
close(c->fd);
buffer_destroy(c->in);
buffer_destroy(c->out);
xfree(c);
}
/* Write message command to a client. */
void
write_message(struct client *c, const char *fmt, ...)
{
struct hdr hdr;
va_list ap;
char *msg;
size_t size;
u_int i;
buffer_ensure(c->out, sizeof hdr);
buffer_add(c->out, sizeof hdr);
size = BUFFER_USED(c->out);
input_store_zero(c->out, CODE_CURSOROFF);
input_store_two(c->out, CODE_CURSORMOVE, c->sy, 1);
input_store_one(c->out, CODE_ATTRIBUTES, 2);
input_store16(c->out, 0);
input_store16(c->out, 7);
va_start(ap, fmt);
xvasprintf(&msg, fmt, ap);
buffer_write(c->out, msg, strlen(msg));
xfree(msg);
va_end(ap);
for (i = strlen(msg); i < c->sx; i++)
input_store8(c->out, ' ');
size = BUFFER_USED(c->out) - size;
hdr.code = MSG_OUTPUT;
hdr.size = size;
memcpy(BUFFER_IN(c->out) - size - sizeof hdr, &hdr, sizeof hdr);
hdr.code = MSG_PAUSE;
hdr.size = 0;
buffer_write(c->out, &hdr, sizeof hdr);
buffer_ensure(c->out, sizeof hdr);
buffer_add(c->out, sizeof hdr);
size = BUFFER_USED(c->out);
screen_draw(&c->session->window->screen, c->out, c->sy - 1, c->sy - 1);
size = BUFFER_USED(c->out) - size;
hdr.code = MSG_OUTPUT;
hdr.size = size;
memcpy(BUFFER_IN(c->out) - size - sizeof hdr, &hdr, sizeof hdr);
}
/* Start user input. */
void
user_start(struct client *c, const char *prompt, const char *now,
size_t len, void (*callback)(struct client *, const char *))
{
struct hdr hdr;
size_t size;
u_int i;
c->callback = callback;
c->prompt = prompt;
c->len = len;
if (c->len > c->sx - strlen(c->prompt))
c->len = c->sx - strlen(c->prompt);
c->buf = xmalloc(c->len + 1);
strlcpy(c->buf, now, c->len + 1);
c->idx = strlen(c->buf);
buffer_ensure(c->out, sizeof hdr);
buffer_add(c->out, sizeof hdr);
size = BUFFER_USED(c->out);
input_store_zero(c->out, CODE_CURSOROFF);
input_store_two(c->out, CODE_CURSORMOVE, c->sy, 1);
input_store_one(c->out, CODE_ATTRIBUTES, 2);
input_store16(c->out, 0);
input_store16(c->out, 7);
i = 0;
buffer_write(c->out, c->prompt, strlen(c->prompt));
i += strlen(c->prompt);
if (*c->buf != '\0') {
buffer_write(c->out, c->buf, strlen(c->buf));
i += strlen(c->buf);
}
for (; i < c->sx; i++)
input_store8(c->out, ' ');
input_store_two(c->out,
CODE_CURSORMOVE, c->sy, 1 + strlen(c->prompt) + c->idx);
input_store_zero(c->out, CODE_CURSORON);
size = BUFFER_USED(c->out) - size;
hdr.code = MSG_OUTPUT;
hdr.size = size;
memcpy(BUFFER_IN(c->out) - size - sizeof hdr, &hdr, sizeof hdr);
}
/* Handle user input. */
void
user_input(struct client *c, size_t in)
{
struct hdr hdr;
size_t size;
int key;
u_int i;
buffer_ensure(c->out, sizeof hdr);
buffer_add(c->out, sizeof hdr);
size = BUFFER_USED(c->out);
while (in != 0) {
if (in < 1)
break;
in--;
key = input_extract8(c->in);
if (key == '\e') {
if (in < 2)
log_fatalx("user_input: underflow");
in -= 2;
key = (int16_t) input_extract16(c->in);
}
again:
if (key == '\r') {
screen_draw(&c->session->window->screen,
c->out, c->sy - 1, c->sy - 1);
c->callback(c, c->buf);
c->prompt = NULL;
xfree(c->buf);
break;
}
switch (key) {
case KEYC_LEFT:
if (c->idx > 0)
c->idx--;
input_store_two(c->out, CODE_CURSORMOVE,
c->sy, 1 + strlen(c->prompt) + c->idx);
break;
case KEYC_RIGHT:
if (c->idx < strlen(c->buf))
c->idx++;
input_store_two(c->out, CODE_CURSORMOVE,
c->sy, 1 + strlen(c->prompt) + c->idx);
break;
case KEYC_HOME:
c->idx = 0;
input_store_two(c->out, CODE_CURSORMOVE,
c->sy, 1 + strlen(c->prompt) + c->idx);
break;
case KEYC_LL:
c->idx = strlen(c->buf);
input_store_two(c->out, CODE_CURSORMOVE,
c->sy, 1 + strlen(c->prompt) + c->idx);
break;
case KEYC_BACKSPACE:
if (c->idx == 0)
break;
if (strlen(c->buf) == 0)
break;
if (c->idx == strlen(c->buf))
c->buf[c->idx - 1] = '\0';
else {
memmove(c->buf + c->idx - 1,
c->buf + c->idx, c->len - c->idx);
}
c->idx--;
input_store_one(c->out, CODE_CURSORLEFT, 1);
input_store_one(c->out, CODE_DELETECHARACTER, 1);
input_store_zero(c->out, CODE_CURSOROFF);
input_store_two(c->out, CODE_CURSORMOVE, c->sy, c->sx);
input_store8(c->out, ' ');
input_store_two(c->out, CODE_CURSORMOVE,
c->sy, 1 + strlen(c->prompt) + c->idx);
input_store_zero(c->out, CODE_CURSORON);
break;
case KEYC_DC:
if (strlen(c->buf) == 0)
break;
if (c->idx == strlen(c->buf))
break;
memmove(c->buf + c->idx,
c->buf + c->idx + 1, c->len - c->idx - 1);
input_store_one(c->out, CODE_DELETECHARACTER, 1);
input_store_zero(c->out, CODE_CURSOROFF);
input_store_two(c->out,CODE_CURSORMOVE, c->sy, c->sx);
input_store8(c->out, ' ');
input_store_two(c->out, CODE_CURSORMOVE,
c->sy, 1 + strlen(c->prompt) + c->idx);
input_store_zero(c->out, CODE_CURSORON);
break;
default:
if (key >= ' ' && key != '\177') {
if (c->idx == c->len)
break;
if (strlen(c->buf) == c->len)
break;
memmove(c->buf + c->idx + 1,
c->buf + c->idx, c->len - c->idx);
c->buf[c->idx++] = key;
input_store_one(
c->out, CODE_INSERTCHARACTER, 1);
input_store8(c->out, key);
break;
}
switch (key) {
case '\001':
key = KEYC_HOME;
goto again;
case '\005':
key = KEYC_LL;
goto again;
case '\013':
c->buf[c->idx + 1] = '\0';
input_store_zero(c->out, CODE_CURSOROFF);
i = 1 + strlen(c->prompt) + c->idx;
for (; i < c->sx; i++)
input_store8(c->out, ' ');
input_store_two(c->out, CODE_CURSORMOVE,
c->sy, 1 + strlen(c->prompt) + c->idx);
input_store_zero(c->out, CODE_CURSORON);
break;
}
}
}
size = BUFFER_USED(c->out) - size;
if (size != 0) {
hdr.code = MSG_OUTPUT;
hdr.size = size;
memcpy(BUFFER_IN(c->out) - size - sizeof hdr, &hdr, sizeof hdr);
} else
buffer_reverse_add(c->out, sizeof hdr);
}
/* Write command to a client. */
void
write_client(struct client *c, u_int cmd, void *buf, size_t len)
{
struct hdr hdr;
hdr.code = cmd;
hdr.size = len;
buffer_write(c->out, &hdr, sizeof hdr);
if (buf != NULL)
buffer_write(c->out, buf, len);
}
/* Write command to a client with two buffers. */
void
write_client2(struct client *c,
u_int cmd, void *buf1, size_t len1, void *buf2, size_t len2)
{
struct hdr hdr;
hdr.code = cmd;
hdr.size = len1 + len2;
buffer_write(c->out, &hdr, sizeof hdr);
if (buf1 != NULL)
buffer_write(c->out, buf1, len1);
if (buf2 != NULL)
buffer_write(c->out, buf2, len2);
}
/* Write command to all clients attached to a specific window. */
void
write_clients(struct window *w, u_int cmd, void *buf, size_t len)
{
struct client *c;
struct hdr hdr;
u_int i;
hdr.code = cmd;
hdr.size = len;
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
c = ARRAY_ITEM(&clients, i);
if (c != NULL && c->session != NULL) {
if (c->session->window == w) {
buffer_write(c->out, &hdr, sizeof hdr);
if (buf != NULL)
buffer_write(c->out, buf, len);
}
}
}
}
/* Lost window: move clients on to next window. */
void
lost_window(struct window *w)
{
struct client *c;
u_int i;
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
c = ARRAY_ITEM(&clients, i);
if (c != NULL && c->session != NULL) {
if (session_has(c->session, w)) {
if (session_detach(c->session, w) != 0)
write_client(c, MSG_EXIT, NULL, 0);
changed_window(c);
}
}
}
}
/* Changed client window. */
void
changed_window(struct client *c)
{
struct window *w;
w = c->session->window;
if (c->sx != w->screen.sx || c->sy != w->screen.sy)
window_resize(w, c->sx, c->sy);
draw_client(c, 0, c->sy - 1);
}
/* Draw window on client. */
void
draw_client(struct client *c, u_int py_upper, u_int py_lower)
{
struct hdr hdr;
size_t size;
buffer_ensure(c->out, sizeof hdr);
buffer_add(c->out, sizeof hdr);
size = BUFFER_USED(c->out);
screen_draw(&c->session->window->screen, c->out, py_upper, py_lower);
size = BUFFER_USED(c->out) - size;
log_debug("redrawing screen, %zu bytes", size);
if (size != 0) {
hdr.code = MSG_OUTPUT;
hdr.size = size;
memcpy(
BUFFER_IN(c->out) - size - sizeof hdr, &hdr, sizeof hdr);
} else
buffer_reverse_add(c->out, sizeof hdr);
}
/* Process a command from the client. */
void
process_client(struct client *c)
{
struct hdr hdr;
if (BUFFER_USED(c->in) < sizeof hdr)
return;
memcpy(&hdr, BUFFER_OUT(c->in), sizeof hdr);
if (BUFFER_USED(c->in) < (sizeof hdr) + hdr.size)
return;
buffer_remove(c->in, sizeof hdr);
switch (hdr.code) {
case MSG_IDENTIFY:
process_identify_msg(c, &hdr);
break;
case MSG_CREATE:
process_create_msg(c, &hdr);
break;
case MSG_NEXT:
process_next_msg(c, &hdr);
break;
case MSG_PREVIOUS:
process_previous_msg(c, &hdr);
break;
case MSG_SIZE:
process_size_msg(c, &hdr);
break;
case MSG_INPUT:
process_input_msg(c, &hdr);
break;
case MSG_REFRESH:
process_refresh_msg(c, &hdr);
break;
case MSG_SELECT:
process_select_msg(c, &hdr);
break;
case MSG_SESSIONS:
process_sessions_msg(c, &hdr);
break;
case MSG_WINDOWS:
process_windows_msg(c, &hdr);
break;
case MSG_RENAME:
process_rename_msg(c, &hdr);
break;
}
}
/* Identify message from client. */
void
process_identify_msg(struct client *c, struct hdr *hdr)
{
struct identify_data data;
if (hdr->size != sizeof data)
log_fatalx("bad MSG_IDENTIFY size");
buffer_read(c->in, &data, hdr->size);
c->sx = data.sx;
if (c->sx == 0)
c->sx = 80;
c->sy = data.sy;
if (c->sy == 0)
c->sy = 25;
/* Try and find session or create if not found. */
c->session = session_find(data.name);
if (c->session == NULL) {
c->session =
session_create(data.name, "/bin/ksh -l", c->sx, c->sy);
}
if (c->session == NULL)
log_fatalx("process_identify_msg: session_create failed");
draw_client(c, 0, c->sy - 1);
}
/* Create message from client. */
void
process_create_msg(struct client *c, struct hdr *hdr)
{
if (c->session == NULL)
log_fatalx("MSG_CREATE before identified");
if (hdr->size != 0)
log_fatalx("bad MSG_CREATE size");
if (session_new(c->session, "/bin/ksh -l", c->sx, c->sy) != 0)
log_fatalx("process_create_msg: session_new failed");
draw_client(c, 0, c->sy - 1);
}
/* Next message from client. */
void
process_next_msg(struct client *c, struct hdr *hdr)
{
if (c->session == NULL)
log_fatalx("MSG_NEXT before identified");
if (hdr->size != 0)
log_fatalx("bad MSG_NEXT size");
if (session_next(c->session) == 0)
changed_window(c);
else
write_message(c, "No next window");
}
/* Previous message from client. */
void
process_previous_msg(struct client *c, struct hdr *hdr)
{
if (c->session == NULL)
log_fatalx("MSG_PREVIOUS before identified");
if (hdr->size != 0)
log_fatalx("bad MSG_PREVIOUS size");
if (session_previous(c->session) == 0)
changed_window(c);
else
write_message(c, "No previous window");
}
/* Size message from client. */
void
process_size_msg(struct client *c, struct hdr *hdr)
{
struct size_data data;
if (c->session == NULL)
log_fatalx("MSG_SIZE before identified");
if (hdr->size != sizeof data)
log_fatalx("bad MSG_SIZE size");
buffer_read(c->in, &data, hdr->size);
c->sx = data.sx;
if (c->sx == 0)
c->sx = 80;
c->sy = data.sy;
if (c->sy == 0)
c->sy = 25;
if (window_resize(c->session->window, c->sx, c->sy) != 0)
draw_client(c, 0, c->sy - 1);
}
/* Input message from client. */
void
process_input_msg(struct client *c, struct hdr *hdr)
{
if (c->session == NULL)
log_fatalx("MSG_INPUT before identified");
if (c->prompt == NULL)
window_input(c->session->window, c->in, hdr->size);
else
user_input(c, hdr->size);
}
/* Refresh message from client. */
void
process_refresh_msg(struct client *c, struct hdr *hdr)
{
struct refresh_data data;
if (c->session == NULL)
log_fatalx("MSG_REFRESH before identified");
if (hdr->size != 0 && hdr->size != sizeof data)
log_fatalx("bad MSG_REFRESH size");
draw_client(c, 0, c->sy - 1);
}
/* Select message from client. */
void
process_select_msg(struct client *c, struct hdr *hdr)
{
struct select_data data;
if (c->session == NULL)
log_fatalx("MSG_SELECT before identified");
if (hdr->size != sizeof data)
log_fatalx("bad MSG_SELECT size");
buffer_read(c->in, &data, hdr->size);
if (c->session == NULL)
return;
if (session_select(c->session, data.idx) == 0)
changed_window(c);
else
write_message(c, "Window %u not present", data.idx);
}
/* Sessions message from client. */
void
process_sessions_msg(struct client *c, struct hdr *hdr)
{
struct sessions_data data;
struct sessions_entry entry;
struct session *s;
u_int i, j;
if (hdr->size != sizeof data)
log_fatalx("bad MSG_SESSIONS size");
buffer_read(c->in, &data, hdr->size);
data.sessions = 0;
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
if (ARRAY_ITEM(&sessions, i) != NULL)
data.sessions++;
}
write_client2(c, MSG_SESSIONS,
&data, sizeof data, NULL, data.sessions * sizeof entry);
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
s = ARRAY_ITEM(&sessions, i);
if (s == NULL)
continue;
strlcpy(entry.name, s->name, sizeof entry.name);
entry.tim = s->tim;
entry.windows = 0;
for (j = 0; j < ARRAY_LENGTH(&s->windows); j++) {
if (ARRAY_ITEM(&s->windows, j) != NULL)
entry.windows++;
}
buffer_write(c->out, &entry, sizeof entry);
}
}
/* Windows message from client. */
void
process_windows_msg(struct client *c, struct hdr *hdr)
{
struct windows_data data;
struct windows_entry entry;
struct session *s;
struct window *w;
u_int i;
if (hdr->size != sizeof data)
log_fatalx("bad MSG_WINDOWS size");
buffer_read(c->in, &data, hdr->size);
s = session_find(data.name);
if (s == NULL) {
data.windows = 0;
write_client(c, MSG_WINDOWS, &data, sizeof data);
return;
}
data.windows = 0;
for (i = 0; i < ARRAY_LENGTH(&s->windows); i++) {
if (ARRAY_ITEM(&windows, i) != NULL)
data.windows++;
}
write_client2(c, MSG_WINDOWS,
&data, sizeof data, NULL, data.windows * sizeof entry);
for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
w = ARRAY_ITEM(&windows, i);
if (w == NULL)
continue;
entry.idx = i;
strlcpy(entry.name, w->name, sizeof entry.name);
strlcpy(entry.title, w->screen.title, sizeof entry.title);
if (ttyname_r(w->fd, entry.tty, sizeof entry.tty) != 0)
*entry.tty = '\0';
buffer_write(c->out, &entry, sizeof entry);
}
}
/* Rename message from client. */
void
process_rename_msg(struct client *c, struct hdr *hdr)
{
if (c->session == NULL)
log_fatalx("MSG_RENAME before identified");
if (hdr->size != 0)
log_fatalx("bad MSG_RENAME size");
user_start(c, "Window name: ",
c->session->window->name, MAXNAMELEN, rename_callback);
}
/* Callback for rename. */
void
rename_callback(struct client *c, const char *string)
{
strlcpy(
c->session->window->name, string, sizeof c->session->window->name);
}

189
session.c Normal file
View File

@ -0,0 +1,189 @@
/* $Id: session.c,v 1.1.1.1 2007-07-09 19:04:12 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <string.h>
#include "tmux.h"
/* Global session list. */
struct sessions sessions;
/* Find session by name. */
struct session *
session_find(const char *name)
{
struct session *s;
u_int i;
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
s = ARRAY_ITEM(&sessions, i);
if (s != NULL && strcmp(s->name, name) == 0)
return (s);
}
return (NULL);
}
/* Create a new session. */
struct session *
session_create(const char *name, const char *cmd, u_int sx, u_int sy)
{
struct session *s;
u_int i;
s = xmalloc(sizeof *s);
s->tim = time(NULL);
strlcpy(s->name, name, sizeof s->name);
ARRAY_INIT(&s->windows);
if (session_new(s, cmd, sx, sy) != 0) {
xfree(s);
return (NULL);
}
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
s = ARRAY_ITEM(&sessions, i);
if (s == NULL) {
ARRAY_SET(&sessions, i, s);
return (s);
}
}
ARRAY_ADD(&sessions, s);
return (s);
}
/* Destroy a session. */
void
session_destroy(struct session *s)
{
u_int i;
if (session_index(s, &i) != 0)
log_fatalx("session not found");
ARRAY_REMOVE(&sessions, i);
while (!ARRAY_EMPTY(&s->windows))
window_remove(&s->windows, ARRAY_FIRST(&s->windows));
xfree(s);
}
/* Find session index. */
int
session_index(struct session *s, u_int *i)
{
for (*i = 0; *i < ARRAY_LENGTH(&sessions); (*i)++) {
if (s == ARRAY_ITEM(&sessions, *i))
return (0);
}
return (-1);
}
/* Create a new window on a session. */
int
session_new(struct session *s, const char *cmd, u_int sx, u_int sy)
{
struct window *w;
if ((w = window_create(cmd, sx, sy)) == NULL)
return (-1);
session_attach(s, w);
s->window = w;
return (0);
}
/* Attach a window to a session. */
void
session_attach(struct session *s, struct window *w)
{
window_add(&s->windows, w);
}
/* Detach a window from a session. */
int
session_detach(struct session *s, struct window *w)
{
if (s->window == w) {
if (session_next(s) != 0)
session_previous(s);
}
window_remove(&s->windows, w);
if (ARRAY_EMPTY(&s->windows)) {
session_destroy(s);
return (1);
}
return (0);
}
/* Return if session has window. */
int
session_has(struct session *s, struct window *w)
{
u_int i;
return (window_index(&s->windows, w, &i) == 0);
}
/* Move session to next window. */
int
session_next(struct session *s)
{
struct window *w;
if (s->window == NULL)
return (-1);
w = window_next(&s->windows, s->window);
if (w == NULL)
return (-1);
s->window = w;
return (0);
}
/* Move session to previous window. */
int
session_previous(struct session *s)
{
struct window *w;
if (s->window == NULL)
return (-1);
w = window_previous(&s->windows, s->window);
if (w == NULL)
return (-1);
s->window = w;
return (0);
}
/* Move session to specific window. */
int
session_select(struct session *s, u_int i)
{
struct window *w;
w = window_at(&s->windows, i);
if (w == NULL)
return (-1);
s->window = w;
return (0);
}

514
tmux.c Normal file
View File

@ -0,0 +1,514 @@
/* $Id: tmux.c,v 1.1.1.1 2007-07-09 19:03:33 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <getopt.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "tmux.h"
#ifdef DEBUG
const char *malloc_options = "AFGJPX";
#endif
int connect_server(void);
int process_server(struct buffer *);
int process_local(struct buffer *, struct buffer *);
void sighandler(int);
__dead void usage(void);
__dead void main_list(char *);
void process_list(struct buffer *, const char *);
/* SIGWINCH received flag. */
volatile sig_atomic_t sigwinch;
/* SIGTERM received flag. */
volatile sig_atomic_t sigterm;
/* Debug output level. */
int debug_level;
/* Path to server socket. */
char socket_path[MAXPATHLEN];
__dead void
usage(void)
{
fprintf(stderr,
"usage: %s [-v] [-n name] [-s path]\n", __progname);
exit(1);
}
void
sighandler(int sig)
{
switch (sig) {
case SIGWINCH:
sigwinch = 1;
break;
case SIGTERM:
sigterm = 1;
break;
case SIGCHLD:
waitpid(WAIT_ANY, NULL, WNOHANG);
break;
}
}
int
main(int argc, char **argv)
{
int opt, srv_fd, loc_fd, mode, listf, n;
char *path, name[MAXNAMELEN];
FILE *f;
struct buffer *srv_in, *srv_out, *loc_in, *loc_out;
struct pollfd pfds[2];
struct hdr hdr;
struct identify_data id;
struct size_data sd;
struct winsize ws;
struct sigaction act;
struct stat sb;
*name = '\0';
path = NULL;
listf = 0;
while ((opt = getopt(argc, argv, "ln:s:v?")) != EOF) {
switch (opt) {
case 'l':
listf = 1;
break;
case 'n':
if (strlcpy(name, optarg, sizeof name) >= sizeof name)
errx(1, "name too long");
break;
case 's':
path = xstrdup(optarg);
break;
case 'v':
debug_level++;
break;
case '?':
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc != 0)
usage();
/* Sort out socket path. */
if (path == NULL) {
xasprintf(&path,
"%s/%s-%lu", _PATH_TMP, __progname, (u_long) getuid());
}
if (realpath(path, socket_path) == NULL)
err(1, "realpath");
xfree(path);
/* Skip to list function if listing. */
if (listf) {
if (*name == '\0')
main_list(NULL);
else
main_list(name);
}
/* And fill name. */
if (*name == '\0')
xsnprintf(name, sizeof name, "s-%lu", (u_long) getpid());
/* Check stdin/stdout. */
if (!isatty(STDIN_FILENO))
errx(1, "stdin is not a tty");
if (!isatty(STDOUT_FILENO))
errx(1, "stdout is not a tty");
/* Set up signal handlers. */
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART;
act.sa_handler = SIG_IGN;
if (sigaction(SIGPIPE, &act, NULL) != 0)
err(1, "sigaction");
if (sigaction(SIGUSR1, &act, NULL) != 0)
err(1, "sigaction");
if (sigaction(SIGUSR2, &act, NULL) != 0)
err(1, "sigaction");
if (sigaction(SIGINT, &act, NULL) != 0)
err(1, "sigaction");
if (sigaction(SIGTSTP, &act, NULL) != 0)
err(1, "sigaction");
if (sigaction(SIGQUIT, &act, NULL) != 0)
err(1, "sigaction");
act.sa_handler = sighandler;
if (sigaction(SIGWINCH, &act, NULL) != 0)
err(1, "sigaction");
if (sigaction(SIGTERM, &act, NULL) != 0)
err(1, "sigaction");
if (sigaction(SIGCHLD, &act, NULL) != 0)
err(1, "sigaction");
/* Start server if necessary. */
if (stat(socket_path, &sb) != 0) {
if (errno != ENOENT)
err(1, "%s", socket_path);
else {
if (server_start() != 0)
errx(1, "couldn't start server");
sleep(1);
}
} else {
if (!S_ISSOCK(sb.st_mode))
errx(1, "%s: not a socket", socket_path);
}
/* Connect to server. */
if ((srv_fd = connect_server()) == -1)
errx(1, "couldn't find server");
if ((mode = fcntl(srv_fd, F_GETFL)) == -1)
err(1, "fcntl");
if (fcntl(srv_fd, F_SETFL, mode|O_NONBLOCK) == -1)
err(1, "fcntl");
srv_in = buffer_create(BUFSIZ);
srv_out = buffer_create(BUFSIZ);
/* Find window size. */
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1)
err(1, "ioctl(TIOCGWINSZ)");
/* Send initial data. */
hdr.code = MSG_IDENTIFY;
hdr.size = sizeof id;
buffer_write(srv_out, &hdr, sizeof hdr);
strlcpy(id.name, name, sizeof id.name);
id.sx = ws.ws_col;
id.sy = ws.ws_row;
buffer_write(srv_out, &id, hdr.size);
/* Start logging to file. */
if (debug_level > 0) {
xasprintf(&path,
"%s-client-%ld.log", __progname, (long) getpid());
f = fopen(path, "w");
log_open(f, LOG_USER, debug_level);
xfree(path);
}
/* Initialise terminal. */
loc_fd = local_init(&loc_in, &loc_out);
setproctitle("client (%s)", name);
/* Main loop. */
n = 0;
while (!sigterm) {
/* Handle SIGWINCH if necessary. */
if (sigwinch) {
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1)
log_fatal("ioctl(TIOCGWINSZ)");
hdr.code = MSG_SIZE;
hdr.size = sizeof sd;
buffer_write(srv_out, &hdr, sizeof hdr);
sd.sx = ws.ws_col;
sd.sy = ws.ws_row;
buffer_write(srv_out, &sd, hdr.size);
sigwinch = 0;
}
/* Set up pollfds. */
pfds[0].fd = srv_fd;
pfds[0].events = POLLIN;
if (BUFFER_USED(srv_out) > 0)
pfds[0].events |= POLLOUT;
pfds[1].fd = loc_fd;
pfds[1].events = POLLIN;
if (BUFFER_USED(loc_out) > 0)
pfds[1].events |= POLLOUT;
/* Do the poll. */
if (poll(pfds, 2, INFTIM) == -1) {
if (errno == EAGAIN || errno == EINTR)
continue;
log_fatal("poll");
}
/* Read/write from sockets. */
if (buffer_poll(&pfds[0], srv_in, srv_out) != 0)
goto server_dead;
if (buffer_poll(&pfds[1], loc_in, loc_out) != 0)
log_fatalx("lost local socket");
/* Output flushed; pause if requested. */
if (n)
usleep(750000);
/* Process any data. */
if ((n = process_server(srv_in)) == -1)
break;
if (process_local(loc_in, srv_out) == -1)
break;
}
local_done();
if (!sigterm)
printf("[detached]\n");
else
printf("[terminated]\n");
exit(0);
server_dead:
local_done();
printf("[lost server]\n");
exit(1);
}
/* Connect to server socket from PID. */
int
connect_server(void)
{
int fd;
struct sockaddr_un sa;
size_t sz;
memset(&sa, 0, sizeof sa);
sa.sun_family = AF_UNIX;
sz = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
if (sz >= sizeof sa.sun_path) {
errno = ENAMETOOLONG;
return (-1);
}
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
return (-1);
if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1)
return (-1);
return (fd);
}
/* Handle data from server. */
int
process_server(struct buffer *srv_in)
{
struct hdr hdr;
for (;;) {
if (BUFFER_USED(srv_in) < sizeof hdr)
break;
memcpy(&hdr, BUFFER_OUT(srv_in), sizeof hdr);
if (BUFFER_USED(srv_in) < (sizeof hdr) + hdr.size)
break;
buffer_remove(srv_in, sizeof hdr);
switch (hdr.code) {
case MSG_OUTPUT:
local_output(srv_in, hdr.size);
break;
case MSG_PAUSE:
if (hdr.size != 0)
log_fatalx("bad MSG_PAUSE size");
return (1);
case MSG_EXIT:
return (-1);
}
}
return (0);
}
/* Handle data from local terminal. */
int
process_local(struct buffer *loc_in, struct buffer *srv_out)
{
struct buffer *b;
struct hdr hdr;
size_t size;
int n, key;
n = 0;
b = buffer_create(BUFSIZ);
while ((key = local_key(&size)) != KEYC_NONE) {
log_debug("key code: %d", key);
if (key == cmd_prefix) {
if ((key = local_key(NULL)) == KEYC_NONE) {
buffer_reverse_remove(loc_in, size);
break;
}
n = cmd_execute(key, srv_out);
break;
}
input_store8(b, '\e');
input_store16(b, (uint16_t) key);
}
if (BUFFER_USED(b) == 0) {
buffer_destroy(b);
return (n);
}
log_debug("transmitting %zu bytes of input", BUFFER_USED(b));
hdr.code = MSG_INPUT;
hdr.size = BUFFER_USED(b);
buffer_write(srv_out, &hdr, sizeof hdr);
buffer_write(srv_out, BUFFER_OUT(b), BUFFER_USED(b));
buffer_destroy(b);
return (n);
}
/* List sessions or windows. */
__dead void
main_list(char *name)
{
struct sessions_data sd;
struct windows_data wd;
int srv_fd, mode;
struct buffer *srv_in, *srv_out;
struct pollfd pfd;
struct hdr hdr;
/* Connect to server. */
if ((srv_fd = connect_server()) == -1)
errx(1, "couldn't find server");
if ((mode = fcntl(srv_fd, F_GETFL)) == -1)
err(1, "fcntl");
if (fcntl(srv_fd, F_SETFL, mode|O_NONBLOCK) == -1)
err(1, "fcntl");
srv_in = buffer_create(BUFSIZ);
srv_out = buffer_create(BUFSIZ);
/* Send query data. */
if (name == NULL) {
hdr.code = MSG_SESSIONS;
hdr.size = sizeof sd;
buffer_write(srv_out, &hdr, sizeof hdr);
buffer_write(srv_out, &sd, hdr.size);
} else {
hdr.code = MSG_WINDOWS;
hdr.size = sizeof wd;
buffer_write(srv_out, &hdr, sizeof hdr);
strlcpy(wd.name, name, sizeof wd.name);
buffer_write(srv_out, &wd, hdr.size);
}
/* Main loop. */
for (;;) {
/* Set up pollfd. */
pfd.fd = srv_fd;
pfd.events = POLLIN;
if (BUFFER_USED(srv_out) > 0)
pfd.events |= POLLOUT;
/* Do the poll. */
if (poll(&pfd, 1, INFTIM) == -1) {
if (errno == EAGAIN || errno == EINTR)
continue;
err(1, "poll");
}
/* Read/write from sockets. */
if (buffer_poll(&pfd, srv_in, srv_out) != 0)
errx(1, "lost server");
/* Process data. */
process_list(srv_in, name);
}
}
void
process_list(struct buffer *srv_in, const char *name)
{
struct sessions_data sd;
struct sessions_entry se;
struct windows_data wd;
struct windows_entry we;
struct hdr hdr;
char *tim;
for (;;) {
if (BUFFER_USED(srv_in) < sizeof hdr)
break;
memcpy(&hdr, BUFFER_OUT(srv_in), sizeof hdr);
if (BUFFER_USED(srv_in) < (sizeof hdr) + hdr.size)
break;
buffer_remove(srv_in, sizeof hdr);
switch (hdr.code) {
case MSG_SESSIONS:
if (hdr.size < sizeof sd)
errx(1, "bad MSG_SESSIONS size");
buffer_read(srv_in, &sd, sizeof sd);
hdr.size -= sizeof sd;
if (sd.sessions == 0 && hdr.size == 0)
exit(0);
if (hdr.size < sd.sessions * sizeof se)
errx(1, "bad MSG_SESSIONS size");
while (sd.sessions-- > 0) {
buffer_read(srv_in, &se, sizeof se);
tim = ctime(&se.tim);
*strchr(tim, '\n') = '\0';
printf("%s: %u windows (created %s)\n",
se.name, se.windows, tim);
}
exit(0);
case MSG_WINDOWS:
if (hdr.size < sizeof wd)
errx(1, "bad MSG_WINDOWS size");
buffer_read(srv_in, &wd, sizeof wd);
hdr.size -= sizeof wd;
if (wd.windows == 0 && hdr.size == 0)
errx(1, "session \"%s\" not found", name);
if (hdr.size < wd.windows * sizeof we)
errx(1, "bad MSG_WINDOWS size");
while (wd.windows-- > 0) {
buffer_read(srv_in, &we, sizeof we);
if (*we.title != '\0') {
printf("%u: %s \"%s\" (%s)\n",
we.idx, we.name, we.title, we.tty);
} else {
printf("%u: %s (%s)\n",
we.idx, we.name, we.tty);
}
}
exit(0);
}
}
}

583
tmux.h Normal file
View File

@ -0,0 +1,583 @@
/* $Id: tmux.h,v 1.1.1.1 2007-07-09 19:03:50 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef NSCR_H
#define NSCR_H
#include <sys/param.h>
#include <sys/queue.h>
#include <poll.h>
#include <stdarg.h>
#include <stdio.h>
#include "array.h"
extern char *__progname;
#define MAXNAMELEN 32
#define MAXTITLELEN 192
/* Definition to shut gcc up about unused arguments. */
#define unused __attribute__ ((unused))
/* Attribute to make gcc check printf-like arguments. */
#define printflike1 __attribute__ ((format (printf, 1, 2)))
#define printflike2 __attribute__ ((format (printf, 2, 3)))
#define printflike3 __attribute__ ((format (printf, 3, 4)))
#define printflike4 __attribute__ ((format (printf, 4, 5)))
/* Ensure buffer size. */
#define ENSURE_SIZE(buf, len, size) do { \
(buf) = ensure_size(buf, &(len), 1, size); \
} while (0)
#define ENSURE_SIZE2(buf, len, nmemb, size) do { \
(buf) = ensure_size(buf, &(len), nmemb, size); \
} while (0)
#define ENSURE_FOR(buf, len, size, adj) do { \
(buf) = ensure_for(buf, &(len), size, adj); \
} while (0)
/* Buffer macros. */
#define BUFFER_USED(b) ((b)->size)
#define BUFFER_FREE(b) ((b)->space - (b)->off - (b)->size)
#define BUFFER_IN(b) ((b)->base + (b)->off + (b)->size)
#define BUFFER_OUT(b) ((b)->base + (b)->off)
/* Buffer structure. */
struct buffer {
u_char *base; /* buffer start */
size_t space; /* total size of buffer */
size_t size; /* size of data in buffer */
size_t off; /* offset of data in buffer */
};
/* Key codes. ncurses defines KEY_*. Grrr. */
#define KEYC_NONE 256
#define KEYC_A1 -1
#define KEYC_A3 -2
#define KEYC_B2 -3
#define KEYC_BACKSPACE -4
#define KEYC_BEG -5
#define KEYC_BTAB -6
#define KEYC_C1 -7
#define KEYC_C3 -8
#define KEYC_CANCEL -9
#define KEYC_CATAB -10
#define KEYC_CLEAR -11
#define KEYC_CLOSE -12
#define KEYC_COMMAND -13
#define KEYC_COPY -14
#define KEYC_CREATE -15
#define KEYC_CTAB -16
#define KEYC_DC -17
#define KEYC_DL -18
#define KEYC_DOWN -19
#define KEYC_EIC -20
#define KEYC_END -21
#define KEYC_ENTER -22
#define KEYC_EOL -23
#define KEYC_EOS -24
#define KEYC_EXIT -25
#define KEYC_F0 -26
#define KEYC_F1 -27
#define KEYC_F10 -28
#define KEYC_F11 -29
#define KEYC_F12 -30
#define KEYC_F13 -31
#define KEYC_F14 -32
#define KEYC_F15 -33
#define KEYC_F16 -34
#define KEYC_F17 -35
#define KEYC_F18 -36
#define KEYC_F19 -37
#define KEYC_F2 -38
#define KEYC_F20 -39
#define KEYC_F21 -40
#define KEYC_F22 -41
#define KEYC_F23 -42
#define KEYC_F24 -43
#define KEYC_F25 -44
#define KEYC_F26 -45
#define KEYC_F27 -46
#define KEYC_F28 -47
#define KEYC_F29 -48
#define KEYC_F3 -49
#define KEYC_F30 -50
#define KEYC_F31 -51
#define KEYC_F32 -52
#define KEYC_F33 -53
#define KEYC_F34 -54
#define KEYC_F35 -55
#define KEYC_F36 -56
#define KEYC_F37 -57
#define KEYC_F38 -58
#define KEYC_F39 -59
#define KEYC_F4 -60
#define KEYC_F40 -61
#define KEYC_F41 -62
#define KEYC_F42 -63
#define KEYC_F43 -64
#define KEYC_F44 -65
#define KEYC_F45 -66
#define KEYC_F46 -67
#define KEYC_F47 -68
#define KEYC_F48 -69
#define KEYC_F49 -70
#define KEYC_F5 -71
#define KEYC_F50 -72
#define KEYC_F51 -73
#define KEYC_F52 -74
#define KEYC_F53 -75
#define KEYC_F54 -76
#define KEYC_F55 -77
#define KEYC_F56 -78
#define KEYC_F57 -79
#define KEYC_F58 -80
#define KEYC_F59 -81
#define KEYC_F6 -82
#define KEYC_F60 -83
#define KEYC_F61 -84
#define KEYC_F62 -85
#define KEYC_F63 -86
#define KEYC_F7 -87
#define KEYC_F8 -88
#define KEYC_F9 -89
#define KEYC_FIND -90
#define KEYC_HELP -91
#define KEYC_HOME -92
#define KEYC_IC -93
#define KEYC_IL -94
#define KEYC_LEFT -95
#define KEYC_LL -96
#define KEYC_MARK -97
#define KEYC_MESSAGE -98
#define KEYC_MOVE -99
#define KEYC_NEXT -100
#define KEYC_NPAGE -101
#define KEYC_OPEN -102
#define KEYC_OPTIONS -103
#define KEYC_PPAGE -104
#define KEYC_PREVIOUS -105
#define KEYC_PRINT -106
#define KEYC_REDO -107
#define KEYC_REFERENCE -108
#define KEYC_REFRESH -109
#define KEYC_REPLACE -110
#define KEYC_RESTART -111
#define KEYC_RESUME -112
#define KEYC_RIGHT -113
#define KEYC_SAVE -114
#define KEYC_SBEG -115
#define KEYC_SCANCEL -116
#define KEYC_SCOMMAND -117
#define KEYC_SCOPY -118
#define KEYC_SCREATE -119
#define KEYC_SDC -120
#define KEYC_SDL -121
#define KEYC_SELECT -122
#define KEYC_SEND -123
#define KEYC_SEOL -124
#define KEYC_SEXIT -125
#define KEYC_SF -126
#define KEYC_SFIND -127
#define KEYC_SHELP -128
#define KEYC_SHOME -129
#define KEYC_SIC -130
#define KEYC_SLEFT -131
#define KEYC_SMESSAGE -132
#define KEYC_SMOVE -133
#define KEYC_SNEXT -134
#define KEYC_SOPTIONS -135
#define KEYC_SPREVIOUS -136
#define KEYC_SPRINT -137
#define KEYC_SR -138
#define KEYC_SREDO -139
#define KEYC_SREPLACE -140
#define KEYC_SRIGHT -141
#define KEYC_SRSUME -142
#define KEYC_SSAVE -143
#define KEYC_SSUSPEND -144
#define KEYC_STAB -145
#define KEYC_SUNDO -146
#define KEYC_SUSPEND -147
#define KEYC_UNDO -148
#define KEYC_UP -149
#define KEYC_MOUSE -150
/* Escape codes. */
/*
AL=\E[%dL parm_insert_line
DC=\E[%dP parm_dch
DL=\E[%dM parm_delete_line
DO=\E[%dB parm_down_cursor
IC=\E[%d@ parm_ich
Km=\E[M key_mouse
LE=\E[%dD parm_left_cursor
RI=\E[%dC parm_right_cursor
UP=\E[%dA parm_up_cursor
ac=++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~
acs_chars
ae=^O exit_alt_charset_mode
al=\E[L insert_line
as=^N enter_alt_charset_mode
bl=^G bell
bt=\E[Z back_tab
cb=\E[1K clear_bol
cd=\E[J clear_eos
ce=\E[K clear_eol
cl=\E[H\E[J clear_screen
cm=\E[%i%d;%dH cursor_address
cr=^M carriage_return
cs=\E[%i%d;%dr change_scroll_region
ct=\E[3g clear_all_tabs
dc=\E[P delete_character
dl=\E[M delete_line
do=^J cursor_down
eA=\E(B\E)0 ena_acs
ei=\E[4l exit_insert_mode
ho=\E[H cursor_home
im=\E[4h enter_insert_mode
is=\E)0 init_2string
le=^H cursor_left
mb=\E[5m enter_blink_mode
md=\E[1m enter_bold_mode
me=\E[m exit_attrbute_mode
mr=\E[7m enter_reverse_mode
nd=\E[C cursor_right
nw=\EE newline
rc=\E8 restore_cursor
rs=\Ec reset_string
sc=\E7 save_cursor
se=\E[23m exit_standout_mode
sf=^J scroll_forward
so=\E[3m enter_standout_mode
sr=\EM scroll_reverse
st=\EH set_tab
ta=^I tab
ue=\E[24m exit_underline_mode
up=\EM cursor_up
s=\E[4m
vb=\Eg flash_screen
ve=\E[34h\E[?25h
cursor_normal
vi=\E[?25l cursor_invisible
vs=\E[34l cursor_visible
E0=\E(B
S0=\E(%p1%c
*/
/* Translated escape codes. */
#define CODE_CURSORUP 0
#define CODE_CURSORDOWN 1
#define CODE_CURSORRIGHT 2
#define CODE_CURSORLEFT 3
#define CODE_INSERTCHARACTER 4
#define CODE_DELETECHARACTER 5
#define CODE_INSERTLINE 6
#define CODE_DELETELINE 7
#define CODE_CLEARLINE 8
#define CODE_CLEARSCREEN 9
#define CODE_CLEARENDOFLINE 10
#define CODE_CLEARENDOFSCREEN 11
#define CODE_CLEARSTARTOFLINE 12
#define CODE_CURSORMOVE 13
#define CODE_ATTRIBUTES 14
#define CODE_CURSOROFF 15
#define CODE_CURSORON 16
#define CODE_CURSORUPSCROLL 17
#define CODE_CURSORDOWNSCROLL 18
#define CODE_SCROLLREGION 19
#define CODE_INSERTON 20
#define CODE_INSERTOFF 21
#define CODE_KCURSOROFF 22
#define CODE_KCURSORON 23
#define CODE_KKEYPADOFF 24
#define CODE_KKEYPADON 25
#define CODE_TITLE 26
/* Message codes. */
#define MSG_IDENTIFY 0
#define MSG_CREATE 1
#define MSG_EXIT 2
#define MSG_SIZE 3
#define MSG_NEXT 4
#define MSG_PREVIOUS 5
#define MSG_INPUT 6 /* input from client to server */
#define MSG_OUTPUT 7 /* output from server to client */
#define MSG_REFRESH 8
#define MSG_SELECT 9
#define MSG_SESSIONS 10
#define MSG_WINDOWS 11
#define MSG_PAUSE 12
#define MSG_RENAME 13
struct identify_data {
char name[MAXNAMELEN];
u_int sx;
u_int sy;
};
struct sessions_data {
u_int sessions;
};
struct sessions_entry {
char name[MAXNAMELEN];
time_t tim;
u_int windows;
};
struct windows_data {
char name[MAXNAMELEN];
u_int windows;
};
struct windows_entry {
u_int idx;
char tty[TTY_NAME_MAX];
char name[MAXNAMELEN];
char title[MAXTITLELEN];
};
struct size_data {
u_int sx;
u_int sy;
};
struct select_data {
u_int idx;
};
struct refresh_data {
u_int py_upper;
u_int py_lower;
};
/* Message header structure. */
struct hdr {
u_int code;
size_t size;
};
/* Attributes. */
#define ATTR_BRIGHT 0x1
#define ATTR_DIM 0x2
#define ATTR_UNDERSCORE 0x4
#define ATTR_BLINK 0x8
#define ATTR_REVERSE 0x10
#define ATTR_HIDDEN 0x20
#define ATTR_ITALICS 0x40
/* Modes. */
#define MODE_CURSOR 0x1
#define MODE_INSERT 0x2
#define MODE_KCURSOR 0x4
#define MODE_KKEYPAD 0x8
/*
* Virtual screen. This is stored as three blocks of 8-bit values, one for
* the actual characters, one for attributes and one for colours. Three
* seperate blocks means memset and friends can be used.
*
* Each block is y by x in size, row then column order. Sizes are 0-based.
*/
struct screen {
char title[MAXTITLELEN];
u_char **grid_data;
u_char **grid_attr;
u_char **grid_colr;
u_int sx; /* size x */
u_int sy; /* size y */
u_int cx; /* cursor x */
u_int cy; /* cursor y */
u_int ry_upper; /* scroll region top */
u_int ry_lower; /* scroll region bottom */
u_char attr;
u_char colr; /* fg:bg */
int mode;
};
/* Window structure. */
struct window {
char name[MAXNAMELEN];
int fd;
struct buffer *in;
struct buffer *out;
u_int references;
struct screen screen;
};
ARRAY_DECL(windows, struct window *);
/* Client session. */
struct session {
char name[MAXNAMELEN];
time_t tim;
struct window *window;
struct windows windows;
};
ARRAY_DECL(sessions, struct session *);
/* Client connection. */
struct client {
int fd;
struct buffer *in;
struct buffer *out;
u_int sx;
u_int sy;
struct session *session;
/* User input. */
const char *prompt;
char *buf;
size_t len;
size_t idx;
void (*callback)(struct client *, const char *);
};
ARRAY_DECL(clients, struct client *);
/* tmux.c */
volatile sig_atomic_t sigterm;
extern int debug_level;
extern char socket_path[MAXPATHLEN];
/* server.c */
int server_start(void);
/* ansi.c */
void input_key(struct buffer *, int);
size_t input_parse(u_char *, size_t, struct buffer *, struct screen *);
uint8_t input_extract8(struct buffer *);
uint16_t input_extract16(struct buffer *);
void input_store8(struct buffer *, uint8_t);
void input_store16(struct buffer *, uint16_t);
void input_store_zero(struct buffer *, u_char);
void input_store_one(struct buffer *, u_char, uint16_t);
void input_store_two(struct buffer *, u_char, uint16_t, uint16_t);
/* screen.c */
void screen_create(struct screen *, u_int, u_int);
void screen_resize(struct screen *, u_int, u_int);
void screen_draw(struct screen *, struct buffer *, u_int, u_int);
void screen_character(struct screen *, u_char);
void screen_sequence(struct screen *, u_char *);
/* local.c */
int local_init(struct buffer **, struct buffer **);
void local_done(void);
int local_key(size_t *);
void local_output(struct buffer *, size_t);
/* command.c */
extern int cmd_prefix;
int cmd_execute(int, struct buffer *);
/* window.c */
extern struct windows windows;
struct window *window_create(const char *, u_int, u_int);
int window_index(struct windows *, struct window *, u_int *);
void window_add(struct windows *, struct window *);
void window_remove(struct windows *, struct window *);
void window_destroy(struct window *);
struct window *window_next(struct windows *, struct window *);
struct window *window_previous(struct windows *, struct window *);
struct window *window_at(struct windows *, u_int);
int window_resize(struct window *, u_int, u_int);
int window_poll(struct window *, struct pollfd *);
void window_input(struct window *, struct buffer *, size_t);
void window_output(struct window *, struct buffer *);
/* session.c */
extern struct sessions sessions;
struct session *session_find(const char *);
struct session *session_create(const char *, const char *, u_int, u_int);
void session_destroy(struct session *);
int session_index(struct session *, u_int *);
int session_new(struct session *, const char *, u_int, u_int);
void session_attach(struct session *, struct window *);
int session_detach(struct session *, struct window *);
int session_has(struct session *, struct window *);
int session_next(struct session *);
int session_previous(struct session *);
int session_select(struct session *, u_int);
/* buffer.c */
struct buffer *buffer_create(size_t);
void buffer_destroy(struct buffer *);
void buffer_clear(struct buffer *);
void buffer_ensure(struct buffer *, size_t);
void buffer_add(struct buffer *, size_t);
void buffer_reverse_add(struct buffer *, size_t);
void buffer_remove(struct buffer *, size_t);
void buffer_reverse_remove(struct buffer *, size_t);
void buffer_insert_range(struct buffer *, size_t, size_t);
void buffer_delete_range(struct buffer *, size_t, size_t);
void buffer_write(struct buffer *, const void *, size_t);
void buffer_read(struct buffer *, void *, size_t);
/* buffer-poll.c */
int buffer_poll(struct pollfd *, struct buffer *, struct buffer *);
/* log.c */
void log_open(FILE *, int, int);
void log_close(void);
void log_vwrite(FILE *, int, const char *, va_list);
void log_write(FILE *, int, const char *, ...);
void printflike1 log_warn(const char *, ...);
void printflike1 log_warnx(const char *, ...);
void printflike1 log_info(const char *, ...);
void printflike1 log_debug(const char *, ...);
void printflike1 log_debug2(const char *, ...);
void printflike1 log_debug3(const char *, ...);
__dead void log_vfatal(const char *, va_list);
__dead void log_fatal(const char *, ...);
__dead void log_fatalx(const char *, ...);
/* xmalloc.c */
void *ensure_size(void *, size_t *, size_t, size_t);
void *ensure_for(void *, size_t *, size_t, size_t);
char *xstrdup(const char *);
void *xcalloc(size_t, size_t);
void *xmalloc(size_t);
void *xrealloc(void *, size_t, size_t);
void xfree(void *);
int printflike2 xasprintf(char **, const char *, ...);
int xvasprintf(char **, const char *, va_list);
int printflike3 xsnprintf(char *, size_t, const char *, ...);
int xvsnprintf(char *, size_t, const char *, va_list);
int printflike3 printpath(char *, size_t, const char *, ...);
char *xdirname(const char *);
char *xbasename(const char *);
#endif

317
window.c Normal file
View File

@ -0,0 +1,317 @@
/* $Id: window.c,v 1.1.1.1 2007-07-09 19:04:12 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <paths.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#define TTYDEFCHARS
#include <termios.h>
#include <unistd.h>
#include <util.h>
#include "tmux.h"
/*
* Each window is attached to a pty. This file contains code to handle them.
*
* A window has two buffers attached, these are filled and emptied by the main
* server poll loop. Output data is received from pty's in ANSI format,
* translated and returned as a series of ANSI escape sequences and strings.
* Input data is received in ANSI format and written directly to the pty.
*
* Each window also has a "virtual" screen (screen.c) which contains the
* current state and is redisplayed when the window is reattached to a client.
*
* A global list of windows is maintained, and a window may also be a member
* of any number of sessions. A reference count is maintained and a window
* removed from the global list and destroyed when it reaches zero.
*/
/* Global window list. */
struct windows windows;
/* Create a new window. */
struct window *
window_create(const char *cmd, u_int sx, u_int sy)
{
struct window *w;
struct winsize ws;
struct termios tio;
int fd, mode;
char pid[16], *ptr, *name;
memset(&ws, 0, sizeof ws);
ws.ws_col = sx;
ws.ws_row = sy;
memset(&tio, 0, sizeof tio);
tio.c_iflag = TTYDEF_IFLAG;
tio.c_oflag = TTYDEF_OFLAG;
tio.c_lflag = TTYDEF_LFLAG;
tio.c_cflag = TTYDEF_CFLAG;
memcpy(&tio.c_cc, ttydefchars, sizeof tio.c_cc);
cfsetspeed(&tio, TTYDEF_SPEED);
xsnprintf(pid, sizeof pid, "%ld", (long) getpid());
switch (forkpty(&fd, NULL, &tio, &ws)) {
case -1:
return (NULL);
case 0:
if (setenv("TMUX", pid, 1) != 0)
log_fatal("setenv");
if (setenv("TERM", "screen", 1) != 0)
log_fatal("setenv");
log_close();
execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);
log_fatal("execl");
}
if ((mode = fcntl(fd, F_GETFL)) == -1)
log_fatal("fcntl");
if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
log_fatal("fcntl");
mode = 1;
if (ioctl(fd, TIOCPKT, &mode) == -1)
log_fatal("ioctl(TIOCPKT)");
w = xmalloc(sizeof *w);
w->fd = fd;
w->in = buffer_create(BUFSIZ);
w->out = buffer_create(BUFSIZ);
screen_create(&w->screen, sx, sy);
name = xstrdup(cmd);
if ((ptr = strchr(name, ' ')) != NULL) {
if (ptr != name && ptr[-1] != '\\')
*ptr = '\0';
else {
while ((ptr = strchr(ptr + 1, ' ')) != NULL) {
if (ptr[-1] != '\\') {
*ptr = '\0';
break;
}
}
}
}
strlcpy(w->name, xbasename(name), sizeof w->name);
xfree(name);
window_add(&windows, w);
w->references = 0;
return (w);
}
/* Find window index in list. */
int
window_index(struct windows *ww, struct window *w, u_int *i)
{
for (*i = 0; *i < ARRAY_LENGTH(ww); (*i)++) {
if (w == ARRAY_ITEM(ww, *i))
return (0);
}
return (-1);
}
/* Add a window to a list. */
void
window_add(struct windows *ww, struct window *w)
{
u_int i;
if (window_index(ww, NULL, &i) != 0)
ARRAY_ADD(ww, w);
else
ARRAY_SET(ww, i, w);
w->references++;
}
/* Remove a window from a list. */
void
window_remove(struct windows *ww, struct window *w)
{
u_int i;
if (window_index(ww, w, &i) != 0)
log_fatalx("window_remove: window not found");
ARRAY_REMOVE(ww, i);
w->references--;
if (w->references == 0) {
window_destroy(w);
window_remove(&windows, w);
}
}
/* Destroy a window. */
void
window_destroy(struct window *w)
{
close(w->fd);
buffer_destroy(w->in);
buffer_destroy(w->out);
xfree(w);
}
/* Locate next window in list. */
struct window *
window_next(struct windows *ww, struct window *w)
{
u_int i;
if (window_index(ww, w, &i) != 0)
log_fatalx("window_next: window not found");
if (i == ARRAY_LENGTH(ww) - 1)
return (NULL);
do {
i++;
w = window_at(ww, i);
if (w != NULL)
return (w);
} while (i != ARRAY_LENGTH(ww) - 1);
return (NULL);
}
/* Locate previous window in list. */
struct window *
window_previous(struct windows *ww, struct window *w)
{
u_int i;
if (window_index(ww, w, &i) != 0)
log_fatalx("window_previous: window not found");
if (i == 0)
return (NULL);
do {
i--;
w = window_at(ww, i);
if (w != NULL)
return (w);
} while (i != 0);
return (NULL);
}
/* Locate window at specific position in list. */
struct window *
window_at(struct windows *ww, u_int i)
{
if (i >= ARRAY_LENGTH(ww))
return (NULL);
return (ARRAY_ITEM(ww, i));
}
/* Resize a window. */
int
window_resize(struct window *w, u_int sx, u_int sy)
{
struct winsize ws;
if (sx == w->screen.sx && sy == w->screen.sy)
return (-1);
memset(&ws, 0, sizeof ws);
ws.ws_col = sx;
ws.ws_row = sy;
screen_resize(&w->screen, sx, sy);
if (ioctl(w->fd, TIOCSWINSZ, &ws) == -1)
log_fatal("ioctl(TIOCSWINSZ)");
return (0);
}
/* Handle window poll results. This is special because of TIOCPKT. */
int
window_poll(struct window *w, struct pollfd *pfd)
{
struct termios tio;
size_t size;
u_char *ptr;
size = BUFFER_USED(w->in);
if (buffer_poll(pfd, w->in, w->out) != 0)
return (-1);
if (BUFFER_USED(w->in) == size)
return (0);
ptr = BUFFER_IN(w->in) - (BUFFER_USED(w->in) - size);
log_debug("window packet: %hhu", *ptr);
switch (*ptr) {
case TIOCPKT_DATA:
case TIOCPKT_FLUSHREAD:
case TIOCPKT_FLUSHWRITE:
case TIOCPKT_STOP:
case TIOCPKT_START:
case TIOCPKT_DOSTOP:
case TIOCPKT_NOSTOP:
buffer_delete_range(w->in, size, 1);
break;
case TIOCPKT_IOCTL:
buffer_delete_range(w->in, size, 1 + sizeof tio);
break;
}
return (0);
}
/* Process window input. */
void
window_input(struct window *w, struct buffer *b, size_t size)
{
int key;
while (size != 0) {
if (size < 1)
break;
size--;
key = input_extract8(b);
if (key == '\e') {
if (size < 2)
log_fatalx("window_input: underflow");
size -= 2;
key = (int16_t) input_extract16(b);
}
input_key(w->out, key);
}
}
/*
* Process window output. Output is translated into a series of ANSI escape
* sequences and strings and returned.
*/
void
window_output(struct window *w, struct buffer *b)
{
size_t used;
used = input_parse(
BUFFER_OUT(w->in), BUFFER_USED(w->in), b, &w->screen);
if (used != 0)
buffer_remove(w->in, used);
}

243
xmalloc.c Normal file
View File

@ -0,0 +1,243 @@
/* $Id: xmalloc.c,v 1.1.1.1 2007-07-09 19:03:33 nicm Exp $ */
/*
* Copyright (c) 2004 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <errno.h>
#include <libgen.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
void *
ensure_for(void *buf, size_t *len, size_t size, size_t adj)
{
if (adj == 0)
log_fatalx("ensure_for: zero adj");
if (SIZE_MAX - size < adj)
log_fatalx("ensure_for: size + adj > SIZE_MAX");
size += adj;
if (*len == 0) {
*len = BUFSIZ;
buf = xmalloc(*len);
}
while (*len <= size) {
buf = xrealloc(buf, 2, *len);
*len *= 2;
}
return (buf);
}
void *
ensure_size(void *buf, size_t *len, size_t nmemb, size_t size)
{
if (nmemb == 0 || size == 0)
log_fatalx("ensure_size: zero size");
if (SIZE_MAX / nmemb < size)
log_fatalx("ensure_size: nmemb * size > SIZE_MAX");
if (*len == 0) {
*len = BUFSIZ;
buf = xmalloc(*len);
}
while (*len <= nmemb * size) {
buf = xrealloc(buf, 2, *len);
*len *= 2;
}
return (buf);
}
char *
xstrdup(const char *s)
{
void *ptr;
size_t len;
len = strlen(s) + 1;
ptr = xmalloc(len);
return (strncpy(ptr, s, len));
}
void *
xcalloc(size_t nmemb, size_t size)
{
void *ptr;
if (size == 0 || nmemb == 0)
log_fatalx("xcalloc: zero size");
if (SIZE_MAX / nmemb < size)
log_fatalx("xcalloc: nmemb * size > SIZE_MAX");
if ((ptr = calloc(nmemb, size)) == NULL)
log_fatal("xcalloc");
return (ptr);
}
void *
xmalloc(size_t size)
{
void *ptr;
if (size == 0)
log_fatalx("xmalloc: zero size");
if ((ptr = malloc(size)) == NULL)
log_fatal("xmalloc");
return (ptr);
}
void *
xrealloc(void *oldptr, size_t nmemb, size_t size)
{
size_t newsize = nmemb * size;
void *newptr;
if (newsize == 0)
log_fatalx("xrealloc: zero size");
if (SIZE_MAX / nmemb < size)
log_fatal("xrealloc: nmemb * size > SIZE_MAX");
if ((newptr = realloc(oldptr, newsize)) == NULL)
log_fatal("xrealloc");
return (newptr);
}
void
xfree(void *ptr)
{
if (ptr == NULL)
log_fatalx("xfree: null pointer");
free(ptr);
}
int printflike2
xasprintf(char **ret, const char *fmt, ...)
{
va_list ap;
int i;
va_start(ap, fmt);
i = xvasprintf(ret, fmt, ap);
va_end(ap);
return (i);
}
int
xvasprintf(char **ret, const char *fmt, va_list ap)
{
int i;
i = vasprintf(ret, fmt, ap);
if (i < 0 || *ret == NULL)
log_fatal("xvasprintf");
return (i);
}
int printflike3
xsnprintf(char *buf, size_t len, const char *fmt, ...)
{
va_list ap;
int i;
va_start(ap, fmt);
i = xvsnprintf(buf, len, fmt, ap);
va_end(ap);
return (i);
}
int
xvsnprintf(char *buf, size_t len, const char *fmt, va_list ap)
{
int i;
if (len > INT_MAX) {
errno = EINVAL;
log_fatal("xvsnprintf");
}
i = vsnprintf(buf, len, fmt, ap);
if (i < 0)
log_fatal("xvsnprintf");
return (i);
}
/*
* Print a path. Same as xsnprintf, but return ENAMETOOLONG on truncation.
*/
int printflike3
printpath(char *buf, size_t len, const char *fmt, ...)
{
va_list ap;
int n;
if (len > INT_MAX) {
errno = ENAMETOOLONG;
return (1);
}
va_start(ap, fmt);
n = xvsnprintf(buf, len, fmt, ap);
va_end(ap);
if ((size_t) n > len) {
errno = ENAMETOOLONG;
return (1);
}
return (0);
}
/*
* Some system modify the path in place. This function and xbasename below
* avoid that by using a temporary buffer.
*/
char *
xdirname(const char *src)
{
char dst[MAXPATHLEN];
strlcpy(dst, src, sizeof dst);
return (dirname(dst));
}
char *
xbasename(const char *src)
{
char dst[MAXPATHLEN];
strlcpy(dst, src, sizeof dst);
return (basename(dst));
}