Softpanorama

May the source be with you, but remember the KISS principle ;-)
Home Switchboard Unix Administration Red Hat TCP/IP Networks Neoliberalism Toxic Managers
(slightly skeptical) Educational society promoting "Back to basics" movement against IT overcomplexity and  bastardization of classic Unix

History of GNU Screen development

News GNU Screen Recommended Links User Manual Reference Attaching to and detaching from screen sessions
Command line options .screenrc examples Splitting screen horizontally Splitting screen Vertically Unix script command
tmux Teraterm OFM Orthodox Editors VIM VNC
Unix History Tips Admin Horror Stories History of GNU Screen development Humor Etc

Screen was initially called BSD screen. It was written by Oliver Laumann (Technical University of Berlin) and published in 1987 in net.sources user group. The version posted was dated Mar 20 18:16:31 1987.  All versions numbered 2.x were copyrighted by Oliver Laumann

Initially it was a full-screen window manager that multiplexes a physical terminal between several processes, typically interactive shells. There is a scrollback history buffer for each virtual terminal and a copy-and-paste mechanism that allows the user to text selection to another window.  Later a brilliant capability of disconnection/reattachment was added which to certain extent defined the screen, although it much more then that.

In 1990 Oliver Laumann handed over maintenance of the code to Jürgen Weigert and Michael Schroeder at the University of Erlangen-Nuremberg, who later moved the project to the GNU Project. They added such features such as split-screen, cut-and-paste, and screen-sharing.

Unfortunately, the development of this important utility was abandoned in Jan 2004. Since 2004 even such a relatively simple enhancement as vertical windows split never managed to reach the production release. Which is really unfortunate but happens with a lot of open source projects that lost the original developer. In case of screen the development was continued but then the second generation of developers lost interest in the project. 

tmux eat screen share while this situation persists.  As of 2017 it have visible share in corporate environment. So we can say it is already represents an entrenched competitor.

Current maintainers as listed in Credits file are

Originally created by Oliver Laumann, this latest version was produced by Wayne 
Davison, Juergen Weigert and Michael Schroeder. 

The last release is dated Aug 7, 2008, corresponding to the version 4.0.3 which is a minor upgrade of version 4.02 dated 27-Jan-2004.  Roots of this version are in the merge of a custom version 2.3PR7 by Wayne Davison and several enhancements to Oliver Laumann's version 2.0.

The current codebase is pretty large, approximately 20K lines of C code. So it's not that easy to get into development, even if you wish to do so. The barrier of entry is pretty high. This estimate of 20K is of cause pretty raw and was is obtained using a very simple metric:

egrep -v "^[ /*]" *.c | wc -l

But it does reflect the current complexity of the program.

Here are some release dates from GNU site:

 screen-3.6.0-3.6.1.diff.gz 16-Jan-1995 03:00  8.6K  
 screen-3.6.1-3.6.2.diff.gz 24-Apr-1995 03:00   19K  
 screen-3.7.1-3.7.2.diff.gz 18-Dec-1996 03:00   10K  
 screen-3.7.1.tar.gz        01-Oct-1996 03:00  368K  
 screen-3.7.2-3.7.4.diff.gz 17-Jul-1997 03:00   33K  
 screen-3.7.2.tar.gz        18-Dec-1996 03:00  372K  
 screen-3.7.4-3.7.6.diff.gz 16-Dec-1998 07:11   27K  
 screen-3.7.4.tar.gz        17-Jul-1997 03:00  379K  
 screen-3.7.6.tar.gz        16-Dec-1998 07:11  382K  
 screen-3.9.4.tar.gz        16-Aug-1999 13:30  488K  
 screen-3.9.8.tar.gz        01-Sep-2000 19:11  493K  
 screen-3.9.9.tar.gz        16-Aug-2001 18:31  513K  
 screen-3.9.10.tar.gz       04-Sep-2001 15:42  509K  
 screen-3.9.11.tar.gz       14-Feb-2002 14:22  705K  
 screen-3.9.15.tar.gz       13-Mar-2003 21:24  810K  
 screen-4.0.2.tar.gz        27-Jan-2004 05:46  821K  
 screen-4.0.2.tar.gz.sig    27-Jan-2004 05:47   65   
 screen-4.0.3.tar.gz        07-Aug-2008 06:30  821K  
 screen-4.0.3.tar.gz.sig    07-Aug-2008 06:30   65  
As we can see the program was actively developed from Oct 1996 to Jan 2004. After that production releases first  stalled and then were completely frozen in Aug 2008. There is still activity in development version in GIT.

The latest enhancement was 2007 attempt to add to screen the ability to split screen vertically:

New screen features available

The development version is available from GNU savanna:
 
From: Michael Schroeder
Subject: New screen features available
Date: Tue, 6 Feb 2007 23:34:31 +0100
User-agent: Mutt/1.4.2.1i
Hi Screen Users,

you probably wonder why the new version of screen is not already
available. Well, it got delayed a bit because of a couple of new
features I've added. The development version is available from
GNU savanna: 

cvs -z3 -d:pserver:address@hidden:/sources/screen co screen

Here are the new features:

- vertical split, complete with resizing

  ^A |   split vertically

  resize [-l] [-h] [-v] <amount>

    -l : resize is local to slice
    -h : resize horizontally
    -v : resize vertically

  amount: 10       resize to size 10
  amount: +10      make 10 bigger
  amount: -10      make 10 smaller
  amount: 10%      make it 10% of all
  amount: =        make all windows equal

- better resize code using weights

  the layout will stay in shape even after heavy resizing

- window groups

  currently a bit ugly to create:
      screen -t <name> //group
  creates a group named <name>

  a group is a subset of windows, ^Aw will only display the
  current group and next/prev will not leave the group.
  Use ^A" to list all windows of the current group (this also
  leaves to group, so that next/prev or a second ^A" will
  show the other windows.

- layouts

  A layout stores the current setup of the display, i.e. all the
  slices and the window assignments.

      layout save Desktop1

  will save the current setup under the name "Desktop1". If you
  detach and reattach later on, the layout will automatically 
  be restored. "Desktop1" will become the current layout.

      layout autosave off

  This turns the autosafe feature off. Layouts are automatically
  saved if autosave is on and the user detachs or switches to
  another layout.

      layout new Desktop2

  Create a new empty layout named "Desktop2".

      layout name "foo"

  Rename the current layout to "foo".

      layout next
      layout prev
      layout load "name"

  Load the next/prev layout / the layout named "name".

      layout attach :last
      layout attach "name"

  Set the layout used when somebody is attaching. Default is ":last",
  this is the layout that was current when the last detach was done.
  
  Besides the restoring of the screen on re-attach, layouts can be
  used to implement a kind of "virtual desktop" in screen. Say
  you put "layout save Desktop1" in your ~/.screenrc. If you
  need a new Desktop, do "^A:layout new Desktop2". You can then
  use "layout next" to switch between both layouts.

So, feedback welcome. You probably have a lot of suggestions and
enhancement requests. Sorry that development is a bit slow at
the moment due to not enough spare time...

Cheers,
  Michael.

-- 
Michael Schroeder           address@hidden
main(_){while(_=~getchar())putchar(~_-1/(~(_|32)/13*2-11)*13);}

But this feature never materialized in production version and exists only as a patch by Bill Pursell (Vertical Split for GNU Screen):

Description

This patch provides a vertical split feature for current releases of GNU Screen. The feature is designed to function in the same manner as screen's existing split command, but dividing the regions vertically instead of horizontally. Both forms of splits can be used together in any combination/quantity/order desired. Something similar is slated to appear in GNU Screen 4.1 soon, and is already available in CVS per this mailing list thread (and has been included in the Debian and Ubuntu screen packages starting with 4.0.3-10). Also, a recent project named ScreenWM is designed specifically to work with a vsplit-patched screen (check it out!).


License

The vertical split patch is written and presently under development by Bill Pursell <[email protected]> and can be freely used and redistributed under the terms of the GNU General Public License (GPL) version 2.


Downloading

The most recent version of the vertical split patch for GNU Screen can be retrieved via the following link:

As the filename implies, it should patch cleanly into pristine GNU Screen 4.0.2 source (just tested, and works fine with 4.0.3 as well). The MD5 checksum can be obtained via this link:

This patch has been downloaded 15723 times.


Bugs

Any assistance (especially in diff/patch form) is appreciated...

It's really unfortunate that vertical split feature was never managed to get into production release for 6 (six)  years. Eric Raymond really should shred his Cathedral and Bazaar paper into peaces and each it mixed with borsch ;-). Also FSF as an organization the collect membership dues can be more then a second SourceForge, providing just an infrastructure for development and nothing more. It should behave more like Debian, then SourceForge... And there should be something like FSF summers of code modeled after Google summers of code. Of course Stallman is now old and completely detached from the software development, so without change of the guard positive changes in FSF role are probably unlikely...

