diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:28:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:28:28 +0000 |
commit | 5a5e2352c9a01f9076994915188c26c6b9036202 (patch) | |
tree | 1e1474b8b914d161946c01ba26a56db68b1fd5d3 | |
parent | Initial commit. (diff) | |
download | screen-upstream.tar.xz screen-upstream.zip |
Adding upstream version 4.9.0.upstream/4.9.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r-- | .gitignore | 21 | ||||
-rw-r--r-- | .iscreenrc | 169 | ||||
-rw-r--r-- | COPYING | 674 | ||||
-rw-r--r-- | ChangeLog | 619 | ||||
l--------- | FAQ | 1 | ||||
-rw-r--r-- | HACKING | 39 | ||||
-rw-r--r-- | INSTALL | 114 | ||||
-rw-r--r-- | Makefile.in | 356 | ||||
-rw-r--r-- | NEWS | 28 | ||||
-rw-r--r-- | NEWS.3.5 | 119 | ||||
-rw-r--r-- | NEWS.3.6 | 48 | ||||
-rw-r--r-- | NEWS.3.7 | 39 | ||||
-rw-r--r-- | NEWS.3.9 | 205 | ||||
-rw-r--r-- | README | 102 | ||||
-rw-r--r-- | TODO | 7 | ||||
-rw-r--r-- | acconfig.h | 579 | ||||
-rw-r--r-- | acls.c | 1147 | ||||
-rw-r--r-- | acls.h | 98 | ||||
-rw-r--r-- | ansi.c | 3207 | ||||
-rw-r--r-- | ansi.h | 175 | ||||
-rw-r--r-- | attacher.c | 1117 | ||||
-rwxr-xr-x | autogen.sh | 2 | ||||
-rw-r--r-- | braille.c | 945 | ||||
-rw-r--r-- | braille.h | 83 | ||||
-rw-r--r-- | braille_tsi.c | 314 | ||||
-rw-r--r-- | canvas.c | 918 | ||||
-rw-r--r-- | canvas.h | 101 | ||||
-rw-r--r-- | comm.c | 348 | ||||
-rw-r--r-- | comm.sh | 96 | ||||
-rw-r--r-- | configure.ac | 1315 | ||||
-rw-r--r-- | display.c | 4082 | ||||
-rw-r--r-- | display.h | 338 | ||||
-rw-r--r-- | doc/.gitignore | 2 | ||||
-rw-r--r-- | doc/FAQ | 253 | ||||
-rw-r--r-- | doc/Makefile.in | 62 | ||||
-rw-r--r-- | doc/README.DOTSCREEN | 151 | ||||
-rw-r--r-- | doc/fdpat.ps | 6501 | ||||
l--------- | doc/install.sh | 1 | ||||
-rw-r--r-- | doc/make.help | 51 | ||||
-rw-r--r-- | doc/screen.1 | 5402 | ||||
-rw-r--r-- | doc/screen.texinfo | 6102 | ||||
-rw-r--r-- | doc/window_to_display.ps | 2959 | ||||
-rw-r--r-- | encoding.c | 2144 | ||||
-rwxr-xr-x | etc/ccdefs | 46 | ||||
-rw-r--r-- | etc/completer.zsh | 53 | ||||
-rwxr-xr-x | etc/countmail | 67 | ||||
-rw-r--r-- | etc/etcscreenrc | 94 | ||||
-rw-r--r-- | etc/gr-braille.tbl | 260 | ||||
-rw-r--r-- | etc/gs-braille.tbl | 261 | ||||
-rwxr-xr-x | etc/mkinstalldirs | 35 | ||||
-rwxr-xr-x | etc/newsyntax | 64 | ||||
-rwxr-xr-x | etc/newsyntax38 | 71 | ||||
-rw-r--r-- | etc/screenrc | 153 | ||||
-rwxr-xr-x | etc/toolcheck | 44 | ||||
-rw-r--r-- | etc/us-braille.tbl | 260 | ||||
-rw-r--r-- | extern.h | 514 | ||||
-rw-r--r-- | fileio.c | 788 | ||||
-rw-r--r-- | help.c | 936 | ||||
-rw-r--r-- | image.h | 189 | ||||
-rw-r--r-- | input.c | 528 | ||||
-rwxr-xr-x | install.sh | 119 | ||||
-rw-r--r-- | layer.c | 1247 | ||||
-rw-r--r-- | layer.h | 163 | ||||
-rw-r--r-- | layout.c | 350 | ||||
-rw-r--r-- | layout.h | 63 | ||||
-rw-r--r-- | list_display.c | 245 | ||||
-rw-r--r-- | list_generic.c | 486 | ||||
-rw-r--r-- | list_generic.h | 73 | ||||
-rw-r--r-- | list_window.c | 708 | ||||
-rw-r--r-- | loadav.c | 396 | ||||
-rw-r--r-- | logfile.c | 251 | ||||
-rw-r--r-- | logfile.h | 87 | ||||
-rw-r--r-- | mark.c | 1449 | ||||
-rw-r--r-- | mark.h | 58 | ||||
-rw-r--r-- | misc.c | 798 | ||||
-rw-r--r-- | nethack.c | 138 | ||||
-rw-r--r-- | os.h | 529 | ||||
-rw-r--r-- | osdef.h.in | 202 | ||||
-rw-r--r-- | osdef.sh | 69 | ||||
-rw-r--r-- | patchlevel.h | 538 | ||||
-rw-r--r-- | process.c | 7414 | ||||
-rw-r--r-- | pty.c | 425 | ||||
-rw-r--r-- | putenv.c | 216 | ||||
-rw-r--r-- | resize.c | 1133 | ||||
-rw-r--r-- | sched.c | 272 | ||||
-rw-r--r-- | sched.h | 48 | ||||
-rw-r--r-- | screen.c | 3448 | ||||
-rw-r--r-- | screen.h | 316 | ||||
-rw-r--r-- | search.c | 383 | ||||
-rw-r--r-- | socket.c | 1939 | ||||
-rw-r--r-- | teln.c | 578 | ||||
-rw-r--r-- | term.c | 298 | ||||
-rw-r--r-- | term.sh | 169 | ||||
-rw-r--r-- | termcap.c | 1545 | ||||
-rw-r--r-- | terminfo/8bits | 17 | ||||
-rw-r--r-- | terminfo/README | 20 | ||||
-rw-r--r-- | terminfo/checktc.c | 204 | ||||
-rw-r--r-- | terminfo/screencap | 23 | ||||
-rw-r--r-- | terminfo/screeninfo.src | 80 | ||||
-rw-r--r-- | terminfo/test.txt | 603 | ||||
-rw-r--r-- | terminfo/tetris.c | 20 | ||||
-rw-r--r-- | tty.sh | 1569 | ||||
-rw-r--r-- | utf8encodings/01 | bin | 0 -> 29808 bytes | |||
-rw-r--r-- | utf8encodings/02 | bin | 0 -> 27550 bytes | |||
-rw-r--r-- | utf8encodings/03 | bin | 0 -> 32926 bytes | |||
-rw-r--r-- | utf8encodings/04 | bin | 0 -> 24302 bytes | |||
-rw-r--r-- | utf8encodings/18 | bin | 0 -> 54862 bytes | |||
-rw-r--r-- | utf8encodings/19 | bin | 0 -> 95776 bytes | |||
-rw-r--r-- | utf8encodings/a1 | bin | 0 -> 536 bytes | |||
-rw-r--r-- | utf8encodings/bf | bin | 0 -> 232 bytes | |||
-rw-r--r-- | utf8encodings/c2 | bin | 0 -> 256 bytes | |||
-rw-r--r-- | utf8encodings/c3 | bin | 0 -> 140 bytes | |||
-rw-r--r-- | utf8encodings/c4 | bin | 0 -> 228 bytes | |||
-rw-r--r-- | utf8encodings/c6 | bin | 0 -> 68 bytes | |||
-rw-r--r-- | utf8encodings/c7 | bin | 0 -> 52 bytes | |||
-rw-r--r-- | utf8encodings/c8 | bin | 0 -> 40 bytes | |||
-rw-r--r-- | utf8encodings/cc | bin | 0 -> 68 bytes | |||
-rw-r--r-- | utf8encodings/cd | bin | 0 -> 52 bytes | |||
-rw-r--r-- | utf8encodings/d6 | bin | 0 -> 212 bytes | |||
-rw-r--r-- | utmp.c | 893 | ||||
-rw-r--r-- | viewport.c | 129 | ||||
-rw-r--r-- | viewport.h | 51 | ||||
-rw-r--r-- | window.c | 2359 | ||||
-rw-r--r-- | window.h | 357 |
124 files changed, 77875 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..529960c --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +*.o +.*.swp +\#*\# +*~ +Makefile +autom4te.cache +cscope.out +TAGS +tags +comm.h +config.h +config.h.in +config.log +config.status +configure +kmapdef.c +osdef.h +term.h +screen +stamp-h.in +tty.c diff --git a/.iscreenrc b/.iscreenrc new file mode 100644 index 0000000..7a937f9 --- /dev/null +++ b/.iscreenrc @@ -0,0 +1,169 @@ +# +# A sample .screenrc which I use for everyday work. +# +# some of the commands commented out here, have been moved to +# /local/etc/screenrc +# +# we want no password, right? +#password # This will ask us for a password. +password none # Same as not even mentioning it. +#password 12Bz/9hNlPLZk # "1234" +#password YahtrWblnJw # ypmatch jnweiger passwd. Well, ... :-) + +scrollback 200 # we have a 200 lines history buffer +markkeys "@=\177:@=^C" # our mad facit-twist terminal buffer overflow... +markkeys "h=^B:l=^F:\$=^E" # some missing emacs style bindings in copymode + +echo -n "booting screen" + +# let it flash, not horn! +#vbell on # "vbell" doesn't work any longer, sorry. +#vbell_msg " Wuff, Wuff!! " # this is the default message +#bell "Bimmmel No. %" # sounds the bell and shows a message + +# we want to login all windows we create. +#login on # "login", "nologin" don't work any longer, sorry 2. + +echo -n "." +# we have no termcap entry for screen on the target machine? Well then +# we tell a lie. +term screen # would be the obvious default here. +#term vt100 # screen will understand vt100 for 99%. + +# we want to survive hangups +# note that the default setting is off now! +autodetach on + +# when we open a window, where shall its CWD be? +chdir # without argument it's my $HOME + +echo -n "." +# I hate nonexisting status lines! Force screen to believe me. +#hardstatus off + +# now some Terminal setup: +# Printing in the leftmost column is not save. We express that fact as :LP@: +# +# Emacs tends to smear it's highlighted status bar across the screen, producing +# ugly areas of bright background, if termcap isn't perfectly sober. +# Give a little :ms@: in the termcap, this may help. +# +# And who invented the initialisation for facit terminals? We tell him that +# we don't like smooth scroll, by specifying :ti=\E[?l:. +# \E[?3l 80 Zeichen +# \E[?3h 132 Zeichen +# LP Last column Printable +# \E[A cursor up +# \E[B cursor down +# \E[?4h smooth scroll +# \E[?4l jump scroll +# \E[%dL insert %d lines +# \E[K clear to end of line +# cs \E[%i%d;%dr for twist and xterm +# ms@ Move in Standout mode is NOT save. +# WS our private variable, it declares that the terminal can +# be resized by an escape-sequence +# The termcap statement takes 2 or three parameters. First parameter lists +# which TERMCAPs are affected by this statement. Second we specify changes +# in screen's view of that terminals. Third we may specify some capabilities +# that user-programs want to see in the $TERMCAP environment variable or in +# screen's termcap entry. +termcap vt* cl=\E[H\E[J\E[?1h:vi=\E[?35h:ve=\E[35l:ti=\E[?4l[vt100] +termcap facit ti=\E[?4l[facit] +termcap xterm* is=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;4;6l:Z0=\E[?3h:Z1=\E[?3l + +echo -n "." +# "\E(B", "\E(0", "\E(1", "\E(2", ... to switch between charsets. +# screen internally emulates G1: "\E)..", G2: "\E*..", G3: "\E+.." +# you can switch between them, with: +# +# code | switch to +# ------+------ +# ^O | G0 +# ^N | G1 +# \En | G2 +# \Eo | G3 +#termcap facit|vt100|xterm* G0 + +# how do we resize windows? under sunview, this is standard, but xterm +# needs to be a specially hacked xterm, to make this work. +termcap xterm* WS=\E[8;%d;%dt + +# ICL 6402 testing: +termcap icl* G0:S0=\E$[start]:E0=\E%[end]:C0=j9k<l6m3n?q\:t7u=v;w>x5 GS=\E(0^O:GE=\E(B^O:G1=k:G2=l:G3=m:G4=j:GV=x:GH=q:GR=u:GL=t:GU=w:GD=v:GC=n + +# Flow control produces trouble. ^S und ^Q will never reach screen, as our +# terminals catch them locally. Who can explain that to me?: +#flow on|off|auto [interrupt] + +# Long lines get wrapped around (the back of your terminal). This is the +# default for vt100. But now programs make different asumptions about your +# terminal. You may find two linefeeds where you'd expect one, or you may +# be confronted with a truncated line. Currently there is no fix, but pressing +# C-A r and doing a redraw. +#wrap on + +# the autoaka allows you to see the currently executing shell command in the +# window name field. To use that, your shell prompt must contain ^[k^[\ or +# you will see the string "(init)" as a name. +# in my .cshrc I may use this for a wonderful tcsh-prompt: +# set prompt="%{^[k^[\\%}%h %c2(%m)%# " +# +# defining a shellaka that contains a pipe-symbol (|) activates the +# autoaka feature. To the left of that | you specify a constant part of +# your prompt as a trigger, to the right you may place a default string +# as in +shellaka '> |tc' +# but beware! specifying a window name with the -t option has priority over +# the autoaka mechanism. Although specifying -t "> |foo" will work. +# shellaka tc + +# ... now a little bit of key bindings +# In case we don't have write permission for /etc/utmp (no s-bit) +# we create even local windows via rlogin. -> Et voila: a utmp-slot +# utmp-slots are strongly recommended to keep sccs and talk happy. +# (thus we have ^A# or. ^Ac for window creation with or without utmp-slot.) +# but if we run suid-root, we produce all the rlogins with -ln, +# as nobody shall refer to these pty's. +bind '!' screen -ln -k faui41 rlogin faui41 +bind '@' screen -ln -k vme2 rlogin faui4_vme2 +#bind '#' screen -k faui43 +bind '#' screen -ln -k faui43 rlogin faui43 +bind '$' screen -ln -k faui44 rlogin faui44 +bind '%' screen -ln -k faui45 rlogin faui45 +bind '\^' screen -ln -k sup1 rlogin fausup1 +bind '&' screen -ln -k sup2 rlogin fausup2 +bind '*' screen -ln -k faui48 rlogin faui48 +bind '(' screen -ln -k faui09 rlogin faui09 +bind ')' screen -ln -k faui10 rlogin faui10 +bind 'J' screen -ln -k 4j rlogin faui4j +bind 'P' screen -ln -k 4p rlogin faui4p +bind '^C' screen -ln -k 45c rlogin faui45c +bind '^D' screen -ln -k 45d rlogin faui45d +bind '^E' screen -ln -k 45e rlogin faui45e +bind '^I' screen -ln -k 45i rlogin faui45i + +# these two are logIn and logOut. As a toggle is too stupid. +#bind 'I' set login on +#bind 'O' set login off +bind 'L' + +# What happens, when you 'think emacs' and want to erase a whole +# line? You type ^A^K right? Under screen it should be ^Aa^K. But... +# killing the window would be a real punishment for a little mistyping. +bind k #wow! I even mange to type ^Ak by accident. +#bind ^k +#bind K kill + +echo -n "." +#screen 1:faui43 # My good old <nr>:<alias> syntax +#screen -k faui43 # The way Wayne Davison thinks about it. +#screen -ln -k faui43 # this one not logged in. +#screen -ln 2:faui09 rlogin faui09 -l jnweiger + +# Finally another bonus feature for people using strange terminal settings like +# different baud rate, etc. The next user will get standard settings +# as ^[c is a reset sequence. +#pow_detach_msg "" # is the default +pow_detach_msg "c" +echo "done." @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..cf08d53 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,619 @@ +Version 4.9.0 (30/01/2022): + * Hardstatus option for used encoding (escape string '%e') + * OpenBSD uses native openpty() from its utils.h + * Fixes: + - fix combining char handling that could lead to a segfault + - CVE-2021-26937: possible denial of service via a crafted UTF-8 character sequence (bug #60030) + - make screen exit code be 0 when checking --help + - session names limit is 80 symbols (bug #61534) + - option -X ignores specified user in multiuser env (bug #37437) + - a lot of reformations/fixes/cleanups (man page and source code) + +Version 4.8.0 (05/02/2020): + * Improve startup time by only polling for files to close + * Fixes: + - Fix for segfault if termcap doesn't have Km entry + - Make screen exit code be 0 when checking --version + - Fix potential memory corruption when using OSC 49 + +Version 4.7.0 (02/10/2019): + * Add support for SGR (1006) mouse mode + * Add support for OSC 11 + * Update Unicode ambiguous and wide tables to 12.1.0 + * Fixes: + - cross-compilation support (bug #43223) + - a lot of manpage fixes and cleanups + +Version 4.6.2 (23/10/2017): + * Fixes: + - revert changes to cursor position restore behavour (bug #51832) + - set freed pointer to NULL (bug #52133) + - documentation fixes + - fix windowlist crashes (bug #43054 & #51500) + +Version 4.6.1 (10/07/2017): + * Fixes: + - problems with starting session in some cases + - parallel make install + - segfault when querying info on nonUTF locale (bug #51402) + +Version 4.6.0 (28/06/2017): + * Update Unicode wide tables to 9.0 (bug #50044) + * Support more serial speeds + * Improved namespaces support + * Migrate from fifos to sockets + * Start viewing scrollback at first line of output (bug #49377) + +Version 4.5.1 (25/02/2017): + * Fixes: + - logfile permissions problem (CVE-2017-5618) + - SunOS build problem (bug #50089) + - FreeBSD core dumps (bug #50143) + +Version 4.5.0 (10/12/2016): + * Allow specifying logfile's name via command line parameter '-L' + * Fixes: + - broken handling of "bind u digraph U+" (bug #48691) + - crash with long $TERM (bug #48983) + - crash when bumping blank window + - build for AIX (bug #49149) + - %x improperly separating arguments + - install with custom DESTDIR (bug #48370) + +Version 4.4.0 (19/06/2016): + * Support up to 24 function keys + * Fix runtime issues + * 'logfile' command, starts logging into new file upon changing + +Version 4.3.1 (28/06/2015): + * Fix resize bug + +Version 4.3.0 (13/06/2015): + * Introduce Xx string escape showing the executed command of a window + * Implement dead/zombie window polling, allowing for auto reconnecting + * Allow setting hardstatus on first line + * New Commands: + - 'sort' command sorting windows by title + - 'bumpleft', 'bumpright' - manually move windows on window list + - 'collapse' removing numbering 'gaps' between windows, by renumbering + - 'windows' command now accepts arguments for use with querying + +Version 4.2.1 (28/04/2014): + * allow for terminal with long $TERM (up to 32 characters) + * allow to use long logins + * documentation fixes + * runtime fixes + +Version 4.2.0 (17/04/2014): + New Commands: + * 'unbindall' to unbind all commands + * 'up', 'down', 'left', 'right' sub-commands for 'focus' + * 'rendition' to specify rendition to use in caption/hardstatus for window-names that have bell/monitor/silence/so turned on. + * 'layout', with the following sub-commands + - 'title' + - 'number' + - 'autosave' ('autosave on' or 'autosave off') + - 'new' + - 'save' ('save <name>') + - 'select' + - 'next' + - 'prev' + - 'attach' + - 'show' + - 'remove' + - 'dump' + * 'group' for moving window(s) into a group. + * 'defmousetrack' and 'mousetrack', to turn on/off mouse-tracking for + displays. It's turned off by default. With mouse-tracking turned on, it's + possible to switch to a region ('focus') using mouse clicks. It's also + possible to select a text region in copy-mode using a mouse click to place + a mark and the scroll wheel to scroll through the buffer. Additional + features might be to allow clicking on window-titles in the caption to + switch to that window. + * All commands prefixed '@' are treated as 'quiet', i.e. '@'-prefixed commands + do not trigger any display messages. + + Changed Commands: + * '-v' parameter to 'split' command for vertical splits. + * 'sorendition' deprecated in favour of 'rendition so'. + * 'digraph' can take a second parameter to specify custom digraphs. For + example, + 'digraph >= ≥' or 'digraph >= U+2265' + Using '0' as the second parameter will remove the digraph. + * 'stuff' will prompt for input if there's nothing to stuff. + * The argument to ":number" can be prefixed with '+' or '-' to use it as a + relative argument. + * '-g' parameter to 'windowlist' to show nested list of windows. + * '//group' parameter to 'screen' to create a grouped window. + * 'blankerprg' shows the currently set command on no argument. + * 'maxwin' can now be used to increase the number of maximum windows. + + .screenrc: + * $PID expands to the PID of the screen session. + * $PWD expands to the current working directory of the session. + * $STY expands to the session name. + * Tilde-expansion in pathnames (e.g. for the 'source' command) + * C-style escapes can be used (e.g. "\n" to get a newline with 'stuff') + * '%p' in caption/hardstatus string expands to the PID of the backend, and + '%+p' expands to the PID of the frontend (display). + * '%S' in caption/hardstatus string expands to the session name. + * '%P' in the caption string evaluates to true if the region is in copy mode. + * '%E' in the caption string evaluates to true if the escape character has + currently been pressed. + + Window List: + * Nested views when there are window groups (with 'windowlist -g'). + * Press 'm' to toggle the most-recent view. + * Press 'g' to toggle nestedness. + * Press 'a' to view all windows in the list. + * Press '/' to search in the list. + * Press ',' and '.' to re-order windows in the list. + * Press 'K' to kill a window (requires confirmation). + + Display List: + * Press 'd' to detach a display, 'D' to power-detach. + + Others: + * Start using 'ChangeLog' for logging changes again. + * Terminfo update for 256-color support. + * Multiple input history (partially from Romain Francoise). + * vi-like fFtT;, searching in copy mode. + * In copy mode, search in reverse direction when 'N' is pressed. + * Tab-completion for command input. + * Some more readline-like bindings in input mode (e.g. ^W, ^D, ^P, ^N etc.) + * Fix displaying unicode characters in the caption/hardstatus on UTF8 locale. + * A revamped displays list (for 'displays' command) + * Increased default maximum number of windows from 40 to 100. + * Increased number color/attribute changes in caption/hardstatus string from 16 to 256. + * Some commands can be remotely queried using the -Q command-line flag. + + In-Progress: + * Scripting support (thanks to Google Summer of Code 2009 project by Rui Guo) + + Developers: + * Alexander Naumov <alexander_naumov@opensuse.org> + * Amadeusz Sławiński <amade@asmblr.net> + * Juergen Weigert <jw@suse.de> + * Michael Schroeder <mls@suse.de> + * Micah Cowan <micah@cowan.name> + * Sadrul Habib Chowdhury <sadrul@users.sourceforge.net> + + Contributors: + * Clavelito <maromomo@hotmail.com> + * Dick <dick@mrns.nl> + * Gabriel <g2p.code@gmail.com> + * Benjamin Andresen <bandresen@gmail.com> + * Takeshi Banse <takebi@laafc.net> + * Maarten Billemont <lhunath@gmail.com> + * Curtis Brown <mrbrown8@juno.com> + * Cyril Brulebois <kibi@debian.org> + * Trent W Buck <trentbuck@gmail.com> + * Stephane Chazelas <stephane_chazelas@yahoo.fr> + * Kees Cook <kees@ubuntu.com> + * Thomas Dickey <tom@invisible-island.net> + * Christian Ebert <blacktrash@gmx.net> + * Geraint Edwards <gedge-lists-screen@yadn.org> + * Romain Francoise <romain@orebokech.com> + * Alexander Gattin <xrgtn@yandex.ru> + * Emanuele Giaquinta <e.giaquinta@glauco.it> + * Yi-Hsuan Hsin <mhsin@mhsin.org> + * Kipling Inscore <kinscore@synaptics.com> + * Chris Jones <cjns1989@gmail.com> + * Max Kalashnikov <mmt@maxkalashnikov.com> + * Steve Kemp <steve@steve.org.uk> + * Ryan Niebur <ryan@debian.org> + * Jan Christoph Nordholz <hesso@pool.math.tu-berlin.de> + * William Pursell <bill.pursell@gmail.com> + * Michael Scherer <misc@mandriva.org> + * Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de> + * Peter Teichman <peter@teichman.org> + +30.10.94 + +This is a quick overview of screen's life story. But it is not up +to date. You'll find more details about the revision history in +patchlevel.h and newer changes are only documented there. + +31.7.93 -- 3.5.1 +================ + +* writelock, number, paste with arg, at, zombie and wall commands added. + +* Access Control Lists and more multi-user support added. + +* select and setenv commands enhanced. + +* socket.c: motorola bugfix. + +* configure.in: --srcdir support. + +* configure.in: recognize alpha and SUNOS3 correctly. + +* doc/screen.texinfo: Documentation by Jason Merrill. + +13.05.93 -- 3.3.3 +================= + +* defautonuke, silence commands added. + +* exec command added. + +* hardcopydir, logdir commands added. + +* Made a superb configure script. + +* BROKEN_PIPE, SOCK_NOT_IN_FS added for braindamaged systems. + +* multi display, multi user support. + +* process command. CS, CE switch cursorkeycap in application mode. + +* lockprg pow_detaches on SIGHUP + +* ins_reg copy_reg commands. + +* new screenrc syntax. + +* split up screen.c and ansi.c + +21.10.92 -- 3.2.9 +================ + +* ChangeLog: replaces CHANGES and is in GNUish format. + +* Makefile (CFLAGS, M_CFLAGS, LIBS, OPTIONS): moved user config here, + merged all Makefiles, GNUified + +* socket.c (FindSocket): ignoring bad files in $SCREENDIR + +* config/config.linux: ported. + +* utmp.c, exec.c, loadav.c: split apart from screen.c/fileio.c + +15.07.92 -- 3.2.8 +================= + +* ansi.c (WriteString): automatic character set switching for 8bit support + +3.2.3-3.2.7 +=========== + +* concept changes: Display structure, Multi attacher + +... + +3.2.2 +===== + +* screen.c (main): -m option, "_M_ake always new session", ignore $STY + +* screen.c (main): -Ssessionname +* fileio.c (RcLine): ^A:sessionname give your session a nicer name. + +* screen.c (main): supporting detached startup via screen -d -m -Ssockname + +* fileio.c (stripdev): moved, could not compile + +* overlay.h: "stackable overlay concept" + +* search.c: vi-like / and ? search AND emacs-like ^S and ^R incremental search + in scrollback + +* mark.c: I meant BSDI not BSD + +* concept change: struct display and struct newwin introduced. + +* screen.c (main): -v option prints version. + +* screen.c (MakeWindow): ^A:screen /dev/ttya opens a character device + instead of forking ShellProg with a pty pair. + +3.2.0 +===== + +Ultrix port + +Irix 3.3 SGI port + +shadow password suite supported + +data loss on stdin overflow fixed + +"refresh off" keyword added. + +3.1.1 +------ + +Screen is now under the GNU copyleft license. See file COPYING. + +command line option -A. $LINES, $COLUMNS improved. + +C-A : vbellwait <sec> + +XENIX support (Ronald Khoo) + +SYSV has uname() instead of gethostname(). + +hpux has setresuid. + +ClearScreen now saves image to scrollback buffer. + +mips has setenv. + +numerous bugfixes. + +3.1 finally released version. +============================= + +3.0.99: last minute changes: +---------------------------- + +MIPS support (J{rvinen Markku) + +SVR4 support (Marc Boucher) + +secopen() secfopen() calls replace stat/access/open. +C-a : echo improved. +'register int' + +Changes up to Screen 3.0 Patchlevel 7 +===================================== + +Better terminfo support: Screen now checks if a termcap/info +entry which the name "screen.$TERM" does exist. Look in the +"VIRTUAL TERMINAL" section of the manual for more details. + +Many security improvements. + +ScrollRegion() bug fixed which caused slow scrolling if AL +or DL was used. + +Pyramid and Ultrix support added. (Tim and Larry) + +ENVIRONMENT support. + /local/etc/screenrc checks for $SYSSCREENRC + $HOME/.screenrc checks for $ISCREENRC and $SCREENRC + /local/screens checks for $ISCREENDIR and $SCREENDIR + .screenrc understands ${VAR} and $VAR . + +screen 3.0 Patchlevel 6 +======================= + +.screenrc: + screen now only opens the windows you explicitly ask for. + If you specify none, you still get one window, of course. + +screen 3.0. Patchlevel 5 +======================== + +Ansi prototyping by Christos. + +copy mode: CTRL-U / CTRL-D exchanged. code cleanup. + +changes to screen 3.0 patchlevel 4 +================================== + +markkeys "string" + allows to rebind the keys used in copy/history mode. + string is made up of pairs "<oldchar>=<newchar>" which are separated + by a colon. Oldchar and newchar are either single ascii characters, + or the two character sequence ^x, where x is an ascii character, or + a 3 digit octal value prepended with '\'. the string "\040=.:^M=q" + rebinds '.' to set marks, and the return key will abort copy mode. + +set scrollback 100 + resizes the scrollback history buffer to 100 lines. a default of 50 + is installed. + +A Howard Chu like scrollback history is installed. Many vi-like keys + are added to the copy mode. The '?' key reports on cursor position. + +screen 3.0 Patchlevel 3 +======================= + +WriteString fixed, it did kill the display variable. + +Yet another LP bugfix. + +non vt100 semi-graphics character support. + +waynes patch fixed + +screen 3.0 Patchlevel 2 +======================= + +wayne patches cursor motion outside scrollregions. + +.screenrc + monitor on|off + +changes in Screen 3.0 Patchlevel 1 +================================== + +screen -wipe + +^A : set vbell_msg "Wuff Wuff" + +Thousand enhancements: help resizable, copy'n'paste in main + socket loop, and no more '\0' hackin'. :WS=\E8;%d;%dt: + +screen can now resize windows under sunview. + +^A : set crlf on|off + effects markroutine join. + +screen learned about sized windows under X + +screen -ls (-d) -q + quiet option. We count the number of detached (attached) sessions and set + a return value of 10+n. The -q option inhibits all startup + warnings/messages. i.e. screen -R -q may return with code 12 or higher + or start a new/old session. + +pow_detach_msg "text string" + new command, allows messages, terminal reset, etc. on logout caused + by pow_detach. + +^A : learned a new keyword "set": + commands like "login on" , "vbell off", ... affect the default for + windows to be created. But commands like "set login off" affect + the actual setting of this window. and not the default. + such commands may be bound to keys. example: + bind 'O' set login off + is valid in your .screenrc as well as typed at the ':' prompt. + a bonus is ":set all" which is synonym to ":help". + At the Colon prompt also KeyNames can be entered, although that makes + not always sense. + +^A x uses a builtin lockprg, if + a) we don't find our lockprg, or + b) user supplies us with the environment variable LOCKPRG set to "builtin" + the builtin locks until your login password is typed. on systems using + "shadow password files" you are prompted for a password. + +markroutine can append joined. + +screen removes the "controlling tty" from utmp while ptys are attached. + +markroutine performs CR+NL when '\n' is pressed + +screen may die quietly, when no TERMCAP entry for "screen" is +found, and screen is run under X-windows + +_SEQUENT_ marks sequent386_ptx + +screen runs now under SunOS4.1.1 (we need setsid()!). + +bug in SetForeWindow fixed. + +rare markroutine bug fixed. + +we don't open every file the attacher tells us. + +we have now our wonderful "Wuff, Wuff" visual_bell + +we have now the interprocess-communication-buffer. secure version. + +'^A =' removes the interprocess-communication-buffer. + +markroutine as in 2.1 + +markroutine: 'a' toggles append mode, + '>' like ' ', but immediately WriteFile(DUMP_EXCHANGE) then. + 'A' like ' ', but first switch to append mode. + +.screenrc understands "screen 2:faui09 rlogin faui09 -l jnweiger" + and "password none" + and "vbell [on|off]" + +'^A :' allows .screenrc commands "online". + +screen now receives new $TERM from attacher, when it is reattached + +MakeClientSocket() fifo version does now test for access. + +.screenrc learns "hardstatus {on|off}" + +termcap's VB is used for vbell if available. + +Attach() code rewritten: + screen now lists socket directory, if it does not find a suitable socket + screen -d [host.tty] detaches a running screen. + +screen -[ls|list] + list all sockets that we find in our sockdir + +when the socket has been removed, send a SIGCHLD to the poor SCREEN +process and it will try to recover. then try a 'screen -r' again. +all the socket stuff lives now in an extra file. + +Major changes in version 2.4: +============================= + +* Test version that presents the erlangen extensions from 2.0 in a 2.3 + screen. + +* window resize support + +* screen locking C-a x + +* support for SYSV + +* password protection + +* copy & paste across screens + +* remote detach and power detach + +Major changes in version 2.3: + +* Terminal emulation has been significantly enhanced and bugfixed. + +* We now fully update the last character on the screen for true auto- + margin terminals, though there may be some delay before the character + can be safely added to the screen. If your terminal has character + insert it will be used to shorten the delay. + +* Added the "termcap" .screenrc command to tweak your terminal's termcap + entry AND to customize the termcap generated for the virtual terminals. + See also the -L and -O command-line options, and the SCREENCAP environ- + ment variable. + +* Fixed screen's character handling when detached or suspended to NOT block + the child processes in their windows -- output continues to be processed + in the background. + +* Added a.k.a.s (window-name aliases) that allow you to customize the + window-information line, including being able to change the name on- + the-fly to reflect what's currently running in the window (see the + -k option, shellaka command, and ALSO KNOWN AS discussion in the doc). + +* Added the ability to log the output of a window to a file (see the + "C-a H" (log) command). + +* Flow-control can now be set for each window and switched interactively + (see the "flow" command, -f option, and FLOW CONTROL discussion). + +* Individual windows can be included or excluded from mention in the + /etc/utmp file (see the "login" command and -l option). + +* Added an activity monitor, which allows you to have a window watched for + the start of any output and alert you when it occurs (see the "C-a M" + (monitor) command). + +* Enhanced the information in the window-information line to keep track of + windows that have: logging turned on '(L)'; beeped in the background '!'; + became active while being monitored '@' (see the "C-a w" (windows) command). + +* Added an on-line help display that lists all the commands and their + key bindings (see the "C-a ?" (help) command). + +* Extended handling of the beep message (and also the new activity message) + to allow '~' to specify a literal beep (see the "beep" and "activity" + .screenrc commands). + +* You can now set the default action on receipt of a hangup signal: detach + or terminate (see the "autodetach" .screenrc command). + +* Routing of characters to their virtual terminals has been enhanced to + not drop characters nor (in rare circumstances) hang up screen. + +* The NFS compatibility has been enhanced. + +Major changes in version 2.0a: + +* Screen allows you to `detach' the "screen" session from the physical + terminal and resume it at a later point in time (possibly on a + different terminal or in a different login session). + + To get an impression of this functionality do the following: + + - call "screen" and create a couple of windows + - type Control-A Control-D (screen terminates; you are back + in the shell) + - call "screen -r" to resume the detached screen + +* Screen supports multiple character sets and the ISO 2022 control + functions to designate and switch between character sets. + This allows you, for instance, to make use of the VT100 graphics + character set or national character sets. @@ -0,0 +1 @@ +doc/FAQ
\ No newline at end of file @@ -0,0 +1,39 @@ +WIP notes on hacking the Screen source. + +* Screen commands are handled by the DoAction function in process.c. + The local variable nr is set to an integer value representing the + command to be evaluated; for every command `foo', there is an + integer value RC_FOO for use as nr's value to represent it. Find + the matching case label to follow processing of the command. + + The RC_FOO values are defined in comm.h, which is automatically + generated by comm.sh, based on the names of the commands + themselves (found in comm.c). + +* The current display is held in the global variable "display". + Variable names like D_foo are shorthands for display->d_foo (where d_foo + is a member of "struct display"). + +* Names like D_IS, D_TI, D_SG usually refer to the values of various + termcap features of the current display. These are found in term.h, + which is automatically generated from term.c by term.sh. + +* The main input-reading function for handling user input from a display, + is disp_readev_fn in display.c. This also handles automatic transformation + of mouse-tracking codes from display coordinates to screen-window + coordinates, and decodes characters from the display's encoding, passing + it on to the foreground input processor. + + Input is passed through ProcessInput in process.c to handle + keybindings (specified by bindkey and such), and then processed by + layer-specific input-processing functions, which you'll find in + instances of struct LayFuncs. For instance, keystrokes are processed + by: + + normal windows: WinPrGocess + window in copy-mode: MarkProcess + window list: WListProcess + command input line: InpProcess + +* Handling string escapes (in hardstatus and the like), such as %w or + %{= bw}, is done in screen.c, MakeWinMsgEv(). @@ -0,0 +1,114 @@ +Installation of GNU screen. + + +0.) This instruction is quite lengthy +------------------------------------- +... and there are still important items near the end. Start here: +Unpack. Screen comes as a compressed tar archive. You need gzip to +uncompress. And... well, you probably already managed that step, +when you are reading this. + +For general documentation on the coding and usage standards this +distributions follows, see the GNU standards document on +https://www.gnu.org/prep/standards/, especially the `Makefile +Conventions', `Configuration', and `User Interfaces' sections. + +0.) autogen.sh +-------------- +First, run ./autogen.sh. This will create configure file that you can use. +This should be already done, so you can skip this step. + +1.) configure & config.status +----------------------------- +Run ./configure. This should create a reasonable Makefile and a config.h file +suited for your machine. Rename config.status to reflect the architecture +(hostname) where it was built. To reconfigure quickly for that architecture +just run that config.status file. +If this process fails, try to find out what configure did do and what it +should have checked. +And then please report a bug (https://savannah.gnu.org/bugs/?group=screen +or mail screen-devel@gnu.org). +Actually the initial Makefile that comes with the distribution just runs +configure -- thus you can start by typing 'make' right after unpacking. +You will be prompted to run 'make' again, which will really make screen. + +2.) Makefile & config.h +----------------------- +Look through the Makefile & user configuration section in config.h and check +pathnames. Change them to suit your installation requirements. Usually +sysadmins discuss the location of SOCKDIR, whether it should be in /tmp or +not. At least it must be on a filesystem that supports sockets/fifos. +SOCKDIR must not point into an AFS (Andrew File System) mounted directory. +If you are uncertain about your NFS implementation, use a UFS directory for +SOCKDIR. Personally, I favour a user's home directory and recommend the the +/tmp/ area. +The path for ETCSCREENRC may also need to be adapted. + +3.) how to actually compile +--------------------------- +Run 'make'. Screen should compile without too many warnings :) +The creation of term.h, comm.h, tty.c or osdef.h may fail on some machines +for some odd reason. (E.g. the sed under SCO-unix is known to be +case-insensitive and breaks term.h.) If so, please mail a short description +of the problem to screen-devel@gnu.org and use the files ending in .dist +as a replacement (or in case of osdef.h retry with an empty file). +You can then try 'make install' (if you dare). + +4.) where to install +-------------------- +You may well run screen from your private binary directory and with a +private socket directory like $HOME/.screen. But to have a full featured +screen and (from a user's point of view) more secure pty's you should +consult a system administrator and discuss installing screen setuid-root +in some globally accessible directory like /usr/local/bin. + +Consider this, when deciding whether you install screen setuid-root: +- On some machines root privileges are required to open pty's. +- Pty's should be owned by the user, so that she can do chmod to prevent + intruder attacks. The PTYs used by screen will remain world read-writable + if screen is not installed setuid-root. +- Some commands only work properly when the pty is owned by the user. + These include mesg and biff. +- The ^At feature may need to lseek and read the kernel file to retrieve + the load average. +- On most machines utmp slots can only be created/manipulated with root + privileges. Users will appear to be logged on the primary terminal + instead of the screen windows, if screen is not installed setuid-root. +- Multi-user screen sessions are only allowed when screen has a root-s-bit. +- If screen sockets of multiple users are kept in one directory (e.g. + /tmp/screens), this directory must be world writable when screen is not + installed setuid-root. Any user can remove or abuse any socket then. + + +5.) doc/screen.1 & doc/screen.texinfo +------------------------------------- +The man page doc/screen.1 should go to /usr/local/man/man1, or some similar +directory. It should format nicely with nroff -man. If it does not, then +try removing extra dots with: sed -e 's/^\.\././' < screen.1 | nroff -man +The info page doc/screen.texinfo contains basically the same information as +the man-page, we may have missed one or another thing in one of the files. +If so, mail me. + +6.) etc/screenrc & etc/etcscreenrc +---------------------------------- +The files screenrc and etc/etcscreenrc are instructive samples that +demonstrate what can/should be done from your private .screenrc and from +$ETCSCREENRC -- do not just copy them. Read them. Look through the +etcscreenrc file for system wide defaults that you like to set, e.g. +autodetach off, startup_message off, vbell on, ... +Since version 3.2.15 the screenrc file syntax changed slightly. All rc files +from previous versions should be run through the 'newsyntax' script that comes +with this package. +If and only if you want to install screen as a console multiplexer, look +at the *.sample files and what 'make cscreen' suggests. + +7.) terminfo/screeninfo.src & terminfo/screencap +------------------------------------------------ +Every now and then we update the termcap/terminfo entries for screen. +E.g. keycodes were added in 3.6.0 -- thus you check that your termcap/terminfo +database is up to date. See the README in the terminfo subdirectory. + +8.) have fun +------------ +To get an idea what the basic screen commands are, read the file README. +Request snail mail address for liquid and solid donations. :-) diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..08b44d3 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,356 @@ +# +# Makefile template for screen +# +# See machine dependant config.h for more configuration options. +# + +srcdir = @srcdir@ +VPATH = @srcdir@ + +DESTDIR = + +# Where to install screen. + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datarootdir = @datarootdir@ +datadir = @datadir@ + +# don't forget to change mandir and infodir in doc/Makefile. +bindir = $(exec_prefix)/bin + +VERSION = @VERSION@ +SCREEN = screen-$(VERSION) + +GIT_REV = +DEFS = @DEFS@ -DGIT_REV=\"$(GIT_REV)\" + +ETCSCREENRC = @ETCSCREENRC@ +ifeq (${ETCSCREENRC}, ) +ETCSCREENRC=$(prefix)/etc/screenrc +endif +SCREENENCODINGS = $(datadir)/screen/utf8encodings + +CC = @CC@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ -DETCSCREENRC='"$(ETCSCREENRC)"' \ + -DSCREENENCODINGS='"$(SCREENENCODINGS)"' +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +CPP=@CPP@ +CPP_DEPEND=$(CC) -MM + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +AWK = @AWK@ + +### Chose some debug configuration options: +# -DDEBUG +# Turn on really heavy debug output. This is written to +# /tmp/debug/{SCREEN,screen}.<pid>. Look at these files and quote +# questionable sections when sending bug-reports to the author. +# -DDUMPSHADOW +# With shadow-pw screen would never dump core. Use this option if +# you still want to have a core. Use only for debugging. +OPTIONS= +#OPTIONS= -DDEBUG + +SHELL=/bin/sh + +CFILES= screen.c ansi.c fileio.c mark.c misc.c resize.c socket.c \ + search.c tty.c term.c window.c utmp.c loadav.c putenv.c help.c \ + termcap.c input.c attacher.c pty.c process.c display.c comm.c \ + kmapdef.c acls.c braille.c braille_tsi.c logfile.c layer.c \ + sched.c teln.c nethack.c encoding.c canvas.c layout.c viewport.c \ + list_display.c list_generic.c list_window.c +OFILES= screen.o ansi.o fileio.o mark.o misc.o resize.o socket.o \ + search.o tty.o term.o window.o utmp.o loadav.o putenv.o help.o \ + termcap.o input.o attacher.o pty.o process.o display.o comm.o \ + kmapdef.o acls.o braille.o braille_tsi.o logfile.o layer.o \ + list_generic.o list_display.o list_window.o \ + sched.o teln.o nethack.o encoding.o canvas.o layout.o viewport.o + +all: screen + +screen: $(OFILES) + $(CC) $(LDFLAGS) -o $@ $(OFILES) $(LIBS) + +.c.o: + $(CC) -c -I. -I$(srcdir) $(M_CFLAGS) $(CPPFLAGS) $(DEFS) \ + $(OPTIONS) $(CFLAGS) $< + +install_bin: .version screen installdirs + -if [ -f $(DESTDIR)$(bindir)/$(SCREEN) ] && [ ! -f $(DESTDIR)$(bindir)/$(SCREEN).old ]; \ + then mv $(DESTDIR)$(bindir)/$(SCREEN) $(DESTDIR)$(bindir)/$(SCREEN).old; fi + $(INSTALL_PROGRAM) screen $(DESTDIR)$(bindir)/$(SCREEN) + -chown root $(DESTDIR)$(bindir)/$(SCREEN) && chmod 4755 $(DESTDIR)$(bindir)/$(SCREEN) +# This doesn't work if $(bindir)/screen is a symlink + -if [ -f $(DESTDIR)$(bindir)/screen ] && [ ! -f $(DESTDIR)$(bindir)/screen.old ]; then mv $(DESTDIR)$(bindir)/screen $(DESTDIR)$(bindir)/screen.old; fi + rm -f $(DESTDIR)$(bindir)/screen + (cd $(DESTDIR)$(bindir) && ln -f -s $(SCREEN) screen) + cp $(srcdir)/utf8encodings/?? $(DESTDIR)$(SCREENENCODINGS) + +############################################################################### +install: installdirs install_bin + cd doc ; $(MAKE) install + -if [ -d $(DESTDIR)/usr/lib/terminfo ]; then \ + PATH="$$PATH:/usr/5bin" tic ${srcdir}/terminfo/screeninfo.src; \ + chmod 644 $(DESTDIR)/usr/lib/terminfo/s/screen*; \ + fi +# Better do this by hand. E.g. under RCS... +# cat ${srcdir}/terminfo/screencap >> /etc/termcap + @echo "termcap entry (${srcdir}/terminfo/screencap) should be installed manually." + @echo "You may also want to install $(srcdir)/etc/etcscreenrc in" $(ETCSCREENRC) + +installdirs: +# Path leading to ETCSCREENRC and Socketdirectory not checked. + $(srcdir)/etc/mkinstalldirs $(DESTDIR)$(bindir) $(DESTDIR)$(SCREENENCODINGS) + cd doc ; $(MAKE) installdirs + +uninstall: .version + rm -f $(DESTDIR)$(bindir)/$(SCREEN) + rm -f $(DESTDIR)$(bindir)/screen + -mv $(DESTDIR)$(bindir)/screen.old $(DESTDIR)$(bindir)/screen + rm -f $(DESTDIR)$(ETCSCREENRC) + cd doc; $(MAKE) uninstall + +shadow: + mkdir shadow; + cd shadow; ln -s ../*.[ch] ../*.in ../*.sh ../configure ../doc ../terminfo ../etc . + rm -f shadow/term.h shadow/tty.c shadow/comm.h shadow/osdef.h + echo "install all Makefiles and config:" > shadow/Makefile + echo " rm -f config.cache" >> shadow/Makefile + echo " sh ./configure" >> shadow/Makefile + +term.h: term.c term.sh + AWK=$(AWK) srcdir=$(srcdir) sh $(srcdir)/term.sh + +kmapdef.c: term.h + +tty.c: tty.sh + sh $(srcdir)/tty.sh tty.c + +comm.h: comm.c comm.sh config.h + AWK=$(AWK) CC="$(CC) $(CFLAGS)" srcdir=${srcdir} sh $(srcdir)/comm.sh + +osdef.h: osdef.sh config.h osdef.h.in + CPP="$(CPP) $(CPPFLAGS)" srcdir=${srcdir} sh $(srcdir)/osdef.sh + +docs: + cd doc; $(MAKE) dvi screen.info + +dvi info screen.info: + -cd doc; $(MAKE) $@ + +mostlyclean: + rm -f $(OFILES) screen config.cache osdef0.c osdef1.sed osdef2.sed + +clean celan: mostlyclean + rm -f tty.c term.h comm.h osdef.h kmapdef.c core + +# Delete all files from the current directory that are created by +# configuring or building the program. +# building of term.h/comm.h requires awk. Keep it in the distribution +# we keep config.h, as this file knows where 'make dist' finds the ETCSCREENRC. +#distclean: mostlyclean +# rm -f $(SCREEN).tar $(SCREEN).tar.gz +# rm -f config.status Makefile +# rm -f osdef.h doc/Makefile + +maintainer-clean: + @echo "This command is not even intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + + +# Delete everything from the current directory that can be +# reconstructed with this Makefile. +realclean: .version mostlyclean + rm -f $(SCREEN).tar $(SCREEN).tar.gz + rm -f config.status Makefile doc/Makefile + rm -f tty.c term.h comm.h osdef.h kmapdef.c + rm -f config.h + echo "install all Makefiles and config:" > Makefile + echo " sh ./configure" >> Makefile + +tags TAGS: $(CFILES) + -ctags *.sh $(CFILES) *.h + -etags *.sh $(CFILES) *.h + +dist: .version $(SCREEN).tar.gz + +$(SCREEN).tar: .version term.h comm.h tty.c kmapdef.c + -rm -rf dist + mkdir dist + mkdir dist/$(SCREEN) + ln acls.h ansi.h display.h extern.h logfile.h mark.h os.h \ + layer.h patchlevel.h screen.h window.h image.h \ + osdef.h.in term.sh tty.sh comm.sh osdef.sh braille.h \ + sched.h \ + $(CFILES) \ + ChangeLog COPYING INSTALL NEWS* TODO install.sh \ + dist/$(SCREEN) + cd dist/$(SCREEN); mv tty.c tty.c.dist + cd dist/$(SCREEN); mv kmapdef.c kmapdef.c.dist + ln configure.in configure dist/$(SCREEN) + sed -e 's@"/local/screens@"/tmp/screens@' -e 's@"/local@"/usr/local@g' < config.h.in > dist/$(SCREEN)/config.h.in + sed -e 's@[ ]/local@ /usr/local@g' -e 's/^CFLAGS = -g/CFLAGS = -O/' < Makefile.in > dist/$(SCREEN)/Makefile.in + ln term.h dist/$(SCREEN)/term.h.dist + ln comm.h dist/$(SCREEN)/comm.h.dist + ln README dist/$(SCREEN)/README + mkdir dist/$(SCREEN)/terminfo + cd terminfo; ln 8bits README checktc.c screencap \ + screeninfo.src test.txt tetris.c \ + ../dist/$(SCREEN)/terminfo + mkdir dist/$(SCREEN)/etc + cd etc; ln * ../dist/$(SCREEN)/etc + mkdir dist/$(SCREEN)/utf8encodings + cd utf8encodings; ln * ../dist/$(SCREEN)/utf8encodings + # sed -e 's/^startup/#startup/' -e 's/^autodetach/#autodetach/' < $(ETCSCREENRC) > dist/$(SCREEN)/etc/etcscreenrc + cp $(HOME)/.screenrc dist/$(SCREEN)/etc/screenrc + mkdir dist/$(SCREEN)/doc + sed -e 's@/local/emacs@/usr/local@g' < doc/Makefile.in > dist/$(SCREEN)/doc/Makefile.in + cd doc; ln FAQ README.DOTSCREEN screen.1 screen.texinfo fdpat.ps make.help window_to_display.ps \ + ../dist/$(SCREEN)/doc + cd doc; if test -f screen.info; then ln screen.info* \ + ../dist/$(SCREEN)/doc; fi + cd dist/$(SCREEN)/doc; ln -s ../install.sh . + cd dist/$(SCREEN); ln -s doc/FAQ . + echo "install all Makefiles and config:" > dist/$(SCREEN)/Makefile + echo " rm -f config.cache" >> dist/$(SCREEN)/Makefile + echo " sh ./configure" >> dist/$(SCREEN)/Makefile + cd dist; tar cf ../$(SCREEN).tar $(SCREEN) + rm -rf dist + +$(SCREEN).tar.gz: $(SCREEN).tar + gzip -nf $(SCREEN).tar || gzip -f $(SCREEN).tar + +# Perform self-tests (if any). +check: + +lint: + lint -I. $(CFILES) + +saber: + #load $(CFLAGS) screen.c ansi.c $(LIBS) + +config: + rm -f config.cache + sh ./configure + + +############################################################################### + +.version: + @rev=`sed < $(srcdir)/patchlevel.h -n -e '/#define REV/s/#define REV *//p'`; \ + vers=`sed < $(srcdir)/patchlevel.h -n -e '/#define VERS/s/#define VERS *//p'`; \ + pat=`sed < $(srcdir)/patchlevel.h -n -e '/#define PATCHLEVEL/s/#define PATCHLEVEL *//p'`; \ + if [ "$${rev}.$${vers}.$${pat}" != "$(VERSION)" ]; then \ + echo "This distribution is screen-$${rev}.$${vers}.$${pat}, but"; \ + echo "the Makefile is from $(VERSION). Please update!"; exit 1; fi + +############################################################################### + +mdepend: $(CFILES) term.h + @rm -f DEPEND ; \ + for i in ${CFILES} ; do \ + echo "$$i" ; \ + echo `echo "$$i" | sed -e 's/.c$$/.o/'`": $$i" `\ + cc -E $$i |\ + grep '^# .*"\./.*\.h"' |\ + (sort -t'"' -u -k 2,2 2>/dev/null || sort -t'"' -u +1 -2) |\ + sed -e 's/.*"\.\/\(.*\)".*/\1/'\ + ` >> DEPEND ; \ + done + + +depend: depend.in + ./config.status || ./configure + +depend.in: $(CFILES) term.h + cp Makefile.in Makefile.in~ + sed -e '/\#\#\# Dependencies/q' < Makefile.in > tmp_make + for i in $(CFILES); do echo $$i; $(CPP_DEPEND) $$i >> tmp_make; done + mv tmp_make Makefile.in + +############################################################################### + +### Dependencies: +screen.o: layout.h viewport.h canvas.h screen.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h braille.h \ + patchlevel.h logfile.h extern.h +ansi.o: layout.h viewport.h canvas.h ansi.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h braille.h extern.h \ + logfile.h +fileio.o: layout.h viewport.h canvas.h fileio.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h +mark.o: layout.h viewport.h canvas.h mark.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h mark.h extern.h +misc.o: layout.h viewport.h canvas.h misc.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h +resize.o: layout.h viewport.h canvas.h resize.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h +socket.o: layout.h viewport.h canvas.h socket.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h +search.o: layout.h viewport.h canvas.h search.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h mark.h extern.h +tty.o: layout.h viewport.h canvas.h tty.c config.h screen.h os.h osdef.h ansi.h acls.h comm.h \ + layer.h term.h image.h display.h window.h extern.h +term.o: layout.h viewport.h canvas.h term.c term.h +window.o: layout.h viewport.h canvas.h window.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h logfile.h +utmp.o: layout.h viewport.h canvas.h utmp.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h +loadav.o: layout.h viewport.h canvas.h loadav.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h +putenv.o: layout.h viewport.h canvas.h putenv.c config.h +help.o: layout.h viewport.h canvas.h help.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h list_generic.h +termcap.o: layout.h viewport.h canvas.h termcap.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h +input.o: layout.h viewport.h canvas.h input.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h +attacher.o: layout.h viewport.h canvas.h attacher.c config.h screen.h os.h osdef.h ansi.h \ + acls.h comm.h layer.h term.h image.h display.h window.h extern.h +pty.o: layout.h viewport.h canvas.h pty.c config.h screen.h os.h osdef.h ansi.h acls.h comm.h \ + layer.h term.h image.h display.h window.h extern.h +process.o: layout.h viewport.h canvas.h process.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h logfile.h +display.o: layout.h viewport.h canvas.h display.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h braille.h +canvas.o: layout.h viewport.h canvas.h canvas.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h \ + braille.h +comm.o: layout.h viewport.h canvas.h comm.c config.h acls.h comm.h +kmapdef.o: layout.h viewport.h canvas.h kmapdef.c config.h +acls.o: layout.h viewport.h canvas.h acls.c config.h screen.h os.h osdef.h ansi.h acls.h comm.h \ + layer.h term.h image.h display.h window.h extern.h +braille.o: layout.h viewport.h canvas.h braille.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h braille.h +braille_tsi.o: layout.h viewport.h canvas.h braille_tsi.c config.h screen.h os.h osdef.h ansi.h \ + acls.h comm.h layer.h term.h image.h display.h window.h extern.h \ + braille.h +logfile.o: layout.h viewport.h canvas.h logfile.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h logfile.h +layer.o: layout.h viewport.h canvas.h layer.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h +sched.o: layout.h viewport.h canvas.h sched.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h logfile.h +teln.o: layout.h viewport.h canvas.h teln.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h +nethack.o: layout.h viewport.h canvas.h nethack.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h +encoding.o: layout.h viewport.h canvas.h encoding.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h +layout.o: layout.h viewport.h canvas.h layout.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h \ + braille.h +viewport.o: layout.h viewport.h canvas.h viewport.c config.h screen.h os.h osdef.h ansi.h acls.h \ + comm.h layer.h term.h image.h display.h window.h extern.h \ + braille.h +list_generic.o: list_generic.h list_generic.c layer.h screen.h osdef.h +list_display.o: list_generic.h list_display.c layer.h screen.h osdef.h +list_window.o: list_generic.h list_window.c window.h layer.h screen.h osdef.h comm.h + @@ -0,0 +1,28 @@ + ------------------------------ + What's new in screen-4.0.3 ? + ------------------------------ + +* zombie command has new option 'onerror' + +* buffer overflow in resize.c fixed + +* minor docu update + +* more robust startup + +* use setresuid; SendAttachMsg() for fd-passing added; DoCSI enhanced. + + ------------------------------ + What's new in screen-4.0.0 ? + ------------------------------ + +* new screenrc parser, not 100% compatible. + +* screenblanker support: new 'idle', 'blanker', 'blankerprg' + commands. + +* zmodem support via the 'zmodem' command. + +* nonblock code rewritten, nonblock now understands a timeout. + new command 'defnonblock'. + diff --git a/NEWS.3.5 b/NEWS.3.5 new file mode 100644 index 0000000..26e49ba --- /dev/null +++ b/NEWS.3.5 @@ -0,0 +1,119 @@ + + ---------------------------- + What's new in screen-3.5 ? + ---------------------------- + + +* Texinfo manpage! Thanks to Jason Merrill. + +* Screen now has a very large 'configure' script. If you have + problems with the resulting configuration please send mail to + screen@uni-erlangen.de. + +* Stackable overlay planes. + All commands are available even if you work with an overlay. Thus + you can be in copy/paste mode on several windows! + +* Unification of key bindings and screen commands. All keys now generate + commands. + +* Screen now reads/writes only in asyncronous mode. + +* Ansi parser speedup code resulting in much faster output of text. + +* Changed the rc file syntax. Commands now directly affect the current + window. The default settings are changed with 'def...' commands. + The 'set' keyword no longer exists. + Please run the 'newsyntax' script on your old screenrc files! + +* Emacs style isearch added to copy mode. Try ^A ESC ^R screen ^R ^R + to locate the last three occurrences of the word 'screen' in the + history buffer. + +* New command 'silence'. Alarms the user whenever there was inactivity + for a specified amount of time on a certain window. + Useful if you want to wait for a compilation to end. + +* Much better margin handling: + Screen now handles autowrapped lines correctly in the redisplay and + copy/paste functions. + +* New commands for pastebuffer management: + 'copy_reg' copies the pastebuffer to a register, + 'ins_reg' pastes a register, + 'register' fills a register with a string, + 'process' stuffs a register into strings input queue. + +* Autonuke feature. Flush the output buffer if the window gets + cleared. Enable this with 'autonuke on'. + +* Modifications to save memory: Empty attribute and font lines don't + get allocated. This is very useful if you have a lage scrollback. + +* Multi display support: + You can now attach from more than one terminal to a session with + the '-x' option. + +* New option '-S' to specify socket name. + +* Experimental multiuser support added: + You can start screen in multiuser Mode by prepending the socket + name with a '/' (or by the command 'multiuser on'). + If another user wants to attach to the screen session, he can do + this by prepending the socketname with 'screenuser/'. + Of course he must be in the access control list for a successful + attach (see the acladd/acldel command). + +* Extension to the 'screen' command: You can now specify tty lines + instead of programs. This can be used for console management. + Added the command 'break' to send a break to the tty line. + Not really a new feature, but terminal initialisation now works + on suns. + +* Input/output filters added. This has been implemented to allow the + user to configure an open tty line, but got soon exended to allow + all sorts of filters. For more information read the explanation + of the 'exec' command in the man page and check the 'fdpat.ps' + document. + +* Screen can now be started detached (screen -d -m -S sockname). + This is useful if you want to start screen in your /etc/rc file + (e.g. as a console multiplexer) + +* Console grabbing added ('console on' command). + +* Windows can now be selected by akas, too. (Per default bound to the + >'< key.) + +* New terminal capabiliteise CS/CE for cursorkey control. + +* setenv/unsetenv commands added. + +* Expansion of environment variables ($VAR) and terminal capabilities + ($:TC:) in the screenrc files and detach messages. + Example: pow_detach_msg "Session of \$LOGNAME \$:cr:\$:nl:ended." + +* New commands: + 'hardcopydir' and 'logdir' to change the output directories, + 'partial' and 'allpartial' to make screen only refresh the line + containing the cursor if a window is selected (useful for slow + modem connections). + +* Cleanup of the provided termcap/terminfo file. Please install + the new one! + +* The program 'terminfo/checktc.c' does a visual check of a + termcap/terminfo entry. Please try it before calling screen and + in a screen session. + +* LOTS of bugfixes and code cleanup. + +Thanks to all the beta testers who helped porting screen to at least +the following platforms: Ultrix, SunOS, Solaris, BSD43, linux, NEWSOS, +Irix, OSF/1, Harris CX/UX, hpux, dynix/ptx, AIX. +And even more thanks to the brave who attempted to use the 'exec' +command features. + + Donnate patches, bugreports, suggestions, money, beer & pizza to + screen@uni-erlangen.de + diff --git a/NEWS.3.6 b/NEWS.3.6 new file mode 100644 index 0000000..6d4360f --- /dev/null +++ b/NEWS.3.6 @@ -0,0 +1,48 @@ + + ---------------------------- + What's new in screen-3.6 ? + ---------------------------- + +* Input translation! This makes the vt100 emulation complete. + As an addition it is now possible to bind any command to any + (function-) key. See the man page for more details (bindkey + command). + +* Status line support. Each window can have a different status line. + Use the ANSI APC string to set the status line, i.e.: + <ESC>_<status string><ESC>\ + (For convenience the xterm sequence is also accepted.) + You may want to add + termcap * '' ':hs:ts=\E_:fs=\E\\:ds=\E_\E\\:' + terminfo * '' ':hs:ts=\E_:fs=\E\\:ds=\E_\E\\:' + to your ~/.screenrc to make screen advertise the hardstatus + support. + +* Zombie feature added. Windows now may generate a message (with a + timestamp) if they die and stay around until the user presses + a key. + +* New paste syntax: Paste can now concatenate registers and paste + either on screen or in anouther register. + This makes the old "ins_reg", "copy_reg" commands obsolete. + +* More architecures supported. Screen now runs on AIX3.2.5, + Solaris, NeXT and some other exotic platforms. + +* Kanji support added. Screen understands JIS, EUC and SJIS coding. + This is an experimental feature. + +* GR charset switching (ISO 2022) implemented. Can be enabled with + the "gr" command. + +* C1 sequences implemented (see the "c1" command). + +* Tek support from Xiaoguang Zhang. Apply tek.patch if you want to + make screen pass tek sequences. + +* List of new commands: + bindkey, c1, command, defc1, defgr, defkanji, gr, kanji, mapdefault, + mapnotnext, maptimeout, pastefont, printcmd, readreg, stuff, zombie + +* Lots of other bugs fixed. + diff --git a/NEWS.3.7 b/NEWS.3.7 new file mode 100644 index 0000000..e398f95 --- /dev/null +++ b/NEWS.3.7 @@ -0,0 +1,39 @@ + + + ---------------------------- + What's new in screen-3.7 ? + ---------------------------- + +* Color support. Screen understands the following capabilities: + AF (setaf) = Set foreground color (ANSI compatible) + AB (setab) = Set background color (ANSI compatible) + AX = Does understand ANSI set default fg/bg color + (\E[39m / \E[49m) + The tweaks for the color xterm would be: + termcap xterm 'AF=\E[3%dm:AB=\E[4%dm' + terminfo xterm 'AF=\E[3%p1%dm:AB=\E[4%p1%dm' + +* New 'digraph' command (bound to ^A^V) + ^A^Va" or ^A^V0344 input an a-umlaut + +* activity/bell message strings can now include the window title: + %t - title + %n - number (a single % still works) + +* 'defhstatus' command to give everey window a default + hardstatus line. ^E is used as a string escape instead of % + (see above). Try 'defhstatus "Screen: window ^E (^Et)"' + +* Input parser changed to understand '^' (see ^E above). + +Note that the linux color xterm has a stupid bug: the characters +get the color of the cursor, therefore if you change color and move +the cursor around all the characters will get the new color... +Here is a patch: + pub/utilities/screen/color_xterm_patch +Btw.: rxvt works fine. + +* Optional Braille support. If you can read Braille and have one of + the devices listed in README.DOTSCREEN, please compile with + -DHAVE_BRAILLE and let us know if this feature is useful. + diff --git a/NEWS.3.9 b/NEWS.3.9 new file mode 100644 index 0000000..4dc7891 --- /dev/null +++ b/NEWS.3.9 @@ -0,0 +1,205 @@ + ------------------------------- + What's new in screen-3.9.15 ? + ------------------------------- + +* unicode combining character support + +* new encoding: chinese GBK + +* new 'backtick' command and string escape to embed command + output into e.g. the hardstatus line + + + ------------------------------- + What's new in screen-3.9.13 ? + ------------------------------- + +* altscreen support from Gurusamy Sarathy + +* new command "maxwin" to set a limit on the number of windows + +* new keys in copy&paste mode: 'B' and 'E' + + + ------------------------------- + What's new in screen-3.9.11 ? + ------------------------------- + +* windowlist, bound to ^A" + +* support for other encodings, e.g. big5, koi8r, cp1251 + new commands 'encoding', 'defencoding' + 'register', 'readreg', 'readbuf', 'writebuf' now understand + an extra encoding parameter + +* support for double utf-8 characters + +* lots of new string escapes and extensions to existsing ones: + %LD, %LM, %Lw, %W, %-w, %+w, %H, %f, %F, %l, %=, %<, %> + +* new commands: 'source', 'eval', 'deflog', 'ignorecase', 'setsid' + +* command key classes: 'bind', 'command' and 'help' understand + a '-c <class>' parameter. See the man page for examples + +* new login state: always - don't remove slot even if screen gets + detached + +* 256 color support (experimental) + +* configurable time format string (for ^At) + +* config option to use localized month/week names + +* new option '-h' for hardcopy: also dump the scrollback buffer + + + ------------------------------ + What's new in screen-3.9.9 ? + ------------------------------ + +* new '-X' option to send commands to screen sessions. + + screen -X echo Hi... + +* added a possibility to change the attributes/color in caption or + hardstatus strings: + + caption always "%3n %{r}%t%{-}%? @%u%?%? %{g}[%h]%{-}%?" + +* new 'dinfo' command to show what screen thinks about your terminal. + +* new 'attrcolor' command to map attributes to color codes: + attrcolor u "-u b" + attrcolor b "r" + +* support for UTF-8: new commands 'utf8', 'defutf8' to change the + encoding of a window, plus a '-U' option to tell screen that + your terminal sends/receives UTF-8 codes. + +* support for 16 colors. + + + ------------------------------ + What's new in screen-3.9.8 ? + ------------------------------ + +* new command 'resize' to resize regions (aka split windows), try: + bind = resize = + bind + resize +1 + bind - resize -1 + bind _ resize max + +* new argument for 'focus': up, down, top, bottom + +* X11 mouse tracking support + +* Support for the "new color model", aka "background color erase": + the bce/defbce commands change the color model of the current + window/new windows. + +* experimental rxvt OSC sequence support (used to set a background + picture or to change the default colors), disabled by default. + + + ---------------------------- + What's new in screen-3.9 ? + ---------------------------- + +* real multiuser support + A window can now be displayed on more than one attached displays. + Screen does all the necessary clipping if the window size doesn't + fit the display. + New command: + ^AF - fit the window size into the display size. + +* split screen support + A display may now host multiple windows. + New commands: + ^AS - split horizontally. This add another region to the display + ^A<Tab> - move the focus to the next region + ^AX - kill the current region + ^AQ - kill all other regions + +* hardstatus emulation support + The last line of the display may now be used as a hardstatus + line if the terminal doesn't have the 'hs' capability. + New commands: + hardstatus [always]lastline + hardstatus [always]message + hardstatus [always]ignore + +* configurable window seperator and hardstatus strings + The window (region) seperator and the hardstatus can be set to an + arbitrary string containing screen's % escape sequences. + The window's hardstatus is just another escape sequence, '%h'. + New commands: + hardstatus string [string] + caption string [string] + The default strings are "%h" (hardstatus) and "%3n %t" (caption). + +* permanent window seperator + The window seperator can be set to stay on screen even if + the display contains only one region + New commands: + caption always + caption splitonly + +* many new escapes + %c - current time HH:MM (*CHANGE*: this was %w in screen-3.7) + %C - current time HH:MM in 24h format + %l - the load of the system + %h - hardstatus of the window + %w - all window names + %W - all window names except the current window + %u - all other users on this window + %? - the part to the next %? is displayed only if an escape + expands to an nonempty string. + %: - "else" part of %? + Some escapes like %c may be qualified with a '0' (like %0c) + to make screen use '0' instead of space as a filler. + Others understand a length qualifier, like %3n. + If escapes like the current time are used as hardstatus/caption + string screen will update them so that you can always have + the current time onscreen. + *CHANGE* ~ is no longer used as bell character, use ^G instead! + +* logfile timestamps and flush timeout + New commands: + logfile flush <secs> + logtstamp [on|off] + logtstamp string [string] + logtstamp after [secs] + +* configurable breaktype + You can now choose one of TIOCSBRK, TCSBRK, tcsendbreak. + New commands: + breaktype + defbreaktype + +* other new commands: + hstatus - set the window's hardstatus + defslowpaste + defsilence + +* optional builtin telnet. + This is useful if screen is used as frontend to a terminal + multiplexor. Use //telnet to access the builtin telnet program, + as in: 'screen //telnet host [port]' + +* remote detach and reattach change: + '-d' is now ignored if the screen is already detached and you + want to reattach. You can also use '-RR' to make screen use + the first session found if more than one session is available. + Thus '-d -RR' or '-x -RR' always gets you a screen. + +* support for history compaction + You can tell screen to suppress trailing blank lines when + scolling up text into the history buffer. (Wayne Davison) + New command: + compacthist + +* optional Braille support. If you can read Braille and have one of + the devices listed in README.DOTSCREEN, please compile with + -DHAVE_BRAILLE and let us know if this feature is useful. + @@ -0,0 +1,102 @@ + + [If you just got the screen package, it pays to read the file INSTALL] + [This intro only describes the most common features to get you started] + [A full description of all features is contained in the source package] + + + +Short introduction to GNU screen. + +Send bugreports, fixes, enhancements, t-shirts, money, beer & pizza to + screen-devel@gnu.org + +Screen provides you with an ANSI/vt100 terminal emulator, which can multiplex +up to 10 pseudo-terminals. On startup, it executes $SHELL in window 0. +Then it reads $HOME/.screenrc to learn configuration, keybindings, and +possibly open more windows. + + C-a ? (help) Show all keybindings. + + C-a c (screen) Create new windows. + + C-a SPACE (next) Advance to next window (with wraparound). + + C-a C-a (other) Toggle between the current and previously + displayed windows. + + C-a 0 (select n) Switch to window n=0 ... 9. + ... + C-a 9 + + C-a w (windows) Show a list of window names in the status line. + + C-a a (meta) Send a literal C-a/C-s/C-q to the + C-a s (xoff) process in the window. + C-a q (xon) For instance, emacs uses C-a and C-s. + + C-a l (redisplay) Redraw this window. + + C-a W (width) Toggle between 80 & 132 columns mode. + + C-a L (login) Try to toggle the window's utmp-slot. + + C-a z (suspend) Suspend the whole screen session. + + C-a x (lockscreen) Execute /usr/bin/lock, $LOCKCMD or a + built-in terminal lock. + + C-a H (log) Log stdout of window n to screenlog.n. + + C-a C-[ (copy) Start copy mode. Move cursor with h,j,k,l. + Set 2 marks with SPACE or y. Abort with ESC. + (C-[ is ESC.) Preceeding second mark with + an a appends the text to the copy buffer. + + C-a C-] (paste) Output copy buffer to current window's stdin. + + C-a < (readbuf) Read the copy buffer from /tmp/screen-exchange. + C-a > (writebuf) Write the copy buffer to /tmp/screen-exchange. + + C-a d (detach) Detach screen. All processes continue and may + spool output to their pty's, but screen + disconnects from your terminal. + + C-a D D (pow_detach) Power detach. Disconnect like C-a d but also + kill the parent shell. + + C-a K (kill) Kill a window and send SIGHUP to its process + group. Per default this would be C-a C-k, + but it is redefined in the demo .screenrc + (think of killing a whole line in emacs). + + C-a : (colon) Online configuration change. + +See the man page or TeXinfo manual for many more keybindings and commands. + +screen -r [pid.tty.host|tty.host] + Reattach to a specific detached session. The terminal emulator + reconfigures according to your $TERMCAP or $TERM settings. + When you have multiple screens detached, you must supply the session + name. + +screen -R reattaches to a detached session or (if none) creates a new + session. + +screen -d [pid.tty.host|tty.host] + Detach a screen session remotely. Has the same effect as typing 'C-a d' + on the controlling terminal. 'screen -D' will power-detach. + +screen -list +screen -ls +screen -wipe + Show all available sessions and their status. Use -wipe to remove + DEAD sessions. + + If sockets are missing, you may send a SIGCHLD to its 'SCREEN' + process and the process will re-establish the socket (think of + someone cleaning /tmp thoroughly). + +screen -h 200 + Starts a new screen session and sets the number of lines in the scrollback + buffer to 200. The default is 100 lines. + @@ -0,0 +1,7 @@ +- display size adaption (Activate) +- process.c cleanup via comm splitting +- writelocks? +- partial? +- type into several windows at once (for cluster admins) +- configurable digraph table +- command line options should overwrite config files. diff --git a/acconfig.h b/acconfig.h new file mode 100644 index 0000000..46d62b0 --- /dev/null +++ b/acconfig.h @@ -0,0 +1,579 @@ +/* Copyright (c) 1993-2000 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, see + * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + * $Id$ GNU + */ + + + + + +/********************************************************************** + * + * User Configuration Section + */ + +/* + * Maximum of simultaneously allowed windows per screen session. + */ +#ifndef MAXWIN +# define MAXWIN 100 +#endif + +/* + * Define SOCKDIR to be the directory to contain the named sockets + * screen creates. This should be in a common subdirectory, such as + * /usr/local or /tmp. It makes things a little more secure if you + * choose a directory which is not writable by everyone or where the + * "sticky" bit is on, but this isn't required. + * If SOCKDIR is not defined screen will put the named sockets in + * the user's home directory. Notice that this can cause you problems + * if some user's HOME directories are AFS- or NFS-mounted. Especially + * AFS is unlikely to support named sockets. + * + * Screen will name the subdirectories "S-$USER" (e.g /tmp/S-davison). + */ +#undef SOCKDIR + +/* + * Define this if the SOCKDIR is not shared between hosts. + */ +#define SOCKDIR_IS_LOCAL_TO_HOST + +/* + * Screen can look for the environment variable $SYSSCREENRC and -if it + * exists- load the file specified in that variable as global screenrc. + * If you want to enable this feature, define ALLOW_SYSSCREENRC to one (1). + * Otherwise ETCSCREENRC is always loaded. + */ +#define ALLOW_SYSSCREENRC 1 + +/* + * Define CHECKLOGIN to force Screen users to enter their Unix password + * in addition to the screen password. + * + * Define NOSYSLOG if yo do not have logging facilities. Currently + * syslog() will be used to trace ``su'' commands only. + */ +#define CHECKLOGIN 1 +#undef NOSYSLOG + + +/* + * define PTYMODE if you do not like the default of 0622, which allows + * public write to your pty. + * define PTYGROUP to some numerical group-id if you do not want the + * tty to be in "your" group. + * Note, screen is unable to change mode or group of the pty if it + * is not installed with sufficient privilege. (e.g. set-uid-root) + * define PTYROFS if the /dev/pty devices are mounted on a read-only + * filesystem so screen should not even attempt to set mode or group + * even if running as root (e.g. on TiVo). + */ +#undef PTYMODE +#undef PTYGROUP +#undef PTYROFS + +/* + * If screen is NOT installed set-uid root, screen can provide tty + * security by exclusively locking the ptys. While this keeps other + * users from opening your ptys, it also keeps your own subprocesses + * from being able to open /dev/tty. Define LOCKPTY to add this + * exclusive locking. + */ +#undef LOCKPTY + +/* + * If you'd rather see the status line on the first line of your + * terminal rather than the last, define TOPSTAT. + */ +#undef TOPSTAT + +/* + * define DETACH can detach a session. An absolute 'must'. + */ +#define DETACH + +/* + * here come the erlangen extensions to screen: + * define LOCK if you want to use a lock program for a screenlock. + * define PASSWORD for secure reattach of your screen. + * define COPY_PASTE to use the famous hacker's treasure zoo. + * define POW_DETACH to have a detach_and_logout key (requires DETACH). + * define REMOTE_DETACH (-d option) to move screen between terminals. + * define AUTO_NUKE to enable Tim MacKenzies clear screen nuking + * define PSEUDOS to allow window input/output filtering + * define MULTI to allow multiple attaches. + * define MULTIUSER to allow other users attach to your session + * (if they are in the acl, of course) + * define MAPKEYS to include input keyboard translation. + * define FONT to support ISO2022/alternet charset support + * define COLOR to include ansi color support. This may expose + * a bug in x11r6-color-xterm. + * define DW_CHARS to include support for double-width character + * sets. + * define ENCODINGS to include support for encodings like euc or big5. + * Needs FONT to work. + * define UTF8 if you want support for UTF-8 encoding. + * Needs FONT and ENCODINGS to work. + * define COLORS16 if you want 16 colors. + * Needs COLOR to work. + * define BUILTIN_TELNET to add telnet support to screen. + * Syntax: screen //telnet host [port] + * define RXVT_OSC if you want support for rxvts special + * change fgcolor/bgcolor/bgpicture sequences + */ +#undef SIMPLESCREEN +#ifndef SIMPLESCREEN +# define LOCK +# define PASSWORD +# define COPY_PASTE +# define REMOTE_DETACH +# define POW_DETACH +# define AUTO_NUKE +# define PSEUDOS +# define MULTI +# define MULTIUSER +# define MAPKEYS +# define COLOR +# define FONT +# define DW_CHARS +# define ENCODINGS +# define UTF8 +# define COLORS16 +# define ZMODEM +# define BLANKER_PRG +#endif /* SIMPLESCREEN */ + +#undef BUILTIN_TELNET +#undef RXVT_OSC +#undef COLORS256 + + +/* + * If you have a braille display you should define HAVE_BRAILLE. + * The code inside #ifdef HAVE_BRAILLE was contributed by Hadi Bargi + * Rangin (bargi@dots.physics.orst.edu). + * WARNING: this is more or less unsupported code, it may be full of + * bugs leading to security holes, enable at your own risk! + */ +#undef HAVE_BRAILLE + + +/* + * As error messages are mostly meaningless to the user, we + * try to throw out phrases that are somewhat more familiar + * to ...well, at least familiar to us NetHack players. + */ +#ifndef NONETHACK +# define NETHACK +#endif /* NONETHACK */ + +/* + * If screen is installed with permissions to update /etc/utmp (such + * as if it is installed set-uid root), define UTMPOK. + */ +#define UTMPOK + +/* Set LOGINDEFAULT to one (1) + * if you want entries added to /etc/utmp by default, else set it to + * zero (0). + * LOGINDEFAULT will be one (1) whenever LOGOUTOK is undefined! + */ +#define LOGINDEFAULT 1 + +/* Set LOGOUTOK to one (1) + * if you want the user to be able to log her/his windows out. + * (Meaning: They are there, but not visible in /etc/utmp). + * Disabling this feature only makes sense if you have a secure /etc/utmp + * database. + * Negative examples: suns usually have a world writable utmp file, + * xterm will run perfectly without s-bit. + * + * If LOGOUTOK is undefined and UTMPOK is defined, all windows are + * initially and permanently logged in. + * + * Set CAREFULUTMP to one (1) if you want that users have at least one + * window per screen session logged in. + */ +#define LOGOUTOK 1 +#undef CAREFULUTMP + + +/* + * If UTMPOK is defined and your system (incorrectly) counts logins by + * counting non-null entries in /etc/utmp (instead of counting non-null + * entries with no hostname that are not on a pseudo tty), define USRLIMIT + * to have screen put an upper-limit on the number of entries to write + * into /etc/utmp. This helps to keep you from exceeding a limited-user + * license. + */ +#undef USRLIMIT + +/* + * both must be defined if you want to favor tcsendbreak over + * other calls to generate a break condition on serial lines. + * (Do not bother, if you are not using plain tty windows.) + */ +#define POSIX_HAS_A_GOOD_TCSENDBREAK +#define SUNOS4_AND_WE_TRUST_TCSENDBREAK + +/* + * to lower the interrupt load on the host machine, you may want to + * adjust the VMIN and VTIME settings used for plain tty windows. + * See the termio(4) manual page (Non-Canonical Mode Input Processing) + * for details. + * if undefined, VMIN=1, VTIME=0 is used as a default - this gives you + * best user responsiveness, but highest interrupt frequency. + * (Do not bother, if you are not using plain tty windows.) + */ +#define TTYVMIN 100 +#define TTYVTIME 2 + +/* + * looks like the above values are ignored by setting FNDELAY. + * This is default for all pty/ttys, you may disable it for + * ttys here. After playing with it for a while, one may find out + * that this feature may cause screen to lock up. + */ +#ifdef bsdi +# define TTY_DISABLE_FNBLOCK /* select barfs without it ... */ +#endif + + +/* + * Some terminals, e.g. Wyse 120, use a bitfield to select attributes. + * This doesn't work with the standard so/ul/m? terminal entries, + * because they will cancel each other out. + * On TERMINFO machines, "sa" (sgr) may work. If you want screen + * to switch attributes only with sgr, define USE_SGR. + * This is *not* recomended, do this only if you must. + */ +#undef USE_SGR + + +/* + * Define USE_LOCALE if you want screen to use the locale names + * for the name of the month and day of the week. + */ +#undef USE_LOCALE + +/* + * Define USE_PAM if your system supports PAM (Pluggable Authentication + * Modules) and you want screen to use it instead of calling crypt(). + * (You may also need to add -lpam to LIBS in the Makefile.) + */ +#undef USE_PAM + +/* + * Define CHECK_SCREEN_W if you want screen to set TERM to screen-w + * if the terminal width is greater than 131 columns. No longer needed + * on modern systems which use $COLUMNS or the tty settings instead. + */ +#undef CHECK_SCREEN_W + +/********************************************************************** + * + * End of User Configuration Section + * + * Rest of this file is modified by 'configure' + * Change at your own risk! + * + */ + +/* + * Some defines to identify special unix variants + */ +#ifndef SVR4 +#undef SVR4 +#endif + +/* #ifndef __osf__ */ +#ifndef MIPS +#undef MIPS +#endif +/* #endif */ + +#ifndef OSX +#undef OSX +#endif + +#ifndef ISC +#undef ISC +#endif + +#ifndef sysV68 +#undef sysV68 +#endif + +#ifndef _POSIX_SOURCE +#undef _POSIX_SOURCE +#endif + +/* + * Define POSIX if your system supports IEEE Std 1003.1-1988 (POSIX). + */ +#undef POSIX + +/* + * Define BSDJOBS if you have BSD-style job control (both process + * groups and a tty that deals correctly with them). + */ +#undef BSDJOBS + +/* + * Define TERMIO if you have struct termio instead of struct sgttyb. + * This is usually the case for SVID systems, where BSD uses sgttyb. + * POSIX systems should define this anyway, even though they use + * struct termios. + */ +#undef TERMIO + +/* + * Define CYTERMIO if you have cyrillic termio modes. + */ +#undef CYTERMIO + +/* + * Define TERMINFO if your machine emulates the termcap routines + * with the terminfo database. + * Thus the .screenrc file is parsed for + * the command 'terminfo' and not 'termcap'. + */ +#undef TERMINFO + +/* + * If your library does not define ospeed, define this. + */ +#undef NEED_OSPEED + +/* + * Define SYSV if your machine is SYSV complient (Sys V, HPUX, A/UX) + */ +#ifndef SYSV +#undef SYSV +#endif + +/* + * Define SIGVOID if your signal handlers return void. On older + * systems, signal returns int, but on newer ones, it returns void. + */ +#undef SIGVOID + +/* + * Define USESIGSET if you have sigset for BSD 4.1 reliable signals. + */ +#undef USESIGSET + +/* + * Define SYSVSIGS if signal handlers must be reinstalled after + * they have been called. + */ +#undef SYSVSIGS + +/* + * Define BSDWAIT if your system defines a 'union wait' in <sys/wait.h> + * + * Only allow BSDWAIT i.e. wait3 on nonposix systems, since + * posix implies wait(3) and waitpid(3). vdlinden@fwi.uva.nl + * + */ +#ifndef POSIX +#undef BSDWAIT +#endif + +/* + * On RISCOS we prefer wait2() over wait3(). rouilj@sni-usa.com + */ +#ifdef BSDWAIT +#undef USE_WAIT2 +#endif + +/* + * If your system has getutent(), pututline(), etc. to write to the + * utmp file, define GETUTENT. + */ +#undef GETUTENT + +/* + * Define UTHOST if the utmp file has a host field. + */ +#undef UTHOST + +/* + * Define if you have the utempter utmp helper program + */ +#undef HAVE_UTEMPTER + +/* + * If ttyslot() breaks getlogin() by returning indexes to utmp entries + * of type DEAD_PROCESS, then our getlogin() replacement should be + * selected by defining BUGGYGETLOGIN. + */ +#undef BUGGYGETLOGIN + +/* + * If your system has the calls setreuid() and setregid(), + * define HAVE_SETREUID. Otherwise screen will use a forked process to + * safely create output files without retaining any special privileges. + */ +#undef HAVE_SETRESUID +#undef HAVE_SETREUID + +/* + * If your system supports BSD4.4's seteuid() and setegid(), define + * HAVE_SETEUID. + */ +#undef HAVE_SETEUID + +/* + * execvpe is now defined in some systems. + */ +#undef HAVE_EXECVPE + +/* + * If you want the "time" command to display the current load average + * define LOADAV. Maybe you must install screen with the needed + * privileges to read /dev/kmem. + * Note that NLIST_ stuff is only checked, when getloadavg() is not available. + */ +#undef LOADAV + +#undef LOADAV_NUM +#undef LOADAV_TYPE +#undef LOADAV_SCALE +#undef LOADAV_GETLOADAVG +#undef LOADAV_UNIX +#undef LOADAV_AVENRUN +#undef LOADAV_USE_NLIST64 + +#undef NLIST_DECLARED +#undef NLIST_STRUCT +#undef NLIST_NAME_UNION + +/* + * If your system has the new format /etc/ttys (like 4.3 BSD) and the + * getttyent(3) library functions, define GETTTYENT. + */ +#undef GETTTYENT + +/* + * Define USEBCOPY if the bcopy/memcpy from your system's C library + * supports the overlapping of source and destination blocks. When + * undefined, screen uses its own (probably slower) version of bcopy(). + * + * SYSV machines may have a working memcpy() -- Oh, this is + * quite unlikely. Tell me if you see one. + * "But then, memmove() should work, if at all available" he thought... + * Boing, never say "works everywhere" unless you checked SCO UNIX. + * Their memove fails the test in the configure script. Sigh. (Juergen) + */ +#undef USEBCOPY +#undef USEMEMCPY +#undef USEMEMMOVE + +/* + * If your system has vsprintf() and requires the use of the macros in + * "varargs.h" to use functions with variable arguments, + * define USEVARARGS. + */ +#undef USEVARARGS + +/* + * If the select return value doesn't treat a descriptor that is + * usable for reading and writing as two hits, define SELECT_BROKEN. + */ +#undef SELECT_BROKEN + +/* + * Define this if your system exits select() immediatly if a pipe is + * opened read-only and no writer has opened it. + */ +#undef BROKEN_PIPE + +/* + * Define this if the unix-domain socket implementation doesn't + * create a socket in the filesystem. + */ +#undef SOCK_NOT_IN_FS + +/* + * If your system has setenv() and unsetenv() define USESETENV + */ +#undef USESETENV + +/* + * If setenv() takes 3 arguments define HAVE_SETENV_3 + */ +#undef HAVE_SETENV_3 + +/* + * If setenv() takes 2 arguments define HAVE_SETENV_2 + */ +#undef HAVE_SETENV_2 + +/* + * If your system does not come with a setenv()/putenv()/getenv() + * functions, you may bring in our own code by defining NEEDPUTENV. + */ +#undef NEEDPUTENV + +/* + * If the passwords are stored in a shadow file and you want the + * builtin lock to work properly, define SHADOWPW. + */ +#undef SHADOWPW + +/* + * define HAVE_NL_LANGINFO if your system has the nl_langinfo() call + * and <langinfo.h> defines CODESET. + */ +#undef HAVE_NL_LANGINFO + +/* + * Newer versions of Solaris include fdwalk, which can greatly improve + * the startup time of screen; otherwise screen spends a lot of time + * closing file descriptors. + */ +#undef HAVE_FDWALK + +/* + * define HAVE_DEV_PTC if you have a /dev/ptc character special + * device. + */ +#undef HAVE_DEV_PTC + +/* + * define HAVE_SVR4_PTYS if you have a /dev/ptmx character special + * device and support the ptsname(), grantpt(), unlockpt() functions. + */ +#undef HAVE_SVR4_PTYS + +/* + * define PTYRANGE0 and or PTYRANGE1 if you want to adapt screen + * to unusual environments. E.g. For SunOs the defaults are "qpr" and + * "0123456789abcdef". For SunOs 4.1.2 + * #define PTYRANGE0 "pqrstuvwxyzPQRST" + * is recommended by Dan Jacobson. + */ +#undef PTYRANGE0 +#undef PTYRANGE1 + @@ -0,0 +1,1147 @@ +/* Copyright (c) 2008, 2009 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Micah Cowan (micah@cowan.name) + * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) + * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, see + * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + */ + +#include "config.h" + +#include <sys/types.h> + +/* XXX: WHY IS THIS HERE?? :XXX */ + +#ifdef CHECKLOGIN +# ifdef _SEQUENT_ +# include <stdio.h> /* needed by <pwd.h> */ +# endif /* _SEQUENT_ */ +# include <pwd.h> +# ifdef SHADOWPW +# include <shadow.h> +# endif /* SHADOWPW */ +#endif /* CHECKLOGIN */ + +#ifndef NOSYSLOG +# include <syslog.h> +#endif + +#include "screen.h" /* includes acls.h */ +#include "extern.h" + + +/************************************************************************ + * user managing code, this does not really belong into the acl stuff * + ************************************************************************/ + +extern struct comm comms[]; +extern struct win *windows, **wtab; +extern char NullStr[]; +extern char SockPath[]; +extern struct display *display, *displays; +struct acluser *users; + +#ifdef MULTIUSER +int maxusercount = 0; /* used in process.c: RC_MONITOR, RC_SILENCE */ + +/* record given user ids here */ +static AclBits userbits; + +/* + * rights a new unknown user will have on windows and cmds. + * These are changed by a "umask ?-..." command: + */ +static char default_w_bit[ACL_BITS_PER_WIN] = +{ + 1, /* EXEC */ + 1, /* WRITE */ + 1 /* READ */ +}; + +static char default_c_bit[ACL_BITS_PER_CMD] = +{ + 0 /* EXEC */ +}; + +/* rights of all users per newly created window */ +/* + * are now stored per user (umask) + * static AclBits default_w_userbits[ACL_BITS_PER_WIN]; + * static AclBits default_c_userbits[ACL_BITS_PER_CMD]; + */ + +static int GrowBitfield __P((AclBits *, int, int, int)); +static struct aclusergroup **FindGroupPtr __P((struct aclusergroup **, struct acluser *, int)); +static int AclSetPermCmd __P((struct acluser *, char *, struct comm *)); +static int AclSetPermWin __P((struct acluser *, struct acluser *, char *, struct win *)); +static int UserAcl __P((struct acluser *, struct acluser **, int, char **)); +static int UserAclCopy __P((struct acluser **, struct acluser **)); + + +static int +GrowBitfield(bfp, len, delta, defaultbit) +AclBits *bfp; +int len, delta, defaultbit; +{ + AclBits n, o = *bfp; + int i; + + if (!(n = (AclBits)calloc(1, (unsigned long)(&ACLBYTE((char *)0, len + delta + 1))))) + return -1; + for (i = 0; i < (len + delta); i++) + { + if (((i < len) && (ACLBIT(i) & ACLBYTE(o, i))) || + ((i >= len) && (defaultbit))) + ACLBYTE(n, i) |= ACLBIT(i); + } + if (len) + free((char *)o); + *bfp = n; + return 0; +} + +#endif /* MULTIUSER */ + +/* + * Returns an nonzero Address. Its contents is either a User-ptr, + * or NULL which may be replaced by a User-ptr to create the entry. + */ +struct acluser ** +FindUserPtr(name) +char *name; +{ + struct acluser **u; + + for (u = &users; *u; u = &(*u)->u_next) + if (!strcmp((*u)->u_name, name)) + break; +#ifdef MULTIUSER + debug3("FindUserPtr %s %sfound, id %d\n", name, (*u)?"":"not ", + (*u)?(*u)->u_id:-1); +#else /* MULTIUSER */ + debug2("FindUserPtr %s %sfound\n", name, (*u)?"":"not "); +#endif /* MULTIUSER */ + return u; +} + +int DefaultEsc = -1; /* initialised by screen.c:main() */ +int DefaultMetaEsc = -1; + +/* + * Add a new user. His password may be NULL or "" if none. His name must not + * be "none", as this represents the NULL-pointer when dealing with groups. + * He has default rights, determined by umask. + */ +int +UserAdd(name, pass, up) +char *name, *pass; +struct acluser **up; +{ +#ifdef MULTIUSER + int j; +#endif + + if (!up) + up = FindUserPtr(name); + if (*up) + { + if (pass) + (*up)->u_password = SaveStr(pass); + return 1; /* he is already there */ + } + if (strcmp("none", name)) /* "none" is a reserved word */ + *up = (struct acluser *)calloc(1, sizeof(struct acluser)); + if (!*up) + return -1; /* he still does not exist */ +#ifdef COPY_PASTE + (*up)->u_plop.buf = NULL; + (*up)->u_plop.len = 0; +# ifdef ENCODINGS + (*up)->u_plop.enc = 0; +# endif +#endif + (*up)->u_Esc = DefaultEsc; + (*up)->u_MetaEsc = DefaultMetaEsc; + strncpy((*up)->u_name, name, MAXLOGINLEN); + (*up)->u_password = NULL; + if (pass) + (*up)->u_password = SaveStr(pass); + if (!(*up)->u_password) + (*up)->u_password = NullStr; + (*up)->u_detachwin = -1; + (*up)->u_detachotherwin = -1; + +#ifdef MULTIUSER + (*up)->u_group = NULL; + /* now find an unused index */ + for ((*up)->u_id = 0; (*up)->u_id < maxusercount; (*up)->u_id++) + if (!(ACLBIT((*up)->u_id) & ACLBYTE(userbits, (*up)->u_id))) + break; + debug2("UserAdd %s id %d\n", name, (*up)->u_id); + if ((*up)->u_id == maxusercount) + { + int j; + struct win *w; + struct acluser *u; + + debug2("growing all bitfields %d += %d\n", maxusercount, USER_CHUNK); + /* the bitfields are full, grow a chunk */ + /* first, the used_uid_indicator: */ + if (GrowBitfield(&userbits, maxusercount, USER_CHUNK, 0)) + { + free((char *)*up); *up = NULL; return -1; + } + /* second, default command bits */ + /* (only if we generate commands dynamically) */ +/* + for (j = 0; j < ACL_BITS_PER_CMD; j++) + if (GrowBitfield(&default_c_userbits[j], maxusercount, USER_CHUNK, + default_c_bit[j])) + { + free((char *)*up); *up = NULL; return -1; + } +*/ + /* third, the bits for each commands */ + for (j = 0; j <= RC_LAST; j++) + { + int i; + + for (i = 0; i < ACL_BITS_PER_CMD; i++) + if (GrowBitfield(&comms[j].userbits[i], maxusercount, USER_CHUNK, + default_c_bit[i])) + { + free((char *)*up); *up = NULL; return -1; + } + } + /* fourth, default window creation bits per user */ + for (u = users; u != *up; u = u->u_next) + { + for (j = 0; j < ACL_BITS_PER_WIN; j++) + { + if (GrowBitfield(&u->u_umask_w_bits[j], maxusercount, USER_CHUNK, + default_w_bit[j])) + { + free((char *)*up); *up = NULL; return -1; + } + } + } + + /* fifth, the bits for each window */ + /* keep these in sync with NewWindowAcl() */ + for (w = windows; w; w = w->w_next) + { + /* five a: the access control list */ + for (j = 0; j < ACL_BITS_PER_WIN; j++) + if (GrowBitfield(&w->w_userbits[j], maxusercount, USER_CHUNK, + default_w_bit[j])) + { + free((char *)*up); *up = NULL; return -1; + } + /* five b: the activity notify list */ + /* five c: the silence notify list */ + if (GrowBitfield(&w->w_mon_notify, maxusercount, USER_CHUNK, 0) || + GrowBitfield(&w->w_lio_notify, maxusercount, USER_CHUNK, 0)) + { + free((char *)*up); *up = NULL; return -1; + } + } + maxusercount += USER_CHUNK; + } + + /* mark the user-entry as "in-use" */ + ACLBYTE(userbits, (*up)->u_id) |= ACLBIT((*up)->u_id); + + /* user id 0 is the session creator, he has all rights */ + if ((*up)->u_id == 0) + AclSetPerm(NULL, *up, "+a", "#?"); + + /* user nobody has a fixed set of rights: */ + if (!strcmp((*up)->u_name, "nobody")) + { + AclSetPerm(NULL, *up, "-rwx", "#?"); + AclSetPerm(NULL, *up, "+x", "su"); + AclSetPerm(NULL, *up, "+x", "detach"); + AclSetPerm(NULL, *up, "+x", "displays"); + AclSetPerm(NULL, *up, "+x", "version"); + } + + /* + * Create his umask: + * Give default_w_bit's for all users, + * but allow himself everything on "his" windows. + */ + for (j = 0; j < ACL_BITS_PER_WIN; j++) + { + if (GrowBitfield(&(*up)->u_umask_w_bits[j], 0, maxusercount, + default_w_bit[j])) + { + free((char *)*up); *up = NULL; return -1; + } + ACLBYTE((*up)->u_umask_w_bits[j], (*up)->u_id) |= ACLBIT((*up)->u_id); + } +#else /* MULTIUSER */ + debug1("UserAdd %s\n", name); +#endif /* MULTIUSER */ + return 0; +} + +#if 0 +/* change user's password */ +int +UserSetPass(name, pass, up) +char *name, *pass; +struct acluser **up; +{ + if (!up) + up = FindUserPtr(name); + if (!*up) + return UserAdd(name, pass, up); + if (!strcmp(name, "nobody")) /* he remains without password */ + return -1; + strncpy((*up)->u_password, pass ? pass : "", 20); + (*up)->u_password[20] = '\0'; + return 0; +} +#endif + +/* + * Remove a user from the list. + * Destroy all his permissions and completely detach him from the session. + */ +int +UserDel(name, up) +char *name; +struct acluser **up; +{ + struct acluser *u; +#ifdef MULTIUSER + int i; +#endif + struct display *old, *next; + + if (!up) + up = FindUserPtr(name); + if (!(u = *up)) + return -1; /* he who does not exist cannot be removed */ + old = display; + for (display = displays; display; display = next) + { + next = display->d_next; /* read the next ptr now, Detach may zap it. */ + if (D_user != u) + continue; + if (display == old) + old = NULL; + Detach(D_REMOTE); + } + display = old; + *up = u->u_next; + +#ifdef MULTIUSER + for (up = &users; *up; up = &(*up)->u_next) + { + /* unlink all group references to this user */ + struct aclusergroup **g = &(*up)->u_group; + + while (*g) + { + if ((*g)->u == u) + { + struct aclusergroup *next = (*g)->next; + + free((char *)(*g)); + *g = next; + } + else + g = &(*g)->next; + } + } + ACLBYTE(userbits, u->u_id) &= ~ACLBIT(u->u_id); + /* restore the bits in his slot to default: */ + AclSetPerm(NULL, u, default_w_bit[ACL_READ] ? "+r" : "-r", "#"); + AclSetPerm(NULL, u, default_w_bit[ACL_WRITE]? "+w" : "-w", "#"); + AclSetPerm(NULL, u, default_w_bit[ACL_EXEC] ? "+x" : "-x", "#"); + AclSetPerm(NULL, u, default_c_bit[ACL_EXEC] ? "+x" : "-x", "?"); + for (i = 0; i < ACL_BITS_PER_WIN; i++) + free((char *)u->u_umask_w_bits[i]); +#endif /* MULTIUSER */ + debug1("FREEING user structure for %s\n", u->u_name); +#ifdef COPY_PASTE + UserFreeCopyBuffer(u); +#endif + free((char *)u); + if (!users) + { + debug("Last user deleted. Feierabend.\n"); + Finit(0); /* Destroying whole session. Noone could ever attach again. */ + } + return 0; +} + + +#ifdef COPY_PASTE + +/* + * returns 0 if the copy buffer was really deleted. + * Also removes any references into the users copybuffer + */ +int +UserFreeCopyBuffer(u) +struct acluser *u; +{ + struct win *w; + struct paster *pa; + + if (!u->u_plop.buf) + return 1; + for (w = windows; w; w = w->w_next) + { + pa = &w->w_paster; + if (pa->pa_pasteptr >= u->u_plop.buf && + pa->pa_pasteptr - u->u_plop.buf < u->u_plop.len) + FreePaster(pa); + } + free((char *)u->u_plop.buf); + u->u_plop.len = 0; + u->u_plop.buf = 0; + return 0; +} +#endif /* COPY_PASTE */ + +#ifdef MULTIUSER +/* + * Traverses group nodes. It searches for a node that references user u. + * If recursive is true, nodes found in the users are also searched using + * depth first method. If none of the nodes references u, the address of + * the last next pointer is returned. This address will contain NULL. + */ +static struct aclusergroup ** +FindGroupPtr(gp, u, recursive) +struct aclusergroup **gp; +struct acluser *u; +int recursive; +{ + struct aclusergroup **g; + + ASSERT(recursive < 1000); /* Ouch, cycle detection failed */ + while (*gp) + { + if ((*gp)->u == u) + return gp; /* found him here. */ + if (recursive && + *(g = FindGroupPtr(&(*gp)->u->u_group, u, recursive + 1))) + return g; /* found him there. */ + gp = &(*gp)->next; + } + return gp; /* *gp is NULL */ +} + +static int +PasswordMatches(pw, password) +const char *pw, *password; +{ + if (!*password) + return 0; + char *buf = crypt((char *)pw, (char *)password); + return (buf && !strcmp(buf, password)); +} + +/* + * Returns nonzero if failed or already linked. + * Both users are created on demand. + * Cyclic links are prevented. + */ +int +AclLinkUser(from, to) +char *from, *to; +{ + struct acluser **u1, **u2; + struct aclusergroup **g; + + if (!*(u1 = FindUserPtr(from)) && UserAdd(from, NULL, u1)) + return -1; + if (!*(u2 = FindUserPtr(to)) && UserAdd(to, NULL, u2)) + return -1; /* hmm, could not find both users. */ + + if (*FindGroupPtr(&(*u2)->u_group, *u1, 1)) + return 1; /* cyclic link detected! */ + if (*(g = FindGroupPtr(&(*u1)->u_group, *u2, 0))) + return 2; /* aha, we are already linked! */ + + if (!(*g = (struct aclusergroup *)malloc(sizeof(struct aclusergroup)))) + return -1; /* Could not alloc link. Poor screen */ + (*g)->u = (*u2); + (*g)->next = NULL; + return 0; +} + +/* + * The user pointer stored at *up will be substituted by a pointer + * to the named user's structure, if passwords match. + * returns NULL if successfull, an static error string otherwise + */ +char * +DoSu(up, name, pw1, pw2) +struct acluser **up; +char *name, *pw1, *pw2; +{ + struct acluser *u; + int sorry = 0; + + if (!(u = *FindUserPtr(name))) + sorry++; + else + { +#ifdef CHECKLOGIN + struct passwd *pp; +#ifdef SHADOWPW + struct spwd *ss; + int t, c; +#endif + char *pass = ""; + + if (!(pp = getpwnam(name))) + { + debug1("getpwnam(\"%s\") failed\n", name); + if (!(pw1 && *pw1 && *pw1 != '\377')) + { + debug("no unix account, no screen passwd\n"); + sorry++; + } + } + else + pass = pp->pw_passwd; +#ifdef SHADOWPW + for (t = 0; t < 13; t++) + { + c = pass[t]; + if (!(c == '.' || c == '/' || + (c >= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z'))) + break; + } + if (t < 13) + { + if (!(ss = getspnam(name))) + { + debug1("getspnam(\"%s\") failed\n", name); + sorry++; + } + else + pass = ss->sp_pwdp; + } +#endif /* SHADOWPW */ + + if (pw2 && *pw2 && *pw2 != '\377') /* provided a system password */ + { + if (!PasswordMatches(pw2, pass)) + { + debug("System password mismatch\n"); + sorry++; + } + } + else /* no pasword provided */ + if (*pass) /* but need one */ + sorry++; +#endif /* CHECKLOGIN */ + if (pw1 && *pw1 && *pw1 != '\377') /* provided a screen password */ + { + if (!PasswordMatches(pw1, u->u_password)) + { + debug("screen password mismatch\n"); + sorry++; + } + } + else /* no pasword provided */ + if (*u->u_password) /* but need one */ + sorry++; + } + + debug2("syslog(LOG_NOTICE, \"screen %s: \"su %s\" ", SockPath, name); + debug2("%s for \"%s\"\n", sorry ? "failed" : "succeeded", (*up)->u_name); +#ifndef NOSYSLOG +# ifdef BSD_42 + openlog("screen", LOG_PID); +# else + openlog("screen", LOG_PID, LOG_AUTH); +# endif /* BSD_42 */ + syslog(LOG_NOTICE, "%s: \"su %s\" %s for \"%s\"", SockPath, name, + sorry ? "failed" : "succeeded", (*up)->u_name); + closelog(); +#else + debug("NOT LOGGED.\n"); +#endif /* NOSYSLOG */ + + if (sorry) + return "Sorry."; + else + *up = u; /* substitute user now */ + return NULL; +} +#endif /* MULTIUSER */ + +/************************************************************************ + * end of user managing code * + ************************************************************************/ + + +#ifdef MULTIUSER + +/* This gives the users default rights to the new window w created by u */ +int +NewWindowAcl(w, u) +struct win *w; +struct acluser *u; +{ + int i, j; + + debug2("NewWindowAcl %s's umask_w_bits for window %d\n", + u ? u->u_name : "everybody", w->w_number); + + /* keep these in sync with UserAdd part five. */ + if (GrowBitfield(&w->w_mon_notify, 0, maxusercount, 0) || + GrowBitfield(&w->w_lio_notify, 0, maxusercount, 0)) + return -1; + for (j = 0; j < ACL_BITS_PER_WIN; j++) + { + /* we start with len 0 for the new bitfield size and add maxusercount */ + if (GrowBitfield(&w->w_userbits[j], 0, maxusercount, 0)) + { + while (--j >= 0) + free((char *)w->w_userbits[j]); + free((char *)w->w_mon_notify); + free((char *)w->w_lio_notify); + return -1; + } + for (i = 0; i < maxusercount; i++) + if (u ? (ACLBIT(i) & ACLBYTE(u->u_umask_w_bits[j], i)) : + default_w_bit[j]) + ACLBYTE(w->w_userbits[j], i) |= ACLBIT(i); + } + return 0; +} + +void +FreeWindowAcl(w) +struct win *w; +{ + int i; + + for (i = 0; i < ACL_BITS_PER_WIN; i++) + free((char *)w->w_userbits[i]); + free((char *)w->w_mon_notify); + free((char *)w->w_lio_notify); +} + + +/* if mode starts with '-' we remove the users exec bit for cmd */ +/* + * NOTE: before you make this function look the same as + * AclSetPermWin, try to merge both functions. + */ +static int +AclSetPermCmd(u, mode, cmd) +struct acluser *u; +char *mode; +struct comm *cmd; +{ + int neg = 0; + char *m = mode; + + while (*m) + { + switch (*m++) + { + case '-': + neg = 1; + continue; + case '+': + neg = 0; + continue; + case 'a': + case 'e': + case 'x': +/* debug3("AclSetPermCmd %s %s %s\n", u->u_name, mode, cmd->name); */ + if (neg) + ACLBYTE(cmd->userbits[ACL_EXEC], u->u_id) &= ~ACLBIT(u->u_id); + else + ACLBYTE(cmd->userbits[ACL_EXEC], u->u_id) |= ACLBIT(u->u_id); + break; + case 'r': + case 'w': + break; + default: + return -1; + } + } + return 0; +} + +/* mode strings of the form +rwx -w+rx r -wx are parsed and evaluated */ +/* + * aclchg nerd -w+w 2 + * releases a writelock on window 2 held by user nerd. + * Letter n allows network access on a window. + * uu should be NULL, except if you want to change his umask. + */ +static int +AclSetPermWin(uu, u, mode, win) +struct acluser *u, *uu; +char *mode; +struct win *win; +{ + int neg = 0; + int bit, bits; + AclBits *bitarray; + char *m = mode; + + if (uu) + { + debug3("AclSetPermWin %s UMASK %s %s\n", uu->u_name, u->u_name, mode); + bitarray = uu->u_umask_w_bits; + } + else + { + ASSERT(win); + bitarray = win->w_userbits; + debug3("AclSetPermWin %s %s %d\n", u->u_name, mode, win->w_number); + } + + while (*m) + { + switch (*m++) + { + case '-': + neg = 1; + continue; + case '+': + neg = 0; + continue; + case 'r': + bits = (1 << ACL_READ); + break; + case 'w': + bits = (1 << ACL_WRITE); + break; + case 'x': + bits = (1 << ACL_EXEC); + break; + case 'a': + bits = (1 << ACL_BITS_PER_WIN) - 1; + break; + default: + return -1; + } + for (bit = 0; bit < ACL_BITS_PER_WIN; bit++) + { + if (!(bits & (1 << bit))) + continue; + if (neg) + ACLBYTE(bitarray[bit], u->u_id) &= ~ACLBIT(u->u_id); + else + ACLBYTE(bitarray[bit], u->u_id) |= ACLBIT(u->u_id); + if (!uu && (win->w_wlockuser == u) && neg && (bit == ACL_WRITE)) + { + debug2("%s lost writelock on win %d\n", u->u_name, win->w_number); + win->w_wlockuser = NULL; + if (win->w_wlock == WLOCK_ON) + win->w_wlock = WLOCK_AUTO; + } + } + } + if (uu && u->u_name[0] == '?' && u->u_name[1] == '\0') + { + /* + * It is Mr. '?', the unknown user. He deserves special treatment as + * he defines the defaults. Sorry, this is global, not per user. + */ + if (win) + { + debug1("AclSetPermWin: default_w_bits '%s'.\n", mode); + for (bit = 0; bit < ACL_BITS_PER_WIN; bit++) + default_w_bit[bit] = + (ACLBYTE(bitarray[bit], u->u_id) & ACLBIT(u->u_id)) ? 1 : 0; + } + else + { + /* + * Hack. I do not want to duplicate all the above code for + * AclSetPermCmd. This asumes that there are not more bits + * per cmd than per win. + */ + debug1("AclSetPermWin: default_c_bits '%s'.\n", mode); + for (bit = 0; bit < ACL_BITS_PER_CMD; bit++) + default_c_bit[bit] = + (ACLBYTE(bitarray[bit], u->u_id) & ACLBIT(u->u_id)) ? 1 : 0; + } + UserDel(u->u_name, NULL); + } + return 0; +} + +/* + * String is broken down into comand and window names, mode applies + * A command name matches first, so do not use these as window names. + * uu should be NULL, except if you want to change his umask. + */ +int +AclSetPerm(uu, u, mode, s) +struct acluser *uu, *u; +char *mode, *s; +{ + struct win *w; + int i; + char *p, ch; + + debug3("AclSetPerm(uu, user '%s', mode '%s', object '%s')\n", + u->u_name, mode, s); + while (*s) + { + switch (*s) + { + case '*': /* all windows and all commands */ + return AclSetPerm(uu, u, mode, "#?"); + case '#': + if (uu) /* window umask or .. */ + AclSetPermWin(uu, u, mode, (struct win *)1); + else /* .. or all windows */ + for (w = windows; w; w = w->w_next) + AclSetPermWin((struct acluser *)0, u, mode, w); + s++; + break; + case '?': + if (uu) /* command umask or .. */ + AclSetPermWin(uu, u, mode, (struct win *)0); + else /* .. or all commands */ + for (i = 0; i <= RC_LAST; i++) + AclSetPermCmd(u, mode, &comms[i]); + s++; + break; + default: + for (p = s; *p && *p != ' ' && *p != '\t' && *p != ','; p++) + ; + if ((ch = *p)) + *p++ = '\0'; + if ((i = FindCommnr(s)) != RC_ILLEGAL) + AclSetPermCmd(u, mode, &comms[i]); + else if (((i = WindowByNoN(s)) >= 0) && wtab[i]) + AclSetPermWin((struct acluser *)0, u, mode, wtab[i]); + else + /* checking group name */ + return -1; + if (ch) + p[-1] = ch; + s = p; + } + } + return 0; +} + +/* + * Generic ACL Manager: + * + * This handles acladd and aclchg identical. + * With 2 or 4 parameters, the second parameter is a password. + * With 3 or 4 parameters the last two parameters specify the permissions + * else user is added with full permissions. + * With 1 parameter the users permissions are copied from user *argv. + * Unlike the other cases, u->u_name should not match *argv here. + * uu should be NULL, except if you want to change his umask. + */ +static int +UserAcl(uu, u, argc, argv) +struct acluser *uu, **u; +int argc; +char **argv; +{ + if ((*u && !strcmp((*u)->u_name, "nobody")) || + (argc > 1 && !strcmp(argv[0], "nobody"))) + return -1; /* do not change nobody! */ + + switch (argc) + { + case 1+1+2: + debug2("UserAcl: user '%s', password '%s':", argv[0], argv[1]); + return (UserAdd(argv[0], argv[1], u) < 0) || + AclSetPerm(uu, *u, argv[2], argv[3]); + case 1+2: + debug1("UserAcl: user '%s', no password:", argv[0]); + return (UserAdd(argv[0], NULL, u) < 0) || + AclSetPerm(uu, *u, argv[1], argv[2]); + case 1+1: + debug2("UserAcl: user '%s', password '%s'\n", argv[0], argv[1]); + return UserAdd(argv[0], argv[1], u) < 0; + case 1: + debug1("UserAcl: user '%s', no password:", argv[0]); + return (UserAdd(argv[0], NULL, u) < 0) || + AclSetPerm(uu, *u, "+a", "#?"); + default: + return -1; + } +} + +static int +UserAclCopy(to_up, from_up) +struct acluser **to_up, **from_up; +{ + struct win *w; + int i, j, to_id, from_id; + + if (!*to_up || !*from_up) + return -1; + debug2("UserAclCopy: from user '%s' to user '%s'\n", + (*from_up)->u_name, (*to_up)->u_name); + if ((to_id = (*to_up)->u_id) == (from_id = (*from_up)->u_id)) + return -1; + for (w = windows; w; w = w->w_next) + { + for (i = 0; i < ACL_BITS_PER_WIN; i++) + { + if (ACLBYTE(w->w_userbits[i], from_id) & ACLBIT(from_id)) + ACLBYTE(w->w_userbits[i], to_id) |= ACLBIT(to_id); + else + { + ACLBYTE(w->w_userbits[i], to_id) &= ~ACLBIT(to_id); + if ((w->w_wlockuser == *to_up) && (i == ACL_WRITE)) + { + debug2("%s lost wlock on win %d\n", + (*to_up)->u_name, w->w_number); + w->w_wlockuser = NULL; + if (w->w_wlock == WLOCK_ON) + w->w_wlock = WLOCK_AUTO; + } + } + } + } + for (j = 0; j <= RC_LAST; j++) + { + for (i = 0; i < ACL_BITS_PER_CMD; i++) + { + if (ACLBYTE(comms[j].userbits[i], from_id) & ACLBIT(from_id)) + ACLBYTE(comms[j].userbits[i], to_id) |= ACLBIT(to_id); + else + ACLBYTE(comms[j].userbits[i], to_id) &= ~ACLBIT(to_id); + } + } + + return 0; +} + +/* + * Syntax: + * user [password] [+rwx #?] + * * [password] [+rwx #?] + * user1,user2,user3 [password] [+rwx #?] + * user1,user2,user3=user + * uu should be NULL, except if you want to change his umask. + */ +int +UsersAcl(uu, argc, argv) +struct acluser *uu; +int argc; +char **argv; +{ + char *s; + int r; + struct acluser **cf_u = NULL; + + if (argc == 1) + { + char *p = NULL; + + s = argv[0]; + while (*s) + if (*s++ == '=') p = s; + if (p) + { + p[-1] = '\0'; + cf_u = FindUserPtr(p); + } + } + + if (argv[0][0] == '*' && argv[0][1] == '\0') + { + struct acluser **u; + + debug("all users acls.\n"); + for (u = &users; *u; u = &(*u)->u_next) + if (strcmp("nobody", (*u)->u_name) && + ((cf_u) ? + ((r = UserAclCopy(u, cf_u)) < 0) : + ((r = UserAcl(uu, u, argc, argv)) < 0))) + return -1; + return 0; + } + + do + { + for (s = argv[0]; *s && *s!=' ' && *s!='\t' && *s!=',' && *s!='='; s++) + ; + *s ? (*s++ = '\0') : (*s = '\0'); + debug2("UsersAcl(uu, \"%s\", argc=%d)\n", argv[0], argc); + if ((cf_u) ? + ((r = UserAclCopy(FindUserPtr(argv[0]), cf_u)) < 0) : + ((r = UserAcl(uu, FindUserPtr(argv[0]), argc, argv)) < 0)) + return -1; + } while (*(argv[0] = s)); + return 0; +} + +/* + * Preprocess argments, so that umask can be set with UsersAcl + * + * all current users umask ±rwxn + * one specific user umask user1±rwxn + * several users umask user1,user2,...±rwxn + * default_w_bits umask ?±rwxn + * default_c_bits umask ??±rwxn + */ +int +AclUmask(u, str, errp) +struct acluser *u; +char *str; +char **errp; +{ + char mode[16]; + char *av[3]; + char *p, c = '\0'; + + /* split str into user and bits section. */ + for (p = str; *p; p++) + if ((c = *p) == '+' || c == '-') + break; + if (!*p) + { + *errp = "Bad argument. Should be ``[user[,user...]{+|-}rwxn''."; + return -1; + } + strncpy(mode, p, 15); + mode[15] = '\0'; + *p = '\0'; + + /* construct argument vector */ + if (!strcmp("??", str)) + { + str++; + av[2] = "?"; + } + else + av[2] = "#"; + av[1] = mode; + av[0] = *str ? str : "*"; + /* call UsersAcl */ + if (UsersAcl(u, 3, av)) + { + *errp = "UsersAcl failed. Hmmm."; + *p = c; + return -1; + } + *p = c; + return 0; +} + +void +AclWinSwap(a, b) +int a, b; +{ + debug2("AclWinSwap(%d, %d) NOP.\n", a, b); +} + +struct acluser *EffectiveAclUser = NULL; /* hook for AT command permission */ + +int +AclCheckPermWin(u, mode, w) +struct acluser *u; +int mode; +struct win *w; +{ + int ok; + + if (mode < 0 || mode >= ACL_BITS_PER_WIN) + return -1; + if (EffectiveAclUser) + { + debug1("AclCheckPermWin: WARNING user %s overridden!\n", u->u_name); + u = EffectiveAclUser; + } + ok = ACLBYTE(w->w_userbits[mode], u->u_id) & ACLBIT(u->u_id); + debug3("AclCheckPermWin(%s, %d, %d) = ", u->u_name, mode, w->w_number); + + if (!ok) + { + struct aclusergroup **g = &u->u_group; + struct acluser *saved_eff = EffectiveAclUser; + + EffectiveAclUser = NULL; + while (*g) + { + if (!AclCheckPermWin((*g)->u, mode, w)) + break; + g = &(*g)->next; + } + EffectiveAclUser = saved_eff; + if (*g) + ok = 1; + } + debug1("%d\n", !ok); + return !ok; +} + +int +AclCheckPermCmd(u, mode, c) +struct acluser *u; +int mode; +struct comm *c; +{ + int ok; + + if (mode < 0 || mode >= ACL_BITS_PER_CMD) + return -1; + if (EffectiveAclUser) + { + debug1("AclCheckPermCmd: WARNING user %s overridden!\n", u->u_name); + u = EffectiveAclUser; + } + ok = ACLBYTE(c->userbits[mode], u->u_id) & ACLBIT(u->u_id); + debug3("AclCheckPermCmd(%s %d %s) = ", u->u_name, mode, c->name); + if (!ok) + { + struct aclusergroup **g = &u->u_group; + struct acluser *saved_eff = EffectiveAclUser; + + EffectiveAclUser = NULL; + while (*g) + { + if (!AclCheckPermCmd((*g)->u, mode, c)) + break; + g = &(*g)->next; + } + EffectiveAclUser = saved_eff; + if (*g) + ok = 1; + } + debug1("%d\n", !ok); + return !ok; +} + +#endif /* MULTIUSER */ @@ -0,0 +1,98 @@ +/* Copyright (c) 2008, 2009 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Micah Cowan (micah@cowan.name) + * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) + * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, see + * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + */ + +#ifdef MULTIUSER + +/* three known bits: */ +#define ACL_EXEC 0 +#define ACL_WRITE 1 +#define ACL_READ 2 + +#define ACL_BITS_PER_CMD 1 /* for comm.h */ +#define ACL_BITS_PER_WIN 3 /* for window.h */ + +#define USER_CHUNK 8 + +#define ACLBYTE(data, w) ((data)[(w) >> 3]) +#define ACLBIT(w) (0x80 >> ((w) & 7)) + +typedef unsigned char * AclBits; + +/* + * How a user joins a group. + * Here is the node to construct one list per user. + */ +struct aclusergroup +{ + struct acluser *u; /* the user who borrows us his rights */ + struct aclusergroup *next; +}; +#endif /* MULTIUSER */ + +/*************** + * ==> user.h + */ + +/* + * a copy buffer + */ +struct plop +{ + char *buf; + int len; +#ifdef ENCODINGS + int enc; +#endif +}; + +/* + * A User has a list of groups, and points to other users. + * users is the User entry of the session owner (creator) + * and anchors all other users. Add/Delete users there. + */ +typedef struct acluser +{ + struct acluser *u_next; /* continue the main user list */ + char u_name[MAXLOGINLEN + 1]; /* login name how he showed up */ + char *u_password; /* his password (may be NullStr). */ + int u_checkpassword; /* nonzero if this u_password is valid */ + int u_detachwin; /* the window where he last detached */ + int u_detachotherwin; /* window that was "other" when he detached */ + int u_Esc, u_MetaEsc; /* the users screen escape character */ +#ifdef COPY_PASTE + struct plop u_plop; +#endif +#ifdef MULTIUSER + int u_id; /* a uniq index in the bitfields. */ + AclBits u_umask_w_bits[ACL_BITS_PER_WIN]; /* his window create umask */ + struct aclusergroup *u_group; /* linked list of pointers to other users */ +#endif +} User; + +extern int DefaultEsc, DefaultMetaEsc; + @@ -0,0 +1,3207 @@ +/* Copyright (c) 2008, 2009 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Micah Cowan (micah@cowan.name) + * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) + * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, see + * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + */ + +#include <sys/types.h> +#include <fcntl.h> +#ifndef sun /* we want to know about TIOCPKT. */ +# include <sys/ioctl.h> +#endif + +#include "config.h" +#include "screen.h" +#include "braille.h" +#include "extern.h" +#include "logfile.h" + +extern struct display *display, *displays; +extern struct win *fore; /* for 83 escape */ +extern struct layer *flayer; /* for 83 escape */ + +extern struct NewWindow nwin_default; /* for ResetWindow() */ +extern int nversion; /* numerical version of screen */ +extern int log_flush, logtstamp_on, logtstamp_after; +extern char *logtstamp_string; +extern char *captionstring; +extern char *hstatusstring; +extern char *wliststr; +#ifdef COPY_PASTE +extern int compacthist; +#endif +#ifdef MULTIUSER +extern struct acluser *EffectiveAclUser; +#endif + +/* widths for Z0/Z1 switching */ +const int Z0width = 132; +const int Z1width = 80; + +/* globals set in WriteString */ +static struct win *curr; /* window we are working on */ +static int rows, cols; /* window size of the curr window */ + +int visual_bell = 0; +int use_hardstatus = 1; /* display status line in hs */ +char *printcmd = 0; +int use_altscreen = 0; /* enable alternate screen support? */ + +unsigned char *blank; /* line filled with spaces */ +unsigned char *null; /* line filled with '\0' */ + +struct mline mline_old; +struct mline mline_blank; +struct mline mline_null; + +struct mchar mchar_null; +struct mchar mchar_blank = {' ' /* , 0, 0, ... */}; +struct mchar mchar_so = {' ', A_SO /* , 0, 0, ... */}; + +int renditions[NUM_RENDS] = {65529 /* =ub */, 65531 /* =b */, 65533 /* =u */ }; + +/* keep string_t and string_t_string in sync! */ +static char *string_t_string[] = +{ + "NONE", + "DCS", /* Device control string */ + "OSC", /* Operating system command */ + "APC", /* Application program command */ + /* - used for status change */ + "PM", /* Privacy message */ + "AKA", /* title for current screen */ + "GM", /* Global message to every display */ + "STATUS" /* User hardstatus line */ +}; + +/* keep state_t and state_t_string in sync! */ +static char *state_t_string[] = +{ + "LIT", /* Literal input */ + "ESC", /* Start of escape sequence */ + "ASTR", /* Start of control string */ + "STRESC", /* ESC seen in control string */ + "CSI", /* Reading arguments in "CSI Pn ;...*/ + "PRIN", /* Printer mode */ + "PRINESC", /* ESC seen in printer mode */ + "PRINCSI", /* CSI seen in printer mode */ + "PRIN4" /* CSI 4 seen in printer mode */ +}; + +static int Special __P((int)); +static void DoESC __P((int, int)); +static void DoCSI __P((int, int)); +static void StringStart __P((enum string_t)); +static void StringChar __P((int)); +static int StringEnd __P((void)); +static void PrintStart __P((void)); +static void PrintChar __P((int)); +static void PrintFlush __P((void)); +#ifdef FONT +static void DesignateCharset __P((int, int)); +static void MapCharset __P((int)); +static void MapCharsetR __P((int)); +#endif +static void SaveCursor __P((struct cursor *)); +static void RestoreCursor __P((struct cursor *)); +static void BackSpace __P((void)); +static void Return __P((void)); +static void LineFeed __P((int)); +static void ReverseLineFeed __P((void)); +static void InsertChar __P((int)); +static void DeleteChar __P((int)); +static void DeleteLine __P((int)); +static void InsertLine __P((int)); +static void Scroll __P((char *, int, int, char *)); +static void ForwardTab __P((void)); +static void BackwardTab __P((void)); +static void ClearScreen __P((void)); +static void ClearFromBOS __P((void)); +static void ClearToEOS __P((void)); +static void ClearLineRegion __P((int, int)); +static void CursorRight __P((int)); +static void CursorUp __P((int)); +static void CursorDown __P((int)); +static void CursorLeft __P((int)); +static void ASetMode __P((int)); +static void SelectRendition __P((void)); +static void RestorePosRendition __P((void)); +static void FillWithEs __P((void)); +static void FindAKA __P((void)); +static void Report __P((char *, int, int)); +static void ScrollRegion __P((int)); +#ifdef COPY_PASTE +static void WAddLineToHist __P((struct win *, struct mline *)); +#endif +static void WLogString __P((struct win *, char *, int)); +static void WReverseVideo __P((struct win *, int)); +static int WindowChangedCheck __P((char *, int, int *)); +static void MFixLine __P((struct win *, int, struct mchar *)); +static void MScrollH __P((struct win *, int, int, int, int, int)); +static void MScrollV __P((struct win *, int, int, int, int)); +static void MClearArea __P((struct win *, int, int, int, int, int)); +static void MInsChar __P((struct win *, struct mchar *, int, int)); +static void MPutChar __P((struct win *, struct mchar *, int, int)); +static void MPutStr __P((struct win *, char *, int, struct mchar *, int, int)); +static void MWrapChar __P((struct win *, struct mchar *, int, int, int, int)); +#ifdef COLOR +static void MBceLine __P((struct win *, int, int, int, int)); +#endif + +#ifdef COLOR +# define CURR_BCE (curr->w_bce ? rend_getbg(&curr->w_rend) : 0) +#else +# define CURR_BCE 0 +#endif + +void +ResetAnsiState(p) +struct win *p; +{ + p->w_state = LIT; + p->w_StringType = NONE; +} + +void +ResetWindow(p) +register struct win *p; +{ + register int i; + + p->w_wrap = nwin_default.wrap; + p->w_origin = 0; + p->w_insert = 0; + p->w_revvid = 0; + p->w_mouse = 0; + p->w_curinv = 0; + p->w_curvvis = 0; + p->w_autolf = 0; + p->w_keypad = 0; + p->w_cursorkeys = 0; + p->w_top = 0; + p->w_bot = p->w_height - 1; + p->w_saved.on = 0; + p->w_x = p->w_y = 0; + p->w_state = LIT; + p->w_StringType = NONE; + bzero(p->w_tabs, p->w_width); + for (i = 8; i < p->w_width; i += 8) + p->w_tabs[i] = 1; + p->w_rend = mchar_null; +#ifdef FONT + ResetCharsets(p); +#endif +#ifdef COLOR + p->w_bce = nwin_default.bce; +#endif +} + +/* adds max 22 bytes */ +int +GetAnsiStatus(w, buf) +struct win *w; +char *buf; +{ + char *p = buf; + + if (w->w_state == LIT) + return 0; + + strcpy(p, state_t_string[w->w_state]); + p += strlen(p); + if (w->w_intermediate) + { + *p++ = '-'; + if (w->w_intermediate > 0xff) + p += AddXChar(p, w->w_intermediate >> 8); + p += AddXChar(p, w->w_intermediate & 0xff); + *p = 0; + } + if (w->w_state == ASTR || w->w_state == STRESC) + sprintf(p, "-%s", string_t_string[w->w_StringType]); + p += strlen(p); + return p - buf; +} + + +#ifdef FONT + +void +ResetCharsets(p) +struct win *p; +{ + p->w_gr = nwin_default.gr; + p->w_c1 = nwin_default.c1; + SetCharsets(p, "BBBB02"); + if (nwin_default.charset) + SetCharsets(p, nwin_default.charset); +#ifdef ENCODINGS + ResetEncoding(p); +#endif +} + +void +SetCharsets(p, s) +struct win *p; +char *s; +{ + int i; + + for (i = 0; i < 4 && *s; i++, s++) + if (*s != '.') + p->w_charsets[i] = ((*s == 'B') ? ASCII : *s); + if (*s && *s++ != '.') + p->w_Charset = s[-1] - '0'; + if (*s && *s != '.') + p->w_CharsetR = *s - '0'; + p->w_ss = 0; + p->w_FontL = p->w_charsets[p->w_Charset]; + p->w_FontR = p->w_charsets[p->w_CharsetR]; +} +#endif /* FONT */ + +/*****************************************************************/ + + +/* + * Here comes the vt100 emulator + * - writes logfiles, + * - sets timestamp and flags activity in window. + * - record program output in window scrollback + * - translate program output for the display and put it into the obuf. + * + */ +void +WriteString(wp, buf, len) +struct win *wp; +register char *buf; +register int len; +{ + register int c; +#ifdef FONT + register int font; +#endif + struct canvas *cv; + + if (!len) + return; + if (wp->w_log) + WLogString(wp, buf, len); + + /* set global variables (yuck!) */ + curr = wp; + cols = curr->w_width; + rows = curr->w_height; + + if (curr->w_silence) + SetTimeout(&curr->w_silenceev, curr->w_silencewait * 1000); + + if (curr->w_monitor == MON_ON) + { + debug2("ACTIVITY %d %d\n", curr->w_monitor, curr->w_bell); + curr->w_monitor = MON_FOUND; + } + + if (cols > 0 && rows > 0) + { + do + { + c = (unsigned char)*buf++; +#ifdef FONT +# ifdef DW_CHARS + if (!curr->w_mbcs) +# endif + curr->w_rend.font = curr->w_FontL; /* Default: GL */ +#endif + + /* The next part is only for speedup */ + if (curr->w_state == LIT && +#ifdef UTF8 + curr->w_encoding != UTF8 && +#endif +#ifdef DW_CHARS + !is_dw_font(curr->w_rend.font) && +# ifdef ENCODINGS + curr->w_rend.font != KANA && !curr->w_mbcs && +# endif +#endif +#ifdef FONT + curr->w_rend.font != '<' && +#endif + c >= ' ' && c != 0x7f && + ((c & 0x80) == 0 || ((c >= 0xa0 || !curr->w_c1) && !curr->w_gr)) && !curr->w_ss && + !curr->w_insert && curr->w_x < cols - 1) + { + register int currx = curr->w_x; + char *imp = buf - 1; + + while (currx < cols - 1) + { + currx++; + if (--len == 0) + break; + c = (unsigned char)*buf++; + if (c < ' ' || c == 0x7f || ((c & 0x80) && ((c < 0xa0 && curr->w_c1) || curr->w_gr))) + break; + } + currx -= curr->w_x; + if (currx > 0) + { + MPutStr(curr, imp, currx, &curr->w_rend, curr->w_x, curr->w_y); + LPutStr(&curr->w_layer, imp, currx, &curr->w_rend, curr->w_x, curr->w_y); + curr->w_x += currx; + } + if (len == 0) + break; + } + /* end of speedup code */ + +#ifdef UTF8 + if (curr->w_encoding == UTF8) + { + c = FromUtf8(c, &curr->w_decodestate); + if (c == -1) + continue; + if (c == -2) + { + c = UCS_REPL; + /* try char again */ + buf--; + len++; + } + if (c > 0xff) + debug1("read UNICODE %04x\n", c); + } +#endif + + tryagain: + switch (curr->w_state) + { + case PRIN: + switch (c) + { + case '\033': + curr->w_state = PRINESC; + break; + default: + PrintChar(c); + } + break; + case PRINESC: + switch (c) + { + case '[': + curr->w_state = PRINCSI; + break; + default: + PrintChar('\033'); + PrintChar(c); + curr->w_state = PRIN; + } + break; + case PRINCSI: + switch (c) + { + case '4': + curr->w_state = PRIN4; + break; + default: + PrintChar('\033'); + PrintChar('['); + PrintChar(c); + curr->w_state = PRIN; + } + break; + case PRIN4: + switch (c) + { + case 'i': + curr->w_state = LIT; + PrintFlush(); + if (curr->w_pdisplay && curr->w_pdisplay->d_printfd >= 0) + { + close(curr->w_pdisplay->d_printfd); + curr->w_pdisplay->d_printfd = -1; + } + curr->w_pdisplay = 0; + break; + default: + PrintChar('\033'); + PrintChar('['); + PrintChar('4'); + PrintChar(c); + curr->w_state = PRIN; + } + break; + case ASTR: + if (c == 0) + break; + if (c == '\033') + { + curr->w_state = STRESC; + break; + } + /* special xterm hack: accept SetStatus sequence. Yucc! */ + /* allow ^E for title escapes */ + if (!(curr->w_StringType == OSC && c < ' ' && c != '\005')) + if (!curr->w_c1 || c != ('\\' ^ 0xc0)) + { + StringChar(c); + break; + } + c = '\\'; + /* FALLTHROUGH */ + case STRESC: + switch (c) + { + case '\\': + if (StringEnd() == 0 || len <= 1) + break; + /* check if somewhere a status is displayed */ + for (cv = curr->w_layer.l_cvlist; cv; cv = cv->c_lnext) + { + display = cv->c_display; + if (D_status == STATUS_ON_WIN) + break; + } + if (cv) + { + if (len > IOSIZE + 1) + len = IOSIZE + 1; + curr->w_outlen = len - 1; + bcopy(buf, curr->w_outbuf, len - 1); + return; /* wait till status is gone */ + } + break; + case '\033': + StringChar('\033'); + break; + default: + curr->w_state = ASTR; + StringChar('\033'); + StringChar(c); + break; + } + break; + case ESC: + switch (c) + { + case '[': + curr->w_NumArgs = 0; + curr->w_intermediate = 0; + bzero((char *) curr->w_args, MAXARGS * sizeof(int)); + curr->w_state = CSI; + break; + case ']': + StringStart(OSC); + break; + case '_': + StringStart(APC); + break; + case 'P': + StringStart(DCS); + break; + case '^': + StringStart(PM); + break; + case '!': + StringStart(GM); + break; + case '"': + case 'k': + StringStart(AKA); + break; + default: + if (Special(c)) + { + curr->w_state = LIT; + break; + } + debug1("not special. c = %x\n", c); + if (c >= ' ' && c <= '/') + { + if (curr->w_intermediate) + { +#ifdef DW_CHARS + if (curr->w_intermediate == '$') + c |= '$' << 8; + else +#endif + c = -1; + } + curr->w_intermediate = c; + } + else if (c >= '0' && c <= '~') + { + DoESC(c, curr->w_intermediate); + curr->w_state = LIT; + } + else + { + curr->w_state = LIT; + goto tryagain; + } + } + break; + case CSI: + switch (c) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (curr->w_NumArgs >= 0 && curr->w_NumArgs < MAXARGS) + { + if (curr->w_args[curr->w_NumArgs] < 100000000) + curr->w_args[curr->w_NumArgs] = + 10 * curr->w_args[curr->w_NumArgs] + (c - '0'); + } + break; + case ';': + case ':': + if (curr->w_NumArgs < MAXARGS) + curr->w_NumArgs++; + break; + default: + if (Special(c)) + break; + if (c >= '@' && c <= '~') + { + if (curr->w_NumArgs < MAXARGS) + curr->w_NumArgs++; + DoCSI(c, curr->w_intermediate); + if (curr->w_state != PRIN) + curr->w_state = LIT; + } + else if ((c >= ' ' && c <= '/') || (c >= '<' && c <= '?')) + curr->w_intermediate = curr->w_intermediate ? -1 : c; + else + { + curr->w_state = LIT; + goto tryagain; + } + } + break; + case LIT: + default: +#ifdef DW_CHARS + if (curr->w_mbcs) + if (c <= ' ' || c == 0x7f || (c >= 0x80 && c < 0xa0 && curr->w_c1)) + curr->w_mbcs = 0; +#endif + if (c < ' ') + { + if (c == '\033') + { + curr->w_intermediate = 0; + curr->w_state = ESC; + if (curr->w_autoaka < 0) + curr->w_autoaka = 0; + } + else + Special(c); + break; + } + if (c >= 0x80 && c < 0xa0 && curr->w_c1) +#ifdef FONT + if ((curr->w_FontR & 0xf0) != 0x20 +# ifdef UTF8 + || curr->w_encoding == UTF8 +# endif + ) +#endif + { + switch (c) + { + case 0xc0 ^ 'D': + case 0xc0 ^ 'E': + case 0xc0 ^ 'H': + case 0xc0 ^ 'M': + case 0xc0 ^ 'N': /* SS2 */ + case 0xc0 ^ 'O': /* SS3 */ + DoESC(c ^ 0xc0, 0); + break; + case 0xc0 ^ '[': + if (curr->w_autoaka < 0) + curr->w_autoaka = 0; + curr->w_NumArgs = 0; + curr->w_intermediate = 0; + bzero((char *) curr->w_args, MAXARGS * sizeof(int)); + curr->w_state = CSI; + break; + case 0xc0 ^ 'P': + StringStart(DCS); + break; + default: + break; + } + break; + } + +#ifdef FONT +# ifdef DW_CHARS + if (!curr->w_mbcs) + { +# endif + if (c < 0x80 || curr->w_gr == 0) + curr->w_rend.font = curr->w_FontL; +# ifdef ENCODINGS + else if (curr->w_gr == 2 && !curr->w_ss) + curr->w_rend.font = curr->w_FontE; +# endif + else + curr->w_rend.font = curr->w_FontR; +# ifdef DW_CHARS + } +# endif +# ifdef UTF8 + if (curr->w_encoding == UTF8) + { + if (curr->w_rend.font == '0') + { + struct mchar mc, *mcp; + + debug1("SPECIAL %x\n", c); + mc.image = c; + mc.mbcs = 0; + mc.font = '0'; + mc.fontx = 0; + mcp = recode_mchar(&mc, 0, UTF8); + debug2("%02x %02x\n", mcp->image, mcp->font); + c = mcp->image | mcp->font << 8; + } + curr->w_rend.font = 0; + } + if (curr->w_encoding == UTF8 && c >= 0x0300 && utf8_iscomb(c)) + { + int ox, oy; + struct mchar omc; + + ox = curr->w_x - 1; + oy = curr->w_y; + if (ox < 0) + { + ox = curr->w_width - 1; + oy--; + } + if (oy < 0) + oy = 0; + copy_mline2mchar(&omc, &curr->w_mlines[oy], ox); + if (omc.image == 0xff && omc.font == 0xff && omc.fontx == 0) + { + ox--; + if (ox >= 0) + { + copy_mline2mchar(&omc, &curr->w_mlines[oy], ox); + omc.mbcs = 0xff; + } + } + if (ox >= 0) + { + utf8_handle_comb(c, &omc); + MFixLine(curr, oy, &omc); + copy_mchar2mline(&omc, &curr->w_mlines[oy], ox); + LPutChar(&curr->w_layer, &omc, ox, oy); + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); + } + break; + } +# ifdef DW_CHARS + if (curr->w_encoding == UTF8 && utf8_isdouble(c)) + curr->w_mbcs = 0xff; +# endif + font = curr->w_rend.font; +# endif +# ifdef DW_CHARS +# ifdef ENCODINGS + if (font == KANA && curr->w_encoding == SJIS && curr->w_mbcs == 0) + { + /* Lets see if it is the first byte of a kanji */ + debug1("%x may be first of SJIS\n", c); + if ((0x81 <= c && c <= 0x9f) || (0xe0 <= c && c <= 0xef)) + { + debug("YES!\n"); + curr->w_mbcs = c; + break; + } + } +# endif + if (font == 031 && c == 0x80 && !curr->w_mbcs) + font = curr->w_rend.font = 0; + if (is_dw_font(font) && c == ' ') + font = curr->w_rend.font = 0; + if (is_dw_font(font) || curr->w_mbcs) + { + int t = c; + if (curr->w_mbcs == 0) + { + curr->w_mbcs = c; + break; + } + if (curr->w_x == cols - 1) + { + curr->w_x += curr->w_wrap ? 1 : -1; + debug1("Patched w_x to %d\n", curr->w_x); + } +# ifdef UTF8 + if (curr->w_encoding != UTF8) +# endif + { + c = curr->w_mbcs; +# ifdef ENCODINGS + if (font == KANA && curr->w_encoding == SJIS) + { + debug2("SJIS !! %x %x\n", c, t); + /* + * SJIS -> EUC mapping: + * First byte: + * 81,82...9f -> 21,23...5d + * e0,e1...ef -> 5f,61...7d + * Second byte: + * 40-7e -> 21-5f + * 80-9e -> 60-7e + * 9f-fc -> 21-7e (increment first byte!) + */ + if (0x40 <= t && t <= 0xfc && t != 0x7f) + { + if (c <= 0x9f) c = (c - 0x81) * 2 + 0x21; + else c = (c - 0xc1) * 2 + 0x21; + if (t <= 0x7e) t -= 0x1f; + else if (t <= 0x9e) t -= 0x20; + else t -= 0x7e, c++; + curr->w_rend.font = KANJI; + } + else + { + /* Incomplete shift-jis - skip first byte */ + c = t; + t = 0; + } + debug2("SJIS after %x %x\n", c, t); + } +# endif + if (t && curr->w_gr && font != 030 && font != 031) + { + t &= 0x7f; + if (t < ' ') + goto tryagain; + } + if (t == '\177') + break; + curr->w_mbcs = t; + } + } +# endif /* DW_CHARS */ + if (font == '<' && c >= ' ') + { + font = curr->w_rend.font = 0; + c |= 0x80; + } +# ifdef UTF8 + else if (curr->w_gr && curr->w_encoding != UTF8) +# else + else if (curr->w_gr) +# endif + { +#ifdef ENCODINGS + if (c == 0x80 && font == 0 && curr->w_encoding == GBK) + c = 0xa4; + else + c &= 0x7f; + if (c < ' ' && font != 031) + goto tryagain; +#else + c &= 0x7f; + if (c < ' ') /* this is ugly but kanji support */ + goto tryagain; /* prevents nicer programming */ +#endif + } +#endif /* FONT */ + if (c == '\177') + break; + curr->w_rend.image = c; +#ifdef UTF8 + if (curr->w_encoding == UTF8) + { + curr->w_rend.font = c >> 8; + curr->w_rend.fontx = c >> 16; + } +#endif +#ifdef DW_CHARS + curr->w_rend.mbcs = curr->w_mbcs; +#endif + if (curr->w_x < cols - 1) + { + if (curr->w_insert) + { + save_mline(&curr->w_mlines[curr->w_y], cols); + MInsChar(curr, &curr->w_rend, curr->w_x, curr->w_y); + LInsChar(&curr->w_layer, &curr->w_rend, curr->w_x, curr->w_y, &mline_old); + curr->w_x++; + } + else + { + MPutChar(curr, &curr->w_rend, curr->w_x, curr->w_y); + LPutChar(&curr->w_layer, &curr->w_rend, curr->w_x, curr->w_y); + curr->w_x++; + } + } + else if (curr->w_x == cols - 1) + { + MPutChar(curr, &curr->w_rend, curr->w_x, curr->w_y); + LPutChar(&curr->w_layer, &curr->w_rend, curr->w_x, curr->w_y); + if (curr->w_wrap) + curr->w_x++; + } + else + { + MWrapChar(curr, &curr->w_rend, curr->w_y, curr->w_top, curr->w_bot, curr->w_insert); + LWrapChar(&curr->w_layer, &curr->w_rend, curr->w_y, curr->w_top, curr->w_bot, curr->w_insert); + if (curr->w_y != curr->w_bot && curr->w_y != curr->w_height - 1) + curr->w_y++; + curr->w_x = 1; + } +#ifdef FONT +# ifdef DW_CHARS + if (curr->w_mbcs) + { + curr->w_rend.mbcs = curr->w_mbcs = 0; + curr->w_x++; + } +# endif + if (curr->w_ss) + { + curr->w_FontL = curr->w_charsets[curr->w_Charset]; + curr->w_FontR = curr->w_charsets[curr->w_CharsetR]; + curr->w_rend.font = curr->w_FontL; + LSetRendition(&curr->w_layer, &curr->w_rend); + curr->w_ss = 0; + } +#endif /* FONT */ + break; + } + } + while (--len); + } + if (!printcmd && curr->w_state == PRIN) + PrintFlush(); +} + +static void +WLogString(p, buf, len) +struct win *p; +char *buf; +int len; +{ + if (!p->w_log) + return; + if (logtstamp_on && p->w_logsilence >= logtstamp_after * 2) + { + char *t = MakeWinMsg(logtstamp_string, p, '%'); + logfwrite(p->w_log, t, strlen(t)); /* long time no write */ + } + p->w_logsilence = 0; + if (logfwrite(p->w_log, buf, len) < 1) + { + WMsg(p, errno, "Error writing logfile"); + logfclose(p->w_log); + p->w_log = 0; + } + if (!log_flush) + logfflush(p->w_log); +} + +static int +Special(c) +register int c; +{ + switch (c) + { + case '\b': + BackSpace(); + return 1; + case '\r': + Return(); + return 1; + case '\n': + if (curr->w_autoaka) + FindAKA(); + case '\013': /* Vertical tab is the same as Line Feed */ + LineFeed(0); + return 1; + case '\007': + WBell(curr, visual_bell); + return 1; + case '\t': + ForwardTab(); + return 1; +#ifdef FONT + case '\017': /* SI */ + MapCharset(G0); + return 1; + case '\016': /* SO */ + MapCharset(G1); + return 1; +#endif + } + return 0; +} + +static void +DoESC(c, intermediate) +int c, intermediate; +{ + debug2("DoESC: %x - inter = %x\n", c, intermediate); + switch (intermediate) + { + case 0: + switch (c) + { + case 'E': + LineFeed(1); + break; + case 'D': + LineFeed(0); + break; + case 'M': + ReverseLineFeed(); + break; + case 'H': + curr->w_tabs[curr->w_x] = 1; + break; + case 'Z': /* jph: Identify as VT100 */ + Report("\033[?%d;%dc", 1, 2); + break; + case '7': + SaveCursor(&curr->w_saved); + break; + case '8': + RestoreCursor(&curr->w_saved); + break; + case 'c': + ClearScreen(); + ResetWindow(curr); + LKeypadMode(&curr->w_layer, 0); + LCursorkeysMode(&curr->w_layer, 0); +#ifndef TIOCPKT + WNewAutoFlow(curr, 1); +#endif + /* XXX + SetRendition(&mchar_null); + InsertMode(0); + ChangeScrollRegion(0, rows - 1); + */ + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); + break; + case '=': + LKeypadMode(&curr->w_layer, curr->w_keypad = 1); +#ifndef TIOCPKT + WNewAutoFlow(curr, 0); +#endif /* !TIOCPKT */ + break; + case '>': + LKeypadMode(&curr->w_layer, curr->w_keypad = 0); +#ifndef TIOCPKT + WNewAutoFlow(curr, 1); +#endif /* !TIOCPKT */ + break; +#ifdef FONT + case 'n': /* LS2 */ + MapCharset(G2); + break; + case 'o': /* LS3 */ + MapCharset(G3); + break; + case '~': + MapCharsetR(G1); /* LS1R */ + break; + /* { */ + case '}': + MapCharsetR(G2); /* LS2R */ + break; + case '|': + MapCharsetR(G3); /* LS3R */ + break; + case 'N': /* SS2 */ + if (curr->w_charsets[curr->w_Charset] != curr->w_charsets[G2] + || curr->w_charsets[curr->w_CharsetR] != curr->w_charsets[G2]) + curr->w_FontR = curr->w_FontL = curr->w_charsets[curr->w_ss = G2]; + else + curr->w_ss = 0; + break; + case 'O': /* SS3 */ + if (curr->w_charsets[curr->w_Charset] != curr->w_charsets[G3] + || curr->w_charsets[curr->w_CharsetR] != curr->w_charsets[G3]) + curr->w_FontR = curr->w_FontL = curr->w_charsets[curr->w_ss = G3]; + else + curr->w_ss = 0; + break; +#endif /* FONT */ + case 'g': /* VBELL, private screen sequence */ + WBell(curr, 1); + break; + } + break; + case '#': + switch (c) + { + case '8': + FillWithEs(); + break; + } + break; +#ifdef FONT + case '(': + DesignateCharset(c, G0); + break; + case ')': + DesignateCharset(c, G1); + break; + case '*': + DesignateCharset(c, G2); + break; + case '+': + DesignateCharset(c, G3); + break; +# ifdef DW_CHARS +/* + * ESC $ ( Fn: invoke multi-byte charset, Fn, to G0 + * ESC $ Fn: same as above. (old sequence) + * ESC $ ) Fn: invoke multi-byte charset, Fn, to G1 + * ESC $ * Fn: invoke multi-byte charset, Fn, to G2 + * ESC $ + Fn: invoke multi-byte charset, Fn, to G3 + */ + case '$': + case '$'<<8 | '(': + DesignateCharset(c & 037, G0); + break; + case '$'<<8 | ')': + DesignateCharset(c & 037, G1); + break; + case '$'<<8 | '*': + DesignateCharset(c & 037, G2); + break; + case '$'<<8 | '+': + DesignateCharset(c & 037, G3); + break; +# endif +#endif /* FONT */ + } +} + +static void +DoCSI(c, intermediate) +int c, intermediate; +{ + register int i, a1 = curr->w_args[0], a2 = curr->w_args[1]; + + if (curr->w_NumArgs > MAXARGS) + curr->w_NumArgs = MAXARGS; + switch (intermediate) + { + case 0: + switch (c) + { + case 'H': + case 'f': + if (a1 < 1) + a1 = 1; + if (curr->w_origin) + a1 += curr->w_top; + if (a1 > rows) + a1 = rows; + if (a2 < 1) + a2 = 1; + if (a2 > cols) + a2 = cols; + LGotoPos(&curr->w_layer, --a2, --a1); + curr->w_x = a2; + curr->w_y = a1; + if (curr->w_autoaka) + curr->w_autoaka = a1 + 1; + break; + case 'J': + if (a1 < 0 || a1 > 2) + a1 = 0; + switch (a1) + { + case 0: + ClearToEOS(); + break; + case 1: + ClearFromBOS(); + break; + case 2: + ClearScreen(); + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); + break; + } + break; + case 'K': + if (a1 < 0 || a1 > 2) + a1 %= 3; + switch (a1) + { + case 0: + ClearLineRegion(curr->w_x, cols - 1); + break; + case 1: + ClearLineRegion(0, curr->w_x); + break; + case 2: + ClearLineRegion(0, cols - 1); + break; + } + break; + case 'X': + a1 = curr->w_x + (a1 ? a1 - 1 : 0); + ClearLineRegion(curr->w_x, a1 < cols ? a1 : cols - 1); + break; + case 'A': + CursorUp(a1 ? a1 : 1); + break; + case 'B': + CursorDown(a1 ? a1 : 1); + break; + case 'C': + CursorRight(a1 ? a1 : 1); + break; + case 'D': + CursorLeft(a1 ? a1 : 1); + break; + case 'E': + curr->w_x = 0; + CursorDown(a1 ? a1 : 1); /* positions cursor */ + break; + case 'F': + curr->w_x = 0; + CursorUp(a1 ? a1 : 1); /* positions cursor */ + break; + case 'G': + case '`': /* HPA */ + curr->w_x = a1 ? a1 - 1 : 0; + if (curr->w_x >= cols) + curr->w_x = cols - 1; + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); + break; + case 'd': /* VPA */ + curr->w_y = a1 ? a1 - 1 : 0; + if (curr->w_y >= rows) + curr->w_y = rows - 1; + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); + break; + case 'm': + SelectRendition(); + break; + case 'g': + if (a1 == 0) + curr->w_tabs[curr->w_x] = 0; + else if (a1 == 3) + bzero(curr->w_tabs, cols); + break; + case 'r': + if (!a1) + a1 = 1; + if (!a2) + a2 = rows; + if (a1 < 1 || a2 > rows || a1 >= a2) + break; + curr->w_top = a1 - 1; + curr->w_bot = a2 - 1; + /* ChangeScrollRegion(curr->w_top, curr->w_bot); */ + if (curr->w_origin) + { + curr->w_y = curr->w_top; + curr->w_x = 0; + } + else + curr->w_y = curr->w_x = 0; + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); + break; + case 's': + SaveCursor(&curr->w_saved); + break; + case 't': + switch(a1) + { + case 11: + if (curr->w_layer.l_cvlist) + Report("\033[1t", 0, 0); + else + Report("\033[2t", 0, 0); + break; + case 7: + LRefreshAll(&curr->w_layer, 0); + break; + case 21: + a1 = strlen(curr->w_title); + if ((unsigned)(curr->w_inlen + 5 + a1) <= sizeof(curr->w_inbuf)) + { + bcopy("\033]l", curr->w_inbuf + curr->w_inlen, 3); + bcopy(curr->w_title, curr->w_inbuf + curr->w_inlen + 3, a1); + bcopy("\033\\", curr->w_inbuf + curr->w_inlen + 3 + a1, 2); + curr->w_inlen += 5 + a1; + } + break; + case 8: + a1 = curr->w_args[2]; + if (a1 < 1) + a1 = curr->w_width; + if (a2 < 1) + a2 = curr->w_height; + if (a1 > 10000 || a2 > 10000) + break; + WChangeSize(curr, a1, a2); + cols = curr->w_width; + rows = curr->w_height; + break; + default: + break; + } + break; + case 'u': + RestoreCursor(&curr->w_saved); + break; + case 'I': + if (!a1) + a1 = 1; + while (a1--) + ForwardTab(); + break; + case 'Z': + if (!a1) + a1 = 1; + while (a1--) + BackwardTab(); + break; + case 'L': + InsertLine(a1 ? a1 : 1); + break; + case 'M': + DeleteLine(a1 ? a1 : 1); + break; + case 'P': + DeleteChar(a1 ? a1 : 1); + break; + case '@': + InsertChar(a1 ? a1 : 1); + break; + case 'h': + ASetMode(1); + break; + case 'l': + ASetMode(0); + break; + case 'i': /* MC Media Control */ + if (a1 == 5) + PrintStart(); + break; + case 'n': + if (a1 == 5) /* Report terminal status */ + Report("\033[0n", 0, 0); + else if (a1 == 6) /* Report cursor position */ + Report("\033[%d;%dR", curr->w_y + 1, curr->w_x + 1); + break; + case 'c': /* Identify as VT100 */ + if (a1 == 0) + Report("\033[?%d;%dc", 1, 2); + break; + case 'x': /* decreqtparm */ + if (a1 == 0 || a1 == 1) + Report("\033[%d;1;1;112;112;1;0x", a1 + 2, 0); + break; + case 'p': /* obscure code from a 97801 term */ + if (a1 == 6 || a1 == 7) + { + curr->w_curinv = 7 - a1; + LCursorVisibility(&curr->w_layer, curr->w_curinv ? -1 : curr->w_curvvis); + } + break; + case 'S': /* code from a 97801 term / DEC vt400 */ + ScrollRegion(a1 ? a1 : 1); + break; + case 'T': /* code from a 97801 term / DEC vt400 */ + case '^': /* SD as per ISO 6429 */ + ScrollRegion(a1 ? -a1 : -1); + break; + } + break; + case '?': + for (a2 = 0; a2 < curr->w_NumArgs; a2++) + { + a1 = curr->w_args[a2]; + debug2("\\E[?%d%c\n",a1,c); + if (c != 'h' && c != 'l') + break; + i = (c == 'h'); + switch (a1) + { + case 1: /* CKM: cursor key mode */ + LCursorkeysMode(&curr->w_layer, curr->w_cursorkeys = i); +#ifndef TIOCPKT + WNewAutoFlow(curr, !i); +#endif /* !TIOCPKT */ + break; + case 2: /* ANM: ansi/vt52 mode */ + if (i) + { +#ifdef FONT +# ifdef ENCODINGS + if (curr->w_encoding) + break; +# endif + curr->w_charsets[0] = curr->w_charsets[1] = + curr->w_charsets[2] = curr->w_charsets[3] = + curr->w_FontL = curr->w_FontR = ASCII; + curr->w_Charset = 0; + curr->w_CharsetR = 2; + curr->w_ss = 0; +#endif + } + break; + case 3: /* COLM: column mode */ + i = (i ? Z0width : Z1width); + ClearScreen(); + curr->w_x = 0; + curr->w_y = 0; + WChangeSize(curr, i, curr->w_height); + cols = curr->w_width; + rows = curr->w_height; + break; + /* case 4: SCLM: scrolling mode */ + case 5: /* SCNM: screen mode */ + if (i != curr->w_revvid) + WReverseVideo(curr, i); + curr->w_revvid = i; + break; + case 6: /* OM: origin mode */ + if ((curr->w_origin = i) != 0) + { + curr->w_y = curr->w_top; + curr->w_x = 0; + } + else + curr->w_y = curr->w_x = 0; + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); + break; + case 7: /* AWM: auto wrap mode */ + curr->w_wrap = i; + break; + /* case 8: ARM: auto repeat mode */ + /* case 9: INLM: interlace mode */ + case 9: /* X10 mouse tracking */ + curr->w_mouse = i ? 9 : 0; + LMouseMode(&curr->w_layer, curr->w_mouse); + break; + /* case 10: EDM: edit mode */ + /* case 11: LTM: line transmit mode */ + /* case 13: SCFDM: space compression / field delimiting */ + /* case 14: TEM: transmit execution mode */ + /* case 16: EKEM: edit key execution mode */ + /* case 18: PFF: Printer term form feed */ + /* case 19: PEX: Printer extend screen / scroll. reg */ + case 25: /* TCEM: text cursor enable mode */ + curr->w_curinv = !i; + LCursorVisibility(&curr->w_layer, curr->w_curinv ? -1 : curr->w_curvvis); + break; + /* case 34: RLM: Right to left mode */ + /* case 35: HEBM: hebrew keyboard map */ + /* case 36: HEM: hebrew encoding */ + /* case 38: TeK Mode */ + /* case 40: 132 col enable */ + /* case 42: NRCM: 7bit NRC character mode */ + /* case 44: margin bell enable */ + case 47: /* xterm-like alternate screen */ + case 1047: /* xterm-like alternate screen */ + case 1049: /* xterm-like alternate screen */ + if (use_altscreen) + { + if (i) + { + if (!curr->w_alt.on) { + SaveCursor(&curr->w_alt.cursor); + EnterAltScreen(curr); + } + } + else + { + if (curr->w_alt.on) { + RestoreCursor(&curr->w_alt.cursor); + LeaveAltScreen(curr); + } + } + if (a1 == 47 && !i) + curr->w_saved.on = 0; + LRefreshAll(&curr->w_layer, 0); + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); + } + break; + case 1048: + if (i) + SaveCursor(&curr->w_saved); + else + RestoreCursor(&curr->w_saved); + break; + /* case 66: NKM: Numeric keypad appl mode */ + /* case 68: KBUM: Keyboard usage mode (data process) */ + case 1000: /* VT200 mouse tracking */ + case 1001: /* VT200 highlight mouse */ + case 1002: /* button event mouse*/ + case 1003: /* any event mouse*/ + curr->w_mouse = i ? a1 : 0; + LMouseMode(&curr->w_layer, curr->w_mouse); + break; + /* case 1005: UTF-8 mouse mode rejected */ + case 1006: /* SGR mouse mode */ + curr->w_extmouse = i ? a1 : 0; + LExtMouseMode(&curr->w_layer, curr->w_extmouse); + break; + /* case 1015: UXRVT mouse mode rejected */ + } + } + break; + case '>': + switch (c) + { + case 'c': /* secondary DA */ + if (a1 == 0) + Report("\033[>%d;%d;0c", 83, nversion); /* 83 == 'S' */ + break; + } + break; + } +} + + +static void +StringStart(type) +enum string_t type; +{ + curr->w_StringType = type; + curr->w_stringp = curr->w_string; + curr->w_state = ASTR; +} + +static void +StringChar(c) +int c; +{ + if (curr->w_stringp >= curr->w_string + MAXSTR - 1) + curr->w_state = LIT; + else + *(curr->w_stringp)++ = c; +} + +/* + * Do string processing. Returns -1 if output should be suspended + * until status is gone. + */ +static int +StringEnd() +{ + struct canvas *cv; + char *p; + int typ; + char *t; + + /* There's two ways to terminate an OSC. If we've seen an ESC + * then it's been ST otherwise it's BEL. */ + t = curr->w_state == STRESC ? "\033\\" : "\a"; + + curr->w_state = LIT; + *curr->w_stringp = '\0'; + switch (curr->w_StringType) + { + case OSC: /* special xterm compatibility hack */ + if (curr->w_string[0] == ';' || (p = index(curr->w_string, ';')) == 0) + break; + typ = atoi(curr->w_string); + p++; +#ifdef MULTIUSER + if (typ == 83) /* 83 = 'S' */ + { + /* special execute commands sequence */ + char *args[MAXARGS]; + int argl[MAXARGS]; + struct acluser *windowuser; + + windowuser = *FindUserPtr(":window:"); + if (windowuser && Parse(p, sizeof(curr->w_string) - (p - curr->w_string), args, argl)) + { + for (display = displays; display; display = display->d_next) + if (D_forecv->c_layer->l_bottom == &curr->w_layer) + break; /* found it */ + if (display == 0 && curr->w_layer.l_cvlist) + display = curr->w_layer.l_cvlist->c_display; + if (display == 0) + display = displays; + EffectiveAclUser = windowuser; + fore = curr; + flayer = fore->w_savelayer ? fore->w_savelayer : &fore->w_layer; + DoCommand(args, argl); + EffectiveAclUser = 0; + fore = 0; + flayer = 0; + } + break; + } +#endif +#ifdef RXVT_OSC + if (typ == 0 || typ == 1 || typ == 2 || typ == 11 || typ == 20 || typ == 39 || typ == 49) + { + int typ2; + typ2 = typ / 10; + if (strcmp(curr->w_xtermosc[typ2], p)) + { + if (typ != 11 || strcmp("?", p)) + { + strncpy(curr->w_xtermosc[typ2], p, sizeof(curr->w_xtermosc[typ2]) - 1); + curr->w_xtermosc[typ2][sizeof(curr->w_xtermosc[typ2]) - 1] = 0; + } + + for (display = displays; display; display = display->d_next) + { + if (!D_CXT) + continue; + if (D_forecv->c_layer->l_bottom == &curr->w_layer) + SetXtermOSC(typ2, p, t); + if ((typ2 == 3 || typ2 == 4) && D_xtermosc[typ2]) + Redisplay(0); + if (typ == 11 && !strcmp("?", p)) + break; + } + } + } + if (typ != 0 && typ != 2) + break; +#else + if (typ < 0 || typ > 2) + break; +#endif + + curr->w_stringp -= p - curr->w_string; + if (curr->w_stringp > curr->w_string) + bcopy(p, curr->w_string, curr->w_stringp - curr->w_string); + *curr->w_stringp = '\0'; + /* FALLTHROUGH */ + case APC: + if (curr->w_hstatus) + { + if (strcmp(curr->w_hstatus, curr->w_string) == 0) + break; /* not changed */ + free(curr->w_hstatus); + curr->w_hstatus = 0; + } + if (curr->w_string != curr->w_stringp) + curr->w_hstatus = SaveStr(curr->w_string); + WindowChanged(curr, 'h'); + break; + case PM: + case GM: + for (display = displays; display; display = display->d_next) + { + for (cv = D_cvlist; cv; cv = cv->c_next) + if (cv->c_layer->l_bottom == &curr->w_layer) + break; + if (cv || curr->w_StringType == GM) + MakeStatus(curr->w_string); + } + return -1; + case DCS: + LAY_DISPLAYS(&curr->w_layer, AddStr(curr->w_string)); + break; + case AKA: + if (curr->w_title == curr->w_akabuf && !*curr->w_string) + break; + if (curr->w_dynamicaka) + ChangeAKA(curr, curr->w_string, strlen(curr->w_string)); + if (!*curr->w_string) + curr->w_autoaka = curr->w_y + 1; + break; + default: + break; + } + return 0; +} + +static void +PrintStart() +{ + curr->w_pdisplay = 0; + + /* find us a nice display to print on, fore prefered */ + display = curr->w_lastdisp; + if (!(display && curr == D_fore && (printcmd || D_PO))) + for (display = displays; display; display = display->d_next) + if (curr == D_fore && (printcmd || D_PO)) + break; + if (!display) + { + struct canvas *cv; + for (cv = curr->w_layer.l_cvlist; cv; cv = cv->c_lnext) + { + display = cv->c_display; + if (printcmd || D_PO) + break; + } + if (!cv) + { + display = displays; + if (!display || display->d_next || !(printcmd || D_PO)) + return; + } + } + curr->w_pdisplay = display; + curr->w_stringp = curr->w_string; + curr->w_state = PRIN; + if (printcmd && curr->w_pdisplay->d_printfd < 0) + curr->w_pdisplay->d_printfd = printpipe(curr, printcmd); +} + +static void +PrintChar(c) +int c; +{ + if (curr->w_stringp >= curr->w_string + MAXSTR - 1) + PrintFlush(); + *(curr->w_stringp)++ = c; +} + +static void +PrintFlush() +{ + display = curr->w_pdisplay; + if (display && printcmd) + { + char *bp = curr->w_string; + int len = curr->w_stringp - curr->w_string; + int r; + while (len && display->d_printfd >= 0) + { + r = write(display->d_printfd, bp, len); + if (r <= 0) + { + WMsg(curr, errno, "printing aborted"); + close(display->d_printfd); + display->d_printfd = -1; + break; + } + bp += r; + len -= r; + } + } + else if (display && curr->w_stringp > curr->w_string) + { + AddCStr(D_PO); + AddStrn(curr->w_string, curr->w_stringp - curr->w_string); + AddCStr(D_PF); + Flush(3); + } + curr->w_stringp = curr->w_string; +} + + +void +WNewAutoFlow(win, on) +struct win *win; +int on; +{ + debug1("WNewAutoFlow: %d\n", on); + if (win->w_flow & FLOW_AUTOFLAG) + win->w_flow = FLOW_AUTOFLAG | (FLOW_AUTO|FLOW_NOW) * on; + else + win->w_flow = (win->w_flow & ~FLOW_AUTO) | FLOW_AUTO * on; + LSetFlow(&win->w_layer, win->w_flow & FLOW_NOW); +} + + +#ifdef FONT + +static void +DesignateCharset(c, n) +int c, n; +{ + curr->w_ss = 0; +# ifdef ENCODINGS + if (c == ('@' & 037)) /* map JIS 6226 to 0208 */ + c = KANJI; +# endif + if (c == 'B') + c = ASCII; + if (curr->w_charsets[n] != c) + { + curr->w_charsets[n] = c; + if (curr->w_Charset == n) + { + curr->w_FontL = c; + curr->w_rend.font = curr->w_FontL; + LSetRendition(&curr->w_layer, &curr->w_rend); + } + if (curr->w_CharsetR == n) + curr->w_FontR = c; + } +} + +static void +MapCharset(n) +int n; +{ + curr->w_ss = 0; + if (curr->w_Charset != n) + { + curr->w_Charset = n; + curr->w_FontL = curr->w_charsets[n]; + curr->w_rend.font = curr->w_FontL; + LSetRendition(&curr->w_layer, &curr->w_rend); + } +} + +static void +MapCharsetR(n) +int n; +{ + curr->w_ss = 0; + if (curr->w_CharsetR != n) + { + curr->w_CharsetR = n; + curr->w_FontR = curr->w_charsets[n]; + } + curr->w_gr = 1; +} + +#endif /* FONT */ + +static void +SaveCursor(cursor) +struct cursor *cursor; +{ + cursor->on = 1; + cursor->x = curr->w_x; + cursor->y = curr->w_y; + cursor->Rend = curr->w_rend; +#ifdef FONT + cursor->Charset = curr->w_Charset; + cursor->CharsetR = curr->w_CharsetR; + bcopy((char *) curr->w_charsets, (char *) cursor->Charsets, + 4 * sizeof(int)); +#endif +} + +static void +RestoreCursor(cursor) +struct cursor *cursor; +{ + if (!cursor->on) + return; + LGotoPos(&curr->w_layer, cursor->x, cursor->y); + curr->w_x = cursor->x; + curr->w_y = cursor->y; + curr->w_rend = cursor->Rend; +#ifdef FONT + bcopy((char *) cursor->Charsets, (char *) curr->w_charsets, + 4 * sizeof(int)); + curr->w_Charset = cursor->Charset; + curr->w_CharsetR = cursor->CharsetR; + curr->w_ss = 0; + curr->w_FontL = curr->w_charsets[curr->w_Charset]; + curr->w_FontR = curr->w_charsets[curr->w_CharsetR]; +#endif + LSetRendition(&curr->w_layer, &curr->w_rend); +} + +static void +BackSpace() +{ + if (curr->w_x > 0) + { + curr->w_x--; + } + else if (curr->w_wrap && curr->w_y > 0) + { + curr->w_x = cols - 1; + curr->w_y--; + } + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); +} + +static void +Return() +{ + if (curr->w_x == 0) + return; + curr->w_x = 0; + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); +} + +static void +LineFeed(out_mode) +int out_mode; +{ + /* out_mode: 0=lf, 1=cr+lf */ + if (out_mode) + curr->w_x = 0; + if (curr->w_y != curr->w_bot) /* Don't scroll */ + { + if (curr->w_y < rows-1) + curr->w_y++; + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); + return; + } + if (curr->w_autoaka > 1) + curr->w_autoaka--; + MScrollV(curr, 1, curr->w_top, curr->w_bot, CURR_BCE); + LScrollV(&curr->w_layer, 1, curr->w_top, curr->w_bot, CURR_BCE); + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); +} + +static void +ReverseLineFeed() +{ + if (curr->w_y == curr->w_top) + { + MScrollV(curr, -1, curr->w_top, curr->w_bot, CURR_BCE); + LScrollV(&curr->w_layer, -1, curr->w_top, curr->w_bot, CURR_BCE); + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); + } + else if (curr->w_y > 0) + CursorUp(1); +} + +static void +InsertChar(n) +int n; +{ + register int y = curr->w_y, x = curr->w_x; + + if (n <= 0) + return; + if (x == cols) + x--; + save_mline(&curr->w_mlines[y], cols); + MScrollH(curr, -n, y, x, curr->w_width - 1, CURR_BCE); + LScrollH(&curr->w_layer, -n, y, x, curr->w_width - 1, CURR_BCE, &mline_old); + LGotoPos(&curr->w_layer, x, y); +} + +static void +DeleteChar(n) +int n; +{ + register int y = curr->w_y, x = curr->w_x; + + if (x == cols) + x--; + save_mline(&curr->w_mlines[y], cols); + MScrollH(curr, n, y, x, curr->w_width - 1, CURR_BCE); + LScrollH(&curr->w_layer, n, y, x, curr->w_width - 1, CURR_BCE, &mline_old); + LGotoPos(&curr->w_layer, x, y); +} + +static void +DeleteLine(n) +int n; +{ + if (curr->w_y < curr->w_top || curr->w_y > curr->w_bot) + return; + if (n > curr->w_bot - curr->w_y + 1) + n = curr->w_bot - curr->w_y + 1; + MScrollV(curr, n, curr->w_y, curr->w_bot, CURR_BCE); + LScrollV(&curr->w_layer, n, curr->w_y, curr->w_bot, CURR_BCE); + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); +} + +static void +InsertLine(n) +int n; +{ + if (curr->w_y < curr->w_top || curr->w_y > curr->w_bot) + return; + if (n > curr->w_bot - curr->w_y + 1) + n = curr->w_bot - curr->w_y + 1; + MScrollV(curr, -n, curr->w_y, curr->w_bot, CURR_BCE); + LScrollV(&curr->w_layer, -n, curr->w_y, curr->w_bot, CURR_BCE); + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); +} + +static void +ScrollRegion(n) +int n; +{ + MScrollV(curr, n, curr->w_top, curr->w_bot, CURR_BCE); + LScrollV(&curr->w_layer, n, curr->w_top, curr->w_bot, CURR_BCE); + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); +} + + +static void +ForwardTab() +{ + register int x = curr->w_x; + + if (x == cols) + { + LineFeed(1); + x = 0; + } + if (curr->w_tabs[x] && x < cols - 1) + x++; + while (x < cols - 1 && !curr->w_tabs[x]) + x++; + curr->w_x = x; + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); +} + +static void +BackwardTab() +{ + register int x = curr->w_x; + + if (curr->w_tabs[x] && x > 0) + x--; + while (x > 0 && !curr->w_tabs[x]) + x--; + curr->w_x = x; + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); +} + +static void +ClearScreen() +{ + LClearArea(&curr->w_layer, 0, 0, curr->w_width - 1, curr->w_height - 1, CURR_BCE, 1); +#ifdef COPY_PASTE + MScrollV(curr, curr->w_height, 0, curr->w_height - 1, CURR_BCE); +#else + MClearArea(curr, 0, 0, curr->w_width - 1, curr->w_height - 1, CURR_BCE); +#endif +} + +static void +ClearFromBOS() +{ + register int y = curr->w_y, x = curr->w_x; + + LClearArea(&curr->w_layer, 0, 0, x, y, CURR_BCE, 1); + MClearArea(curr, 0, 0, x, y, CURR_BCE); + RestorePosRendition(); +} + +static void +ClearToEOS() +{ + register int y = curr->w_y, x = curr->w_x; + + if (x == 0 && y == 0) + { + ClearScreen(); + RestorePosRendition(); + return; + } + LClearArea(&curr->w_layer, x, y, cols - 1, rows - 1, CURR_BCE, 1); + MClearArea(curr, x, y, cols - 1, rows - 1, CURR_BCE); + RestorePosRendition(); +} + +static void +ClearLineRegion(from, to) +int from, to; +{ + register int y = curr->w_y; + LClearArea(&curr->w_layer, from, y, to, y, CURR_BCE, 1); + MClearArea(curr, from, y, to, y, CURR_BCE); + RestorePosRendition(); +} + +static void +CursorRight(n) +register int n; +{ + register int x = curr->w_x; + + if (x == cols) + { + LineFeed(1); + x = 0; + } + if ((curr->w_x += n) >= cols) + curr->w_x = cols - 1; + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); +} + +static void +CursorUp(n) +register int n; +{ + if (curr->w_y < curr->w_top) /* if above scrolling rgn, */ + { + if ((curr->w_y -= n) < 0) /* ignore its limits */ + curr->w_y = 0; + } + else + if ((curr->w_y -= n) < curr->w_top) + curr->w_y = curr->w_top; + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); +} + +static void +CursorDown(n) +register int n; +{ + if (curr->w_y > curr->w_bot) /* if below scrolling rgn, */ + { + if ((curr->w_y += n) > rows - 1) /* ignore its limits */ + curr->w_y = rows - 1; + } + else + if ((curr->w_y += n) > curr->w_bot) + curr->w_y = curr->w_bot; + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); +} + +static void +CursorLeft(n) +register int n; +{ + if ((curr->w_x -= n) < 0) + curr->w_x = 0; + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); +} + +static void +ASetMode(on) +int on; +{ + register int i; + + for (i = 0; i < curr->w_NumArgs; ++i) + { + switch (curr->w_args[i]) + { + /* case 2: KAM: Lock keyboard */ + case 4: /* IRM: Insert mode */ + curr->w_insert = on; + LAY_DISPLAYS(&curr->w_layer, InsertMode(on)); + break; + /* case 12: SRM: Echo mode on */ + case 20: /* LNM: Linefeed mode */ + curr->w_autolf = on; + break; + case 34: + curr->w_curvvis = !on; + LCursorVisibility(&curr->w_layer, curr->w_curinv ? -1 : curr->w_curvvis); + break; + default: + break; + } + } +} + +static char rendlist[] = +{ + ~((1 << NATTR) - 1), A_BD, A_DI, A_SO, A_US, A_BL, 0, A_RV, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, ~(A_BD|A_SO|A_DI), ~A_SO, ~A_US, ~A_BL, 0, ~A_RV +}; + +static void +SelectRendition() +{ +#ifdef COLOR + register int j, i = 0, a = curr->w_rend.attr, c = curr->w_rend.color; +# ifdef COLORS256 + int cx = curr->w_rend.colorx; +# endif +#else + register int j, i = 0, a = curr->w_rend.attr; +#endif + + do + { + j = curr->w_args[i]; +#ifdef COLOR + if ((j == 38 || j == 48) && i + 2 < curr->w_NumArgs && curr->w_args[i + 1] == 5) + { + int jj; + + i += 2; + jj = curr->w_args[i]; + if (jj < 0 || jj > 255) + continue; +# ifdef COLORS256 + if (j == 38) + { + c = (c & 0xf0) | ((jj & 0x0f) ^ 9); + a |= A_BFG; + if (jj >= 8 && jj < 16) + c |= 0x08; + else + a ^= A_BFG; + a = (a & 0xbf) | (jj & 8 ? 0x40 : 0); + cx = (cx & 0xf0) | (jj >> 4 & 0x0f); + } + else + { + c = (c & 0x0f) | ((jj & 0x0f) ^ 9) << 4; + a |= A_BBG; + if (jj >= 8 && jj < 16) + c |= 0x80; + else + a ^= A_BBG; + cx = (cx & 0x0f) | (jj & 0xf0); + } + continue; +# else + jj = color256to16(jj) + 30; + if (jj >= 38) + jj += 60 - 8; + j = j == 38 ? jj : jj + 10; +# endif + } +# ifdef COLORS16 + if (j == 0 || (j >= 30 && j <= 39 && j != 38)) + a &= 0xbf; + if (j == 0 || (j >= 40 && j <= 49 && j != 48)) + a &= 0x7f; + if (j >= 90 && j <= 97) + a |= 0x40; + if (j >= 100 && j <= 107) + a |= 0x80; +# endif + if (j >= 90 && j <= 97) + j -= 60; + if (j >= 100 && j <= 107) + j -= 60; + if (j >= 30 && j <= 39 && j != 38) + c = (c & 0xf0) | ((j - 30) ^ 9); + else if (j >= 40 && j <= 49 && j != 48) + c = (c & 0x0f) | (((j - 40) ^ 9) << 4); + if (j == 0) + c = 0; +# ifdef COLORS256 + if (j == 0 || (j >= 30 && j <= 39 && j != 38)) + cx &= 0xf0; + if (j == 0 || (j >= 40 && j <= 49 && j != 48)) + cx &= 0x0f; +# endif +#endif + if (j < 0 || j >= (int)(sizeof(rendlist)/sizeof(*rendlist))) + continue; + j = rendlist[j]; + if (j & (1 << NATTR)) + a &= j; + else + a |= j; + } + while (++i < curr->w_NumArgs); + curr->w_rend.attr = a; +#ifdef COLOR + curr->w_rend.color = c; +# ifdef COLORS256 + curr->w_rend.colorx = cx; +# endif +#endif + LSetRendition(&curr->w_layer, &curr->w_rend); +} + +static void +FillWithEs() +{ + register int i; + register unsigned char *p, *ep; + + LClearAll(&curr->w_layer, 1); + curr->w_y = curr->w_x = 0; + for (i = 0; i < rows; ++i) + { + clear_mline(&curr->w_mlines[i], 0, cols + 1); + p = curr->w_mlines[i].image; + ep = p + cols; + while (p < ep) + *p++ = 'E'; + } + LRefreshAll(&curr->w_layer, 1); +} + + +/* + * Ugly autoaka hack support: + * ChangeAKA() sets a new aka + * FindAKA() searches for an autoaka match + */ + +void +ChangeAKA(p, s, l) +struct win *p; +char *s; +int l; +{ + int i, c; + + for (i = 0; l > 0; l--) + { + if (p->w_akachange + i == p->w_akabuf + sizeof(p->w_akabuf) - 1) + break; + c = (unsigned char)*s++; + if (c == 0) + break; + if (c < 32 || c == 127 || (c >= 128 && c < 160 && p->w_c1)) + continue; + p->w_akachange[i++] = c; + } + p->w_akachange[i] = 0; + p->w_title = p->w_akachange; + if (p->w_akachange != p->w_akabuf) + if (p->w_akachange[0] == 0 || p->w_akachange[-1] == ':') + p->w_title = p->w_akabuf + strlen(p->w_akabuf) + 1; + WindowChanged(p, 't'); + WindowChanged((struct win *)0, 'w'); + WindowChanged((struct win *)0, 'W'); +} + +static void +FindAKA() +{ + register unsigned char *cp, *line; + register struct win *wp = curr; + register int len = strlen(wp->w_akabuf); + int y; + + y = (wp->w_autoaka > 0 && wp->w_autoaka <= wp->w_height) ? wp->w_autoaka - 1 : wp->w_y; + cols = wp->w_width; + try_line: + cp = line = wp->w_mlines[y].image; + if (wp->w_autoaka > 0 && *wp->w_akabuf != '\0') + { + for (;;) + { + if (cp - line >= cols - len) + { + if (++y == wp->w_autoaka && y < rows) + goto try_line; + return; + } + if (strncmp((char *)cp, wp->w_akabuf, len) == 0) + break; + cp++; + } + cp += len; + } + for (len = cols - (cp - line); len && *cp == ' '; len--, cp++) + ; + if (len) + { + if (wp->w_autoaka > 0 && (*cp == '!' || *cp == '%' || *cp == '^')) + wp->w_autoaka = -1; + else + wp->w_autoaka = 0; + line = cp; + while (len && *cp != ' ') + { + if (*cp++ == '/') + line = cp; + len--; + } + ChangeAKA(wp, (char *)line, cp - line); + } + else + wp->w_autoaka = 0; +} + +static void +RestorePosRendition() +{ + LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); + LSetRendition(&curr->w_layer, &curr->w_rend); +} + +/* Send a terminal report as if it were typed. */ +static void +Report(fmt, n1, n2) +char *fmt; +int n1, n2; +{ + register int len; + char rbuf[40]; /* enough room for all replys */ + + sprintf(rbuf, fmt, n1, n2); + len = strlen(rbuf); + +#ifdef PSEUDOS + if (W_UWP(curr)) + { + if ((unsigned)(curr->w_pwin->p_inlen + len) <= sizeof(curr->w_pwin->p_inbuf)) + { + bcopy(rbuf, curr->w_pwin->p_inbuf + curr->w_pwin->p_inlen, len); + curr->w_pwin->p_inlen += len; + } + } + else +#endif + { + if ((unsigned)(curr->w_inlen + len) <= sizeof(curr->w_inbuf)) + { + bcopy(rbuf, curr->w_inbuf + curr->w_inlen, len); + curr->w_inlen += len; + } + } +} + + + +/* + *====================================================================* + *====================================================================* + */ + +/********************************************************************** + * + * Memory subsystem. + * + */ + +static void +MFixLine(p, y, mc) +struct win *p; +int y; +struct mchar *mc; +{ + struct mline *ml = &p->w_mlines[y]; + if (mc->attr && ml->attr == null) + { + if ((ml->attr = (unsigned char *)calloc(p->w_width + 1, 1)) == 0) + { + ml->attr = null; + mc->attr = p->w_rend.attr = 0; + WMsg(p, 0, "Warning: no space for attr - turned off"); + } + } +#ifdef FONT + if (mc->font && ml->font == null) + { + if ((ml->font = (unsigned char *)calloc(p->w_width + 1, 1)) == 0) + { + ml->font = null; + p->w_FontL = p->w_charsets[p->w_ss ? p->w_ss : p->w_Charset] = 0; + p->w_FontR = p->w_charsets[p->w_ss ? p->w_ss : p->w_CharsetR] = 0; + mc->font = mc->fontx = p->w_rend.font = 0; + WMsg(p, 0, "Warning: no space for font - turned off"); + } + } + if (mc->fontx && ml->fontx == null) + { + if ((ml->fontx = (unsigned char *)calloc(p->w_width + 1, 1)) == 0) + { + ml->fontx = null; + mc->fontx = 0; + } + } +#endif +#ifdef COLOR + if (mc->color && ml->color == null) + { + if ((ml->color = (unsigned char *)calloc(p->w_width + 1, 1)) == 0) + { + ml->color = null; + mc->color = p->w_rend.color = 0; + WMsg(p, 0, "Warning: no space for color - turned off"); + } + } +# ifdef COLORS256 + if (mc->colorx && ml->colorx == null) + { + if ((ml->colorx = (unsigned char *)calloc(p->w_width + 1, 1)) == 0) + { + ml->colorx = null; + mc->colorx = p->w_rend.colorx = 0; + WMsg(p, 0, "Warning: no space for extended colors - turned off"); + } + } +# endif +#endif +} + +/*****************************************************************/ + +#ifdef DW_CHARS +# define MKillDwRight(p, ml, x) \ + if (dw_right(ml, x, p->w_encoding)) \ + { \ + if (x > 0) \ + copy_mchar2mline(&mchar_blank, ml, x - 1); \ + copy_mchar2mline(&mchar_blank, ml, x); \ + } + +# define MKillDwLeft(p, ml, x) \ + if (dw_left(ml, x, p->w_encoding)) \ + { \ + copy_mchar2mline(&mchar_blank, ml, x); \ + copy_mchar2mline(&mchar_blank, ml, x + 1); \ + } +#else +# define MKillDwRight(p, ml, x) ; +# define MKillDwLeft(p, ml, x) ; +#endif + +static void +MScrollH(p, n, y, xs, xe, bce) +struct win *p; +int n, y, xs, xe, bce; +{ + struct mline *ml; + + if (n == 0) + return; + ml = &p->w_mlines[y]; + MKillDwRight(p, ml, xs); + MKillDwLeft(p, ml, xe); + if (n > 0) + { + if (xe - xs + 1 > n) + { + MKillDwRight(p, ml, xs + n); + bcopy_mline(ml, xs + n, xs, xe + 1 - xs - n); + } + else + n = xe - xs + 1; + clear_mline(ml, xe + 1 - n, n); +#ifdef COLOR + if (bce) + MBceLine(p, y, xe + 1 - n, n, bce); +#endif + } + else + { + n = -n; + if (xe - xs + 1 > n) + { + MKillDwLeft(p, ml, xe - n); + bcopy_mline(ml, xs, xs + n, xe + 1 - xs - n); + } + else + n = xe - xs + 1; + clear_mline(ml, xs, n); +#ifdef COLOR + if (bce) + MBceLine(p, y, xs, n, bce); +#endif + } +} + +static void +MScrollV(p, n, ys, ye, bce) +struct win *p; +int n, ys, ye, bce; +{ + int i, cnt1, cnt2; + struct mline tmp[256]; + struct mline *ml; + + if (n == 0) + return; + if (n > 0) + { + if (ye - ys + 1 < n) + n = ye - ys + 1; + if (n > 256) + { + MScrollV(p, n - 256, ys, ye, bce); + n = 256; + } +#ifdef COPY_PASTE + if (compacthist) + { + ye = MFindUsedLine(p, ye, ys); + if (ye - ys + 1 < n) + n = ye - ys + 1; + if (n <= 0) + return; + } +#endif + /* Clear lines */ + ml = p->w_mlines + ys; + for (i = ys; i < ys + n; i++, ml++) + { +#ifdef COPY_PASTE + if (ys == p->w_top) + WAddLineToHist(p, ml); +#endif + if (ml->attr != null) + free(ml->attr); + ml->attr = null; +#ifdef FONT + if (ml->font != null) + free(ml->font); + ml->font = null; + if (ml->fontx != null) + free(ml->fontx); + ml->fontx = null; +#endif +#ifdef COLOR + if (ml->color != null) + free(ml->color); + ml->color = null; +# ifdef COLORS256 + if (ml->colorx != null) + free(ml->colorx); + ml->colorx = null; +# endif +#endif + bclear((char *)ml->image, p->w_width + 1); +#ifdef COLOR + if (bce) + MBceLine(p, i, 0, p->w_width, bce); +#endif + } + /* switch 'em over */ + cnt1 = n * sizeof(struct mline); + cnt2 = (ye - ys + 1 - n) * sizeof(struct mline); + if (cnt1 && cnt2) + Scroll((char *)(p->w_mlines + ys), cnt1, cnt2, (char *)tmp); + } + else + { + n = -n; + if (ye - ys + 1 < n) + n = ye - ys + 1; + if (n > 256) + { + MScrollV(p, - (n - 256), ys, ye, bce); + n = 256; + } + + ml = p->w_mlines + ye; + /* Clear lines */ + for (i = ye; i > ye - n; i--, ml--) + { + if (ml->attr != null) + free(ml->attr); + ml->attr = null; +#ifdef FONT + if (ml->font != null) + free(ml->font); + ml->font = null; + if (ml->fontx != null) + free(ml->fontx); + ml->fontx = null; +#endif +#ifdef COLOR + if (ml->color != null) + free(ml->color); + ml->color = null; +# ifdef COLORS256 + if (ml->colorx != null) + free(ml->colorx); + ml->colorx = null; +# endif +#endif + bclear((char *)ml->image, p->w_width + 1); +#ifdef COLOR + if (bce) + MBceLine(p, i, 0, p->w_width, bce); +#endif + } + cnt1 = n * sizeof(struct mline); + cnt2 = (ye - ys + 1 - n) * sizeof(struct mline); + if (cnt1 && cnt2) + Scroll((char *)(p->w_mlines + ys), cnt2, cnt1, (char *)tmp); + } +} + +static void +Scroll(cp, cnt1, cnt2, tmp) +char *cp, *tmp; +int cnt1, cnt2; +{ + if (!cnt1 || !cnt2) + return; + if (cnt1 <= cnt2) + { + bcopy(cp, tmp, cnt1); + bcopy(cp + cnt1, cp, cnt2); + bcopy(tmp, cp + cnt2, cnt1); + } + else + { + bcopy(cp + cnt1, tmp, cnt2); + bcopy(cp, cp + cnt2, cnt1); + bcopy(tmp, cp, cnt2); + } +} + +static void +MClearArea(p, xs, ys, xe, ye, bce) +struct win *p; +int xs, ys, xe, ye, bce; +{ + int n, y; + int xxe; + struct mline *ml; + + /* Check for zero-height window */ + if (ys < 0 || ye < ys) + return; + + /* check for magic margin condition */ + if (xs >= p->w_width) + xs = p->w_width - 1; + if (xe >= p->w_width) + xe = p->w_width - 1; + + MKillDwRight(p, p->w_mlines + ys, xs); + MKillDwLeft(p, p->w_mlines + ye, xe); + + ml = p->w_mlines + ys; + for (y = ys; y <= ye; y++, ml++) + { + xxe = (y == ye) ? xe : p->w_width - 1; + n = xxe - xs + 1; + if (n > 0) + clear_mline(ml, xs, n); +#ifdef COLOR + if (n > 0 && bce) + MBceLine(p, y, xs, xs + n - 1, bce); +#endif + xs = 0; + } +} + +static void +MInsChar(p, c, x, y) +struct win *p; +struct mchar *c; +int x, y; +{ + int n; + struct mline *ml; + + ASSERT(x >= 0 && x < p->w_width); + MFixLine(p, y, c); + ml = p->w_mlines + y; + n = p->w_width - x - 1; + MKillDwRight(p, ml, x); + if (n > 0) + { + MKillDwRight(p, ml, p->w_width - 1); + bcopy_mline(ml, x, x + 1, n); + } + copy_mchar2mline(c, ml, x); +#ifdef DW_CHARS + if (c->mbcs) + { + if (--n > 0) + { + MKillDwRight(p, ml, p->w_width - 1); + bcopy_mline(ml, x + 1, x + 2, n); + } + copy_mchar2mline(c, ml, x + 1); + ml->image[x + 1] = c->mbcs; +# ifdef UTF8 + if (p->w_encoding != UTF8) + ml->font[x + 1] |= 0x80; + else if (p->w_encoding == UTF8 && c->mbcs) + { + ml->font[x + 1] = c->mbcs; + ml->fontx[x + 1] = 0; + } +# else + ml->font[x + 1] |= 0x80; +# endif + } +#endif +} + +static void +MPutChar(p, c, x, y) +struct win *p; +struct mchar *c; +int x, y; +{ + struct mline *ml; + + MFixLine(p, y, c); + ml = &p->w_mlines[y]; + MKillDwRight(p, ml, x); + MKillDwLeft(p, ml, x); + copy_mchar2mline(c, ml, x); +#ifdef DW_CHARS + if (c->mbcs) + { + MKillDwLeft(p, ml, x + 1); + copy_mchar2mline(c, ml, x + 1); + ml->image[x + 1] = c->mbcs; +# ifdef UTF8 + if (p->w_encoding != UTF8) + ml->font[x + 1] |= 0x80; + else if (p->w_encoding == UTF8 && c->mbcs) + { + ml->font[x + 1] = c->mbcs; + ml->fontx[x + 1] = 0; + } +# else + ml->font[x + 1] |= 0x80; +# endif + } +#endif +} + + +static void +MWrapChar(p, c, y, top, bot, ins) +struct win *p; +struct mchar *c; +int y, top, bot; +int ins; +{ + struct mline *ml; + int bce; + +#ifdef COLOR + bce = rend_getbg(c); +#else + bce = 0; +#endif + MFixLine(p, y, c); + ml = &p->w_mlines[y]; + copy_mchar2mline(&mchar_null, ml, p->w_width); + if (y == bot) + MScrollV(p, 1, top, bot, bce); + else if (y < p->w_height - 1) + y++; + if (ins) + MInsChar(p, c, 0, y); + else + MPutChar(p, c, 0, y); +} + +static void +MPutStr(p, s, n, r, x, y) +struct win *p; +char *s; +int n; +struct mchar *r; +int x, y; +{ + struct mline *ml; + int i; + unsigned char *b; + + if (n <= 0) + return; + MFixLine(p, y, r); + ml = &p->w_mlines[y]; + MKillDwRight(p, ml, x); + MKillDwLeft(p, ml, x + n - 1); + bcopy(s, (char *)ml->image + x, n); + if (ml->attr != null) + { + b = ml->attr + x; + for (i = n; i-- > 0;) + *b++ = r->attr; + } +#ifdef FONT + if (ml->font != null) + { + b = ml->font + x; + for (i = n; i-- > 0;) + *b++ = r->font; + } + if (ml->fontx != null) + { + b = ml->fontx + x; + for (i = n; i-- > 0;) + *b++ = r->fontx; + } +#endif +#ifdef COLOR + if (ml->color != null) + { + b = ml->color + x; + for (i = n; i-- > 0;) + *b++ = r->color; + } +# ifdef COLORS256 + if (ml->colorx != null) + { + b = ml->colorx + x; + for (i = n; i-- > 0;) + *b++ = r->colorx; + } +# endif +#endif +} + +#ifdef COLOR +static void +MBceLine(p, y, xs, xe, bce) +struct win *p; +int y, xs, xe, bce; +{ + struct mchar mc; + struct mline *ml; + int x; + + mc = mchar_null; + rend_setbg(&mc, bce); + MFixLine(p, y, &mc); + ml = p->w_mlines + y; +# ifdef COLORS16 + if (mc.attr) + for (x = xs; x <= xe; x++) + ml->attr[x] = mc.attr; +# endif + if (mc.color) + for (x = xs; x <= xe; x++) + ml->color[x] = mc.color; +# ifdef COLORS256 + if (mc.colorx) + for (x = xs; x <= xe; x++) + ml->colorx[x] = mc.colorx; +# endif +} +#endif + + +#ifdef COPY_PASTE +static void +WAddLineToHist(wp, ml) +struct win *wp; +struct mline *ml; +{ + register unsigned char *q, *o; + struct mline *hml; + + if (wp->w_histheight == 0) + return; + hml = &wp->w_hlines[wp->w_histidx]; + q = ml->image; ml->image = hml->image; hml->image = q; + + q = ml->attr; o = hml->attr; hml->attr = q; ml->attr = null; + if (o != null) + free(o); + +#ifdef FONT + q = ml->font; o = hml->font; hml->font = q; ml->font = null; + if (o != null) + free(o); + q = ml->fontx; o = hml->fontx; hml->fontx = q; ml->fontx = null; + if (o != null) + free(o); +#endif + +#ifdef COLOR + q = ml->color; o = hml->color; hml->color = q; ml->color = null; + if (o != null) + free(o); +# ifdef COLORS256 + q = ml->colorx; o = hml->colorx; hml->colorx = q; ml->colorx = null; + if (o != null) + free(o); +# endif +#endif + + if (++wp->w_histidx >= wp->w_histheight) + wp->w_histidx = 0; + if (wp->w_scrollback_height < wp->w_histheight) + ++wp->w_scrollback_height; +} +#endif + +int +MFindUsedLine(p, ye, ys) +struct win *p; +int ys, ye; +{ + int y; + struct mline *ml = p->w_mlines + ye; + + debug2("MFindUsedLine: %d %d\n", ye, ys); + for (y = ye; y >= ys; y--, ml--) + { + if (bcmp((char*)ml->image, blank, p->w_width)) + break; + if (ml->attr != null && bcmp((char*)ml->attr, null, p->w_width)) + break; +#ifdef COLOR + if (ml->color != null && bcmp((char*)ml->color, null, p->w_width)) + break; +# ifdef COLORS256 + if (ml->colorx != null && bcmp((char*)ml->colorx, null, p->w_width)) + break; +# endif +#endif +#ifdef UTF8 + if (p->w_encoding == UTF8) + { + if (ml->font != null && bcmp((char*)ml->font, null, p->w_width)) + break; + if (ml->fontx != null && bcmp((char*)ml->fontx, null, p->w_width)) + break; + } +#endif + } + debug1("MFindUsedLine returning %d\n", y); + return y; +} + + +/* + *====================================================================* + *====================================================================* + */ + +/* + * Tricky: send only one bell even if the window is displayed + * more than once. + */ +void +WBell(p, visual) +struct win *p; +int visual; +{ + struct canvas *cv; + if (displays == NULL) + p->w_bell = BELL_DONE; + for (display = displays; display; display = display->d_next) + { + for (cv = D_cvlist; cv; cv = cv->c_next) + if (cv->c_layer->l_bottom == &p->w_layer) + break; + if (cv && !visual) + AddCStr(D_BL); + else if (cv && D_VB) + AddCStr(D_VB); + else + p->w_bell = visual ? BELL_VISUAL : BELL_FOUND; + } +} + +/* + * This should be reverse video. + * Only change video if window is fore. + * Because it is used in some termcaps to emulate + * a visual bell we do this hack here. + * (screen uses \Eg as special vbell sequence) + */ +static void +WReverseVideo(p, on) +struct win *p; +int on; +{ + struct canvas *cv; + for (cv = p->w_layer.l_cvlist; cv; cv = cv->c_lnext) + { + display = cv->c_display; + if (cv != D_forecv) + continue; + ReverseVideo(on); + if (!on && p->w_revvid && !D_CVR) + { + if (D_VB) + AddCStr(D_VB); + else + p->w_bell = BELL_VISUAL; + } + } +} + +void +WMsg(p, err, str) +struct win *p; +int err; +char *str; +{ + extern struct layer *flayer; + struct layer *oldflayer = flayer; + flayer = &p->w_layer; + LMsg(err, "%s", str); + flayer = oldflayer; +} + +void +WChangeSize(p, w, h) +struct win *p; +int w, h; +{ + int wok = 0; + struct canvas *cv; + + if (p->w_layer.l_cvlist == 0) + { + /* window not displayed -> works always */ + ChangeWindowSize(p, w, h, p->w_histheight); + return; + } + for (cv = p->w_layer.l_cvlist; cv; cv = cv->c_lnext) + { + display = cv->c_display; + if (p != D_fore) + continue; /* change only fore */ + if (D_CWS) + break; + if (D_CZ0 && (w == Z0width || w == Z1width)) + wok = 1; + } + if (cv == 0 && wok == 0) /* can't change any display */ + return; + if (!D_CWS) + h = p->w_height; + ChangeWindowSize(p, w, h, p->w_histheight); + for (display = displays; display; display = display->d_next) + { + if (p == D_fore) + { + if (D_cvlist && D_cvlist->c_next == 0) + ResizeDisplay(w, h); + else + ResizeDisplay(w, D_height); + ResizeLayersToCanvases(); /* XXX Hmm ? */ + continue; + } + for (cv = D_cvlist; cv; cv = cv->c_next) + if (cv->c_layer->l_bottom == &p->w_layer) + break; + if (cv) + Redisplay(0); + } +} + +static int +WindowChangedCheck(s, what, hp) +char *s; +int what; +int *hp; +{ + int h = 0; + int l; + while(*s) + { + if (*s++ != (hp ? '%' : '\005')) + continue; + l = 0; + s += (*s == '+'); + s += (*s == '-'); + while (*s >= '0' && *s <= '9') + s++; + if (*s == 'L') + { + s++; + l = 0x100; + } + if (*s == 'h') + h = 1; + if (*s == what || ((*s | l) == what) || what == 'd') + break; + if (*s) + s++; + } + if (hp) + *hp = h; + return *s ? 1 : 0; +} + +void +WindowChanged(p, what) +struct win *p; +int what; +{ + int inwstr, inhstr, inlstr; + int inwstrh = 0, inhstrh = 0, inlstrh = 0; + int got, ox, oy; + struct display *olddisplay = display; + struct canvas *cv; + + inwstr = inhstr = 0; + + if (what == 'f') + { + WindowChanged((struct win *)0, 'w'|0x100); + WindowChanged((struct win *)0, 'W'|0x100); + } + + if (what) + { + inwstr = WindowChangedCheck(captionstring, what, &inwstrh); + inhstr = WindowChangedCheck(hstatusstring, what, &inhstrh); + inlstr = WindowChangedCheck(wliststr, what, &inlstrh); + } + else + { + inwstr = inhstr = 0; + inlstr = 1; + } + + if (p == 0) + { + for (display = displays; display; display = display->d_next) + { + ox = D_x; + oy = D_y; + for (cv = D_cvlist; cv; cv = cv->c_next) + { + if (inlstr || (inlstrh && p && p->w_hstatus && *p->w_hstatus && WindowChangedCheck(p->w_hstatus, what, (int *)0))) + WListUpdatecv(cv, (struct win *)0); + p = Layer2Window(cv->c_layer); + if (inwstr || (inwstrh && p && p->w_hstatus && *p->w_hstatus && WindowChangedCheck(p->w_hstatus, what, (int *)0))) + if (cv->c_ye + 1 < D_height) + RefreshLine(cv->c_ye + 1, 0, D_width - 1, 0); + } + p = D_fore; + if (inhstr || (inhstrh && p && p->w_hstatus && *p->w_hstatus && WindowChangedCheck(p->w_hstatus, what, (int *)0))) + RefreshHStatus(); + if (ox != -1 && oy != -1) + GotoPos(ox, oy); + } + display = olddisplay; + return; + } + + if (p->w_hstatus && *p->w_hstatus && (inwstrh || inhstrh || inlstrh) && WindowChangedCheck(p->w_hstatus, what, (int *)0)) + { + inwstr |= inwstrh; + inhstr |= inhstrh; + inlstr |= inlstrh; + } + if (!inwstr && !inhstr && !inlstr) + return; + for (display = displays; display; display = display->d_next) + { + got = 0; + ox = D_x; + oy = D_y; + for (cv = D_cvlist; cv; cv = cv->c_next) + { + if (inlstr) + WListUpdatecv(cv, p); + if (Layer2Window(cv->c_layer) != p) + continue; + got = 1; + if (inwstr && cv->c_ye + 1 < D_height) + RefreshLine(cv->c_ye + 1, 0, D_width - 1, 0); + } + if (got && inhstr && p == D_fore) + RefreshHStatus(); + if (ox != -1 && oy != -1) + GotoPos(ox, oy); + } + display = olddisplay; +} + @@ -0,0 +1,175 @@ +/* Copyright (c) 2008, 2009 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Micah Cowan (micah@cowan.name) + * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) + * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, see + * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + * $Id$ GNU + */ + +#define NATTR 6 + +#define ATTR_DI 0 /* Dim mode */ +#define ATTR_US 1 /* Underscore mode */ +#define ATTR_BD 2 /* Bold mode */ +#define ATTR_RV 3 /* Reverse mode */ +#define ATTR_SO 4 /* Standout mode */ +#define ATTR_BL 5 /* Blinking */ + +#define A_DI (1<<ATTR_DI) +#define A_US (1<<ATTR_US) +#define A_BD (1<<ATTR_BD) +#define A_RV (1<<ATTR_RV) +#define A_SO (1<<ATTR_SO) +#define A_BL (1<<ATTR_BL) +#define A_MAX (1<<(NATTR-1)) + +#define ATYP_M (1<<0) +#define ATYP_S (1<<1) +#define ATYP_U (1<<2) + +#ifdef COLORS16 +/* pseudo attributes */ +# define ATTR_BFG 6 /* bright foreground */ +# define ATTR_BBG 7 /* bright background */ +# define A_BFG (1<<ATTR_BFG) +# define A_BBG (1<<ATTR_BBG) +#endif + +/* + * Parser state + */ +/* keep state_t and state_t_string in sync! */ +enum state_t +{ + LIT, /* Literal input */ + ESC, /* Start of escape sequence */ + ASTR, /* Start of control string */ + STRESC, /* ESC seen in control string */ + CSI, /* Reading arguments in "CSI Pn ;...*/ + PRIN, /* Printer mode */ + PRINESC, /* ESC seen in printer mode */ + PRINCSI, /* CSI seen in printer mode */ + PRIN4 /* CSI 4 seen in printer mode */ +}; + +/* keep string_t and string_t_string in sync! */ +enum string_t +{ + NONE, + DCS, /* Device control string */ + OSC, /* Operating system command */ + APC, /* Application program command */ + /* - used for status change */ + PM, /* Privacy message */ + AKA, /* title for current screen */ + GM, /* Global message to every display */ + STATUS /* User hardstatus line */ +}; + +/* + * Types of movement used by GotoPos() + */ +enum move_t { + M_NONE, + M_UP, + M_CUP, + M_DO, + M_CDO, + M_LE, + M_CLE, + M_RI, + M_CRI, + M_RW, + M_CR /* CR and rewrite */ +}; + +#define EXPENSIVE 1000 + +#define G0 0 +#define G1 1 +#define G2 2 +#define G3 3 + +#define ASCII 0 + +#ifdef TOPSTAT +#define STATLINE (0) +#else +#define STATLINE (D_height-1) +#endif + +#ifdef ENCODINGS + +#define KANJI ('B' & 037) +#define KANJI0212 ('D' & 037) +#define KANA 'I' + +#define EUC_JP 1 +#define SJIS 2 +#define EUC_KR 3 +#define EUC_CN 4 +#define BIG5 5 +#define KOI8R 6 +#define CP1251 7 +#define GBK 20 + +#define EUC EUC_JP + +#endif + +#ifdef UTF8 +#undef UTF8 +#define UTF8 8 +#endif + +#ifdef UTF8 +# define UCS_REPL 0xfffd /* character for illegal codes */ +# define UCS_REPL_DW 0xff1f /* character for illegal codes */ +# define UCS_HIDDEN 0xffff +#endif + +#ifdef DW_CHARS +# define is_dw_font(f) ((f) && ((f) & 0x60) == 0) + +# ifdef UTF8 +# define dw_left(ml, x, enc) ((enc == UTF8) ? \ + (unsigned char)(ml)->font[(x) + 1] == 0xff && (unsigned char)(ml)->image[(x) + 1] == 0xff : \ + ((unsigned char)(ml)->font[x] & 0x1f) != 0 && ((unsigned char)(ml)->font[x] & 0xe0) == 0 \ + ) +# define dw_right(ml, x, enc) ((enc == UTF8) ? \ + (unsigned char)(ml)->font[x] == 0xff && (unsigned char)(ml)->image[x] == 0xff : \ + ((unsigned char)(ml)->font[x] & 0xe0) == 0x80 \ + ) +# else +# define dw_left(ml, x, enc) ( \ + ((unsigned char)(ml)->font[x] & 0x1f) != 0 && ((unsigned char)(ml)->font[x] & 0xe0) == 0 \ + ) +# define dw_right(ml, x, enc) ( \ + ((unsigned char)(ml)->font[x] & 0xe0) == 0x80 \ + ) +# endif /* UTF8 */ +#else +# define dw_left(ml, x, enc) 0 +# define dw_right(ml, x, enc) 0 +#endif diff --git a/attacher.c b/attacher.c new file mode 100644 index 0000000..18ba43c --- /dev/null +++ b/attacher.c @@ -0,0 +1,1117 @@ +/* Copyright (c) 2008, 2009 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Micah Cowan (micah@cowan.name) + * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) + * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, see + * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + */ + +#include "config.h" +#include <stdbool.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <signal.h> +#include "screen.h" +#include "extern.h" + +#include <pwd.h> + +static int WriteMessage __P((int, struct msg *)); +static sigret_t AttacherSigInt __P(SIGPROTOARG); +#if defined(SIGWINCH) && defined(TIOCGWINSZ) +static sigret_t AttacherWinch __P(SIGPROTOARG); +#endif +#ifdef LOCK +static sigret_t DoLock __P(SIGPROTOARG); +static void LockTerminal __P((void)); +static sigret_t LockHup __P(SIGPROTOARG); +static void screen_builtin_lck __P((void)); +#endif +#ifdef DEBUG +static sigret_t AttacherChld __P(SIGPROTOARG); +#endif +static sigret_t AttachSigCont __P(SIGPROTOARG); + +extern int real_uid, real_gid, eff_uid, eff_gid; +extern int ServerSocket; +extern struct display *displays; +extern char *SockName, *SockMatch, SockPath[]; +extern char HostName[]; +extern struct passwd *ppp; +extern char *attach_tty, *attach_term, *LoginName, *preselect; +/* Indicator whether the current tty exists in another namespace. */ +extern bool attach_tty_is_in_new_ns; +/* Content of the tty symlink when attach_tty_is_in_new_ns == true. */ +extern char attach_tty_name_in_ns[]; +extern int xflag, dflag, rflag, quietflag, adaptflag; +extern struct mode attach_Mode; +extern struct NewWindow nwin_options; +extern int MasterPid, attach_fd; + +#ifdef MULTIUSER +extern char *multi; +extern int multiattach, multi_uid, own_uid; +extern int tty_mode, tty_oldmode; +# ifndef USE_SETEUID +static int multipipe[2]; +# endif +#endif + + +static int ContinuePlease; + +static sigret_t +AttachSigCont SIGDEFARG +{ + debug("SigCont()\n"); + ContinuePlease = 1; + SIGRETURN; +} + +static int QueryResult; + +static sigret_t +QueryResultSuccess SIGDEFARG +{ + QueryResult = 1; + SIGRETURN; +} + +static sigret_t +QueryResultFail SIGDEFARG +{ + QueryResult = 2; + SIGRETURN; +} + +/* + * Send message to a screen backend. + * returns 1 if we could attach one, or 0 if none. + * Understands MSG_ATTACH, MSG_DETACH, MSG_POW_DETACH + * MSG_CONT, MSG_WINCH and nothing else! + * + * if type == MSG_ATTACH and sockets are used, attaches + * tty filedescriptor. + */ + +static int +WriteMessage(s, m) +int s; +struct msg *m; +{ + int r, l = sizeof(*m); + bool is_socket; + + is_socket = IsSocket(SockPath); + if (is_socket && m->type == MSG_ATTACH) + return SendAttachMsg(s, m, attach_fd); + + while(l > 0) + { + r = write(s, (char *)m + (sizeof(*m) - l), l); + if (r == -1 && errno == EINTR) + continue; + if (r == -1 || r == 0) + return -1; + l -= r; + } + return 0; +} + + +int +Attach(how) +int how; +{ + int n, lasts; + struct msg m; + struct stat st; + char *s; + bool is_socket; + + debug2("Attach: how=%d, tty=%s\n", how, attach_tty); +#ifdef MULTIUSER +# ifndef USE_SETEUID + while ((how == MSG_ATTACH || how == MSG_CONT) && multiattach) + { + int ret; + + if (pipe(multipipe)) + Panic(errno, "pipe"); + if (chmod(attach_tty, 0666)) + Panic(errno, "chmod %s", attach_tty); + tty_oldmode = tty_mode; + eff_uid = -1; /* make UserContext fork */ + real_uid = multi_uid; + if ((ret = UserContext()) <= 0) + { + char dummy; + eff_uid = 0; + real_uid = own_uid; + if (ret < 0) + Panic(errno, "UserContext"); + close(multipipe[1]); + read(multipipe[0], &dummy, 1); + if (tty_oldmode >= 0) + { + chmod(attach_tty, tty_oldmode); + tty_oldmode = -1; + } + ret = UserStatus(); +#ifdef LOCK + if (ret == SIG_LOCK) + LockTerminal(); + else +#endif +#ifdef SIGTSTP + if (ret == SIG_STOP) + kill(getpid(), SIGTSTP); + else +#endif + if (ret == SIG_POWER_BYE) + { + int ppid; + if (setgid(real_gid) || setuid(real_uid)) + Panic(errno, "setuid/gid"); + if ((ppid = getppid()) > 1) + Kill(ppid, SIGHUP); + exit(0); + } + else + exit(ret); + dflag = 0; +#ifdef MULTI + xflag = 1; +#endif + how = MSG_ATTACH; + continue; + } + close(multipipe[0]); + eff_uid = real_uid; + break; + } +# else /* USE_SETEUID */ + if ((how == MSG_ATTACH || how == MSG_CONT) && multiattach) + { + real_uid = multi_uid; + eff_uid = own_uid; +#ifdef HAVE_SETRESUID + if (setresuid(multi_uid, own_uid, multi_uid)) + Panic(errno, "setresuid"); +#else + xseteuid(multi_uid); + xseteuid(own_uid); +#endif + if (chmod(attach_tty, 0666)) + Panic(errno, "chmod %s", attach_tty); + tty_oldmode = tty_mode; + } +# endif /* USE_SETEUID */ +#endif /* MULTIUSER */ + + bzero((char *) &m, sizeof(m)); + m.type = how; + m.protocol_revision = MSG_REVISION; + strncpy(m.m_tty, attach_tty_is_in_new_ns ? attach_tty_name_in_ns : attach_tty, sizeof(m.m_tty) - 1); + m.m_tty[sizeof(m.m_tty) - 1] = 0; + + is_socket = IsSocket(SockPath); + if (how == MSG_WINCH) + { + if ((lasts = MakeClientSocket(0, is_socket)) >= 0) + { + WriteMessage(lasts, &m); + close(lasts); + } + return 0; + } + + if (how == MSG_CONT) + { + if ((lasts = MakeClientSocket(0, is_socket)) < 0) + { + Panic(0, "Sorry, cannot contact session \"%s\" again.\r\n", + SockName); + } + } + else + { + n = FindSocket(&lasts, (int *)0, (int *)0, SockMatch, &is_socket); + switch (n) + { + case 0: + if (rflag && (rflag & 1) == 0) + return 0; + if (quietflag) + eexit(10); + if (SockMatch && *SockMatch) { + Panic(0, "There is no screen to be %sed matching %s.", + xflag ? "attach" : + dflag ? "detach" : + "resum", SockMatch); + } else { + Panic(0, "There is no screen to be %sed.", + xflag ? "attach" : + dflag ? "detach" : + "resum"); + } + /* NOTREACHED */ + case 1: + break; + default: + if (rflag < 3) + { + if (quietflag) + eexit(10 + n); + Panic(0, "Type \"screen [-d] -r [pid.]tty.host\" to resume one of them."); + } + /* NOTREACHED */ + } + } + /* + * Go in UserContext. Advantage is, you can kill your attacher + * when things go wrong. Any disadvantages? jw. + * Do this before the attach to prevent races! + */ +#ifdef MULTIUSER + if (!multiattach) +#endif + { + if (setuid(real_uid)) + Panic(errno, "setuid"); + } +#if defined(MULTIUSER) && defined(USE_SETEUID) + else + { + /* This call to xsetuid should also set the saved uid */ + xseteuid(real_uid); /* multi_uid, allow backend to send signals */ + } +#endif + eff_uid = real_uid; + if (setgid(real_gid)) + Panic(errno, "setgid"); + eff_gid = real_gid; + + debug2("Attach: uid %d euid %d\n", (int)getuid(), (int)geteuid()); + MasterPid = 0; + for (s = SockName; *s; s++) + { + if (*s > '9' || *s < '0') + break; + MasterPid = 10 * MasterPid + (*s - '0'); + } + debug1("Attach decided, it is '%s'\n", SockPath); + debug1("Attach found MasterPid == %d\n", MasterPid); + if (stat(SockPath, &st) == -1) + Panic(errno, "stat %s", SockPath); + if ((st.st_mode & 0600) != 0600) + Panic(0, "Socket is in wrong mode (%03o)", (int)st.st_mode); + + /* + * Change: if -x or -r ignore failing -d + */ + if ((xflag || rflag) && dflag && (st.st_mode & 0700) == 0600) + dflag = 0; + + /* + * Without -x, the mode must match. + * With -x the mode is irrelevant unless -d. + */ + if ((dflag || !xflag) && (st.st_mode & 0700) != (dflag ? 0700 : 0600)) + Panic(0, "That screen is %sdetached.", dflag ? "already " : "not "); +#ifdef REMOTE_DETACH + if (dflag && + (how == MSG_DETACH || how == MSG_POW_DETACH)) + { + m.m.detach.dpid = getpid(); + strncpy(m.m.detach.duser, LoginName, sizeof(m.m.detach.duser) - 1); + m.m.detach.duser[sizeof(m.m.detach.duser) - 1] = 0; +# ifdef POW_DETACH + if (dflag == 2) + m.type = MSG_POW_DETACH; + else +# endif + m.type = MSG_DETACH; + /* If there is no password for the session, or the user enters the correct + * password, then we get a SIGCONT. Otherwise we get a SIG_BYE */ + signal(SIGCONT, AttachSigCont); + if (WriteMessage(lasts, &m)) + Panic(errno, "WriteMessage"); + close(lasts); + while (!ContinuePlease) + pause(); /* wait for SIGCONT */ + signal(SIGCONT, SIG_DFL); + ContinuePlease = 0; + if (how != MSG_ATTACH) + return 0; /* we detached it. jw. */ + sleep(1); /* we dont want to overrun our poor backend. jw. */ + if ((lasts = MakeClientSocket(0, is_socket)) == -1) + Panic(0, "Cannot contact screen again. Sigh."); + m.type = how; + } +#endif + ASSERT(how == MSG_ATTACH || how == MSG_CONT); + strncpy(m.m.attach.envterm, attach_term, MAXTERMLEN); + m.m.attach.envterm[MAXTERMLEN] = 0; + debug1("attach: sending %d bytes... ", (int)sizeof(m)); + + strncpy(m.m.attach.auser, LoginName, sizeof(m.m.attach.auser) - 1); + m.m.attach.auser[sizeof(m.m.attach.auser) - 1] = 0; + m.m.attach.esc = DefaultEsc; + m.m.attach.meta_esc = DefaultMetaEsc; + strncpy(m.m.attach.preselect, preselect ? preselect : "", sizeof(m.m.attach.preselect) - 1); + m.m.attach.preselect[sizeof(m.m.attach.preselect) - 1] = 0; + m.m.attach.apid = getpid(); + m.m.attach.adaptflag = adaptflag; + m.m.attach.lines = m.m.attach.columns = 0; + if ((s = getenv("LINES"))) + m.m.attach.lines = atoi(s); + if ((s = getenv("COLUMNS"))) + m.m.attach.columns = atoi(s); + m.m.attach.encoding = nwin_options.encoding > 0 ? nwin_options.encoding + 1 : 0; + +#ifdef REMOTE_DETACH +#ifdef POW_DETACH + if (dflag == 2) + m.m.attach.detachfirst = MSG_POW_DETACH; + else +#endif + if (dflag) + m.m.attach.detachfirst = MSG_DETACH; + else +#endif + m.m.attach.detachfirst = MSG_ATTACH; + +#ifdef MULTIUSER + /* setup CONT signal handler to repair the terminal mode */ + if (multi && (how == MSG_ATTACH || how == MSG_CONT)) + signal(SIGCONT, AttachSigCont); +#endif + + if (WriteMessage(lasts, &m)) + Panic(errno, "WriteMessage"); + close(lasts); + debug1("Attach(%d): sent\n", m.type); +#ifdef MULTIUSER + if (multi && (how == MSG_ATTACH || how == MSG_CONT)) + { + while (!ContinuePlease) + pause(); /* wait for SIGCONT */ + signal(SIGCONT, SIG_DFL); + ContinuePlease = 0; +# ifndef USE_SETEUID + close(multipipe[1]); +# else + xseteuid(own_uid); + if (tty_oldmode >= 0) + if (chmod(attach_tty, tty_oldmode)) + Panic(errno, "chmod %s", attach_tty); + tty_oldmode = -1; + xseteuid(real_uid); +# endif + } +#endif + rflag = 0; + return 1; +} + + +static int AttacherPanic = 0; + +#ifdef DEBUG +static sigret_t +AttacherChld SIGDEFARG +{ + AttacherPanic = 1; + SIGRETURN; +} +#endif + +static sigret_t +AttacherSigAlarm SIGDEFARG +{ +#ifdef DEBUG + static int tick_cnt = 0; + if ((tick_cnt = (tick_cnt + 1) % 4) == 0) + debug("tick\n"); +#endif + SIGRETURN; +} + +/* + * the frontend's Interrupt handler + * we forward SIGINT to the poor backend + */ +static sigret_t +AttacherSigInt SIGDEFARG +{ + signal(SIGINT, AttacherSigInt); + Kill(MasterPid, SIGINT); + SIGRETURN; +} + +/* + * Unfortunately this is also the SIGHUP handler, so we have to + * check if the backend is already detached. + */ + +sigret_t +AttacherFinit SIGDEFARG +{ + struct stat statb; + struct msg m; + int s; + bool is_socket; + + debug("AttacherFinit();\n"); + signal(SIGHUP, SIG_IGN); + /* Check if signal comes from backend */ + if (stat(SockPath, &statb) == 0 && (statb.st_mode & 0777) != 0600) + { + debug("Detaching backend!\n"); + bzero((char *) &m, sizeof(m)); + strncpy(m.m_tty, attach_tty_is_in_new_ns ? attach_tty_name_in_ns : attach_tty, sizeof(m.m_tty) - 1); + m.m_tty[sizeof(m.m_tty) - 1] = 0; + debug1("attach_tty is %s\n", attach_tty); + m.m.detach.dpid = getpid(); + m.type = MSG_HANGUP; + m.protocol_revision = MSG_REVISION; + is_socket = IsSocket(SockPath); + if ((s = MakeClientSocket(0, is_socket)) >= 0) + { + WriteMessage(s, &m); + close(s); + } + } +#ifdef MULTIUSER + if (tty_oldmode >= 0) + { + if (setuid(own_uid)) + Panic(errno, "setuid"); + chmod(attach_tty, tty_oldmode); + } +#endif + exit(0); + SIGRETURN; +} + +#ifdef POW_DETACH +static sigret_t +AttacherFinitBye SIGDEFARG +{ + int ppid; + debug("AttacherFintBye()\n"); +#if defined(MULTIUSER) && !defined(USE_SETEUID) + if (multiattach) + exit(SIG_POWER_BYE); +#endif + if (setgid(real_gid)) + Panic(errno, "setgid"); +#ifdef MULTIUSER + if (setuid(own_uid)) + Panic(errno, "setuid"); +#else + if (setuid(real_uid)) + Panic(errno, "setuid"); +#endif + /* we don't want to disturb init (even if we were root), eh? jw */ + if ((ppid = getppid()) > 1) + Kill(ppid, SIGHUP); /* carefully say good bye. jw. */ + exit(0); + SIGRETURN; +} +#endif + +#if defined(DEBUG) && defined(SIG_NODEBUG) +static sigret_t +AttacherNoDebug SIGDEFARG +{ + debug("AttacherNoDebug()\n"); + signal(SIG_NODEBUG, AttacherNoDebug); + if (dfp) + { + debug("debug: closing debug file.\n"); + fflush(dfp); + fclose(dfp); + dfp = NULL; + } + SIGRETURN; +} +#endif /* SIG_NODEBUG */ + +static int SuspendPlease; + +static sigret_t +SigStop SIGDEFARG +{ + debug("SigStop()\n"); + SuspendPlease = 1; + SIGRETURN; +} + +#ifdef LOCK +static int LockPlease; + +static sigret_t +DoLock SIGDEFARG +{ +# ifdef SYSVSIGS + signal(SIG_LOCK, DoLock); +# endif + debug("DoLock()\n"); + LockPlease = 1; + SIGRETURN; +} +#endif + +#if defined(SIGWINCH) && defined(TIOCGWINSZ) +static int SigWinchPlease; + +static sigret_t +AttacherWinch SIGDEFARG +{ + debug("AttacherWinch()\n"); + SigWinchPlease = 1; + SIGRETURN; +} +#endif + + +/* + * Attacher loop - no return + */ + +void +Attacher() +{ + signal(SIGHUP, AttacherFinit); + signal(SIG_BYE, AttacherFinit); +#ifdef POW_DETACH + signal(SIG_POWER_BYE, AttacherFinitBye); +#endif +#if defined(DEBUG) && defined(SIG_NODEBUG) + signal(SIG_NODEBUG, AttacherNoDebug); +#endif +#ifdef LOCK + signal(SIG_LOCK, DoLock); +#endif + signal(SIGINT, AttacherSigInt); +#ifdef BSDJOBS + signal(SIG_STOP, SigStop); +#endif +#if defined(SIGWINCH) && defined(TIOCGWINSZ) + signal(SIGWINCH, AttacherWinch); +#endif +#ifdef DEBUG + signal(SIGCHLD, AttacherChld); +#endif + debug("attacher: going for a nap.\n"); + dflag = 0; +#ifdef MULTI + xflag = 1; +#endif + for (;;) + { + signal(SIGALRM, AttacherSigAlarm); + alarm(15); + pause(); + alarm(0); + if (kill(MasterPid, 0) < 0 && errno != EPERM) + { + debug1("attacher: Panic! MasterPid %d does not exist.\n", MasterPid); + AttacherPanic++; + } + if (AttacherPanic) + { + fcntl(0, F_SETFL, 0); + SetTTY(0, &attach_Mode); + printf("\nError: Cannot find master process to attach to!\n"); + eexit(1); + } +#ifdef BSDJOBS + if (SuspendPlease) + { + SuspendPlease = 0; +#if defined(MULTIUSER) && !defined(USE_SETEUID) + if (multiattach) + exit(SIG_STOP); +#endif + signal(SIGTSTP, SIG_DFL); + debug("attacher: killing myself SIGTSTP\n"); + kill(getpid(), SIGTSTP); + debug("attacher: continuing from stop\n"); + signal(SIG_STOP, SigStop); + (void) Attach(MSG_CONT); + } +#endif +#ifdef LOCK + if (LockPlease) + { + LockPlease = 0; +#if defined(MULTIUSER) && !defined(USE_SETEUID) + if (multiattach) + exit(SIG_LOCK); +#endif + LockTerminal(); +# ifdef SYSVSIGS + signal(SIG_LOCK, DoLock); +# endif + (void) Attach(MSG_CONT); + } +#endif /* LOCK */ +#if defined(SIGWINCH) && defined(TIOCGWINSZ) + if (SigWinchPlease) + { + SigWinchPlease = 0; +# ifdef SYSVSIGS + signal(SIGWINCH, AttacherWinch); +# endif + (void) Attach(MSG_WINCH); + } +#endif /* SIGWINCH */ + } +} + +#ifdef LOCK + +/* ADDED by Rainer Pruy 10/15/87 */ +/* POLISHED by mls. 03/10/91 */ + +static char LockEnd[] = "Welcome back to screen !!\n"; + +static sigret_t +LockHup SIGDEFARG +{ + int ppid = getppid(); + if (setgid(real_gid)) + Panic(errno, "setgid"); +#ifdef MULTIUSER + if (setuid(own_uid)) + Panic(errno, "setuid"); +#else + if (setuid(real_uid)) + Panic(errno, "setuid"); +#endif + if (ppid > 1) + Kill(ppid, SIGHUP); + exit(0); +} + +static void +LockTerminal() +{ + char *prg; + int sig, pid; + sigret_t (*sigs[NSIG])__P(SIGPROTOARG); + + for (sig = 1; sig < NSIG; sig++) + sigs[sig] = signal(sig, sig == SIGCHLD ? SIG_DFL : SIG_IGN); + signal(SIGHUP, LockHup); + printf("\n"); + + prg = getenv("LOCKPRG"); + if (prg && strcmp(prg, "builtin") && !access(prg, X_OK)) + { + signal(SIGCHLD, SIG_DFL); + debug1("lockterminal: '%s' seems executable, execl it!\n", prg); + if ((pid = fork()) == 0) + { + /* Child */ + displays = 0; /* beware of Panic() */ + ServerSocket = -1; + if (setgid(real_gid)) + Panic(errno, "setgid"); +#ifdef MULTIUSER + if (setuid(own_uid)) + Panic(errno, "setuid"); +#else + if (setuid(real_uid)) /* this should be done already */ + Panic(errno, "setuid"); +#endif + closeallfiles(0); /* important: /etc/shadow may be open */ + execl(prg, "SCREEN-LOCK", NULL); + exit(errno); + } + if (pid == -1) + Msg(errno, "Cannot lock terminal - fork failed"); + else + { +#ifdef BSDWAIT + union wait wstat; +#else + int wstat; +#endif + int wret; + +#ifdef hpux + signal(SIGCHLD, SIG_DFL); +#endif + errno = 0; + while (((wret = wait(&wstat)) != pid) || + ((wret == -1) && (errno == EINTR)) + ) + errno = 0; + + if (errno) + { + Msg(errno, "Lock"); + sleep(2); + } + else if (WTERMSIG(wstat) != 0) + { + fprintf(stderr, "Lock: %s: Killed by signal: %d%s\n", prg, + WTERMSIG(wstat), WIFCORESIG(wstat) ? " (Core dumped)" : ""); + sleep(2); + } + else if (WEXITSTATUS(wstat)) + { + debug2("Lock: %s: return code %d\n", prg, WEXITSTATUS(wstat)); + } + else + printf("%s", LockEnd); + } + } + else + { + if (prg) + { + debug1("lockterminal: '%s' seems NOT executable, we use our builtin\n", prg); + } + else + { + debug("lockterminal: using buitin.\n"); + } + screen_builtin_lck(); + } + /* reset signals */ + for (sig = 1; sig < NSIG; sig++) + { + if (sigs[sig] != (sigret_t(*)__P(SIGPROTOARG)) -1) + signal(sig, sigs[sig]); + } +} /* LockTerminal */ + +#ifdef USE_PAM + +/* + * PAM support by Pablo Averbuj <pablo@averbuj.com> + */ + +#include <security/pam_appl.h> + +static int PAM_conv __P((int, const struct pam_message **, struct pam_response **, void *)); + +static int +PAM_conv(num_msg, msg, resp, appdata_ptr) +int num_msg; +const struct pam_message **msg; +struct pam_response **resp; +void *appdata_ptr; +{ + int replies = 0; + struct pam_response *reply = NULL; + + reply = malloc(sizeof(struct pam_response)*num_msg); + if (!reply) + return PAM_CONV_ERR; + #define COPY_STRING(s) (s) ? strdup(s) : NULL + + for (replies = 0; replies < num_msg; replies++) + { + switch (msg[replies]->msg_style) + { + case PAM_PROMPT_ECHO_OFF: + /* wants password */ + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = appdata_ptr ? strdup((char *)appdata_ptr) : 0; + break; + case PAM_TEXT_INFO: + /* ignore the informational mesage */ + /* but first clear out any drek left by malloc */ + reply[replies].resp = NULL; + break; + case PAM_PROMPT_ECHO_ON: + /* user name given to PAM already */ + /* fall through */ + default: + /* unknown or PAM_ERROR_MSG */ + free(reply); + return PAM_CONV_ERR; + } + } + *resp = reply; + return PAM_SUCCESS; +} + +static struct pam_conv PAM_conversation = { + &PAM_conv, + NULL +}; + + +#endif + +/* -- original copyright by Luigi Cannelloni 1985 (luigi@faui70.UUCP) -- */ +static void +screen_builtin_lck() +{ + char fullname[100], *cp1, message[100 + 100]; +#ifdef USE_PAM + pam_handle_t *pamh = 0; + int pam_error; + char *tty_name; +#endif + char *pass = 0, mypass[16 + 1], salt[3]; + int using_pam = 1; + +#ifdef USE_PAM + if (!ppp->pw_uid) + { +#endif + using_pam = 0; + pass = ppp->pw_passwd; + if (pass == 0 || *pass == 0) + { + if ((pass = getpass("Key: "))) + { + strncpy(mypass, pass, sizeof(mypass) - 1); + mypass[sizeof(mypass) - 1] = 0; + if (*mypass == 0) + return; + if ((pass = getpass("Again: "))) + { + if (strcmp(mypass, pass)) + { + fprintf(stderr, "Passwords don't match.\007\n"); + sleep(2); + return; + } + } + } + if (pass == 0) + { + fprintf(stderr, "Getpass error.\007\n"); + sleep(2); + return; + } + + salt[0] = 'A' + (int)(time(0) % 26); + salt[1] = 'A' + (int)((time(0) >> 6) % 26); + salt[2] = 0; + pass = crypt(mypass, salt); + if (!pass) + { + fprintf(stderr, "crypt() error.\007\n"); + sleep(2); + return; + } + pass = ppp->pw_passwd = SaveStr(pass); + } +#ifdef USE_PAM + } +#endif + + debug("screen_builtin_lck looking in gcos field\n"); + strncpy(fullname, ppp->pw_gecos, sizeof(fullname) - 9); + fullname[sizeof(fullname) - 9] = 0; + + if ((cp1 = index(fullname, ',')) != NULL) + *cp1 = '\0'; + if ((cp1 = index(fullname, '&')) != NULL) + { + strncpy(cp1, ppp->pw_name, 8); + cp1[8] = 0; + if (*cp1 >= 'a' && *cp1 <= 'z') + *cp1 -= 'a' - 'A'; + } + + sprintf(message, "Screen used by %s%s<%s> on %s.\nPassword:\007", + fullname, fullname[0] ? " " : "", ppp->pw_name, HostName); + + /* loop here to wait for correct password */ + for (;;) + { + debug("screen_builtin_lck awaiting password\n"); + errno = 0; + if ((cp1 = getpass(message)) == NULL) + { + AttacherFinit(SIGARG); + /* NOTREACHED */ + } + if (using_pam) + { +#ifdef USE_PAM + PAM_conversation.appdata_ptr = cp1; + pam_error = pam_start("screen", ppp->pw_name, &PAM_conversation, &pamh); + if (pam_error != PAM_SUCCESS) + AttacherFinit(SIGARG); /* goodbye */ + + if (strncmp(attach_tty, "/dev/", 5) == 0) + tty_name = attach_tty + 5; + else + tty_name = attach_tty; + pam_error = pam_set_item(pamh, PAM_TTY, tty_name); + if (pam_error != PAM_SUCCESS) + AttacherFinit(SIGARG); /* goodbye */ + + pam_error = pam_authenticate(pamh, 0); + pam_end(pamh, pam_error); + PAM_conversation.appdata_ptr = 0; + if (pam_error == PAM_SUCCESS) + break; +#endif + } + else + { + char *buf = crypt(cp1, pass); + if (buf && !strncmp(buf, pass, strlen(pass))) + break; + } + debug("screen_builtin_lck: NO!!!!!\n"); + bzero(cp1, strlen(cp1)); + } + bzero(cp1, strlen(cp1)); + debug("password ok.\n"); +} + +#endif /* LOCK */ + + +void +SendCmdMessage(sty, match, av, query) +char *sty; +char *match; +char **av; +int query; +{ + int i, s; + struct msg m; + char *p; + int len, n; + bool is_socket; + + if (sty == 0) + { + i = FindSocket(&s, (int *)0, (int *)0, match, &is_socket); + if (i == 0) + Panic(0, "No screen session found."); + if (i != 1) + Panic(0, "Use -S to specify a session."); + } + else + { +#ifdef NAME_MAX + if (strlen(sty) > NAME_MAX) + sty[NAME_MAX] = 0; +#endif + if (strlen(sty) > 2 * MAXSTR - 1) + sty[2 * MAXSTR - 1] = 0; + sprintf(SockPath + strlen(SockPath), "/%s", sty); + is_socket = IsSocket(SockPath); + if ((s = MakeClientSocket(1, is_socket)) == -1) + exit(1); + } + bzero((char *)&m, sizeof(m)); + m.type = query ? MSG_QUERY : MSG_COMMAND; + if (attach_tty) + { + strncpy(m.m_tty, attach_tty_is_in_new_ns ? attach_tty_name_in_ns : attach_tty, sizeof(m.m_tty) - 1); + m.m_tty[sizeof(m.m_tty) - 1] = 0; + } + p = m.m.command.cmd; + n = 0; + for (; *av && n < MAXARGS - 1; ++av, ++n) + { + len = strlen(*av) + 1; + if (p + len >= m.m.command.cmd + sizeof(m.m.command.cmd) - 1) + break; + strcpy(p, *av); + p += len; + } + *p = 0; + m.m.command.nargs = n; + strncpy(m.m.attach.auser, LoginName, sizeof(m.m.attach.auser) - 1); + m.m.command.auser[sizeof(m.m.command.auser) - 1] = 0; + m.protocol_revision = MSG_REVISION; + strncpy(m.m.command.preselect, preselect ? preselect : "", sizeof(m.m.command.preselect) - 1); + m.m.command.preselect[sizeof(m.m.command.preselect) - 1] = 0; + m.m.command.apid = getpid(); + debug1("SendCommandMsg writing '%s'\n", m.m.command.cmd); + if (query) + { + /* Create a server socket so we can get back the result */ + char *sp = SockPath + strlen(SockPath); + char query[] = "-queryX"; + char c; + int r = -1; + for (c = 'A'; c <= 'Z'; c++) + { + query[6] = c; + strcpy(sp, query); /* XXX: strncpy? */ + if ((r = MakeServerSocket(is_socket)) >= 0) + break; + } + if (r < 0) + { + for (c = '0'; c <= '9'; c++) + { + query[6] = c; + strcpy(sp, query); + if ((r = MakeServerSocket(is_socket)) >= 0) + break; + } + } + + if (r < 0) + Panic(0, "Could not create a listening socket to read the results."); + + strncpy(m.m.command.writeback, SockPath, sizeof(m.m.command.writeback) - 1); + m.m.command.writeback[sizeof(m.m.command.writeback) - 1] = '\0'; + + /* Send the message, then wait for a response */ + signal(SIGCONT, QueryResultSuccess); + signal(SIG_BYE, QueryResultFail); + if (WriteMessage(s, &m)) + Msg(errno, "write"); + close(s); + while (!QueryResult) + pause(); + signal(SIGCONT, SIG_DFL); + signal(SIG_BYE, SIG_DFL); + + /* Read the result and spit it out to stdout */ + ReceiveRaw(r); + unlink(SockPath); + if (QueryResult == 2) /* An error happened */ + exit(1); + } + else + { + if (WriteMessage(s, &m)) + Msg(errno, "write"); + close(s); + } +} + diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..68f4a17 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,2 @@ +#!/bin/sh +autoreconf -i diff --git a/braille.c b/braille.c new file mode 100644 index 0000000..93a3ee1 --- /dev/null +++ b/braille.c @@ -0,0 +1,945 @@ +/* + * A braille interface to unix tty terminals + * + * Authors: Hadi Bargi Rangin bargi@dots.physics.orst.edu + * Bill Barry barryb@dots.physics.orst.edu + * + * Copyright (c) 1995 by Science Access Project, Oregon State University. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, see + * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + */ + +#include <stdio.h> +#include <fcntl.h> +#include <sys/stat.h> + +#include "config.h" +#include "screen.h" +#include "extern.h" +#include "braille.h" + +#ifdef HAVE_BRAILLE + + +extern int bd_init_powerbraille_40 __P((void)); +extern int bd_init_powerbraille_80 __P((void)); +extern int bd_init_navigator_40 __P((void)); + +extern struct layer *flayer; +extern struct display *displays, *display; +extern char *rc_name; + + + + +/* global variables */ + +struct braille_display bd; + +struct bd_type { + char *name; + int (*init) __P((void)); +}; + +static struct bd_type bd_typelist[] = +{ + {"powerbraille_40", bd_init_powerbraille_40}, + {"powerbraille_80", bd_init_powerbraille_80}, + {"navigator_40" , bd_init_navigator_40} +}; + +static void position_braille_cursor __P((void)); +static int initialize_braille_display_type __P((char *)); +static int open_braille_device __P(()); +static int load_braille_table __P((char *)); +static void bd_signal __P((void)); +static void bd_bc_left __P((void)); +static void bd_bc_right __P((void)); +static void bd_bc_up __P((void)); +static void bd_bc_down __P((void)); +static void bd_upper_left __P((void)); +static void bd_upper_right __P((void)); +static void bd_lower_left __P((void)); +static void bd_lower_right __P((void)); +static int bd_do_search __P((int, int, int)); +static void bd_normalize __P((int, int)); +static void bd_readev_fn __P((struct event *, char *)); +static void bd_writeev_fn __P((struct event *, char *)); +static void bd_selectev_fn __P((struct event *, char *)); + +static unsigned char btable_local [] = +{ + 0xC8,0xC1,0xC3,0xC9,0xD9,0xD1,0xCB,0xDB, + 0xD3,0xCA,0xDA,0xC5,0xC7,0xCD,0xDD,0xD5, + 0xCF,0xDF,0xD7,0xCE,0xDE,0xE5,0xE7,0xFA, + 0xED,0xFD,0xF5,0xEA,0xF3,0xFB,0xD8,0xF8, + 0x00,0x2E,0x10,0x3C,0x2B,0x29,0x2F,0x04, + 0x37,0x3E,0x21,0x2C,0x20,0x24,0x28,0x0C, + 0x34,0x02,0x06,0x12,0x32,0x22,0x16,0x36, + 0x26,0x14,0x31,0x30,0x23,0x3F,0x1C,0x39, + 0x48,0x41,0x43,0x49,0x59,0x51,0x4B,0x5B, + 0x53,0x4A,0x5A,0x45,0x47,0x4D,0x5D,0x55, + 0x4F,0x5F,0x57,0x4E,0x5E,0x65,0x67,0x7A, + 0x6D,0x7D,0x75,0x6A,0x73,0x7B,0x58,0x38, + 0x08,0x01,0x03,0x09,0x19,0x11,0x0B,0x1B, + 0x13,0x0A,0x1A,0x05,0x07,0x0D,0x1D,0x15, + 0x0F,0x1F,0x17,0x0E,0x1E,0x25,0x27,0x3A, + 0x2D,0x3D,0x35,0x2A,0x33,0x3B,0x18,0x78, + 0x88,0x81,0x83,0x89,0x99,0x91,0x8B,0x9B, + 0x93,0x8A,0x9A,0x85,0x87,0x8D,0x9D,0x95, + 0x8F,0x9F,0x97,0x8E,0x9E,0xA5,0xA7,0xBA, + 0xAD,0xBD,0xB5,0xAA,0xB3,0xBB,0x98,0xB8, + 0x40,0x6E,0x50,0x7C,0x6B,0x69,0x6F,0x44, + 0x77,0x7E,0x61,0x6C,0x60,0x64,0x68,0x4C, + 0x74,0x42,0x46,0x52,0x72,0x62,0x56,0x76, + 0x66,0x54,0x71,0x70,0x63,0x7F,0x5C,0x79, + 0xC0,0xEE,0xD0,0xFC,0xEB,0xE9,0xEF,0xC4, + 0xF7,0xFE,0xE1,0xEC,0xE0,0xE4,0xE8,0xCC, + 0xF4,0xC2,0xC6,0xD2,0xF2,0xE2,0xD6,0xF6, + 0xE6,0xD4,0xF1,0xF0,0xE3,0xFF,0xDC,0xF9, + 0x80,0xAE,0x90,0xBC,0xAB,0xA9,0xAF,0x84, + 0xB7,0xBE,0xA1,0xAC,0xA0,0xA4,0xA8,0x8C, + 0xB4,0x82,0x86,0x92,0xB2,0xA2,0x96,0xB6, + 0xA6,0x94,0xB1,0xB0,0xA3,0xBF,0x9C,0xB9 +}; + +void +InitBraille() +{ + bd.bd_start_braille=0; + bd.bd_port = 0; + bd.bd_braille_table = SaveStr("internal us-braille.tbl"); + bd.bd_type = 0; + bd.bd_baud = 9600; + bd.bd_bell = 1; + bd.bd_eightdot = 1; + bd.bd_info = 0; + bd.bd_link = 1; + bd.bd_ncells = 0; + bd.bd_width = 0; + bd.bd_ncrc = 1; + bd.bd_scroll = 1; + bd.bd_skip = 0; + bd.bd_using_braille = 0; + bd.bd_obuflen = 0; + bd.bd_fd = -1; + bcopy((char *)btable_local, bd.bd_btable, 256); +} + +static int +initialize_braille_display_type(s) +char *s; +{ + int i; + + for (i = 0; i < sizeof(bd_typelist)/sizeof(*bd_typelist); i++) + if (!strcmp(s, bd_typelist[i].name)) + break; + if (i == sizeof(bd_typelist)/sizeof(*bd_typelist)) + { + Msg(0, "No entry for bd_type: %s ", s); + return -1; + } + bd.bd_type = bd_typelist[i].name; + if ((*bd_typelist[i].init)()) + return -1; + + if (!bd.bd_width) + bd.bd_width = bd.bd_ncells; + + return 0; +} + +void +StartBraille() +{ + bd.bd_dpy = displays; + + debug("StartBraille called\n"); + evdeq(&bd.bd_readev); + evdeq(&bd.bd_writeev); + evdeq(&bd.bd_selectev); + bd.bd_using_braille = 0; + + if (!bd.bd_start_braille) + return; + + if (bd.bd_type == 0 || bd.bd_port == 0) + return; + + if (bd.bd_fd < 0 && open_braille_device()) + { + Msg(0, "bd_port turned off"); + free(bd.bd_port); + bd.bd_port = 0; + return; + } + + /* check if braille display is connected and turned on */ + if (bd.bd_response_test()) + { + Msg(0, "Make sure that braille display is connected and turned on. "); + Msg(0, "start_braille turned off"); + bd.bd_start_braille = 0; + } + else + { + bd.bd_using_braille = 1; + bd.bd_readev.fd = bd.bd_writeev.fd = bd.bd_fd; + bd.bd_readev.type = EV_READ; + bd.bd_writeev.type = EV_WRITE; + bd.bd_selectev.type = EV_ALWAYS; + bd.bd_readev.data = bd.bd_writeev.data = bd.bd_selectev.data = (char *)&bd; + bd.bd_readev.handler = bd_readev_fn; + bd.bd_writeev.handler = bd_writeev_fn; + bd.bd_selectev.handler = bd_selectev_fn; + evenq(&bd.bd_readev); + bd.bd_writeev.condpos = &bd.bd_obuflen; + bd.bd_writeev.condneg = 0; + evenq(&bd.bd_writeev); + bd.bd_selectev.pri = -20; + evenq(&bd.bd_selectev); + } +} + + +static int +load_braille_table(tablename) +char *tablename; +{ + int i, j, c, p; + FILE *fp; + char buffer[80], a[10]; + + if ((fp = secfopen(tablename, "r")) == 0) + { + Msg(errno, "Braille table not found: %s ", tablename); + return -1; + } + bzero(bd.bd_btable, 256); + /* format: + * Dec Hex Braille Description + * 7 07 (12-45--8) BEL + */ + while (fgets(buffer, sizeof(buffer), fp)) + { + if (buffer[0] == '#') + continue; + sscanf(buffer,"%d %x %8s", &i, &j, a); + if (i < 0 || i > 255) + continue; + for (j=1, p=1, c=0; j<9; j++, p*=2) + if (a[j] == '0' + j) + c += p; + bd.bd_btable[i] = c; + } + fclose(fp); + return 0; +} + + +static int +open_braille_device(s) +char *s; +{ + char str[256]; + + sprintf(str, "%d cs8 -istrip ixon ixoff", bd.bd_baud); + bd.bd_fd = OpenTTY(bd.bd_port, str); + if (bd.bd_fd == -1) + { + Msg(errno, "open comm port failed: %s ", bd.bd_port); + return -1; + } + fcntl(bd.bd_fd, F_SETFL, FNBLOCK); + return 0; +} + + +static void +position_braille_cursor() +{ + int sx = bd.bd_sx; + int bx = BD_FORE->w_bd_x; + int eol = BD_FORE->w_width; + int w = bd.bd_width; + + if (bd.bd_scroll) + bx = sx - w + bd.bd_ncrc; /* keep rc centered in window */ + else + bx = w * (int)(sx / w); /* increase bc in integral steps */ + + if (bx > eol - w) + bx = eol - w; + if (bx < 0) + bx = 0; + BD_FORE->w_bd_x = bx; + BD_FORE->w_bd_y = bd.bd_sy; +} + + +void +RefreshBraille() +{ + int i, y, xs, xe; + int cursor_pos; + + if (!bd.bd_using_braille) + return; + if (!BD_FORE) + return; + bcopy(bd.bd_line, bd.bd_oline, bd.bd_ncells); + bd.bd_refreshing = 1; + flayer = bd.bd_dpy->d_forecv->c_layer; + bd.bd_sx = flayer->l_x; + bd.bd_sy = flayer->l_y; + display = bd.bd_dpy; + if ((D_obufp != D_obuf) && bd.bd_link) + { + /* jump to real cursor */ + debug("calling position_braille_cursor\n"); + position_braille_cursor(); + } + bclear(bd.bd_line, bd.bd_ncells); + + y = BD_FORE->w_bd_y; + xs = BD_FORE->w_bd_x; + + if (bd.bd_info & 1) + { + sprintf(bd.bd_line, "%02d%02d", (BD_FORE->w_bd_x + 1) % 100, (BD_FORE->w_bd_y + 1) % 100); + bd.bd_line[4] = ' '; + } + if (bd.bd_info & 2) + { + sprintf(bd.bd_line + bd.bd_ncells - 4, "%02d%02d",(bd.bd_sx +1) % 100, (bd.bd_sy +1) % 100); + } + + xe = xs + bd.bd_width - 1; + + if (xs > flayer->l_width - 1) + xs = flayer->l_width - 1; + if (xe > flayer->l_width - 1) + xe = flayer->l_width - 1; + + if (D_status) + { + sprintf(bd.bd_line, "**%-*.*s", bd.bd_ncells - 2, bd.bd_ncells - 2, D_status_lastmsg ? D_status_lastmsg : "unknown msg"); + xs = xe = -1; + } + else if (xs <= xe) + { + LayRedisplayLine(-1, xs, xe, 1); + LayRedisplayLine(y, xs, xe, 1); + } + + debug1("Braille: got >%s<\n", bd.bd_line); + + bd.bd_refreshing = 0; + + if (y == bd.bd_sy && xs <= bd.bd_sx && bd.bd_sx <= xe) + cursor_pos = bd.bd_sx - xs + (bd.bd_info & 1 ? 4 : 0); + else + cursor_pos = bd.bd_ncells; + for (i = 0; i < bd.bd_ncells; i++) + if (bd.bd_line[i] != bd.bd_oline[i]) + break; + if (bd.bd_cursorpos != cursor_pos || i < bd.bd_ncells) + bd.write_line_braille(bd.bd_line, bd.bd_ncells, cursor_pos); + bd.bd_cursorpos = cursor_pos; +} + + +/********************************************************************** + * + */ + +/* + * So, why is there a Flush() down below? The reason is simple: the + * cursor warp (if bd_link is on) checks the obuf to see if something + * happened. If there would be no Flush, screen would warp the + * bd cursor if a bd movement command tries to ring the bell. + * (In other words: this is a gross hack!) + */ +static void +bd_signal() +{ + if (!bd.bd_bell) + return; + display = bd.bd_dpy; + if (D_obufp != D_obuf) + AddCStr(D_BL); + else + { + AddCStr(D_BL); + Flush(0); + } +} + +static int +bd_do_search(y, xs, xe) +int y, xs, xe; +{ + int oy = BD_FORE->w_bd_y; + + if (!bd.bd_skip) /* no skip mode, found it */ + { + if (xs > xe) + return 0; + bd.bd_searchmin = xs; + bd.bd_searchmax = xe; + return 1; + } + flayer = bd.bd_dpy->d_forecv->c_layer; + bd.bd_searchmax = -1; + bd.bd_searchmin = flayer->l_width; + if (xs <= xe) + { + BD_FORE->w_bd_y = y; /* stupid hack */ + bd.bd_refreshing = bd.bd_searching = 1; + bd.bd_searchstart = xs; + bd.bd_searchend = xe; + LayRedisplayLine(-1, xs, xe, 1); + LayRedisplayLine(y, xs, xe, 1); + bd.bd_refreshing = bd.bd_searching = 0; + BD_FORE->w_bd_y = oy; + } + return bd.bd_searchmax >= 0; +} + +static void +bd_normalize(x, y) +int x, y; +{ + if (x > BD_FORE->w_width - bd.bd_width) + x = BD_FORE->w_width - bd.bd_width; + if (x < 0) + x = 0; + if (y < 0) + { + bd_signal(); + y = 0; + } + if (y >= BD_FORE->w_height) + { + bd_signal(); + y = BD_FORE->w_height - 1; + } + if (x != BD_FORE->w_bd_x || y != BD_FORE->w_bd_y) + bd.bd_moved = 1; + BD_FORE->w_bd_x = x; + BD_FORE->w_bd_y = y; +} + +static void +bd_bc_left() +{ + int bx = BD_FORE->w_bd_x, by = BD_FORE->w_bd_y; + int ex; + + ex = bx - 1; + bx = 0; + for (; by >= 0; by--) + { + if (bd_do_search(by, 0, ex)) + { + if (!bd.bd_skip && by != BD_FORE->w_bd_y) + bd_signal(); + bx = bd.bd_searchmax + 1 - bd.bd_width; + break; + } + ex = BD_FORE->w_width - 1; + } + bd_normalize(bx, by); +} + +static void +bd_bc_right() +{ + int bx = BD_FORE->w_bd_x, by = BD_FORE->w_bd_y; + int sx; + + sx = bx + bd.bd_width; + bx = BD_FORE->w_width - bd.bd_width; + for (; by < BD_FORE->w_height; by++) + { + if (bd_do_search(by, sx, BD_FORE->w_width - 1)) + { + if (!bd.bd_skip && by != BD_FORE->w_bd_y) + bd_signal(); + bx = bd.bd_searchmin; + break; + } + sx = 0; + } + bd_normalize(bx, by); +} + +static void +bd_bc_up() +{ + int bx = BD_FORE->w_bd_x, by = BD_FORE->w_bd_y; + + for (by--; by >= 0; by--) + if (bd_do_search(by, bx, bx + bd.bd_width - 1)) + break; + bd_normalize(bx, by); +} + +static void +bd_bc_down() +{ + int bx = BD_FORE->w_bd_x, by = BD_FORE->w_bd_y; + + for (by++; by < BD_FORE->w_height; by++) + if (bd_do_search(by, bx, bx + bd.bd_width - 1)) + break; + bd_normalize(bx, by); +} + + +static void +bd_upper_left() +{ + bd_normalize(0, 0); +} + + +static void +bd_upper_right() +{ + bd_normalize(BD_FORE->w_width - bd.bd_width, 0); +} + + +static void +bd_lower_left() +{ + bd_normalize(0, BD_FORE->w_height - 1); +} + + +static void +bd_lower_right() +{ + bd_normalize(BD_FORE->w_width - bd.bd_width, BD_FORE->w_height -1); +} + +/********************************************************************** + * + */ + + +static void +bd_check(x, c) +int x, c; +{ + if (c == ' ') + return; + if (x < bd.bd_searchstart || x > bd.bd_searchend) + return; + if (x > bd.bd_searchmax) + bd.bd_searchmax = x; + if (x < bd.bd_searchmin) + bd.bd_searchmin = x; +} + + + +/*ARGSUSED*/ +void +BGotoPos(la, x, y) +struct layer *la; +int x, y; +{ +} + +/*ARGSUSED*/ +void +BCDisplayLine(la, ml, y, xs, xe, isblank) +struct layer *la; +struct mline *ml; +int y, xs, xe; +int isblank; +{ + int x; + int sx, ex; + char *l; + + if (y != BD_FORE->w_bd_y) + return; + if (bd.bd_searching) + { + for (x = xs; x <= xe; x++) + bd_check(x, ml->image[x]); + return; + } + l = bd.bd_line; + sx = BD_FORE->w_bd_x; + ex = sx + bd.bd_width - 1; + if (bd.bd_info & 1) + l += 4; + for (x = xs; x <= xe; x++) + if (x >= sx && x <= ex) + l[x - sx] = ml->image[x]; +} + +/*ARGSUSED*/ +void +BPutChar(la, c, x, y) +struct layer *la; +struct mchar *c; +int x, y; +{ + int sx, ex; + char *l; + + if (y != BD_FORE->w_bd_y) + return; + if (bd.bd_searching) + { + bd_check(x, c->image); + return; + } + l = bd.bd_line; + sx = BD_FORE->w_bd_x; + ex = sx + bd.bd_width - 1; + if (bd.bd_info & 1) + l += 4; + if (x >= sx && x <= ex) + l[x - sx] = c->image; +} + +/*ARGSUSED*/ +void +BPutStr(la, s, n, r, x, y) +struct layer *la; +char *s; +int n; +struct mchar *r; +int x, y; +{ + int sx, ex; + char *l; + + if (y != BD_FORE->w_bd_y) + return; + if (bd.bd_searching) + { + for (; n > 0; n--, s++, x++) + bd_check(x, *s); + return; + } + l = bd.bd_line; + sx = BD_FORE->w_bd_x; + ex = sx + bd.bd_width - 1; + if (bd.bd_info & 1) + l += 4; + for (; n > 0; n--, s++, x++) + if (x >= sx && x <= ex) + l[x - sx] = *s; +} + + + +/********************************************************************** + * + */ + +static char *infonames[] = {"none", "bc", "sc", "bc+sc"}; + +void +DoBrailleAction(act, msgok) +struct action *act; +int msgok; +{ + int nr, dosig; + int n, l, o; + char *s, **args; + struct stat st; + + nr = act->nr; + args = act->args; + dosig = display && !*rc_name; + + switch(nr) + { + case RC_BD_BELL: + if (ParseSwitch(act, &bd.bd_bell) || !msgok) + { + bd_signal(); + break; + } + Msg(0, bd.bd_bell ? "bd_bell is on." : "bd_bell is off."); + break; + + case RC_BD_EIGHTDOT: + if (ParseSwitch(act, &bd.bd_eightdot) || !msgok) + break; + Msg(0, "switched to %d-dots system.", bd.bd_eightdot ? 8 : 6); + break; + + case RC_BD_INFO: + n = bd.bd_info; + if (*args) + { + if (strlen(*args) == 4) + n = args[0][n] - '0'; + else if (ParseNum(act, &n)) + break; + } + if (n < 0 || n > 3) + { + Msg(0, "Out of range; 0 <= bd_info >= 3 "); + break; + } + /* bd_width at the beginning is unknown */ + if (bd.bd_width == 0) + break; + + o = (bd.bd_info * 2 + 2) & 12; + l = (n * 2 + 2) & 12; + if (l >= bd.bd_ncells) + { + Msg(0, "bd_info is too large for braille display."); + break; + } + if (l >= bd.bd_width + o) + { + Msg(0, "bd_info is too large for bd_width."); + break; + } + bd.bd_width += o - l; + bd.bd_info = n; + + if (msgok) + Msg(0, "bd_info is %s.", infonames[n]); + position_braille_cursor(); + break; + + case RC_BD_LINK: + if (*args == 0 && bd.bd_moved) + bd.bd_link = 0; + if (ParseSwitch(act, &bd.bd_link)) + break; + if (bd.bd_link) + { + bd.bd_moved = 0; + if (dosig) + bd_signal(); + position_braille_cursor(); + } + if (msgok) + Msg(0, bd.bd_link ? "bd_link is on." : "bd_link is off."); + break; + + case RC_BD_SKIP: + if (ParseSwitch(act, &bd.bd_skip)) + break; + if (bd.bd_skip && dosig) + bd_signal(); + if (msgok) + Msg(0, bd.bd_skip ? "bd_skip is on." : "bd_skip is off."); + break; + + case RC_BD_SCROLL: + if (ParseSwitch(act, &bd.bd_scroll) || !msgok) + { + position_braille_cursor(); + break; + } + Msg(0, bd.bd_scroll ? "bd_scroll is on." : "bd_scroll is off."); + break; + + case RC_BD_NCRC: + n = bd.bd_ncrc; + if (*args) + { + if (args[0][0] == '+') + n = (n + atoi(*args + 1)) % bd.bd_width + 1; + else if (args[0][0] == '-') + n = (n - atoi(*args + 1)) % bd.bd_width + 1; + else if (ParseNum(act, &n)) + break; + } + if (n < 1 || n > bd.bd_width) + { + Msg(0, "Out of range; 1 <= bd_ncrc >= %d", bd.bd_width); + break; + } + bd.bd_ncrc = n; + if (msgok) + Msg(0, "bd_ncrc status is: %d ", bd.bd_ncrc); + position_braille_cursor(); + break; + + case RC_BD_BRAILLE_TABLE: + s = 0; + if (*args) + { + if (ParseSaveStr(act, &s)) + break; + if (load_braille_table(s)) + { + free(s); + break; + } + if (bd.bd_braille_table) + free(bd.bd_braille_table); + bd.bd_braille_table = s; + } + if (msgok) + Msg(0, "bd_braille_table is: %s ", bd.bd_braille_table); + break; + + case RC_BD_PORT: + s = 0; + if (*args) + { + if (ParseSaveStr(act, &s)) + break; + + if (stat(s, &st) || !S_ISCHR(st.st_mode) || access(s, R_OK|W_OK)) + { + Msg(0, "Cannot access braille device port %s", s); + free(s); + break; + } + if (bd.bd_fd >= 0) + close(bd.bd_fd); + bd.bd_fd = -1; + if (bd.bd_port) + free(bd.bd_port); + bd.bd_port = s; + } + if (msgok) + Msg(0, "bd_port is: %s ", bd.bd_port ? bd.bd_port : "not set"); + StartBraille(); + break; + + case RC_BD_TYPE: + s = 0; + if (*args) + if (ParseSaveStr(act, &s) || initialize_braille_display_type(s)) + break; + if (msgok) + Msg(0, "bd_type is: %s ", bd.bd_type ? bd.bd_type : "not set"); + StartBraille(); + break; + + case RC_BD_START_BRAILLE: + if (ParseSwitch(act, &bd.bd_start_braille)) + break; + if (msgok) + Msg(0, bd.bd_start_braille ? "bd_start_braille is on." : "bd_start_braille is off."); + StartBraille(); + break; + + case RC_BD_WIDTH: + n = bd.bd_width; + if (*args) + { + if (ParseNum(act, &n)) + break; + } + if (n <= 0) + { + Msg(0, "Invalid value for bd_width: %d ", n); + break; + } + l = (bd.bd_info * 2 + 2) & 12; + if (n > bd.bd_ncells - l || n < l) + { + Msg(0, "bd_info is too large for bd_width."); + break; + } + bd.bd_width = n; + if (msgok) + Msg(0, "bd_width is: %d ", bd.bd_width); + break; + + case RC_BD_BC_LEFT: + bd_bc_left(); + break; + + case RC_BD_BC_RIGHT: + bd_bc_right(); + break; + + case RC_BD_BC_UP: + bd_bc_up(); + break; + + case RC_BD_BC_DOWN: + bd_bc_down(); + break; + + case RC_BD_UPPER_LEFT: + bd_upper_left(); + break; + + case RC_BD_UPPER_RIGHT: + bd_upper_right(); + break; + + case RC_BD_LOWER_LEFT: + bd_lower_left(); + break; + + case RC_BD_LOWER_RIGHT: + bd_lower_right(); + break; + + default: + break; + } +} + +static void +bd_readev_fn(ev, data) +struct event *ev; +char *data; +{ + bd.buttonpress(); +} + +static void +bd_writeev_fn(ev, data) +struct event *ev; +char *data; +{ + int len; + + if (bd.bd_obuflen == 0) + return; + if ((len = write(bd.bd_fd, bd.bd_obuf, bd.bd_obuflen)) < 0) + len = bd.bd_obuflen; /* dead braille display */ + if ((bd.bd_obuflen -= len)) + bcopy(bd.bd_obuf + len, bd.bd_obuf, bd.bd_obuflen); +} + +static void +bd_selectev_fn(ev, data) +struct event *ev; +char *data; +{ + RefreshBraille(); +} + +#endif /* HAVE_BRAILLE */ diff --git a/braille.h b/braille.h new file mode 100644 index 0000000..266ad41 --- /dev/null +++ b/braille.h @@ -0,0 +1,83 @@ +/* + * Authors: Hadi Bargi Rangin bargi@dots.physics.orst.edu + * Bill Barry barryb@dots.physics.orst.edu + * Randy Lundquist randyl@dots.physics.orst.edu + * + * Modifications Copyright (c) 1995 by + * Science Access Project, Oregon State University. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, see + * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + * $Id$ GNU + */ + +#ifdef HAVE_BRAILLE + +extern void StartBraille __P((void)); + +struct braille_display +{ + struct display *bd_dpy; /* display we are connected to */ + int bd_start_braille; /* screenrc var to request to turn braille on */ + int bd_using_braille; /* all is fine, use braille */ + struct event bd_readev; + struct event bd_writeev; + struct event bd_selectev; + int bd_fd; /* file descriptor */ + int bd_obuflen; /* current number of charactors in output buffer */ + char bd_obuf[IOSIZE]; + int bd_info; /* default, no info, 0,1,2,3 */ + int bd_ncrc; /* default 1, numbers of cells on the right side of real cursor, 1...bd_width */ + int bd_skip; /* default off, on/off */ + int bd_link; /* default, linked, on/off */ + int bd_width; /* length of braille display to use, <=bd_ncells */ + int bd_scroll; /* default on, scroll on/off */ + char *bd_braille_table; /* braille code */ + + int bd_bell; /* bell used for navigation on/off */ + int bd_ncells; /* real number of cells on braille display */ + int bd_eightdot; /* eightdot on/off */ + int bd_baud; /* communication baudrate between port and braille display */ + char *bd_port; /* serial port to use */ + char *bd_type; /* kind of braille display */ + double bd_version; /* rom version of braille display */ + char bd_btable[256]; /* braille translation table */ + + /* functions which communicate with braille displays */ + int (*write_line_braille) __P((char [],int, int)); + void (*buttonpress) __P((void)); + int (*bd_response_test) __P((void)); + + int bd_refreshing; /* are we doing a refresh? */ + char bd_line[40+1]; /* bd_ncells chars displayed on braille */ + int bd_cursorpos; /* cursor position on braille */ + char bd_oline[40+1]; /* bd_ncells chars displayed on braille */ + int bd_sx, bd_sy; /* screen cursor pos */ + int bd_moved; /* used braille move keys */ + + int bd_searching; /* are we seaching (bd_skip is on) */ + int bd_searchmax; /* search: max x */ + int bd_searchmin; /* search: min x */ + int bd_searchstart; + int bd_searchend; +}; + +extern struct braille_display bd; + +#define BD_FORE bd.bd_dpy->d_fore + +#endif diff --git a/braille_tsi.c b/braille_tsi.c new file mode 100644 index 0000000..8e05d75 --- /dev/null +++ b/braille_tsi.c @@ -0,0 +1,314 @@ +/* bd-tsi.c, TSI specific key bindings and display commands + * + * dotscreen + * A braille interface to unix tty terminals + * Authors: Hadi Bargi Rangin bargi@dots.physics.orst.edu + * Bill Barry barryb@dots.physics.orst.edu + * + * Copyright (c) 1995 by Science Access Project, Oregon State University. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, see + * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + */ + +#include "config.h" +#include "screen.h" +#include "extern.h" +#include "braille.h" + +#ifdef HAVE_BRAILLE + +extern struct display *display; + +struct key2rc { + int key; + int nr; + char *arg1; + char *arg2; +}; + +static int tsi_ctype = 1; /* cursor type, 0,1,2 */ + +static int tsi_line_type; /* indicates number of cells on powerbraille + display 01=20 cells 02=40 cells 03=80 cells */ + +static int display_status_tsi __P((void)); +static int write_line_tsi __P((char*, int, int)); +static void buttonpress_tsi __P((structkey2rc*)); +static void buttonpress_navigator_40 __P((void)); +static void buttonpress_powerbraille_40 __P((void)); +static void buttonpress_powerbraille_80 __P((void)); + +int +bd_init_powerbraille_40() +{ + bd.write_line_braille = write_line_tsi; + bd.buttonpress = buttonpress_powerbraille_40; + bd.bd_response_test = display_status_tsi; + bd.bd_ncells = 40; + tsi_line_type = 2; + return 0; +} + +int +bd_init_powerbraille_80() +{ + bd.write_line_braille = write_line_tsi; + bd.buttonpress = buttonpress_powerbraille_80; + bd.bd_response_test = display_status_tsi; + bd.bd_ncells = 80; + tsi_line_type = 3; + return 0; +} + +int +bd_init_navigator_40() +{ + bd.write_line_braille = write_line_tsi; + bd.buttonpress = buttonpress_navigator_40; + bd.bd_response_test = display_status_tsi; + bd.bd_ncells = 40; + tsi_line_type = 2; + return 0; +} + +static int +display_status_tsi() +{ + char obuf[3], ibuf[20]; + int r; + + obuf[0] = 0xff; + obuf[1] = 0xff; + obuf[2] = 0x0a; + r = read(bd.bd_fd, ibuf, 20); /* flush the input port */ + r = write(bd.bd_fd, obuf, 3); + if (r != 3) + return -1; + + /* we have written to the display asking for a response + we wait 1 second for the response, read it and if no + response we wait 2 seconds, if still no response, + return -1 to indicate no braille display available */ + + sleep(1); + r = read(bd.bd_fd, ibuf, 2); + if (r == -1) { + sleep(2); + r = read(bd.bd_fd, ibuf, 2); + } + + debug2("first chars from braille display %d %d\n", ibuf[0], ibuf[1]); + if (r != 2 || ibuf[0] != 0 || ibuf[1] != 5) + return -1; + + r = read(bd.bd_fd, ibuf, 2); + if (r != 2) + return -1; + + debug2("braille display size:%d dots:%d\n", ibuf[0], ibuf[1]); + bd.bd_ncells = (unsigned char)ibuf[0]; + if (bd.bd_ncells <= 1) + return -1; + + r = read(bd.bd_fd, ibuf, 1); + if (r != 1) + return -1; + + if (ibuf[0] == 'V') + r = read(bd.bd_fd, ibuf, 3); + else + r = read(bd.bd_fd, ibuf + 1, 2) + 1; + + if (r != 3) + return -1; + + ibuf[3] = 0; + debug1("braille display version %s\n", ibuf); + bd.bd_version = atof(ibuf); + return 0; +} + +static int +write_line_tsi(char *bstr, int line_length, int cursor_pos) +{ + int obp, i; + bd.bd_obuf[0] = 0xff; + bd.bd_obuf[1] = 0xff; + bd.bd_obuf[2] = tsi_line_type; + bd.bd_obuf[3] = 0x07; + bd.bd_obuf[4] = cursor_pos; + bd.bd_obuf[5] = tsi_ctype; + obp = 6; + + for (i = 0; i < line_length; i++) { + bd.bd_obuf[2*i + obp] = 0; + bd.bd_obuf[2*i + 1 + obp] = bd.bd_btable[(int)(unsigned char)bstr[i]]; + } + for (i = line_length; i < bd.bd_ncells; i++) { + bd.bd_obuf[2*i + obp] = 0; + bd.bd_obuf[2*i + 1 + obp] = bd.bd_btable[(int)' ']; + } + + bd.bd_obuflen = 2*bd.bd_ncells + obp; + return 0; +} + +static struct key2rc keys_navigator_40[] = { + {0x4000000, RC_STUFF, "-k", "kl"}, /* 1 */ + {0x10000000, RC_STUFF, "-k", "kr"}, /* 3 */ + {0x8000000, RC_STUFF, "-k", "ku"}, /* 2 */ + {0x20000000, RC_STUFF, "-k", "kd"}, /* 4 */ + {0x2000, RC_BD_BC_LEFT, 0, 0}, /* 6 */ + {0x8000, RC_BD_BC_RIGHT, 0, 0}, /* 8 */ + {0x4000, RC_BD_BC_UP, 0, 0}, /* 7 */ + {0x10000, RC_BD_BC_DOWN, 0, 0}, /* 9 */ + {0x6000, RC_BD_UPPER_LEFT, 0, 0}, /* 6, 7 */ + {0xc000, RC_BD_UPPER_RIGHT, 0, 0}, /* 7, 8 */ + {0x12000, RC_BD_LOWER_LEFT, 0, 0}, /* 6, 9 */ + {0x18000, RC_BD_LOWER_RIGHT, 0, 0}, /* 8, 9 */ + {0xa000, RC_BD_INFO, "1032", 0}, /* bc 6, 8 */ + {0x14000000, RC_BD_INFO, "2301", 0}, /* sc 1, 3 */ + {0x4008000, RC_BD_INFO, "3330", 0}, /* bc+sc 1, 8 */ + {0x8010000, RC_BD_BELL, 0, 0}, /* 2, 9 */ + {0x8004000, RC_BD_EIGHTDOT, 0, 0}, /* 2, 7 */ + {0x40000000, RC_STUFF, "\015", 0}, /* 5 */ + {0x20000, RC_BD_LINK, 0, 0}, /* 10 */ + {0x10002000, RC_BD_SCROLL, 0, 0}, /* 3, 6 */ + {0x20010000, RC_BD_NCRC, "+", 0}, /* 4, 9 */ + {0x14000, RC_BD_SKIP, 0, 0}, /* 7, 9*/ + {-1, RC_ILLEGAL, 0, 0} +}; + +static struct key2rc keys_powerbraille_40[] = { + {0x4000000, RC_STUFF, "-k", "kl"}, /* 1 */ + {0x10000000, RC_STUFF, "-k", "kr"}, /* 3 */ + {0x8000000, RC_STUFF, "-k", "ku"}, /* 2 */ + {0x20000000, RC_STUFF, "-k", "kd"}, /* 4 */ + {0x2000, RC_BD_BC_LEFT, 0, 0}, /* 6 */ + {0x8000, RC_BD_BC_RIGHT, 0, 0}, /* 8 */ + {0x4000, RC_BD_BC_UP, 0, 0}, /* 7 */ + {0x10000, RC_BD_BC_DOWN, 0, 0}, /* 9 */ + {0x8002000, RC_BD_UPPER_LEFT, 0, 0}, /* 2, 6 */ + {0xc000, RC_BD_UPPER_RIGHT, 0, 0}, /* 7, 8 */ + {0x20002000, RC_BD_LOWER_LEFT, 0, 0}, /* 3, 6 */ + {0x18000, RC_BD_LOWER_RIGHT, 0, 0}, /* 8, 9 */ + {0x8008000, RC_BD_INFO, "1032", 0}, /* bc 2, 8 */ + {0x6000, RC_BD_INFO, "2301", 0}, /* 6, 7 */ + {0x8004000, RC_BD_INFO, "3330", 0}, /* bc+sc 2, 7 */ + {0x8010000, RC_BD_BELL, 0, 0}, /* 2, 9 */ + {0x20008000, RC_BD_EIGHTDOT, 0, 0}, /* 4, 6 */ + {0x40000000, RC_STUFF, "\015", 0}, /* 5 */ + {0x20000, RC_BD_LINK, 0, 0}, /* 10 */ + {0xa000, RC_BD_SCROLL, 0, 0}, /* 6, 8 */ + {0x20010000, RC_BD_NCRC, "+", 0}, /* 4, 9 */ + {0x20004000, RC_BD_SKIP, 0, 0}, /* 4, 7 */ + {-1, RC_ILLEGAL, 0, 0} +}; + +static struct key2rc keys_powerbraille_80[] = { + {0x4000000, RC_STUFF, "-k", "kl"}, /* 1 */ + {0x10000000, RC_STUFF, "-k", "kr"}, /* 3 */ + {0x8000000, RC_STUFF, "-k", "ku"}, /* 2 */ + {0x20000000, RC_STUFF, "-k", "kd"}, /* 4 */ + {0x40000, RC_BD_BC_LEFT, 0, 0}, /* 6 */ + {0x100000, RC_BD_BC_RIGHT, 0, 0}, /* 8 */ + {0x4000, RC_BD_BC_UP, 0, 0}, /* 7 */ + {0x10000, RC_BD_BC_DOWN, 0, 0}, /* 9 */ + {0x44000, RC_BD_UPPER_LEFT, 0, 0}, /* 6, 7 */ + {0x104000, RC_BD_UPPER_RIGHT, 0, 0}, /* 7, 8 */ + {0x50000, RC_BD_LOWER_LEFT, 0, 0}, /* 6, 9 */ + {0x110000, RC_BD_LOWER_RIGHT, 0, 0}, /* 8, 9 */ + {0x8100000, RC_BD_INFO, "1032", 0}, /* 2, 8 */ + {0x8040000, RC_BD_INFO, "2301", 0}, /* 2, 6 */ + {0x140000, RC_BD_INFO, "3330", 0}, /* 6, 8 */ + {0x8010000, RC_BD_BELL, 0, 0}, /* 2, 9 */ + {0x8004000, RC_BD_EIGHTDOT, 0, 0}, /* 2, 7 */ + {0x40000000, RC_STUFF, "\015", 0}, /* 5 */ + {0x20000, RC_BD_LINK, 0, 0}, /* 10 */ + {0x20004000, RC_BD_SCROLL, 0, 0}, /* 4, 7 */ + {0x20010000, RC_BD_NCRC, "+", 0}, /* 4, 9 */ + {0x40010000, RC_BD_SKIP, 0, 0}, /* 5, 9 */ + {-1, RC_ILLEGAL, 0, 0} +}; + +static void +buttonpress_tsi(struct key2rc *tab) +{ + int i, nb; + int bkeys; + unsigned char buf[10]; + nb = read(bd.bd_fd, buf, 10); + debug1("buttonpress_tsi: read %d bytes\n", nb); + for (i = 0, bkeys = 0; i < nb; i++) { + switch (buf[i] & 0xE0) { + case 0x00: bkeys += ((int)(buf[i] & 0x1f)); break; + case 0x20: bkeys += ((int)(buf[i] & 0x1f) << 5); break; + case 0x40: bkeys += ((int)(buf[i] & 0x1f) << 9); break; + case 0x60: bkeys += ((int)(buf[i] & 0x1f) << 13); break; + case 0xA0: bkeys += ((int)(buf[i] & 0x1f) << 18); break; + case 0xC0: bkeys += ((int)(buf[i] & 0x1f) << 22); break; + case 0xE0: bkeys += ((int)(buf[i] & 0x1f) << 26); break; + default: break; + } + } + debug1("bkeys %x\n", bkeys); + + for (i = 0; tab[i].key != -1; i++) + if (bkeys == tab[i].key) + break; + + debug1("bkey index %d\n", i); + + if (tab[i].key != -1 && tab[i].nr != RC_ILLEGAL) { + char *args[3]; + int argl[2]; + + struct action act; + args[0] = tab[i].arg1; + args[1] = tab[i].arg2; + args[2] = 0; + argl[0] = args[0] ? strlen(args[0]) : 0; + argl[1] = args[1] ? strlen(args[1]) : 0; + act.nr = tab[i].nr; + act.args = args; + act.argl = argl; + display = bd.bd_dpy; + DoAction(&act, -2); + } +} + +static void +buttonpress_navigator_40() +{ + buttonpress_tsi(keys_navigator_40); +} + +static void +buttonpress_powerbraille_40() +{ + buttonpress_tsi(keys_powerbraille_40); +} + +static void +buttonpress_powerbraille_80() +{ + buttonpress_tsi(keys_powerbraille_80); +} + +#endif /* HAVE_BRAILLE */ diff --git a/canvas.c b/canvas.c new file mode 100644 index 0000000..919e94d --- /dev/null +++ b/canvas.c @@ -0,0 +1,918 @@ +/* Copyright (c) 2008, 2009 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Micah Cowan (micah@cowan.name) + * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) + * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, see + * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + */ + +#include "config.h" +#include "screen.h" +#include "extern.h" +#include "canvas.h" +#include "list_generic.h" + +extern struct display *display; +extern struct win *fore, *windows; +extern struct layer *flayer; +extern int captionalways; +extern struct LayFuncs BlankLf; +extern int focusminwidth, focusminheight; + +static void +CanvasInitBlank(cv) +struct canvas *cv; +{ + cv->c_blank.l_cvlist = cv; + cv->c_blank.l_width = cv->c_xe - cv->c_xs + 1; + cv->c_blank.l_height = cv->c_ye - cv->c_ys + 1; + cv->c_blank.l_x = cv->c_blank.l_y = 0; + cv->c_blank.l_layfn = &BlankLf; + cv->c_blank.l_data = 0; + cv->c_blank.l_next = 0; + cv->c_blank.l_bottom = &cv->c_blank; + cv->c_blank.l_blocking = 0; + cv->c_layer = &cv->c_blank; +} + +static void +FreePerp(pcv) +struct canvas *pcv; +{ + struct canvas *cv; + + if (!pcv->c_slperp) + return; + cv = pcv->c_slperp; + cv->c_slprev = pcv->c_slprev; + if (cv->c_slprev) + cv->c_slprev->c_slnext = cv; + cv->c_slback = pcv->c_slback; + if (cv->c_slback && cv->c_slback->c_slperp == pcv) + cv->c_slback->c_slperp = cv; + cv->c_slorient = pcv->c_slorient; + cv->c_slweight = pcv->c_slweight; + while (cv->c_slnext) + { + cv = cv->c_slnext; + cv->c_slorient = pcv->c_slorient; + cv->c_slback = pcv->c_slback; + cv->c_slweight = pcv->c_slweight; + } + cv->c_slnext = pcv->c_slnext; + if (cv->c_slnext) + cv->c_slnext->c_slprev = cv; + LayerCleanupMemory(&pcv->c_blank); + free(pcv); +} + +void +FreeCanvas(cv) +struct canvas *cv; +{ + struct viewport *vp, *nvp; + struct canvas **cvp; + struct win *p; + + if (cv->c_slprev) + cv->c_slprev->c_slnext = cv->c_slnext; + if (cv->c_slnext) + cv->c_slnext->c_slprev = cv->c_slprev; + if (cv->c_slback && cv->c_slback->c_slperp == cv) + cv->c_slback->c_slperp = cv->c_slnext ? cv->c_slnext : cv->c_slprev; + if (cv->c_slperp) + { + while (cv->c_slperp) + FreeCanvas(cv->c_slperp); + LayerCleanupMemory(&cv->c_blank); + free(cv); + return; + } + + if (display) + { + if (D_forecv == cv) + D_forecv = 0; + /* remove from canvas chain as SetCanvasWindow might call + * some layer function */ + for (cvp = &D_cvlist; *cvp ; cvp = &(*cvp)->c_next) + if (*cvp == cv) + { + *cvp = cv->c_next; + break; + } + } + p = cv->c_layer ? Layer2Window(cv->c_layer) : 0; + SetCanvasWindow(cv, 0); + if (p) + WindowChanged(p, 'u'); + if (flayer == cv->c_layer) + flayer = 0; + for (vp = cv->c_vplist; vp; vp = nvp) + { + vp->v_canvas = 0; + nvp = vp->v_next; + vp->v_next = 0; + free(vp); + } + evdeq(&cv->c_captev); + LayerCleanupMemory(&cv->c_blank); + free(cv); +} + +int +CountCanvas(cv) +struct canvas *cv; +{ + int num = 0; + for (; cv; cv = cv->c_slnext) + { + if (cv->c_slperp) + { + struct canvas *cvp; + int nump = 1, n; + for (cvp = cv->c_slperp; cvp; cvp = cvp->c_slnext) + if (cvp->c_slperp) + { + n = CountCanvas(cvp->c_slperp); + if (n > nump) + nump = n; + } + num += nump; + } + else + num++; + } + return num; +} + +int +CountCanvasPerp(cv) +struct canvas *cv; +{ + struct canvas *cvp; + int num = 1, n; + for (cvp = cv->c_slperp; cvp; cvp = cvp->c_slnext) + if (cvp->c_slperp) + { + n = CountCanvas(cvp->c_slperp); + if (n > num) + num = n; + } + return num; +} + +struct canvas * +FindCanvas(x, y) +int x, y; +{ + struct canvas *cv, *mcv = 0; + int m, mm = 0; + + for (cv = D_cvlist; cv; cv = cv->c_next) + { + /* ye + 1 because of caption line */ + if (x >= cv->c_xs && x <= cv->c_xe && y >= cv->c_ys && y <= cv->c_ye + 1) + return cv; + if (cv == D_forecv) + continue; + m = 0; + if (x >= D_forecv->c_xs && x <= D_forecv->c_xe) + { + if (x < cv->c_xs || x > cv->c_xe) + continue; + if (y < D_forecv->c_ys && y < cv->c_ys) + continue; + if (y > D_forecv->c_ye + 1 && y > cv->c_ye + 1) + continue; + if (y < cv->c_ys) + m = cv->c_ys - y; + if (y > cv->c_ye + 1) + m = y - (cv->c_ye + 1); + } + if (y >= D_forecv->c_ys && y <= D_forecv->c_ye + 1) + { + if (y < cv->c_ys || y > cv->c_ye + 1) + continue; + if (x < D_forecv->c_xs && x < cv->c_xs) + continue; + if (x > D_forecv->c_xe && x > cv->c_xe) + continue; + if (x < cv->c_xs) + m = cv->c_xs - x; + if (x > cv->c_xe) + m = x - cv->c_xe; + } + if (m && (!mm || m < mm)) + { + mcv = cv; + mm = m; + } + } + return mcv ? mcv : D_forecv; +} + +void +SetCanvasWindow(cv, wi) +struct canvas *cv; +struct win *wi; +{ + struct win *p = 0, **pp; + struct layer *l; + struct canvas *cvp, **cvpp; + + l = cv->c_layer; + display = cv->c_display; + + if (l) + { + /* remove old layer */ + for (cvpp = &l->l_cvlist; (cvp = *cvpp); cvpp = &cvp->c_lnext) + if (cvp == cv) + break; + ASSERT(cvp); + *cvpp = cvp->c_lnext; + + p = Layer2Window(l); + l = cv->c_layer; + cv->c_layer = 0; + + if (p && cv == D_forecv) + { +#ifdef MULTIUSER + ReleaseAutoWritelock(display, p); +#endif + if (p->w_silence) + { + SetTimeout(&p->w_silenceev, p->w_silencewait * 1000); + evenq(&p->w_silenceev); + } + D_other = fore; + D_fore = 0; + } + if (l->l_cvlist == 0 && (p == 0 || l != p->w_savelayer)) + KillLayerChain(l); + } + + /* find right layer to display on canvas */ + if (wi && wi->w_type != W_TYPE_GROUP) + { + l = &wi->w_layer; + if (wi->w_savelayer && (wi->w_blocked || wi->w_savelayer->l_cvlist == 0)) + l = wi->w_savelayer; + } + else + { + l = &cv->c_blank; + if (wi) + l->l_data = (char *)wi; + else + l->l_data = 0; + } + + /* add our canvas to the layer's canvaslist */ + ASSERT(l->l_cvlist != cv); + cv->c_lnext = l->l_cvlist; + l->l_cvlist = cv; + cv->c_layer = l; + cv->c_xoff = cv->c_xs; + cv->c_yoff = cv->c_ys; + RethinkViewportOffsets(cv); + + if (flayer == 0) + flayer = l; + + if (wi && wi->w_type == W_TYPE_GROUP) + { + /* auto-start windowlist on groups */ + struct display *d = display; + struct layer *oldflayer = flayer; + flayer = l; + display_windows(0, 0, wi); + flayer = oldflayer; + display = d; + } + + if (wi && D_other == wi) + D_other = wi->w_next; /* Might be 0, but that's OK. */ + if (cv == D_forecv) + { + D_fore = wi; + fore = D_fore; /* XXX ? */ + if (wi) + { +#ifdef MULTIUSER + ObtainAutoWritelock(display, wi); +#endif + /* + * Place the window at the head of the most-recently-used list + */ + if (windows != wi) + { + for (pp = &windows; (p = *pp); pp = &p->w_next) + if (p == wi) + break; + ASSERT(p); + *pp = p->w_next; + p->w_next = windows; + windows = p; + WListLinkChanged(); + } + } + } +} + +static void +cv_winid_fn(ev, data) +struct event *ev; +char *data; +{ + int ox, oy; + struct canvas *cv = (struct canvas *)data; + + display = cv->c_display; + if (D_status == STATUS_ON_WIN) + { + SetTimeout(ev, 1); + evenq(ev); + return; + } + ox = D_x; + oy = D_y; + if (cv->c_ye + 1 < D_height) + RefreshLine(cv->c_ye + 1, 0, D_width - 1, 0); + if (ox != -1 && oy != -1) + GotoPos(ox, oy); +} + +int +MakeDefaultCanvas() +{ + struct canvas *cv; + + ASSERT(display); + if ((cv = (struct canvas *)calloc(1, sizeof *cv)) == 0) + return -1; + cv->c_xs = 0; + cv->c_xe = D_width - 1; + cv->c_ys = (D_has_hstatus == HSTATUS_FIRSTLINE); + cv->c_ye = D_height - 1 - (D_has_hstatus == HSTATUS_LASTLINE) - captionalways; + debug2("MakeDefaultCanvas 0,0 %d,%d\n", cv->c_xe, cv->c_ye); + cv->c_xoff = 0; + cv->c_yoff = 0; + cv->c_next = 0; + cv->c_display = display; + cv->c_vplist = 0; + cv->c_slnext = 0; + cv->c_slprev = 0; + cv->c_slperp = 0; + cv->c_slweight = 1; + cv->c_slback = &D_canvas; + D_canvas.c_slperp = cv; + D_canvas.c_xs = cv->c_xs; + D_canvas.c_xe = cv->c_xe; + D_canvas.c_ys = cv->c_ys; + D_canvas.c_ye = cv->c_ye; + cv->c_slorient = SLICE_UNKN; + cv->c_captev.type = EV_TIMEOUT; + cv->c_captev.data = (char *)cv; + cv->c_captev.handler = cv_winid_fn; + + CanvasInitBlank(cv); + cv->c_lnext = 0; + + D_cvlist = cv; + RethinkDisplayViewports(); + D_forecv = cv; /* default input focus */ + return 0; +} + +static struct canvas ** +CreateCanvasChainRec(cv, cvp) +struct canvas *cv; +struct canvas **cvp; +{ + for (; cv; cv = cv->c_slnext) + { + if (cv->c_slperp) + cvp = CreateCanvasChainRec(cv->c_slperp, cvp); + else + { + *cvp = cv; + cvp = &cv->c_next; + } + } + return cvp; +} + +void +RecreateCanvasChain() +{ + struct canvas **cvp; + cvp = CreateCanvasChainRec(D_canvas.c_slperp, &D_cvlist); + *cvp = 0; +} + +void +EqualizeCanvas(cv, gflag) +struct canvas *cv; +int gflag; +{ + struct canvas *cv2; + for (; cv; cv = cv->c_slnext) + { + if (cv->c_slperp && gflag) + { + cv->c_slweight = CountCanvasPerp(cv); + for (cv2 = cv->c_slperp; cv2; cv2 = cv2->c_slnext) + if (cv2->c_slperp) + EqualizeCanvas(cv2->c_slperp, gflag); + } + else + cv->c_slweight = 1; + } +} + +void +ResizeCanvas(cv) +struct canvas *cv; +{ + struct canvas *cv2, *cvn, *fcv; + int nh, i, maxi, hh, m, w, wsum; + int need, got; + int xs, ys, xe, ye; + int focusmin = 0; + + xs = cv->c_xs; + ys = cv->c_ys; + xe = cv->c_xe; + ye = cv->c_ye; + cv = cv->c_slperp; + debug2("ResizeCanvas: %d,%d", xs, ys); + debug2(" %d,%d\n", xe, ye); + if (cv == 0) + return; + if (cv->c_slorient == SLICE_UNKN) + { + ASSERT(!cv->c_slnext && !cv->c_slperp); + cv->c_xs = xs; + cv->c_xe = xe; + cv->c_ys = ys; + cv->c_ye = ye; + cv->c_xoff = cv->c_xs; + cv->c_yoff = cv->c_ys; + cv->c_blank.l_width = cv->c_xe - cv->c_xs + 1; + cv->c_blank.l_height = cv->c_ye - cv->c_ys + 1; + return; + } + + fcv = 0; + if (focusminwidth || focusminheight) + { + debug("searching for focus canvas\n"); + cv2 = D_forecv; + while (cv2->c_slback) + { + if (cv2->c_slback == cv->c_slback) + { + fcv = cv2; + focusmin = cv->c_slorient == SLICE_VERT ? focusminheight : focusminwidth; + if (focusmin > 0) + focusmin--; + else if (focusmin < 0) + focusmin = cv->c_slorient == SLICE_VERT ? ye - ys + 2 : xe - xs + 2; + debug1("found, focusmin=%d\n", focusmin); + } + cv2 = cv2->c_slback; + } + } + if (focusmin) + { + m = CountCanvas(cv) * 2; + nh = cv->c_slorient == SLICE_VERT ? ye - ys + 2 : xe - xs + 2; + nh -= m; + if (nh < 0) + nh = 0; + if (focusmin > nh) + focusmin = nh; + debug1("corrected to %d\n", focusmin); + } + + /* pass 1: calculate weight sum */ + for (cv2 = cv, wsum = 0; cv2; cv2 = cv2->c_slnext) + { + debug1(" weight %d\n", cv2->c_slweight); + wsum += cv2->c_slweight; + } + debug1("wsum = %d\n", wsum); + if (wsum == 0) + wsum = 1; + w = wsum; + + /* pass 2: calculate need/excess space */ + nh = cv->c_slorient == SLICE_VERT ? ye - ys + 2 : xe - xs + 2; + for (cv2 = cv, need = got = 0; cv2; cv2 = cv2->c_slnext) + { + m = cv2->c_slperp ? CountCanvasPerp(cv2) * 2 - 1 : 1; + if (cv2 == fcv) + m += focusmin; + hh = cv2->c_slweight ? nh * cv2->c_slweight / w : 0; + w -= cv2->c_slweight; + nh -= hh; + debug2(" should %d min %d\n", hh, m); + if (hh <= m + 1) + need += m + 1 - hh; + else + got += hh - m - 1; + } + debug2("need: %d, got %d\n", need, got); + if (need > got) + need = got; + + /* pass 3: distribute space */ + nh = cv->c_slorient == SLICE_VERT ? ye - ys + 2 : xe - xs + 2; + i = cv->c_slorient == SLICE_VERT ? ys : xs; + maxi = cv->c_slorient == SLICE_VERT ? ye : xe; + w = wsum; + for (; cv; cv = cvn) + { + cvn = cv->c_slnext; + if (i > maxi) + { + if (cv->c_slprev && !cv->c_slback->c_slback && !cv->c_slprev->c_slperp && !cv->c_slprev->c_slprev) + { + cv->c_slprev->c_slorient = SLICE_UNKN; + if (!captionalways) + { + cv->c_slback->c_ye++; + cv->c_slprev->c_ye++; + } + } + SetCanvasWindow(cv, 0); + FreeCanvas(cv); + continue; + } + m = cv->c_slperp ? CountCanvasPerp(cv) * 2 - 1 : 1; + if (cv == fcv) + m += focusmin; + hh = cv->c_slweight ? nh * cv->c_slweight / w : 0; + w -= cv->c_slweight; + nh -= hh; + debug2(" should %d min %d\n", hh, m); + if (hh <= m + 1) + { + hh = m + 1; + debug1(" -> %d\n", hh); + } + else + { + int hx = 1; //FIXME Division by zero (got) is posible. "hx = 1" is random number here!!! + if (got != 0) + hx = need * (hh - m - 1) / got; + else + debug(" got = 0\n"); + + debug3(" -> %d - %d = %d\n", hh, hx, hh - hx); + got -= (hh - m - 1); + hh -= hx; + need -= hx; + debug2(" now need=%d got=%d\n", need, got); + } + ASSERT(hh >= m + 1); + /* hh is window size plus pation line */ + if (i + hh > maxi + 2) + { + hh = maxi + 2 - i; + debug1(" not enough space, reducing to %d\n", hh); + } + if (i + hh == maxi + 1) + { + hh++; + debug(" incrementing as no other canvas will fit\n"); + } + if (cv->c_slorient == SLICE_VERT) + { + cv->c_xs = xs; + cv->c_xe = xe; + cv->c_ys = i; + cv->c_ye = i + hh - 2; + cv->c_xoff = xs; + cv->c_yoff = i; + } + else + { + cv->c_xs = i; + cv->c_xe = i + hh - 2; + cv->c_ys = ys; + cv->c_ye = ye; + cv->c_xoff = i; + cv->c_yoff = ys; + } + cv->c_xoff = cv->c_xs; + cv->c_yoff = cv->c_ys; + cv->c_blank.l_width = cv->c_xe - cv->c_xs + 1; + cv->c_blank.l_height = cv->c_ye - cv->c_ys + 1; + if (cv->c_slperp) + { + ResizeCanvas(cv); + if (!cv->c_slperp->c_slnext) + { + debug("deleting perp node\n"); + FreePerp(cv->c_slperp); + FreePerp(cv); + } + } + i += hh; + } +} + +static struct canvas * +AddPerp(cv) +struct canvas *cv; +{ + struct canvas *pcv; + debug("Creating new perp node\n"); + + if ((pcv = (struct canvas *)calloc(1, sizeof *cv)) == 0) + return 0; + pcv->c_next = 0; + pcv->c_display = cv->c_display; + pcv->c_slnext = cv->c_slnext; + pcv->c_slprev = cv->c_slprev; + pcv->c_slperp = cv; + pcv->c_slback = cv->c_slback; + if (cv->c_slback && cv->c_slback->c_slperp == cv) + cv->c_slback->c_slperp = pcv; + pcv->c_slorient = cv->c_slorient; + pcv->c_xoff = 0; + pcv->c_yoff = 0; + pcv->c_xs = cv->c_xs; + pcv->c_xe = cv->c_xe; + pcv->c_ys = cv->c_ys; + pcv->c_ye = cv->c_ye; + if (pcv->c_slnext) + pcv->c_slnext->c_slprev = pcv; + if (pcv->c_slprev) + pcv->c_slprev->c_slnext = pcv; + pcv->c_slweight = cv->c_slweight; + CanvasInitBlank(pcv); + cv->c_slweight = 1; + cv->c_slnext = 0; + cv->c_slprev = 0; + cv->c_slperp = 0; + cv->c_slback = pcv; + cv->c_slorient = SLICE_UNKN; + return pcv; +} + +int +AddCanvas(orient) +int orient; +{ + struct canvas *cv; + int xs, xe, ys, ye; + int h, num; + + cv = D_forecv; + debug2("AddCanvas orient %d, forecv is %d\n", orient, cv->c_slorient); + + if (cv->c_slorient != SLICE_UNKN && cv->c_slorient != orient) + if (!AddPerp(cv)) + return -1; + + cv = D_forecv; + xs = cv->c_slback->c_xs; + xe = cv->c_slback->c_xe; + ys = cv->c_slback->c_ys; + ye = cv->c_slback->c_ye; + if (!captionalways && cv == D_canvas.c_slperp && !cv->c_slnext) + ye--; /* need space for caption */ + debug2("Adding Canvas to slice %d,%d ", xs, ys); + debug2("%d,%d\n", xe, ye); + + num = CountCanvas(cv->c_slback->c_slperp) + 1; + debug1("Num = %d\n", num); + if (orient == SLICE_VERT) + h = ye - ys + 1; + else + h = xe - xs + 1; + + h -= 2 * num - 1; + if (h < 0) + return -1; /* can't fit in */ + + if ((cv = (struct canvas *)calloc(1, sizeof *cv)) == 0) + return -1; + + D_forecv->c_slback->c_ye = ye; /* in case we modified it above */ + D_forecv->c_slorient = orient; /* in case it was UNKN */ + cv->c_slnext = D_forecv->c_slnext; + cv->c_slprev = D_forecv; + D_forecv->c_slnext = cv; + if (cv->c_slnext) + cv->c_slnext->c_slprev = cv; + cv->c_slorient = orient; + cv->c_slback = D_forecv->c_slback; + + cv->c_xs = xs; + cv->c_xe = xe; + cv->c_ys = ys; + cv->c_ye = ye; + cv->c_xoff = 0; + cv->c_yoff = 0; + cv->c_display = display; + cv->c_vplist = 0; + cv->c_captev.type = EV_TIMEOUT; + cv->c_captev.data = (char *)cv; + cv->c_captev.handler = cv_winid_fn; + + CanvasInitBlank(cv); + cv->c_lnext = 0; + + cv->c_next = 0; + + cv = cv->c_slback; + EqualizeCanvas(cv->c_slperp, 0); + ResizeCanvas(cv); + RecreateCanvasChain(); + RethinkDisplayViewports(); + ResizeLayersToCanvases(); + return 0; +} + +void +RemCanvas() +{ + int ye; + struct canvas *cv; + + debug("RemCanvas\n"); + cv = D_forecv; + ye = cv->c_slback->c_ye; + if (cv->c_slorient == SLICE_UNKN) + return; + while (cv->c_slprev) + cv = cv->c_slprev; + if (!cv->c_slnext) + return; + if (!cv->c_slnext->c_slnext && cv->c_slback->c_slback) + { + /* two canvases in slice, kill perp node */ + cv = D_forecv; + debug("deleting perp node\n"); + FreePerp(cv->c_slprev ? cv->c_slprev : cv->c_slnext); + FreePerp(cv->c_slback); + } + /* free canvas */ + cv = D_forecv; + D_forecv = cv->c_slprev; + if (!D_forecv) + D_forecv = cv->c_slnext; + FreeCanvas(cv); + + cv = D_forecv; + while (D_forecv->c_slperp) + D_forecv = D_forecv->c_slperp; + + /* if only one canvas left, set orient back to unknown */ + if (!cv->c_slnext && !cv->c_slprev && !cv->c_slback->c_slback && !cv->c_slperp) + { + cv->c_slorient = SLICE_UNKN; + if (!captionalways) + cv->c_slback->c_ye = ++ye; /* caption line no longer needed */ + } + cv = cv->c_slback; + EqualizeCanvas(cv->c_slperp, 0); + ResizeCanvas(cv); + + D_fore = Layer2Window(D_forecv->c_layer); + flayer = D_forecv->c_layer; + + RecreateCanvasChain(); + RethinkDisplayViewports(); + ResizeLayersToCanvases(); +} + +void +OneCanvas() +{ + struct canvas *cv = D_forecv, *ocv = 0; + + if (cv->c_slprev) + { + ocv = cv->c_slprev; + cv->c_slprev->c_slnext = cv->c_slnext; + } + if (cv->c_slnext) + { + ocv = cv->c_slnext; + cv->c_slnext->c_slprev = cv->c_slprev; + } + if (!ocv) + return; + if (cv->c_slback && cv->c_slback->c_slperp == cv) + cv->c_slback->c_slperp = ocv; + cv->c_slorient = SLICE_UNKN; + while (D_canvas.c_slperp) + FreeCanvas(D_canvas.c_slperp); + cv = D_forecv; + D_canvas.c_slperp = cv; + cv->c_slback = &D_canvas; + cv->c_slnext = 0; + cv->c_slprev = 0; + ASSERT(!cv->c_slperp); + if (!captionalways) + D_canvas.c_ye++; /* caption line no longer needed */ + ResizeCanvas(&D_canvas); + RecreateCanvasChain(); + RethinkDisplayViewports(); + ResizeLayersToCanvases(); +} + +void +DupLayoutCv(cvf, cvt, save) +struct canvas *cvf, *cvt; +int save; +{ + while(cvf) + { + cvt->c_slorient = cvf->c_slorient; + cvt->c_slweight = cvf->c_slweight; + if (cvf == D_forecv) + D_forecv = cvt; + if (!save) + { + cvt->c_display = display; + if (!cvf->c_slperp) + { + cvt->c_captev.type = EV_TIMEOUT; + cvt->c_captev.data = (char *)cvt; + cvt->c_captev.handler = cv_winid_fn; + cvt->c_blank.l_cvlist = 0; + cvt->c_blank.l_layfn = &BlankLf; + cvt->c_blank.l_bottom = &cvt->c_blank; + } + cvt->c_layer = cvf->c_layer; + } + else + { + struct win *p = cvf->c_layer ? Layer2Window(cvf->c_layer) : 0; + cvt->c_layer = p ? &p->w_layer : 0; + } + if (cvf->c_slperp) + { + cvt->c_slperp = (struct canvas *)calloc(1, sizeof(struct canvas)); + cvt->c_slperp->c_slback = cvt; + CanvasInitBlank(cvt->c_slperp); + DupLayoutCv(cvf->c_slperp, cvt->c_slperp, save); + } + if (cvf->c_slnext) + { + cvt->c_slnext = (struct canvas *)calloc(1, sizeof(struct canvas)); + cvt->c_slnext->c_slprev = cvt; + cvt->c_slnext->c_slback = cvt->c_slback; + CanvasInitBlank(cvt->c_slnext); + } + cvf = cvf->c_slnext; + cvt = cvt->c_slnext; + } +} + +void +PutWindowCv(cv) +struct canvas *cv; +{ + struct win *p; + for (; cv; cv = cv->c_slnext) + { + if (cv->c_slperp) + { + PutWindowCv(cv->c_slperp); + continue; + } + p = cv->c_layer ? (struct win *)cv->c_layer->l_data : 0; + cv->c_layer = 0; + SetCanvasWindow(cv, p); + } +} + diff --git a/canvas.h b/canvas.h new file mode 100644 index 0000000..d918013 --- /dev/null +++ b/canvas.h @@ -0,0 +1,101 @@ +/* Copyright (c) 2008, 2009 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Micah Cowan (micah@cowan.name) + * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) + * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, see + * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + * $Id$ GNU + */ + +#ifndef SCREEN_CANVAS_H +#define SCREEN_CANVAS_H + +#define SLICE_UNKN 0 +#define SLICE_VERT (1 << 0) +#define SLICE_HORI (1 << 1) + +#define SLICE_THIS (1 << 2) /* used in equal test */ +#define SLICE_GLOBAL (1 << 3) + +struct canvas +{ + struct canvas *c_next; /* next canvas on display */ + struct display *c_display; /* back pointer to display */ + + struct canvas *c_slnext; /* next canvas in display slice */ + struct canvas *c_slprev; /* prev canvas in display slice */ + struct canvas *c_slperp; /* perpendicular slice */ + struct canvas *c_slback; /* perpendicular slice back pointer */ + int c_slorient; /* our slice orientation */ + int c_slweight; /* size ratio */ + + struct viewport *c_vplist; + struct layer *c_layer; /* layer on this canvas */ + struct canvas *c_lnext; /* next canvas that displays layer */ + struct layer c_blank; /* bottom layer, always blank */ + int c_xoff; /* canvas x offset on display */ + int c_yoff; /* canvas y offset on display */ + int c_xs; + int c_xe; + int c_ys; + int c_ye; + struct event c_captev; /* caption changed event */ +}; + +struct win; /* forward declaration */ + +extern void SetCanvasWindow __P((struct canvas *, struct win *)); +extern void SetForeCanvas __P((struct display *, struct canvas *)); +extern struct canvas *FindCanvas __P((int, int)); +extern int MakeDefaultCanvas __P((void)); +extern int AddCanvas __P((int)); +extern void RemCanvas __P((void)); +extern void OneCanvas __P((void)); +extern void FreeCanvas __P((struct canvas *)); +extern void ResizeCanvas __P((struct canvas *)); +extern void RecreateCanvasChain __P((void)); +extern void RethinkViewportOffsets __P((struct canvas *)); +extern int CountCanvasPerp __P((struct canvas *)); +extern void EqualizeCanvas __P((struct canvas *, int)); +extern void DupLayoutCv __P((struct canvas *, struct canvas *, int)); +extern void PutWindowCv __P((struct canvas *)); + +#define CV_CALL(cv, cmd) \ +{ \ + struct display *olddisplay = display; \ + struct layer *oldflayer = flayer; \ + struct layer *l = cv->c_layer; \ + struct canvas *cvlist = l->l_cvlist; \ + struct canvas *cvlnext = cv->c_lnext; \ + flayer = l; \ + l->l_cvlist = cv; \ + cv->c_lnext = 0; \ + cmd; \ + flayer = oldflayer; \ + l->l_cvlist = cvlist; \ + cv->c_lnext = cvlnext; \ + display = olddisplay; \ +} + +#endif /* SCREEN_CANVAS_H */ + @@ -0,0 +1,348 @@ +/* Copyright (c) 2008, 2009 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Micah Cowan (micah@cowan.name) + * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) + * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * +#ifdef HAVE_BRAILLE + * Modified by: + * Authors: Hadi Bargi Rangin bargi@dots.physics.orst.edu + * Bill Barry barryb@dots.physics.orst.edu + * + * Modifications Copyright (c) 1995 by + * Science Access Project, Oregon State University. +#endif + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, see + * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + */ + +#include "config.h" +#include "os.h" +#include "acls.h" +#include "comm.h" + +#define bcopy :-( /* or include screen.h here */ + +/* Must be in alpha order ! */ + +struct comm comms[RC_LAST + 1] = +{ +#ifdef MULTIUSER + { "acladd", ARGS_1234 }, + { "aclchg", ARGS_23 }, + { "acldel", ARGS_1 }, + { "aclgrp", ARGS_12 }, + { "aclumask", ARGS_1|ARGS_ORMORE }, +#endif + { "activity", ARGS_1 }, +#ifdef MULTIUSER + { "addacl", ARGS_1234 }, +#endif + { "allpartial", NEED_DISPLAY|ARGS_1 }, + { "altscreen", ARGS_01 }, + { "at", ARGS_2|ARGS_ORMORE }, +#ifdef COLOR + { "attrcolor", ARGS_12 }, +#endif + { "autodetach", ARGS_1 }, +#ifdef AUTO_NUKE + { "autonuke", NEED_DISPLAY|ARGS_1 }, +#endif + { "backtick", ARGS_1|ARGS_ORMORE }, +#ifdef COLOR + { "bce", NEED_FORE|ARGS_01 }, +#endif + +#ifdef HAVE_BRAILLE +/* keywords for braille display (bd) */ + { "bd_bc_down", ARGS_0 }, + { "bd_bc_left", ARGS_0 }, + { "bd_bc_right", ARGS_0 }, + { "bd_bc_up", ARGS_0 }, + { "bd_bell", ARGS_01 }, + { "bd_braille_table", ARGS_01 }, + { "bd_eightdot", ARGS_01 }, + { "bd_info", ARGS_01 }, + { "bd_link", ARGS_01 }, + { "bd_lower_left", ARGS_0 }, + { "bd_lower_right", ARGS_0 }, + { "bd_ncrc", ARGS_01 }, + { "bd_port", ARGS_01 }, + { "bd_scroll", ARGS_01 }, + { "bd_skip", ARGS_01 }, + { "bd_start_braille", ARGS_01 }, + { "bd_type", ARGS_01 }, + { "bd_upper_left", ARGS_0 }, + { "bd_upper_right", ARGS_0 }, + { "bd_width", ARGS_01 }, +#endif + + { "bell", ARGS_01 }, + { "bell_msg", ARGS_01 }, + { "bind", ARGS_1|ARGS_ORMORE }, +#ifdef MAPKEYS + { "bindkey", ARGS_0|ARGS_ORMORE }, +#endif + { "blanker", NEED_DISPLAY|ARGS_0}, +#ifdef BLANKER_PRG + { "blankerprg", ARGS_0|ARGS_ORMORE }, +#endif + { "break", NEED_FORE|ARGS_01 }, + { "breaktype", NEED_FORE|ARGS_01 }, +#ifdef COPY_PASTE + { "bufferfile", ARGS_01 }, +#endif + { "bumpleft", NEED_FORE|ARGS_0 }, + { "bumpright", NEED_FORE|ARGS_0 }, + { "c1", NEED_FORE|ARGS_01 }, + { "caption", ARGS_12 }, +#ifdef MULTIUSER + { "chacl", ARGS_23 }, +#endif + { "charset", NEED_FORE|ARGS_1 }, + { "chdir", ARGS_01 }, +#ifdef DW_CHARS + { "cjkwidth", ARGS_01 }, +#endif + { "clear", NEED_FORE|ARGS_0 }, + { "collapse", ARGS_0 }, + { "colon", NEED_LAYER|ARGS_01 }, + { "command", NEED_DISPLAY|ARGS_02 }, +#ifdef COPY_PASTE + { "compacthist", ARGS_01 }, +#endif + { "console", NEED_FORE|ARGS_01 }, +#ifdef COPY_PASTE + { "copy", NEED_FORE|NEED_DISPLAY|ARGS_0 }, + { "crlf", ARGS_01 }, +#endif + { "debug", ARGS_01 }, +#ifdef AUTO_NUKE + { "defautonuke", ARGS_1 }, +#endif +#ifdef COLOR + { "defbce", ARGS_1 }, +#endif + { "defbreaktype", ARGS_01 }, + { "defc1", ARGS_1 }, + { "defcharset", ARGS_01 }, + { "defdynamictitle", ARGS_1 }, +#ifdef ENCODINGS + { "defencoding", ARGS_1 }, +#endif + { "defescape", ARGS_1 }, + { "defflow", ARGS_12 }, + { "defgr", ARGS_1 }, + { "defhstatus", ARGS_01 }, +#ifdef ENCODINGS + { "defkanji", ARGS_1 }, +#endif + { "deflog", ARGS_1 }, +#if defined(UTMPOK) && defined(LOGOUTOK) + { "deflogin", ARGS_1 }, +#endif + { "defmode", ARGS_1 }, + { "defmonitor", ARGS_1 }, + { "defmousetrack", ARGS_1 }, +#ifdef MULTI + { "defnonblock", ARGS_1 }, +#endif + { "defobuflimit", ARGS_1 }, +#ifdef COPY_PASTE + { "defscrollback", ARGS_1 }, +#endif + { "defshell", ARGS_1 }, + { "defsilence", ARGS_1 }, + { "defslowpaste", ARGS_1 }, +#ifdef UTF8 + { "defutf8", ARGS_1 }, +#endif + { "defwrap", ARGS_1 }, + { "defwritelock", ARGS_1 }, +#ifdef DETACH + { "detach", NEED_DISPLAY|ARGS_01 }, +#endif + { "digraph", NEED_LAYER|ARGS_012 }, + { "dinfo", NEED_DISPLAY|ARGS_0 }, + { "displays", NEED_LAYER|ARGS_0 }, + { "dumptermcap", NEED_FORE|ARGS_0 }, + { "dynamictitle", ARGS_1 }, + { "echo", CAN_QUERY|ARGS_12 }, +#ifdef ENCODINGS + { "encoding", ARGS_12 }, +#endif + { "escape", ARGS_1 }, + { "eval", ARGS_1|ARGS_ORMORE }, +#ifdef PSEUDOS + { "exec", ARGS_0|ARGS_ORMORE }, +#endif + { "fit", NEED_DISPLAY|ARGS_0 }, + { "flow", NEED_FORE|ARGS_01 }, + { "focus", NEED_DISPLAY|ARGS_01 }, + { "focusminsize", ARGS_02 }, + { "gr", NEED_FORE|ARGS_01 }, + { "group", NEED_FORE|ARGS_01 }, + { "hardcopy", NEED_FORE|ARGS_012 }, + { "hardcopy_append", ARGS_1 }, + { "hardcopydir", ARGS_01 }, + { "hardstatus", ARGS_012 }, + { "height", ARGS_0123 }, + { "help", NEED_LAYER|ARGS_02 }, +#ifdef COPY_PASTE + { "history", NEED_DISPLAY|NEED_FORE|ARGS_0 }, +#endif + { "hstatus", NEED_FORE|ARGS_1 }, + { "idle", ARGS_0|ARGS_ORMORE }, + { "ignorecase", ARGS_01 }, + { "info", CAN_QUERY|NEED_LAYER|ARGS_0 }, +#ifdef ENCODINGS + { "kanji", NEED_FORE|ARGS_12 }, +#endif + { "kill", NEED_FORE|ARGS_0 }, + { "lastmsg", CAN_QUERY|NEED_DISPLAY|ARGS_0 }, + { "layout", ARGS_1|ARGS_ORMORE}, + { "license", NEED_LAYER|ARGS_0 }, +#ifdef LOCK + { "lockscreen", NEED_DISPLAY|ARGS_0 }, +#endif + { "log", NEED_FORE|ARGS_01 }, + { "logfile", ARGS_012 }, +#if defined(UTMPOK) && defined(LOGOUTOK) + { "login", NEED_FORE|ARGS_01 }, +#endif + { "logtstamp", ARGS_012 }, +#ifdef MAPKEYS + { "mapdefault", NEED_DISPLAY|ARGS_0 }, + { "mapnotnext", NEED_DISPLAY|ARGS_0 }, + { "maptimeout", ARGS_01 }, +#endif +#ifdef COPY_PASTE + { "markkeys", ARGS_1 }, +#endif + { "maxwin", ARGS_01 }, + { "meta", NEED_LAYER|ARGS_0 }, + { "monitor", NEED_FORE|ARGS_01 }, + { "mousetrack", NEED_DISPLAY | ARGS_01 }, + { "msgminwait", ARGS_1 }, + { "msgwait", ARGS_1 }, +#ifdef MULTIUSER + { "multiuser", ARGS_1 }, +#endif + { "nethack", ARGS_1 }, + { "next", ARGS_0 }, +#ifdef MULTI + { "nonblock", NEED_DISPLAY|ARGS_01 }, +#endif + { "number", CAN_QUERY|NEED_FORE|ARGS_01 }, + { "obuflimit", NEED_DISPLAY|ARGS_01 }, + { "only", NEED_DISPLAY|ARGS_0 }, + { "other", ARGS_0 }, + { "partial", NEED_FORE|ARGS_01 }, +#ifdef PASSWORD + { "password", ARGS_01 }, +#endif +#ifdef COPY_PASTE + { "paste", NEED_LAYER|ARGS_012 }, + { "pastefont", ARGS_01 }, +#endif + { "pow_break", NEED_FORE|ARGS_01 }, +#if defined(DETACH) && defined(POW_DETACH) + { "pow_detach", NEED_DISPLAY|ARGS_0 }, + { "pow_detach_msg", ARGS_01 }, +#endif + { "prev", ARGS_0 }, + { "printcmd", ARGS_01 }, + { "process", NEED_DISPLAY|ARGS_01 }, + { "quit", ARGS_0 }, +#ifdef COPY_PASTE + { "readbuf", ARGS_0123 }, +#endif + { "readreg", ARGS_0|ARGS_ORMORE }, + { "redisplay", NEED_DISPLAY|ARGS_0 }, + { "register", ARGS_24 }, + { "remove", NEED_DISPLAY|ARGS_0 }, +#ifdef COPY_PASTE + { "removebuf", ARGS_0 }, +#endif + { "rendition", ARGS_23 }, + { "reset", NEED_FORE|ARGS_0 }, + { "resize", NEED_DISPLAY|ARGS_0|ARGS_ORMORE }, + { "screen", ARGS_0|ARGS_ORMORE }, +#ifdef COPY_PASTE + { "scrollback", NEED_FORE|ARGS_1 }, +#endif + { "select", CAN_QUERY|ARGS_01 }, + { "sessionname", ARGS_01 }, + { "setenv", ARGS_012 }, + { "setsid", ARGS_1 }, + { "shell", ARGS_1 }, + { "shelltitle", ARGS_1 }, + { "silence", NEED_FORE|ARGS_01 }, + { "silencewait", ARGS_1 }, + { "sleep", ARGS_1 }, + { "slowpaste", NEED_FORE|ARGS_01 }, + { "sorendition", ARGS_012 }, + { "sort", ARGS_0}, + { "source", ARGS_1 }, + { "split", NEED_DISPLAY|ARGS_01 }, + { "startup_message", ARGS_1 }, + { "stuff", NEED_LAYER|ARGS_012 }, +#ifdef MULTIUSER + { "su", NEED_DISPLAY|ARGS_012 }, +#endif +#ifdef BSDJOBS + { "suspend", NEED_DISPLAY|ARGS_0 }, +#endif + { "term", ARGS_1 }, + { "termcap", ARGS_23 }, + { "termcapinfo", ARGS_23 }, + { "terminfo", ARGS_23 }, + { "time", CAN_QUERY|ARGS_01 }, + { "title", CAN_QUERY|NEED_FORE|ARGS_01 }, + { "umask", ARGS_1|ARGS_ORMORE }, + { "unbindall", ARGS_0 }, + { "unsetenv", ARGS_1 }, +#ifdef UTF8 + { "utf8", NEED_FORE|ARGS_012 }, +#endif + { "vbell", ARGS_01 }, + { "vbell_msg", ARGS_01 }, + { "vbellwait", ARGS_1 }, + { "verbose", ARGS_01 }, + { "version", ARGS_0 }, + { "wall", NEED_DISPLAY|ARGS_1}, + { "width", ARGS_0123 }, + { "windowlist", ARGS_012 }, + { "windows", CAN_QUERY|ARGS_01 }, + { "wrap", NEED_FORE|ARGS_01 }, +#ifdef COPY_PASTE + { "writebuf", ARGS_0123 }, +#endif + { "writelock", NEED_FORE|ARGS_01 }, + { "xoff", NEED_LAYER|ARGS_0 }, + { "xon", NEED_LAYER|ARGS_0 }, +#ifdef ZMODEM + { "zmodem", ARGS_012 }, +#endif + { "zombie", ARGS_012 }, + { "zombie_timeout", ARGS_1 } +}; @@ -0,0 +1,96 @@ +#! /bin/sh + +if test -z "$AWK"; then + AWK=awk +fi +if test -z "$CC"; then + CC=cc +fi +if test -z "$srcdir"; then + srcdir=. +fi + +LC_ALL=C +export LC_ALL + +rm -f comm.h +cat << EOF > comm.h +/* + * This file is automagically created from comm.c -- DO NOT EDIT + */ + +struct comm +{ + char *name; + int flags; +#ifdef MULTIUSER + AclBits userbits[ACL_BITS_PER_CMD]; +#endif +}; + +#define ARGS_MASK (3) + +#define ARGS_0 (0) +#define ARGS_1 (1) +#define ARGS_2 (2) +#define ARGS_3 (3) + +#define ARGS_PLUS1 (1<<2) +#define ARGS_PLUS2 (1<<3) +#define ARGS_PLUS3 (1<<4) +#define ARGS_ORMORE (1<<5) + +#define NEED_FORE (1<<6) /* this command needs a fore window */ +#define NEED_DISPLAY (1<<7) /* this command needs a display */ +#define NEED_LAYER (1<<8) /* this command needs a layer */ +#define CAN_QUERY (1<<9) /* this command can be queried, i.e. used with -Q to + get back a result to stdout */ + +#define ARGS_01 (ARGS_0 | ARGS_PLUS1) +#define ARGS_02 (ARGS_0 | ARGS_PLUS2) +#define ARGS_12 (ARGS_1 | ARGS_PLUS1) +#define ARGS_23 (ARGS_2 | ARGS_PLUS1) +#define ARGS_24 (ARGS_2 | ARGS_PLUS2) +#define ARGS_34 (ARGS_3 | ARGS_PLUS1) +#define ARGS_012 (ARGS_0 | ARGS_PLUS1 | ARGS_PLUS2) +#define ARGS_0123 (ARGS_0 | ARGS_PLUS1 | ARGS_PLUS2 | ARGS_PLUS3) +#define ARGS_123 (ARGS_1 | ARGS_PLUS1 | ARGS_PLUS2) +#define ARGS_124 (ARGS_1 | ARGS_PLUS1 | ARGS_PLUS3) +#define ARGS_1234 (ARGS_1 | ARGS_PLUS1 | ARGS_PLUS2 | ARGS_PLUS3) + +struct action +{ + int nr; + char **args; + int *argl; + int quiet; /* Suppress (currently unused) + 0x01 - Error message + 0x02 - Normal message + */ +}; + +#define RC_ILLEGAL -1 + +EOF +$AWK < ${srcdir}/comm.c >> comm.h ' +/^ [{] ".*/ { if (old > $2) { + printf("***ERROR: %s <= %s !!!\n\n", $2, old); + exit 1; + } + old = $2; + } +' + +$CC -E -I. -I${srcdir} ${srcdir}/comm.c > comm.cpp +sed < comm.cpp \ + -n \ + -e '/^ *{ "/y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e '/^ *{ "/s/^ *{ "\([^"]*\)".*/\1/p' \ +| $AWK ' +/.*/ { printf "#define RC_%s %d\n",$0,i++; + } +END { printf "\n#define RC_LAST %d\n",i-1; + } +' >> comm.h +chmod a-w comm.h +rm -f comm.cpp diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..c0f02df --- /dev/null +++ b/configure.ac @@ -0,0 +1,1315 @@ +dnl Process this file with autoconf to produce a configure script. +dnl +dnl $Id$ GNU +dnl +dnl Many thanks to David MacKenzie for writing autoconf and +dnl providing a sample configure.in file for screen. +dnl +AC_REVISION($Revision$)dnl +AC_INIT(screen.c) +AC_CONFIG_HEADER(config.h) +AC_PREREQ(2.60) + +dnl +dnl Define some useful macros +dnl +AC_DEFUN([AC_PROGRAM_SOURCE], +[AC_REQUIRE([AC_PROG_CPP])AC_PROVIDE([$0])cat > conftest.c <<EOF +#include "confdefs.h" +[$1] +_CUT_HERE_ +[$2] +EOF +eval "$ac_cpp conftest.c 2>&5 | sed -e '1,/_CUT_HERE_/d' -e 's/ //g' > conftest.out" +. ./conftest.out +rm -f conftest* +])dnl +dnl +define(AC_NOTE, +[echo "$1" 1>&AC_FD_MSG +])dnl + +dnl +dnl Extract version from patchlevel.h +dnl +rev=`sed < ${srcdir}/patchlevel.h -n -e '/#define REV/s/#define REV *//p'` +vers=`sed < ${srcdir}/patchlevel.h -n -e '/#define VERS/s/#define VERS *//p'` +pat=`sed < ${srcdir}/patchlevel.h -n -e '/#define PATCHLEVEL/s/#define PATCHLEVEL *//p'` +VERSION="$rev.$vers.$pat" +AC_NOTE(this is screen version $VERSION) +AC_SUBST(VERSION) +AC_PREFIX_PROGRAM(screen) +AC_PREFIX_PROGRAM(gzip) + +old_CFLAGS="$CFLAGS" +AC_PROG_CC +AC_PROG_CPP +AC_PROG_GCC_TRADITIONAL +AC_ISC_POSIX +AC_USE_SYSTEM_EXTENSIONS + +AC_PROG_AWK + +AC_PROG_INSTALL + +if test -f etc/toolcheck; then +AC_CHECKING(for buggy tools) +sh etc/toolcheck 1>&AC_FD_MSG +fi + + + +dnl SOCKDIR +AC_MSG_CHECKING(if a system-wide socket dir should be used) +AC_ARG_ENABLE(socket-dir, + [ --disable-socket-dir disable system wide socket-dir and use ~/.screen instead], + [ + AC_MSG_RESULT(no. ~/.screen will be used instead.) + ], + [ + AC_MSG_RESULT(yes) + AC_MSG_CHECKING(for the socket dir) + SOCKDIR="(eff_uid ? \"/tmp/uscreens\" : \"/tmp/screens\")" + AC_ARG_WITH(socket-dir, [ --with-socket-dir=path where to put the per-user sockets], [ + case "${withval}" in + *\"*) SOCKDIR="${withval}" ;; + *) SOCKDIR="\"${withval}\"" ;; + esac + ]) + AC_MSG_RESULT(${SOCKDIR}) + AC_DEFINE_UNQUOTED(SOCKDIR, $SOCKDIR) + ] +) + + +dnl +dnl **** special unix variants **** +dnl +if test "$cross_compiling" = no && test -n "$ISC" ; then + AC_DEFINE(ISC) LIBS="$LIBS -linet" +fi + +dnl AC_CHECKING(for OSF1) +dnl if test -f /bin/uname ; then +dnl if test `/bin/uname` = OSF1 || test -f /osf_boot; then +dnl AC_DEFINE(OSF1) # this disables MIPS again.... +dnl fi +dnl fi + +if test "$cross_compiling" = no && test -f /sysV68 ; then +AC_DEFINE(sysV68) +fi + +if test "$cross_compiling" = no ; then +AC_CHECKING(for MIPS) +if test -f /lib/libmld.a || test -f /usr/lib/libmld.a || test -f /usr/lib/cmplrs/cc/libmld.a; then +oldlibs="$LIBS" +test -f /bin/mx || LIBS="$LIBS -lmld" # for nlist. But not on alpha. +dnl djm@eng.umd.edu: "... for one thing, it doubles the size of the executable" +AC_CHECKING(mld library) +AC_TRY_LINK(,,,LIBS="$oldlibs") +dnl +dnl +if test -r /dev/ptc; then +AC_DEFINE(MIPS) +AC_CHECKING(wait3) +AC_TRY_LINK(,[wait3();], , +AC_CHECKING(wait2) +AC_TRY_LINK(,[wait2();], +dnl John Rouillard (rouilj@sni-usa.com): +dnl need -I/usr/include/bsd in RISCOS otherwise sockets are broken, no +dnl job control etc. +dnl Detect RISCOS if wait2 is present, but not wait3. +AC_DEFINE(USE_WAIT2) LIBS="$LIBS -lbsd" ; CC="$CC -I/usr/include/bsd" +)) +fi +fi +fi + + +AC_CHECKING(for Ultrix) +AC_EGREP_CPP(YES_IS_DEFINED, +[#if defined(ultrix) || defined(__ultrix) + YES_IS_DEFINED; +#endif +], ULTRIX=1) + +if test "$cross_compiling" = no && test -f /usr/lib/libpyr.a ; then +oldlibs="$LIBS" +LIBS="$LIBS -lpyr" +AC_CHECKING(Pyramid OSX) +AC_TRY_LINK(,[open_controlling_pty("")], AC_DEFINE(OSX), LIBS="$oldlibs") +fi + +dnl ghazi@caip.rutgers.edu (Kaveh R. Ghazi): +dnl BBN butterfly is not POSIX, but a MACH BSD system. +dnl Do not define POSIX and TERMIO. +AC_CHECKING(for butterfly) +AC_EGREP_CPP(YES_IS_DEFINED, +[#if defined(butterfly) + YES_IS_DEFINED; +#endif +], butterfly=1) + +if test -z "$butterfly"; then +if test -n "$ULTRIX"; then + test -z "$GCC" && CC="$CC -YBSD" +fi +AC_CHECKING(for POSIX.1) +AC_EGREP_CPP(YES_IS_DEFINED, +[#include <sys/types.h> +#include <unistd.h> +main () { +#ifdef _POSIX_VERSION + YES_IS_DEFINED; +#endif +], AC_NOTE(- you have a POSIX system) AC_DEFINE(POSIX) posix=1) +fi + +AC_CHECKING(for System V) +AC_TRY_COMPILE( +[#include <sys/types.h> +#include <signal.h> +#include <fcntl.h>], [int x = SIGCHLD | FNDELAY;], , AC_DEFINE(SYSV)) + +AC_CHECKING(for sequent/ptx) +AC_EGREP_CPP(YES_IS_DEFINED, +[#ifdef _SEQUENT_ + YES_IS_DEFINED; +#endif +], LIBS="$LIBS -lsocket -linet";seqptx=1) + +AC_CHECKING(SVR4) +AC_EGREP_CPP(yes, +[main () { +#if defined(SVR4) || defined(__SVR4) + yes; +#endif +], AC_NOTE(- you have a SVR4 system) AC_DEFINE(SVR4) svr4=1) +if test -n "$svr4" ; then +oldlibs="$LIBS" +LIBS="$LIBS -lelf" +AC_CHECKING(SVR4) +AC_TRY_LINK([#include <utmpx.h> +],, +[AC_CHECK_HEADER(dwarf.h, AC_DEFINE(BUGGYGETLOGIN), +[AC_CHECK_HEADER(elf.h, AC_DEFINE(BUGGYGETLOGIN))])] +,LIBS="$oldlibs") +fi + +AC_CHECK_HEADERS([stropts.h string.h strings.h]) + +AC_CHECKING(for Solaris 2.x) +AC_EGREP_CPP(YES_IS_DEFINED, +[#if defined(SVR4) && defined(sun) + YES_IS_DEFINED; +#endif +], LIBS="$LIBS -lsocket -lnsl -lkstat") + +dnl +dnl **** typedefs **** +dnl +dnl (currently not used) +dnl +dnl AC_CHECKING(for pid_t) +dnl AC_EGREP_CPP(pid_t,[#include <sys/types.h> +dnl ],AC_DEFINE(PID_T_DEFINED)) +dnl +dnl AC_CHECKING(for sig_t) +dnl AC_EGREP_CPP(sig_t,[#include <sys/types.h> +dnl #include <signal.h> +dnl ],AC_DEFINE(SIG_T_DEFINED)) +dnl +dnl AC_CHECKING(for uid_t) +dnl AC_EGREP_CPP(uid_t,[#include <sys/types.h> +dnl ],AC_DEFINE(UID_T_DEFINED)) +dnl + +dnl +dnl **** Job control **** +dnl + +AC_CHECKING(BSD job jontrol) +AC_TRY_LINK( +[#include <sys/types.h> +#include <sys/ioctl.h> +], [ +#ifdef POSIX +tcsetpgrp(0, 0); +#else +int x = TIOCSPGRP; +#ifdef SYSV +setpgrp(); +#else +int y = TIOCNOTTY; +#endif +#endif +], AC_NOTE(- you have jobcontrol) AC_DEFINE(BSDJOBS), AC_NOTE(- you don't have jobcontrol)) + +dnl +dnl **** setresuid(), setreuid(), seteuid() **** +dnl +AC_CHECKING(setresuid) +AC_TRY_LINK(,[ +setresuid(0, 0, 0); +], AC_DEFINE(HAVE_SETRESUID)) +AC_CHECKING(setreuid) +AC_TRY_LINK(,[ +setreuid(0, 0); +], AC_DEFINE(HAVE_SETREUID)) +dnl +dnl seteuid() check: +dnl linux seteuid was broken before V1.1.11 +dnl NeXT, AUX, ISC, and ultrix are still broken (no saved uid support) +dnl Solaris seteuid doesn't change the saved uid, bad for +dnl multiuser screen sessions +AC_CHECKING(seteuid) +AC_TRY_LINK(,[ +#if defined(linux) || defined(NeXT) || defined(_AUX_SOURCE) || defined(AUX) || defined(ultrix) || (defined(sun) && defined(SVR4)) || defined(ISC) || defined(sony_news) +seteuid_is_broken(0); +#else +seteuid(0); +#endif +], AC_DEFINE(HAVE_SETEUID)) + +dnl execvpe +AC_CHECKING(execvpe) +AC_TRY_LINK(,[ + execvpe(0, 0, 0); +], AC_DEFINE(HAVE_EXECVPE) +CFLAGS="$CFLAGS -D_GNU_SOURCE") + +dnl +dnl **** select() **** +dnl + +AC_CHECKING(select) +AC_TRY_LINK(,[select(0, 0, 0, 0, 0);],, +LIBS="$LIBS -lnet -lnsl" +AC_CHECKING(select with $LIBS) +AC_TRY_LINK(,[select(0, 0, 0, 0, 0);],, +AC_MSG_ERROR(!!! no select - no screen)) +) +dnl +dnl **** FIFO tests **** +dnl + +AC_CHECKING(fifos) +AC_TRY_RUN([ +/* For select - According to POSIX 1003.1-2001 */ +#include <sys/select.h> + +/* For select - According to earlier standards */ +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include <sys/stat.h> +#include <fcntl.h> + +#ifndef O_NONBLOCK +#define O_NONBLOCK O_NDELAY +#endif +#ifndef S_IFIFO +#define S_IFIFO 0010000 +#endif + +char *fin = "/tmp/conftest$$"; + +main() +{ + struct stat stb; + fd_set f; + + (void)alarm(5); + unlink(fin); +#ifdef POSIX + if (mkfifo(fin, 0777)) +#else + if (mknod(fin, S_IFIFO|0777, 0)) +#endif + exit(1); + if (stat(fin, &stb) || (stb.st_mode & S_IFIFO) != S_IFIFO) + exit(1); + close(0); +#ifdef __386BSD__ + /* + * The next test fails under 386BSD, but screen works using fifos. + * Fifos in O_RDWR mode are only used for the BROKEN_PIPE case and for + * the select() configuration test. + */ + exit(0); +#endif + if (open(fin, O_RDONLY | O_NONBLOCK)) + exit(1); + if (fork() == 0) + { + close(0); + if (open(fin, O_WRONLY | O_NONBLOCK)) + exit(1); + close(0); + if (open(fin, O_WRONLY | O_NONBLOCK)) + exit(1); + if (write(0, "TEST", 4) == -1) + exit(1); + exit(0); + } + FD_SET(0, &f); + if (select(1, &f, 0, 0, 0) == -1) + exit(1); + exit(0); +} +], AC_NOTE(- your fifos are usable) fifo=1, +AC_NOTE(- your fifos are not usable), +AC_NOTE(- skipping check because we are cross compiling; assuming fifos are usable) fifo=1) +rm -f /tmp/conftest* + +if test -n "$fifo"; then +AC_CHECKING(for broken fifo implementation) +AC_TRY_RUN([ +/* For select - According to POSIX 1003.1-2001 */ +#include <sys/select.h> + +/* For select - According to earlier standards */ +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include <sys/stat.h> +#include <fcntl.h> + +#ifndef O_NONBLOCK +#define O_NONBLOCK O_NDELAY +#endif +#ifndef S_IFIFO +#define S_IFIFO 0010000 +#endif + +char *fin = "/tmp/conftest$$"; + +main() +{ + struct timeval tv; + fd_set f; + +#ifdef POSIX + if (mkfifo(fin, 0600)) +#else + if (mknod(fin, S_IFIFO|0600, 0)) +#endif + exit(1); + close(0); + if (open(fin, O_RDONLY|O_NONBLOCK)) + exit(1); + FD_SET(0, &f); + tv.tv_sec = 1; + tv.tv_usec = 0; + if (select(1, &f, 0, 0, &tv)) + exit(1); + exit(0); +} +], AC_NOTE(- your implementation is ok), +AC_NOTE(- you have a broken implementation) AC_DEFINE(BROKEN_PIPE) fifobr=1, +AC_NOTE(- skipping check because we are cross compiling; assuming fifo implementation is ok)) +rm -f /tmp/conftest* +fi + +dnl +dnl **** SOCKET tests **** +dnl +dnl may need LIBS="$LIBS -lsocket" here +dnl + +AC_CHECKING(sockets) +AC_TRY_RUN([ +/* For select - According to POSIX 1003.1-2001 */ +#include <sys/select.h> + +/* For select - According to earlier standards */ +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/un.h> + +char *son = "/tmp/conftest$$"; + +main() +{ + int s1, s2, l; + struct sockaddr_un a; + fd_set f; + + (void)alarm(5); + if ((s1 = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + exit(1); + a.sun_family = AF_UNIX; + strcpy(a.sun_path, son); + (void) unlink(son); + if (bind(s1, (struct sockaddr *) &a, strlen(son)+2) == -1) + exit(1); + if (listen(s1, 2)) + exit(1); + if (fork() == 0) + { + if ((s2 = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + kill(getppid(), 3); + (void)connect(s2, (struct sockaddr *)&a, strlen(son) + 2); + if (write(s2, "HELLO", 5) == -1) + kill(getppid(), 3); + exit(0); + } + l = sizeof(a); + close(0); + if (accept(s1, &a, &l)) + exit(1); + FD_SET(0, &f); + if (select(1, &f, 0, 0, 0) == -1) + exit(1); + exit(0); +} +], AC_NOTE(- your sockets are usable) sock=1, +AC_NOTE(- your sockets are not usable), +AC_NOTE(- skipping check because we are cross compiling; assuming sockets are usable) sock=1) +rm -f /tmp/conftest* + +if test -n "$sock"; then +AC_CHECKING(socket implementation) +AC_TRY_RUN([ +/* For select - According to POSIX 1003.1-2001 */ +#include <sys/select.h> + +/* For select - According to earlier standards */ +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> + +char *son = "/tmp/conftest$$"; + +main() +{ + int s; + struct stat stb; + struct sockaddr_un a; + if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + exit(0); + a.sun_family = AF_UNIX; + strcpy(a.sun_path, son); + (void) unlink(son); + if (bind(s, (struct sockaddr *) &a, strlen(son)+2) == -1) + exit(0); + if (stat(son, &stb)) + exit(1); + close(s); + exit(0); +} +],AC_NOTE(- you are normal), +AC_NOTE(- unix domain sockets are not kept in the filesystem) +AC_DEFINE(SOCK_NOT_IN_FS) socknofs=1, +AC_NOTE(- skipping check because we are cross compiling; assuming sockets are normal)) +rm -f /tmp/conftest* +fi + + +dnl +dnl **** choose sockets or fifos **** +dnl +dnl **** check the select implementation **** +dnl + +AC_TRY_RUN([ +/* For select - According to POSIX 1003.1-2001 */ +#include <sys/select.h> + +/* For select - According to earlier standards */ +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include <sys/stat.h> +#include <fcntl.h> + +char *nam = "/tmp/conftest$$"; + +#ifdef NAMEDPIPE + +#ifndef O_NONBLOCK +#define O_NONBLOCK O_NDELAY +#endif +#ifndef S_IFIFO +#define S_IFIFO 0010000 +#endif + +main() +{ + fd_set f; + +#ifdef __FreeBSD__ +/* From Andrew A. Chernov (ache@astral.msk.su): + * opening RDWR fifo fails in BSD 4.4, but select return values are + * right. + */ + exit(0); +#endif + (void)alarm(5); +#ifdef POSIX + if (mkfifo(nam, 0777)) +#else + if (mknod(nam, S_IFIFO|0777, 0)) +#endif + exit(1); + close(0); + if (open(nam, O_RDWR | O_NONBLOCK)) + exit(1); + if (write(0, "TEST", 4) == -1) + exit(1); + +#else + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> + +main() +{ + int s1, s2, l; + struct sockaddr_un a; + fd_set f; + + (void)alarm(5); + if ((s1 = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + exit(1); + a.sun_family = AF_UNIX; + strcpy(a.sun_path, nam); + (void) unlink(nam); + if (bind(s1, (struct sockaddr *) &a, strlen(nam)+2) == -1) + exit(1); + if (listen(s1, 2)) + exit(1); + if (fork() == 0) + { + if ((s2 = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + kill(getppid(), 3); + (void)connect(s2, (struct sockaddr *)&a, strlen(nam) + 2); + if (write(s2, "HELLO", 5) == -1) + kill(getppid(), 3); + exit(0); + } + l = sizeof(a); + close(0); + if (accept(s1, (struct sockaddr *)&a, &l)) + exit(1); +#endif + + + FD_SET(0, &f); + if (select(1, &f, 0, 0, 0) == -1) + exit(1); + if (select(1, &f, &f, 0, 0) != 2) + exit(1); + exit(0); +} +],AC_NOTE(- select is ok), +AC_NOTE(- select can't count) AC_DEFINE(SELECT_BROKEN), +AC_NOTE(- skipping check because we are cross compiling; assuming select is ok)) + +dnl +dnl **** termcap or terminfo **** +dnl +AC_CHECKING(for tgetent) +AC_TRY_LINK(,tgetent((char *)0, (char *)0);,, +olibs="$LIBS" +LIBS="-lcurses $olibs" +AC_CHECKING(libcurses) +AC_TRY_LINK(,[ +#ifdef __hpux +__sorry_hpux_libcurses_is_totally_broken_in_10_10(); +#else +tgetent((char *)0, (char *)0); +#endif +],, +LIBS="-ltermcap $olibs" +AC_CHECKING(libtermcap) +AC_TRY_LINK(,tgetent((char *)0, (char *)0);,, +LIBS="-ltermlib $olibs" +AC_CHECKING(libtermlib) +AC_TRY_LINK(,tgetent((char *)0, (char *)0);,, +LIBS="-lncursesw $olibs" +AC_CHECKING(libncursesw) +AC_TRY_LINK(,tgetent((char *)0, (char *)0);,, +LIBS="-ltinfow $olibs" +AC_CHECKING(libtinfow) +AC_TRY_LINK(,tgetent((char *)0, (char *)0);,, +LIBS="-lncurses $olibs" +AC_CHECKING(libncurses) +AC_TRY_LINK(,tgetent((char *)0, (char *)0);,, +LIBS="-ltinfo $olibs" +AC_CHECKING(libtinfo) +AC_TRY_LINK(,tgetent((char *)0, (char *)0);,, +AC_MSG_ERROR(!!! no tgetent - no screen))))))))) + +AC_TRY_RUN([ +main() +{ + exit(strcmp(tgoto("%p1%d", 0, 1), "1") ? 0 : 1); +}], AC_NOTE(- you use the termcap database), +AC_NOTE(- you use the terminfo database) AC_DEFINE(TERMINFO), +AC_NOTE(- skipping check because we are cross compiling; assuming terminfo database is used) AC_DEFINE(TERMINFO)) +AC_CHECKING(ospeed) +AC_TRY_LINK(extern short ospeed;,ospeed=5;,,AC_DEFINE(NEED_OSPEED)) + +dnl +dnl **** PTY specific things **** +dnl +if test "$cross_compiling" = no ; then +AC_CHECKING(for /dev/ptc) +if test -r /dev/ptc; then +AC_DEFINE(HAVE_DEV_PTC) +fi +fi + +if test "$cross_compiling" = no ; then +AC_CHECKING(for SVR4 ptys) +sysvr4ptys= +if test -c /dev/ptmx ; then +AC_TRY_LINK([],[ptsname(0);grantpt(0);unlockpt(0);],[AC_DEFINE(HAVE_SVR4_PTYS) +sysvr4ptys=1]) +fi +fi + +AC_CHECK_FUNCS(getpt) + +dnl check for openpty() +if test -z "$sysvr4ptys"; then +AC_CHECK_FUNCS(openpty,, +[AC_CHECK_LIB(util,openpty, [AC_DEFINE(HAVE_OPENPTY)] [LIBS="$LIBS -lutil"])]) +fi + +if test "$cross_compiling" = no ; then +AC_CHECKING(for ptyranges) +if test -d /dev/ptym ; then +pdir='/dev/ptym' +else +pdir='/dev' +fi +dnl SCO uses ptyp%d +AC_EGREP_CPP(YES_IS_DEFINED, +[#ifdef M_UNIX + YES_IS_DEFINED; +#endif +], ptys=`echo /dev/ptyp??`, ptys=`echo $pdir/pty??`) +dnl if test -c /dev/ptyp19; then +dnl ptys=`echo /dev/ptyp??` +dnl else +dnl ptys=`echo $pdir/pty??` +dnl fi +if test "$ptys" != "$pdir/pty??" ; then +p0=`echo $ptys | tr ' ' '\012' | sed -e 's/^.*\(.\).$/\1/g' | sort -u | tr -d '\012'` +p1=`echo $ptys | tr ' ' '\012' | sed -e 's/^.*\(.\)$/\1/g' | sort -u | tr -d '\012'` +AC_DEFINE_UNQUOTED(PTYRANGE0,"$p0") +AC_DEFINE_UNQUOTED(PTYRANGE1,"$p1") +fi +fi + +dnl **** pty mode/group handling **** +dnl +dnl support provided by Luke Mewburn <lm@rmit.edu.au>, 931222 +AC_ARG_WITH(pty-mode, [ --with-pty-mode=mode default mode for ptys], [ ptymode="${withval}" ]) +AC_ARG_WITH(pty-group, [ --with-pty-group=group default group for ptys], [ ptygrp="${withval}" ]) +test -n "$ptymode" || ptymode=0620 +if test -n "$ptygrp" ; then +AC_DEFINE_UNQUOTED(PTYMODE, $ptymode) +AC_DEFINE_UNQUOTED(PTYGROUP,$ptygrp) +else + +AC_CHECKING(default tty permissions/group) +rm -f conftest_grp +AC_TRY_RUN([ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +main() +{ + struct stat sb; + char *x,*ttyname(); + int om, m; + FILE *fp; + + if (!(x = ttyname(0))) exit(1); + if (stat(x, &sb)) exit(1); + om = sb.st_mode; + if (om & 002) exit(0); + m = system("mesg y"); + if (m == -1 || m == 127) exit(1); + if (stat(x, &sb)) exit(1); + m = sb.st_mode; + if (chmod(x, om)) exit(1); + if (m & 002) exit(0); + if (sb.st_gid == getgid()) exit(1); + if (!(fp=fopen("conftest_grp", "w"))) + exit(1); + fprintf(fp, "%d\n", sb.st_gid); + fclose(fp); + exit(0); +} +],[ + if test -f conftest_grp; then + ptygrp=`cat conftest_grp` + AC_NOTE([- pty mode: $ptymode, group: $ptygrp]) + AC_DEFINE_UNQUOTED(PTYMODE, $ptymode) + AC_DEFINE_UNQUOTED(PTYGROUP,$ptygrp) + else + AC_NOTE(- ptys are world accessable) + fi +],[ + WRITEPATH='' + XTERMPATH='' + AC_PATH_PROG(WRITEPATH, write) + AC_PATH_PROG(XTERMPATH, xterm) + found= + if test -n "$WRITEPATH$XTERMPATH"; then + findfollow= + lsfollow= + found=`find $WRITEPATH $XTERMPATH -follow -print 2>/dev/null` + if test -n "$found"; then + findfollow=-follow + lsfollow=L + fi + if test -n "$XTERMPATH"; then + ptygrpn=`ls -l$lsfollow $XTERMPATH | sed -n -e 1p | $AWK '{print $4}'` + if test tty != "$ptygrpn"; then + XTERMPATH= + fi + fi + fi + if test -n "$WRITEPATH$XTERMPATH"; then + found=`find $WRITEPATH $XTERMPATH $findfollow -perm -2000 -print` + if test -n "$found"; then + ptygrp=`ls -ln$lsfollow $found | sed -n -e 1p | $AWK '{print $4}'` + AC_NOTE([- pty mode: $ptymode, group: $ptygrp]) + AC_DEFINE_UNQUOTED(PTYMODE, $ptymode) + AC_DEFINE_UNQUOTED(PTYGROUP,$ptygrp) + else + AC_NOTE(- ptys are world accessable) + fi + else + AC_NOTE(- can't determine - assume ptys are world accessable) + fi + ], + AC_NOTE(- skipping check because we are cross compiling; assuming ptys are world accessable) +) +rm -f conftest_grp +fi + +dnl +dnl **** utmp handling **** +dnl +AC_CHECKING(getutent) +AC_TRY_LINK([ +#include <time.h> /* to get time_t on SCO */ +#include <sys/types.h> +#if defined(SVR4) && !defined(DGUX) +#include <utmpx.h> +#define utmp utmpx +#else +#include <utmp.h> +#endif +#ifdef __hpux +#define pututline _pututline +#endif +], +[int x = DEAD_PROCESS; pututline((struct utmp *)0); getutent();], AC_DEFINE(GETUTENT), +olibs="$LIBS" +LIBS="$LIBS -lgen" +AC_CHECKING(getutent with -lgen) +AC_TRY_LINK([ +#include <time.h> +#include <sys/types.h> +#if defined(SVR4) && !defined(DGUX) +#include <utmpx.h> +#define utmp utmpx +#else +#include <utmp.h> +#endif +#ifdef __hpux +#define pututline _pututline +#endif +], +[int x = DEAD_PROCESS; pututline((struct utmp *)0); getutent();], AC_DEFINE(GETUTENT), LIBS="$olibs") +) +AC_CHECKING(ut_host) +AC_TRY_COMPILE([ +#include <time.h> +#include <sys/types.h> +#if defined(SVR4) && !defined(DGUX) +#include <utmpx.h> +#define utmp utmpx +#else +#include <utmp.h> +#endif +],[struct utmp u; u.ut_host[0] = 0;], AC_DEFINE(UTHOST)) +AC_CHECK_HEADER(utempter.h, have_utempter=yes, have_utempter=no) +if test "$have_utempter" = yes; then + AC_DEFINE(HAVE_UTEMPTER) + LIBS="$LIBS -lutempter" +fi + +dnl +dnl **** loadav **** +dnl +if test "$cross_compiling" = no ; then +AC_CHECKING(for libutil(s)) +test -f /usr/lib/libutils.a && LIBS="$LIBS -lutils" +test -f /usr/lib/libutil.a && LIBS="$LIBS -lutil" +fi + +AC_CHECKING(getloadavg) +AC_TRY_LINK(,[getloadavg((double *)0, 0);], +AC_DEFINE(LOADAV_GETLOADAVG) load=1, +if test "$cross_compiling" = no && test -f /usr/lib/libkvm.a ; then +olibs="$LIBS" +LIBS="$LIBS -lkvm" +AC_CHECKING(getloadavg with -lkvm) +AC_TRY_LINK(,[getloadavg((double *)0, 0);], +AC_DEFINE(LOADAV_GETLOADAVG) load=1, LIBS="$olibs") +fi +) + +if test -z "$load" ; then +AC_EGREP_CPP(YES_IS_DEFINED, +[#if defined(NeXT) || defined(apollo) || defined(linux) + YES_IS_DEFINED; +#endif +], load=1) +fi +if test -z "$load" && test "$cross_compiling" = no ; then +AC_CHECKING(for kernelfile) +for core in /unix /vmunix /dynix /hp-ux /xelos /dev/ksyms /kernel/unix /kernel/genunix /unicos /mach /netbsd /386bsd /dgux /bsd /stand/vmunix; do + if test -f $core || test -c $core; then + break + fi +done +if test ! -f $core && test ! -c $core ; then + AC_NOTE(- no kernelfile found) +else + AC_NOTE(- using kernelfile '$core') + if test -r $core ; then + AC_DEFINE_UNQUOTED(LOADAV_UNIX,"$core") + AC_CHECK_HEADER(nlist.h, + [AC_DEFINE(NLIST_STRUCT) + AC_CHECKING(n_un in struct nlist) + AC_TRY_COMPILE([#include <nlist.h>], + [struct nlist n; n.n_un.n_name = 0;], + AC_DEFINE(NLIST_NAME_UNION))]) + + AC_CHECKING(for nlist declaration) + AC_EGREP_CPP([nlist(( | )( | )*.*\(|\()],[ +#ifdef NLIST_STRUCT +# include <nlist.h> +#else +# include <a.out.h> +#endif +],AC_DEFINE(NLIST_DECLARED)) + + AC_CHECKING(for avenrun symbol) + nlist64= + for av in avenrun _avenrun _Loadavg avenrun _avenrun _Loadavg; do + AC_TRY_RUN([ +#include <sys/types.h> +#ifdef NLIST_STRUCT +#include <nlist.h> +#else +#include <a.out.h> +#endif + +$nlist64 + +struct nlist nl[2]; + +main() +{ +#if !defined(_AUX_SOURCE) && !defined(AUX) +# ifdef NLIST_NAME_UNION + nl[0].n_un.n_name = "$av"; +# else + nl[0].n_name = "$av"; +# endif +#else + strncpy(nl[0].n_name, "$av", sizeof(nl[0].n_name)); +#endif + nlist(LOADAV_UNIX, nl); + if (nl[0].n_value == 0) + exit(1); + exit(0); +} + ],avensym=$av;break) + if test "$av" = _Loadavg; then + nlist64='#define nlist nlist64' + fi + done + if test -z "$avensym" ; then + AC_NOTE(- no avenrun symbol found) + else + AC_NOTE(- using avenrun symbol '$avensym') + AC_DEFINE_UNQUOTED(LOADAV_AVENRUN,"$avensym") + if test -n "$nlist64"; then + AC_NOTE(- used nlist64 to find it) + AC_DEFINE(LOADAV_USE_NLIST64) + fi + load=1 + fi + else + AC_NOTE( Can't configure the load average display feature) + AC_NOTE( because $core is not readable by you.) + AC_NOTE( To configure the load average display feature,) + AC_NOTE( re-run configure as root if possible.) + AC_NOTE( If you are not the system administrator then disregard) + AC_NOTE( this warning. You can still use screen without) + AC_NOTE( the load average display feature.) + fi +fi +fi + +AC_PROGRAM_SOURCE([ +#include <sys/types.h> +#include <sys/param.h> +],[ +#if !defined(LOADAV_GETLOADAVG) && ((defined(hp300) && !defined(__hpux)) || defined(sun) || (defined(ultrix) && defined(mips)) || defined(_SEQUENT_) || defined(sgi) || (defined(SVR4) && !defined(__hpux)) || defined(sony_news) || (!defined(__osf__) && defined(__alpha)) || defined(_IBMR2) || defined(_AUX_SOURCE) || defined(AUX) || defined(m88k)) +loadtype=long +# if defined(apollo) || defined(_IBMR2) || defined(_AUX_SOURCE) || defined(AUX) +loadscale=65536 +# else +# if defined(FSCALE) && !defined(__osf__) +# undef FSCALE +loadscale=FSCALE +# else +# ifdef sgi +loadtype=int +loadscale=1024 +# else +# if defined(MIPS) || defined(SVR4) || defined(m88k) +loadscale=256 +# else /* not MIPS */ +loadscale=1000 /* our default value */ +# endif /* MIPS */ +# endif /* sgi */ +# endif /* not FSCALE */ +# endif /* not apollo */ +#else +loadtype=double +loadscale=1 +#endif +#ifdef alliant +loadnum=4 +#else +loadnum=3 +#endif +]) + +if test -n "$load" ; then AC_DEFINE(LOADAV) fi +if test -n "$loadtype" ; then AC_DEFINE_UNQUOTED(LOADAV_TYPE,$loadtype) fi +if test -n "$loadnum" ; then AC_DEFINE_UNQUOTED(LOADAV_NUM,$loadnum) fi +if test -n "$loadscale" ; then AC_DEFINE_UNQUOTED(LOADAV_SCALE,$loadscale) fi + +dnl +dnl **** signal handling **** +dnl +if test -n "$posix" ; then + +dnl POSIX has reliable signals with void return type. +AC_NOTE(assuming posix signal definition) +AC_DEFINE(SIGVOID) + +else + +AC_CHECKING(return type of signal handlers) +AC_TRY_COMPILE( +[#include <sys/types.h> +#include <signal.h> +#ifdef signal +#undef signal +#endif +extern void (*signal ()) ();], [int i;], AC_DEFINE(SIGVOID)) +AC_CHECKING(sigset) +AC_TRY_LINK([ +#include <sys/types.h> +#include <signal.h> +],[ +#ifdef SIGVOID +sigset(0, (void (*)())0); +#else +sigset(0, (int (*)())0); +#endif +], AC_DEFINE(USESIGSET)) +AC_CHECKING(signal implementation) +AC_TRY_RUN([ +#include <sys/types.h> +#include <signal.h> + +#ifndef SIGCLD +#define SIGCLD SIGCHLD +#endif +#ifdef USESIGSET +#define signal sigset +#endif + +int got; + +#ifdef SIGVOID +void +#endif +hand() +{ + got++; +} + +main() +{ + /* on hpux we use sigvec to get bsd signals */ +#ifdef __hpux + (void)signal(SIGCLD, hand); + kill(getpid(), SIGCLD); + kill(getpid(), SIGCLD); + if (got < 2) + exit(1); +#endif + exit(0); +} +],,AC_DEFINE(SYSVSIGS),:) + +fi + +dnl +dnl **** libraries **** +dnl + +AC_CHECKING(for crypt and sec libraries) +if test "$cross_compiling" = no ; then +test -f /lib/libcrypt_d.a || test -f /usr/lib/libcrypt_d.a && LIBS="$LIBS -lcrypt_d" +fi +oldlibs="$LIBS" +LIBS="$LIBS -lcrypt" +AC_CHECKING(crypt) +AC_TRY_LINK(,,,LIBS="$oldlibs") +if test "$cross_compiling" = no ; then +test -f /lib/libsec.a || test -f /usr/lib/libsec.a && LIBS="$LIBS -lsec" +test -f /lib/libshadow.a || test -f /usr/lib/libshadow.a && LIBS="$LIBS -lshadow" +fi + +oldlibs="$LIBS" +LIBS="$LIBS -lsun" +AC_CHECKING(IRIX sun library) +AC_TRY_LINK(,,,LIBS="$oldlibs") + +AC_CHECKING(syslog) +AC_TRY_LINK(,[closelog();], , [oldlibs="$LIBS" +LIBS="$LIBS -lbsd" +AC_CHECKING(syslog in libbsd.a) +AC_TRY_LINK(, [closelog();], AC_NOTE(- found.), [LIBS="$oldlibs" +AC_NOTE(- bad news: syslog missing.) AC_DEFINE(NOSYSLOG)])]) + +AC_EGREP_CPP(YES_IS_DEFINED, +[#ifdef M_UNIX + YES_IS_DEFINED; +#endif +], LIBS="$LIBS -lsocket -lcrypt_i") + +dnl +dnl **** misc things **** +dnl +AC_CHECKING(wait union) +AC_TRY_COMPILE([#include <sys/types.h> +#include <sys/wait.h> +],[ + union wait x; + int y; +#ifdef WEXITSTATUS + y = WEXITSTATUS(x); +#endif +],AC_DEFINE(BSDWAIT)) + +if test -z "$butterfly"; then +AC_CHECKING(for termio or termios) +AC_TRY_CPP([#include <termio.h>], AC_DEFINE(TERMIO), +if test -n "$posix"; then +AC_TRY_CPP([#include <termios.h>], AC_DEFINE(TERMIO)) +fi +) +fi + +dnl AC_CHECK_HEADER(shadow.h, AC_DEFINE(SHADOWPW)) +AC_CHECKING(getspnam) +AC_TRY_LINK([#include <shadow.h>], [getspnam("x");],AC_DEFINE(SHADOWPW)) + +AC_CHECKING(getttyent) +AC_TRY_LINK(,[getttyent();], AC_DEFINE(GETTTYENT)) + +AC_CHECKING(fdwalk) +AC_TRY_LINK([#include <stdlib.h>], [fdwalk(NULL, NULL);],AC_DEFINE(HAVE_FDWALK)) + +AC_CHECKING(whether memcpy/memmove/bcopy handles overlapping arguments) +AC_TRY_RUN([ +main() { + char buf[10]; + strcpy(buf, "abcdefghi"); + bcopy(buf, buf + 2, 3); + if (strncmp(buf, "ababcf", 6)) + exit(1); + strcpy(buf, "abcdefghi"); + bcopy(buf + 2, buf, 3); + if (strncmp(buf, "cdedef", 6)) + exit(1); + exit(0); /* libc version works properly. */ +}], AC_DEFINE(USEBCOPY),,:) + +AC_TRY_RUN([ +#define bcopy(s,d,l) memmove(d,s,l) +main() { + char buf[10]; + strcpy(buf, "abcdefghi"); + bcopy(buf, buf + 2, 3); + if (strncmp(buf, "ababcf", 6)) + exit(1); + strcpy(buf, "abcdefghi"); + bcopy(buf + 2, buf, 3); + if (strncmp(buf, "cdedef", 6)) + exit(1); + exit(0); /* libc version works properly. */ +}], AC_DEFINE(USEMEMMOVE),, + AC_NOTE(- skipping check because we are cross compiling; use memmove) AC_DEFINE(USEMEMMOVE)) + + +AC_TRY_RUN([ +#define bcopy(s,d,l) memcpy(d,s,l) +main() { + char buf[10]; + strcpy(buf, "abcdefghi"); + bcopy(buf, buf + 2, 3); + if (strncmp(buf, "ababcf", 6)) + exit(1); + strcpy(buf, "abcdefghi"); + bcopy(buf + 2, buf, 3); + if (strncmp(buf, "cdedef", 6)) + exit(1); + exit(0); /* libc version works properly. */ +}], AC_DEFINE(USEMEMCPY),,:) + +AC_SYS_LONG_FILE_NAMES + +AC_MSG_CHECKING(for vsprintf) +AC_TRY_LINK([#include <stdarg.h>],[va_list valist; vsprintf(0,0,valist);], AC_MSG_RESULT(yes);AC_DEFINE(USEVARARGS), AC_MSG_RESULT(no)) + +AC_HEADER_DIRENT + +AC_MSG_CHECKING(for setenv) +if test -z "$ac_setenv_args"; then + AC_TRY_LINK( + [#include <stdlib.h>], + [ + setenv((char *) 0, (char *) 0, 0); + ], ac_setenv_args=3) +fi +if test -z "$ac_setenv_args"; then + AC_TRY_LINK( + [#include <stdlib.h>], + [ + setenv((char *) 0, (char *) 0); + ], ac_setenv_args=2) +fi +if test -n "$ac_setenv_args"; then + AC_DEFINE(USESETENV) + if test "$ac_setenv_args" = 3; then + AC_DEFINE(HAVE_SETENV_3) + elif test "$ac_setenv_args" = 2; then + AC_DEFINE(HAVE_SETENV_2) + fi +else + AC_MSG_RESULT(no) + AC_MSG_CHECKING(for putenv) + AC_TRY_LINK(,[putenv((char *)0);unsetenv((char *)0);], AC_MSG_RESULT(yes) , AC_MSG_RESULT(no);AC_DEFINE(NEEDPUTENV)) +fi +AC_MSG_CHECKING([for nl_langinfo(CODESET)]) +AC_TRY_LINK([ +#include <langinfo.h> +],[nl_langinfo(CODESET);], AC_MSG_RESULT(yes);AC_DEFINE(HAVE_NL_LANGINFO), AC_MSG_RESULT(no)) + +AC_SEARCH_LIBS(gethostname, nsl) + +AC_CHECK_FUNCS(rename fchmod fchown strerror lstat _exit utimes vsnprintf getcwd setlocale strftime) + +AC_ARG_ENABLE(pam, [ --enable-pam enable PAM support]) +if test "$enable_pam" = "yes"; then + AC_MSG_CHECKING(for PAM support) + oldlibs="$LIBS" + LIBS="$LIBS -lpam" + AC_TRY_LINK([#include <security/pam_appl.h>], [ + pam_start(0, 0, 0, 0); + pam_authenticate(0, 0); + pam_end(0,0); + ], AC_MSG_RESULT(yes);AC_DEFINE(USE_PAM), + AC_MSG_RESULT(no);LIBS="$oldlibs") +fi + +AC_ARG_ENABLE(use-locale, + [ --enable-use-locale use localized month/day names (default: yes)], + [], + [enable_use_locale=yes] +) +if test "$enable_use_locale" = "yes"; then + AC_DEFINE(USE_LOCALE) +fi +AC_ARG_ENABLE(telnet, [ --enable-telnet enable builtin telnet]) +if test "$enable_telnet" = "yes"; then + AC_DEFINE(BUILTIN_TELNET) +fi +AC_ARG_ENABLE(colors256, [ --enable-colors256 enable support for 256 colors]) +if test "$enable_colors256" = "yes"; then + AC_DEFINE(COLORS256) +fi +AC_ARG_ENABLE(rxvt_osc, [ --enable-rxvt_osc enable support for rxvt OSC codes]) +if test "$enable_rxvt_osc" = "yes"; then + AC_DEFINE(RXVT_OSC) +fi + +dnl +dnl **** the end **** +dnl +if test -z "$old_CFLAGS"; then + if test "x$CFLAGS" = "x-g"; then + CFLAGS="-O" + fi +fi +dnl Ptx bug workaround -- insert -lc after -ltermcap +test -n "$seqptx" && LIBS="-ltermcap -lc -lsocket -linet -lnsl -lsec -lseq" + +ETCSCREENRC= +AC_MSG_CHECKING(for the global screenrc file) +AC_ARG_WITH(sys-screenrc, [ --with-sys-screenrc=path to the global screenrc file], [ ETCSCREENRC="${withval}" ]) +AC_SUBST(ETCSCREENRC) + +AC_OUTPUT(Makefile doc/Makefile, [[ +# a hook for preserving undef directive in config.h +mv config.h conftest +sed -e 's@^\(.*\)defin.\( .*\) .*/\*\(.*KEEP_UNDEF_HERE\)@\1undef\2 /\*\3@' < conftest > config.h +rm -f conftest +]]) + +echo "" +if test -z "$AWK"; then +echo "!!! Since you have no awk you must copy the files 'comm.h.dist'" +echo "!!! and 'term.h.dist' to 'comm.h' and 'term.h'." +echo "!!! Do _not_ change the user configuration section in config.h!" +echo "Please check the pathnames in the Makefile." +else +echo "Now please check the pathnames in the Makefile and in the user" +echo "configuration section in config.h." +fi +echo "Then type 'make' to make screen. Good luck." +echo "" diff --git a/display.c b/display.c new file mode 100644 index 0000000..978b0a8 --- /dev/null +++ b/display.c @@ -0,0 +1,4082 @@ +/* Copyright (c) 2008, 2009 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Micah Cowan (micah@cowan.name) + * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) + * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, see + * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + */ + +#include <sys/types.h> +#include <signal.h> +#include <fcntl.h> +#ifndef sun +# include <sys/ioctl.h> +#endif + +#include "config.h" +#include "screen.h" +#include "extern.h" +#include "braille.h" +#include "canvas.h" + +/* CSI parsing status */ +enum + { + CSI_PB=0, CSI_PX=1, CSI_PY=2, CSI_DONE=3, + CSI_ESC_SEEN, CSI_BEGIN, CSI_INACTIVE, CSI_INVALID + }; + +static int CountChars __P((int)); +static int DoAddChar __P((int)); +static int BlankResize __P((int, int)); +static int CallRewrite __P((int, int, int, int)); +static void disp_readev_fn __P((struct event *, char *)); +static void disp_processinput __P((struct display *, unsigned char *, int)); +static void disp_writeev_fn __P((struct event *, char *)); +#ifdef linux +static void disp_writeev_eagain __P((struct event *, char *)); +#endif +static void disp_status_fn __P((struct event *, char *)); +static void disp_hstatus_fn __P((struct event *, char *)); +static void disp_blocked_fn __P((struct event *, char *)); +#ifdef MAPKEYS +static void disp_map_fn __P((struct event *, char *)); +#endif +static void disp_idle_fn __P((struct event *, char *)); +#ifdef BLANKER_PRG +static void disp_blanker_fn __P((struct event *, char *)); +#endif +static void WriteLP __P((int, int)); +static void INSERTCHAR __P((int)); +static void RAW_PUTCHAR __P((int)); +#ifdef COLOR +static void SetBackColor __P((int)); +#endif +static void RemoveStatusMinWait __P((void)); + + +extern struct layer *flayer; +extern struct win *windows, *fore; +extern struct LayFuncs WinLf; + +extern int use_hardstatus; +extern int MsgWait, MsgMinWait; +extern const int Z0width, Z1width; +extern unsigned char *blank, *null; +extern struct mline mline_blank, mline_null, mline_old; +extern struct mchar mchar_null, mchar_blank, mchar_so; +extern struct NewWindow nwin_default; +extern struct action idleaction; + +/* XXX shouldn't be here */ +extern char *hstatusstring; +extern char *captionstring; + +extern int pastefont; +extern int idletimo; + +#ifdef BLANKER_PRG +extern int pty_preopen; +#if defined(TIOCSWINSZ) || defined(TIOCGWINSZ) +extern struct winsize glwz; +#endif +extern char **NewEnv; +extern int real_uid, real_gid; +extern int ServerSocket, eff_uid, eff_gid; +#endif + +/* + * tputs needs this to calculate the padding + */ +#ifndef NEED_OSPEED +extern +#endif /* NEED_OSPEED */ +short ospeed; + + +struct display *display, *displays; +#ifdef COLOR +int attr2color[8][4]; +int nattr2color; +#endif + +#ifndef MULTI +struct display TheDisplay; +#endif + +/* + * The default values + */ +int defobuflimit = OBUF_MAX; +int defnonblock = -1; +int defmousetrack = 0; +#ifdef AUTO_NUKE +int defautonuke = 0; +#endif +int captionalways; +int hardstatusemu = HSTATUS_IGNORE; + +int focusminwidth, focusminheight; + +/* + * Default layer management + */ + +void +DefProcess(bufp, lenp) +char **bufp; +int *lenp; +{ + *bufp += *lenp; + *lenp = 0; +} + +void +DefRedisplayLine(y, xs, xe, isblank) +int y, xs, xe, isblank; +{ + if (isblank == 0 && y >= 0) + DefClearLine(y, xs, xe, 0); +} + +void +DefClearLine(y, xs, xe, bce) +int y, xs, xe, bce; +{ + LClearLine(flayer, y, xs, xe, bce, (struct mline *)0); +} + +/*ARGSUSED*/ +int +DefRewrite(y, xs, xe, rend, doit) +int y, xs, xe, doit; +struct mchar *rend; +{ + return EXPENSIVE; +} + +/*ARGSUSED*/ +int +DefResize(wi, he) +int wi, he; +{ + return -1; +} + +void +DefRestore() +{ + LAY_DISPLAYS(flayer, InsertMode(0)); + /* ChangeScrollRegion(0, D_height - 1); */ + LKeypadMode(flayer, 0); + LCursorkeysMode(flayer, 0); + LCursorVisibility(flayer, 0); + LMouseMode(flayer, 0); + LSetRendition(flayer, &mchar_null); + LSetFlow(flayer, nwin_default.flowflag & FLOW_NOW); +} + +/* + * Blank layer management + */ + +struct LayFuncs BlankLf = +{ + DefProcess, + 0, + DefRedisplayLine, + DefClearLine, + DefRewrite, + BlankResize, + DefRestore, + 0 +}; + +/*ARGSUSED*/ +static int +BlankResize(wi, he) +int wi, he; +{ + flayer->l_width = wi; + flayer->l_height = he; + return 0; +} + + +/* + * Generate new display, start with a blank layer. + * The termcap arrays are not initialised here. + * The new display is placed in the displays list. + */ + +struct display * +MakeDisplay(uname, utty, term, fd, pid, Mode) +char *uname, *utty, *term; +int fd, pid; +struct mode *Mode; +{ + struct acluser **u; + struct baud_values *b; + + if (!*(u = FindUserPtr(uname)) && UserAdd(uname, (char *)0, u)) + return 0; /* could not find or add user */ + +#ifdef MULTI + if ((display = (struct display *)calloc(1, sizeof(*display))) == 0) + return 0; +#else + if (displays) + return 0; + bzero((char *)&TheDisplay, sizeof(TheDisplay)); + display = &TheDisplay; +#endif + display->d_next = displays; + displays = display; + D_flow = 1; + D_nonblock = defnonblock; + D_userfd = fd; + D_readev.fd = D_writeev.fd = fd; + D_readev.type = EV_READ; + D_writeev.type = EV_WRITE; + D_readev.data = D_writeev.data = (char *)display; + D_readev.handler = disp_readev_fn; + D_writeev.handler = disp_writeev_fn; + evenq(&D_readev); + D_writeev.condpos = &D_obuflen; + D_writeev.condneg = &D_obuffree; + evenq(&D_writeev); + D_statusev.type = EV_TIMEOUT; + D_statusev.data = (char *)display; + D_statusev.handler = disp_status_fn; + D_hstatusev.type = EV_TIMEOUT; + D_hstatusev.data = (char *)display; + D_hstatusev.handler = disp_hstatus_fn; + D_blockedev.type = EV_TIMEOUT; + D_blockedev.data = (char *)display; + D_blockedev.handler = disp_blocked_fn; + D_blockedev.condpos = &D_obuffree; + D_blockedev.condneg = &D_obuflenmax; + D_hstatusev.handler = disp_hstatus_fn; +#ifdef MAPKEYS + D_mapev.type = EV_TIMEOUT; + D_mapev.data = (char *)display; + D_mapev.handler = disp_map_fn; +#endif + D_idleev.type = EV_TIMEOUT; + D_idleev.data = (char *)display; + D_idleev.handler = disp_idle_fn; +#ifdef BLANKER_PRG + D_blankerev.type = EV_READ; + D_blankerev.data = (char *)display; + D_blankerev.handler = disp_blanker_fn; + D_blankerev.fd = -1; +#endif + D_OldMode = *Mode; + D_status_obuffree = -1; + Resize_obuf(); /* Allocate memory for buffer */ + D_obufmax = defobuflimit; + D_obuflenmax = D_obuflen - D_obufmax; +#ifdef AUTO_NUKE + D_auto_nuke = defautonuke; +#endif + D_obufp = D_obuf; + D_printfd = -1; + D_userpid = pid; + +#ifdef POSIX + if ((b = lookup_baud((int)cfgetospeed(&D_OldMode.tio)))) + D_dospeed = b->idx; +#else +# ifdef TERMIO + if ((b = lookup_baud(D_OldMode.tio.c_cflag & CBAUD))) + D_dospeed = b->idx; +# else + D_dospeed = (short)D_OldMode.m_ttyb.sg_ospeed; +# endif +#endif + debug1("New displays ospeed = %d\n", D_dospeed); + + strncpy(D_usertty, utty, sizeof(D_usertty) - 1); + D_usertty[sizeof(D_usertty) - 1] = 0; + strncpy(D_termname, term, MAXTERMLEN); + D_termname[MAXTERMLEN] = 0; + D_user = *u; + D_processinput = ProcessInput; + D_mousetrack = defmousetrack; + return display; +} + + +void +FreeDisplay() +{ + struct win *p; +#ifdef MULTI + struct display *d, **dp; +#endif + +#ifdef FONT + FreeTransTable(); +#endif +#ifdef BLANKER_PRG + KillBlanker(); +#endif + if (D_userfd >= 0) + { + Flush(3); + if (!display) + return; + SetTTY(D_userfd, &D_OldMode); + fcntl(D_userfd, F_SETFL, 0); + } + freetty(); + if (D_tentry) + free(D_tentry); + D_tentry = 0; + if (D_processinputdata) + free(D_processinputdata); + D_processinputdata = 0; + D_tcinited = 0; + evdeq(&D_hstatusev); + evdeq(&D_statusev); + evdeq(&D_readev); + evdeq(&D_writeev); + evdeq(&D_blockedev); +#ifdef MAPKEYS + evdeq(&D_mapev); + if (D_kmaps) + { + free(D_kmaps); + D_kmaps = 0; + D_aseqs = 0; + D_nseqs = 0; + D_seqp = 0; + D_seql = 0; + D_seqh = 0; + } +#endif + evdeq(&D_idleev); +#ifdef BLANKER_PRG + evdeq(&D_blankerev); +#endif +#ifdef HAVE_BRAILLE + if (bd.bd_dpy == display) + { + bd.bd_start_braille = 0; + StartBraille(); + } +#endif + +#ifdef MULTI + for (dp = &displays; (d = *dp) ; dp = &d->d_next) + if (d == display) + break; + ASSERT(d); + if (D_status_lastmsg) + free(D_status_lastmsg); + if (D_obuf) + free(D_obuf); + *dp = display->d_next; +#else /* MULTI */ + ASSERT(display == displays); + ASSERT(display == &TheDisplay); + displays = 0; +#endif /* MULTI */ + + while (D_canvas.c_slperp) + FreeCanvas(D_canvas.c_slperp); + D_cvlist = 0; + + for (p = windows; p; p = p->w_next) + { + if (p->w_pdisplay == display) + p->w_pdisplay = 0; + if (p->w_lastdisp == display) + p->w_lastdisp = 0; + if (p->w_readev.condneg == &D_status || p->w_readev.condneg == &D_obuflenmax) + p->w_readev.condpos = p->w_readev.condneg = 0; + } +#ifdef ZMODEM + for (p = windows; p; p = p->w_next) + if (p->w_zdisplay == display) + zmodem_abort(p, 0); +#endif + if (D_mousetrack) + { + D_mousetrack = 0; + MouseMode(0); + } +#ifdef MULTI + free((char *)display); +#endif + display = 0; +} + +/* + * if the adaptflag is on, we keep the size of this display, else + * we may try to restore our old window sizes. + */ +void +InitTerm(adapt) +int adapt; +{ + ASSERT(display); + ASSERT(D_tcinited); + D_top = D_bot = -1; + AddCStr(D_IS); + AddCStr(D_TI); + /* Check for toggle */ + if (D_IM && strcmp(D_IM, D_EI)) + AddCStr(D_EI); + D_insert = 0; +#ifdef MAPKEYS + AddCStr(D_KS); + AddCStr(D_CCS); +#else + /* Check for toggle */ + if (D_KS && strcmp(D_KS, D_KE)) + AddCStr(D_KE); + if (D_CCS && strcmp(D_CCS, D_CCE)) + AddCStr(D_CCE); +#endif + D_keypad = 0; + D_cursorkeys = 0; + AddCStr(D_ME); + AddCStr(D_EA); + AddCStr(D_CE0); + D_rend = mchar_null; + D_atyp = 0; + if (adapt == 0) + ResizeDisplay(D_defwidth, D_defheight); + ChangeScrollRegion(0, D_height - 1); + D_x = D_y = 0; + Flush(3); + ClearAll(); + debug1("we %swant to adapt all our windows to the display\n", + (adapt) ? "" : "don't "); + /* In case the size was changed by a init sequence */ + CheckScreenSize((adapt) ? 2 : 0); +} + +void +FinitTerm() +{ + ASSERT(display); +#ifdef BLANKER_PRG + KillBlanker(); +#endif + if (D_tcinited) + { + ResizeDisplay(D_defwidth, D_defheight); + InsertMode(0); + ChangeScrollRegion(0, D_height - 1); + KeypadMode(0); + CursorkeysMode(0); + CursorVisibility(0); + if (D_mousetrack) + D_mousetrack = 0; + MouseMode(0); + ExtMouseMode(0); + SetRendition(&mchar_null); + SetFlow(FLOW_NOW); +#ifdef MAPKEYS + AddCStr(D_KE); + AddCStr(D_CCE); +#endif + if (D_hstatus) + ShowHStatus((char *)0); +#ifdef RXVT_OSC + ClearAllXtermOSC(); +#endif + D_x = D_y = -1; + GotoPos(0, D_height - 1); + AddChar('\r'); + AddChar('\n'); + AddCStr(D_TE); + } + Flush(3); +} + + +static void +INSERTCHAR(c) +int c; +{ + ASSERT(display); + if (!D_insert && D_x < D_width - 1) + { + if (D_IC || D_CIC) + { + if (D_IC) + AddCStr(D_IC); + else + AddCStr2(D_CIC, 1); + RAW_PUTCHAR(c); + return; + } + InsertMode(1); + if (!D_insert) + { + RefreshLine(D_y, D_x, D_width-1, 0); + return; + } + } + RAW_PUTCHAR(c); +} + +void +PUTCHAR(c) +int c; +{ + ASSERT(display); + if (D_insert && D_x < D_width - 1) + InsertMode(0); + RAW_PUTCHAR(c); +} + +void +PUTCHARLP(c) +int c; +{ + if (D_x < D_width - 1) + { + if (D_insert) + InsertMode(0); + RAW_PUTCHAR(c); + return; + } + if (D_CLP || D_y != D_bot) + { + int y = D_y; + RAW_PUTCHAR(c); + if (D_AM && !D_CLP) + GotoPos(D_width - 1, y); + return; + } + debug("PUTCHARLP: lp_missing!\n"); + D_lp_missing = 1; + D_rend.image = c; + D_lpchar = D_rend; +#ifdef DW_CHARS + /* XXX -> PutChar ? */ + if (D_mbcs) + { + D_lpchar.mbcs = c; + D_lpchar.image = D_mbcs; + D_mbcs = 0; + D_x--; + } +#endif +} + +/* + * RAW_PUTCHAR() is for all text that will be displayed. + * NOTE: charset Nr. 0 has a conversion table, but c1, c2, ... don't. + */ + +STATIC void +RAW_PUTCHAR(c) +int c; +{ + ASSERT(display); + +#ifdef FONT +# ifdef UTF8 + if (D_encoding == UTF8) + { + c = (c & 255) | (unsigned char)D_rend.font << 8 | (unsigned char)D_rend.fontx << 16; +# ifdef DW_CHARS + if (D_mbcs) + { + c = D_mbcs; + if (D_x == D_width) + D_x += D_AM ? 1 : -1; + D_mbcs = 0; + } + else if (utf8_isdouble(c)) + { + D_mbcs = c; + D_x++; + return; + } +# endif + if (c < 32) + { + AddCStr2(D_CS0, '0'); + AddChar(c + 0x5f); + AddCStr(D_CE0); + goto addedutf8; + } + if (c < 0x80) + { + if (D_xtable && D_xtable[(int)(unsigned char)D_rend.font] && D_xtable[(int)(unsigned char)D_rend.font][(int)(unsigned char)c]) + AddStr(D_xtable[(int)(unsigned char)D_rend.font][(int)(unsigned char)c]); + else + AddChar(c); + } + else + AddUtf8(c); + goto addedutf8; + } +# endif +# ifdef DW_CHARS + if (is_dw_font(D_rend.font)) + { + int t = c; + if (D_mbcs == 0) + { + D_mbcs = c; + D_x++; + return; + } + D_x--; + if (D_x == D_width - 1) + D_x += D_AM ? 1 : -1; + c = D_mbcs; + D_mbcs = t; + } +# endif +# if defined(ENCODINGS) && defined(DW_CHARS) + if (D_encoding) + c = PrepareEncodedChar(c); +# endif +# ifdef DW_CHARS + kanjiloop: +# endif + if (D_xtable && D_xtable[(int)(unsigned char)D_rend.font] && D_xtable[(int)(unsigned char)D_rend.font][(int)(unsigned char)c]) + AddStr(D_xtable[(int)(unsigned char)D_rend.font][(int)(unsigned char)c]); + else + AddChar(D_rend.font != '0' ? c : D_c0_tab[(int)(unsigned char)c]); +#else /* FONT */ + AddChar(c); +#endif /* FONT */ + +#ifdef UTF8 +addedutf8: +#endif + if (++D_x >= D_width) + { + if (D_AM == 0) + D_x = D_width - 1; + else if (!D_CLP || D_x > D_width) + { + D_x -= D_width; + if (D_y < D_height-1 && D_y != D_bot) + D_y++; + } + } +#ifdef DW_CHARS + if (D_mbcs) + { + c = D_mbcs; + D_mbcs = 0; + goto kanjiloop; + } +#endif +} + +static int +DoAddChar(c) +int c; +{ + /* this is for ESC-sequences only (AddChar is a macro) */ + AddChar(c); + return c; +} + +void +AddCStr(s) +char *s; +{ + if (display && s && *s) + { + ospeed = D_dospeed; + tputs(s, 1, DoAddChar); + } +} + +void +AddCStr2(s, c) +char *s; +int c; +{ + if (display && s && *s) + { + ospeed = D_dospeed; + tputs(tgoto(s, 0, c), 1, DoAddChar); + } +} + + +/* Insert mode is a toggle on some terminals, so we need this hack: + */ +void +InsertMode(on) +int on; +{ + if (display && on != D_insert && D_IM) + { + D_insert = on; + if (on) + AddCStr(D_IM); + else + AddCStr(D_EI); + } +} + +/* ...and maybe keypad application mode is a toggle, too: + */ +void +KeypadMode(on) +int on; +{ +#ifdef MAPKEYS + if (display) + D_keypad = on; +#else + if (display && D_keypad != on && D_KS) + { + D_keypad = on; + if (on) + AddCStr(D_KS); + else + AddCStr(D_KE); + } +#endif +} + +void +CursorkeysMode(on) +int on; +{ +#ifdef MAPKEYS + if (display) + D_cursorkeys = on; +#else + if (display && D_cursorkeys != on && D_CCS) + { + D_cursorkeys = on; + if (on) + AddCStr(D_CCS); + else + AddCStr(D_CCE); + } +#endif +} + +void +ReverseVideo(on) +int on; +{ + if (display && D_revvid != on && D_CVR) + { + D_revvid = on; + if (D_revvid) + AddCStr(D_CVR); + else + AddCStr(D_CVN); + } +} + +void +CursorVisibility(v) +int v; +{ + if (display && D_curvis != v) + { + if (D_curvis) + AddCStr(D_VE); /* do this always, just to be safe */ + D_curvis = 0; + if (v == -1 && D_VI) + AddCStr(D_VI); + else if (v == 1 && D_VS) + AddCStr(D_VS); + else + return; + D_curvis = v; + } +} + +void +MouseMode(mode) +int mode; +{ + if (!display) + return; + + if (mode < D_mousetrack) + mode = D_mousetrack; + + if (D_mouse != mode) + { + char mousebuf[20]; + if (!D_CXT) + return; + if (D_mouse) + { + sprintf(mousebuf, "\033[?%dl", D_mouse); + AddStr(mousebuf); + } + if (mode) + { + sprintf(mousebuf, "\033[?%dh", mode); + AddStr(mousebuf); + } + D_mouse = mode; + D_mouse_parse.state = CSI_INACTIVE; + } +} + +void +ExtMouseMode(mode) + int mode; +{ + if (display && D_extmouse != mode) + { + char mousebuf[20]; + if (!D_CXT) + return; + if (D_extmouse) + { + sprintf(mousebuf, "\033[?%dl", D_extmouse); + AddStr(mousebuf); + } + if (mode) + { + sprintf(mousebuf, "\033[?%dh", mode); + AddStr(mousebuf); + } + D_extmouse = mode; + D_mouse_parse.state = CSI_INACTIVE; + } +} + +static int StrCost; + +/* ARGSUSED */ +static int +CountChars(c) +int c; +{ + StrCost++; + return c; +} + +int +CalcCost(s) +register char *s; +{ + ASSERT(display); + if (s) + { + StrCost = 0; + ospeed = D_dospeed; + tputs(s, 1, CountChars); + return StrCost; + } + else + return EXPENSIVE; +} + +static int +CallRewrite(y, xs, xe, doit) +int y, xs, xe, doit; +{ + struct canvas *cv, *cvlist, *cvlnext; + struct viewport *vp; + struct layer *oldflayer; + int cost; + + debug3("CallRewrite %d %d %d\n", y, xs, xe); + ASSERT(display); + ASSERT(xe >= xs); + + vp = 0; + for (cv = D_cvlist; cv; cv = cv->c_next) + { + if (y < cv->c_ys || y > cv->c_ye || xe < cv->c_xs || xs > cv->c_xe) + continue; + for (vp = cv->c_vplist; vp; vp = vp->v_next) + if (y >= vp->v_ys && y <= vp->v_ye && xe >= vp->v_xs && xs <= vp->v_xe) + break; + if (vp) + break; + } + if (doit) + { + oldflayer = flayer; + flayer = cv->c_layer; + cvlist = flayer->l_cvlist; + cvlnext = cv->c_lnext; + flayer->l_cvlist = cv; + cv->c_lnext = 0; + LayRewrite(y - vp->v_yoff, xs - vp->v_xoff, xe - vp->v_xoff, &D_rend, 1); + flayer->l_cvlist = cvlist; + cv->c_lnext = cvlnext; + flayer = oldflayer; + return 0; + } + if (cv == 0 || cv->c_layer == 0) + return EXPENSIVE; /* not found or nothing on it */ + if (xs < vp->v_xs || xe > vp->v_xe) + return EXPENSIVE; /* crosses viewport boundaries */ + if (y - vp->v_yoff < 0 || y - vp->v_yoff >= cv->c_layer->l_height) + return EXPENSIVE; /* line not on layer */ + if (xs - vp->v_xoff < 0 || xe - vp->v_xoff >= cv->c_layer->l_width) + return EXPENSIVE; /* line not on layer */ +#ifdef UTF8 + if (D_encoding == UTF8) + D_rend.font = 0; +#endif + oldflayer = flayer; + flayer = cv->c_layer; + debug3("Calling Rewrite %d %d %d\n", y - vp->v_yoff, xs - vp->v_xoff, xe - vp->v_xoff); + cost = LayRewrite(y - vp->v_yoff, xs - vp->v_xoff, xe - vp->v_xoff, &D_rend, 0); + flayer = oldflayer; + if (D_insert) + cost += D_EIcost + D_IMcost; + return cost; +} + + +void +GotoPos(x2, y2) +int x2, y2; +{ + register int dy, dx, x1, y1; + register int costx, costy; + register int m; + register char *s; + int CMcost; + enum move_t xm = M_NONE, ym = M_NONE; + + if (!display) + return; + + x1 = D_x; + y1 = D_y; + + if (x1 == D_width) + { + if (D_CLP && D_AM) + x1 = -1; /* don't know how the terminal treats this */ + else + x1--; + } + if (x2 == D_width) + x2--; + dx = x2 - x1; + dy = y2 - y1; + if (dy == 0 && dx == 0) + return; + debug2("GotoPos (%d,%d)", x1, y1); + debug2(" -> (%d,%d)\n", x2, y2); + if (!D_MS) /* Safe to move ? */ + SetRendition(&mchar_null); + if (y1 < 0 /* don't know the y position */ + || (y2 > D_bot && y1 <= D_bot) /* have to cross border */ + || (y2 < D_top && y1 >= D_top)) /* of scrollregion ? */ + { + DoCM: + if (D_HO && !x2 && !y2) + AddCStr(D_HO); + else + AddCStr(tgoto(D_CM, x2, y2)); + D_x = x2; + D_y = y2; + return; + } + + /* some scrollregion implementations don't allow movements + * away from the region. sigh. + */ + if ((y1 > D_bot && y2 > y1) || (y1 < D_top && y2 < y1)) + goto DoCM; + + /* Calculate CMcost */ + if (D_HO && !x2 && !y2) + s = D_HO; + else + s = tgoto(D_CM, x2, y2); + CMcost = CalcCost(s); + + /* Calculate the cost to move the cursor to the right x position */ + costx = EXPENSIVE; + if (x1 >= 0) /* relativ x positioning only if we know where we are */ + { + if (dx > 0) + { + if (D_CRI && (dx > 1 || !D_ND)) + { + costx = CalcCost(tgoto(D_CRI, 0, dx)); + xm = M_CRI; + } + if ((m = D_NDcost * dx) < costx) + { + costx = m; + xm = M_RI; + } + /* Speedup: dx <= LayRewrite() */ + if (dx < costx && (m = CallRewrite(y1, x1, x2 - 1, 0)) < costx) + { + costx = m; + xm = M_RW; + } + } + else if (dx < 0) + { + if (D_CLE && (dx < -1 || !D_BC)) + { + costx = CalcCost(tgoto(D_CLE, 0, -dx)); + xm = M_CLE; + } + if ((m = -dx * D_LEcost) < costx) + { + costx = m; + xm = M_LE; + } + } + else + costx = 0; + } + /* Speedup: LayRewrite() >= x2 */ + if (x2 + D_CRcost < costx && (m = (x2 ? CallRewrite(y1, 0, x2 - 1, 0) : 0) + D_CRcost) < costx) + { + costx = m; + xm = M_CR; + } + + /* Check if it is already cheaper to do CM */ + if (costx >= CMcost) + goto DoCM; + + /* Calculate the cost to move the cursor to the right y position */ + costy = EXPENSIVE; + if (dy > 0) + { + if (D_CDO && dy > 1) /* DO & NL are always != 0 */ + { + costy = CalcCost(tgoto(D_CDO, 0, dy)); + ym = M_CDO; + } + if ((m = dy * ((x2 == 0) ? D_NLcost : D_DOcost)) < costy) + { + costy = m; + ym = M_DO; + } + } + else if (dy < 0) + { + if (D_CUP && (dy < -1 || !D_UP)) + { + costy = CalcCost(tgoto(D_CUP, 0, -dy)); + ym = M_CUP; + } + if ((m = -dy * D_UPcost) < costy) + { + costy = m; + ym = M_UP; + } + } + else + costy = 0; + + /* Finally check if it is cheaper to do CM */ + if (costx + costy >= CMcost) + goto DoCM; + + switch (xm) + { + case M_LE: + while (dx++ < 0) + AddCStr(D_BC); + break; + case M_CLE: + AddCStr2(D_CLE, -dx); + break; + case M_RI: + while (dx-- > 0) + AddCStr(D_ND); + break; + case M_CRI: + AddCStr2(D_CRI, dx); + break; + case M_CR: + AddCStr(D_CR); + D_x = 0; + x1 = 0; + /* FALLTHROUGH */ + case M_RW: + if (x1 < x2) + (void) CallRewrite(y1, x1, x2 - 1, 1); + break; + default: + break; + } + + switch (ym) + { + case M_UP: + while (dy++ < 0) + AddCStr(D_UP); + break; + case M_CUP: + AddCStr2(D_CUP, -dy); + break; + case M_DO: + s = (x2 == 0) ? D_NL : D_DO; + while (dy-- > 0) + AddCStr(s); + break; + case M_CDO: + AddCStr2(D_CDO, dy); + break; + default: + break; + } + D_x = x2; + D_y = y2; +} + +void +ClearAll() +{ + ASSERT(display); + ClearArea(0, 0, 0, D_width - 1, D_width - 1, D_height - 1, 0, 0); +} + +void +ClearArea(x1, y1, xs, xe, x2, y2, bce, uselayfn) +int x1, y1, xs, xe, x2, y2, bce, uselayfn; +{ + int y, xxe; + struct canvas *cv; + struct viewport *vp; + + debug2("Clear %d,%d", x1, y1); + debug2(" %d-%d", xs, xe); + debug2(" %d,%d", x2, y2); + debug2(" uselayfn=%d bce=%d\n", uselayfn, bce); + ASSERT(display); + if (x1 == D_width) + x1--; + if (x2 == D_width) + x2--; + if (xs == -1) + xs = x1; + if (xe == -1) + xe = x2; + if (D_UT) /* Safe to erase ? */ + SetRendition(&mchar_null); +#ifdef COLOR + if (D_BE) + SetBackColor(bce); +#endif + if (D_lp_missing && y1 <= D_bot && xe >= D_width - 1) + { + if (y2 > D_bot || (y2 == D_bot && x2 >= D_width - 1)) + D_lp_missing = 0; + } + if (x2 == D_width - 1 && (xs == 0 || y1 == y2) && xe == D_width - 1 && y2 == D_height - 1 && (!bce || D_BE)) + { +#ifdef AUTO_NUKE + if (x1 == 0 && y1 == 0 && D_auto_nuke) + NukePending(); +#endif + if (x1 == 0 && y1 == 0 && D_CL) + { + AddCStr(D_CL); + D_y = D_x = 0; + return; + } + /* + * Workaround a hp700/22 terminal bug. Do not use CD where CE + * is also appropriate. + */ + if (D_CD && (y1 < y2 || !D_CE)) + { + GotoPos(x1, y1); + AddCStr(D_CD); + return; + } + } + if (x1 == 0 && xs == 0 && (xe == D_width - 1 || y1 == y2) && y1 == 0 && D_CCD && (!bce || D_BE)) + { + GotoPos(x1, y1); + AddCStr(D_CCD); + return; + } + xxe = xe; + for (y = y1; y <= y2; y++, x1 = xs) + { + if (y == y2) + xxe = x2; + if (x1 == 0 && D_CB && (xxe != D_width - 1 || (D_x == xxe && D_y == y)) && (!bce || D_BE)) + { + GotoPos(xxe, y); + AddCStr(D_CB); + continue; + } + if (xxe == D_width - 1 && D_CE && (!bce || D_BE)) + { + GotoPos(x1, y); + AddCStr(D_CE); + continue; + } + if (uselayfn) + { + vp = 0; + for (cv = D_cvlist; cv; cv = cv->c_next) + { + if (y < cv->c_ys || y > cv->c_ye || xxe < cv->c_xs || x1 > cv->c_xe) + continue; + for (vp = cv->c_vplist; vp; vp = vp->v_next) + if (y >= vp->v_ys && y <= vp->v_ye && xxe >= vp->v_xs && x1 <= vp->v_xe) + break; + if (vp) + break; + } + if (cv && cv->c_layer && x1 >= vp->v_xs && xxe <= vp->v_xe && + y - vp->v_yoff >= 0 && y - vp->v_yoff < cv->c_layer->l_height && + xxe - vp->v_xoff >= 0 && x1 - vp->v_xoff < cv->c_layer->l_width) + { + struct layer *oldflayer = flayer; + struct canvas *cvlist, *cvlnext; + flayer = cv->c_layer; + cvlist = flayer->l_cvlist; + cvlnext = cv->c_lnext; + flayer->l_cvlist = cv; + cv->c_lnext = 0; + LayClearLine(y - vp->v_yoff, x1 - vp->v_xoff, xxe - vp->v_xoff, bce); + flayer->l_cvlist = cvlist; + cv->c_lnext = cvlnext; + flayer = oldflayer; + continue; + } + } + ClearLine((struct mline *)0, y, x1, xxe, bce); + } +} + + +/* + * if cur_only > 0, we only redisplay current line, as a full refresh is + * too expensive over a low baud line. + */ +void +Redisplay(cur_only) +int cur_only; +{ + ASSERT(display); + + /* XXX do em all? */ + InsertMode(0); + ChangeScrollRegion(0, D_height - 1); + KeypadMode(0); + CursorkeysMode(0); + CursorVisibility(0); + MouseMode(0); + ExtMouseMode(0); + SetRendition(&mchar_null); + SetFlow(FLOW_NOW); + + ClearAll(); +#ifdef RXVT_OSC + RefreshXtermOSC(); +#endif + if (cur_only > 0 && D_fore) + RefreshArea(0, D_fore->w_y, D_width - 1, D_fore->w_y, 1); + else + RefreshAll(1); + RefreshHStatus(); + CV_CALL(D_forecv, LayRestore();LaySetCursor()); +} + +void +RedisplayDisplays(cur_only) +int cur_only; +{ + struct display *olddisplay = display; + for (display = displays; display; display = display->d_next) + Redisplay(cur_only); + display = olddisplay; +} + + +/* XXX: use oml! */ +void +ScrollH(y, xs, xe, n, bce, oml) +int y, xs, xe, n, bce; +struct mline *oml; +{ + int i; + + if (n == 0) + return; + if (xe != D_width - 1) + { + RefreshLine(y, xs, xe, 0); + /* UpdateLine(oml, y, xs, xe); */ + return; + } + GotoPos(xs, y); + if (D_UT) + SetRendition(&mchar_null); +#ifdef COLOR + if (D_BE) + SetBackColor(bce); +#endif + if (n > 0) + { + if (n >= xe - xs + 1) + n = xe - xs + 1; + if (D_CDC && !(n == 1 && D_DC)) + AddCStr2(D_CDC, n); + else if (D_DC) + { + for (i = n; i--; ) + AddCStr(D_DC); + } + else + { + RefreshLine(y, xs, xe, 0); + /* UpdateLine(oml, y, xs, xe); */ + return; + } + } + else + { + if (-n >= xe - xs + 1) + n = -(xe - xs + 1); + if (!D_insert) + { + if (D_CIC && !(n == -1 && D_IC)) + AddCStr2(D_CIC, -n); + else if (D_IC) + { + for (i = -n; i--; ) + AddCStr(D_IC); + } + else if (D_IM) + { + InsertMode(1); + SetRendition(&mchar_null); +#ifdef COLOR + SetBackColor(bce); +#endif + for (i = -n; i--; ) + INSERTCHAR(' '); + bce = 0; /* all done */ + } + else + { + /* UpdateLine(oml, y, xs, xe); */ + RefreshLine(y, xs, xe, 0); + return; + } + } + else + { + SetRendition(&mchar_null); +#ifdef COLOR + SetBackColor(bce); +#endif + for (i = -n; i--; ) + INSERTCHAR(' '); + bce = 0; /* all done */ + } + } + if (bce && !D_BE) + { + if (n > 0) + ClearLine((struct mline *)0, y, xe - n + 1, xe, bce); + else + ClearLine((struct mline *)0, y, xs, xs - n - 1, bce); + } + if (D_lp_missing && y == D_bot) + { + if (n > 0) + WriteLP(D_width - 1 - n, y); + D_lp_missing = 0; + } +} + +void +ScrollV(xs, ys, xe, ye, n, bce) +int xs, ys, xe, ye, n, bce; +{ + int i; + int up; + int oldbot; + int alok, dlok, aldlfaster; + int missy = 0; + + ASSERT(display); + if (n == 0) + return; + if (n >= ye - ys + 1 || -n >= ye - ys + 1) + { + ClearArea(xs, ys, xs, xe, xe, ye, bce, 0); + return; + } + if (xs > D_vpxmin || xe < D_vpxmax) + { + RefreshArea(xs, ys, xe, ye, 0); + return; + } + + if (D_lp_missing) + { + if (D_bot > ye || D_bot < ys) + missy = D_bot; + else + { + missy = D_bot - n; + if (missy > ye || missy < ys) + D_lp_missing = 0; + } + } + + up = 1; + if (n < 0) + { + up = 0; + n = -n; + } + if (n >= ye - ys + 1) + n = ye - ys + 1; + + oldbot = D_bot; + if (ys < D_top || D_bot != ye) + ChangeScrollRegion(ys, ye); + alok = (D_AL || D_CAL || (ys >= D_top && ye == D_bot && up)); + dlok = (D_DL || D_CDL || (ys >= D_top && ye == D_bot && !up)); + if (D_top != ys && !(alok && dlok)) + ChangeScrollRegion(ys, ye); + + if (D_lp_missing && + (oldbot != D_bot || + (oldbot == D_bot && up && D_top == ys && D_bot == ye))) + { + WriteLP(D_width - 1, oldbot); + if (oldbot == D_bot) /* have scrolled */ + { + if (--n == 0) + { +/* XXX + ChangeScrollRegion(oldtop, oldbot); +*/ + if (bce && !D_BE) + ClearLine((struct mline *)0, ye, xs, xe, bce); + return; + } + } + } + + if (D_UT) + SetRendition(&mchar_null); +#ifdef COLOR + if (D_BE) + SetBackColor(bce); +#endif + + aldlfaster = (n > 1 && ys >= D_top && ye == D_bot && ((up && D_CDL) || (!up && D_CAL))); + + if ((up || D_SR) && D_top == ys && D_bot == ye && !aldlfaster) + { + if (up) + { + GotoPos(0, ye); + for(i = n; i-- > 0; ) + AddCStr(D_NL); /* was SF, I think NL is faster */ + } + else + { + GotoPos(0, ys); + for(i = n; i-- > 0; ) + AddCStr(D_SR); + } + } + else if (alok && dlok) + { + if (up || ye != D_bot) + { + GotoPos(0, up ? ys : ye+1-n); + if (D_CDL && !(n == 1 && D_DL)) + AddCStr2(D_CDL, n); + else + for(i = n; i--; ) + AddCStr(D_DL); + } + if (!up || ye != D_bot) + { + GotoPos(0, up ? ye+1-n : ys); + if (D_CAL && !(n == 1 && D_AL)) + AddCStr2(D_CAL, n); + else + for(i = n; i--; ) + AddCStr(D_AL); + } + } + else + { + RefreshArea(xs, ys, xe, ye, 0); + return; + } + if (bce && !D_BE) + { + if (up) + ClearArea(xs, ye - n + 1, xs, xe, xe, ye, bce, 0); + else + ClearArea(xs, ys, xs, xe, xe, ys + n - 1, bce, 0); + } + if (D_lp_missing && missy != D_bot) + WriteLP(D_width - 1, missy); +/* XXX + ChangeScrollRegion(oldtop, oldbot); + if (D_lp_missing && missy != D_bot) + WriteLP(D_width - 1, missy); +*/ +} + +void +SetAttr(new) +register int new; +{ + register int i, j, old, typ; + + if (!display || (old = D_rend.attr) == new) + return; +#ifdef COLORS16 + D_col16change = (old ^ new) & (A_BFG | A_BBG); + new ^= D_col16change; + if (old == new) + return; +#endif +#if defined(TERMINFO) && defined(USE_SGR) + if (D_SA) + { + char *tparm(); + SetFont(ASCII); + ospeed = D_dospeed; + tputs(tparm(D_SA, new & A_SO, new & A_US, new & A_RV, new & A_BL, + new & A_DI, new & A_BD, 0 , 0 , + 0), 1, DoAddChar); + D_rend.attr = new; + D_atyp = 0; +# ifdef COLOR + if (D_hascolor) + rend_setdefault(&D_rend); +# endif + return; + } +#endif + D_rend.attr = new; + typ = D_atyp; + if ((new & old) != old) + { + if ((typ & ATYP_U)) + AddCStr(D_UE); + if ((typ & ATYP_S)) + AddCStr(D_SE); + if ((typ & ATYP_M)) + { + AddCStr(D_ME); +#ifdef COLOR + /* ansi attrib handling: \E[m resets color, too */ + if (D_hascolor) + rend_setdefault(&D_rend); +#endif +#ifdef FONT + if (!D_CG0) + { + /* D_ME may also reset the alternate charset */ + D_rend.font = 0; +# ifdef ENCODINGS + D_realfont = 0; +# endif + } +#endif + } + old = 0; + typ = 0; + } + old ^= new; + for (i = 0, j = 1; old && i < NATTR; i++, j <<= 1) + { + if ((old & j) == 0) + continue; + old ^= j; + if (D_attrtab[i]) + { + AddCStr(D_attrtab[i]); + typ |= D_attrtyp[i]; + } + } + D_atyp = typ; +} + +#ifdef FONT +void +SetFont(new) +int new; +{ + int old = D_rend.font; + if (!display || old == new) + return; + D_rend.font = new; +#ifdef ENCODINGS + if (D_encoding && CanEncodeFont(D_encoding, new)) + return; + if (new == D_realfont) + return; + D_realfont = new; +#endif + if (D_xtable && D_xtable[(int)(unsigned char)new] && + D_xtable[(int)(unsigned char)new][256]) + { + AddCStr(D_xtable[(int)(unsigned char)new][256]); + return; + } + + if (!D_CG0 && new != '0') + { + new = ASCII; + if (old == new) + return; + } + + if (new == ASCII) + AddCStr(D_CE0); +#ifdef DW_CHARS + else if (new < ' ') + { + AddStr("\033$"); + if (new > 2) + AddChar('('); + AddChar(new + '@'); + } +#endif + else + AddCStr2(D_CS0, new); +} +#endif + +#ifdef COLOR + +int +color256to16(jj) +int jj; +{ + int min, max; + int r, g, b; + + if (jj >= 232) + { + jj = (jj - 232) / 6; + jj = (jj & 1) << 3 | (jj & 2 ? 7 : 0); + } + else if (jj >= 16) + { + jj -= 16; + r = jj / 36; + g = (jj / 6) % 6; + b = jj % 6; + min = r < g ? (r < b ? r : b) : (g < b ? g : b); + max = r > g ? (r > b ? r : b) : (g > b ? g : b); + if (min == max) + jj = ((max + 1) & 2) << 2 | ((max + 1) & 4 ? 7 : 0); + else + jj = (b - min) / (max - min) << 2 | (g - min) / (max - min) << 1 | (r - +min) / (max - min) | (max > 3 ? 8 : 0); + } + return jj; +} + +#ifdef COLORS256 +int +color256to88(jj) +int jj; +{ + int r, g, b; + + if (jj >= 232) + return (jj - 232) / 3 + 80; + if (jj >= 16) + { + jj -= 16; + r = jj / 36; + g = (jj / 6) % 6; + b = jj % 6; + return ((r + 1) / 2) * 16 + ((g + 1) / 2) * 4 + ((b + 1) / 2) + 16; + } + return jj; +} +#endif + +void +SetColor(f, b) +int f, b; +{ + int of, ob; + static unsigned char sftrans[8] = {0,4,2,6,1,5,3,7}; + + if (!display) + return; + + of = rend_getfg(&D_rend); + ob = rend_getbg(&D_rend); + +#ifdef COLORS16 + /* intense default not invented yet */ + if (f == 0x100) + f = 0; + if (b == 0x100) + b = 0; +#endif + debug2("SetColor %d %d", coli2e(of), coli2e(ob)); + debug2(" -> %d %d\n", coli2e(f), coli2e(b)); + debug2("(%d %d", of, ob); + debug2(" -> %d %d)\n", f, b); + + if (!D_CAX && D_hascolor && ((f == 0 && f != of) || (b == 0 && b != ob))) + { + if (D_OP) + AddCStr(D_OP); + else + { + int oattr; + oattr = D_rend.attr; + AddCStr(D_ME ? D_ME : "\033[m"); +#ifdef FONT + if (D_ME && !D_CG0) + { + /* D_ME may also reset the alternate charset */ + D_rend.font = 0; +# ifdef ENCODINGS + D_realfont = 0; +# endif + } +#endif + D_atyp = 0; + D_rend.attr = 0; + SetAttr(oattr); + } + of = ob = 0; + } + rend_setfg(&D_rend, f); + rend_setbg(&D_rend, b); +#ifdef COLORS16 + D_col16change = 0; +#endif + if (!D_hascolor) + return; + f = f ? coli2e(f) : -1; + b = b ? coli2e(b) : -1; + of = of ? coli2e(of) : -1; + ob = ob ? coli2e(ob) : -1; +#ifdef COLORS256 + if (f != of && f > 15 && D_CCO != 256) + f = D_CCO == 88 && D_CAF ? color256to88(f) : color256to16(f); + if (f != of && f > 15 && D_CAF) + { + AddCStr2(D_CAF, f); + of = f; + } + if (b != ob && b > 15 && D_CCO != 256) + b = D_CCO == 88 && D_CAB ? color256to88(b) : color256to16(b); + if (b != ob && b > 15 && D_CAB) + { + AddCStr2(D_CAB, b); + ob = b; + } +#endif + if (f != of && f != (of | 8)) + { + if (f == -1) + AddCStr("\033[39m"); /* works because AX is set */ + else if (D_CAF) + AddCStr2(D_CAF, f & 7); + else if (D_CSF) + AddCStr2(D_CSF, sftrans[f & 7]); + } + if (b != ob && b != (ob | 8)) + { + if (b == -1) + AddCStr("\033[49m"); /* works because AX is set */ + else if (D_CAB) + AddCStr2(D_CAB, b & 7); + else if (D_CSB) + AddCStr2(D_CSB, sftrans[b & 7]); + } +#ifdef COLORS16 + if (f != of && D_CXT && (f & 8) != 0 && f != -1) + { +# ifdef TERMINFO + AddCStr2("\033[9%p1%dm", f & 7); +# else + AddCStr2("\033[9%dm", f & 7); +# endif + } + if (b != ob && D_CXT && (b & 8) != 0 && b != -1) + { +# ifdef TERMINFO + AddCStr2("\033[10%p1%dm", b & 7); +# else + AddCStr2("\033[10%dm", b & 7); +# endif + } +#endif +} + +static void +SetBackColor(new) +int new; +{ + if (!display) + return; + SetColor(rend_getfg(&D_rend), new); +} +#endif /* COLOR */ + +void +SetRendition(mc) +struct mchar *mc; +{ + if (!display) + return; +#ifdef COLOR + if (nattr2color && D_hascolor && (mc->attr & nattr2color) != 0) + { + static struct mchar mmc; + int i; + mmc = *mc; + for (i = 0; i < 8; i++) + if (attr2color[i] && (mc->attr & (1 << i)) != 0) + { + if (mc->color == 0 && attr2color[i][3]) + ApplyAttrColor(attr2color[i][3], &mmc); + else if ((mc->color & 0x0f) == 0 && attr2color[i][2]) + ApplyAttrColor(attr2color[i][2], &mmc); + else if ((mc->color & 0xf0) == 0 && attr2color[i][1]) + ApplyAttrColor(attr2color[i][1], &mmc); + else + ApplyAttrColor(attr2color[i][0], &mmc); + } + mc = &mmc; + debug2("SetRendition: mapped to %02x %02x\n", (unsigned char)mc->attr, 0x99 - (unsigned char)mc->color); + } +# ifdef COLORS16 + if (D_hascolor && D_CC8 && (mc->attr & (A_BFG|A_BBG))) + { + int a = mc->attr; + if ((mc->attr & A_BFG) && D_MD) + a |= A_BD; + if ((mc->attr & A_BBG) && D_MB) + a |= A_BL; + if (D_rend.attr != a) + SetAttr(a); + } + else +# endif /* COLORS16 */ +#endif /* COLOR */ + if (D_rend.attr != mc->attr) + SetAttr(mc->attr); + +#ifdef COLOR + if (D_rend.color != mc->color +# ifdef COLORS256 + || D_rend.colorx != mc->colorx +# endif +# ifdef COLORS16 + || D_col16change +# endif + ) + SetColor(rend_getfg(mc), rend_getbg(mc)); +#endif +#ifdef FONT + if (D_rend.font != mc->font) + SetFont(mc->font); +#ifdef UTF8 + if (D_encoding == UTF8) + D_rend.fontx = mc->fontx; +#endif +#endif +} + +void +SetRenditionMline(ml, x) +struct mline *ml; +int x; +{ + if (!display) + return; +#ifdef COLOR + if (nattr2color && D_hascolor && (ml->attr[x] & nattr2color) != 0) + { + struct mchar mc; + copy_mline2mchar(&mc, ml, x); + SetRendition(&mc); + return; + } +# ifdef COLORS16 + if (D_hascolor && D_CC8 && (ml->attr[x] & (A_BFG|A_BBG))) + { + int a = ml->attr[x]; + if ((ml->attr[x] & A_BFG) && D_MD) + a |= A_BD; + if ((ml->attr[x] & A_BBG) && D_MB) + a |= A_BL; + if (D_rend.attr != a) + SetAttr(a); + } + else +# endif /* COLORS16 */ +#endif /* COLOR */ + if (D_rend.attr != ml->attr[x]) + SetAttr(ml->attr[x]); +#ifdef COLOR + if (D_rend.color != ml->color[x] +# ifdef COLORS256 + || D_rend.colorx != ml->colorx[x] +# endif +# ifdef COLORS16 + || D_col16change +# endif + ) + { + struct mchar mc; + copy_mline2mchar(&mc, ml, x); + SetColor(rend_getfg(&mc), rend_getbg(&mc)); + } +#endif +#ifdef FONT + if (D_rend.font != ml->font[x]) + SetFont(ml->font[x]); +#ifdef UTF8 + if (D_encoding == UTF8) + D_rend.fontx = ml->fontx[x]; +#endif +#endif +} + +void +MakeStatus(msg) +char *msg; +{ + register char *s, *t; + register int max; + + if (!display) + return; + + if (D_blocked) + return; + if (!D_tcinited) + { + debug("tc not inited, just writing msg\n"); + if (D_processinputdata) + return; /* XXX: better */ + AddStr(msg); + AddStr("\r\n"); + Flush(0); + return; + } + if (!use_hardstatus || !D_HS) + { + max = D_width; + if (D_CLP == 0) + max--; + } + else + max = D_WS > 0 ? D_WS : (D_width - !D_CLP); + if (D_status) + { + /* same message? */ + if (strcmp(msg, D_status_lastmsg) == 0) + { + debug("same message - increase timeout"); + if (!D_status_obufpos) + SetTimeout(&D_statusev, MsgWait); + return; + } + RemoveStatusMinWait(); + } + for (s = t = msg; *s && t - msg < max; ++s) + if (*s == BELL) + AddCStr(D_BL); + else if ((unsigned char)*s >= ' ' && *s != 0177) + *t++ = *s; + *t = '\0'; + if (t == msg) + return; + if (t - msg >= D_status_buflen) + { + char *buf; + if (D_status_lastmsg) + buf = realloc(D_status_lastmsg, t - msg + 1); + else + buf = malloc(t - msg + 1); + if (buf) + { + D_status_lastmsg = buf; + D_status_buflen = t - msg + 1; + } + } + if (t - msg < D_status_buflen) + strcpy(D_status_lastmsg, msg); + D_status_len = t - msg; + D_status_lastx = D_x; + D_status_lasty = D_y; + if (!use_hardstatus || D_has_hstatus == HSTATUS_IGNORE || D_has_hstatus == HSTATUS_MESSAGE) + { + D_status = STATUS_ON_WIN; + debug1("using STATLINE %d\n", STATLINE); + GotoPos(0, STATLINE); + SetRendition(&mchar_so); + InsertMode(0); + AddStr(msg); + if (D_status_len < max) + { + /* Wayne Davison: add extra space for readability */ + D_status_len++; + SetRendition(&mchar_null); + AddChar(' '); + if (D_status_len < max) + { + D_status_len++; + AddChar(' '); + AddChar('\b'); + } + AddChar('\b'); + } + D_x = -1; + } + else + { + D_status = STATUS_ON_HS; + ShowHStatus(msg); + } + + D_status_obufpos = D_obufp - D_obuf; + ASSERT(D_status_obufpos > 0); + + if (D_status == STATUS_ON_WIN) + { + struct display *olddisplay = display; + struct layer *oldflayer = flayer; + + /* this is copied over from RemoveStatus() */ + D_status = 0; + GotoPos(0, STATLINE); + RefreshLine(STATLINE, 0, D_status_len - 1, 0); + GotoPos(D_status_lastx, D_status_lasty); + flayer = D_forecv ? D_forecv->c_layer : 0; + if (flayer) + LaySetCursor(); + display = olddisplay; + flayer = oldflayer; + D_status = STATUS_ON_WIN; + } +} + +void +RemoveStatus() +{ + struct display *olddisplay; + struct layer *oldflayer; + int where; + + if (!display) + return; + if (!(where = D_status)) + return; + + debug("RemoveStatus\n"); + if (D_status_obuffree >= 0) + { + D_obuflen = D_status_obuflen; + D_obuffree = D_status_obuffree; + D_status_obuffree = -1; + } + D_status = 0; + D_status_obufpos = 0; + D_status_bell = 0; + evdeq(&D_statusev); + olddisplay = display; + oldflayer = flayer; + if (where == STATUS_ON_WIN) + { + if (captionalways || (D_canvas.c_slperp && D_canvas.c_slperp->c_slnext)) + { + GotoPos(0, STATLINE); + RefreshLine(STATLINE, 0, D_status_len - 1, 0); + GotoPos(D_status_lastx, D_status_lasty); + } + } + else + RefreshHStatus(); + flayer = D_forecv ? D_forecv->c_layer : 0; + if (flayer) + LaySetCursor(); + display = olddisplay; + flayer = oldflayer; +} + +/* Remove the status but make sure that it is seen for MsgMinWait ms */ +static void +RemoveStatusMinWait() +{ + /* XXX: should flush output first if D_status_obufpos is set */ + if (!D_status_bell && !D_status_obufpos) + { + struct timeval now; + int ti; + gettimeofday(&now, NULL); + ti = (now.tv_sec - D_status_time.tv_sec) * 1000 + (now.tv_usec - D_status_time.tv_usec) / 1000; + if (ti < MsgMinWait) + DisplaySleep1000(MsgMinWait - ti, 0); + } + RemoveStatus(); +} + +#ifdef UTF8 +static int +strlen_onscreen(unsigned char *c, unsigned char *end) +{ + int len = 0; + while (*c && (!end || c < end)) + { + int v, dec = 0; + do + { + v = FromUtf8(*c++, &dec); + if (v == -2) + c--; + } + while (v < 0 && (!end || c < end)); + if (!utf8_iscomb(v)) + { + if (utf8_isdouble(v)) + len++; + len++; + } + } + + return len; +} + +static int +PrePutWinMsg(s, start, max) +char *s; +int start, max; +{ + /* Avoid double-encoding problem for a UTF-8 message on a UTF-8 locale. + Ideally, this would not be necessary. But fixing it the Right Way will + probably take way more time. So this will have to do for now. */ + if (D_encoding == UTF8) + { + int chars = strlen_onscreen((unsigned char *)(s + start), (unsigned char *)(s + max)); + D_encoding = 0; + PutWinMsg(s, start, max + ((max - start) - chars)); /* Multibyte count */ + D_encoding = UTF8; + D_x -= (max - chars); /* Yak! But this is necessary to count for + the fact that not every byte represents a + character. */ + return start + chars; + } + else + { + PutWinMsg(s, start, max); + return max; + } +} +#else +static int +PrePutWinMsg(s, start, max) +char *s; +int start, max; +{ + PutWinMsg(s, start, max); + return max; +} +#endif + +/* refresh the display's hstatus line */ +void +ShowHStatus(str) +char *str; +{ + int l, ox, oy, max; + + if (D_status == STATUS_ON_WIN && D_has_hstatus == HSTATUS_LASTLINE && STATLINE == D_height-1) + return; /* sorry, in use */ + if (D_blocked) + return; + + if (D_HS && D_has_hstatus == HSTATUS_HS) + { + if (!D_hstatus && (str == 0 || *str == 0)) + return; + debug("ShowHStatus: using HS\n"); + SetRendition(&mchar_null); + InsertMode(0); + if (D_hstatus) + AddCStr(D_DS); + D_hstatus = 0; + if (str == 0 || *str == 0) + return; + AddCStr2(D_TS, 0); + max = D_WS > 0 ? D_WS : (D_width - !D_CLP); + if ((int)strlen(str) > max) + AddStrn(str, max); + else + AddStr(str); + AddCStr(D_FS); + D_hstatus = 1; + } + else if (D_has_hstatus == HSTATUS_LASTLINE) + { + debug("ShowHStatus: using last line\n"); + ox = D_x; + oy = D_y; + str = str ? str : ""; + l = strlen(str); + if (l > D_width) + l = D_width; + GotoPos(0, D_height - 1); + SetRendition(captionalways || D_cvlist == 0 || D_cvlist->c_next ? &mchar_null: &mchar_so); + l = PrePutWinMsg(str, 0, l); + if (!captionalways && D_cvlist && !D_cvlist->c_next) + while (l++ < D_width) + PUTCHARLP(' '); + if (l < D_width) + ClearArea(l, D_height - 1, l, D_width - 1, D_width - 1, D_height - 1, 0, 0); + if (ox != -1 && oy != -1) + GotoPos(ox, oy); + D_hstatus = *str ? 1 : 0; + SetRendition(&mchar_null); + } + else if (D_has_hstatus == HSTATUS_FIRSTLINE) + { + debug("ShowHStatus: using first line\n"); + ox = D_x; + oy = D_y; + str = str ? str : ""; + l = strlen(str); + if (l > D_width) + l = D_width; + GotoPos(0, 0); + SetRendition(captionalways || D_cvlist == 0 || D_cvlist->c_next ? &mchar_null: &mchar_so); + l = PrePutWinMsg(str, 0, l); + if (!captionalways || (D_cvlist && !D_cvlist->c_next)) + while (l++ < D_width) + PUTCHARLP(' '); + if (l < D_width) + ClearArea(l, 0, l, D_width - 1, D_width - 1, 0, 0, 0); + if (ox != -1 && oy != -1) + GotoPos(ox, oy); + D_hstatus = *str ? 1 : 0; + SetRendition(&mchar_null); + } + else if (str && *str && D_has_hstatus == HSTATUS_MESSAGE) + { + debug("ShowHStatus: using message\n"); + Msg(0, "%s", str); + } +} + + +/* + * Refreshes the harstatus of the fore window. Shouldn't be here... + */ +void +RefreshHStatus() +{ + char *buf; +#ifdef UTF8 + int extrabytes = strlen(hstatusstring) - strlen_onscreen((unsigned char *)hstatusstring, NULL); +#else + int extrabytes = 0; +#endif + evdeq(&D_hstatusev); + if (D_status == STATUS_ON_HS) + return; + buf = MakeWinMsgEv(hstatusstring, D_fore, '%', (D_HS && D_has_hstatus == HSTATUS_HS && D_WS > 0) ? D_WS : D_width - !D_CLP + extrabytes, &D_hstatusev, 0); + if (buf && *buf) + { + ShowHStatus(buf); + if (D_has_hstatus != HSTATUS_IGNORE && D_hstatusev.timeout.tv_sec) + evenq(&D_hstatusev); + } + else + ShowHStatus((char *)0); +} + +/*********************************************************************/ +/* + * Here come the routines that refresh an arbitrary part of the screen. + */ + +void +RefreshAll(isblank) +int isblank; +{ + struct canvas *cv; + + ASSERT(display); + debug("Signalling full refresh!\n"); + for (cv = D_cvlist; cv; cv = cv->c_next) + { + CV_CALL(cv, LayRedisplayLine(-1, -1, -1, isblank)); + display = cv->c_display; /* just in case! */ + } + RefreshArea(0, 0, D_width - 1, D_height - 1, isblank); +} + +void +RefreshArea(xs, ys, xe, ye, isblank) +int xs, ys, xe, ye, isblank; +{ + int y; + ASSERT(display); + debug2("Refresh Area: %d,%d", xs, ys); + debug3(" - %d,%d (isblank=%d)\n", xe, ye, isblank); + if (!isblank && xs == 0 && xe == D_width - 1 && ye == D_height - 1 && (ys == 0 || D_CD)) + { + ClearArea(xs, ys, xs, xe, xe, ye, 0, 0); + isblank = 1; + } + for (y = ys; y <= ye; y++) + RefreshLine(y, xs, xe, isblank); +} + +void +RefreshLine(y, from, to, isblank) +int y, from, to, isblank; +{ + struct viewport *vp, *lvp; + struct canvas *cv, *lcv, *cvlist, *cvlnext; + struct layer *oldflayer; + int xx, yy, l; + char *buf; + struct win *p; + + ASSERT(display); + + debug2("RefreshLine %d %d", y, from); + debug2(" %d %d\n", to, isblank); + + if (D_status == STATUS_ON_WIN && y == STATLINE) + { + if (to >= D_status_len) + D_status_len = to + 1; + return; /* can't refresh status */ + } + + if (isblank == 0 && D_CE && to == D_width - 1 && from < to && D_status != STATUS_ON_HS) + { + GotoPos(from, y); + if (D_UT || D_BE) + SetRendition(&mchar_null); + AddCStr(D_CE); + isblank = 1; + } + + if ((y == D_height - 1 && D_has_hstatus == HSTATUS_LASTLINE) || (y == 0 && D_has_hstatus == HSTATUS_FIRSTLINE) ) + { + RefreshHStatus(); + return; + } + + while (from <= to) + { + lcv = 0; + lvp = 0; + for (cv = display->d_cvlist; cv; cv = cv->c_next) + { + if (y == cv->c_ye + 1 && from >= cv->c_xs && from <= cv->c_xe) + { +#ifdef UTF8 + int extrabytes = strlen(captionstring) - strlen_onscreen((unsigned char *)captionstring, NULL); +#else + int extrabytes = 0; +#endif + p = Layer2Window(cv->c_layer); + buf = MakeWinMsgEv(captionstring, p, '%', cv->c_xe - cv->c_xs + (cv->c_xe + 1 < D_width || D_CLP) + extrabytes, &cv->c_captev, 0); + if (cv->c_captev.timeout.tv_sec) + evenq(&cv->c_captev); + xx = to > cv->c_xe ? cv->c_xe : to; + l = strlen(buf); + GotoPos(from, y); + SetRendition(&mchar_so); + if (l > xx - cv->c_xs + 1) + l = xx - cv->c_xs + 1; + l = PrePutWinMsg(buf, from - cv->c_xs, l + extrabytes); + from = cv->c_xs + l; + for (; from <= xx; from++) + PUTCHARLP(' '); + break; + } + if (from == cv->c_xe + 1 && y >= cv->c_ys && y <= cv->c_ye + 1) + { + GotoPos(from, y); + SetRendition(&mchar_so); + PUTCHARLP(' '); + from++; + break; + } + if (y < cv->c_ys || y > cv->c_ye || to < cv->c_xs || from > cv->c_xe) + continue; + debug2("- canvas hit: %d %d", cv->c_xs, cv->c_ys); + debug2(" %d %d\n", cv->c_xe, cv->c_ye); + for (vp = cv->c_vplist; vp; vp = vp->v_next) + { + debug2(" - vp: %d %d", vp->v_xs, vp->v_ys); + debug2(" %d %d\n", vp->v_xe, vp->v_ye); + /* find leftmost overlapping vp */ + if (y >= vp->v_ys && y <= vp->v_ye && from <= vp->v_xe && to >= vp->v_xs && (lvp == 0 || lvp->v_xs > vp->v_xs)) + { + lcv = cv; + lvp = vp; + } + } + } + if (cv) + continue; /* we advanced from */ + if (lvp == 0) + break; + if (from < lvp->v_xs) + { + if (!isblank) + DisplayLine(&mline_null, &mline_blank, y, from, lvp->v_xs - 1); + from = lvp->v_xs; + } + + /* call LayRedisplayLine on canvas lcv viewport lvp */ + yy = y - lvp->v_yoff; + xx = to < lvp->v_xe ? to : lvp->v_xe; + + if (lcv->c_layer && lcv->c_xoff + lcv->c_layer->l_width == from) + { + GotoPos(from, y); + SetRendition(&mchar_blank); + PUTCHARLP('|'); + from++; + } + if (lcv->c_layer && yy == lcv->c_layer->l_height) + { + GotoPos(from, y); + SetRendition(&mchar_blank); + while (from <= lvp->v_xe && from - lvp->v_xoff < lcv->c_layer->l_width) + { + PUTCHARLP('-'); + from++; + } + if (from >= lvp->v_xe + 1) + continue; + } + if (lcv->c_layer == 0 || yy >= lcv->c_layer->l_height || from - lvp->v_xoff >= lcv->c_layer->l_width) + { + if (!isblank) + DisplayLine(&mline_null, &mline_blank, y, from, lvp->v_xe); + from = lvp->v_xe + 1; + continue; + } + + if (xx - lvp->v_xoff >= lcv->c_layer->l_width) + xx = lcv->c_layer->l_width + lvp->v_xoff - 1; + oldflayer = flayer; + flayer = lcv->c_layer; + cvlist = flayer->l_cvlist; + cvlnext = lcv->c_lnext; + flayer->l_cvlist = lcv; + lcv->c_lnext = 0; + LayRedisplayLine(yy, from - lvp->v_xoff, xx - lvp->v_xoff, isblank); + flayer->l_cvlist = cvlist; + lcv->c_lnext = cvlnext; + flayer = oldflayer; + + from = xx + 1; + } + if (!isblank && from <= to) + DisplayLine(&mline_null, &mline_blank, y, from, to); +} + +/*********************************************************************/ + +/* clear lp_missing by writing the char on the screen. The + * position must be safe. + */ +static void +WriteLP(x2, y2) +int x2, y2; +{ + struct mchar oldrend; + + ASSERT(display); + ASSERT(D_lp_missing); + oldrend = D_rend; + debug2("WriteLP(%d,%d)\n", x2, y2); +#ifdef DW_CHARS + if (D_lpchar.mbcs) + { + if (x2 > 0) + x2--; + else + D_lpchar = mchar_blank; + } +#endif + /* Can't use PutChar */ + GotoPos(x2, y2); + SetRendition(&D_lpchar); + PUTCHAR(D_lpchar.image); +#ifdef DW_CHARS + if (D_lpchar.mbcs) + PUTCHAR(D_lpchar.mbcs); +#endif + D_lp_missing = 0; + SetRendition(&oldrend); +} + +void +ClearLine(oml, y, from, to, bce) +struct mline *oml; +int from, to, y, bce; +{ + int x; +#ifdef COLOR + struct mchar bcechar; +#endif + + debug3("ClearLine %d,%d-%d\n", y, from, to); + if (D_UT) /* Safe to erase ? */ + SetRendition(&mchar_null); +#ifdef COLOR + if (D_BE) + SetBackColor(bce); +#endif + if (from == 0 && D_CB && (to != D_width - 1 || (D_x == to && D_y == y)) && (!bce || D_BE)) + { + GotoPos(to, y); + AddCStr(D_CB); + return; + } + if (to == D_width - 1 && D_CE && (!bce || D_BE)) + { + GotoPos(from, y); + AddCStr(D_CE); + return; + } + if (oml == 0) + oml = &mline_null; +#ifdef COLOR + if (!bce) + { + DisplayLine(oml, &mline_blank, y, from, to); + return; + } + bcechar = mchar_null; + rend_setbg(&bcechar, bce); + for (x = from; x <= to; x++) + copy_mchar2mline(&bcechar, &mline_old, x); + DisplayLine(oml, &mline_old, y, from, to); +#else + DisplayLine(oml, &mline_blank, y, from, to); +#endif +} + +void +DisplayLine(oml, ml, y, from, to) +struct mline *oml, *ml; +int from, to, y; +{ + register int x; + int last2flag = 0, delete_lp = 0; + + ASSERT(display); + ASSERT(y >= 0 && y < D_height); + ASSERT(from >= 0 && from < D_width); + ASSERT(to >= 0 && to < D_width); + if (!D_CLP && y == D_bot && to == D_width - 1) + { + if (D_lp_missing || !cmp_mline(oml, ml, to)) + { +#ifdef DW_CHARS + if ((D_IC || D_IM) && from < to && !dw_left(ml, to, D_encoding)) +#else + if ((D_IC || D_IM) && from < to) +#endif + { + last2flag = 1; + D_lp_missing = 0; + to--; + } + else + { + delete_lp = !cmp_mchar_mline(&mchar_blank, oml, to) && (D_CE || D_DC || D_CDC); + D_lp_missing = !cmp_mchar_mline(&mchar_blank, ml, to); + copy_mline2mchar(&D_lpchar, ml, to); + } + } + to--; + } +#ifdef DW_CHARS + if (D_mbcs) + { + /* finish dw-char (can happen after a wrap) */ + debug("DisplayLine finishing kanji\n"); + SetRenditionMline(ml, from); + PUTCHAR(ml->image[from]); + from++; + } +#endif + for (x = from; x <= to; x++) + { +#if 0 /* no longer needed */ + if (x || D_x != D_width || D_y != y - 1) +#endif + { + if (ml != NULL && (x < to || x != D_width - 1 || ml->image[x + 1])) + if (cmp_mline(oml, ml, x)) + continue; + GotoPos(x, y); + } +#ifdef DW_CHARS + if (dw_right(ml, x, D_encoding)) + { + x--; + debug1("DisplayLine on right side of dw char- x now %d\n", x); + GotoPos(x, y); + } + if (x == to && dw_left(ml, x, D_encoding)) + break; /* don't start new kanji */ +#endif + SetRenditionMline(ml, x); + PUTCHAR(ml->image[x]); +#ifdef DW_CHARS + if (dw_left(ml, x, D_encoding)) + PUTCHAR(ml->image[++x]); +#endif + } +#if 0 /* not needed any longer */ + /* compare != 0 because ' ' can happen when clipping occures */ + if (to == D_width - 1 && y < D_height - 1 && D_x == D_width && ml->image[to + 1]) + GotoPos(0, y + 1); +#endif + if (last2flag) + { + GotoPos(x, y); + SetRenditionMline(ml, x + 1); + PUTCHAR(ml->image[x + 1]); + GotoPos(x, y); + SetRenditionMline(ml, x); + INSERTCHAR(ml->image[x]); + } + else if (delete_lp) + { + if (D_UT) + SetRendition(&mchar_null); + if (D_DC) + AddCStr(D_DC); + else if (D_CDC) + AddCStr2(D_CDC, 1); + else if (D_CE) + AddCStr(D_CE); + } +} + +void +PutChar(c, x, y) +struct mchar *c; +int x, y; +{ + GotoPos(x, y); + SetRendition(c); + PUTCHARLP(c->image); +#ifdef DW_CHARS + if (c->mbcs) + { +# ifdef UTF8 + if (D_encoding == UTF8) + D_rend.font = 0; +# endif + PUTCHARLP(c->mbcs); + } +#endif +} + +void +InsChar(c, x, xe, y, oml) +struct mchar *c; +int x, xe, y; +struct mline *oml; +{ + GotoPos(x, y); + if (y == D_bot && !D_CLP) + { + if (x == D_width - 1) + { + D_lp_missing = 1; + D_lpchar = *c; + return; + } + if (xe == D_width - 1) + D_lp_missing = 0; + } + if (x == xe) + { + SetRendition(c); + PUTCHARLP(c->image); + return; + } + if (!(D_IC || D_CIC || D_IM) || xe != D_width - 1) + { + RefreshLine(y, x, xe, 0); + GotoPos(x + 1, y); + /* UpdateLine(oml, y, x, xe); */ + return; + } + InsertMode(1); + if (!D_insert) + { +#ifdef DW_CHARS + if (c->mbcs && D_IC) + AddCStr(D_IC); + if (D_IC) + AddCStr(D_IC); + else + AddCStr2(D_CIC, c->mbcs ? 2 : 1); +#else + if (D_IC) + AddCStr(D_IC); + else + AddCStr2(D_CIC, 1); +#endif + } + SetRendition(c); + RAW_PUTCHAR(c->image); +#ifdef DW_CHARS + if (c->mbcs) + { +# ifdef UTF8 + if (D_encoding == UTF8) + D_rend.font = 0; +# endif + if (D_x == D_width - 1) + PUTCHARLP(c->mbcs); + else + RAW_PUTCHAR(c->mbcs); + } +#endif +} + +void +WrapChar(c, x, y, xs, ys, xe, ye, ins) +struct mchar *c; +int x, y; +int xs, ys, xe, ye; +int ins; +{ + int bce; + +#ifdef COLOR + bce = rend_getbg(c); +#else + bce = 0; +#endif + debug("WrapChar:"); + debug2(" x %d y %d", x, y); + debug2(" Dx %d Dy %d", D_x, D_y); + debug2(" xs %d ys %d", xs, ys); + debug3(" xe %d ye %d ins %d\n", xe, ye, ins); + if (xs != 0 || x != D_width || !D_AM) + { + if (y == ye) + ScrollV(xs, ys, xe, ye, 1, bce); + else if (y < D_height - 1) + y++; + if (ins) + InsChar(c, xs, xe, y, 0); + else + PutChar(c, xs, y); + return; + } + if (y == ye) /* we have to scroll */ + { + debug("- scrolling\n"); + ChangeScrollRegion(ys, ye); + if (D_bot != y || D_x != D_width || (!bce && !D_BE)) + { + debug("- have to call ScrollV\n"); + ScrollV(xs, ys, xe, ye, 1, bce); + y--; + } + } + else if (y == D_bot) /* remove unusable region? */ + ChangeScrollRegion(0, D_height - 1); + if (D_x != D_width || D_y != y) + { + if (D_CLP && y >= 0) /* don't even try if !LP */ + RefreshLine(y, D_width - 1, D_width - 1, 0); + debug2("- refresh last char -> x,y now %d,%d\n", D_x, D_y); + if (D_x != D_width || D_y != y) /* sorry, no bonus */ + { + if (y == ye) + ScrollV(xs, ys, xe, ye, 1, bce); + GotoPos(xs, y == ye || y == D_height - 1 ? y : y + 1); + } + } + debug("- writeing new char"); + if (y != ye && y < D_height - 1) + y++; + if (ins != D_insert) + InsertMode(ins); + if (ins && !D_insert) + { + InsChar(c, 0, xe, y, 0); + debug2(" -> done with insert (%d,%d)\n", D_x, D_y); + return; + } + D_y = y; + D_x = 0; + SetRendition(c); + RAW_PUTCHAR(c->image); +#ifdef DW_CHARS + if (c->mbcs) + { +# ifdef UTF8 + if (D_encoding == UTF8) + D_rend.font = 0; +# endif + RAW_PUTCHAR(c->mbcs); + } +#endif + debug2(" -> done (%d,%d)\n", D_x, D_y); +} + +int +ResizeDisplay(wi, he) +int wi, he; +{ + ASSERT(display); + debug2("ResizeDisplay: to (%d,%d).\n", wi, he); + if (D_width == wi && D_height == he) + { + debug("ResizeDisplay: No change\n"); + return 0; + } + if (D_width != wi && (D_height == he || !D_CWS) && D_CZ0 && (wi == Z0width || wi == Z1width)) + { + debug("ResizeDisplay: using Z0/Z1\n"); + AddCStr(wi == Z0width ? D_CZ0 : D_CZ1); + ChangeScreenSize(wi, D_height, 0); + return (he == D_height) ? 0 : -1; + } + if (D_CWS) + { + debug("ResizeDisplay: using WS\n"); + AddCStr(tgoto(D_CWS, wi, he)); + ChangeScreenSize(wi, he, 0); + return 0; + } + return -1; +} + +void +ChangeScrollRegion(newtop, newbot) +int newtop, newbot; +{ + if (display == 0) + return; + if (newtop == newbot) + return; /* xterm etc can't do it */ + if (newtop == -1) + newtop = 0; + if (newbot == -1) + newbot = D_height - 1; + if (D_CS == 0) + { + D_top = 0; + D_bot = D_height - 1; + return; + } + if (D_top == newtop && D_bot == newbot) + return; + debug2("ChangeScrollRegion: (%d - %d)\n", newtop, newbot); + AddCStr(tgoto(D_CS, newbot, newtop)); + D_top = newtop; + D_bot = newbot; + D_y = D_x = -1; /* Just in case... */ +} + +#ifdef RXVT_OSC +#define WT_FLAG "2" /* change to "0" to set both title and icon */ + +void +SetXtermOSC(i, s, t) +int i; +char *s; +char *t; +{ + static char *oscs[][2] = { + { WT_FLAG ";", "screen" }, /* set window title */ + { "11;", ""}, /* background RGB */ + { "20;", "" }, /* background */ + { "39;", "black" }, /* default foreground (black?) */ + { "49;", "white" } /* default background (white?) */ + }; + + ASSERT(display); + if (!D_CXT) + return; + if (!s) + s = ""; + if (!D_xtermosc[i] && !*s) + return; + if (i == 0 && !D_xtermosc[0]) + AddStr("\033[22;" WT_FLAG "t"); /* stack titles (xterm patch #251) */ + if (!*s) + s = oscs[i][1]; + D_xtermosc[i] = 1; + AddStr("\033]"); + AddStr(oscs[i][0]); + AddStr(s); + AddStr(t); +} + +void +ClearAllXtermOSC() +{ + int i; + for (i = 4; i >= 0; i--) + SetXtermOSC(i, 0, "\a"); + if (D_xtermosc[0]) + AddStr("\033[23;" WT_FLAG "t"); /* unstack titles (xterm patch #251) */ +} +#undef WT_FLAG +#endif + +/* + * Output buffering routines + */ + +void +AddStr(str) +char *str; +{ + register char c; + + ASSERT(display); + +#ifdef UTF8 + if (D_encoding == UTF8) + { + while ((c = *str++)) + AddUtf8((unsigned char)c); + return; + } +#endif + while ((c = *str++)) + AddChar(c); +} + +void +AddStrn(str, n) +char *str; +int n; +{ + register char c; + + ASSERT(display); +#ifdef UTF8 + if (D_encoding == UTF8) + { + while ((c = *str++) && n-- > 0) + AddUtf8((unsigned char)c); + } + else +#endif + while ((c = *str++) && n-- > 0) + AddChar(c); + while (n-- > 0) + AddChar(' '); +} + +void +Flush(progress) +int progress; +{ + register int l; + int wr; + register char *p; + + ASSERT(display); + l = D_obufp - D_obuf; + debug1("Flush(): %d\n", l); + if (l == 0) + return; + ASSERT(l + D_obuffree == D_obuflen); + if (D_userfd < 0) + { + D_obuffree += l; + D_obufp = D_obuf; + return; + } + p = D_obuf; + if (!progress) + { + if (fcntl(D_userfd, F_SETFL, 0)) + debug1("Warning: BLOCK fcntl failed: %d\n", errno); + } + while (l) + { + if (progress) + { + fd_set w; + FD_ZERO(&w); + FD_SET(D_userfd, &w); + struct timeval t; + t.tv_sec = progress; + t.tv_usec = 0; + wr = select(FD_SETSIZE, (fd_set *)0, &w, (fd_set *)0, &t); + if (wr == -1) + { + if (errno == EINTR) + continue; + debug1("Warning: select failed: %d\n", errno); + break; + } + if (wr == 0) + { + /* no progress after 3 seconds. sorry. */ + debug1("Warning: no progress after %d seconds\n", progress); + break; + } + } + wr = write(D_userfd, p, l); + if (wr <= 0) + { + if (errno == EINTR) + continue; + debug1("Writing to display: %d\n", errno); + break; + } + D_obuffree += wr; + p += wr; + l -= wr; + } + if (l) + debug1("Warning: Flush could not write %d bytes\n", l); + D_obuffree += l; + D_obufp = D_obuf; + if (!progress) + { + if (fcntl(D_userfd, F_SETFL, FNBLOCK)) + debug1("Warning: NBLOCK fcntl failed: %d\n", errno); + } + if (D_blocked == 1) + D_blocked = 0; + D_blocked_fuzz = 0; +} + +void +freetty() +{ + if (D_userfd >= 0) + close(D_userfd); + debug1("did freetty %d\n", D_userfd); + D_userfd = -1; + D_obufp = 0; + D_obuffree = 0; + if (D_obuf) + free(D_obuf); + D_obuf = 0; + D_obuflen = 0; + D_obuflenmax = -D_obufmax; + D_blocked = 0; + D_blocked_fuzz = 0; +} + +/* + * Asynchronous output routines by + * Tim MacKenzie (tym@dibbler.cs.monash.edu.au) + */ + +void +Resize_obuf() +{ + register int ind; + + ASSERT(display); + if (D_status_obuffree >= 0) + { + ASSERT(D_obuffree == -1); + RemoveStatusMinWait(); + if (--D_obuffree > 0) /* redo AddChar decrement */ + return; + } + if (D_obuflen && D_obuf) + { + ind = D_obufp - D_obuf; + D_obuflen += GRAIN; + D_obuffree += GRAIN; + D_obuf = realloc(D_obuf, D_obuflen); + } + else + { + ind = 0; + D_obuflen = GRAIN; + D_obuffree = GRAIN; + D_obuf = malloc(D_obuflen); + } + if (!D_obuf) + Panic(0, "Out of memory"); + D_obufp = D_obuf + ind; + D_obuflenmax = D_obuflen - D_obufmax; + debug1("ResizeObuf: resized to %d\n", D_obuflen); +} + +void +DisplaySleep1000(n, eat) +int n; +int eat; +{ + char buf; + fd_set r; + struct timeval t; + + if (n <= 0) + return; + if (!display) + { + debug("DisplaySleep has no display sigh\n"); + sleep1000(n); + return; + } + t.tv_usec = (n % 1000) * 1000; + t.tv_sec = n / 1000; + FD_ZERO(&r); + FD_SET(D_userfd, &r); + if (select(FD_SETSIZE, &r, (fd_set *)0, (fd_set *)0, &t) > 0) + { + debug("display activity stopped sleep\n"); + if (eat) + read(D_userfd, &buf, 1); + } + debug2("DisplaySleep(%d) ending, eat was %d\n", n, eat); +} + +#ifdef AUTO_NUKE +void +NukePending() +{/* Nuke pending output in current display, clear screen */ + register int len; + int oldtop = D_top, oldbot = D_bot; + struct mchar oldrend; + int oldkeypad = D_keypad, oldcursorkeys = D_cursorkeys; + int oldcurvis = D_curvis; + int oldmouse = D_mouse; + int oldextmouse = D_extmouse; + + oldrend = D_rend; + len = D_obufp - D_obuf; + debug1("NukePending: nuking %d chars\n", len); + + /* Throw away any output that we can... */ +# ifdef POSIX + tcflush(D_userfd, TCOFLUSH); +# else +# ifdef TCFLSH + (void) ioctl(D_userfd, TCFLSH, (char *) 1); +# endif +# endif + + D_obufp = D_obuf; + D_obuffree += len; + D_top = D_bot = -1; + AddCStr(D_IS); + AddCStr(D_TI); + /* Turn off all attributes. (Tim MacKenzie) */ + if (D_ME) + AddCStr(D_ME); + else + { +#ifdef COLOR + if (D_hascolor) + AddStr("\033[m"); /* why is D_ME not set? */ +#endif + AddCStr(D_SE); + AddCStr(D_UE); + } + /* Check for toggle */ + if (D_IM && strcmp(D_IM, D_EI)) + AddCStr(D_EI); + D_insert = 0; + /* Check for toggle */ +#ifdef MAPKEYS + if (D_KS && strcmp(D_KS, D_KE)) + AddCStr(D_KS); + if (D_CCS && strcmp(D_CCS, D_CCE)) + AddCStr(D_CCS); +#else + if (D_KS && strcmp(D_KS, D_KE)) + AddCStr(D_KE); + D_keypad = 0; + if (D_CCS && strcmp(D_CCS, D_CCE)) + AddCStr(D_CCE); + D_cursorkeys = 0; +#endif + AddCStr(D_CE0); + D_rend = mchar_null; + D_atyp = 0; + AddCStr(D_DS); + D_hstatus = 0; + AddCStr(D_VE); + D_curvis = 0; + ChangeScrollRegion(oldtop, oldbot); + SetRendition(&oldrend); + KeypadMode(oldkeypad); + CursorkeysMode(oldcursorkeys); + CursorVisibility(oldcurvis); + MouseMode(oldmouse); + ExtMouseMode(oldextmouse); + if (D_CWS) + { + debug("ResizeDisplay: using WS\n"); + AddCStr(tgoto(D_CWS, D_width, D_height)); + } + else if (D_CZ0 && (D_width == Z0width || D_width == Z1width)) + { + debug("ResizeDisplay: using Z0/Z1\n"); + AddCStr(D_width == Z0width ? D_CZ0 : D_CZ1); + } +} +#endif /* AUTO_NUKE */ + +#ifdef linux +/* linux' select can't handle flow control, so wait 100ms if + * we get EAGAIN + */ +static void +disp_writeev_eagain(ev, data) +struct event *ev; +char *data; +{ + display = (struct display *)data; + evdeq(&D_writeev); + D_writeev.type = EV_WRITE; + D_writeev.handler = disp_writeev_fn; + evenq(&D_writeev); +} +#endif + +static void +disp_writeev_fn(ev, data) +struct event *ev; +char *data; +{ + int len, size = OUTPUT_BLOCK_SIZE; + + display = (struct display *)data; + len = D_obufp - D_obuf; + if (len < size) + size = len; + if (D_status_obufpos && size > D_status_obufpos) + size = D_status_obufpos; + ASSERT(len >= 0); + size = write(D_userfd, D_obuf, size); + if (size >= 0) + { + len -= size; + if (len) + { + bcopy(D_obuf + size, D_obuf, len); + debug2("ASYNC: wrote %d - remaining %d\n", size, len); + } + D_obufp -= size; + D_obuffree += size; + if (D_status_obufpos) + { + D_status_obufpos -= size; + if (!D_status_obufpos) + { + debug("finished writing the status message\n"); + /* we're finished displaying the message! */ + if (D_status == STATUS_ON_WIN) + { + /* setup continue trigger */ + D_status_obuflen = D_obuflen; + D_status_obuffree = D_obuffree; + /* setting obbuffree to 0 will make AddChar call + * ResizeObuf */ + D_obuffree = D_obuflen = 0; + } + gettimeofday(&D_status_time, NULL); + SetTimeout(&D_statusev, MsgWait); + evenq(&D_statusev); +#ifdef HAVE_BRAILLE + RefreshBraille(); /* let user see multiple Msg()s */ +#endif + } + } + if (D_blocked_fuzz) + { + D_blocked_fuzz -= size; + if (D_blocked_fuzz < 0) + D_blocked_fuzz = 0; + } + if (D_blockedev.queued) + { + if (D_obufp - D_obuf > D_obufmax / 2) + { + debug2("%s: resetting timeout to %g secs\n", D_usertty, D_nonblock/1000.); + SetTimeout(&D_blockedev, D_nonblock); + } + else + { + debug1("%s: deleting blocked timeout\n", D_usertty); + evdeq(&D_blockedev); + } + } + if (D_blocked == 1 && D_obuf == D_obufp) + { + /* empty again, restart output */ + debug1("%s: buffer empty, unblocking\n", D_usertty); + D_blocked = 0; + Activate(D_fore ? D_fore->w_norefresh : 0); + D_blocked_fuzz = D_obufp - D_obuf; + } + } + else + { +#ifdef linux + /* linux flow control is badly broken */ + if (errno == EAGAIN) + { + evdeq(&D_writeev); + D_writeev.type = EV_TIMEOUT; + D_writeev.handler = disp_writeev_eagain; + SetTimeout(&D_writeev, 100); + evenq(&D_writeev); + } +#endif + if (errno != EINTR && errno != EAGAIN) +#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) + if (errno != EWOULDBLOCK) +#endif + Msg(errno, "Error writing output to display"); + } +} + +/* maximum mouse sequence length is SGR: ESC [ < b ; x ; y M */ +#define MAX_MOUSE_SEQUENCE (3+10+1+10+1+10+1) + +static void +disp_readev_fn(ev, data) +struct event *ev; +char *data; +{ + /* We have to intercept mouse sequences in order to translate them + * properly, and an incoming sequence could be spread over multiple + * I/O reads. When this occurs, we buffer intermediate state in + * D_mouse_params, and then use the reservation at the front of + * bufspace to prepend the completed and translated mouse sequence. + */ + int size; + char bufspace[MAX_MOUSE_SEQUENCE + IOSIZE]; + unsigned char *buf = (unsigned char*)bufspace + MAX_MOUSE_SEQUENCE; + + struct canvas *cv; + + display = (struct display *)data; + + /* Hmmmm... a bit ugly... */ + if (D_forecv) + for (cv = D_forecv->c_layer->l_cvlist; cv; cv = cv->c_lnext) + { + display = cv->c_display; + if (D_status == STATUS_ON_WIN) + RemoveStatus(); + } + + display = (struct display *)data; + if (D_fore == 0) + size = IOSIZE; + else + { +#ifdef PSEUDOS + if (W_UWP(D_fore)) + size = sizeof(D_fore->w_pwin->p_inbuf) - D_fore->w_pwin->p_inlen; + else +#endif + size = sizeof(D_fore->w_inbuf) - D_fore->w_inlen; + } + + if (size > IOSIZE) + size = IOSIZE; + if (size <= 0) + size = 1; /* Always allow one char for command keys */ + + size = read(D_userfd, buf, size); + if (size < 0) + { + if (errno == EINTR || errno == EAGAIN) + return; +#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) + if (errno == EWOULDBLOCK) + return; +#endif + debug1("Read error: %d - hangup!\n", errno); + Hangup(); + sleep(1); + return; + } + else if (size == 0) + { + debug("Found EOF - hangup!\n"); + Hangup(); + sleep(1); + return; + } + if (D_blocked == 4) + { + D_blocked = 0; +#ifdef BLANKER_PRG + KillBlanker(); +#endif + Activate(D_fore ? D_fore->w_norefresh : 0); + ResetIdle(); + return; + } +#ifdef ZMODEM + if (D_blocked > 1) /* 2, 3 */ + { + char *bufp; + struct win *p; + + flayer = 0; + for (p = windows; p ; p = p->w_next) + if (p->w_zdisplay == display) + { + flayer = &p->w_layer; + bufp = (char *)buf; + while (size > 0) + LayProcess(&bufp, &size); + return; + } + debug("zmodem window gone, deblocking display"); + zmodem_abort(0, display); + } +#endif + if (idletimo > 0) + ResetIdle(); + if (D_fore) + D_fore->w_lastdisp = display; + + if (D_mouse && D_forecv) + { + unsigned char *bp = (unsigned char *)buf; + unsigned char *end = bp + size; + unsigned char *mark = NULL; + + /* When mouse mode is enabled, buffer up incoming CSI until we + * know whether it is a mouse sequence. If not a mouse event, + * emit the CSI unchanged; if an invalid mouse event, swallow + * it; otherwise, translate the sequence and emit it. + * + * Supported mouse events take two flavors. + * + * VT200: CSI M Cb Cx Cy + * SGR: CSI < Ps ; Ps ; Ps M|m + * + * UTF-8 and UXRVT modes are explicitly rejected because they + * introduce incompatibilities with other control sequences. + * + * NOTE: applications wishing to use SGR mode will normally + * enable VT200 mode first as a fallback in case the terminal + * does not support SGR mode. Thus, we must expect to receive + * either mode's sequences when mouse mode is enabled. We will + * dutifully translate whatever arrives, without attempting to + * convert between modes on behalf of the application. + */ + switch (D_mouse_parse.state) + { + case CSI_PY: + case CSI_PX: + case CSI_PB: + /* Partial mouse sequence; do not restore suppressed + * characters. We will emit a translated version (if valid) + * or swallow them (otherwise). + */ + break; + + case CSI_BEGIN: + /* Partial CSI; restore suppressed characters in case it + * turns out not to be a mouse sequence after all. + */ + *(--buf) = '['; + ++size; + /* fall through */ + + case CSI_ESC_SEEN: + /* Escape character; restore it in case this turns out not + * to be the start of a mouse sequence after all. + */ + *(--buf) = '\033'; + ++size; + break; + + default: + break; + }; + + while (bp != end) + { + unsigned char c = *(bp++); + + switch (D_mouse_parse.state) + { + case CSI_INACTIVE: + if (c == '\033') + { + /* potential escape sequence */ + mark = bp-1; + D_mouse_parse.state = CSI_ESC_SEEN; + } + break; + + case CSI_ESC_SEEN: + if (c == '[') + { + /* continue buffering an escape sequence */ + D_mouse_parse.state = CSI_BEGIN; + } + else + D_mouse_parse.state = CSI_INACTIVE; + break; + + case CSI_BEGIN: + if (c == 'M') + { + /* VT200 mouse sequence */ + D_mouse_parse.state = CSI_PB; + D_mouse_parse.sgrmode = 0; + } + else if (c == '<') + { + /* SGR mouse sequence */ + D_mouse_parse.state = CSI_PB; + D_mouse_parse.params[D_mouse_parse.state] = 0; + D_mouse_parse.sgrmode = 1; + } + else + D_mouse_parse.state = CSI_INACTIVE; + break; + + case CSI_PB: + case CSI_PX: + case CSI_PY: + if (D_mouse_parse.sgrmode) + { + /* SGR mode: parse decimal numbers */ + if ('0' <= c && c <= '9') + { + D_mouse_parse.params[D_mouse_parse.state] *= 10; + D_mouse_parse.params[D_mouse_parse.state] += c - '0'; + } + else if (D_mouse_parse.state == CSI_PY) + { + if (c == 'M' || c == 'm') + D_mouse_parse.state = CSI_DONE; + else + D_mouse_parse.state = CSI_INVALID; + } + else if (c == ';') + { + D_mouse_parse.state++; + D_mouse_parse.params[D_mouse_parse.state] = 0; + } + else + D_mouse_parse.state = CSI_INVALID; + } + else + { + /* VT200 mode: read raw binary values */ + D_mouse_parse.params[D_mouse_parse.state++] = c; + } + break; + + default: + break; + } + + if (D_mouse_parse.state == CSI_INVALID) + { + /* swallow invalid sequence, but emit whatever came before */ + if (buf < mark) + disp_processinput(display, buf, mark-buf); + + buf = bp; + size = end - bp; + D_mouse_parse.state = CSI_INACTIVE; + } + else if (D_mouse_parse.state == CSI_DONE) + { + /* emit whatever came before this sequence */ + if (buf < mark) + disp_processinput(display, buf, mark-buf); + + buf = bp; + size = end - bp; + + int x = D_mouse_parse.params[CSI_PX]; + int y = D_mouse_parse.params[CSI_PY]; + int bias = D_mouse_parse.sgrmode? 1 : 33; + + x -= bias; + y -= bias; + + if (x >= D_forecv->c_xs && x <= D_forecv->c_xe && y >= D_forecv->c_ys && y <= D_forecv->c_ye) + { + if ((D_fore && D_fore->w_mouse) || (D_mousetrack && D_forecv->c_layer->l_mode == 1)) + { + /* Send clicks only if the window is expecting clicks */ + x -= D_forecv->c_xoff; + y -= D_forecv->c_yoff; + + if (x >= 0 && x < D_forecv->c_layer->l_width && y >= 0 && y < D_forecv->c_layer->l_height) + { + char tmp[MAX_MOUSE_SEQUENCE+1]; + int n; + + x += bias; + y += bias; + + if (D_mouse_parse.sgrmode) + { + n = snprintf( + tmp, MAX_MOUSE_SEQUENCE, "\033[<%d;%d;%d%c", + D_mouse_parse.params[CSI_PB], x, y, c); + } + else + { + n = snprintf( + tmp, MAX_MOUSE_SEQUENCE, "\033[M%c%c%c", + D_mouse_parse.params[CSI_PB], x, y); + } + + if (n > MAX_MOUSE_SEQUENCE) + n = MAX_MOUSE_SEQUENCE; + + /* emit sequence */ + buf -= n; + size += n; + memcpy(buf, tmp, n); + } + } + } + else if (D_mousetrack) + { + /* 'focus' to the clicked region, only on mouse up */ + int focus = 0; + if (D_mouse_parse.sgrmode) + focus = (c == 'm'); + else + focus = (D_mouse_parse.params[CSI_PB] == '#'); + + struct canvas *cv = FindCanvas(x, y); + if (focus && cv) + { + SetForeCanvas(display, cv); + /* XXX: Do we want to reset the input buffer? */ + } + } + + D_mouse_parse.state = CSI_INACTIVE; + } + } + + if (D_mouse_parse.state != CSI_INACTIVE) + { + /* suppress partial sequence at end */ + size = mark? mark - buf : 0; + } + } + + if (size > 0) + disp_processinput(display, buf, size); +} + +static void +disp_processinput(display, buf, size) + struct display* display; + unsigned char* buf; + int size; +{ +#ifdef ENCODINGS + if (D_encoding != (D_forecv ? D_forecv->c_layer->l_encoding : 0)) + { + int i, j, c, enc; + char buf2[IOSIZE * 2 + 10]; + enc = D_forecv ? D_forecv->c_layer->l_encoding : 0; + for (i = j = 0; i < size; i++) + { + c = ((unsigned char *)buf)[i]; + c = DecodeChar(c, D_encoding, &D_decodestate); + if (c == -2) + i--; /* try char again */ + if (c < 0) + continue; + if (pastefont) + { + int font = 0; + j += EncodeChar(buf2 + j, c, enc, &font); + j += EncodeChar(buf2 + j, -1, enc, &font); + } + else + j += EncodeChar(buf2 + j, c, enc, 0); + if (j > (int)sizeof(buf2) - 10) /* just in case... */ + break; + } + (*D_processinput)(buf2, j); + return; + } +#endif + (*D_processinput)((char *)buf, size); +} + +static void +disp_status_fn(ev, data) +struct event *ev; +char *data; +{ + display = (struct display *)data; + debug1("disp_status_fn for display %x\n", (int)display); + if (D_status) + RemoveStatus(); +} + +static void +disp_hstatus_fn(ev, data) +struct event *ev; +char *data; +{ + display = (struct display *)data; + if (D_status == STATUS_ON_HS) + { + SetTimeout(ev, 1); + evenq(ev); + return; + } + RefreshHStatus(); +} + +static void +disp_blocked_fn(ev, data) +struct event *ev; +char *data; +{ + struct win *p; + + display = (struct display *)data; + debug1("blocked timeout %s\n", D_usertty); + if (D_obufp - D_obuf > D_obufmax + D_blocked_fuzz) + { + debug("stopping output to display\n"); + D_blocked = 1; + /* re-enable all windows */ + for (p = windows; p; p = p->w_next) + if (p->w_readev.condneg == &D_obuflenmax) + { + debug1("freeing window #%d\n", p->w_number); + p->w_readev.condpos = p->w_readev.condneg = 0; + } + } +} + +#ifdef MAPKEYS +static void +disp_map_fn(ev, data) +struct event *ev; +char *data; +{ + char *p; + int l, i; + unsigned char *q; + display = (struct display *)data; + debug("Flushing map sequence\n"); + if (!(l = D_seql)) + return; + p = (char *)D_seqp - l; + D_seqp = D_kmaps + 3; + D_seql = 0; + if ((q = D_seqh) != 0) + { + D_seqh = 0; + i = q[0] << 8 | q[1]; + i &= ~KMAP_NOTIMEOUT; + debug1("Mapping former hit #%d - ", i); + debug2("%d(%s) - ", q[2], q + 3); + if (StuffKey(i)) + ProcessInput2((char *)q + 3, q[2]); + if (display == 0) + return; + l -= q[2]; + p += q[2]; + } + else + D_dontmap = 1; + ProcessInput(p, l); +} +#endif + +static void +disp_idle_fn(ev, data) +struct event *ev; +char *data; +{ + struct display *olddisplay; + display = (struct display *)data; + debug("idle timeout\n"); + if (idletimo <= 0 || idleaction.nr == RC_ILLEGAL) + return; + olddisplay = display; + flayer = D_forecv->c_layer; + fore = D_fore; + DoAction(&idleaction, -1); + if (idleaction.nr == RC_BLANKER) + return; + for (display = displays; display; display = display->d_next) + if (olddisplay == display) + break; + if (display) + ResetIdle(); +} + +void +ResetIdle() +{ + if (idletimo > 0) + { + SetTimeout(&D_idleev, idletimo); + if (!D_idleev.queued) + evenq(&D_idleev); + } + else + evdeq(&D_idleev); +} + + +#ifdef BLANKER_PRG + +static void +disp_blanker_fn(ev, data) +struct event *ev; +char *data; +{ + char buf[IOSIZE], *b; + int size; + + display = (struct display *)data; + size = read(D_blankerev.fd, buf, IOSIZE); + if (size <= 0) + { + evdeq(&D_blankerev); + close(D_blankerev.fd); + D_blankerev.fd = -1; + return; + } + for (b = buf; size; size--) + AddChar(*b++); +} + +void +KillBlanker() +{ + int oldtop = D_top, oldbot = D_bot; + struct mchar oldrend; + + if (D_blankerev.fd == -1) + return; + if (D_blocked == 4) + D_blocked = 0; + evdeq(&D_blankerev); + close(D_blankerev.fd); + D_blankerev.fd = -1; + Kill(D_blankerpid, SIGHUP); + D_top = D_bot = -1; + oldrend = D_rend; + if (D_ME) + { + AddCStr(D_ME); + AddCStr(D_ME); + } + else + { +#ifdef COLOR + if (D_hascolor) + AddStr("\033[m\033[m"); /* why is D_ME not set? */ +#endif + AddCStr(D_SE); + AddCStr(D_UE); + } + AddCStr(D_VE); + AddCStr(D_CE0); + D_rend = mchar_null; + D_atyp = 0; + D_curvis = 0; + D_x = D_y = -1; + ChangeScrollRegion(oldtop, oldbot); + SetRendition(&oldrend); + ClearAll(); +} + +void +RunBlanker(cmdv) +char **cmdv; +{ + char *m; + int pid; + int slave = -1; + int ptype = 0; + char termname[MAXTERMLEN + 6]; +#ifndef TIOCSWINSZ + char libuf[20], cobuf[20]; +#endif + char **np; + + strcpy(termname, "TERM="); + strncpy(termname + 5, D_termname, MAXTERMLEN - 6); + termname[sizeof(termname) - 1] = 0; + KillBlanker(); + D_blankerpid = -1; + if ((D_blankerev.fd = OpenDevice(cmdv, 0, &ptype, &m)) == -1) + { + Msg(0, "OpenDevice failed"); + return; + } +#ifdef O_NOCTTY + if (pty_preopen) + { + if ((slave = open(m, O_RDWR|O_NOCTTY)) == -1) + { + Msg(errno, "%s", m); + close(D_blankerev.fd); + D_blankerev.fd = -1; + return; + } + } +#endif + switch (pid = (int)fork()) + { + case -1: + Msg(errno, "fork"); + close(D_blankerev.fd); + D_blankerev.fd = -1; + close(slave); + return; + case 0: + displays = 0; + ServerSocket = -1; +#ifdef SIGPIPE + signal(SIGPIPE, SIG_DFL); +#endif + if (setgid(real_gid) || setuid(real_uid)) + Panic(errno, "setuid/setgid"); + eff_uid = real_uid; + eff_gid = real_gid; + brktty(D_userfd); + freetty(); +#ifdef DEBUG + if (dfp && dfp != stderr) + fclose(dfp); +#endif + if (slave != -1) + { + close(0); + dup(slave); + close(slave); + closeallfiles(D_blankerev.fd); + slave = dup(0); + } + else + closeallfiles(D_blankerev.fd); +#ifdef DEBUG + if (dfp) + { + char buf[256]; + + sprintf(buf, "%s/screen.blanker", DEBUGDIR); + if ((dfp = fopen(buf, "a")) == 0) + dfp = stderr; + else + (void) chmod(buf, 0666); + } + debug1("=== RunBlanker: pid %d\n", (int)getpid()); +#endif + close(0); + close(1); + close(2); + if (open(m, O_RDWR)) + Panic(errno, "Cannot open %s", m); + dup(0); + dup(0); + close(D_blankerev.fd); + if (slave != -1) + close(slave); + InitPTY(0); + fgtty(0); + SetTTY(0, &D_OldMode); + np = NewEnv + 3; + *np++ = NewEnv[0]; + *np++ = termname; +#ifdef TIOCSWINSZ + glwz.ws_col = D_width; + glwz.ws_row = D_height; + (void)ioctl(0, TIOCSWINSZ, (char *)&glwz); +#else + /* Always turn off nonblocking mode */ + (void)fcntl(0, F_SETFL, 0); + sprintf(libuf, "LINES=%d", D_height); + sprintf(cobuf, "COLUMNS=%d", D_width); + *np++ = libuf; + *np++ = cobuf; +#endif + debug1("calling execvpe %s\n", *cmdv); + execvpe(*cmdv, cmdv, NewEnv + 3); + debug1("exec error: %d\n", errno); + Panic(errno, "Cannot exec '%s'", *cmdv); + default: + break; + } + D_blankerpid = pid; + evenq(&D_blankerev); + D_blocked = 4; + ClearAll(); + close(slave); +} + +#endif /* BLANKER_PRG */ + + diff --git a/display.h b/display.h new file mode 100644 index 0000000..b094619 --- /dev/null +++ b/display.h @@ -0,0 +1,338 @@ +/* Copyright (c) 2008, 2009 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Micah Cowan (micah@cowan.name) + * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) + * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, see + * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + * $Id$ GNU + */ + +#ifndef SCREEN_DISPLAY_H +#define SCREEN_DISPLAY_H + +#include "layout.h" +#include "canvas.h" +#include "viewport.h" + +#ifdef MAPKEYS + +#define KMAP_KEYS (T_OCAPS-T_CAPS) +#define KMAP_AKEYS (T_OCAPS-T_CURSOR) + +#define KMAP_NOTIMEOUT 0x4000 + +struct kmap_ext +{ + char *str; + int fl; + struct action um; + struct action dm; + struct action mm; +}; + +#else + +#define KMAP_KEYS 0 + +#endif + +struct win; /* forward declaration */ + +struct mouse_parse +{ + char sgrmode; /* non-zero if parsing an SGR sequence */ + int state; /* current state of parsing */ + int params[3]; /* parsed params: button, x, y */ +}; + +struct display +{ + struct display *d_next; /* linked list */ + struct acluser *d_user; /* user who owns that display */ + struct canvas d_canvas; /* our canvas slice */ + struct canvas *d_cvlist; /* the canvases of this display */ + struct canvas *d_forecv; /* current input focus */ + struct layout *d_layout; /* layout we're using */ + void (*d_processinput) __P((char *, int)); + char *d_processinputdata; /* data for processinput */ + int d_vpxmin, d_vpxmax; /* min/max used position on display */ + struct win *d_fore; /* pointer to fore window */ + struct win *d_other; /* pointer to other window */ + int d_nonblock; /* -1 don't block if obufmax reached */ + /* >0: block after nonblock secs */ + char d_termname[MAXTERMLEN + 1]; /* $TERM */ + char *d_tentry; /* buffer for tgetstr */ + char d_tcinited; /* termcap inited flag */ + int d_width, d_height; /* width/height of the screen */ + int d_defwidth, d_defheight; /* default width/height of windows */ + int d_top, d_bot; /* scrollregion start/end */ + int d_x, d_y; /* cursor position */ + struct mchar d_rend; /* current rendition */ + int d_col16change; /* the 16col bits changed in attr */ + char d_atyp; /* current attribute types */ +#ifdef DW_CHARS + int d_mbcs; /* saved char for multibytes charset */ +#endif +#ifdef ENCODINGS + int d_encoding; /* what encoding type the display is */ + int d_decodestate; /* state of our decoder */ + int d_realfont; /* real font of terminal */ +#endif + int d_insert; /* insert mode flag */ + int d_keypad; /* application keypad flag */ + int d_cursorkeys; /* application cursorkeys flag */ + int d_revvid; /* reverse video */ + int d_curvis; /* cursor visibility */ + int d_has_hstatus; /* display has hardstatus line */ + int d_hstatus; /* hardstatus used */ + int d_lp_missing; /* last character on bot line missing */ + int d_mouse; /* mouse mode */ + int d_extmouse; /* extended mouse mode */ + struct mouse_parse d_mouse_parse; /* state of mouse code parsing */ + int d_mousetrack; /* set when user wants to use mouse even when the window + does not */ +#ifdef RXVT_OSC + int d_xtermosc[5]; /* osc used */ +#endif + struct mchar d_lpchar; /* missing char */ + struct timeval d_status_time; /* time of status display */ + int d_status; /* is status displayed? */ + char d_status_bell; /* is it only a vbell? */ + int d_status_len; /* length of status line */ + char *d_status_lastmsg; /* last displayed message */ + int d_status_buflen; /* last message buffer len */ + int d_status_lastx; /* position of the cursor */ + int d_status_lasty; /* before status was displayed */ + int d_status_obuflen; /* saved obuflen */ + int d_status_obuffree; /* saved obuffree */ + int d_status_obufpos; /* end of status position in obuf */ + struct event d_statusev; /* timeout event */ + struct event d_hstatusev; /* hstatus changed event */ + int d_kaablamm; /* display kaablamm msg */ + struct action *d_ESCseen; /* Was the last char an ESC (^a) */ + int d_userpid; /* pid of attacher */ + char d_usertty[MAXPATHLEN]; /* tty we are attached to */ + int d_userfd; /* fd of the tty */ + struct event d_readev; /* userfd read event */ + struct event d_writeev; /* userfd write event */ + struct event d_blockedev; /* blocked timeout */ + struct mode d_OldMode; /* tty mode when screen was started */ + struct mode d_NewMode; /* New tty mode */ + int d_flow; /* tty's flow control on/off flag*/ + int d_intrc; /* current intr when flow is on */ + char *d_obuf; /* output buffer */ + int d_obuflen; /* len of buffer */ + int d_obufmax; /* len where we are blocking the pty */ + int d_obuflenmax; /* len - max */ + char *d_obufp; /* pointer in buffer */ + int d_obuffree; /* free bytes in buffer */ +#ifdef AUTO_NUKE + int d_auto_nuke; /* autonuke flag */ +#endif +#ifdef MAPKEYS + int d_nseqs; /* number of valid mappings */ + int d_aseqs; /* number of allocated mappings */ + unsigned char *d_kmaps; /* keymaps */ + unsigned char *d_seqp; /* pointer into keymap array */ + int d_seql; /* number of parsed chars */ + unsigned char *d_seqh; /* last hit */ + struct event d_mapev; /* timeout event */ + int d_dontmap; /* do not map next */ + int d_mapdefault; /* do map next to default */ +#endif + union tcu d_tcs[T_N]; /* terminal capabilities */ + char *d_attrtab[NATTR]; /* attrib emulation table */ + char d_attrtyp[NATTR]; /* attrib group table */ + int d_hascolor; /* do we support color */ + short d_dospeed; /* baudrate of tty */ +#ifdef FONT + char d_c0_tab[256]; /* conversion for C0 */ + char ***d_xtable; /* char translation table */ +#endif + int d_UPcost, d_DOcost, d_LEcost, d_NDcost; + int d_CRcost, d_IMcost, d_EIcost, d_NLcost; + int d_printfd; /* fd for vt100 print sequence */ +#ifdef UTMPOK + slot_t d_loginslot; /* offset, where utmp_logintty belongs */ + struct utmp d_utmp_logintty; /* here the original utmp structure is stored */ + int d_loginttymode; +# ifdef _SEQUENT_ + char d_loginhost[100+1]; +# endif /* _SEQUENT_ */ +#endif + int d_blocked; + int d_blocked_fuzz; + struct event d_idleev; /* screen blanker */ +#ifdef BLANKER_PRG + int d_blankerpid; + struct event d_blankerev; +#endif +}; + +#ifdef MULTI +# define DISPLAY(x) display->x +#else +extern struct display TheDisplay; +# define DISPLAY(x) TheDisplay.x +#endif + +#define D_user DISPLAY(d_user) +#define D_username (DISPLAY(d_user) ? DISPLAY(d_user)->u_name : 0) +#define D_canvas DISPLAY(d_canvas) +#define D_cvlist DISPLAY(d_cvlist) +#define D_layout DISPLAY(d_layout) +#define D_forecv DISPLAY(d_forecv) +#define D_processinput DISPLAY(d_processinput) +#define D_processinputdata DISPLAY(d_processinputdata) +#define D_vpxmin DISPLAY(d_vpxmin) +#define D_vpxmax DISPLAY(d_vpxmax) +#define D_fore DISPLAY(d_fore) +#define D_other DISPLAY(d_other) +#define D_nonblock DISPLAY(d_nonblock) +#define D_termname DISPLAY(d_termname) +#define D_tentry DISPLAY(d_tentry) +#define D_tcinited DISPLAY(d_tcinited) +#define D_width DISPLAY(d_width) +#define D_height DISPLAY(d_height) +#define D_defwidth DISPLAY(d_defwidth) +#define D_defheight DISPLAY(d_defheight) +#define D_top DISPLAY(d_top) +#define D_bot DISPLAY(d_bot) +#define D_x DISPLAY(d_x) +#define D_y DISPLAY(d_y) +#define D_rend DISPLAY(d_rend) +#define D_col16change DISPLAY(d_col16change) +#define D_atyp DISPLAY(d_atyp) +#define D_mbcs DISPLAY(d_mbcs) +#define D_encoding DISPLAY(d_encoding) +#define D_decodestate DISPLAY(d_decodestate) +#define D_realfont DISPLAY(d_realfont) +#define D_insert DISPLAY(d_insert) +#define D_keypad DISPLAY(d_keypad) +#define D_cursorkeys DISPLAY(d_cursorkeys) +#define D_revvid DISPLAY(d_revvid) +#define D_curvis DISPLAY(d_curvis) +#define D_has_hstatus DISPLAY(d_has_hstatus) +#define D_hstatus DISPLAY(d_hstatus) +#define D_lp_missing DISPLAY(d_lp_missing) +#define D_mouse DISPLAY(d_mouse) +#define D_mouse_parse DISPLAY(d_mouse_parse) +#define D_extmouse DISPLAY(d_extmouse) +#define D_mousetrack DISPLAY(d_mousetrack) +#define D_xtermosc DISPLAY(d_xtermosc) +#define D_lpchar DISPLAY(d_lpchar) +#define D_status DISPLAY(d_status) +#define D_status_time DISPLAY(d_status_time) +#define D_status_bell DISPLAY(d_status_bell) +#define D_status_len DISPLAY(d_status_len) +#define D_status_lastmsg DISPLAY(d_status_lastmsg) +#define D_status_buflen DISPLAY(d_status_buflen) +#define D_status_lastx DISPLAY(d_status_lastx) +#define D_status_lasty DISPLAY(d_status_lasty) +#define D_status_obuflen DISPLAY(d_status_obuflen) +#define D_status_obuffree DISPLAY(d_status_obuffree) +#define D_status_obufpos DISPLAY(d_status_obufpos) +#define D_statusev DISPLAY(d_statusev) +#define D_hstatusev DISPLAY(d_hstatusev) +#define D_kaablamm DISPLAY(d_kaablamm) +#define D_ESCseen DISPLAY(d_ESCseen) +#define D_userpid DISPLAY(d_userpid) +#define D_usertty DISPLAY(d_usertty) +#define D_userfd DISPLAY(d_userfd) +#define D_OldMode DISPLAY(d_OldMode) +#define D_NewMode DISPLAY(d_NewMode) +#define D_flow DISPLAY(d_flow) +#define D_intr DISPLAY(d_intr) +#define D_obuf DISPLAY(d_obuf) +#define D_obuflen DISPLAY(d_obuflen) +#define D_obufmax DISPLAY(d_obufmax) +#define D_obuflenmax DISPLAY(d_obuflenmax) +#define D_obufp DISPLAY(d_obufp) +#define D_obuffree DISPLAY(d_obuffree) +#define D_auto_nuke DISPLAY(d_auto_nuke) +#define D_nseqs DISPLAY(d_nseqs) +#define D_aseqs DISPLAY(d_aseqs) +#define D_seqp DISPLAY(d_seqp) +#define D_seql DISPLAY(d_seql) +#define D_seqh DISPLAY(d_seqh) +#define D_dontmap DISPLAY(d_dontmap) +#define D_mapdefault DISPLAY(d_mapdefault) +#define D_kmaps DISPLAY(d_kmaps) +#define D_tcs DISPLAY(d_tcs) +#define D_attrtab DISPLAY(d_attrtab) +#define D_attrtyp DISPLAY(d_attrtyp) +#define D_hascolor DISPLAY(d_hascolor) +#define D_dospeed DISPLAY(d_dospeed) +#define D_c0_tab DISPLAY(d_c0_tab) +#define D_xtable DISPLAY(d_xtable) +#define D_UPcost DISPLAY(d_UPcost) +#define D_DOcost DISPLAY(d_DOcost) +#define D_LEcost DISPLAY(d_LEcost) +#define D_NDcost DISPLAY(d_NDcost) +#define D_CRcost DISPLAY(d_CRcost) +#define D_IMcost DISPLAY(d_IMcost) +#define D_EIcost DISPLAY(d_EIcost) +#define D_NLcost DISPLAY(d_NLcost) +#define D_printfd DISPLAY(d_printfd) +#define D_loginslot DISPLAY(d_loginslot) +#define D_utmp_logintty DISPLAY(d_utmp_logintty) +#define D_loginttymode DISPLAY(d_loginttymode) +#define D_loginhost DISPLAY(d_loginhost) +#define D_readev DISPLAY(d_readev) +#define D_writeev DISPLAY(d_writeev) +#define D_blockedev DISPLAY(d_blockedev) +#define D_mapev DISPLAY(d_mapev) +#define D_blocked DISPLAY(d_blocked) +#define D_blocked_fuzz DISPLAY(d_blocked_fuzz) +#define D_idleev DISPLAY(d_idleev) +#define D_blankerev DISPLAY(d_blankerev) +#define D_blankerpid DISPLAY(d_blankerpid) + + +#define GRAIN 4096 /* Allocation grain size for output buffer */ +#define OBUF_MAX 256 /* default for obuflimit */ + +#define OUTPUT_BLOCK_SIZE 256 /* Block size of output to tty */ + +#define AddChar(c) \ +do \ + { \ + if (--D_obuffree <= 0) \ + Resize_obuf(); \ + *D_obufp++ = (c); \ + } \ +while (0) + +#define STATUS_OFF 0 +#define STATUS_ON_WIN 1 +#define STATUS_ON_HS 2 + +#define HSTATUS_IGNORE 0 +#define HSTATUS_LASTLINE 1 +#define HSTATUS_MESSAGE 2 +#define HSTATUS_HS 3 +#define HSTATUS_FIRSTLINE 4 +#define HSTATUS_ALWAYS (1<<3) + +#endif /* SCREEN_DISPLAY_H */ + diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 0000000..2681820 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,2 @@ +Makefile +screen.info* @@ -0,0 +1,253 @@ + jw 21.10.93 + 05.05.94 + + screen: frequently asked questions -- known problems -- unimplemented bugs +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + +Q: Why is it impossible to download a file with Kermit/sz/rz when + screen is running? Do I need to set some special variables? + +A: Screen always interprets control-sequences sent by the + applications and translates/optimizes them for the current + terminal type. Screen always parses the user input for its + escape character (CTRL-A). Both are basic screen features and + cannot be switched off. Even if it were possible to switch + screen into a completely transparent mode, you could never switch + between windows, while kermit/sz/rz is downloading a file. You + must wait til the end as kermit/sz/rz will not transmit your + input during a file transfer and as kermit/sz/rz would be very + confused if screen switched away the window containing the + other kermit/sz/rz. Simply detach your screen session for each + file transfer and start the transfer program only from the shell + where you started screen. + +Q: I am using screen with a YYY terminal, which supports the XXX + graphic language. I am very happy with it, except one thing: I + cannot render graphics into screen windows. + +A: You are out of luck there. Screen provides a fixed set of escape + sequences in order to make it possible to switch terminal types. + Screen has to know exactly what the escape sequences do to the + terminal because it must hold an image in memory. Otherwise + screen could not restore the image if you switch to another + window. Because of this you have to change screens escape + sequence parser (ansi.c) to pass the XXX graphics sequences to + the terminal. Of course the graphics will be lost if you switch + to another window. Screen will only honour graphics sequences + that are demanded by an overwhelming majority. + +Q: For some unknown reason, the fifo in /tmp/screens/S-myname is + gone, and i can't resume my screen session. Is there a way to + recreate the fifo? + +A: Screen checks the fifo/socket whenever it receives a SIGCHLD + signal. If missing, the fifo/socket is recreated then. If screen + is running non set-uid the user can issue a 'kill -CHLD + screenpid' directly (it is -CHILD on some systems). Screenpid is + the process-id of the screen process found in a 'ps -x' listing. + But usually this won't work, as screen should be installed set- + uid root. In this case you will not be able to send it a signal, + but the kernel will. It does so, whenever a child of screen + changes its state. Find the process-id (shellpid below) of the + "least important" shell running inside screen. The try 'kill + -STOP shellpid'. If the fifo/socket does not reappear, destroy + the shell process. You sacrify one shell to save the rest. If + nothing works, please do not forget to remove all processes + running in the lost screen session. + +Q: When you start "screen" a page of text comes up to start you + off. Is there a way to get rid of this text as a command line + argument or by using a switch of some sort. + +A: Just put the following line in your ~/.screenrc: + startup_message off + Many peole ask this, although it is in the man page, too :-) + +Q: Start "screen emacs" and run emacs function suspend-emacs + (ctrl-z). The window containing emacs vanishes. + +A: This is a known bug. Unfortunatly there is no easy fix + because this is specified in the POSIX standard. When a new + window is created Screen opens up a new session because the + window has to get the pty as a controlling terminal (a + session can only have one controlling terminal). With the + setsid() call the process also creates a new process + group. This process group is orphaned, because there is no + process in the session which is not in the process + group. Now if the process group leader (i.e. your program) + gets a TTIN/TTOU/TSTP, POSIX states that the kernel must + send a KILL signal to the process group because there is no + one left to continue the process. Even if screen would + try to restart the program, that would be after it received the + KILL signal which cannot be caught or ignored. + + tromey@klab.caltech.edu (Tom Tromey): I've noticed this exact + same problem. I put this in my .emacs file. It seems to work: + + ;; If running under screen, disable C-z. + (if (and (getenv "STY") (not window-system)) + (global-unset-key "\C-z")) + +Q: Screen gets the terminal size wrong and messes up. + +A: Before you start screen: Check with 'stty -a' what the terminal + driver thinks about rows and columns. Check the environment + variables LINES and COLUMNS. Then from within screen check with + the info command (CTRL-A i) what size screen thinks your terminal + is. If correcting tty driver setting and environment variables + does not help, look up the terminal capability definition. First + the TERMCAP environment variable. If this is not set, look up the + terminals name as defined in the environment variable TERM in + /etc/termcap or in the terminfo database with untic or infocmp. + There may be :li=...: and :co=...: or even :ll=...: entries + (cols#... and lines#... when it's terminfo) defined incorrectly. + Either construct your own TERMCAP environment variables with + correct settings, use screens terminfo/termcap command in your + .screenrc file or have the database corrected by the system + administrator. + +Q: Screen messes up the terminal output when I use my favourite ap- + plication. Setting the terminal size does not help. + +A: Probably you got the termcap/terminfo entries wrong. Fixing this + is a three stage procedure. First, find out if terminfo or + termcap is used. If your system only has /etc/termcap, + but not /usr/lib/terminfo/... then you are using termcap. + Easy. But if your system has both, then it depends how the appli- + cation and how screen were linked. Beware, if your applica- + tion runs on another host via rlogin, telnet or the like, you + should check the terminfo/termcap databases there. If you cannot + tell if terminfo or termcap is used (or you just want to be + save), the do all steps in stage 3 in parallel for both + systems (on all envolved hosts). Second: Understand the basic + rules how screen does its terminal emulation. When screen is + started or reattached, it relies on the TERM environment variable + to correctly reflect the terminal type you have physically + in front of you. And the entry should either exist in the system + terminfo/termcap database or be specified via the TERMCAP en- + vironment variable (if screen is using the termcap system). On + the other end, screen understands one set of control codes. It + relies on the application using these codes. This means applica- + tions that run under screen must be able to adapt their con- + trol codes to screen. The application should use the TERM vari- + able and termcap or terminfo library to find out how to drive + its terminal. When running under screen, the terminal is virtual + and is only defined by the set of control codes that screen + understands. The TERM variable is automatically set to + "screen" and the "screen"-entries should exist in the data- + bases. If your application uses hardcoded control codes rather + than a database, you are on your own. Hint: The codes under- + stood by screen are a superset of the very common definition + named "vt100". Look at the documentation of screen. The + codes are listed there. Third: Have the entry "screen" in- + stalled on all hosts or make sure you can live with "vt100". + Check the codes sent by your application, when the TERM variable + is set to "screen". Do not try to set the TERM variable inside + screen to anything other than "screen" or "vt100" or compati- + ble. Thus your application can drive screen correctly. Also take + care that a good entry is installed for your physical terminal + that screen has to drive. Even if the entry was good enough + for your application to drive the terminal directly, screen may + find flaws, as it tries to use other capabilities while op- + timizing the screen output. The screenrc commands + "termcap" and/or "terminfo" may help to fine-tune capabilities + without calling the supervisor to change the database. + +Q: I cannot configure screen. Sed does not work. + +A: The regular expressions used in our configure scrip are too + complicated for GNU sed version 2.03. In this regard it is bug + compatible with Ultrix 3.1 "sed": GNU sed version 2.03 dumps + core with our configure script. Try an older release. E.g. from + ftp.uni-erlangen.de:/pub/utilities/screen/sed-2.02b.tar.gz + +Q: When reattaching a session from a different Workstation, the + DISPLAY environment variable should be updated. Even ``CTLR-A + : setenv DISPLAY newhost:0'' does not work as expected. + +A: Under unix every process has its own environment. The environ- + ment of the SCREEN process can be changed with the `setenv' com- + mand. This however cannot affect the environment of the + shells or applications already running under screen. Subsequently + spawned processes will reflect the changes. One should be aware + of this problem when running applications from very old shells. + Screen is a means for keeping processes alive. + +Q: About once every 5 times I ran the program, rather than getting + a "screen," I got someone elses IRC output/input. + +A: What probably happened is that an IRC process was left running on + a pseudo tty in such a way that the kernel thought the tty was + available for reallocation. You can fix this behaviour by + applying the SunOS 4.1.x tty jumbo patch (100513-04). + +Q: Screen compiled on SunOS 5.3 cannot reattach a detached session. + +A: You are using /usr/ucb/cc, this compiler is wrong. Actually it + links with a C-library that mis-interprets dirent. Try again + with /opt/SUNWspro/bin/cc! + +Q: The "talk" command does not work when Screen is active. + +A: Talk and several other programs rely on entries in the Utmp- + Database (/etc/utmp). On some systems this Database is world + writable, on others it is not. If it is not, screen must be + installed with the appropriate permissions (user or group s-bit) + just like any program that uses PTYs (rlogin, xterm, ...). When + screen cannot write to utmp, you will see messages on you display + which do not belong to any screen window. + When screen can update utmp, it is not guaranteed that it does as + you expect. First this depends on the config.h file defining + UTMPOK, LOGINDEFAULT, and perhaps CAREFULUTMP. Second it depends + on the screenrc files (system wide and per user), if utmp entries + are done. Third, you can control whether windows are logged in + with screens ``login'' command. + +Q: Seteuid() does not work as expected in AIX. Attempting a multi- + user-attach results in a screen-panic: "seteuid: not owner". + +A: This is not a screen problem. According to Kay Nettle + (pkn@cs.utexas.edu) you need the AIX patch PTF 423674. + +Q: When I type cd directory (any directory or just blank) from + within one of the windows in screen, the whole thing just freezes + up. + +A: You display the current working directory in xterm's title bar, + This may be caused by hardcoded ESC-sequences in the shell prompt + or in an cd alias. In Xterm the coding is + ESC ] n ; string_to_display ^G + where n = 1, 2, 3 selects the location of the displayed string. + Screen misinterprets this as the ansi operating system comment + sequence: + ESC ] osc_string + and waits (according to ansi) for the string terminator + ESC \ + Screen versions after 3.5.12 may provide a workaround. + +Q: Mesg or biff cannot be turned on or off while running screen. + +A: Screen failed to change the owner of the pty it uses. You need to + install screen setuid-root. See the file INSTALL for details. + +Q: The cursor left key deletes the characters instead of just moving the + cursor. A redisplay (^Al) brings everything back. + +A: Your terminal emulator treats the backspace as "destructive". You + can probably change this somewhere in the setup. We can't think + of a reason why anybody would want a destructive backspace, but + if you really must have it, add the lines + termcap <TERM> 'bc@:bs@' + terminfo <TERM> 'bc@:bs@' + to your ~/.screenrc (replace <TERM> with the terminal type your + emulator uses). + +Q: I have an old SysV OS (like Motorola SysV68) and sometimes screen + doesn't reset the attributes correctly. A redisplay (^Al) doesn't + make things better. + +A: The libcurses library has a bug if attributes are cleared with + the special ue/se capabilities. As a workaround (other than upgrading + your system) modify 'rmul' (and 'rmso'?) in screen's terminfo entry: + rmul=\E[m, rmso=\E[m diff --git a/doc/Makefile.in b/doc/Makefile.in new file mode 100644 index 0000000..f7c7db7 --- /dev/null +++ b/doc/Makefile.in @@ -0,0 +1,62 @@ +# Makefile for Screen documentation + +srcdir = @srcdir@ +VPATH = @srcdir@ + +DESTDIR = + +prefix = @prefix@ +datarootdir = @datarootdir@ +mandir = @mandir@ +infodir = @infodir@ + +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +MAKEINFO = makeinfo +TEXI2DVI = texi2dvi +TEXI2PDF = texi2pdf + +SHELL = /bin/sh + +all: screen.info + +dvi screen.dvi: screen.texinfo mostlyclean + $(TEXI2DVI) $(srcdir)/screen.texinfo + +pdf screen.pdf: screen.texinfo mostlyclean + $(TEXI2PDF) $(srcdir)/screen.texinfo + +info screen.info: screen.texinfo + @rm -f screen.info* + $(MAKEINFO) $(srcdir)/screen.texinfo -o screen.info + +install: installdirs + $(INSTALL_DATA) $(srcdir)/screen.1 $(DESTDIR)$(mandir)/man1/screen.1 + -$(MAKE) screen.info + -if test -f screen.info; then d=.; else d=$(srcdir); fi; \ + if test -f $$d/screen.info; then \ + for f in $$d/screen.info*; do $(INSTALL_DATA) $$f $(DESTDIR)$(infodir);done; \ + if $(SHELL) -c 'install-info --version' >/dev/null 2>&1; then \ + install-info --info-dir=$(DESTDIR)$(infodir) $$d/screen.info; \ + else true; fi; \ + fi + +uninstall: + rm -f $(DESTDIR)$(mandir)/man1/screen.1 + rm -f $(DESTDIR)$(infodir)/screen.info* + +installdirs: + $(srcdir)/../etc/mkinstalldirs $(DESTDIR)$(mandir)/man1 $(DESTDIR)$(infodir) + +mostlyclean: + -rm -f *.cp *.cps *.fn *.fns *.ky *.kys *.pg *.tp *.vr + -rm -f *.log *.aux *.toc *~ + +clean distclean clobber: mostlyclean + -rm -f *.dvi + +realclean: clean + -rm -f *.info* + +check installcheck: + diff --git a/doc/README.DOTSCREEN b/doc/README.DOTSCREEN new file mode 100644 index 0000000..f17fa37 --- /dev/null +++ b/doc/README.DOTSCREEN @@ -0,0 +1,151 @@ +From bargi@dots.physics.orst.edu Thu Aug 31 23:42 MET 1995 +Received: from faui45.informatik.uni-erlangen.de (root@faui45.informatik.uni-erlangen.de [131.188.34.45]) by immd4.informatik.uni-erlangen.de with ESMTP + id XAA14775 (8.6.12/7.4f-FAU);; Thu, 31 Aug 1995 23:42:15 +0200 +Received: from dots.physics.orst.edu (bargi@dots.PHYSICS.ORST.EDU [128.193.96.106]) by uni-erlangen.de with ESMTP + id XAA03048 (8.6.12/7.4f-FAU); for <screen@uni-erlangen.de>; Thu, 31 Aug 1995 23:42:03 +0200 +Received: (from bargi@localhost) by dots.physics.orst.edu (8.6.11/8.6.9) id OAA15627; Thu, 31 Aug 1995 14:41:47 -0700 +From: Hadi Bargi Rangin <bargi@dots.physics.orst.edu> +Message-Id: <199508312141.OAA15627@dots.physics.orst.edu> +Subject: README.DOTSCREEN +To: screen@uni-erlangen.de +Date: Thu, 31 Aug 1995 14:41:47 -0700 (PDT) +Cc: bargi@dots.physics.orst.edu (Hadi Bargi Rangin) +X-Mailer: ELM [version 2.4 PL24] +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Content-Type: text/plain; charset=US-ASCII +Content-Length: 5423 +Status: RO + +Hallo, + + leider war readme file fuer dotscreen nicht in unserem dotscreen-Packet, +deshalb schicke ich es Euch nachtraeglich. Nachdem Ihr unseren packet +getestet habt, koennen wir Euch anrufen und vielleicht mehr ueber die +Einzelnheiten sprechen, ob wir unsere Weiterentwicklung koordinieren +wollen. + +Danke, + +Gruss, +Hadi + +=============================================================================== +Quick introduction to dotscreen +------------------------------- + +1. Introduction +2. Is dotscreen different as screen? +3. Installation instructions +4. Functions description +5. Further development + + +1. Introduction +=============== + +Dotscreen, is a system which allow a person direct access to unix via +a braille display. The emphasis is on direct, because the braille +display is connected directly to the serial port on the unix +machine. You no longer must use a dos machine running a terminal +emulation logged into a unix machine. Dotscreen is built on top of +screen, a powerful full-screen window manager for unix tty terminals. +Screen keeps track of what is being displayed in each window that it +is managing so that it can easily switch back and forth between these +windows. Dotscreen makes this stored window information available via +a braille display. Dotscreen only allows access to tty terminal +sessions, it does not allow access to X-Windows, however, it will run +in an xterm window. Currently, it works with the TSI Navigator 40 and +the TSI PowerBraille 40 braille displays. We plan to support other +displays as demand and information about other displays is made +available to us + +2. Is dotscreen different than screen? +====================================== + +All of screens functions still work in dotscreen. A few of the +functions are not accessible via braille, but we expect to remedy that +in future releases. + +3. Installation instructions +============================ + +Please read the INSTALL file for full installation instructions. +In addition to those instructions, note that you must create +a .screenrc file and that file must contain the type of braille +display that you are using and the serial device that the display +is connected to. A minimal .screenrc file should contain something +like the following four lines, (these are only examples, please +customize them for your configuration) + + +# example of .screenrc when using braille display +bd_start_braille on +bd_type powerbraille_40 +bd_port /dev/ttyS0 +bd_braille_table /home/gardner/us-braille.tbl +# end of example + + +4. Functions description +======================== + +The basic operation of screen is described in README. The braille +navigation commands are similar to commands usually found on dos +braille screenreaders. Also, because dotscreen is built on top of +screen, the user can switch back and forth easily between many running +applications. The braille commands can be changed any time after +starting screen using the internal screen "C-a :" command line. All +braille commands begin with "bd_"; following is the list of braille +commands: + +bd_start_braille on/off # Starts/stops using braille features on screen +bd_link on/off # links/unlinks braille cursor to/from screen cursor +bd_bell on/off # turn on/off sending bell-signal to terminal +bd_scroll on/off # enables/disables scrolling +bd_skip on/off # skip/don't skip balnk lines +bd_width <integer-value> # number of braille cells that user want to use, + # this value is always <= total number of cells +bd_ncrc <interger-value> # number of cells displayed on the right side + # of physical cursor (default = 1) +bd_info <integer-value> # displays braille/screen cursor position + # depending on its value, (no info: 0, only + # bc-info: 1, only sc-info: 2, bc- and sc-info: 3 +bd_port <serial-device> # serial port which braille display is connected to +bd_braille_table <bl-table> # braille table to be used. German, US and GS + # braille tables are provided +bd_type <braille-display-type> + # braille display type being used + +Note: currently valid value for some parameters: +bd_type: + 1. navigator_40 + 2. powerbraille_40. + +bd_braille_table: + 1. gr-braille.tbl German braille code + 2. us-braille.tbl US computer braille code + 3. gs-braille.tbl GS braille code + +Since the braille tables are in files, you should give the full +pathnames of the files either in .screenrc or using the +internal screen "C-a :" command line. + +5. Further development +====================== + +As mentioned above, currently Dotscreen works with Telesensory braille +displays the PowerBraille and the Navigator because Telesensory has +given us the information needed to program their braille display. We +plan to add support for other braille displays when and if we get the +requisite information from the braille display manufacturer. +Also some things such as cursor navigation from the braille display +have not been implemented. If you find a feature missing that +you wish to have, please contact us. + +This software has been developed within the Science Access Project at +Oregon State University under the direction of John Gardner. + +Authors: Hadi Bargi Rangin (bargi@dots.physics.orst.edu) + Bill Barry (barryb@dots.physics.orst.edu) + diff --git a/doc/fdpat.ps b/doc/fdpat.ps new file mode 100644 index 0000000..6b10099 --- /dev/null +++ b/doc/fdpat.ps @@ -0,0 +1,6501 @@ +%!PS-Adobe-2.0 EPSF-1.2 +%%DocumentFonts: Courier Helvetica-Bold Times-Roman +%%Pages: 1 +%%BoundingBox: 5 34 589 773 +%%EndComments + +/arrowHeight 10 def +/arrowWidth 5 def + +/IdrawDict 53 dict def +IdrawDict begin + +/reencodeISO { +dup dup findfont dup length dict begin +{ 1 index /FID ne { def }{ pop pop } ifelse } forall +/Encoding ISOLatin1Encoding def +currentdict end definefont +} def + +/ISOLatin1Encoding [ +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright +/parenleft/parenright/asterisk/plus/comma/minus/period/slash +/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon +/less/equal/greater/question/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N +/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash/bracketright +/asciicircum/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m +/n/o/p/q/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/asciitilde +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/dotlessi/grave/acute/circumflex/tilde/macron/breve +/dotaccent/dieresis/.notdef/ring/cedilla/.notdef/hungarumlaut +/ogonek/caron/space/exclamdown/cent/sterling/currency/yen/brokenbar +/section/dieresis/copyright/ordfeminine/guillemotleft/logicalnot +/hyphen/registered/macron/degree/plusminus/twosuperior/threesuperior +/acute/mu/paragraph/periodcentered/cedilla/onesuperior/ordmasculine +/guillemotright/onequarter/onehalf/threequarters/questiondown +/Agrave/Aacute/Acircumflex/Atilde/Adieresis/Aring/AE/Ccedilla +/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute/Icircumflex +/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis +/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute +/Thorn/germandbls/agrave/aacute/acircumflex/atilde/adieresis +/aring/ae/ccedilla/egrave/eacute/ecircumflex/edieresis/igrave +/iacute/icircumflex/idieresis/eth/ntilde/ograve/oacute/ocircumflex +/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex/udieresis +/yacute/thorn/ydieresis +] def +/Courier reencodeISO def +/Helvetica-Bold reencodeISO def +/Times-Roman reencodeISO def + +/none null def +/numGraphicParameters 17 def +/stringLimit 65535 def + +/Begin { +save +numGraphicParameters dict begin +} def + +/End { +end +restore +} def + +/SetB { +dup type /nulltype eq { +pop +false /brushRightArrow idef +false /brushLeftArrow idef +true /brushNone idef +} { +/brushDashOffset idef +/brushDashArray idef +0 ne /brushRightArrow idef +0 ne /brushLeftArrow idef +/brushWidth idef +false /brushNone idef +} ifelse +} def + +/SetCFg { +/fgblue idef +/fggreen idef +/fgred idef +} def + +/SetCBg { +/bgblue idef +/bggreen idef +/bgred idef +} def + +/SetF { +/printSize idef +/printFont idef +} def + +/SetP { +dup type /nulltype eq { +pop true /patternNone idef +} { +dup -1 eq { +/patternGrayLevel idef +/patternString idef +} { +/patternGrayLevel idef +} ifelse +false /patternNone idef +} ifelse +} def + +/BSpl { +0 begin +storexyn +newpath +n 1 gt { +0 0 0 0 0 0 1 1 true subspline +n 2 gt { +0 0 0 0 1 1 2 2 false subspline +1 1 n 3 sub { +/i exch def +i 1 sub dup i dup i 1 add dup i 2 add dup false subspline +} for +n 3 sub dup n 2 sub dup n 1 sub dup 2 copy false subspline +} if +n 2 sub dup n 1 sub dup 2 copy 2 copy false subspline +patternNone not brushLeftArrow not brushRightArrow not and and { ifill } if +brushNone not { istroke } if +0 0 1 1 leftarrow +n 2 sub dup n 1 sub dup rightarrow +} if +end +} dup 0 4 dict put def + +/Circ { +newpath +0 360 arc +patternNone not { ifill } if +brushNone not { istroke } if +} def + +/CBSpl { +0 begin +dup 2 gt { +storexyn +newpath +n 1 sub dup 0 0 1 1 2 2 true subspline +1 1 n 3 sub { +/i exch def +i 1 sub dup i dup i 1 add dup i 2 add dup false subspline +} for +n 3 sub dup n 2 sub dup n 1 sub dup 0 0 false subspline +n 2 sub dup n 1 sub dup 0 0 1 1 false subspline +patternNone not { ifill } if +brushNone not { istroke } if +} { +Poly +} ifelse +end +} dup 0 4 dict put def + +/Elli { +0 begin +newpath +4 2 roll +translate +scale +0 0 1 0 360 arc +patternNone not { ifill } if +brushNone not { istroke } if +end +} dup 0 1 dict put def + +/Line { +0 begin +2 storexyn +newpath +x 0 get y 0 get moveto +x 1 get y 1 get lineto +brushNone not { istroke } if +0 0 1 1 leftarrow +0 0 1 1 rightarrow +end +} dup 0 4 dict put def + +/MLine { +0 begin +storexyn +newpath +n 1 gt { +x 0 get y 0 get moveto +1 1 n 1 sub { +/i exch def +x i get y i get lineto +} for +patternNone not brushLeftArrow not brushRightArrow not and and { ifill } if +brushNone not { istroke } if +0 0 1 1 leftarrow +n 2 sub dup n 1 sub dup rightarrow +} if +end +} dup 0 4 dict put def + +/Poly { +3 1 roll +newpath +moveto +-1 add +{ lineto } repeat +closepath +patternNone not { ifill } if +brushNone not { istroke } if +} def + +/Rect { +0 begin +/t exch def +/r exch def +/b exch def +/l exch def +newpath +l b moveto +l t lineto +r t lineto +r b lineto +closepath +patternNone not { ifill } if +brushNone not { istroke } if +end +} dup 0 4 dict put def + +/Text { +ishow +} def + +/idef { +dup where { pop pop pop } { exch def } ifelse +} def + +/ifill { +0 begin +gsave +patternGrayLevel -1 ne { +fgred bgred fgred sub patternGrayLevel mul add +fggreen bggreen fggreen sub patternGrayLevel mul add +fgblue bgblue fgblue sub patternGrayLevel mul add setrgbcolor +eofill +} { +eoclip +originalCTM setmatrix +pathbbox /t exch def /r exch def /b exch def /l exch def +/w r l sub ceiling cvi def +/h t b sub ceiling cvi def +/imageByteWidth w 8 div ceiling cvi def +/imageHeight h def +bgred bggreen bgblue setrgbcolor +eofill +fgred fggreen fgblue setrgbcolor +w 0 gt h 0 gt and { +l b translate w h scale +w h true [w 0 0 h neg 0 h] { patternproc } imagemask +} if +} ifelse +grestore +end +} dup 0 8 dict put def + +/istroke { +gsave +brushDashOffset -1 eq { +[] 0 setdash +1 setgray +} { +brushDashArray brushDashOffset setdash +fgred fggreen fgblue setrgbcolor +} ifelse +brushWidth setlinewidth +originalCTM setmatrix +stroke +grestore +} def + +/ishow { +0 begin +gsave +fgred fggreen fgblue setrgbcolor +printFont printSize scalefont setfont +/descender 0 printFont /FontBBox get 1 get printFont /FontMatrix +get transform exch pop def +/vertoffset 1 printSize sub descender sub def { +0 vertoffset moveto show +/vertoffset vertoffset printSize sub def +} forall +grestore +end +} dup 0 2 dict put def + +/patternproc { +0 begin +/patternByteLength patternString length def +/patternHeight patternByteLength 8 mul sqrt cvi def +/patternWidth patternHeight def +/patternByteWidth patternWidth 8 idiv def +/imageByteMaxLength imageByteWidth imageHeight mul +stringLimit patternByteWidth sub min def +/imageMaxHeight imageByteMaxLength imageByteWidth idiv patternHeight idiv +patternHeight mul patternHeight max def +/imageHeight imageHeight imageMaxHeight sub store +/imageString imageByteWidth imageMaxHeight mul patternByteWidth add string def +0 1 imageMaxHeight 1 sub { +/y exch def +/patternRow y patternByteWidth mul patternByteLength mod def +/patternRowString patternString patternRow patternByteWidth getinterval def +/imageRow y imageByteWidth mul def +0 patternByteWidth imageByteWidth 1 sub { +/x exch def +imageString imageRow x add patternRowString putinterval +} for +} for +imageString +end +} dup 0 12 dict put def + +/min { +dup 3 2 roll dup 4 3 roll lt { exch } if pop +} def + +/max { +dup 3 2 roll dup 4 3 roll gt { exch } if pop +} def + +/midpoint { +0 begin +/y1 exch def +/x1 exch def +/y0 exch def +/x0 exch def +x0 x1 add 2 div +y0 y1 add 2 div +end +} dup 0 4 dict put def + +/thirdpoint { +0 begin +/y1 exch def +/x1 exch def +/y0 exch def +/x0 exch def +x0 2 mul x1 add 3 div +y0 2 mul y1 add 3 div +end +} dup 0 4 dict put def + +/subspline { +0 begin +/movetoNeeded exch def +y exch get /y3 exch def +x exch get /x3 exch def +y exch get /y2 exch def +x exch get /x2 exch def +y exch get /y1 exch def +x exch get /x1 exch def +y exch get /y0 exch def +x exch get /x0 exch def +x1 y1 x2 y2 thirdpoint +/p1y exch def +/p1x exch def +x2 y2 x1 y1 thirdpoint +/p2y exch def +/p2x exch def +x1 y1 x0 y0 thirdpoint +p1x p1y midpoint +/p0y exch def +/p0x exch def +x2 y2 x3 y3 thirdpoint +p2x p2y midpoint +/p3y exch def +/p3x exch def +movetoNeeded { p0x p0y moveto } if +p1x p1y p2x p2y p3x p3y curveto +end +} dup 0 17 dict put def + +/storexyn { +/n exch def +/y n array def +/x n array def +n 1 sub -1 0 { +/i exch def +y i 3 2 roll put +x i 3 2 roll put +} for +} def + +%%EndProlog + +%%BeginIdrawPrologue +/arrowhead { +0 begin +transform originalCTM itransform +/taily exch def +/tailx exch def +transform originalCTM itransform +/tipy exch def +/tipx exch def +/dy tipy taily sub def +/dx tipx tailx sub def +/angle dx 0 ne dy 0 ne or { dy dx atan } { 90 } ifelse def +gsave +originalCTM setmatrix +tipx tipy translate +angle rotate +newpath +arrowHeight neg arrowWidth 2 div moveto +0 0 lineto +arrowHeight neg arrowWidth 2 div neg lineto +patternNone not { +originalCTM setmatrix +/padtip arrowHeight 2 exp 0.25 arrowWidth 2 exp mul add sqrt brushWidth mul +arrowWidth div def +/padtail brushWidth 2 div def +tipx tipy translate +angle rotate +padtip 0 translate +arrowHeight padtip add padtail add arrowHeight div dup scale +arrowheadpath +ifill +} if +brushNone not { +originalCTM setmatrix +tipx tipy translate +angle rotate +arrowheadpath +istroke +} if +grestore +end +} dup 0 9 dict put def + +/arrowheadpath { +newpath +arrowHeight neg arrowWidth 2 div moveto +0 0 lineto +arrowHeight neg arrowWidth 2 div neg lineto +} def + +/leftarrow { +0 begin +y exch get /taily exch def +x exch get /tailx exch def +y exch get /tipy exch def +x exch get /tipx exch def +brushLeftArrow { tipx tipy tailx taily arrowhead } if +end +} dup 0 4 dict put def + +/rightarrow { +0 begin +y exch get /tipy exch def +x exch get /tipx exch def +y exch get /taily exch def +x exch get /tailx exch def +brushRightArrow { tipx tipy tailx taily arrowhead } if +end +} dup 0 4 dict put def + +%%EndIdrawPrologue + +%I Idraw 10 Grid 8.86154 8.86154 + +%%Page: 1 1 + +Begin +%I b u +%I cfg u +%I cbg u +%I f u +%I p u +%I t +[ .959646 0 0 .959646 0 0 ] concat +/originalCTM matrix currentmatrix def + +Begin %I Line +%I b 65535 +0 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +none SetP %I p n +%I t +[ .833333 -0 -0 .833333 -23.3333 378.333 ] concat +%I +37 397 764 397 Line +%I 1 +End + +Begin %I Line +%I b 65535 +0 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +none SetP %I p n +%I t +[ .833333 -0 -0 .833333 -23.3333 282.5 ] concat +%I +37 397 764 397 Line +%I 1 +End + +Begin %I Line +%I b 65535 +0 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +none SetP %I p n +%I t +[ .833333 -0 -0 .833333 -24.1667 185.833 ] concat +%I +37 397 764 397 Line +%I 1 +End + +Begin %I Line +%I b 65535 +0 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +none SetP %I p n +%I t +[ .833333 -0 -0 .833333 -23.3333 -5.83331 ] concat +%I +37 397 764 397 Line +%I 1 +End + +Begin %I Line +%I b 65535 +0 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +none SetP %I p n +%I t +[ .833333 -0 -0 .833333 -23.3333 -101.667 ] concat +%I +37 397 764 397 Line +%I 1 +End + +Begin %I Line +%I b 65535 +0 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +none SetP %I p n +%I t +[ .833333 -0 -0 .833333 -23.3333 -197.5 ] concat +%I +37 397 764 397 Line +%I 1 +End + +Begin %I Line +%I b 65535 +0 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +none SetP %I p n +%I t +[ .833333 -0 -0 .833333 -23.3333 392.5 ] concat +%I +37 34 764 34 Line +%I 1 +End + +Begin %I Line +%I b 65535 +0 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +none SetP %I p n +%I t +[ .833333 -0 -0 .833333 -15.8333 2.5 ] concat +%I +258 963 258 41 Line +%I 1 +End + +Begin %I Line +%I b 65535 +0 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +none SetP %I p n +%I t +[ .833333 -0 -0 .833333 198.833 3 ] concat +%I +258 963 258 41 Line +%I 1 +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f -*-courier-medium-r-normal-*-8-*-*-*-*-*-*-* +Courier 8 SetF +%I t +[ 1 0 0 1 213.667 797 ] concat +%I +[ +(Subshell, error back) +] Text +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f -*-courier-medium-r-normal-*-8-*-*-*-*-*-*-* +Courier 8 SetF +%I t +[ 1 0 0 1 428 797 ] concat +%I +[ +(Input echo filter) +] Text +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f -*-courier-medium-r-normal-*-8-*-*-*-*-*-*-* +Courier 8 SetF +%I t +[ 1 0 0 1 22.8333 704.333 ] concat +%I +[ +(Input filter) +] Text +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f -*-courier-medium-r-normal-*-8-*-*-*-*-*-*-* +Courier 8 SetF +%I t +[ 1 0 0 1 214.833 704.333 ] concat +%I +[ +(Input Filter, error back) +] Text +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f -*-courier-medium-r-normal-*-8-*-*-*-*-*-*-* +Courier 8 SetF +%I t +[ 1 0 0 1 428.833 704.333 ] concat +%I +[ +(input filter, error echo) +] Text +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f -*-courier-medium-r-normal-*-8-*-*-*-*-*-*-* +Courier 8 SetF +%I t +[ 1 0 0 1 214 607.5 ] concat +%I +[ +(Output filter) +] Text +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f -*-courier-medium-r-normal-*-8-*-*-*-*-*-*-* +Courier 8 SetF +%I t +[ 1 0 0 1 428 607.5 ] concat +%I +[ +(Error robot) +] Text +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-helvetica-bold-r-*-140-* +Helvetica-Bold 14 SetF +%I t +[ 1.6 0 0 1.6 15 729.966 ] concat +%I +[ +(...) +] Text +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-helvetica-bold-r-*-140-* +Helvetica-Bold 14 SetF +%I t +[ 1.6 0 0 1.6 15 57.4667 ] concat +%I +[ +(...) +] Text +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-helvetica-bold-r-*-140-* +Helvetica-Bold 14 SetF +%I t +[ 1.6 0 0 1.6 214.167 57.4667 ] concat +%I +[ +(...) +] Text +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-helvetica-bold-r-*-140-* +Helvetica-Bold 14 SetF +%I t +[ 1.6 0 0 1.6 428.333 57.4667 ] concat +%I +[ +(...) +] Text +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 190.833 45.8332 ] concat +%I 5 +161 743 +179 743 +205 743 +205 716 +205 707 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 45.8332 ] concat +%I 2 +232 778 +55 778 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 190.833 45.8332 ] concat +%I 2 +55 743 +143 743 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 -50.0001 ] concat +%I 5 +196 707 +196 716 +196 734 +179 734 +161 734 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 -50.0001 ] concat +%I 5 +161 743 +179 743 +205 743 +205 716 +205 707 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 -57.5001 ] concat +%I 2 +232 778 +55 778 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 -50.0001 ] concat +%I 2 +55 743 +143 743 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 -50.0001 ] concat +%I 2 +143 734 +55 734 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 -50.0001 ] concat +%I 5 +489 760 +471 760 +462 760 +462 752 +462 707 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 141.667 ] concat +%I 5 +188 707 +188 716 +188 725 +179 725 +161 725 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 141.667 ] concat +%I 5 +196 707 +196 716 +196 734 +179 734 +161 734 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 141.667 ] concat +%I 5 +161 743 +179 743 +205 743 +205 716 +205 707 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 134.167 ] concat +%I 2 +232 778 +55 778 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 141.667 ] concat +%I 2 +55 743 +143 743 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 141.667 ] concat +%I 2 +143 734 +55 734 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 -145.833 ] concat +%I 5 +188 707 +188 716 +188 725 +179 725 +161 725 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 -153.333 ] concat +%I 2 +143 734 +55 734 +2 BSpl +%I 1 +End + +Begin %I Pict +%I b 65535 +1 0 0 [] 0 SetB +%I cfg u +%I cbg u +%I f u +%I p u +%I t +[ 1 0 0 1 0 -671.666 ] concat + +Begin %I Pict +%I b u +%I cfg u +%I cbg u +%I f u +%I p u +%I t +[ 1 0 0 1 214.167 -0.000366211 ] concat + +Begin %I Poly +%I b 65535 +2 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 141.667 ] concat +%I 4 +143 716 +161 716 +161 752 +143 752 +4 Poly +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-times-medium-r-*-140-* +Times-Roman 14 SetF +%I t +[ 1 0 0 1 99.8333 758.833 ] concat +%I +[ +(P) +] Text +End + +End %I eop + +Begin %I Pict +%I b u +%I cfg u +%I cbg u +%I f u +%I p u +%I t +[ 1 0 0 1 221.667 -0.000366211 ] concat + +Begin %I Poly +%I b 65535 +2 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 141.667 ] concat +%I 4 +223 752 +241 752 +241 787 +223 787 +4 Poly +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-times-medium-r-*-140-* +Times-Roman 14 SetF +%I t +[ 1 0 0 1 164 788.416 ] concat +%I +[ +(W) +] Text +End + +End %I eop + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-times-medium-r-*-140-* +Times-Roman 14 SetF +%I t +[ 1 0 0 1 344.333 725.833 ] concat +%I +[ +(210) +] Text +End + +End %I eop + +Begin %I Pict +%I b 65535 +1 0 0 [] 0 SetB +%I cfg u +%I cbg u +%I f u +%I p u +%I t +[ 1 0 0 1 -214.167 -671.666 ] concat + +Begin %I Pict +%I b u +%I cfg u +%I cbg u +%I f u +%I p u +%I t +[ 1 0 0 1 214.167 -0.000366211 ] concat + +Begin %I Poly +%I b 65535 +2 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 141.667 ] concat +%I 4 +143 716 +161 716 +161 752 +143 752 +4 Poly +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-times-medium-r-*-140-* +Times-Roman 14 SetF +%I t +[ 1 0 0 1 99.8333 758.833 ] concat +%I +[ +(P) +] Text +End + +End %I eop + +Begin %I Pict +%I b u +%I cfg u +%I cbg u +%I f u +%I p u +%I t +[ 1 0 0 1 221.667 -0.000366211 ] concat + +Begin %I Poly +%I b 65535 +2 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 141.667 ] concat +%I 4 +223 752 +241 752 +241 787 +223 787 +4 Poly +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-times-medium-r-*-140-* +Times-Roman 14 SetF +%I t +[ 1 0 0 1 164 788.416 ] concat +%I +[ +(W) +] Text +End + +End %I eop + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-times-medium-r-*-140-* +Times-Roman 14 SetF +%I t +[ 1 0 0 1 344.333 725.833 ] concat +%I +[ +(210) +] Text +End + +End %I eop + +Begin %I Pict +%I b 65535 +1 0 0 [] 0 SetB +%I cfg u +%I cbg u +%I f u +%I p u +%I t +[ 1 0 0 1 199.166 -671.666 ] concat + +Begin %I Pict +%I b u +%I cfg u +%I cbg u +%I f u +%I p u +%I t +[ 1 0 0 1 214.167 -0.000366211 ] concat + +Begin %I Poly +%I b 65535 +2 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 141.667 ] concat +%I 4 +143 716 +161 716 +161 752 +143 752 +4 Poly +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-times-medium-r-*-140-* +Times-Roman 14 SetF +%I t +[ 1 0 0 1 99.8333 758.833 ] concat +%I +[ +(P) +] Text +End + +End %I eop + +Begin %I Pict +%I b u +%I cfg u +%I cbg u +%I f u +%I p u +%I t +[ 1 0 0 1 221.667 -0.000366211 ] concat + +Begin %I Poly +%I b 65535 +2 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 141.667 ] concat +%I 4 +223 752 +241 752 +241 787 +223 787 +4 Poly +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-times-medium-r-*-140-* +Times-Roman 14 SetF +%I t +[ 1 0 0 1 164 788.416 ] concat +%I +[ +(W) +] Text +End + +End %I eop + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-times-medium-r-*-140-* +Times-Roman 14 SetF +%I t +[ 1 0 0 1 344.333 725.833 ] concat +%I +[ +(210) +] Text +End + +End %I eop + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 141.667 ] concat +%I 5 +444 707 +444 760 +444 778 +471 778 +489 778 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 190.833 141.667 ] concat +%I 5 +196 707 +196 716 +196 734 +179 734 +161 734 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 190.833 141.667 ] concat +%I 5 +161 743 +179 743 +205 743 +205 716 +205 707 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 190.833 134.167 ] concat +%I 2 +232 778 +55 778 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 190.833 141.667 ] concat +%I 2 +55 743 +143 743 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 190.833 141.667 ] concat +%I 2 +143 734 +55 734 +2 BSpl +%I 1 +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-helvetica-bold-r-*-140-* +Helvetica-Bold 14 SetF +%I t +[ 1.6 0 0 1.6 428.333 729.966 ] concat +%I +[ +(..: .:. .::) +] Text +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 45.8332 ] concat +%I 2 +55 743 +143 743 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 190.833 30.8334 ] concat +%I 2 +232 778 +55 778 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 45 ] concat +%I 5 +444 707 +444 760 +444 778 +471 778 +489 778 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 390 141.667 ] concat +%I 5 +196 707 +196 716 +196 734 +179 734 +161 734 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 390 141.667 ] concat +%I 5 +161 743 +179 743 +205 743 +205 716 +205 707 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 390 141.667 ] concat +%I 2 +55 743 +143 743 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 390 141.667 ] concat +%I 2 +143 734 +55 734 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 390 134.167 ] concat +%I 2 +232 778 +55 778 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 175.833 237.5 ] concat +%I 6 +400 619 +391 619 +365 619 +365 645 +391 645 +489 645 +6 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 390 141.667 ] concat +%I 5 +188 707 +188 716 +188 725 +179 725 +161 725 +5 BSpl +%I 1 +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-helvetica-bold-r-*-140-* +Helvetica-Bold 14 SetF +%I t +[ 1.6 0 0 1.6 15 633.3 ] concat +%I +[ +(.!.) +] Text +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 45.8333 ] concat +%I 5 +188 707 +188 716 +188 725 +179 725 +161 725 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 38.3334 ] concat +%I 2 +143 734 +55 734 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -237.5 45.8332 ] concat +%I 5 +453 707 +453 752 +453 769 +471 769 +489 769 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 45.8333 ] concat +%I 5 +161 743 +179 743 +205 743 +205 716 +205 707 +5 BSpl +%I 1 +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-helvetica-bold-r-*-140-* +Helvetica-Bold 14 SetF +%I t +[ 1.6 0 0 1.6 214.167 633.3 ] concat +%I +[ +(.!!) +] Text +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 45.8332 ] concat +%I 5 +453 707 +453 752 +453 769 +471 769 +489 769 +5 BSpl +%I 1 +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-helvetica-bold-r-*-140-* +Helvetica-Bold 14 SetF +%I t +[ 1.6 0 0 1.6 214.167 729.966 ] concat +%I +[ +(..!) +] Text +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 390 45.8332 ] concat +%I 5 +161 743 +179 743 +205 743 +205 716 +205 707 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 390 45.8332 ] concat +%I 2 +55 743 +143 743 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 390 45.8334 ] concat +%I 2 +232 778 +55 778 +2 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 175.833 45.8332 ] concat +%I 5 +453 707 +453 752 +453 769 +471 769 +489 769 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 175.833 141.667 ] concat +%I 6 +400 619 +391 619 +365 619 +365 645 +391 645 +489 645 +6 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 390 45.8333 ] concat +%I 2 +143 734 +55 734 +2 BSpl +%I 1 +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-helvetica-bold-r-*-140-* +Helvetica-Bold 14 SetF +%I t +[ 1.6 0 0 1.6 428.333 633.3 ] concat +%I +[ +(.!:) +] Text +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-helvetica-bold-r-*-140-* +Helvetica-Bold 14 SetF +%I t +[ 1.6 0 0 1.6 15 537.466 ] concat +%I +[ +(.:!) +] Text +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -236.667 -50.8333 ] concat +%I 5 +444 707 +444 760 +444 778 +471 778 +489 778 +5 BSpl +%I 1 +End + +Begin %I BSpl +%I b 65535 +1 0 1 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -237.5 45.8333 ] concat +%I 6 +400 619 +391 619 +365 619 +365 645 +391 645 +489 645 +6 BSpl +%I 1 +End + +Begin %I Pict +%I b 65535 +1 0 0 [] 0 SetB +%I cfg u +%I cbg u +%I f u +%I p u +%I t u + +Begin %I Pict +%I b u +%I cfg u +%I cbg u +%I f u +%I p u +%I t +[ 1 0 0 1 214.167 -0.000366211 ] concat + +Begin %I Poly +%I b 65535 +2 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 141.667 ] concat +%I 4 +143 716 +161 716 +161 752 +143 752 +4 Poly +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-times-medium-r-*-140-* +Times-Roman 14 SetF +%I t +[ 1 0 0 1 99.8333 758.833 ] concat +%I +[ +(P) +] Text +End + +End %I eop + +Begin %I Pict +%I b u +%I cfg u +%I cbg u +%I f u +%I p u +%I t +[ 1 0 0 1 221.667 -0.000366211 ] concat + +Begin %I Poly +%I b 65535 +2 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 141.667 ] concat +%I 4 +223 752 +241 752 +241 787 +223 787 +4 Poly +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-times-medium-r-*-140-* +Times-Roman 14 SetF +%I t +[ 1 0 0 1 164 788.416 ] concat +%I +[ +(W) +] Text +End + +End %I eop + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-times-medium-r-*-140-* +Times-Roman 14 SetF +%I t +[ 1 0 0 1 344.333 725.833 ] concat +%I +[ +(210) +] Text +End + +End %I eop + +Begin %I Pict +%I b 65535 +1 0 0 [] 0 SetB +%I cfg u +%I cbg u +%I f u +%I p u +%I t +[ 1 0 0 1 -214.167 6.10352e-05 ] concat + +Begin %I Pict +%I b u +%I cfg u +%I cbg u +%I f u +%I p u +%I t +[ 1 0 0 1 214.167 -0.000366211 ] concat + +Begin %I Poly +%I b 65535 +2 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 141.667 ] concat +%I 4 +143 716 +161 716 +161 752 +143 752 +4 Poly +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-times-medium-r-*-140-* +Times-Roman 14 SetF +%I t +[ 1 0 0 1 99.8333 758.833 ] concat +%I +[ +(P) +] Text +End + +End %I eop + +Begin %I Pict +%I b u +%I cfg u +%I cbg u +%I f u +%I p u +%I t +[ 1 0 0 1 221.667 -0.000366211 ] concat + +Begin %I Poly +%I b 65535 +2 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p +1 SetP +%I t +[ .833333 -0 -0 .833333 -23.3333 141.667 ] concat +%I 4 +223 752 +241 752 +241 787 +223 787 +4 Poly +End + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-times-medium-r-*-140-* +Times-Roman 14 SetF +%I t +[ 1 0 0 1 164 788.416 ] concat +%I +[ +(W) +] Text +End + +End %I eop + +Begin %I Text +%I cfg Black +0 0 0 SetCFg +%I f *-times-medium-r-*-140-* +Times-Roman 14 SetF +%I t +[ 1 0 0 1 344.333 725.833 ] concat +%I +[ +(210) +] Text +End + +End %I eop + +Begin %I Pict +%I b 65535 +1 0 0 [] 0 SetB +%I cfg u +%I cbg u +%I f u +%I p u +%I t +[ 1 0 0 1 199.166 7.62939e-05 ] concat + +Begin %I Pict +%I b u +%I cfg u +%I cbg u +%I f u +%I p u +%I t +[ 1 0 0 1 214.167 -0.000366211 ] concat + +Begin %I Poly +%I b 65535 +2 0 0 [] 0 SetB +%I cfg Black +0 0 0 SetCFg +%I cbg White +1 1 1 SetCBg +%I p |