It's really unfortunate that vertical split feature was never managed to get into production release for 6 (six)  years.  Eric Raymond really should shred his Cathedral and Bazaar paper into peaces and each it mixed with borsch ;-). Also FSF as an organization the collect membership dues can be more then a second SourceForge, providing just an infrastructure for development and nothing more. It should behave more like Debian, then SourceForge... And there should be something like FSF summers of code modeled after Google summers of code. Of course Stallman is now old and completely detached from the software development, so without change of the guard positive changes in FSF role are probably unlikely...

In any case this page is a call for developers to put more efforts into this important and brilliant product, a real gem of Unix programming art.


Top Visited
Switchboard
Latest
Past week
Past month

NEWS CONTENTS

Old News ;-)

[Oct 16, 2012] A little history lesson

It's really unfortunate that vertical split feature was never managed to get into production release for 6 (six) years.
2011-03-28 Make 'layout' subcommands work when detached. Sadrul Habib Chowdhury 3 -23/+103
2010-05-11 Fix updating paused regions (in split mode). Sadrul Habib Chowdhury 1 -1/+7
2010-03-08 Fix refresh when double-cell characters end a line Sadrul Habib Chowdhury 2 -0/+16
2010-03-04 Use intelligent screen refresh only in split regions. Sadrul Habib Chowdhury 1 -56/+44
2009-12-17 Fix cursor positioning after a search in copy mode Sadrul Habib Chowdhury 1 -0/+4
2009-12-08 Refresh cleverly to improve scrolling speed. Sadrul Habib Chowdhury 3 -0/+181
2009-07-14 Document the new "-v" argument to the split command. Curtis Brown 2 -8/+24
2008-08-16 Fix a crash with layouts, splits and killed windows Sadrul Habib Chowdhury 2 -23/+26
2008-08-10 Fix a bug involving windowlist, splits and layouts Sadrul Habib Chowdhury 2 -0/+5
2008-08-06 Fix canvas resizing for vertical splits. Sadrul Habib Chowdhury 1 -3/+4
2008-07-22 Update the caption when the group for a window changes. Sadrul Habib Chowdhury 1 -0/+3
2006-10-23 first stab at vertical split mls 10 -219/+495
2005-12-20 From: bill <[email protected]> jnweiger 1 -0/+201
2005-12-16 12.10.2004, 4.00.03jw1 jnweiger 2 -2/+9

[Aug 06, 2010] Re Vertical split in GNU screen

Aug 06, 2010 | Cygwin list

[email protected]

On Fri, Aug 06, 2010 at 03:50:28PM +0200, flo wrote:
Hi, i'm new with mailing-lists use! i'd like to re-talk about a feature with cygwin screen! How can we add the marvellous "Vertical split in GNU screen" with cygwin? I know there were a talk about that between Jeenu V <jeenuv at gmail dot com> and Andrew Schulman <schulman dot andrew at epamail dot epa dot gov> the 19 Apr 2010! Thanks in advance for somebody answer! If i'm not at the right place to talk about this or if i don't use this list in the good way, please don't shout at me, just show me!

Florent LOTTIN

Andrew Schulman-3

this post by [email protected]

> Hi, i'm new with mailing-lists use!
> i'd like to re-talk about a feature with cygwin screen! How can we add
> the marvellous "Vertical split in GNU screen" with cygwin?
> I know there were a talk about that between Jeenu V <jeenuv at gmail
> dot com> and Andrew Schulman <schulman dot andrew at epamail dot epa
> dot gov> the 19 Apr 2010!
> Thanks in advance for somebody answer!
> If i'm not at the right place to talk about this or if i don't use
> this list in the good way, please don't shout at me, just show me!
> Florent LOTTIN

...[show rest of quote]


Hi Florent. There is a patch that's been available for a while, that adds vertical split support to screen. I believe that Debian, Ubuntu, and probably others already incorporate it into their official screen packages.

I remember the exchange with Jeenu, and after that I did go get the patch, build screen with it for Cygwin, and try it out. What I found, if I remember right, is that I saw a lot of what I considered strange behavior, and very little documentation or help about how the vertical split feature is supposed to work. It didn't seem to me to be working very well, so I put it aside.

If people would like to try out the patched version, I can post it as a test release. Maybe you can tell me if the vertical split feature is working the way it's supposed to.

Andrew.


--
Problem reports: http://cygwin.com/problems.html
FAQ: http://cygwin.com/faq/
Documentation: http://cygwin.com/docs.html
Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple

[email protected]
Thanks a lot for your quick answer! I will be pleased if you could
post the version of Screen with vsplit patch as a test release
because i didn't try to compile for cygwin yet!
I will try it and give my feedback! I'm on winxp sp3!
Rgds,
Florent..

2010/8/6 Andrew Schulman <[hidden email]>:


>> Hi, i'm new with mailing-lists use!
>> i'd like to re-talk about a feature with cygwin screen! How can we add
>> the marvellous "Vertical split in GNU screen" with cygwin?
>> I know there were a talk about that between Jeenu V <jeenuv at gmail
>> dot com> and Andrew Schulman <schulman dot andrew at epamail dot epa
>> dot gov> the 19 Apr 2010!
>> Thanks in advance for somebody answer!
>> If i'm not at the right place to talk about this or if i don't use
>> this list in the good way, please don't shout at me, just show me!
>> Florent LOTTIN
>
> Hi Florent. There is a patch that's been available for a while, that adds
> vertical split support to screen. I believe that Debian, Ubuntu, and
> probably others already incorporate it into their official screen packages.
>
> I remember the exchange with Jeenu, and after that I did go get the patch,
> build screen with it for Cygwin, and try it out. What I found, if I
> remember right, is that I saw a lot of what I considered strange behavior,
> and very little documentation or help about how the vertical split feature
> is supposed to work. It didn't seem to me to be working very well, so I
> put it aside.
>
> If people would like to try out the patched version, I can post it as a
> test release. Maybe you can tell me if the vertical split feature is
> working the way it's supposed to.
>
> Andrew.

...[show rest of quote]

Re: First release of screen by Axel Beckert

Oct 8, 2012
Hi,
On Mon, Oct 08, 2012 at 10:58:44AM +0200, Guillaume Quintin wrote:
> Someone told me that screen was very young as a program.
> I went to check here:
> 
> I saw screen v3.7.1 and it was in 1996 so I guess screen is much older.
It is.
> Do you know approximatively the year of the first release of screen?
Screen recently had its 25th birthday and is hence older than Linux. See this Usenet posting from 1987 for its initial announcement: https://groups.google.com/group/net.sources/browse_thread/thread/e55f5059d2329d36

[Nov 07, 2007] New screen features available

The development version is available from GNU savanna:
From: Michael Schroeder
Subject: New screen features available
Date: Tue, 6 Feb 2007 23:34:31 +0100
User-agent: Mutt/1.4.2.1i
Hi Screen Users,

you probably wonder why the new version of screen is not already
available. Well, it got delayed a bit because of a couple of new
features I've added. The development version is available from
GNU savanna: 

cvs -z3 -d:pserver:address@hidden:/sources/screen co screen

Here are the new features:

- vertical split, complete with resizing

  ^A |   split vertically

  resize [-l] [-h] [-v] <amount>

    -l : resize is local to slice
    -h : resize horizontally
    -v : resize vertically

  amount: 10       resize to size 10
  amount: +10      make 10 bigger
  amount: -10      make 10 smaller
  amount: 10%      make it 10% of all
  amount: =        make all windows equal

- better resize code using weights

  the layout will stay in shape even after heavy resizing

- window groups

  currently a bit ugly to create:
      screen -t <name> //group
  creates a group named <name>

  a group is a subset of windows, ^Aw will only display the
  current group and next/prev will not leave the group.
  Use ^A" to list all windows of the current group (this also
  leaves to group, so that next/prev or a second ^A" will
  show the other windows.

- layouts

  A layout stores the current setup of the display, i.e. all the
  slices and the window assignments.

      layout save Desktop1

  will save the current setup under the name "Desktop1". If you
  detach and reattach later on, the layout will automatically 
  be restored. "Desktop1" will become the current layout.

      layout autosave off

  This turns the autosafe feature off. Layouts are automatically
  saved if autosave is on and the user detachs or switches to
  another layout.

      layout new Desktop2

  Create a new empty layout named "Desktop2".

      layout name "foo"

  Rename the current layout to "foo".

      layout next
      layout prev
      layout load "name"

  Load the next/prev layout / the layout named "name".

      layout attach :last
      layout attach "name"

  Set the layout used when somebody is attaching. Default is ":last",
  this is the layout that was current when the last detach was done.
  
  Besides the restoring of the screen on re-attach, layouts can be
  used to implement a kind of "virtual desktop" in screen. Say
  you put "layout save Desktop1" in your ~/.screenrc. If you
  need a new Desktop, do "^A:layout new Desktop2". You can then
  use "layout next" to switch between both layouts.

So, feedback welcome. You probably have a lot of suggestions and
enhancement requests. Sorry that development is a bit slow at
the moment due to not enough spare time...

Cheers,
  Michael.

-- 
Michael Schroeder           address@hidden
main(_){while(_=~getchar())putchar(~_-1/(~(_|32)/13*2-11)*13);}

New screen features -- groups

From: Mag Gam
Subject: New screen features -- groups
Date: Mon, 19 Feb 2007 23:03:26 -0500

I have downloaded the latest-greatest-screen, and it has a new feature called 'group creation' which is a superset of 'windows'. I have a scenario like this:

2 production servers (prod1, prod2)
2 development servers (dev1,dev2)
4 Q/A servers (qa1,qa2,qa3,qa4)

How can I create 3 groups (prod, dev,qa), and have these servers included in them respectivly?

BSD screen manager -- Part 1 of 2 - (nf)

net.sources

Other recipients:

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
# README
# screen.1
# Makefile
# screen.c
# This archive created: Fri Mar 20 18:16:31 1987
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(2025 characters)'
if test -f 'README'
then
echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'
"screen" is a window manager that allows you to handle several independent
screens (UNIX ttys) on a single physical terminal; each screen has its own
set of processes connected to it (typically interactive shells). Each
virtual terminal created by "screen" emulates a DEC VT100 plus several ANSI
X3.64 functions (including DEC VT102 features such as line and character
deletion and insertion).

Since "screen" uses pseudo-ttys, the select system call, and UNIX-domain
sockets, it will not run under a system that does not include these
features of 4.2 and 4.3 BSD UNIX.

This version of ``screen'' is a ``beta-test version'', i.e. I solicit
input on bugs (e.g., I'm not sure if "screen" works correctly on terminals
other than those on which I have been able to test it, namely VT100, VT220,
MDL110, IBM-PC with QNX, KDE-820), misfeatures, or just suggestions for
possible enhancements. After receiving enough of these and incorporating
the pertinent changes into screen, I want to post a final version of
"screen" to mod.sources.

If you want to get an idea how "screen" works but don't want to read the
entire manual do the following:

- call screen without arguments
- wait for the shell prompt; execute some commands
- type ^A ^C (Control-A followed by Control-C)
- wait for the shell prompt; do something in the new window
- type ^A ^A repeatedly to switch between the two windows
- terminate the first shell ("screen" switches to the other window)
- terminate the second shell

If you have got "vttest" (the VT100 test program from mod.sources) you
may want to run it from within "screen" to verify that it correctly
emulates a VT100 on your terminal (except for 132 column mode and
double width/height characters, of course).

Mail bug-reports and useful modifications to:

US...!pyramid!tub!net or Europe...!mcvax!unido!tub!net or [email protected]

Regards,
Oliver Laumann
Technical University of Berlin,
Communications and Operating Systems Research Group.
SHAR_EOF
if test 2025 -ne "`wc -c < 'README'`"
then
echo shar: error transmitting "'README'" '(should have been 2025 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'screen.1'" '(14608 characters)'
if test -f 'screen.1'
then
echo shar: will not over-write existing file "'screen.1'"
else
cat << \SHAR_EOF > 'screen.1'
.if n .ds Q \&"
.if n .ds U \&"
.if t .ds Q ``
.if t .ds U ''
.TH SCREEN 1 "2 March 1987"
.UC 4
.SH NAME
screen \- screen manager with VT100/ANSI terminal emulation
.SH SYNOPSIS
.B screen
[
.B \-a
] [
.B \-e\fIxy\fP
] [
.B \-c \fIcmd args\fP ]
.ta .5i 1.8i
.SH DESCRIPTION
.I screen
is a full-screen window manager that
multiplexes a physical terminal between several processes (typically
interactive shells). Each virtual terminal provides the functions
of the DEC VT100 terminal and, in addition, several control functions
from the ANSI X3.64 (ISO 6429) standard (e.g. insert/delete line).
.PP
When
.I screen
is called, it creates a single window with a shell; the pathname of the
shell is taken from the environment symbol $SHELL; if this is not
defined, \*Q/bin/sh\*U is used.
New windows can be created at any time by calling
.I screen
with the
.B -c
option from within a previously created window;
.B -c
is followed by the full pathname of the program to start in the newly
created window and, optionally, arguments to the program.
For instance,
.IP
screen -c /bin/csh
.PP
will create a window with a C-Shell and switch to that window.
When the process associated with the currently displayed window
terminates (e.g. ^D has been typed to a shell),
.I screen
switches to the previously displayed window;
when no more windows are left,
.I screen
exits.
.SH "COMMAND KEYS"
An easier way to create a new window is to type \*QC-a c\*U (the notation
\*QC-x\*U will be used as a shorthand for Control-x in this manual; x is
an arbitrary letter).
\*QC-a c\*U creates a new window running a shell and switches to that
window immediately, regardless of the state of the process running
in the current window.
.I Screen
recognizes several such commands; each command consists of
\*QC-a\*U followed by a one-letter function.
For convenience, the letter after a \*QC-a\*U can be entered both with or
without the control key pressed (with the exception of
\*QC-a C-a\*U and \*QC-a a\*U; see below), thus, \*QC-a c\*U as well as
\*QC-a C-c\*U can be used to create a window.
.PP
The following commands are recognized by
.IR screen :
.IP "\fBC-a c\fP or \fBC-a C-c\fP"
Create a new window with a shell and switch to that window.
.IP "\fBC-a k\fP or \fBC-a C-k\fP"
Kill the current window and switch to the previously displayed window.
.IP "\fBC-a C-a\fP\0\0\0\0\0"
Switch to the previously displayed window.
.IP "\fBC-a 0\fP to \fBC-a 9\fP"
Switch to the window with the number 0 (1, 2, .., 9, respectively).
When a new window is established, the first available number from the
range 0..9 is assigned to this window.
Thus, the first window can be activated by \*QC-a 0\*U; at most
10 windows can be present at any time.
.IP "\fBC-a space\fP or \fBC-a C-space\fP or \fBC-a n\fP or \fBC-a C-n\fP"
Switch to the next window. This function can be used repeatedly to
cycle through the list of windows.
(Control-space is not supported by all terminals.)
.IP "\fBC-a p\fP or \fBC-a C-p\fP or \fBC-a -\fP"
Switch to the previous window (the opposite of \fBC-a space\fP).
.IP "\fBC-a l\fP or \fBC-a C-l\fP"
Redisplay the current window.
.IP "\fBC-a z\fP or \fBC-a C-z\fP"
Suspend
.IR screen .
.IP "\fBC-a h\fP or \fBC-a C-h\fP"
Write a hardcopy of the current window to the file \*Qhardcopy.\fIn\fP\*U
in the directory in which
.I screen
was called, where \fIn\fP is the number of the current window.
.IP "\fBC-a w\fP or \fBC-a C-w\fP"
Display a list of all windows.
For each window, the number of the window and the process that has been
started in the window is displayed; the current window is marked with a
`*'.
.IP "\fBC-a v\fP or \fBC-a C-v\fP"
Display the version.
.IP "\fBC-a a\fP\0\0\0\0\0"
Send the character \*QC-a\*U to the processes running in the window.
.IP
.PP
The
.B -e
option can be used to specify a different command character and
a character which, when typed immediately after the command character,
generates a literal command character.
The defaults for these two characters are \*QC-a\*U and `a'.
(Note that the function to switch to the previous window is actually the
command character typed twice; for instance, when
.I screen
is called with the option \*Q\fB-e]x\fP\*U (or \*Q\fB-e ]x\fP\*U),
this function becomes \*Q]]\*U).
.SH CUSTOMIZATION
When
.I screen
is invoked, it executes initialization commands from the file `.screenrc'
in the user's home directory.
Commands in `.screenrc' are mainly used to automatically
establish a number of windows each time
.I screen
is called, and to bind functions to specific keys.
Each line in `.screenrc' contains one initialization command; lines
starting with `#' are ignored.
Commands can have arguments; arguments are separated by tabs and spaces
and can be surrounded by single quotes or double quotes.
.PP
The following initialization commands are recognized by
.IR screen :
.PP
.ne 3
.B "escape \fIxy\fP"
.PP
Set the command character to \fIx\fP and the character generating a literal
command character to \fIy\fP (see the -e option above).
.PP
.ne 3
.B "screen [\fIn\fP] [\fIcmds args\fP]"
.PP
Establish a window.
If an optional number \fIn\fP in the range 0..9 is given, the window
number \fIn\fP is assigned to the newly created window (or, if this
number is already in use, the next higher number).
Note that \fIn\fP has a value of zero for the standard shell window
created after `.screenrc' has been read.
If a command is specified after `screen', this command (with the given
arguments) is started in the window; if no command is given, a shell
is created in the window.
Thus, if your `.screenrc' contains the lines
.PP
.nf
# example for .screenrc:
screen 1
screen 2 /usr/ucb/telnet foobar
.fi
.PP
.I screen
creates a shell window (window #1), a window with a TELNET connection
to the machine foobar (window #2), and, finally, a second shell window
(the default window) which gets a window number of zero.
When the initialization is completed,
.I screen
always switches to the default window, so window #0 is displayed
when the above `.screenrc' is used.
.PP
.ne 3
.B "bind \fIkey\fP [\fIfunction\fP | \fIcmd args\fP]"
.PP
Bind a function to a key.
By default, each function provided by
.I screen
is bound to one or more keys as indicated by the above table, e.g. the
function to create a new window is bound to \*QC-c\*U and \*Qc\*U.
The `bind' command can be used to redefine the key bindings and to
define new bindings.
The \fIkey\fP
argument is either a single character, a sequence of the form
\*Q^x\*U meaning \*QC-x\*U, or an octal number specifying the
ASCII code of the character.
If no further argument is given, any previously established binding
for this key is removed.
The \fIfunction\fP argument can be one of the following keywords:
.PP
.nf
shell Create new window with a shell
kill Kill the current window
other Switch to previously displayed window
next Switch to the next window
prev Switch to the previous window
redisplay Redisplay current window
hardcopy Make hardcopy of current window
suspend Suspend \fIscreen\fP
windows Display list of window
version Display the version
select0 Switch to window #0
\0\0...
select9 Switch to window #9
.fi
.PP
In addition, a key can be bound such that a window is created running
a different command than the shell when that key is pressed.
In this case, the pathname of the command optionally followed by
arguments must be given instead of one of the above-listed keywords;
the pathname must start with a `/'.
For example, the commands
.PP
.nf
bind ' ' windows
bind ^f /usr/ucb/telnet foobar
bind 033 /bin/su
.fi
.PP
would bind the space key to the function that displays a list
of windows (that is, the function usually invoked by \*QC-a C-w\*U
or \*QC-a w\*U would also be available as \*QC-a space\*U),
bind \*QC-f\*U to the function \*Qcreate a window with a TELNET
connection to foobar\*U, and bind \*Qescape\*U to the function
that creates a window with a super-user shell.
.SH "VIRTUAL TERMINAL"
.I Screen
prints error messages and other diagnostics in a \fImessage line\fP above
the bottom of the screen.
The message line is removed when a key is pressed or, automatically,
after a couple of seconds.
The message line facility can be used by an application running in
the current window by means of the ANSI \fIPrivacy message\fP
control sequence (for instance, from within the shell, something like
.IP
echo '^[^Hello world^[\e' (where ^[ is an \fIescape\fP)
.PP
can be used to display a message line.
.PP
.I
Screen
never writes in the last position of the screen, unless the boolean
capability `LP' is found in the termcap entry of the terminal.
Usually,
.I screen
cannot predict whether or not a particular terminal scrolls when
a character is written in the last column of the last line;
`LP' indicates that it is safe to write in this position.
Note that the `LP' capability is independent of `am' (automatic
margins); for certain terminals, such as the VT100, it is reasonable
to set `am' as well as `LP' in the corresponding termcap entry
(the VT100 does not move the cursor when a character is written in
the last column of each line).
.PP
.I Screen
puts into the environment of each process started in a newly created
window the symbols \*QWINDOW=\fIn\fP\*U (where \fIn\fP is the number
of the respective window), \*QTERM=screen\*U, and a TERMCAP variable
reflecting the capabilities of the virtual terminal emulated by
.IR screen .
The actual set of capabilities supported by the virtual terminal
depends on the capabilities supported by the physical terminal.
If, for instance, the physical terminal does not support standout mode,
.I screen
does not put the `so' and `se' capabilities into the window's TERMCAP
variable, accordingly.
However, a minimum number of capabilities must be supported by a
terminal in order to run
.IR screen ,
namely scrolling, clear screen, and direct cursor addressing
(in addition,
.I screen
does not run on hardcopy terminals or on terminals that overstrike).
.PP
Some capabilities are only put into the TERMCAP
variable of the virtual terminal if they can be efficiently
implemented by the physical terminal.
For instance, `dl' (delete line) is only put into the TERMCAP
variable if the terminal supports either delete line itself or
scrolling regions.
If
.I screen
is called with the
.B -a
option, \fIall\fP capabilities are put into the environment,
even if
.I screen
must redraw parts of the display in order to implement a function.
.PP
The following is a list of control sequences recognized by
.IR screen .
\*Q(V)\*U and \*Q(A)\*U indicate VT100-specific and ANSI-specific
functions, respectively.
.PP
.nf
.TP 20
.B "ESC E"
Next Line
.TP 20
.B "ESC D"
Index
.TP 20
.B "ESC M"
Reverse Index
.TP 20
.B "ESC H"
Horizontal Tab Set
.TP 20
.B "ESC 7"
(V) Save Cursor and attributes
.TP 20
.B "ESC 8"
(V) Restore Cursor and Attributes
.TP 20
.B "ESC c"
Reset to Initial State
.TP 20
.B "ESC ="
(V) Application Keypad Mode
.TP 20
.B "ESC >"
(V) Numeric Keypad Mode
.TP 20
.B "ESC # 8"
(V) Fill Screen with E's
.TP 20
.B "ESC \e"
(A) String Terminator
.TP 20
.B "ESC ^"
(A) Privacy Message (Message Line)
.TP 20
.B "ESC P"
(A) Device Control String (not used)
.TP 20
.B "ESC _"
(A) Application Program Command (not used)
.TP 20
.B "ESC ]"
(A) Operating System Command (not used)
.TP 20
.B "ESC [ Pn ; Pn H"
Direct Cursor Addressing
.TP 20
.B "ESC [ Pn ; Pn f"
Direct Cursor Addressing
.TP 20
.B "ESC [ Pn J"
Erase in Display
.TP 20
\h'\w'ESC 'u'Pn = None or \fB0\fP
From Cursor to End of Screen
.TP 20
\h'\w'ESC 'u'\fB1\fP
From Beginning of Screen to Cursor
.TP 20
\h'\w'ESC 'u'\fB2\fP
Entire Screen
.TP 20
.B "ESC [ Pn K"
Erase in Line
.TP 20
\h'\w'ESC 'u'Pn = None or \fB0\fP
From Cursor to End of Line
.TP 20
\h'\w'ESC 'u'\fB1\fP
From Beginning of Line to Cursor
.TP 20
\h'\w'ESC 'u'\fB2\fP
Entire Line
.TP 20
.B "ESC [ Pn A"
Cursor Up
.TP 20
.B "ESC [ Pn B"
Cursor Down
.TP 20
.B "ESC [ Pn C"
Cursor Right
.TP 20
.B "ESC [ Pn D"
Cursor Left
.TP 20
.B "ESC [ Ps ;...; Ps m"
Select Graphic Rendition
.TP 20
\h'\w'ESC 'u'Ps = None or \fB0\fP
Default Rendition
.TP 20
\h'\w'ESC 'u'\fB1\fP
Bold
.TP 20
\h'\w'ESC 'u'\fB2\fP
(A) Faint
.TP 20
\h'\w'ESC 'u'\fB3\fP
(A) \fIStandout\fP Mode (ANSI: Italicised)
.TP 20
\h'\w'ESC 'u'\fB4\fP
Underlined
.TP 20
\h'\w'ESC 'u'\fB5\fP
Blinking
.TP 20
\h'\w'ESC 'u'\fB7\fP
Negative Image
.TP 20
\h'\w'ESC 'u'\fB22\fP
(A) Normal Intensity
.TP 20
\h'\w'ESC 'u'\fB23\fP
(A) \fIStandout\fP Mode off (ANSI: Italicised off)
.TP 20
\h'\w'ESC 'u'\fB24\fP
(A) Not Underlined
.TP 20
\h'\w'ESC 'u'\fB25\fP
(A) Not Blinking
.TP 20
\h'\w'ESC 'u'\fB27\fP
(A) Positive Image
.TP 20
.B "ESC [ Pn g"
Tab Clear
.TP 20
\h'\w'ESC 'u'Pn = None or \fB0\fP
Clear Tab at Current Position
.TP 20
\h'\w'ESC 'u'\fB3\fP
Clear All Tabs
.TP 20
.B "ESC [ Pn ; Pn r"
(V) Set Scrolling Region
.TP 20
.B "ESC [ Pn I"
(A) Horizontal Tab
.TP 20
.B "ESC [ Pn Z"
(A) Backward Tab
.TP 20
.B "ESC [ Pn L"
(A) Insert Line
.TP 20
.B "ESC [ Pn M"
(A) Delete Line
.TP 20
.B "ESC [ Pn @"
(A) Insert Character
.TP 20
.B "ESC [ Pn P"
(A) Delete Character
.TP 20
.B "ESC [ Ps ;...; Ps h"
Set Mode
.TP 20
.B "ESC [ Ps ;...; Ps l"
Reset Mode
.TP 20
\h'\w'ESC 'u'Ps = \fB4\fP
(A) Insert Mode
.TP 20
\h'\w'ESC 'u'\fB?5\fP
(V) Visible Bell (\fIOn\fP followed by \fIOff\fP)
.TP 20
\h'\w'ESC 'u'\fB?6\fP
(V) \fIOrigin\fP Mode
.TP 20
\h'\w'ESC 'u'\fB?7\fP
(V) \fIWrap\fP Mode
.fi
.SH FILES
.nf
.ta 2i
$(HOME)/.screenrc \fIscreen\fP initialization commands
.br
$(HOME)/.screen Directory created by \fIscreen\fP
.br
$(HOME)/.screen/\fItty\fP Socket created by \fIscreen\fP
.br
hardcopy.[0-9] Screen images created by the hardcopy function
.br
/etc/termcap Terminal capability data base
.fi
.SH "SEE ALSO"
termcap(5)
.SH AUTHOR
Oliver Laumann
.SH BUGS
Standout mode is not cleared before newline or cursor addressing.
.PP
If `LP' is not set but `am' is set, the last character in the last line is never
written, and it is not correctly re-displayed when the screen is
scrolled up or when a character is deleted in the last line.
.PP
The VT100 \*Qwrap around with cursor addressing\*U bug is not compensated
when
.I screen
is running on a VT100.
.PP
`AL,' `DL', and similar parameterized capabilities are not used if present.
.PP
`dm' (delete mode), `xn', and `xs' are not handled
correctly (they are ignored).
.PP
Different character sets are not supported.
.PP
`ms' is not advertised in the termcap entry (in order to compensate
a bug in
.IR curses (3X)).
.PP
Scrolling regions are only emulated if the physical terminal supports
scrolling regions.
.PP
.I Screen
does not make use of hardware tabs.
SHAR_EOF
if test 14608 -ne "`wc -c < 'screen.1'`"
then
echo shar: error transmitting "'screen.1'" '(should have been 14608 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'Makefile'" '(215 characters)'
if test -f 'Makefile'
then
echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
CFILES= screen.c ansi.c
OFILES= screen.o ansi.o

screen: $(OFILES)
cc $(CFLAGS) -o screen $(OFILES) -ltermcap

screen.o: screen.c screen.h
cc $(CFLAGS) -c screen.c

ansi.o: ansi.c screen.h
cc $(CFLAGS) -c ansi.c
SHAR_EOF
if test 215 -ne "`wc -c < 'Makefile'`"
then
echo shar: error transmitting "'Makefile'" '(should have been 215 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'screen.c'" '(23389 characters)'
if test -f 'screen.c'
then
echo shar: will not over-write existing file "'screen.c'"
else
cat << \SHAR_EOF > 'screen.c'
/* Copyright (c) 1987, Oliver Laumann, Technical University of Berlin.
* Not derived from licensed software.
*
* Permission is granted to freely use, copy, modify, and redistribute
* this software, provided that no attempt is made to gain profit from it,
* the author is not construed to be liable for any results of using the
* software, alterations are clearly marked as such, and this notice is
* not modified.
*/

static char ScreenVersion[] = "screen 1.1b 20-Mar-87";

#include <stdio.h>
#include <sgtty.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include "screen.h"

#define MAXWIN 10
#define MAXARGS 64
#define MAXLINE 1024
#define MSGWAIT 4

#define DEFAULT_SHELL "/bin/sh"

#define Ctrl(c) ((c)&037)

#define PtyProto "/dev/ptyXY"
#define TtyProto "/dev/ttyXY"

extern char *blank, Term[], **environ;
extern rows, cols;
extern status;
extern time_t TimeDisplayed;
extern char AnsiVersion[];
extern short ospeed;
extern errno;
extern sys_nerr;
extern char *sys_errlist[];
extern char *rindex(), *malloc(), *getenv(), *MakeTermcap(), *ttyname();
static SigChld();
static char *Filename(), **SaveArgs();

static char PtyName[32], TtyName[32];
static char *ShellProg;
static char *ShellArgs[2];
static char inbuf[IOSIZE];
static inlen;
static ESCseen;
static GotSignal;
static char SockPath[512];
static char SockDir[] = ".screen";
static char *SockName;
static char *NewEnv[MAXARGS];
static char Esc = Ctrl('a');
static char MetaEsc = 'a';
static char *home;
static HasWindow;

struct mode {
struct sgttyb m_ttyb;
struct tchars m_tchars;
struct ltchars m_ltchars;
int m_ldisc;
int m_lmode;
} OldMode, NewMode;

static struct win *curr, *other;
static CurrNum, OtherNum;
static struct win *wtab[MAXWIN];

#define MSG_CREATE 0
#define MSG_ERROR 1

struct msg {
int type;
union {
struct {
int aflag;
int nargs;
char line[MAXLINE];
} create;
char message[MAXLINE];
} m;
};

#define KEY_IGNORE 0
#define KEY_HARDCOPY 1
#define KEY_SUSPEND 2
#define KEY_SHELL 3
#define KEY_NEXT 4
#define KEY_PREV 5
#define KEY_KILL 6
#define KEY_REDISPLAY 7
#define KEY_WINDOWS 8
#define KEY_VERSION 9
#define KEY_OTHER 10
#define KEY_0 11
#define KEY_1 12
#define KEY_2 13
#define KEY_3 14
#define KEY_4 15
#define KEY_5 16
#define KEY_6 17
#define KEY_7 18
#define KEY_8 19
#define KEY_9 20
#define KEY_CREATE 21

struct key {
int type;
char **args;
} ktab[256];

char *KeyNames[] = {
"hardcopy", "suspend", "shell", "next", "prev", "kill", "redisplay",
"windows", "version", "other", "select0", "select1", "select2", "select3",
"select4", "select5", "select6", "select7", "select8", "select9",
0
};

main (ac, av) char **av; {
register n, len;
register struct win **pp, *p;
char *ap;
int s, r, w, x = 0;
int aflag = 0;
struct timeval tv;
time_t now;
char buf[IOSIZE], *myname = (ac == 0) ? "screen" : av[0];
char rc[256];

while (--ac) {
ap = *++av;
if (strcmp (ap, "-c") == 0) {
if (ac < 2)
goto help;
CheckSockName (1);
s = MakeClientSocket ();
SendCreateMsg (s, ac, av, aflag);
close (s);
exit (0);
} else if (strcmp (ap, "-a") == 0) {
aflag = 1;
} else if (ap[0] == '-' && ap[1] == 'e') {
if (ap[2]) {
ap += 2;
} else {
if (--ac == 0) goto help;
ap = *++av;
}
if (strlen (ap) != 2)
Msg (0, "Two characters are required with -e option.");
Esc = ap[0];
MetaEsc = ap[1];
} else {
help:
Msg (0, "Use: %s [-a] [-exy] [-c cmd args]", myname);
}
}
if ((ShellProg = getenv ("SHELL")) == 0)
ShellProg = DEFAULT_SHELL;
ShellArgs[0] = ShellProg;
CheckSockName (0);
s = MakeServerSocket ();
InitTerm ();
MakeNewEnv ();
GetTTY (0, &OldMode);
ospeed = (short)OldMode.m_ttyb.sg_ospeed;
InitKeytab ();
sprintf (rc, "%.*s/.screenrc", 245, home);
ReadRc (rc);
if ((n = MakeWindow (ShellProg, ShellArgs, aflag, 0)) == -1) {
SetTTY (0, &OldMode);
FinitTerm ();
exit (1);
}
SetCurrWindow (n);
HasWindow = 1;
SetMode (&OldMode, &NewMode);
SetTTY (0, &NewMode);
signal (SIGCHLD, SigChld);
tv.tv_usec = 0;
while (1) {
if (status) {
time (&now);
if (now - TimeDisplayed < MSGWAIT) {
tv.tv_sec = MSGWAIT - (now - TimeDisplayed);
} else RemoveStatus (curr);
}
r = 0;
w = 0;
if (inlen)
w |= 1 << curr->ptyfd;
else
r |= 1 << 0;
for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
if (!(p = *pp))
continue;
if ((*pp)->active && status)
continue;
if ((*pp)->outlen > 0)
continue;
r |= 1 << (*pp)->ptyfd;
}
r |= 1 << s;
fflush (stdout);
if (select (32, &r, &w, &x, status ? &tv : (struct timeval *)0) == -1) {
if (errno == EINTR)
continue;
HasWindow = 0;
Msg (errno, "select");
/*NOTREACHED*/
}
if (GotSignal && !status) {
SigHandler ();
continue;
}
if (r & 1 << s) {
RemoveStatus (curr);
ReceiveMsg (s);
}
if (r & 1 << 0) {
RemoveStatus (curr);
if (ESCseen) {
inbuf[0] = Esc;
inlen = read (0, inbuf+1, IOSIZE-1) + 1;
ESCseen = 0;
} else {
inlen = read (0, inbuf, IOSIZE);
}
if (inlen > 0)
inlen = ProcessInput (inbuf, inlen);
}
if (GotSignal && !status) {
SigHandler ();
continue;
}
for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
if (!(p = *pp))
continue;
if (p->outlen) {
WriteString (p, p->outbuf, p->outlen);
} else if (r & 1 << p->ptyfd) {
if ((len = read (p->ptyfd, buf, IOSIZE)) == -1) {
if (errno == EWOULDBLOCK)
len = 0;
}
if (len > 0)
WriteString (p, buf, len);
}
}
if (GotSignal && !status) {
SigHandler ();
continue;
}
if (w & 1 << curr->ptyfd && inlen > 0) {
if ((len = write (curr->ptyfd, inbuf, inlen)) > 0) {
inlen -= len;
bcopy (inbuf+len, inbuf, inlen);
}
}
if (GotSignal && !status)
SigHandler ();
}
/*NOTREACHED*/
}

static SigHandler () {
while (GotSignal) {
GotSignal = 0;
DoWait ();
}
}

static SigChld () {
GotSignal = 1;
}

static DoWait () {
register pid;
register struct win **pp;
union wait wstat;

while ((pid = wait3 (&wstat, WNOHANG|WUNTRACED, NULL)) > 0) {
for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
if (*pp && pid == (*pp)->wpid) {
if (WIFSTOPPED (wstat)) {
kill((*pp)->wpid, SIGCONT);
} else {
if (*pp == curr)
curr = 0;
if (*pp == other)
other = 0;
FreeWindow (*pp);
*pp = 0;
}
}
}
}
CheckWindows ();
}

static CheckWindows () {
register struct win **pp;

/* If the current window disappeared and the "other" window is still
* there, switch to the "other" window, else switch to the window
* with the lowest index.
* If there current window is still there, but the "other" window
* vanished, "SetCurrWindow" is called in order to assign a new value
* to "other".
* If no window is alive at all, exit.
*/
if (!curr && other) {
SwitchWindow (OtherNum);
return;
}
if (curr && !other) {
SetCurrWindow (CurrNum);
return;
}
for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
if (*pp) {
if (!curr)
SwitchWindow (pp-wtab);
return;
}
}
SetTTY (0, &OldMode);
FinitTerm ();
exit (0);
}

InitKeytab () {
register i;

ktab['h'].type = ktab[Ctrl('h')].type = KEY_HARDCOPY;
ktab['z'].type = ktab[Ctrl('z')].type = KEY_SUSPEND;
ktab['c'].type = ktab[Ctrl('c')].type = KEY_SHELL;
ktab[' '].type = ktab[Ctrl(' ')].type =
ktab['n'].type = ktab[Ctrl('n')].type = KEY_NEXT;
ktab['-'].type = ktab['p'].type = ktab[Ctrl('p')].type = KEY_PREV;
ktab['k'].type = ktab[Ctrl('k')].type = KEY_KILL;
ktab['l'].type = ktab[Ctrl('l')].type = KEY_REDISPLAY;
ktab['w'].type = ktab[Ctrl('w')].type = KEY_WINDOWS;
ktab['v'].type = ktab[Ctrl('v')].type = KEY_VERSION;
ktab[Esc].type = KEY_OTHER;
for (i = 0; i <= 9; i++)
ktab[i+'0'].type = KEY_0+i;
}

static ProcessInput (buf, len) char *buf; {
register n, k;
register char *s, *p;

for (s = p = buf; len > 0; len--, s++) {
if (*s == Esc) {
if (len > 1) {
len--; s++;
k = ktab[*s].type;
if (*s == MetaEsc) {
*p++ = Esc;
} else if (k >= KEY_0 && k <= KEY_9) {
p = buf;
SwitchWindow (k - KEY_0);
} else switch (ktab[*s].type) {
case KEY_HARDCOPY:
p = buf;
DumpWindow ();
break;
case KEY_SUSPEND:
p = buf;
SetTTY (0, &OldMode);
FinitTerm ();
kill (getpid (), SIGTSTP);
SetTTY (0, &NewMode);
Activate (wtab[CurrNum]);
break;
case KEY_SHELL:
p = buf;
if ((n = MakeWindow (ShellProg, ShellArgs, 0, 0)) != -1)
SwitchWindow (n);
break;
case KEY_NEXT:
p = buf;
SwitchWindow (NextWindow ());
break;
case KEY_PREV:
p = buf;
SwitchWindow (PreviousWindow ());
break;
case KEY_KILL:
p = buf;
FreeWindow (wtab[CurrNum]);
if (other == curr)
other = 0;
curr = wtab[CurrNum] = 0;
CheckWindows ();
break;
case KEY_REDISPLAY:
p = buf;
Activate (wtab[CurrNum]);
break;
case KEY_WINDOWS:
p = buf;
ShowWindows ();
break;
case KEY_VERSION:
p = buf;
Msg (0, "%s %s", ScreenVersion, AnsiVersion);
break;
case KEY_OTHER:
p = buf;
SwitchWindow (OtherNum);
break;
case KEY_CREATE:
p = buf;
if ((n = MakeWindow (ktab[*s].args[0], ktab[*s].args,
0, 0)) != -1)
SwitchWindow (n);
break;
}
} else ESCseen = 1;
} else *p++ = *s;
}
return p - buf;
}

static SwitchWindow (n) {
if (!wtab[n])
return;
SetCurrWindow (n);
Activate (wtab[n]);
}

static SetCurrWindow (n) {
/*
* If we come from another window, this window becomes the
* "other" window:
*/
if (curr) {
curr->active = 0;
other = curr;
OtherNum = CurrNum;
}
CurrNum = n;
curr = wtab[n];
curr->active = 1;
/*
* If the "other" window is currently undefined (at program start
* or because it has died), or if the "other" window is equal to the
* one just selected, we try to find a new one:
*/
if (other == 0 || other == curr) {
OtherNum = NextWindow ();
other = wtab[OtherNum];
}
}

static NextWindow () {
register struct win **pp;

for (pp = wtab+CurrNum+1; pp != wtab+CurrNum; ++pp) {
if (pp == wtab+MAXWIN)
pp = wtab;
if (*pp)
break;
}
return pp-wtab;
}

static PreviousWindow () {
register struct win **pp;

for (pp = wtab+CurrNum-1; pp != wtab+CurrNum; --pp) {
if (pp < wtab)
pp = wtab+MAXWIN-1;
if (*pp)
break;
}
return pp-wtab;
}

static FreeWindow (wp) struct win *wp; {
register i;

close (wp->ptyfd);
for (i = 0; i < rows; ++i) {
free (wp->image[i]);
free (wp->attr[i]);
}
free (wp->image);
free (wp->attr);
free (wp);
}

static MakeWindow (prog, args, aflag, StartAt) char *prog, **args; {
register struct win **pp, *p;
register char **cp;
register n, f;
int tf;
int mypid;
char ebuf[10];

pp = wtab+StartAt;
do {
if (*pp == 0)
break;
if (++pp == wtab+MAXWIN)
pp = wtab;
} while (pp != wtab+StartAt);
if (*pp) {
Msg (0, "No more windows.");
return -1;
}
n = pp - wtab;
if ((f = OpenPTY ()) == -1) {
Msg (0, "No more PTYs.");
return -1;
}
fcntl (f, F_SETFL, FNDELAY);
if ((p = *pp = (struct win *)malloc (sizeof (struct win))) == 0) {
nomem:
Msg (0, "Out of memory.");
return -1;
}
if ((p->image = (char **)malloc (rows * sizeof (char *))) == 0)
goto nomem;
for (cp = p->image; cp < p->image+rows; ++cp) {
if ((*cp = malloc (cols)) == 0)
goto nomem;
bclear (*cp, cols);
}
if ((p->attr = (char **)malloc (rows * sizeof (char *))) == 0)
goto nomem;
for (cp = p->attr; cp < p->attr+rows; ++cp) {
if ((*cp = malloc (cols)) == 0)
goto nomem;
bzero (*cp, cols);
}
if ((p->tabs = malloc (cols+1)) == 0) /* +1 because 0 <= x <= cols */
goto nomem;
ResetScreen (p);
p->active = 0;
p->ptyfd = f;
strncpy (p->cmd, Filename (args[0]), MAXSTR-1);
p->cmd[MAXSTR-1] = '\0';
switch (p->wpid = fork ()) {
case -1:
Msg (errno, "Cannot fork");
free (p);
return -1;
case 0:
mypid = getpid ();
if ((f = open ("/dev/tty", O_RDWR)) != -1) {
ioctl (f, TIOCNOTTY, (char *)0);
close (f);
}
if ((tf = open (TtyName, O_RDWR)) == -1) {
SendErrorMsg ("Cannot open %s: %s", TtyName, sys_errlist[errno]);
exit (1);
}
dup2 (tf, 0);
dup2 (tf, 1);
dup2 (tf, 2);
for (f = getdtablesize () - 1; f > 2; f--)
close (f);
ioctl (0, TIOCSPGRP, &mypid);
setpgrp (0, mypid);
SetTTY (0, &OldMode);
NewEnv[2] = MakeTermcap (aflag);
sprintf (ebuf, "WINDOW=%d", n);
NewEnv[3] = ebuf;
execve (prog, args, NewEnv);
SendErrorMsg ("Cannot exec %s: %s", prog, sys_errlist[errno]);
sleep (2);
exit (1);
}
return n;
}

static DumpWindow () {
register i, j, k;
register char *p;
register FILE *f;
char fn[20];

sprintf (fn, "hardcopy.%d", CurrNum);
if ((f = fopen (fn, "w")) == NULL) {
Msg (0, "Cannot open \"%s\".", fn);
return;
}
Msg (0, "Dumping screen image...");
for (i = 0; i < rows; ++i) {
p = curr->image[i];
for (k = cols-1; k >= 0 && p[k] == ' '; --k) ;
for (j = 0; j <= k; ++j)
putc (p[j], f);
putc ('\n', f);
}
fclose (f);
Msg (0, "Screen image written to \"%s\".", fn);
}

static ShowWindows () {
char buf[1024];
register char *s;
register struct win **pp, *p;

for (s = buf, pp = wtab; pp < wtab+MAXWIN; ++pp) {
if ((p = *pp) == 0)
continue;
if (s - buf + 5 + strlen (p->cmd) > cols-1)
break;
if (s > buf) {
*s++ = ' '; *s++ = ' ';
}
*s++ = pp - wtab + '0';
if (p == curr)
*s++ = '*';
*s++ = ' ';
strcpy (s, p->cmd);
s += strlen (s);
}
Msg (0, buf);
}

static OpenPTY () {
register char *p, *l, *d;
register i, f, tf;

strcpy (PtyName, PtyProto);
strcpy (TtyName, TtyProto);
for (p = PtyName, i = 0; *p != 'X'; ++p, ++i) ;
for (l = "pqr"; *p = *l; ++l) {
for (d = "0123456789abcdef"; p[1] = *d; ++d) {
if ((f = open (PtyName, O_RDWR)) != -1) {
TtyName[i] = p[0];
TtyName[i+1] = p[1];
if ((tf = open (TtyName, O_RDWR)) != -1) {
close (tf);
return f;
}
close (f);
}
}
}
return -1;
}

static SetTTY (fd, mp) struct mode *mp; {
ioctl (fd, TIOCSETP, &mp->m_ttyb);
ioctl (fd, TIOCSETC, &mp->m_tchars);
ioctl (fd, TIOCSLTC, &mp->m_ltchars);
ioctl (fd, TIOCLSET, &mp->m_lmode);
ioctl (fd, TIOCSETD, &mp->m_ldisc);
}

static GetTTY (fd, mp) struct mode *mp; {
ioctl (fd, TIOCGETP, &mp->m_ttyb);
ioctl (fd, TIOCGETC, &mp->m_tchars);
ioctl (fd, TIOCGLTC, &mp->m_ltchars);
ioctl (fd, TIOCLGET, &mp->m_lmode);
ioctl (fd, TIOCGETD, &mp->m_ldisc);
}

static SetMode (op, np) struct mode *op, *np; {
*np = *op;
np->m_ttyb.sg_flags &= ~(CRMOD|ECHO);
np->m_ttyb.sg_flags |= CBREAK;
np->m_tchars.t_intrc = -1;
np->m_tchars.t_quitc = -1;
np->m_ltchars.t_suspc = -1;
np->m_ltchars.t_dsuspc = -1;
np->m_ltchars.t_flushc = -1;
np->m_ltchars.t_lnextc = -1;
}

static CheckSockName (client) {
struct stat s;
register char *p;

if (client) {
if ((SockName = getenv ("STY")) == 0 || *SockName == '\0')
Msg (0, "$STY is undefined or invalid.");
} else {
if ((p = ttyname (0)) == 0 || (p = ttyname (1)) == 0 ||
(p = ttyname (2)) == 0 || *p == '\0')
Msg (0, "screen must run on a tty.");
SockName = Filename (p);
}
if ((home = getenv ("HOME")) == 0)
Msg (0, "$HOME is undefined.");
sprintf (SockPath, "%s/%s", home, SockDir);
if (stat (SockPath, &s) == -1) {
if (errno == ENOENT) {
if (mkdir (SockPath, 0700) == -1)
Msg (errno, "Cannot make directory %s", SockPath);
} else Msg (errno, "Cannot get status of %s", SockPath);
} else {
if ((s.st_mode & S_IFMT) != S_IFDIR)
Msg (0, "%s is not a directory.", SockPath);
if ((s.st_mode & 0777) != 0700)
Msg (0, "Directory %s must have mode 700.", SockPath);
}
strcat (SockPath, "/");
strcat (SockPath, SockName);
}

static MakeServerSocket () {
register s;
struct sockaddr_un sun;

(void) unlink (SockPath);
if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
Msg (errno, "socket");
sun.sun_family = AF_UNIX;
strcpy (sun.sun_path, SockPath);
if (bind (s, (struct sockaddr *)&sun, strlen (SockPath)+2) == -1)
Msg (errno, "bind");
if (listen (s, 5) == -1)
Msg (errno, "listen");
return s;
}

static MakeClientSocket () {
register s;
struct sockaddr_un sun;

if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
Msg (errno, "socket");
sun.sun_family = AF_UNIX;
strcpy (sun.sun_path, SockPath);
if (connect (s, (struct sockaddr *)&sun, strlen (SockPath)+2) == -1)
Msg (errno, "connect: %s", SockPath);
return s;
}

static SendCreateMsg (s, ac, av, aflag) char **av; {
struct msg m;
register char *p;
register len, n;

m.type = MSG_CREATE;
for (p = m.m.create.line, n = 0; --ac && n < MAXARGS-1; ++n) {
len = strlen (*++av) + 1;
if (p + len >= m.m.create.line+MAXLINE)
break;
strcpy (p, *av);
p += len;
}
m.m.create.nargs = n;
m.m.create.aflag = aflag;
if (write (s, &m, sizeof (m)) != sizeof (m))
Msg (errno, "write");
}

/*VARARGS1*/
static SendErrorMsg (fmt, p1, p2, p3, p4, p5, p6) char *fmt; {
register s;
struct msg m;

s = MakeClientSocket ();
m.type = MSG_ERROR;
sprintf (m.m.message, fmt, p1, p2, p3, p4, p5, p6);
(void) write (s, &m, sizeof (m));
close (s);
}

static ReceiveMsg (s) {
register ns;
struct sockaddr_un sun;
int len = sizeof (sun);
struct msg m;

if ((ns = accept (s, (struct sockaddr *)&sun, &len)) == -1) {
Msg (errno, "accept");
return;
}
if ((len = read (ns, &m, sizeof (m))) != sizeof (m)) {
if (len == -1)
Msg (errno, "read");
else
Msg (0, "Short message (%d bytes)", len);
close (ns);
return;
}
switch (m.type) {
case MSG_CREATE:
ExecCreate (&m);
break;
case MSG_ERROR:
Msg (0, "%s", m.m.message);
break;
default:
Msg (0, "Invalid message (type %d).", m.type);
}
close (ns);
}

static ExecCreate (mp) struct msg *mp; {
char *args[MAXARGS];
register n;
register char **pp = args, *p = mp->m.create.line;

for (n = mp->m.create.nargs; n > 0; --n) {
*pp++ = p;
p += strlen (p) + 1;
}
*pp = 0;
if ((n = MakeWindow (mp->m.create.line, args, mp->m.create.aflag, 0)) != -1)
SwitchWindow (n);
}

static ReadRc (fn) char *fn; {
FILE *f;
register char *p, **pp, **ap;
register argc, num, c;
char buf[256];
char *args[MAXARGS];
int key;

ap = args;
if ((f = fopen (fn, "r")) == NULL)
return;
while (fgets (buf, 256, f) != NULL) {
if (p = rindex (buf, '\n'))
*p = '\0';
if ((argc = Parse (fn, buf, ap)) == 0)
continue;
if (strcmp (ap[0], "escape") == 0) {
p = ap[1];
if (argc < 2 || strlen (p) != 2)
Msg (0, "%s: two characters required after escape.", fn);
Esc = *p++;
MetaEsc = *p;
} else if (strcmp (ap[0], "screen") == 0) {
num = 0;
if (argc > 1 && IsNum (ap[1], 10)) {
num = atoi (ap[1]);
if (num < 0 || num > MAXWIN-1)
Msg (0, "%s: illegal screen number %d.", fn, num);
--argc; ++ap;
}
if (argc < 2) {
ap[1] = ShellProg; argc = 2;
}
ap[argc] = 0;
(void) MakeWindow (ap[1], ap+1, 0, num);
} else if (strcmp (ap[0], "bind") == 0) {
p = ap[1];
if (argc < 2 || *p == '\0')
Msg (0, "%s: key expected after bind.", fn);
if (p[1] == '\0') {
key = *p;
} else if (p[0] == '^' && p[1] != '\0' && p[2] == '\0') {
c = p[1];
if (isupper (c))
p[1] = tolower (c);
key = Ctrl(c);
} else if (IsNum (p, 7)) {
(void) sscanf (p, "%o", &key);
} else {
Msg (0,
"%s: bind: character, ^x, or octal number expected.", fn);
}
if (argc < 3) {
ktab[key].type = 0;
} else {
for (pp = KeyNames; *pp; ++pp)
if (strcmp (ap[2], *pp) == 0) break;
if (*pp) {
ktab[key].type = pp-KeyNames+1;
} else if (ap[2][0] == '/') {
ktab[key].type = KEY_CREATE;
ktab[key].args = SaveArgs (argc-2, ap+2);
} else Msg (0, "%s: unknown function \"%s\".", fn, ap[2]);
}
} else Msg (0, "%s: unknown keyword \"%s\".", fn, ap[0]);
}
fclose (f);
}

static Parse (fn, buf, args) char *fn, *buf, **args; {
register char *p = buf, **ap = args;
register delim, argc = 0;

argc = 0;
for (;;) {
while (*p && (*p == ' ' || *p == '\t')) ++p;
if (*p == '\0' || *p == '#')
return argc;
if (argc > MAXARGS-1)
Msg (0, "%s: too many tokens.", fn);
delim = 0;
if (*p == '"' || *p == '\'') {
delim = *p; *p = '\0'; ++p;
}
++argc;
*ap = p; ++ap;
while (*p && !(delim ? *p == delim : (*p == ' ' || *p == '\t')))
++p;
if (*p == '\0') {
if (delim)
Msg (0, "%s: Missing quote.", fn);
else
return argc;
}
*p++ = '\0';
}
}

static char **SaveArgs (argc, argv) register argc; register char **argv; {
register char **ap, **pp;

if ((pp = ap = (char **)malloc ((argc+1) * sizeof (char **))) == 0)
Msg (0, "Out of memory.");
while (argc--) {
if ((*pp = malloc (strlen (*argv)+1)) == 0)
Msg (0, "Out of memory.");
strcpy (*pp, *argv);
++pp; ++argv;
}
*pp = 0;
return ap;
}

static MakeNewEnv () {
register char **op, **np = NewEnv;
static char buf[MAXSTR];

if (strlen (SockName) > MAXSTR-5)
SockName = "?";
sprintf (buf, "STY=%s", SockName);
*np++ = buf;
*np++ = Term;
np += 2;
for (op = environ; *op; ++op) {
if (np == NewEnv + MAXARGS - 1)
break;
if (!IsSymbol (*op, "TERM") && !IsSymbol (*op, "TERMCAP")
&& !IsSymbol (*op, "STY"))
*np++ = *op;
}
*np = 0;
}

static IsSymbol (e, s) register char *e, *s; {
register char *p;
register n;

for (p = e; *p && *p != '='; ++p) ;
if (*p) {
*p = '\0';
n = strcmp (e, s);
*p = '=';
return n == 0;
}
return 0;
}

/*VARARGS2*/
Msg (err, fmt, p1, p2, p3, p4, p5, p6) char *fmt; {
char buf[1024];
register char *p = buf;

sprintf (p, fmt, p1, p2, p3, p4, p5, p6);
if (err) {
p += strlen (p);
if (err > 0 && err < sys_nerr)
sprintf (p, ": %s", sys_errlist[err]);
else
sprintf (p, ": Error %d", err);
}
if (HasWindow) {
MakeStatus (buf, curr);
} else {
printf ("%s\r\n", buf);
exit (1);
}
}

bclear (p, n) char *p; {
bcopy (blank, p, n);
}

static char *Filename (s) char *s; {
register char *p;

p = s + strlen (s) - 1;
while (p >= s && *p != '/') --p;
return ++p;
}

static IsNum (s, base) register char *s; register base; {
for (base += '0'; *s; ++s)
if (*s < '0' || *s > base)
return 0;
return 1;
}
SHAR_EOF
if test 23389 -ne "`wc -c < 'screen.c'`"
then
echo shar: error transmitting "'screen.c'" '(should have been 23389 characters)'
fi
fi # end of overwriting check
# End of shell archive
exit 0

Recommended Links

Google matched content

Softpanorama Recommended

Top articles

Sites



Etc

Society

Groupthink : Two Party System as Polyarchy : Corruption of Regulators : Bureaucracies : Understanding Micromanagers and Control Freaks : Toxic Managers :   Harvard Mafia : Diplomatic Communication : Surviving a Bad Performance Review : Insufficient Retirement Funds as Immanent Problem of Neoliberal Regime : PseudoScience : Who Rules America : Neoliberalism  : The Iron Law of Oligarchy : Libertarian Philosophy

Quotes

War and Peace : Skeptical Finance : John Kenneth Galbraith :Talleyrand : Oscar Wilde : Otto Von Bismarck : Keynes : George Carlin : Skeptics : Propaganda  : SE quotes : Language Design and Programming Quotes : Random IT-related quotesSomerset Maugham : Marcus Aurelius : Kurt Vonnegut : Eric Hoffer : Winston Churchill : Napoleon Bonaparte : Ambrose BierceBernard Shaw : Mark Twain Quotes

Bulletin:

Vol 25, No.12 (December, 2013) Rational Fools vs. Efficient Crooks The efficient markets hypothesis : Political Skeptic Bulletin, 2013 : Unemployment Bulletin, 2010 :  Vol 23, No.10 (October, 2011) An observation about corporate security departments : Slightly Skeptical Euromaydan Chronicles, June 2014 : Greenspan legacy bulletin, 2008 : Vol 25, No.10 (October, 2013) Cryptolocker Trojan (Win32/Crilock.A) : Vol 25, No.08 (August, 2013) Cloud providers as intelligence collection hubs : Financial Humor Bulletin, 2010 : Inequality Bulletin, 2009 : Financial Humor Bulletin, 2008 : Copyleft Problems Bulletin, 2004 : Financial Humor Bulletin, 2011 : Energy Bulletin, 2010 : Malware Protection Bulletin, 2010 : Vol 26, No.1 (January, 2013) Object-Oriented Cult : Political Skeptic Bulletin, 2011 : Vol 23, No.11 (November, 2011) Softpanorama classification of sysadmin horror stories : Vol 25, No.05 (May, 2013) Corporate bullshit as a communication method  : Vol 25, No.06 (June, 2013) A Note on the Relationship of Brooks Law and Conway Law

History:

Fifty glorious years (1950-2000): the triumph of the US computer engineering : Donald Knuth : TAoCP and its Influence of Computer Science : Richard Stallman : Linus Torvalds  : Larry Wall  : John K. Ousterhout : CTSS : Multix OS Unix History : Unix shell history : VI editor : History of pipes concept : Solaris : MS DOSProgramming Languages History : PL/1 : Simula 67 : C : History of GCC developmentScripting Languages : Perl history   : OS History : Mail : DNS : SSH : CPU Instruction Sets : SPARC systems 1987-2006 : Norton Commander : Norton Utilities : Norton Ghost : Frontpage history : Malware Defense History : GNU Screen : OSS early history

Classic books:

The Peter Principle : Parkinson Law : 1984 : The Mythical Man-MonthHow to Solve It by George Polya : The Art of Computer Programming : The Elements of Programming Style : The Unix Hater’s Handbook : The Jargon file : The True Believer : Programming Pearls : The Good Soldier Svejk : The Power Elite

Most popular humor pages:

Manifest of the Softpanorama IT Slacker Society : Ten Commandments of the IT Slackers Society : Computer Humor Collection : BSD Logo Story : The Cuckoo's Egg : IT Slang : C++ Humor : ARE YOU A BBS ADDICT? : The Perl Purity Test : Object oriented programmers of all nations : Financial Humor : Financial Humor Bulletin, 2008 : Financial Humor Bulletin, 2010 : The Most Comprehensive Collection of Editor-related Humor : Programming Language Humor : Goldman Sachs related humor : Greenspan humor : C Humor : Scripting Humor : Real Programmers Humor : Web Humor : GPL-related Humor : OFM Humor : Politically Incorrect Humor : IDS Humor : "Linux Sucks" Humor : Russian Musical Humor : Best Russian Programmer Humor : Microsoft plans to buy Catholic Church : Richard Stallman Related Humor : Admin Humor : Perl-related Humor : Linus Torvalds Related humor : PseudoScience Related Humor : Networking Humor : Shell Humor : Financial Humor Bulletin, 2011 : Financial Humor Bulletin, 2012 : Financial Humor Bulletin, 2013 : Java Humor : Software Engineering Humor : Sun Solaris Related Humor : Education Humor : IBM Humor : Assembler-related Humor : VIM Humor : Computer Viruses Humor : Bright tomorrow is rescheduled to a day after tomorrow : Classic Computer Humor

The Last but not Least Technology is dominated by two types of people: those who understand what they do not manage and those who manage what they do not understand ~Archibald Putt. Ph.D


Copyright © 1996-2021 by Softpanorama Society. www.softpanorama.org was initially created as a service to the (now defunct) UN Sustainable Development Networking Programme (SDNP) without any remuneration. This document is an industrial compilation designed and created exclusively for educational use and is distributed under the Softpanorama Content License. Original materials copyright belong to respective owners. Quotes are made for educational purposes only in compliance with the fair use doctrine.

FAIR USE NOTICE This site contains copyrighted material the use of which has not always been specifically authorized by the copyright owner. We are making such material available to advance understanding of computer science, IT technology, economic, scientific, and social issues. We believe this constitutes a 'fair use' of any such copyrighted material as provided by section 107 of the US Copyright Law according to which such material can be distributed without profit exclusively for research and educational purposes.

This is a Spartan WHYFF (We Help You For Free) site written by people for whom English is not a native language. Grammar and spelling errors should be expected. The site contain some broken links as it develops like a living tree...

You can use PayPal to to buy a cup of coffee for authors of this site

Disclaimer:

The statements, views and opinions presented on this web page are those of the author (or referenced source) and are not endorsed by, nor do they necessarily reflect, the opinions of the Softpanorama society. We do not warrant the correctness of the information provided or its fitness for any purpose. The site uses AdSense so you need to be aware of Google privacy policy. You you do not want to be tracked by Google please disable Javascript for this site. This site is perfectly usable without Javascript.

Last modified: March, 12, 2019