screen-4.1.0/ 0000755 0001750 0001750 00000000000 11732171240 011031 5 ustar abe abe screen-4.1.0/COPYING 0000644 0001750 0001750 00000104513 11642704565 012104 0 ustar abe abe GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
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.
Copyright (C)
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 .
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:
Copyright (C)
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
.
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
.
screen-4.1.0/configure.in 0000644 0001750 0001750 00000074032 11720315605 013352 0 ustar abe abe 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)
dnl
dnl Define some useful macros
dnl
AC_DEFUN([AC_PROGRAM_SOURCE],
[AC_REQUIRE([AC_PROG_CPP])AC_PROVIDE([$0])cat > conftest.c <&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_TRY_RUN(main(){exit(0);},,[
if test $CC != cc ; then
AC_NOTE(Your $CC failed - restarting with CC=cc)
AC_NOTE()
CC=cc
export CC
exec $0 $configure_args
fi
])
AC_TRY_RUN(main(){exit(0);},,
exec 5>&2
eval $ac_link
AC_NOTE(CC=$CC; CFLAGS=$CFLAGS; LIBS=$LIBS;)
AC_NOTE($ac_compile)
AC_MSG_ERROR(Can't run the compiler - sorry))
AC_TRY_RUN([
main()
{
int __something_strange_();
__something_strange_(0);
}
],AC_MSG_ERROR(Your compiler does not set the exit status - sorry))
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 -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 -f /sysV68 ; then
AC_DEFINE(sysV68)
fi
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
AC_CHECKING(for Ultrix)
AC_EGREP_CPP(yes,
[#if defined(ultrix) || defined(__ultrix)
yes;
#endif
], ULTRIX=1)
if 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,
[#if defined(butterfly)
yes;
#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,
[#include
#include
main () {
#ifdef _POSIX_VERSION
yes;
#endif
], AC_NOTE(- you have a POSIX system) AC_DEFINE(POSIX) posix=1)
fi
AC_CHECKING(for System V)
AC_TRY_COMPILE(
[#include
#include
#include ], [int x = SIGCHLD | FNDELAY;], , AC_DEFINE(SYSV))
AC_CHECKING(for sequent/ptx)
AC_EGREP_CPP(yes,
[#ifdef _SEQUENT_
yes;
#endif
], LIBS="$LIBS -lsocket -linet";seqptx=1)
oldlibs="$LIBS"
LIBS="$LIBS -lelf"
AC_CHECKING(SVR4)
AC_TRY_LINK([#include
],,
[AC_CHECK_HEADER(dwarf.h, AC_DEFINE(SVR4) AC_DEFINE(BUGGYGETLOGIN),
[AC_CHECK_HEADER(elf.h, AC_DEFINE(SVR4) AC_DEFINE(BUGGYGETLOGIN))])]
,LIBS="$oldlibs")
AC_CHECK_HEADERS([stropts.h string.h strings.h])
AC_CHECKING(for Solaris 2.x)
AC_EGREP_CPP(yes,
[#if defined(SVR4) && defined(sun)
yes
#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
dnl ],AC_DEFINE(PID_T_DEFINED))
dnl
dnl AC_CHECKING(for sig_t)
dnl AC_EGREP_CPP(sig_t,[#include
dnl #include
dnl ],AC_DEFINE(SIG_T_DEFINED))
dnl
dnl AC_CHECKING(for uid_t)
dnl AC_EGREP_CPP(uid_t,[#include
dnl ],AC_DEFINE(UID_T_DEFINED))
dnl
dnl
dnl **** Job control ****
dnl
AC_CHECKING(BSD job jontrol)
AC_TRY_LINK(
[#include
#include
], [
#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
/* For select - According to earlier standards */
#include
#include
#include
#include
#include
#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))
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
/* For select - According to earlier standards */
#include
#include
#include
#include
#include
#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)
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
/* For select - According to earlier standards */
#include
#include
#include
#include
#include
#include
#include
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))
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
/* For select - According to earlier standards */
#include
#include
#include
#include
#include
#include
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)
rm -f /tmp/conftest*
fi
dnl
dnl **** choose sockets or fifos ****
dnl
if test -n "$fifo"; then
if test -n "$sock"; then
if test -n "$nore"; then
AC_NOTE(- hmmm... better take the fifos)
AC_DEFINE(NAMEDPIPE)
elif test -n "$fifobr"; then
AC_NOTE(- as your fifos are broken lets use the sockets.)
else
AC_NOTE(- both sockets and fifos usable. let's take sockets.)
fi
else
AC_NOTE(- using named pipes, of course)
AC_DEFINE(NAMEDPIPE)
fi
elif test -n "$sock"; then
AC_NOTE(- using unix-domain sockets, of course)
else
AC_MSG_ERROR(you have neither usable sockets nor usable pipes -> no screen)
fi
dnl
dnl **** check the select implementation ****
dnl
AC_CHECKING(select return value)
AC_TRY_RUN([
/* For select - According to POSIX 1003.1-2001 */
#include
/* For select - According to earlier standards */
#include
#include
#include
#include
#include
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
#include
#include
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))
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="-lncurses $olibs"
AC_CHECKING(libncurses)
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_CHECKING(ospeed)
AC_TRY_LINK(extern short ospeed;,ospeed=5;,,AC_DEFINE(NEED_OSPEED))
dnl
dnl **** PTY specific things ****
dnl
AC_CHECKING(for /dev/ptc)
if test -r /dev/ptc; then
AC_DEFINE(HAVE_DEV_PTC)
fi
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
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
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,
[#ifdef M_UNIX
yes;
#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
dnl **** pty mode/group handling ****
dnl
dnl support provided by Luke Mewburn , 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
#include
#include
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
]
)
rm -f conftest_grp
fi
dnl
dnl **** utmp handling ****
dnl
AC_CHECKING(getutent)
AC_TRY_LINK([
#include /* to get time_t on SCO */
#include
#if defined(SVR4) && !defined(DGUX)
#include
#define utmp utmpx
#else
#include
#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
#include
#if defined(SVR4) && !defined(DGUX)
#include
#define utmp utmpx
#else
#include
#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
#include
#if defined(SVR4) && !defined(DGUX)
#include
#define utmp utmpx
#else
#include
#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
AC_CHECKING(for libutil(s))
test -f /usr/lib/libutils.a && LIBS="$LIBS -lutils"
test -f /usr/lib/libutil.a && LIBS="$LIBS -lutil"
AC_CHECKING(getloadavg)
AC_TRY_LINK(,[getloadavg((double *)0, 0);],
AC_DEFINE(LOADAV_GETLOADAVG) load=1,
if 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,
[#if defined(NeXT) || defined(apollo) || defined(linux)
yes;
#endif
], load=1)
fi
if test -z "$load" ; 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 ],
[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
#else
# include
#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
#ifdef NLIST_STRUCT
#include
#else
#include
#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
#include
],[
#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
#include
#ifdef signal
#undef signal
#endif
extern void (*signal ()) ();], [int i;], AC_DEFINE(SIGVOID))
AC_CHECKING(sigset)
AC_TRY_LINK([
#include
#include
],[
#ifdef SIGVOID
sigset(0, (void (*)())0);
#else
sigset(0, (int (*)())0);
#endif
], AC_DEFINE(USESIGSET))
AC_CHECKING(signal implementation)
AC_TRY_RUN([
#include
#include
#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)
test -f /lib/libcrypt_d.a || test -f /usr/lib/libcrypt_d.a && LIBS="$LIBS -lcrypt_d"
oldlibs="$LIBS"
LIBS="$LIBS -lcrypt"
AC_CHECKING(crypt)
AC_TRY_LINK(,,,LIBS="$oldlibs")
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"
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,
[#ifdef M_UNIX
yes;
#endif
], LIBS="$LIBS -lsocket -lcrypt_i")
dnl
dnl **** misc things ****
dnl
AC_CHECKING(wait union)
AC_TRY_COMPILE([#include
#include
],[
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 ], AC_DEFINE(TERMIO),
if test -n "$posix"; then
AC_TRY_CPP([#include ], AC_DEFINE(TERMIO))
fi
)
fi
dnl AC_CHECK_HEADER(shadow.h, AC_DEFINE(SHADOWPW))
AC_CHECKING(getspnam)
AC_TRY_LINK([#include ], [getspnam("x");],AC_DEFINE(SHADOWPW))
AC_CHECKING(getttyent)
AC_TRY_LINK(,[getttyent();], AC_DEFINE(GETTTYENT))
AC_CHECKING(fdwalk)
AC_TRY_LINK([#include ], [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_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(,[vsprintf(0,0,0);], 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 ],
[
setenv((char *) 0, (char *) 0, 0);
], ac_setenv_args=3)
fi
if test -z "$ac_setenv_args"; then
AC_TRY_LINK(
[#include ],
[
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
],[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 ], [
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-locale use localized month/day names])
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"
AC_TRY_RUN(main(){exit(0);},,AC_MSG_ERROR(Can't run the compiler - internal error. Sorry.))
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 ""
screen-4.1.0/TODO 0000644 0001750 0001750 00000000357 11642704565 011542 0 ustar abe abe - 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.
screen-4.1.0/autogen.sh 0000755 0001750 0001750 00000000032 11642704565 013041 0 ustar abe abe #!/bin/sh
exec autoreconf
screen-4.1.0/logfile.c 0000644 0001750 0001750 00000016353 11642704565 012642 0 ustar abe abe /* 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
* http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
****************************************************************
*/
#include /* dev_t, ino_t, off_t, ... */
#include /* struct stat */
#include /* O_WRONLY for logfile_reopen */
#include "config.h"
#include "screen.h"
#include "extern.h"
#include "logfile.h"
static void changed_logfile __P((struct logfile *));
static struct logfile *lookup_logfile __P((char *));
static int stolen_logfile __P((struct logfile *));
static struct logfile *logroot = NULL;
static void
changed_logfile(l)
struct logfile *l;
{
struct stat o, *s = l->st;
if (fstat(fileno(l->fp), &o) < 0) /* get trouble later */
return;
if (o.st_size > s->st_size) /* aha, appended text */
{
s->st_size = o.st_size; /* this should have changed */
s->st_mtime = o.st_mtime; /* only size and mtime */
}
}
/*
* Requires fd to be open and need_fd to be closed.
* If possible, need_fd will be open afterwards and refer to
* the object originally reffered by fd. fd will be closed then.
* Works just like ``fcntl(fd, DUPFD, need_fd); close(fd);''
*
* need_fd is returned on success, else -1 is returned.
*/
int
lf_move_fd(fd, need_fd)
int need_fd, fd;
{
int r = -1;
if (fd == need_fd)
return fd;
if (fd >=0 && fd < need_fd)
r = lf_move_fd(dup(fd), need_fd);
close(fd);
return r;
}
static int
logfile_reopen(name, wantfd, l)
char *name;
int wantfd;
struct logfile *l;
{
int got_fd;
close(wantfd);
if (((got_fd = open(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) ||
lf_move_fd(got_fd, wantfd) < 0)
{
logfclose(l);
debug1("logfile_reopen: failed for %s\n", name);
return -1;
}
changed_logfile(l);
debug2("logfile_reopen: %d = %s\n", wantfd, name);
return 0;
}
static int (* lf_reopen_fn)() = logfile_reopen;
/*
* Whenever logfwrite discoveres that it is required to close and
* reopen the logfile, the function registered here is called.
* If you do not register anything here, the above logfile_reopen()
* will be used instead.
* Your function should perform the same steps as logfile_reopen():
* a) close the original filedescriptor without flushing any output
* b) open a new logfile for future output on the same filedescriptor number.
* c) zero out st_dev, st_ino to tell the stolen_logfile() indcator to
* reinitialise itself.
* d) return 0 on success.
*/
void
logreopen_register(fn)
int (*fn) __P((char *, int, struct logfile *));
{
lf_reopen_fn = fn ? fn : logfile_reopen;
}
/*
* If the logfile has been removed, truncated, unlinked or the like,
* return nonzero.
* The l->st structure initialised by logfopen is updated
* on every call.
*/
static int
stolen_logfile(l)
struct logfile *l;
{
struct stat o, *s = l->st;
o = *s;
if (fstat(fileno(l->fp), s) < 0) /* remember that stat failed */
s->st_ino = s->st_dev = 0;
ASSERT(s == l->st);
if (!o.st_dev && !o.st_ino) /* nothing to compare with */
return 0;
if ((!s->st_dev && !s->st_ino) || /* stat failed, that's new! */
!s->st_nlink || /* red alert: file unlinked */
(s->st_size < o.st_size) || /* file truncated */
(s->st_mtime != o.st_mtime) || /* file modified */
((s->st_ctime != o.st_ctime) && /* file changed (moved) */
!(s->st_mtime == s->st_ctime && /* and it was not a change */
o.st_ctime < s->st_ctime))) /* due to delayed nfs write */
{
debug1("stolen_logfile: %s stolen!\n", l->name);
debug3("st_dev %d, st_ino %d, st_nlink %d\n",
(int)s->st_dev, (int)s->st_ino, (int)s->st_nlink);
debug2("s->st_size %d, o.st_size %d\n", (int)s->st_size, (int)o.st_size);
debug2("s->st_mtime %d, o.st_mtime %d\n",
(int)s->st_mtime, (int)o.st_mtime);
debug2("s->st_ctime %d, o.st_ctime %d\n",
(int)s->st_ctime, (int)o.st_ctime);
return -1;
}
debug1("stolen_logfile: %s o.k.\n", l->name);
return 0;
}
static struct logfile *
lookup_logfile(name)
char *name;
{
struct logfile *l;
for (l = logroot; l; l = l->next)
if (!strcmp(name, l->name))
return l;
return NULL;
}
struct logfile *
logfopen(name, fp)
char *name;
FILE *fp;
{
struct logfile *l;
if (!fp)
{
if (!(l = lookup_logfile(name)))
return NULL;
l->opencount++;
return l;
}
if (!(l = (struct logfile *)malloc(sizeof(struct logfile))))
return NULL;
if (!(l->st = (struct stat *)malloc(sizeof(struct stat))))
{
free((char *)l);
return NULL;
}
if (!(l->name = SaveStr(name)))
{
free((char *)l->st);
free((char *)l);
return NULL;
}
l->fp = fp;
l->opencount = 1;
l->writecount = 0;
l->flushcount = 0;
changed_logfile(l);
l->next = logroot;
logroot = l;
return l;
}
int
islogfile(name)
char *name;
{
if (!name)
return logroot ? 1 : 0;
return lookup_logfile(name) ? 1 : 0;
}
int
logfclose(l)
struct logfile *l;
{
struct logfile **lp;
for (lp = &logroot; *lp; lp = &(*lp)->next)
if (*lp == l)
break;
if (!*lp)
return -1;
if ((--l->opencount) > 0)
return 0;
if (l->opencount < 0)
abort();
*lp = l->next;
fclose(l->fp);
free(l->name);
free((char *)l);
return 0;
}
/*
* XXX
* write and flush both *should* check the file's stat, if it disappeared
* or changed, re-open it.
*/
int
logfwrite(l, buf, n)
struct logfile *l;
char *buf;
int n;
{
int r;
if (stolen_logfile(l) && lf_reopen_fn(l->name, fileno(l->fp), l))
return -1;
r = fwrite(buf, n, 1, l->fp);
l->writecount += l->flushcount + 1;
l->flushcount = 0;
changed_logfile(l);
return r;
}
int
logfflush(l)
struct logfile *l;
{
int r = 0;
if (!l)
for (l = logroot; l; l = l->next)
{
if (stolen_logfile(l) && lf_reopen_fn(l->name, fileno(l->fp), l))
return -1;
r |= fflush(l->fp);
l->flushcount++;
changed_logfile(l);
}
else
{
if (stolen_logfile(l) && lf_reopen_fn(l->name, fileno(l->fp), l))
return -1;
r = fflush(l->fp);
l->flushcount++;
changed_logfile(l);
}
return r;
}
screen-4.1.0/NEWS.3.9 0000644 0001750 0001750 00000015123 11642704565 012056 0 ustar abe abe -------------------------------
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 ' 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 - 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
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.
screen-4.1.0/input.c 0000644 0001750 0001750 00000033411 11642704565 012352 0 ustar abe abe /* 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
* http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
****************************************************************
*/
#include
#include "config.h"
#include "screen.h"
#include "extern.h"
#define INPUTLINE (flayer->l_height - 1)
static void InpProcess __P((char **, int *));
static void InpAbort __P((void));
static void InpRedisplayLine __P((int, int, int, int));
extern struct layer *flayer;
extern struct display *display;
extern struct mchar mchar_blank, mchar_so;
struct inpline
{
char buf[101]; /* text buffer */
int len; /* length of the editible string */
int pos; /* cursor position in editable string */
struct inpline *next, *prev;
};
/* 'inphist' is used to store the current input when scrolling through history.
* inpline->prev == history-prev
* inpline->next == history-next
*/
static struct inpline inphist;
struct inpdata
{
struct inpline inp;
int inpmaxlen; /* 100, or less, if caller has shorter buffer */
char *inpstring; /* the prompt */
int inpstringlen; /* length of the prompt */
int inpmode; /* INP_NOECHO, INP_RAW, INP_EVERY */
void (*inpfinfunc) __P((char *buf, int len, char *priv));
char *priv; /* private data for finfunc */
int privdata; /* private data space */
char *search; /* the search string */
};
static struct LayFuncs InpLf =
{
InpProcess,
InpAbort,
InpRedisplayLine,
DefClearLine,
DefRewrite,
DefResize,
DefRestore,
0
};
/*
** Here is the input routine
*/
/* called once, after InitOverlayPage in Input() or Isearch() */
void
inp_setprompt(p, s)
char *p, *s;
{
struct inpdata *inpdata;
inpdata = (struct inpdata *)flayer->l_data;
if (p)
{
inpdata->inpstringlen = strlen(p);
inpdata->inpstring = p;
}
if (s)
{
if (s != inpdata->inp.buf)
strncpy(inpdata->inp.buf, s, sizeof(inpdata->inp.buf) - 1);
inpdata->inp.buf[sizeof(inpdata->inp.buf) - 1] = 0;
inpdata->inp.pos = inpdata->inp.len = strlen(inpdata->inp.buf);
}
InpRedisplayLine(INPUTLINE, 0, flayer->l_width - 1, 0);
flayer->l_x = inpdata->inpstringlen + (inpdata->inpmode & INP_NOECHO ? 0 : inpdata->inp.pos);
flayer->l_y = INPUTLINE;
}
/*
* We dont use HS status line with Input().
* If we would use it, then we should check e_tgetflag("es") if
* we are allowed to use esc sequences there.
*
* mode is an OR of
* INP_NOECHO == suppress echoing of characters.
* INP_RAW == raw mode. call finfunc after each character typed.
* INP_EVERY == digraph mode.
*/
void
Input(istr, len, mode, finfunc, priv, data)
char *istr;
int len;
int mode;
void (*finfunc) __P((char *buf, int len, char *priv));
char *priv;
int data;
{
int maxlen;
struct inpdata *inpdata;
if (!flayer)
return;
if (len > 100)
len = 100;
if (!(mode & INP_NOECHO))
{
maxlen = flayer->l_width - 1 - strlen(istr);
if (len > maxlen)
len = maxlen;
}
if (len < 0)
{
LMsg(0, "Width %d chars too small", -len);
return;
}
if (InitOverlayPage(sizeof(*inpdata), &InpLf, 1))
return;
flayer->l_mode = 1;
inpdata = (struct inpdata *)flayer->l_data;
inpdata->inpmaxlen = len;
inpdata->inpfinfunc = finfunc;
inpdata->inp.pos = inpdata->inp.len = 0;
inpdata->inp.prev = inphist.prev;
inpdata->inpmode = mode;
inpdata->privdata = data;
if (!priv)
priv = (char*)&inpdata->privdata;
inpdata->priv = priv;
inpdata->inpstringlen = 0;
inpdata->inpstring = NULL;
inpdata->search = NULL;
if (istr)
inp_setprompt(istr, (char *)NULL);
}
static void
erase_chars(inpdata, from, to, x, mv)
struct inpdata *inpdata;
char *from;
char *to;
int x;
int mv;
{
int chng;
ASSERT(from < to);
if (inpdata->inp.len > to - inpdata->inp.buf)
bcopy(to, from, inpdata->inp.len - (to - inpdata->inp.buf));
chng = to - from;
if (mv)
{
x -= chng;
inpdata->inp.pos -= chng;
}
inpdata->inp.len -= chng;
if (!(inpdata->inpmode & INP_NOECHO))
{
struct mchar mc;
char *s = from < to ? from : to;
mc = mchar_so;
while (s < inpdata->inp.buf+inpdata->inp.len)
{
mc.image = *s++;
LPutChar(flayer, &mc, x++, INPUTLINE);
}
while (chng--)
LPutChar(flayer, &mchar_blank, x++, INPUTLINE);
x = inpdata->inpstringlen + inpdata->inp.pos;
LGotoPos(flayer, x, INPUTLINE);
}
}
static void
InpProcess(ppbuf, plen)
char **ppbuf;
int *plen;
{
int len, x;
char *pbuf;
char ch;
struct inpdata *inpdata;
struct display *inpdisplay;
int prev, next, search = 0;
inpdata = (struct inpdata *)flayer->l_data;
inpdisplay = display;
#define RESET_SEARCH do { if (inpdata->search) Free(inpdata->search); } while (0)
LGotoPos(flayer, inpdata->inpstringlen + (inpdata->inpmode & INP_NOECHO ? 0 : inpdata->inp.pos), INPUTLINE);
if (ppbuf == 0)
{
InpAbort();
return;
}
x = inpdata->inpstringlen + inpdata->inp.pos;
len = *plen;
pbuf = *ppbuf;
while (len)
{
char *p = inpdata->inp.buf + inpdata->inp.pos;
ch = *pbuf++;
len--;
if (inpdata->inpmode & INP_EVERY)
{
inpdata->inp.buf[inpdata->inp.len] = ch;
if (ch)
{
display = inpdisplay;
(*inpdata->inpfinfunc)(inpdata->inp.buf, inpdata->inp.len, inpdata->priv);
ch = inpdata->inp.buf[inpdata->inp.len];
}
}
else if (inpdata->inpmode & INP_RAW)
{
display = inpdisplay;
(*inpdata->inpfinfunc)(&ch, 1, inpdata->priv); /* raw */
if (ch)
continue;
}
if (((unsigned char)ch & 0177) >= ' ' && ch != 0177 && inpdata->inp.len < inpdata->inpmaxlen)
{
if (inpdata->inp.len > inpdata->inp.pos)
bcopy(p, p+1, inpdata->inp.len - inpdata->inp.pos);
inpdata->inp.buf[inpdata->inp.pos++] = ch;
inpdata->inp.len++;
if (!(inpdata->inpmode & INP_NOECHO))
{
struct mchar mc;
mc = mchar_so;
mc.image = *p++;
LPutChar(flayer, &mc, x, INPUTLINE);
x++;
if (p < inpdata->inp.buf+inpdata->inp.len)
{
while (p < inpdata->inp.buf+inpdata->inp.len)
{
mc.image = *p++;
LPutChar(flayer, &mc, x++, INPUTLINE);
}
x = inpdata->inpstringlen + inpdata->inp.pos;
LGotoPos(flayer, x, INPUTLINE);
}
}
RESET_SEARCH;
}
else if ((ch == '\b' || ch == 0177) && inpdata->inp.pos > 0)
{
erase_chars(inpdata, p-1, p, x, 1);
RESET_SEARCH;
}
else if (ch == '\025') /* CTRL-U */
{
x = inpdata->inpstringlen;
if (inpdata->inp.len && !(inpdata->inpmode & INP_NOECHO))
{
LClearArea(flayer, x, INPUTLINE, x + inpdata->inp.len - 1, INPUTLINE, 0, 0);
LGotoPos(flayer, x, INPUTLINE);
}
inpdata->inp.len = inpdata->inp.pos = 0;
}
else if (ch == '\013') /* CTRL-K */
{
x = inpdata->inpstringlen + inpdata->inp.pos;
if (inpdata->inp.len > inpdata->inp.pos && !(inpdata->inpmode & INP_NOECHO))
{
LClearArea(flayer, x, INPUTLINE, x + inpdata->inp.len - inpdata->inp.pos - 1, INPUTLINE, 0, 0);
LGotoPos(flayer, x, INPUTLINE);
}
inpdata->inp.len = inpdata->inp.pos;
}
else if (ch == '\027' && inpdata->inp.pos > 0) /* CTRL-W */
{
char *oldp = p--;
while (p > inpdata->inp.buf && *p == ' ')
p--;
while (p > inpdata->inp.buf && *(p - 1) != ' ')
p--;
erase_chars(inpdata, p, oldp, x, 1);
RESET_SEARCH;
}
else if (ch == '\004' && inpdata->inp.pos < inpdata->inp.len) /* CTRL-D */
{
erase_chars(inpdata, p, p+1, x, 0);
RESET_SEARCH;
}
else if (ch == '\001' || (unsigned char)ch == 0201) /* CTRL-A */
{
LGotoPos(flayer, x -= inpdata->inp.pos, INPUTLINE);
inpdata->inp.pos = 0;
}
else if ((ch == '\002' || (unsigned char)ch == 0202) && inpdata->inp.pos > 0) /* CTRL-B */
{
LGotoPos(flayer, --x, INPUTLINE);
inpdata->inp.pos--;
}
else if (ch == '\005' || (unsigned char)ch == 0205) /* CTRL-E */
{
LGotoPos(flayer, x += inpdata->inp.len - inpdata->inp.pos, INPUTLINE);
inpdata->inp.pos = inpdata->inp.len;
}
else if ((ch == '\006' || (unsigned char)ch == 0206) && inpdata->inp.pos < inpdata->inp.len) /* CTRL-F */
{
LGotoPos(flayer, ++x, INPUTLINE);
inpdata->inp.pos++;
}
else if ((prev = ((ch == '\020' || (unsigned char)ch == 0220) && /* CTRL-P */
inpdata->inp.prev)) ||
(next = ((ch == '\016' || (unsigned char)ch == 0216) && /* CTRL-N */
inpdata->inp.next)) ||
(search = ((ch == '\022' || (unsigned char)ch == 0222) && inpdata->inp.prev)))
{
struct mchar mc;
struct inpline *sel;
int pos = -1;
mc = mchar_so;
if (prev)
sel = inpdata->inp.prev;
else if (next)
sel = inpdata->inp.next;
else
{
/* search */
inpdata->inp.buf[inpdata->inp.len] = 0; /* Remove the ctrl-r from the end */
if (!inpdata->search)
inpdata->search = SaveStr(inpdata->inp.buf);
for (sel = inpdata->inp.prev; sel; sel = sel->prev)
{
char *f;
if ((f = strstr(sel->buf, inpdata->search)))
{
pos = f - sel->buf;
break;
}
}
if (!sel)
continue; /* Did not find a match. Process the next input. */
}
if (inpdata->inp.len && !(inpdata->inpmode & INP_NOECHO))
LClearArea(flayer, inpdata->inpstringlen, INPUTLINE, inpdata->inpstringlen + inpdata->inp.len - 1, INPUTLINE, 0, 0);
if ((prev || search) && !inpdata->inp.next)
inphist = inpdata->inp;
memcpy(&inpdata->inp, sel, sizeof(struct inpline));
if (pos != -1)
inpdata->inp.pos = pos;
if (inpdata->inp.len > inpdata->inpmaxlen)
inpdata->inp.len = inpdata->inpmaxlen;
if (inpdata->inp.pos > inpdata->inp.len)
inpdata->inp.pos = inpdata->inp.len;
x = inpdata->inpstringlen;
p = inpdata->inp.buf;
if (!(inpdata->inpmode & INP_NOECHO))
{
while (p < inpdata->inp.buf+inpdata->inp.len)
{
mc.image = *p++;
LPutChar(flayer, &mc, x++, INPUTLINE);
}
}
x = inpdata->inpstringlen + inpdata->inp.pos;
LGotoPos(flayer, x, INPUTLINE);
}
else if (ch == '\003' || ch == '\007' || ch == '\033' ||
ch == '\000' || ch == '\n' || ch == '\r')
{
if (ch != '\n' && ch != '\r')
inpdata->inp.len = 0;
inpdata->inp.buf[inpdata->inp.len] = 0;
if (inpdata->inp.len && !(inpdata->inpmode & (INP_NOECHO | INP_RAW)))
{
struct inpline *store;
/* Look for a duplicate first */
for (store = inphist.prev; store; store = store->prev)
{
if (strcmp(store->buf, inpdata->inp.buf) == 0)
{
if (store->next)
store->next->prev = store->prev;
if (store->prev)
store->prev->next = store->next;
store->pos = inpdata->inp.pos;
break;
}
}
if (!store)
{
store = malloc(sizeof(struct inpline));
memcpy(store, &inpdata->inp, sizeof(struct inpline));
}
store->next = &inphist;
store->prev = inphist.prev;
if (inphist.prev)
inphist.prev->next = store;
inphist.prev = store;
}
flayer->l_data = 0; /* so inpdata does not get freed */
InpAbort(); /* redisplays... */
*ppbuf = pbuf;
*plen = len;
display = inpdisplay;
if ((inpdata->inpmode & INP_RAW) == 0)
(*inpdata->inpfinfunc)(inpdata->inp.buf, inpdata->inp.len, inpdata->priv);
else
(*inpdata->inpfinfunc)(pbuf - 1, 0, inpdata->priv);
if (inpdata->search)
free(inpdata->search);
free(inpdata);
return;
}
else
{
/* The user was searching, and then pressed some non-control input. So reset
* the search string. */
RESET_SEARCH;
}
}
if (!(inpdata->inpmode & INP_RAW))
{
flayer->l_x = inpdata->inpstringlen + (inpdata->inpmode & INP_NOECHO ? 0 : inpdata->inp.pos);
flayer->l_y = INPUTLINE;
}
*ppbuf = pbuf;
*plen = len;
}
static void
InpAbort()
{
LAY_CALL_UP(LayRedisplayLine(INPUTLINE, 0, flayer->l_width - 1, 0));
ExitOverlayPage();
}
static void
InpRedisplayLine(y, xs, xe, isblank)
int y, xs, xe, isblank;
{
int q, r, s, l, v;
struct inpdata *inpdata;
inpdata = (struct inpdata *)flayer->l_data;
if (y != INPUTLINE)
{
LAY_CALL_UP(LayRedisplayLine(y, xs, xe, isblank));
return;
}
inpdata->inp.buf[inpdata->inp.len] = 0;
q = xs;
v = xe - xs + 1;
s = 0;
r = inpdata->inpstringlen;
if (v > 0 && q < r)
{
l = v;
if (l > r - q)
l = r - q;
LPutStr(flayer, inpdata->inpstring + q - s, l, &mchar_so, q, y);
q += l;
v -= l;
}
s = r;
r += inpdata->inp.len;
if (!(inpdata->inpmode & INP_NOECHO) && v > 0 && q < r)
{
l = v;
if (l > r - q)
l = r - q;
LPutStr(flayer, inpdata->inp.buf + q - s, l, &mchar_so, q, y);
q += l;
v -= l;
}
s = r;
r = flayer->l_width;
if (!isblank && v > 0 && q < r)
{
l = v;
if (l > r - q)
l = r - q;
LClearArea(flayer, q, y, q + l - 1, y, 0, 0);
q += l;
}
}
screen-4.1.0/NEWS 0000644 0001750 0001750 00000001352 11642704565 011545 0 ustar abe abe ------------------------------
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'.
screen-4.1.0/FAQ 0000777 0001750 0001750 00000000000 11732171240 012457 2doc/FAQ ustar abe abe screen-4.1.0/ansi.h 0000644 0001750 0001750 00000010675 11642704565 012161 0 ustar abe abe /* 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
* http://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<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
screen-4.1.0/term.sh 0000644 0001750 0001750 00000005623 11642704565 012356 0 ustar abe abe #! /bin/sh
if test -z "$AWK"; then
AWK=awk
fi
if test -z "$srcdir"; then
srcdir=.
fi
LC_ALL=C
export LC_ALL
rm -f term.h
cat << EOF > term.h
/*
* This file is automagically created from term.c -- DO NOT EDIT
*/
#define T_FLG 0
#define T_NUM 1
#define T_STR 2
struct term
{
char *tcname;
int type;
};
union tcu
{
int flg;
int num;
char *str;
};
EOF
#
# SCO-Unix sufferers may need to use the following lines:
# perl -p < ${srcdir}/term.c \
# -e 's/"/"C/ if /"[A-Z]."/;' \
# -e 'y/[a-z]/[A-Z]/ if /"/;' \
#
sed < ${srcdir}/term.c \
-e '/"[A-Z]."/s/"/"C/' \
-e '/"/y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \
| $AWK '
/^ [{] ".*KMAPDEF[(].*$/{
if (min == 0) min = s
max = s;
}
/^ [{] ".*KMAPADEF[(].*$/{
if (amin == 0) amin = s
amax = s;
}
/^ [{] ".*KMAPMDEF[(].*$/{
if (mmin == 0) mmin = s
mmax = s;
}
/^ [{] ".*$/{
a=substr($2,2,length($2)-3);
b=substr($3,3,3);
if (nolist == 0) {
printf "#define d_%s d_tcs[%d].%s\n",a,s,b
printf "#define D_%s (D_tcs[%d].%s)\n",a,s,b
}
s++;
}
/\/* define/{
printf "#define %s %d\n",$3,s
}
/\/* nolist/{
nolist = 1;
}
/\/* list/{
nolist = 0;
}
END {
printf "\n#ifdef MAPKEYS\n"
printf "# define KMAPDEFSTART %d\n", min
printf "# define NKMAPDEF %d\n", max-min+1
printf "# define KMAPADEFSTART %d\n", amin
printf "# define NKMAPADEF %d\n", amax-amin+1
printf "# define KMAPMDEFSTART %d\n", mmin
printf "# define NKMAPMDEF %d\n", mmax-mmin+1
printf "#endif\n"
}
' | sed -e s/NUM/num/ -e s/STR/str/ -e s/FLG/flg/ \
>> term.h
rm -f kmapdef.c
cat << EOF > kmapdef.c
/*
* This file is automagically created from term.c -- DO NOT EDIT
*/
#include "config.h"
#ifdef MAPKEYS
EOF
$AWK < ${srcdir}/term.c '
/^ [{] ".*KMAP.*$/{
for (i = 0; i < 3; i++) {
q = $(5+i)
if (substr(q, 1, 5) == "KMAPD") {
if (min == 0) min = s
max = s
arr[s] = substr(q, 9, length(q)-9)
}
if (substr(q, 1, 5) == "KMAPA") {
if (amin == 0) amin = s
amax = s
anarr[s] = substr(q, 10, length(q)-10)
}
if (substr(q, 1, 5) == "KMAPM") {
if (mmin == 0) mmin = s
mmax = s
mnarr[s] = substr(q, 10, length(q)-10)
}
}
}
/^ [{] ".*$/{
s++;
}
END {
printf "char *kmapdef[] = {\n"
for (s = min; s <= max; s++) {
if (arr[s])
printf "%s", arr[s]
else
printf "0"
if (s < max)
printf ",\n"
else
printf "\n"
}
printf "};\n\n"
printf "char *kmapadef[] = {\n"
for (s = amin; s <= amax; s++) {
if (anarr[s])
printf "%s", anarr[s]
else
printf "0"
if (s < amax)
printf ",\n"
else
printf "\n"
}
printf "};\n\n"
printf "char *kmapmdef[] = {\n"
for (s = mmin; s <= mmax; s++) {
if (mnarr[s])
printf "%s", mnarr[s]
else
printf "0"
if (s < mmax)
printf ",\n"
else
printf "\n"
}
printf "};\n\n#endif\n"
}
' >> kmapdef.c
chmod a-w kmapdef.c
chmod a-w term.h
screen-4.1.0/fileio.c 0000644 0001750 0001750 00000046567 11642704565 012502 0 ustar abe abe /* 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
* http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
****************************************************************
*/
#include
#include
#include
#include
#ifndef SIGINT
# include
#endif
#include "config.h"
#include "screen.h"
#include "extern.h"
extern struct display *display, *displays;
extern struct win *fore;
extern struct layer *flayer;
extern int real_uid, eff_uid;
extern int real_gid, eff_gid;
extern char *extra_incap, *extra_outcap;
extern char *home, *RcFileName;
extern char SockPath[], *SockName;
#ifdef COPY_PASTE
extern char *BufferFile;
#endif
extern int hardcopy_append;
extern char *hardcopydir;
static char *CatExtra __P((char *, char *));
static char *findrcfile __P((char *));
char *rc_name = "";
int rc_recursion = 0;
static char *
CatExtra(str1, str2)
register char *str1, *str2;
{
register char *cp;
register int len1, len2, add_colon;
len1 = strlen(str1);
if (len1 == 0)
return str2;
add_colon = (str1[len1 - 1] != ':');
if (str2)
{
len2 = strlen(str2);
if ((cp = realloc(str2, (unsigned) len1 + len2 + add_colon + 1)) == NULL)
Panic(0, "%s", strnomem);
bcopy(cp, cp + len1 + add_colon, len2 + 1);
}
else
{
if (len1 == 0)
return 0;
if ((cp = malloc((unsigned) len1 + add_colon + 1)) == NULL)
Panic(0, "%s", strnomem);
cp[len1 + add_colon] = '\0';
}
bcopy(str1, cp, len1);
if (add_colon)
cp[len1] = ':';
return cp;
}
static char *
findrcfile(rcfile)
char *rcfile;
{
char buf[256];
char *p;
/* Tilde prefix support courtesy ,
* taken from a Debian patch. */
if (rcfile && *rcfile == '~')
{
static char rcfilename_tilde_exp[MAXPATHLEN+1];
char *slash_position = strchr(rcfile, '/');
if (slash_position == rcfile+1)
{
char *home = getenv("HOME");
if (!home)
{
Msg(0, "%s: source: tilde expansion failed", rc_name);
return NULL;
}
snprintf(rcfilename_tilde_exp, MAXPATHLEN, "%s/%s", home, rcfile+2);
}
else if (slash_position)
{
struct passwd *p;
*slash_position = 0;
p = getpwnam(rcfile+1);
if (!p)
{
Msg(0, "%s: source: tilde expansion failed for user %s", rc_name, rcfile+1);
return NULL;
}
snprintf(rcfilename_tilde_exp, MAXPATHLEN, "%s/%s", p->pw_dir, slash_position+1);
}
else
{
Msg(0, "%s: source: illegal tilde expression.", rc_name);
return NULL;
}
rcfile = rcfilename_tilde_exp;
}
if (rcfile)
{
char *rcend = rindex(rc_name, '/');
if (*rcfile != '/' && rcend && (rcend - rc_name) + strlen(rcfile) + 2 < sizeof(buf))
{
strncpy(buf, rc_name, rcend - rc_name + 1);
strcpy(buf + (rcend - rc_name) + 1, rcfile);
if (access(buf, R_OK) == 0)
return SaveStr(buf);
}
debug1("findrcfile: you specified '%s'\n", rcfile);
return SaveStr(rcfile);
}
debug("findrcfile: you specified nothing...\n");
if ((p = getenv("SCREENRC")) != NULL && *p != '\0')
{
debug1(" $SCREENRC has: '%s'\n", p);
return SaveStr(p);
}
else
{
debug(" ...nothing in $SCREENRC, defaulting $HOME/.screenrc\n");
if (strlen(home) > sizeof(buf) - 12)
Panic(0, "Rc: home too large");
sprintf(buf, "%s/.screenrc", home);
return SaveStr(buf);
}
}
/*
* this will be called twice:
* 1) rcfilename = "/etc/screenrc"
* 2) rcfilename = RcFileName
*/
int
StartRc(rcfilename, nopanic)
char *rcfilename;
int nopanic;
{
register int argc, len;
register char *p, *cp;
char buf[2048];
char *args[MAXARGS];
int argl[MAXARGS];
FILE *fp;
char *oldrc_name = rc_name;
/* always fix termcap/info capabilities */
extra_incap = CatExtra("TF", extra_incap);
/* Special settings for vt100 and others */
if (display && (!strncmp(D_termname, "vt", 2) || !strncmp(D_termname, "xterm", 5)))
extra_incap = CatExtra("xn:f0=\033Op:f1=\033Oq:f2=\033Or:f3=\033Os:f4=\033Ot:f5=\033Ou:f6=\033Ov:f7=\033Ow:f8=\033Ox:f9=\033Oy:f.=\033On:f,=\033Ol:fe=\033OM:f+=\033Ok:f-=\033Om:f*=\033Oj:f/=\033Oo:fq=\033OX", extra_incap);
rc_name = findrcfile(rcfilename);
if (rc_name == NULL || (fp = secfopen(rc_name, "r")) == NULL)
{
const char *rc_nonnull = rc_name ? rc_name : rcfilename;
if (!rc_recursion && RcFileName && !strcmp(RcFileName, rc_nonnull))
{
/*
* User explicitly gave us that name,
* this is the only case, where we get angry, if we can't read
* the file.
*/
debug3("StartRc: '%s','%s', '%s'\n", RcFileName, rc_name ? rc_name : "(null)", rcfilename);
if (!nopanic) Panic(0, "Unable to open \"%s\".", rc_nonnull);
/* possibly NOTREACHED */
}
debug1("StartRc: '%s' no good. ignored\n", rc_nonnull);
if (rc_name)
Free(rc_name);
rc_name = oldrc_name;
return 1;
}
while (fgets(buf, sizeof buf, fp) != NULL)
{
if ((p = rindex(buf, '\n')) != NULL)
*p = '\0';
if ((argc = Parse(buf, sizeof buf, args, argl)) == 0)
continue;
if (strcmp(args[0], "echo") == 0)
{
if (!display)
continue;
if (argc < 2 || (argc == 3 && strcmp(args[1], "-n")) || argc > 3)
{
Msg(0, "%s: 'echo [-n] \"string\"' expected.", rc_name);
continue;
}
AddStr(args[argc - 1]);
if (argc != 3)
{
AddStr("\r\n");
Flush(0);
}
}
else if (strcmp(args[0], "sleep") == 0)
{
if (!display)
continue;
debug("sleeeeeeep\n");
if (argc != 2)
{
Msg(0, "%s: sleep: one numeric argument expected.", rc_name);
continue;
}
DisplaySleep1000(1000 * atoi(args[1]), 1);
}
#ifdef TERMINFO
else if (!strcmp(args[0], "termcapinfo") || !strcmp(args[0], "terminfo"))
#else
else if (!strcmp(args[0], "termcapinfo") || !strcmp(args[0], "termcap"))
#endif
{
if (!display)
continue;
if (argc < 3 || argc > 4)
{
Msg(0, "%s: %s: incorrect number of arguments.", rc_name, args[0]);
continue;
}
for (p = args[1]; p && *p; p = cp)
{
if ((cp = index(p, '|')) != 0)
*cp++ = '\0';
len = strlen(p);
if (p[len - 1] == '*')
{
if (!(len - 1) || !strncmp(p, D_termname, len - 1))
break;
}
else if (!strcmp(p, D_termname))
break;
}
if (!(p && *p))
continue;
extra_incap = CatExtra(args[2], extra_incap);
if (argc == 4)
extra_outcap = CatExtra(args[3], extra_outcap);
}
else if (!strcmp(args[0], "source"))
{
if (rc_recursion <= 10)
{
rc_recursion++;
(void)StartRc(args[1], 0);
rc_recursion--;
}
}
}
fclose(fp);
Free(rc_name);
rc_name = oldrc_name;
return 0;
}
void
FinishRc(rcfilename)
char *rcfilename;
{
char buf[2048];
FILE *fp;
char *oldrc_name = rc_name;
rc_name = findrcfile(rcfilename);
if (rc_name == NULL || (fp = secfopen(rc_name, "r")) == NULL)
{
const char *rc_nonnull = rc_name ? rc_name : rcfilename;
if (rc_recursion)
Msg(errno, "%s: source %s", oldrc_name, rc_nonnull);
else if (RcFileName && !strcmp(RcFileName, rc_nonnull))
{
/*
* User explicitly gave us that name,
* this is the only case, where we get angry, if we can't read
* the file.
*/
debug3("FinishRc:'%s','%s','%s'\n", RcFileName, rc_name ? rc_name : "(null)", rcfilename);
Panic(0, "Unable to open \"%s\".", rc_nonnull);
/* NOTREACHED */
}
debug1("FinishRc: '%s' no good. ignored\n", rc_nonnull);
if (rc_name)
Free(rc_name);
rc_name = oldrc_name;
return;
}
debug("finishrc is going...\n");
while (fgets(buf, sizeof buf, fp) != NULL)
RcLine(buf, sizeof buf);
(void) fclose(fp);
Free(rc_name);
rc_name = oldrc_name;
}
void
do_source(rcfilename)
char *rcfilename;
{
if (rc_recursion > 10)
{
Msg(0, "%s: source: recursion limit reached", rc_name);
return;
}
rc_recursion++;
FinishRc(rcfilename);
rc_recursion--;
}
/*
* Running a Command Line in the environment determined by the display.
* The fore window is taken from the display as well as the user.
* This is bad when we run detached.
*/
void
RcLine(ubuf, ubufl)
char *ubuf;
int ubufl;
{
char *args[MAXARGS];
int argl[MAXARGS];
#ifdef MULTIUSER
extern struct acluser *EffectiveAclUser; /* acl.c */
extern struct acluser *users; /* acl.c */
#endif
if (display)
{
fore = D_fore;
flayer = D_forecv->c_layer;
}
else
flayer = fore ? fore->w_savelayer : 0;
if (Parse(ubuf, ubufl, args, argl) <= 0)
return;
#ifdef MULTIUSER
if (!display)
{
/* the session owner does it, when there is no display here */
EffectiveAclUser = users;
debug("RcLine: WARNING, no display no user! Session owner executes command\n");
}
#endif
DoCommand(args, argl);
#ifdef MULTIUSER
EffectiveAclUser = 0;
#endif
}
/*
* needs display for copybuffer access and termcap dumping
*/
void
WriteFile(user, fn, dump)
struct acluser *user;
char *fn;
int dump;
{
/* dump==0: create .termcap,
* dump==1: hardcopy,
* #ifdef COPY_PASTE
* dump==2: BUFFERFILE
* #endif COPY_PASTE
* dump==1: scrollback,
*/
register int i, j, k;
register char *p;
register FILE *f;
char fnbuf[1024];
char *mode = "w";
#ifdef COPY_PASTE
int public = 0;
# ifdef _MODE_T
mode_t old_umask;
# else
int old_umask;
# endif
# ifdef HAVE_LSTAT
struct stat stb, stb2;
int fd, exists = 0;
# endif
#endif
switch (dump)
{
case DUMP_TERMCAP:
if (fn == 0)
{
i = SockName - SockPath;
if (i > (int)sizeof(fnbuf) - 9)
i = 0;
strncpy(fnbuf, SockPath, i);
strcpy(fnbuf + i, ".termcap");
fn = fnbuf;
}
break;
case DUMP_HARDCOPY:
case DUMP_SCROLLBACK:
if (fn == 0)
{
if (fore == 0)
return;
if (hardcopydir && *hardcopydir && strlen(hardcopydir) < sizeof(fnbuf) - 21)
sprintf(fnbuf, "%s/hardcopy.%d", hardcopydir, fore->w_number);
else
sprintf(fnbuf, "hardcopy.%d", fore->w_number);
fn = fnbuf;
}
if (hardcopy_append && !access(fn, W_OK))
mode = "a";
break;
#ifdef COPY_PASTE
case DUMP_EXCHANGE:
if (fn == 0)
{
strncpy(fnbuf, BufferFile, sizeof(fnbuf) - 1);
fnbuf[sizeof(fnbuf) - 1] = 0;
fn = fnbuf;
}
public = !strcmp(fn, DEFAULT_BUFFERFILE);
# ifdef HAVE_LSTAT
exists = !lstat(fn, &stb);
if (public && exists && (S_ISLNK(stb.st_mode) || stb.st_nlink > 1))
{
Msg(0, "No write to links, please.");
return;
}
# endif
break;
#endif
}
debug2("WriteFile(%d) %s\n", dump, fn);
if (UserContext() > 0)
{
debug("Writefile: usercontext\n");
#ifdef COPY_PASTE
if (dump == DUMP_EXCHANGE && public)
{
old_umask = umask(0);
# ifdef HAVE_LSTAT
if (exists)
{
if ((fd = open(fn, O_WRONLY, 0666)) >= 0)
{
if (fstat(fd, &stb2) == 0 && stb.st_dev == stb2.st_dev && stb.st_ino == stb2.st_ino)
ftruncate(fd, 0);
else
{
close(fd);
fd = -1;
}
}
}
else
fd = open(fn, O_WRONLY|O_CREAT|O_EXCL, 0666);
f = fd >= 0 ? fdopen(fd, mode) : 0;
# else
f = fopen(fn, mode);
# endif
umask(old_umask);
}
else
#endif /* COPY_PASTE */
f = fopen(fn, mode);
if (f == NULL)
{
debug2("WriteFile: fopen(%s,\"%s\") failed\n", fn, mode);
UserReturn(0);
}
else
{
switch (dump)
{
case DUMP_HARDCOPY:
case DUMP_SCROLLBACK:
if (!fore)
break;
if (*mode == 'a')
{
putc('>', f);
for (j = fore->w_width - 2; j > 0; j--)
putc('=', f);
fputs("<\n", f);
}
if (dump == DUMP_SCROLLBACK)
{
#ifdef COPY_PASTE
for (i = 0; i < fore->w_histheight; i++)
{
p = (char *)(WIN(i)->image);
for (k = fore->w_width - 1; k >= 0 && p[k] == ' '; k--)
;
for (j = 0; j <= k; j++)
putc(p[j], f);
putc('\n', f);
}
#endif
}
for (i = 0; i < fore->w_height; i++)
{
p = (char *)fore->w_mlines[i].image;
for (k = fore->w_width - 1; k >= 0 && p[k] == ' '; k--)
;
for (j = 0; j <= k; j++)
putc(p[j], f);
putc('\n', f);
}
break;
case DUMP_TERMCAP:
if ((p = index(MakeTermcap(fore->w_aflag), '=')) != NULL)
{
fputs(++p, f);
putc('\n', f);
}
break;
#ifdef COPY_PASTE
case DUMP_EXCHANGE:
p = user->u_plop.buf;
for (i = user->u_plop.len; i-- > 0; p++)
if (*p == '\r' && (i == 0 || p[1] != '\n'))
putc('\n', f);
else
putc(*p, f);
break;
#endif
}
(void) fclose(f);
UserReturn(1);
}
}
if (UserStatus() <= 0)
Msg(0, "Cannot open \"%s\"", fn);
else if (display && !*rc_name)
{
switch (dump)
{
case DUMP_TERMCAP:
Msg(0, "Termcap entry written to \"%s\".", fn);
break;
case DUMP_HARDCOPY:
case DUMP_SCROLLBACK:
Msg(0, "Screen image %s to \"%s\".",
(*mode == 'a') ? "appended" : "written", fn);
break;
#ifdef COPY_PASTE
case DUMP_EXCHANGE:
Msg(0, "Copybuffer written to \"%s\".", fn);
#endif
}
}
}
#ifdef COPY_PASTE
/*
* returns an allocated buffer which holds a copy of the file named fn.
* lenp (if nonzero) points to a location, where the buffer size should be
* stored.
*/
char *
ReadFile(fn, lenp)
char *fn;
int *lenp;
{
int i, l, size;
char c, *bp, *buf;
struct stat stb;
ASSERT(lenp);
debug1("ReadFile(%s)\n", fn);
if ((i = secopen(fn, O_RDONLY, 0)) < 0)
{
Msg(errno, "no %s -- no slurp", fn);
return NULL;
}
if (fstat(i, &stb))
{
Msg(errno, "no good %s -- no slurp", fn);
close(i);
return NULL;
}
size = stb.st_size;
if ((buf = malloc(size)) == NULL)
{
close(i);
Msg(0, "%s", strnomem);
return NULL;
}
errno = 0;
if ((l = read(i, buf, size)) != size)
{
if (l < 0)
l = 0;
Msg(errno, "Got only %d bytes from %s", l, fn);
close(i);
}
else
{
if (read(i, &c, 1) > 0)
Msg(0, "Slurped only %d characters (of %d) into buffer - try again",
l, size);
else
Msg(0, "Slurped %d characters into buffer", l);
}
close(i);
*lenp = l;
for (bp = buf; l-- > 0; bp++)
if (*bp == '\n' && (bp == buf || bp[-1] != '\r'))
*bp = '\r';
return buf;
}
void
KillBuffers()
{
if (UserContext() > 0)
UserReturn(unlink(BufferFile) ? errno : 0);
errno = UserStatus();
Msg(errno, "%s %sremoved", BufferFile, errno ? "not " : "");
}
#endif /* COPY_PASTE */
/*
* (Almost) secure open and fopen...
*/
FILE *
secfopen(name, mode)
char *name;
char *mode;
{
FILE *fi;
#ifndef USE_SETEUID
int flags, fd;
#endif
debug2("secfopen(%s, %s)\n", name, mode);
#ifdef USE_SETEUID
xseteuid(real_uid);
xsetegid(real_gid);
fi = fopen(name, mode);
xseteuid(eff_uid);
xsetegid(eff_gid);
return fi;
#else
if (eff_uid == real_uid)
return fopen(name, mode);
if (mode[0] && mode[1] == '+')
flags = O_RDWR;
else
flags = (mode[0] == 'r') ? O_RDONLY : O_WRONLY;
if (mode[0] == 'w')
flags |= O_CREAT | O_TRUNC;
else if (mode[0] == 'a')
flags |= O_CREAT | O_APPEND;
else if (mode[0] != 'r')
{
errno = EINVAL;
return 0;
}
if ((fd = secopen(name, flags, 0666)) < 0)
return 0;
if ((fi = fdopen(fd, mode)) == 0)
{
close(fd);
return 0;
}
return fi;
#endif
}
int
secopen(name, flags, mode)
char *name;
int flags;
int mode;
{
int fd;
#ifndef USE_SETEUID
int q;
struct stat stb;
#endif
debug3("secopen(%s, 0x%x, 0%03o)\n", name, flags, mode);
#ifdef USE_SETEUID
xseteuid(real_uid);
xsetegid(real_gid);
fd = open(name, flags, mode);
xseteuid(eff_uid);
xsetegid(eff_gid);
return fd;
#else
if (eff_uid == real_uid)
return open(name, flags, mode);
/* Truncation/creation is done in UserContext */
if ((flags & O_TRUNC) || ((flags & O_CREAT) && access(name, F_OK)))
{
if (UserContext() > 0)
{
if ((fd = open(name, flags, mode)) >= 0)
{
close(fd);
UserReturn(0);
}
if (errno == 0)
errno = EACCES;
UserReturn(errno);
}
if ((q = UserStatus()))
{
if (q > 0)
errno = q;
return -1;
}
}
if (access(name, F_OK))
return -1;
if ((fd = open(name, flags & ~(O_TRUNC | O_CREAT), 0)) < 0)
return -1;
debug("open successful\n");
if (fstat(fd, &stb))
{
close(fd);
return -1;
}
debug("fstat successful\n");
if (stb.st_uid != real_uid)
{
switch (flags & (O_RDONLY | O_WRONLY | O_RDWR))
{
case O_RDONLY:
q = 0004;
break;
case O_WRONLY:
q = 0002;
break;
default:
q = 0006;
break;
}
if ((stb.st_mode & q) != q)
{
debug1("secopen: permission denied (%03o)\n", stb.st_mode & 07777);
close(fd);
errno = EACCES;
return -1;
}
}
debug1("secopen ok - returning %d\n", fd);
return fd;
#endif
}
int
printpipe(p, cmd)
struct win *p;
char *cmd;
{
int pi[2];
if (pipe(pi))
{
WMsg(p, errno, "printing pipe");
return -1;
}
switch (fork())
{
case -1:
WMsg(p, errno, "printing fork");
return -1;
case 0:
display = p->w_pdisplay;
displays = 0;
#ifdef DEBUG
if (dfp && dfp != stderr)
fclose(dfp);
#endif
close(0);
dup(pi[0]);
closeallfiles(0);
if (setgid(real_gid) || setuid(real_uid))
Panic(errno, "printpipe setuid");
#ifdef SIGPIPE
signal(SIGPIPE, SIG_DFL);
#endif
execl("/bin/sh", "sh", "-c", cmd, (char *)0);
Panic(errno, "/bin/sh");
default:
break;
}
close(pi[0]);
return pi[1];
}
int
readpipe(cmdv)
char **cmdv;
{
int pi[2];
if (pipe(pi))
{
Msg(errno, "pipe");
return -1;
}
switch (fork())
{
case -1:
Msg(errno, "fork");
return -1;
case 0:
displays = 0;
#ifdef DEBUG
if (dfp && dfp != stderr)
fclose(dfp);
#endif
close(1);
if (dup(pi[1]) != 1)
{
close(pi[1]);
Panic(0, "dup");
}
closeallfiles(1);
if (setgid(real_gid) || setuid(real_uid))
{
close(1);
Panic(errno, "setuid/setgid");
}
#ifdef SIGPIPE
signal(SIGPIPE, SIG_DFL);
#endif
execvp(*cmdv, cmdv);
close(1);
Panic(errno, "%s", *cmdv);
default:
break;
}
close(pi[1]);
return pi[0];
}
screen-4.1.0/.iscreenrc 0000644 0001750 0001750 00000015166 11642704565 013034 0 ustar abe abe #
# 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" don'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 is'nt 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 non'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=j9kx5 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
# Flowcontrol 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 wonderfull tcsh-prompt:
# set prompt="%{^[k^[\\%}%h %c2(%m)%# "
#
# defining a shellaka that contains a pipe-symbol (|) activites 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 recomended to keep sccs and talk happy.
# (thus we have ^A# or. ^Ac for windowcreation 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 amange to type ^Ak by accident.
#bind ^k
#bind K kill
echo -n "."
#screen 1:faui43 # My good old : 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."
screen-4.1.0/list_generic.c 0000644 0001750 0001750 00000024627 11642704565 013673 0 ustar abe abe /* Copyright (c) 2010
* Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
* Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
*
* 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
* http://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 "list_generic.h"
#include "layer.h"
#include "extern.h"
/* Deals with a generic list display */
extern struct layer *flayer;
static void ListProcess __P((char **, int *));
static void ListAbort __P((void));
static void ListRedisplayLine __P((int, int, int, int));
static void ListClearLine __P((int, int, int, int));
static int ListRewrite __P((int, int, int, struct mchar *, int));
static int ListResize __P((int, int));
static void ListRestore __P((void));
static void ListFree __P((void *));
struct LayFuncs ListLf =
{
ListProcess,
ListAbort,
ListRedisplayLine,
ListClearLine,
ListRewrite,
ListResize,
ListRestore,
ListFree
};
/** Returns non-zero on success. */
struct ListData *
glist_display(struct GenericList *list, const char *name)
{
struct ListData *ldata;
if (InitOverlayPage(sizeof(struct ListData), &ListLf, 0))
return NULL;
ldata = flayer->l_data;
ldata->name = name; /* We do not SaveStr, since the strings should be all static literals */
ldata->list_fn = list;
flayer->l_mode = 1;
flayer->l_x = 0;
flayer->l_y = flayer->l_height - 1;
return ldata;
}
static void
glist_decide_top(struct ListData *ldata)
{
int count = flayer->l_height - 5; /* 2 for header, 1 for footer */
struct ListRow *top = ldata->selected;
for (; count && top != ldata->root; top = top->prev, count--)
;
ldata->top = top;
}
static struct ListRow *
glist_search_dir(struct ListData *ldata, struct ListRow *start, int dir)
{
struct ListRow *row = (dir == 1) ? start->next : start->prev;
for (; row; row = (dir == 1) ? row->next : row->prev)
if (ldata->list_fn->gl_matchrow(ldata, row, ldata->search))
return row;
if (dir == 1)
row = ldata->root;
else
{
/* First, go to the end */
if (!start->next)
row = start;
else
for (row = start->next; row->next; row = row->next)
;
}
for (; row != start; row = (dir == 1) ? row->next : row->prev)
if (ldata->list_fn->gl_matchrow(ldata, row, ldata->search))
break;
return row;
}
static void
glist_search(char *buf, int len, char *data)
{
struct ListData *ldata = (struct ListData *)data;
struct ListRow *row;
if (ldata->search)
Free(ldata->search);
if (len > 0)
ldata->search = SaveStr(buf);
else
return;
for (row = ldata->selected; row; row = row->next)
if (ldata->list_fn->gl_matchrow(ldata, row, ldata->search))
break;
if (!row)
for (row = ldata->root; row != ldata->selected; row = row->next)
if (ldata->list_fn->gl_matchrow(ldata, row, ldata->search))
break;
if (row == ldata->selected)
return;
ldata->selected = row;
if (ldata->selected->y == -1)
glist_decide_top(ldata);
glist_display_all(ldata);
}
static void ListProcess(char **ppbuf, int *plen)
{
struct ListData *ldata = flayer->l_data;
int count = 0;
while (*plen > 0)
{
struct ListRow *old;
unsigned char ch;
if (!flayer->l_mouseevent.start && ldata->list_fn->gl_pinput &&
ldata->list_fn->gl_pinput(ldata, ppbuf, plen))
continue;
ch = **ppbuf;
++*ppbuf;
--*plen;
if (flayer->l_mouseevent.start)
{
int r = LayProcessMouse(flayer, ch);
if (r == -1)
{
LayProcessMouseSwitch(flayer, 0);
continue;
}
else
{
if (r)
ch = 0222;
else
continue;
}
}
if (!ldata->selected)
{
*plen = 0;
break;
}
old = ldata->selected;
processchar:
switch (ch)
{
case ' ':
break;
case '\r':
case '\n':
break;
case 0220: /* up */
case 16: /* ^P */
case 'k':
if (!ldata->selected->prev) /* There's no where to go */
break;
ldata->selected = old->prev;
break;
case 0216: /* down */
case 14: /* ^N like emacs */
case 'j':
if (!ldata->selected->next) /* Nothing to do */
break;
ldata->selected = old->next;
break;
case 033: /* escape */
case 007: /* ^G */
ListAbort();
*plen = 0;
return;
case 0201: /* home */
case 0001: /* ^A */
ldata->selected = ldata->root;
break;
case 0205: /* end */
case 0005: /* ^E */
while (ldata->selected->next)
ldata->selected = ldata->selected->next;
if (ldata->selected->y != -1)
{
/* Both old and current selections are on the screen. So we can just
* redraw these two affected rows. */
}
break;
case 0004: /* ^D (half-page down) */
case 0006: /* page-down, ^F */
count = (flayer->l_height - 4) >> (ch == 0004);
for (; ldata->selected->next && --count;
ldata->selected = ldata->selected->next)
;
break;
case 0025: /* ^U (half-page up) */
case 0002: /* page-up, ^B */
count = (flayer->l_height - 4) >> (ch == 0025);
for (; ldata->selected->prev && --count;
ldata->selected = ldata->selected->prev)
;
break;
case '/': /* start searching */
if (ldata->list_fn->gl_matchrow)
{
char *s;
Input("Search: ", 80, INP_COOKED, glist_search, (char *)ldata, 0);
if ((s = ldata->search))
{
for (; *s; s++)
{
char *ss = s;
int n = 1;
LayProcess(&ss, &n);
}
}
}
break;
/* The following deal with searching. */
case 'n': /* search next */
if (ldata->list_fn->gl_matchrow && ldata->search)
ldata->selected = glist_search_dir(ldata, ldata->selected, 1);
break;
case 'N': /* search prev */
if (ldata->list_fn->gl_matchrow && ldata->search)
ldata->selected = glist_search_dir(ldata, ldata->selected, -1);
break;
/* Now, mouse events. */
case 0222:
if (flayer->l_mouseevent.start)
{
int button = flayer->l_mouseevent.buffer[0];
if (button == 'a') /* Scroll down */
ch = 'j';
else if (button == '`') /* Scroll up */
ch = 'k';
else if (button == ' ') /* Left click */
{
int y = flayer->l_mouseevent.buffer[2];
struct ListRow *r = ldata->top;
for (r = ldata->top; r && r->y != -1 && r->y != y; r = r->next)
;
if (r && r->y == y)
ldata->selected = r;
ch = 0;
}
else
ch = 0;
LayProcessMouseSwitch(flayer, 0);
if (ch)
goto processchar;
}
else
LayProcessMouseSwitch(flayer, 1);
break;
}
if (old == ldata->selected) /* The selection didn't change */
continue;
if (ldata->selected->y == -1)
{
/* We need to list all the rows, since we are scrolling down. But first,
* find the top of the visible list. */
glist_decide_top(ldata);
glist_display_all(ldata);
}
else
{
/* just redisplay the two lines. */
ldata->list_fn->gl_printrow(ldata, old);
ldata->list_fn->gl_printrow(ldata, ldata->selected);
flayer->l_y = ldata->selected->y;
LaySetCursor();
}
}
}
static void ListAbort(void)
{
LAY_CALL_UP(LRefreshAll(flayer, 0));
ExitOverlayPage();
}
static void ListFree(void *d)
{
struct ListData *ldata = d;
glist_remove_rows(ldata);
if (ldata->list_fn->gl_free)
ldata->list_fn->gl_free(ldata);
if (ldata->search)
Free(ldata->search);
}
static void ListRedisplayLine(int y, int xs, int xe, int isblank)
{
struct ListData *ldata;
ASSERT(flayer);
ldata = flayer->l_data;
if (y < 0)
{
glist_display_all(ldata);
return;
}
if (!isblank)
LClearArea(flayer, xs, y, xe, y, 0, 0);
if (ldata->top && y < ldata->top->y)
ldata->list_fn->gl_printheader(ldata);
else if (y + 1 == flayer->l_height)
ldata->list_fn->gl_printfooter(ldata);
else
{
struct ListRow *row;
for (row = ldata->top; row && row->y != -1; row = row->next)
if (row->y == y)
{
ldata->list_fn->gl_printrow(ldata, row);
break;
}
}
}
static void ListClearLine(int y, int xs, int xe, int bce)
{
DefClearLine(y, xs, xe, bce);
}
static int ListRewrite(int y, int xs, int xe, struct mchar *rend, int doit)
{
return EXPENSIVE;
}
static int ListResize (int wi, int he)
{
if (wi < 10 || he < 5)
return -1;
flayer->l_width = wi;
flayer->l_height = he;
flayer->l_y = he - 1;
return 0;
}
static void ListRestore (void)
{
DefRestore();
}
struct ListRow *
glist_add_row(struct ListData *ldata, void *data, struct ListRow *after)
{
struct ListRow *r = calloc(1, sizeof(struct ListRow));
r->data = data;
if (after)
{
r->next = after->next;
r->prev = after;
after->next = r;
if (r->next)
r->next->prev = r;
}
else
{
r->next = ldata->root;
if (ldata->root)
ldata->root->prev = r;
ldata->root = r;
}
return r;
}
void
glist_remove_rows(struct ListData *ldata)
{
struct ListRow *row;
for (row = ldata->root; row; )
{
struct ListRow *r = row;
row = row->next;
ldata->list_fn->gl_freerow(ldata, r);
free(r);
}
ldata->root = ldata->selected = ldata->top = NULL;
}
void
glist_display_all(struct ListData *list)
{
int y;
struct ListRow *row;
LClearAll(flayer, 0);
y = list->list_fn->gl_printheader(list);
if (!list->top)
list->top = list->root;
if (!list->selected)
list->selected = list->root;
for (row = list->root; row != list->top; row = row->next)
row->y = -1;
for (row = list->top; row; row = row->next)
{
row->y = y++;
if (!list->list_fn->gl_printrow(list, row))
{
row->y = -1;
y--;
}
if (y + 1 == flayer->l_height)
break;
}
for (; row; row = row->next)
row->y = -1;
list->list_fn->gl_printfooter(list);
if (list->selected && list->selected->y != -1)
flayer->l_y = list->selected->y;
else
flayer->l_y = flayer->l_height - 1;
LaySetCursor();
}
void glist_abort(void)
{
ListAbort();
}
screen-4.1.0/comm.c 0000644 0001750 0001750 00000022324 11642704565 012147 0 ustar abe abe /* 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
* http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
****************************************************************
*/
#include "config.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
{ "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 },
{ "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 },
#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 },
{ "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
#ifdef NETHACK
{ "nethack", ARGS_1 },
#endif
{ "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 },
{ "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_0 },
{ "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 }
};
screen-4.1.0/mark.c 0000644 0001750 0001750 00000104302 11720315605 012131 0 ustar abe abe /* 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
* http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
****************************************************************
*/
#include
#include
#include "config.h"
#include "screen.h"
#include "mark.h"
#include "extern.h"
#ifdef COPY_PASTE
/*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* WARNING: these routines use the global variables "fore" and
* "flayer" to make things easier.
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
static int is_letter __P((int));
static void nextword __P((int *, int *, int, int));
static int linestart __P((int));
static int lineend __P((int));
static int rem __P((int, int , int , int , int , char *, int));
static int eq __P((int, int ));
static int MarkScrollDownDisplay __P((int));
static int MarkScrollUpDisplay __P((int));
static void MarkProcess __P((char **, int *));
static void MarkAbort __P((void));
static void MarkRedisplayLine __P((int, int, int, int));
static int MarkRewrite __P((int, int, int, struct mchar *, int));
extern struct layer *flayer;
extern struct display *display, *displays;
extern struct win *fore;
extern struct mline mline_blank, mline_null;
extern struct mchar mchar_so;
#ifdef FONT
int pastefont = 1;
#endif
struct LayFuncs MarkLf =
{
MarkProcess,
MarkAbort,
MarkRedisplayLine,
DefClearLine,
MarkRewrite,
DefResize,
DefRestore,
0
};
int join_with_cr = 0;
int compacthist = 0;
unsigned char mark_key_tab[256]; /* this array must be initialised first! */
static struct markdata *markdata;
/*
* VI like is_letter: 0 - whitespace
* 1 - letter
* 2 - other
*/
static int is_letter(c)
char c;
{
if ((c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
c == '_' || c == '.' ||
c == '@' || c == ':' ||
c == '%' || c == '!' ||
c == '-' || c == '+')
/* thus we can catch email-addresses as a word :-) */
return 1;
else if (c != ' ')
return 2;
return 0;
}
static int
linestart(y)
int y;
{
register int x;
register unsigned char *i;
for (x = markdata->left_mar, i = WIN(y)->image + x; x < fore->w_width - 1; x++)
if (*i++ != ' ')
break;
if (x == fore->w_width - 1)
x = markdata->left_mar;
return x;
}
static int
lineend(y)
int y;
{
register int x;
register unsigned char *i;
for (x = markdata->right_mar, i = WIN(y)->image + x; x >= 0; x--)
if (*i-- != ' ')
break;
if (x < 0)
x = markdata->left_mar;
return x;
}
/*
* nextchar sets *xp to the num-th occurrence of the target in the line.
*
* Returns -1 if the target doesn't appear num times, 0 otherwise.
*/
static int
nextchar(int *xp, int *yp, int direction, char target, int num)
{
int width; /* width of the current window. */
int x; /* x coordinate of the current cursor position. */
int step; /* amount to increment x (+1 or -1) */
int adjust; /* Final adjustment of cursor position. */
char *displayed_line; /* Line in which search takes place. */
debug("nextchar\n");
x = *xp;
step = 1;
adjust = 0;
width = fore->w_width;
displayed_line = (char *)WIN(*yp) -> image;
switch(direction) {
case 't':
adjust = -1; /* fall through */
case 'f':
step = 1;
break;
case 'T':
adjust = 1; /* fall through */
case 'F':
step = -1;
break;
default:
ASSERT(0);
}
x += step;
debug1("ml->image = %s\n", displayed_line);
debug2("num = %d, width = %d\n",num, width);
debug2("x = %d target = %c\n", x, target );
for ( ;x>=0 && x <= width; x += step) {
if (displayed_line[x] == target) {
if (--num == 0) {
*xp = x + adjust;
return 0;
}
}
}
return -1;
}
/*
* nextword calculates the cursor position of the num'th word.
* If the cursor is on a word, it counts as the first.
* NW_BACK: search backward
* NW_ENDOFWORD: find the end of the word
* NW_MUSTMOVE: move at least one char
* NW_BIG: match WORDs not words
*/
#define NW_BACK (1<<0)
#define NW_ENDOFWORD (1<<1)
#define NW_MUSTMOVE (1<<2)
#define NW_BIG (1<<3)
static void
nextword(xp, yp, flags, num)
int *xp, *yp, flags, num;
{
int xx = fore->w_width, yy = fore->w_histheight + fore->w_height;
register int sx, oq, q, x, y;
struct mline *ml;
x = *xp;
y = *yp;
sx = (flags & NW_BACK) ? -1 : 1;
if ((flags & NW_ENDOFWORD) && (flags & NW_MUSTMOVE))
x += sx;
ml = WIN(y);
for (oq = -1; ; x += sx, oq = q)
{
if (x >= xx || x < 0)
q = 0;
else if (flags & NW_BIG)
q = ml->image[x] == ' ';
else
q = is_letter(ml->image[x]);
if (oq >= 0 && oq != q)
{
if (oq == 0 || !(flags & NW_ENDOFWORD))
*xp = x;
else
*xp = x-sx;
*yp = y;
if ((!(flags & NW_ENDOFWORD) && q) ||
((flags & NW_ENDOFWORD) && oq))
{
if (--num <= 0)
return;
}
}
if (x == xx)
{
x = -1;
if (++y >= yy)
return;
ml = WIN(y);
}
else if (x < 0)
{
x = xx;
if (--y < 0)
return;
ml = WIN(y);
}
}
}
/*
* y1, y2 are WIN coordinates
*
* redisplay: 0 - just copy
* 1 - redisplay + copy
* 2 - count + copy, don't redisplay
*/
static int
rem(x1, y1, x2, y2, redisplay, pt, yend)
int x1, y1, x2, y2, redisplay, yend;
char *pt;
{
int i, j, from, to, ry, c;
int l = 0;
unsigned char *im;
struct mline *ml;
#ifdef FONT
int cf, font;
unsigned char *fo;
#endif
markdata->second = 0;
if (y2 < y1 || ((y2 == y1) && (x2 < x1)))
{
i = y2;
y2 = y1;
y1 = i;
i = x2;
x2 = x1;
x1 = i;
}
ry = y1 - markdata->hist_offset;
i = y1;
if (redisplay != 2 && pt == 0 && ry <0)
{
i -= ry;
ry = 0;
}
for (; i <= y2; i++, ry++)
{
if (redisplay != 2 && pt == 0 && ry > yend)
break;
ml = WIN(i);
from = (i == y1) ? x1 : 0;
if (from < markdata->left_mar)
from = markdata->left_mar;
for (to = fore->w_width, im = ml->image + to; to >= 0; to--)
if (*im-- != ' ')
break;
if (i == y2 && x2 < to)
to = x2;
if (to > markdata->right_mar)
to = markdata->right_mar;
if (redisplay == 1 && from <= to && ry >=0 && ry <= yend)
MarkRedisplayLine(ry, from, to, 0);
if (redisplay != 2 && pt == 0) /* don't count/copy */
continue;
j = from;
#ifdef DW_CHARS
if (dw_right(ml, j, fore->w_encoding))
j--;
#endif
im = ml->image + j;
#ifdef FONT
fo = ml->font + j;
font = ASCII;
#endif
for (; j <= to; j++)
{
c = (unsigned char)*im++;
#ifdef FONT
cf = (unsigned char)*fo++;
# ifdef UTF8
if (fore->w_encoding == UTF8)
{
c |= cf << 8;
if (c == UCS_HIDDEN)
continue;
c = ToUtf8_comb(pt, c);
l += c;
if (pt)
pt += c;
continue;
}
# endif
# ifdef DW_CHARS
if (is_dw_font(cf))
{
c = c << 8 | (unsigned char)*im++;
fo++;
j++;
}
# endif
if (pastefont)
{
c = EncodeChar(pt, c | cf << 16, fore->w_encoding, &font);
l += c;
if (pt)
pt += c;
continue;
}
#endif /* FONT */
if (pt)
*pt++ = c;
l++;
}
#ifdef FONT
if (pastefont && font != ASCII)
{
if (pt)
{
strcpy(pt, "\033(B");
pt += 3;
}
l += 3;
}
#endif
if (i != y2 && (to != fore->w_width - 1 || ml->image[to + 1] == ' '))
{
/*
* this code defines, what glues lines together
*/
switch (markdata->nonl)
{
case 0: /* lines separated by newlines */
if (pt)
*pt++ = '\r';
l++;
if (join_with_cr)
{
if (pt)
*pt++ = '\n';
l++;
}
break;
case 1: /* nothing to separate lines */
break;
case 2: /* lines separated by blanks */
if (pt)
*pt++ = ' ';
l++;
break;
case 3: /* seperate by comma, for csh junkies */
if (pt)
*pt++ = ',';
l++;
break;
}
}
}
return l;
}
/* Check if two chars are identical. All digits are treated
* as same. Used for GetHistory()
*/
static int
eq(a, b)
int a, b;
{
if (a == b)
return 1;
if (a == 0 || b == 0)
return 1;
if (a <= '9' && a >= '0' && b <= '9' && b >= '0')
return 1;
return 0;
}
/**********************************************************************/
int
GetHistory() /* return value 1 if copybuffer changed */
{
int i = 0, q = 0, xx, yy, x, y;
unsigned char *linep;
struct mline *ml;
ASSERT(display && fore);
x = fore->w_x;
if (x >= fore->w_width)
x = fore->w_width - 1;
y = fore->w_y + fore->w_histheight;
debug2("cursor is at x=%d, y=%d\n", x, y);
ml = WIN(y);
for (xx = x - 1, linep = ml->image + xx; xx >= 0; xx--)
if ((q = *linep--) != ' ' )
break;
debug3("%c at (%d,%d)\n", q, xx, y);
for (yy = y - 1; yy >= 0; yy--)
{
ml = WIN(yy);
linep = ml->image;
if (xx < 0 || eq(linep[xx], q))
{ /* line is matching... */
for (i = fore->w_width - 1, linep += i; i >= x; i--)
if (*linep-- != ' ')
break;
if (i >= x)
break;
}
}
if (yy < 0)
return 0;
if (D_user->u_plop.buf)
UserFreeCopyBuffer(D_user);
if ((D_user->u_plop.buf = (char *)malloc((unsigned) (i - x + 2))) == NULL)
{
LMsg(0, "Not enough memory... Sorry.");
return 0;
}
bcopy((char *)linep - i + x + 1, D_user->u_plop.buf, i - x + 1);
D_user->u_plop.len = i - x + 1;
#ifdef ENCODINGS
D_user->u_plop.enc = fore->w_encoding;
#endif
return 1;
}
/**********************************************************************/
void
MarkRoutine()
{
int x, y;
ASSERT(fore && display && D_user);
debug2("MarkRoutine called: fore nr %d, display %s\n",
fore->w_number, D_usertty);
if (InitOverlayPage(sizeof(*markdata), &MarkLf, 1))
return;
flayer->l_encoding = fore->w_encoding;
flayer->l_mode = 1;
markdata = (struct markdata *)flayer->l_data;
markdata->md_user = D_user; /* XXX: Correct? */
markdata->md_window = fore;
markdata->second = 0;
markdata->rep_cnt = 0;
markdata->append_mode = 0;
markdata->write_buffer = 0;
markdata->nonl = 0;
markdata->left_mar = 0;
markdata->right_mar = fore->w_width - 1;
markdata->hist_offset = fore->w_histheight;
x = fore->w_x;
y = D2W(fore->w_y);
if (x >= fore->w_width)
x = fore->w_width - 1;
LGotoPos(flayer, x, W2D(y));
LMsg(0, "Copy mode - Column %d Line %d(+%d) (%d,%d)",
x + 1, W2D(y + 1), fore->w_histheight, fore->w_width, fore->w_height);
markdata->cx = markdata->x1 = x;
markdata->cy = markdata->y1 = y;
flayer->l_x = x;
flayer->l_y = W2D(y);
}
static void
MarkProcess(inbufp,inlenp)
char **inbufp;
int *inlenp;
{
char *inbuf, *pt;
int inlen;
int cx, cy, x2, y2, j, yend;
int newcopylen = 0, od;
int in_mark;
int rep_cnt;
struct acluser *md_user;
/*
char *extrap = 0, extrabuf[100];
*/
markdata = (struct markdata *)flayer->l_data;
fore = markdata->md_window;
md_user = markdata->md_user;
if (inbufp == 0)
{
MarkAbort();
return;
}
LGotoPos(flayer, markdata->cx, W2D(markdata->cy));
inbuf= *inbufp;
inlen= *inlenp;
pt = inbuf;
in_mark = 1;
while (in_mark && (inlen /* || extrap */))
{
unsigned char ch = (unsigned char )*pt++;
inlen--;
if (flayer->l_mouseevent.start)
{
int r = LayProcessMouse(flayer, ch);
if (r == -1)
LayProcessMouseSwitch(flayer, 0);
else
{
if (r)
ch = 0222;
else
continue;
}
}
od = mark_key_tab[(int)ch];
rep_cnt = markdata->rep_cnt;
if (od >= '0' && od <= '9' && !markdata->f_cmd.flag)
{
if (rep_cnt < 1001 && (od != '0' || rep_cnt != 0))
{
markdata->rep_cnt = 10 * rep_cnt + od - '0';
continue;
/*
* Now what is that 1001 here? Well, we have a screen with
* 25 * 80 = 2000 characters. Movement is at most across the full
* screen. This we do with word by word movement, as character by
* character movement never steps over line boundaries. The most words
* we can place on the screen are 1000 single letter words. Thus 1001
* is sufficient. Users with bigger screens never write in single letter
* words, as they should be more advanced. jw.
* Oh, wrong. We still give even the experienced user a factor of ten.
*/
}
}
cx = markdata->cx;
cy = markdata->cy;
if (markdata -> f_cmd.flag) {
debug2("searching for %c:%d\n",od,rep_cnt);
markdata->f_cmd.flag = 0;
markdata->rep_cnt = 0;
if (isgraph (od)) {
markdata->f_cmd.target = od;
rep_cnt = (rep_cnt) ? rep_cnt : 1;
nextchar(&cx, &cy, markdata->f_cmd.direction, od, rep_cnt );
revto(cx, cy);
continue;
}
}
processchar:
switch (od)
{
case 'f': /* fall through */
case 'F': /* fall through */
case 't': /* fall through */
case 'T': /* fall through */
/*
* Set f_cmd to do a search on the next key stroke.
* If we break, rep_cnt will be reset, so we
* continue instead. It might be cleaner to
* store the rep_count in f_cmd and
* break here so later followon code will be
* hit.
*/
markdata->f_cmd.flag = 1;
markdata->f_cmd.direction = od;
debug("entering char search\n");
continue;
case ';':
case ',':
if (!markdata->f_cmd.target)
break;
if (!rep_cnt)
rep_cnt = 1;
nextchar(&cx, &cy,
od == ';' ? markdata->f_cmd.direction : (markdata->f_cmd.direction ^ 0x20),
markdata->f_cmd.target, rep_cnt );
revto(cx, cy);
break;
case 'o':
case 'x':
if (!markdata->second)
break;
markdata->cx = markdata->x1;
markdata->cy = markdata->y1;
markdata->x1 = cx;
markdata->y1 = cy;
revto(markdata->cx, markdata->cy);
break;
case '\014': /* CTRL-L Redisplay */
Redisplay(0);
LGotoPos(flayer, cx, W2D(cy));
break;
case 0202: /* M-C-b */
case '\010': /* CTRL-H Backspace */
case 'h':
if (rep_cnt == 0)
rep_cnt = 1;
revto(cx - rep_cnt, cy);
break;
case 0216: /* M-C-p */
case '\016': /* CTRL-N */
case 'j':
if (rep_cnt == 0)
rep_cnt = 1;
revto(cx, cy + rep_cnt);
break;
case '+':
if (rep_cnt == 0)
rep_cnt = 1;
j = cy + rep_cnt;
if (j > fore->w_histheight + fore->w_height - 1)
j = fore->w_histheight + fore->w_height - 1;
revto(linestart(j), j);
break;
case '-':
if (rep_cnt == 0)
rep_cnt = 1;
cy -= rep_cnt;
if (cy < 0)
cy = 0;
revto(linestart(cy), cy);
break;
case '^':
revto(linestart(cy), cy);
break;
case '\n':
revto(markdata->left_mar, cy + 1);
break;
case 0220: /* M-C-p */
case '\020': /* CTRL-P */
case 'k':
if (rep_cnt == 0)
rep_cnt = 1;
revto(cx, cy - rep_cnt);
break;
case 0206: /* M-C-f */
case 'l':
if (rep_cnt == 0)
rep_cnt = 1;
revto(cx + rep_cnt, cy);
break;
case '\001': /* CTRL-A from tcsh/emacs */
case '0':
revto(markdata->left_mar, cy);
break;
case '\004': /* CTRL-D down half screen */
if (rep_cnt == 0)
rep_cnt = (fore->w_height + 1) >> 1;
revto_line(cx, cy + rep_cnt, W2D(cy));
break;
case '$':
revto(lineend(cy), cy);
break;
case '\022': /* CTRL-R emacs style backwards search */
ISearch(-1);
in_mark = 0;
break;
case '\023': /* CTRL-S emacs style search */
ISearch(1);
in_mark = 0;
break;
case '\025': /* CTRL-U up half screen */
if (rep_cnt == 0)
rep_cnt = (fore->w_height + 1) >> 1;
revto_line(cx, cy - rep_cnt, W2D(cy));
break;
case '\007': /* CTRL-G show cursorpos */
if (markdata->left_mar == 0 && markdata->right_mar == fore->w_width - 1)
LMsg(0, "Column %d Line %d(+%d)", cx+1, W2D(cy)+1,
markdata->hist_offset);
else
LMsg(0, "Column %d(%d..%d) Line %d(+%d)", cx+1,
markdata->left_mar+1, markdata->right_mar+1, W2D(cy)+1, markdata->hist_offset);
break;
case '\002': /* CTRL-B back one page */
if (rep_cnt == 0)
rep_cnt = 1;
rep_cnt *= fore->w_height;
revto(cx, cy - rep_cnt);
break;
case '\006': /* CTRL-F forward one page */
if (rep_cnt == 0)
rep_cnt = 1;
rep_cnt *= fore->w_height;
revto(cx, cy + rep_cnt);
break;
case '\005': /* CTRL-E scroll up */
if (rep_cnt == 0)
rep_cnt = 1;
rep_cnt = MarkScrollUpDisplay(rep_cnt);
if (cy < D2W(0))
revto(cx, D2W(0));
else
LGotoPos(flayer, cx, W2D(cy));
break;
case '\031': /* CTRL-Y scroll down */
if (rep_cnt == 0)
rep_cnt = 1;
rep_cnt = MarkScrollDownDisplay(rep_cnt);
if (cy > D2W(fore->w_height-1))
revto(cx, D2W(fore->w_height-1));
else
LGotoPos(flayer, cx, W2D(cy));
break;
case '@':
/* it may be useful to have a key that does nothing */
break;
case '%':
rep_cnt--;
/* rep_cnt is a percentage for the history buffer */
if (rep_cnt < 0)
rep_cnt = 0;
if (rep_cnt > 100)
rep_cnt = 100;
revto_line(markdata->left_mar, (rep_cnt * (fore->w_histheight + fore->w_height)) / 100, (fore->w_height - 1) / 2);
break;
case 0201:
case 'g':
rep_cnt = 1;
/* FALLTHROUGH */
case 0205:
case 'G':
/* rep_cnt is here the WIN line number */
if (rep_cnt == 0)
rep_cnt = fore->w_histheight + fore->w_height;
revto_line(markdata->left_mar, --rep_cnt, (fore->w_height - 1) / 2);
break;
case 'H':
revto(markdata->left_mar, D2W(0));
break;
case 'M':
revto(markdata->left_mar, D2W((fore->w_height - 1) / 2));
break;
case 'L':
revto(markdata->left_mar, D2W(fore->w_height - 1));
break;
case '|':
revto(--rep_cnt, cy);
break;
case 'w':
if (rep_cnt == 0)
rep_cnt = 1;
nextword(&cx, &cy, NW_MUSTMOVE, rep_cnt);
revto(cx, cy);
break;
case 'e':
case 'E':
if (rep_cnt == 0)
rep_cnt = 1;
nextword(&cx, &cy, NW_ENDOFWORD|NW_MUSTMOVE | (od == 'E' ? NW_BIG : 0), rep_cnt);
revto(cx, cy);
break;
case 'b':
case 'B':
if (rep_cnt == 0)
rep_cnt = 1;
nextword(&cx, &cy, NW_BACK|NW_ENDOFWORD|NW_MUSTMOVE | (od == 'B' ? NW_BIG : 0), rep_cnt);
revto(cx, cy);
break;
case 'a':
markdata->append_mode = 1 - markdata->append_mode;
debug1("append mode %d--\n", markdata->append_mode);
LMsg(0, (markdata->append_mode) ? ":set append" : ":set noappend");
break;
case 'v':
case 'V':
/* this sets start column to column 9 for VI :set nu users */
if (markdata->left_mar == 8)
rep_cnt = 1;
else
rep_cnt = 9;
/* FALLTHROUGH */
case 'c':
case 'C':
/* set start column (c) and end column (C) */
if (markdata->second)
{
rem(markdata->x1, markdata->y1, cx, cy, 1, (char *)0, fore->w_height-1); /* Hack */
markdata->second = 1; /* rem turns off second */
}
rep_cnt--;
if (rep_cnt < 0)
rep_cnt = cx;
if (od != 'C')
{
markdata->left_mar = rep_cnt;
if (markdata->left_mar > markdata->right_mar)
markdata->left_mar = markdata->right_mar;
}
else
{
markdata->right_mar = rep_cnt;
if (markdata->left_mar > markdata->right_mar)
markdata->right_mar = markdata->left_mar;
}
if (markdata->second)
{
markdata->cx = markdata->x1; markdata->cy = markdata->y1;
revto(cx, cy);
}
if (od == 'v' || od == 'V')
LMsg(0, (markdata->left_mar != 8) ? ":set nonu" : ":set nu");
break;
case 'J':
/* how do you join lines in VI ? */
markdata->nonl = (markdata->nonl + 1) % 4;
switch (markdata->nonl)
{
case 0:
if (join_with_cr)
LMsg(0, "Multiple lines (CR/LF)");
else
LMsg(0, "Multiple lines (LF)");
break;
case 1:
LMsg(0, "Lines joined");
break;
case 2:
LMsg(0, "Lines joined with blanks");
break;
case 3:
LMsg(0, "Lines joined with comma");
break;
}
break;
case '/':
Search(1);
in_mark = 0;
break;
case '?':
Search(-1);
in_mark = 0;
break;
case 'n':
Search(0);
break;
case 'N':
markdata->isdir = -markdata->isdir;
Search(0);
markdata->isdir = -markdata->isdir;
break;
case 'y':
case 'Y':
if (markdata->second == 0)
{
revto(linestart(cy), cy);
markdata->second++;
cx = markdata->x1 = markdata->cx;
cy = markdata->y1 = markdata->cy;
}
if (--rep_cnt > 0)
revto(cx, cy + rep_cnt);
revto(lineend(markdata->cy), markdata->cy);
if (od == 'y')
break;
/* FALLTHROUGH */
case 'W':
if (od == 'W')
{
if (rep_cnt == 0)
rep_cnt = 1;
if (!markdata->second)
{
nextword(&cx, &cy, NW_BACK|NW_ENDOFWORD, 1);
revto(cx, cy);
markdata->second++;
cx = markdata->x1 = markdata->cx;
cy = markdata->y1 = markdata->cy;
}
nextword(&cx, &cy, NW_ENDOFWORD, rep_cnt);
revto(cx, cy);
}
cx = markdata->cx;
cy = markdata->cy;
/* FALLTHROUGH */
case 'A':
if (od == 'A')
markdata->append_mode = 1;
/* FALLTHROUGH */
case '>':
if (od == '>')
markdata->write_buffer = 1;
/* FALLTHROUGH */
case ' ':
case '\r':
if (!markdata->second)
{
markdata->second++;
markdata->x1 = cx;
markdata->y1 = cy;
revto(cx, cy);
LMsg(0, "First mark set - Column %d Line %d", cx+1, W2D(cy)+1);
break;
}
else
{
int append_mode = markdata->append_mode;
int write_buffer = markdata->write_buffer;
x2 = cx;
y2 = cy;
newcopylen = rem(markdata->x1, markdata->y1, x2, y2, 2, (char *)0, 0); /* count */
if (md_user->u_plop.buf && !append_mode)
UserFreeCopyBuffer(md_user);
yend = fore->w_height - 1;
if (fore->w_histheight - markdata->hist_offset < fore->w_height)
{
markdata->second = 0;
yend -= MarkScrollUpDisplay(fore->w_histheight - markdata->hist_offset);
}
if (newcopylen > 0)
{
/* the +3 below is for : cr + lf + \0 */
if (md_user->u_plop.buf)
md_user->u_plop.buf = realloc(md_user->u_plop.buf,
(unsigned) (md_user->u_plop.len + newcopylen + 3));
else
{
md_user->u_plop.len = 0;
md_user->u_plop.buf = malloc((unsigned) (newcopylen + 3));
}
if (!md_user->u_plop.buf)
{
MarkAbort();
in_mark = 0;
LMsg(0, "Not enough memory... Sorry.");
md_user->u_plop.len = 0;
md_user->u_plop.buf = 0;
break;
}
if (append_mode)
{
switch (markdata->nonl)
{
/*
* this code defines, what glues lines together
*/
case 0:
if (join_with_cr)
{
md_user->u_plop.buf[md_user->u_plop.len] = '\r';
md_user->u_plop.len++;
}
md_user->u_plop.buf[md_user->u_plop.len] = '\n';
md_user->u_plop.len++;
break;
case 1:
break;
case 2:
md_user->u_plop.buf[md_user->u_plop.len] = ' ';
md_user->u_plop.len++;
break;
case 3:
md_user->u_plop.buf[md_user->u_plop.len] = ',';
md_user->u_plop.len++;
break;
}
}
md_user->u_plop.len += rem(markdata->x1, markdata->y1, x2, y2,
markdata->hist_offset == fore->w_histheight,
md_user->u_plop.buf + md_user->u_plop.len, yend);
#ifdef ENCODINGS
md_user->u_plop.enc = fore->w_encoding;
#endif
}
if (markdata->hist_offset != fore->w_histheight)
{
LAY_CALL_UP(LRefreshAll(flayer, 0));
}
ExitOverlayPage();
WindowChanged(fore, 'P');
if (append_mode)
LMsg(0, "Appended %d characters to buffer",
newcopylen);
else
LMsg(0, "Copied %d characters into buffer", md_user->u_plop.len);
if (write_buffer)
WriteFile(md_user, (char *)0, DUMP_EXCHANGE);
in_mark = 0;
break;
}
case 0222:
if (flayer->l_mouseevent.start)
{
int button = flayer->l_mouseevent.buffer[0];
if (button == 'a')
{
/* Scroll down */
od = 'j';
}
else if (button == '`')
{
/* Scroll up */
od = 'k';
}
else if (button == ' ')
{
/* Left click */
cx = flayer->l_mouseevent.buffer[1];
cy = D2W(flayer->l_mouseevent.buffer[2]);
revto(cx, cy);
od = ' ';
}
else
od = 0;
LayProcessMouseSwitch(flayer, 0);
if (od)
goto processchar;
}
else
LayProcessMouseSwitch(flayer, 1);
break;
default:
MarkAbort();
LMsg(0, "Copy mode aborted");
in_mark = 0;
break;
}
if (in_mark) /* markdata may be freed */
markdata->rep_cnt = 0;
}
if (in_mark)
{
flayer->l_x = markdata->cx;
flayer->l_y = W2D(markdata->cy);
}
*inbufp = pt;
*inlenp = inlen;
}
void revto(tx, ty)
int tx, ty;
{
revto_line(tx, ty, -1);
}
/* tx, ty: WINDOW, line: DISPLAY */
void revto_line(tx, ty, line)
int tx, ty, line;
{
int fx, fy;
int x, y, t, revst, reven, qq, ff, tt, st, en, ce = 0;
int ystart = 0, yend = fore->w_height-1;
int i, ry;
unsigned char *wi;
struct mline *ml;
struct mchar mc;
if (tx < 0)
tx = 0;
else if (tx > fore->w_width - 1)
tx = fore->w_width -1;
if (ty < 0)
ty = 0;
else if (ty > fore->w_histheight + fore->w_height - 1)
ty = fore->w_histheight + fore->w_height - 1;
fx = markdata->cx; fy = markdata->cy;
#ifdef DW_CHARS
/* don't just move inside of a kanji, the user wants to see something */
ml = WIN(ty);
if (ty == fy && fx + 1 == tx && dw_right(ml, tx, fore->w_encoding) && tx < D_width - 1)
tx++;
if (ty == fy && fx - 1 == tx && dw_right(ml, fx, fore->w_encoding) && tx)
tx--;
#endif
markdata->cx = tx; markdata->cy = ty;
/*
* if we go to a position that is currently offscreen
* then scroll the screen
*/
i = 0;
if (line >= 0 && line < fore->w_height)
i = W2D(ty) - line;
else if (ty < markdata->hist_offset)
i = ty - markdata->hist_offset;
else if (ty > markdata->hist_offset + (fore->w_height - 1))
i = ty - markdata->hist_offset - (fore->w_height - 1);
if (i > 0)
yend -= MarkScrollUpDisplay(i);
else if (i < 0)
ystart += MarkScrollDownDisplay(-i);
if (markdata->second == 0)
{
flayer->l_x = tx;
flayer->l_y = W2D(ty);
LGotoPos(flayer, tx, W2D(ty));
return;
}
qq = markdata->x1 + markdata->y1 * fore->w_width;
ff = fx + fy * fore->w_width; /* "from" offset in WIN coords */
tt = tx + ty * fore->w_width; /* "to" offset in WIN coords*/
if (ff > tt)
{
st = tt; en = ff;
x = tx; y = ty;
}
else
{
st = ff; en = tt;
x = fx; y = fy;
}
if (st > qq)
{
st++;
x++;
}
if (en < qq)
en--;
if (tt > qq)
{
revst = qq; reven = tt;
}
else
{
revst = tt; reven = qq;
}
ry = y - markdata->hist_offset;
if (ry < ystart)
{
y += (ystart - ry);
x = 0;
st = y * fore->w_width;
ry = ystart;
}
ml = WIN(y);
for (t = st; t <= en; t++, x++)
{
if (x >= fore->w_width)
{
x = 0;
y++, ry++;
ml = WIN(y);
}
if (ry > yend)
break;
if (t == st || x == 0)
{
wi = ml->image + fore->w_width;
for (ce = fore->w_width; ce >= 0; ce--, wi--)
if (*wi != ' ')
break;
}
if (x <= ce && x >= markdata->left_mar && x <= markdata->right_mar)
{
#ifdef DW_CHARS
if (dw_right(ml, x, fore->w_encoding))
{
if (t == revst)
revst--;
t--;
x--;
}
#endif
if (t >= revst && t <= reven)
{
mc = mchar_so;
#ifdef FONT
if (pastefont)
mc.font = ml->font[x];
#endif
mc.image = ml->image[x];
}
else
copy_mline2mchar(&mc, ml, x);
#ifdef DW_CHARS
if (dw_left(ml, x, fore->w_encoding))
{
mc.mbcs = ml->image[x + 1];
LPutChar(flayer, &mc, x, W2D(y));
t++;
}
#endif
LPutChar(flayer, &mc, x, W2D(y));
#ifdef DW_CHARS
if (dw_left(ml, x, fore->w_encoding))
x++;
#endif
}
}
flayer->l_x = tx;
flayer->l_y = W2D(ty);
LGotoPos(flayer, tx, W2D(ty));
}
static void
MarkAbort()
{
int yend, redisp;
debug("MarkAbort\n");
markdata = (struct markdata *)flayer->l_data;
fore = markdata->md_window;
yend = fore->w_height - 1;
redisp = markdata->second;
if (fore->w_histheight - markdata->hist_offset < fore->w_height)
{
markdata->second = 0;
yend -= MarkScrollUpDisplay(fore->w_histheight - markdata->hist_offset);
}
if (markdata->hist_offset != fore->w_histheight)
{
LAY_CALL_UP(LRefreshAll(flayer, 0));
}
else
{
rem(markdata->x1, markdata->y1, markdata->cx, markdata->cy, redisp, (char *)0, yend);
}
ExitOverlayPage();
WindowChanged(fore, 'P');
}
static void
MarkRedisplayLine(y, xs, xe, isblank)
int y; /* NOTE: y is in DISPLAY coords system! */
int xs, xe;
int isblank;
{
int wy, x, i, rm;
int sta, sto, cp; /* NOTE: these 3 are in WINDOW coords system */
unsigned char *wi;
struct mline *ml;
struct mchar mchar_marked;
if (y < 0) /* No special full page handling */
return;
markdata = (struct markdata *)flayer->l_data;
fore = markdata->md_window;
mchar_marked = mchar_so;
wy = D2W(y);
ml = WIN(wy);
if (markdata->second == 0)
{
if (dw_right(ml, xs, fore->w_encoding) && xs > 0)
xs--;
if (dw_left(ml, xe, fore->w_encoding) && xe < fore->w_width - 1)
xe++;
if (xs == 0 && y > 0 && wy > 0 && WIN(wy - 1)->image[flayer->l_width] == 0)
LCDisplayLineWrap(flayer, ml, y, xs, xe, isblank);
else
LCDisplayLine(flayer, ml, y, xs, xe, isblank);
return;
}
sta = markdata->y1 * fore->w_width + markdata->x1;
sto = markdata->cy * fore->w_width + markdata->cx;
if (sta > sto)
{
i=sta; sta=sto; sto=i;
}
cp = wy * fore->w_width + xs;
rm = markdata->right_mar;
for (x = fore->w_width, wi = ml->image + fore->w_width; x >= 0; x--, wi--)
if (*wi != ' ')
break;
if (x < rm)
rm = x;
for (x = xs; x <= xe; x++, cp++)
if (cp >= sta && x >= markdata->left_mar)
break;
#ifdef DW_CHARS
if (dw_right(ml, x, fore->w_encoding))
x--;
#endif
if (x > xs)
LCDisplayLine(flayer, ml, y, xs, x - 1, isblank);
for (; x <= xe; x++, cp++)
{
if (cp > sto || x > rm)
break;
#ifdef FONT
if (pastefont)
mchar_marked.font = ml->font[x];
#endif
mchar_marked.image = ml->image[x];
#ifdef DW_CHARS
mchar_marked.mbcs = 0;
if (dw_left(ml, x, fore->w_encoding))
{
mchar_marked.mbcs = ml->image[x + 1];
cp++;
}
#endif
LPutChar(flayer, &mchar_marked, x, y);
#ifdef DW_CHARS
if (dw_left(ml, x, fore->w_encoding))
x++;
#endif
}
if (x <= xe)
LCDisplayLine(flayer, ml, y, x, xe, isblank);
}
/*
* This ugly routine is to speed up GotoPos()
*/
static int
MarkRewrite(ry, xs, xe, rend, doit)
int ry, xs, xe, doit;
struct mchar *rend;
{
int dx, x, y, st, en, t, rm;
unsigned char *i;
struct mline *ml;
struct mchar mchar_marked;
mchar_marked = mchar_so;
debug3("MarkRewrite %d, %d-%d\n", ry, xs, xe);
markdata = (struct markdata *)flayer->l_data;
fore = markdata->md_window;
y = D2W(ry);
ml = WIN(y);
#ifdef UTF8
if (fore->w_encoding && fore->w_encoding != UTF8 && D_encoding == UTF8 && ContainsSpecialDeffont(ml, xs, xe, fore->w_encoding))
return EXPENSIVE;
#endif
dx = xe - xs + 1;
if (doit)
{
i = ml->image + xs;
while (dx--)
PUTCHAR(*i++);
return 0;
}
if (markdata->second == 0)
st = en = -1;
else
{
st = markdata->y1 * fore->w_width + markdata->x1;
en = markdata->cy * fore->w_width + markdata->cx;
if (st > en)
{
t = st; st = en; en = t;
}
}
t = y * fore->w_width + xs;
for (rm = fore->w_width, i = ml->image + fore->w_width; rm >= 0; rm--)
if (*i-- != ' ')
break;
if (rm > markdata->right_mar)
rm = markdata->right_mar;
x = xs;
while (dx--)
{
if (t >= st && t <= en && x >= markdata->left_mar && x <= rm)
{
#ifdef FONT
if (pastefont)
mchar_marked.font = ml->font[x];
#endif
rend->image = mchar_marked.image;
if (!cmp_mchar(rend, &mchar_marked))
return EXPENSIVE;
}
else
{
rend->image = ml->image[x];
if (!cmp_mchar_mline(rend, ml, x))
return EXPENSIVE;
}
x++;
}
return xe - xs + 1;
}
/*
* scroll the screen contents up/down.
*/
static int MarkScrollUpDisplay(n)
int n;
{
int i;
debug1("MarkScrollUpDisplay(%d)\n", n);
if (n <= 0)
return 0;
if (n > fore->w_histheight - markdata->hist_offset)
n = fore->w_histheight - markdata->hist_offset;
markdata->hist_offset += n;
i = (n < flayer->l_height) ? n : (flayer->l_height);
LScrollV(flayer, i, 0, flayer->l_height - 1, 0);
while (i-- > 0)
MarkRedisplayLine(flayer->l_height - i - 1, 0, flayer->l_width - 1, 1);
return n;
}
static int
MarkScrollDownDisplay(n)
int n;
{
int i;
debug1("MarkScrollDownDisplay(%d)\n", n);
if (n <= 0)
return 0;
if (n > markdata->hist_offset)
n = markdata->hist_offset;
markdata->hist_offset -= n;
i = (n < flayer->l_height) ? n : (flayer->l_height);
LScrollV(flayer, -i, 0, fore->w_height - 1, 0);
while (i-- > 0)
MarkRedisplayLine(i, 0, flayer->l_width - 1, 1);
return n;
}
void
MakePaster(pa, buf, len, bufiscopy)
struct paster *pa;
char *buf;
int len;
int bufiscopy;
{
FreePaster(pa);
pa->pa_pasteptr = buf;
pa->pa_pastelen = len;
if (bufiscopy)
pa->pa_pastebuf = buf;
pa->pa_pastelayer = flayer;
DoProcess(Layer2Window(flayer), &pa->pa_pasteptr, &pa->pa_pastelen, pa);
}
void
FreePaster(pa)
struct paster *pa;
{
if (pa->pa_pastebuf)
free(pa->pa_pastebuf);
pa->pa_pastebuf = 0;
pa->pa_pasteptr = 0;
pa->pa_pastelen = 0;
pa->pa_pastelayer = 0;
evdeq(&pa->pa_slowev);
}
#endif /* COPY_PASTE */
screen-4.1.0/resize.c 0000644 0001750 0001750 00000061251 11732171225 012506 0 ustar abe abe /* 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
* http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
****************************************************************
*/
#include
#include
#ifndef sun
#include
#endif
#ifdef ISC
# include
# include
# include
#endif
#include "config.h"
#include "screen.h"
#include "extern.h"
static void CheckMaxSize __P((int));
static void FreeMline __P((struct mline *));
static int AllocMline __P((struct mline *ml, int));
static void MakeBlankLine __P((unsigned char *, int));
static void kaablamm __P((void));
static int BcopyMline __P((struct mline *, int, struct mline *, int, int, int));
static void SwapAltScreen __P((struct win *));
extern struct layer *flayer;
extern struct display *display, *displays;
extern unsigned char *blank, *null;
extern struct mline mline_blank, mline_null, mline_old;
extern struct win *windows;
extern int Z0width, Z1width;
extern int captionalways;
#if defined(TIOCGWINSZ) || defined(TIOCSWINSZ)
struct winsize glwz;
#endif
static struct mline mline_zero = {
(unsigned char *)0,
(unsigned char *)0
#ifdef FONT
,(unsigned char *)0
#endif
#ifdef COLOR
,(unsigned char *)0
# ifdef COLORS256
,(unsigned char *)0
# endif
#endif
};
/*
* ChangeFlag: 0: try to modify no window
* 1: modify fore (and try to modify no other) + redisplay
* 2: modify all windows
*
* Note: Activate() is only called if change_flag == 1
* i.e. on a WINCH event
*/
void
CheckScreenSize(change_flag)
int change_flag;
{
int wi, he;
if (display == 0)
{
debug("CheckScreenSize: No display -> no check.\n");
return;
}
#ifdef TIOCGWINSZ
if (ioctl(D_userfd, TIOCGWINSZ, (char *)&glwz) != 0)
{
debug2("CheckScreenSize: ioctl(%d, TIOCGWINSZ) errno %d\n", D_userfd, errno);
wi = D_CO;
he = D_LI;
}
else
{
wi = glwz.ws_col;
he = glwz.ws_row;
if (wi == 0)
wi = D_CO;
if (he == 0)
he = D_LI;
}
#else
wi = D_CO;
he = D_LI;
#endif
debug2("CheckScreenSize: screen is (%d,%d)\n", wi, he);
#if 0 /* XXX: Fixme */
if (change_flag == 2)
{
debug("Trying to adapt all windows (-A)\n");
for (p = windows; p; p = p->w_next)
if (p->w_display == 0 || p->w_display == display)
ChangeWindowSize(p, wi, he, p->w_histheight);
}
#endif
if (D_width == wi && D_height == he)
{
debug("CheckScreenSize: No change -> return.\n");
return;
}
#ifdef BLANKER_PRG
KillBlanker();
#endif
ResetIdle();
ChangeScreenSize(wi, he, change_flag);
/* XXX Redisplay logic */
#if 0
if (change_flag == 1)
Redisplay(D_fore ? D_fore->w_norefresh : 0);
#endif
}
void
ChangeScreenSize(wi, he, change_fore)
int wi, he;
int change_fore;
{
struct win *p;
struct canvas *cv;
int wwi;
debug2("ChangeScreenSize from (%d,%d) ", D_width, D_height);
debug3("to (%d,%d) (change_fore: %d)\n",wi, he, change_fore);
cv = &D_canvas;
cv->c_xe = wi - 1;
cv->c_ye = he - 1 - ((cv->c_slperp && cv->c_slperp->c_slnext) || captionalways) - (D_has_hstatus == HSTATUS_LASTLINE);
cv->c_blank.l_height = cv->c_ye - cv->c_ys + 1;
if (cv->c_slperp)
{
ResizeCanvas(cv);
RecreateCanvasChain();
RethinkDisplayViewports();
}
if (D_forecv == 0)
D_forecv = D_cvlist;
if (D_forecv)
D_fore = Layer2Window(D_forecv->c_layer);
D_width = wi;
D_height = he;
CheckMaxSize(wi);
if (D_CWS)
{
D_defwidth = D_CO;
D_defheight = D_LI;
}
else
{
if (D_CZ0 && (wi == Z0width || wi == Z1width) &&
(D_CO == Z0width || D_CO == Z1width))
D_defwidth = D_CO;
else
D_defwidth = wi;
D_defheight = he;
}
debug2("Default size: (%d,%d)\n", D_defwidth, D_defheight);
if (change_fore)
ResizeLayersToCanvases();
if (change_fore == 2 && D_CWS == NULL && displays->d_next == 0)
{
/* adapt all windows - to be removed ? */
for (p = windows; p; p = p->w_next)
{
debug1("Trying to change window %d.\n", p->w_number);
wwi = wi;
#if 0
if (D_CZ0 && p->w_width != wi && (wi == Z0width || wi == Z1width))
{
if (p->w_width > (Z0width + Z1width) / 2)
wwi = Z0width;
else
wwi = Z1width;
}
#endif
if (p->w_savelayer && p->w_savelayer->l_cvlist == 0)
ResizeLayer(p->w_savelayer, wwi, he, 0);
#if 0
ChangeWindowSize(p, wwi, he, p->w_histheight);
#endif
}
}
}
void
ResizeLayersToCanvases()
{
struct canvas *cv;
struct layer *l;
int lx, ly;
debug("ResizeLayersToCanvases\n");
D_kaablamm = 0;
for (cv = D_cvlist; cv; cv = cv->c_next)
{
l = cv->c_layer;
if (l == 0)
continue;
debug("Doing canvas: ");
if (l->l_width == cv->c_xe - cv->c_xs + 1 &&
l->l_height == cv->c_ye - cv->c_ys + 1)
{
debug("already fitting.\n");
continue;
}
if (!MayResizeLayer(l))
{
debug("may not resize.\n");
}
else
{
debug("doing resize.\n");
ResizeLayer(l, cv->c_xe - cv->c_xs + 1, cv->c_ye - cv->c_ys + 1, display);
}
/* normalize window, see screen.c */
lx = cv->c_layer->l_x;
ly = cv->c_layer->l_y;
if (ly + cv->c_yoff < cv->c_ys)
{
cv->c_yoff = cv->c_ys - ly;
RethinkViewportOffsets(cv);
}
else if (ly + cv->c_yoff > cv->c_ye)
{
cv->c_yoff = cv->c_ye - ly;
RethinkViewportOffsets(cv);
}
if (lx + cv->c_xoff < cv->c_xs)
{
int n = cv->c_xs - (lx + cv->c_xoff);
if (n < (cv->c_xe - cv->c_xs + 1) / 2)
n = (cv->c_xe - cv->c_xs + 1) / 2;
if (cv->c_xoff + n > cv->c_xs)
n = cv->c_xs - cv->c_xoff;
cv->c_xoff += n;
RethinkViewportOffsets(cv);
}
else if (lx + cv->c_xoff > cv->c_xe)
{
int n = lx + cv->c_xoff - cv->c_xe;
if (n < (cv->c_xe - cv->c_xs + 1) / 2)
n = (cv->c_xe - cv->c_xs + 1) / 2;
if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
cv->c_xoff -= n;
RethinkViewportOffsets(cv);
}
}
Redisplay(0);
if (D_kaablamm)
{
kaablamm();
D_kaablamm = 0;
}
}
int
MayResizeLayer(l)
struct layer *l;
{
int cvs = 0;
debug("MayResizeLayer:\n");
for (; l; l = l->l_next)
{
if (l->l_cvlist)
if (++cvs > 1 || l->l_cvlist->c_lnext)
{
debug1("may not - cvs %d\n", cvs);
return 0;
}
}
debug("may resize\n");
return 1;
}
/*
* Easy implementation: rely on the fact that the only layers
* supporting resize are Win and Blank. So just kill all overlays.
*
* This is a lot harder if done the right way...
*/
static void
kaablamm()
{
Msg(0, "Aborted because of window size change.");
}
/* Kills non-resizable layers. */
#define RESIZE_OR_KILL_LAYERS(l, wi, he) do \
{ \
struct layer *_last = NULL; \
flayer = (l); \
while (flayer->l_next) \
{ \
if (LayResize(wi, he) == 0) \
{ \
_last = flayer; \
flayer = flayer->l_next; \
} \
else \
{ \
struct canvas *_cv; \
for (_cv = flayer->l_cvlist; _cv; _cv = _cv->c_lnext) \
_cv->c_display->d_kaablamm = 1; \
ExitOverlayPage(); \
if (_last) \
_last->l_next = flayer; \
} \
} \
/* We assume that the bottom-most layer, i.e. when flayer->l_next == 0, \
* is always resizable. Currently, WinLf and BlankLf can be the bottom-most layers. \
*/ \
LayResize(wi, he); \
} while (0)
void
ResizeLayer(l, wi, he, norefdisp)
struct layer *l;
int wi, he;
struct display *norefdisp;
{
struct win *p;
struct canvas *cv;
struct layer *oldflayer = flayer;
struct display *d, *olddisplay = display;
if (l->l_width == wi && l->l_height == he)
return;
p = Layer2Window(l);
/* If 'flayer' and 'l' are for the same window, then we will not
* restore 'flayer'. */
if (oldflayer && (l == oldflayer || Layer2Window(oldflayer) == p))
oldflayer = NULL;
flayer = l;
if (p)
{
/* It's a window layer. Kill the overlays on it in all displays. */
for (d = displays; d; d = d->d_next)
for (cv = d->d_cvlist; cv; cv = cv->c_next)
{
if (p == Layer2Window(cv->c_layer))
{
/* Canvas 'cv' on display 'd' shows this window. Remove any non-resizable
* layers over it. */
RESIZE_OR_KILL_LAYERS(cv->c_layer, wi, he);
}
}
}
else
{
/* It's a Blank layer. Just kill the non-resizable overlays over it. */
RESIZE_OR_KILL_LAYERS(flayer, wi, he);
}
for (display = displays; display; display = display->d_next)
{
if (display == norefdisp)
continue;
for (cv = D_cvlist; cv; cv = cv->c_next)
if (Layer2Window(cv->c_layer) == p)
{
CV_CALL(cv, LayRedisplayLine(-1, -1, -1, 0));
RefreshArea(cv->c_xs, cv->c_ys, cv->c_xe, cv->c_ye, 0);
}
if (D_kaablamm)
{
kaablamm();
D_kaablamm = 0;
}
}
/* If we started resizing a non-flayer layer, then restore the flayer.
* Otherwise, flayer should already be updated to the topmost foreground layer. */
if (oldflayer)
flayer = oldflayer;
display = olddisplay;
}
static void
FreeMline(ml)
struct mline *ml;
{
if (ml->image)
free(ml->image);
if (ml->attr && ml->attr != null)
free(ml->attr);
#ifdef FONT
if (ml->font && ml->font != null)
free(ml->font);
#endif
#ifdef COLOR
if (ml->color && ml->color != null)
free(ml->color);
# ifdef COLORS256
if (ml->colorx && ml->colorx != null)
free(ml->colorx);
# endif
#endif
*ml = mline_zero;
}
static int
AllocMline(ml, w)
struct mline *ml;
int w;
{
ml->image = malloc(w);
ml->attr = null;
#ifdef FONT
ml->font = null;
#endif
#ifdef COLOR
ml->color = null;
# ifdef COLORS256
ml->colorx = null;
# endif
#endif
if (ml->image == 0)
return -1;
return 0;
}
static int
BcopyMline(mlf, xf, mlt, xt, l, w)
struct mline *mlf, *mlt;
int xf, xt, l, w;
{
int r = 0;
bcopy((char *)mlf->image + xf, (char *)mlt->image + xt, l);
if (mlf->attr != null && mlt->attr == null)
{
if ((mlt->attr = (unsigned char *)calloc(w, 1)) == 0)
mlt->attr = null, r = -1;
}
if (mlt->attr != null)
bcopy((char *)mlf->attr + xf, (char *)mlt->attr + xt, l);
#ifdef FONT
if (mlf->font != null && mlt->font == null)
{
if ((mlt->font = (unsigned char *)calloc(w, 1)) == 0)
mlt->font = null, r = -1;
}
if (mlt->font != null)
bcopy((char *)mlf->font + xf, (char *)mlt->font + xt, l);
#endif
#ifdef COLOR
if (mlf->color != null && mlt->color == null)
{
if ((mlt->color = (unsigned char *)calloc(w, 1)) == 0)
mlt->color = null, r = -1;
}
if (mlt->color != null)
bcopy((char *)mlf->color + xf, (char *)mlt->color + xt, l);
# ifdef COLORS256
if (mlf->colorx != null && mlt->colorx == null)
{
if ((mlt->colorx = (unsigned char *)calloc(w, 1)) == 0)
mlt->colorx = null, r = -1;
}
if (mlt->colorx != null)
bcopy((char *)mlf->colorx + xf, (char *)mlt->colorx + xt, l);
# endif
#endif
return r;
}
static int maxwidth;
static void
CheckMaxSize(wi)
int wi;
{
unsigned char *oldnull = null;
unsigned char *oldblank = blank;
struct win *p;
int i;
struct mline *ml;
wi = ((wi + 1) + 255) & ~255;
if (wi <= maxwidth)
return;
maxwidth = wi;
debug1("New maxwidth: %d\n", maxwidth);
blank = (unsigned char *)xrealloc((char *)blank, maxwidth);
null = (unsigned char *)xrealloc((char *)null, maxwidth);
mline_old.image = (unsigned char *)xrealloc((char *)mline_old.image, maxwidth);
mline_old.attr = (unsigned char *)xrealloc((char *)mline_old.attr, maxwidth);
#ifdef FONT
mline_old.font = (unsigned char *)xrealloc((char *)mline_old.font, maxwidth);
#endif
#ifdef COLOR
mline_old.color = (unsigned char *)xrealloc((char *)mline_old.color, maxwidth);
# ifdef COLORS256
mline_old.colorx = (unsigned char *)xrealloc((char *)mline_old.colorx, maxwidth);
# endif
#endif
if (!(blank && null && mline_old.image && mline_old.attr IFFONT(&& mline_old.font) IFCOLOR(&& mline_old.color) IFCOLORX(&& mline_old.colorx)))
Panic(0, "%s", strnomem);
MakeBlankLine(blank, maxwidth);
bzero((char *)null, maxwidth);
mline_blank.image = blank;
mline_blank.attr = null;
mline_null.image = null;
mline_null.attr = null;
#ifdef FONT
mline_blank.font = null;
mline_null.font = null;
#endif
#ifdef COLOR
mline_blank.color = null;
mline_null.color = null;
# ifdef COLORS256
mline_blank.colorx = null;
mline_null.colorx = null;
# endif
#endif
#define RESET_AFC(x, bl) do { if (x == old##bl) x = bl; } while (0)
#define RESET_LINES(lines, count) \
do { \
ml = lines; \
for (i = 0; i < count; i++, ml++) \
{ \
RESET_AFC(ml->image, blank); \
RESET_AFC(ml->attr, null); \
IFFONT(RESET_AFC(ml->font, null)); \
IFCOLOR(RESET_AFC(ml->color, null)); \
IFCOLORX(RESET_AFC(ml->colorx, null)); \
} \
} while (0)
/* We have to run through all windows to substitute
* the null and blank references.
*/
for (p = windows; p; p = p->w_next)
{
RESET_LINES(p->w_mlines, p->w_height);
#ifdef COPY_PASTE
RESET_LINES(p->w_hlines, p->w_histheight);
RESET_LINES(p->w_alt.hlines, p->w_alt.histheight);
#endif
RESET_LINES(p->w_alt.mlines, p->w_alt.height);
}
}
char *
xrealloc(mem, len)
char *mem;
int len;
{
register char *nmem;
if (mem == 0)
return malloc(len);
if ((nmem = realloc(mem, len)))
return nmem;
free(mem);
return (char *)0;
}
static void
MakeBlankLine(p, n)
register unsigned char *p;
register int n;
{
while (n--)
*p++ = ' ';
}
#ifdef COPY_PASTE
#define OLDWIN(y) ((y < p->w_histheight) \
? &p->w_hlines[(p->w_histidx + y) % p->w_histheight] \
: &p->w_mlines[y - p->w_histheight])
#define NEWWIN(y) ((y < hi) ? &nhlines[y] : &nmlines[y - hi])
#else
#define OLDWIN(y) (&p->w_mlines[y])
#define NEWWIN(y) (&nmlines[y])
#endif
int
ChangeWindowSize(p, wi, he, hi)
struct win *p;
int wi, he, hi;
{
struct mline *mlf = 0, *mlt = 0, *ml, *nmlines, *nhlines;
int fy, ty, l, lx, lf, lt, yy, oty, addone;
int ncx, ncy, naka, t;
int y, shift;
if (wi <= 0 || he <= 0)
wi = he = hi = 0;
if (p->w_type == W_TYPE_GROUP)
return 0;
if (wi > 1000)
{
Msg(0, "Window width too large. Truncated to 1000.");
wi = 1000;
}
if (he > 1000)
{
Msg(0, "Window height too large. Truncated to 1000.");
he = 1000;
}
if (p->w_width == wi && p->w_height == he && p->w_histheight == hi)
{
debug("ChangeWindowSize: No change.\n");
return 0;
}
CheckMaxSize(wi);
/* XXX */
#if 0
/* just in case ... */
if (wi && (p->w_width != wi || p->w_height != he) && p->w_lay != &p->w_winlay)
{
debug("ChangeWindowSize: No resize because of overlay?\n");
return -1;
}
#endif
debug("ChangeWindowSize");
debug3(" from (%d,%d)+%d", p->w_width, p->w_height, p->w_histheight);
debug3(" to(%d,%d)+%d\n", wi, he, hi);
fy = p->w_histheight + p->w_height - 1;
ty = hi + he - 1;
nmlines = nhlines = 0;
ncx = 0;
ncy = 0;
naka = 0;
if (wi)
{
if (wi != p->w_width || he != p->w_height)
{
if ((nmlines = (struct mline *)calloc(he, sizeof(struct mline))) == 0)
{
KillWindow(p);
Msg(0, "%s", strnomem);
return -1;
}
}
else
{
debug1("image stays the same: %d lines\n", he);
nmlines = p->w_mlines;
fy -= he;
ty -= he;
ncx = p->w_x;
ncy = p->w_y;
naka = p->w_autoaka;
}
}
#ifdef COPY_PASTE
if (hi)
{
if ((nhlines = (struct mline *)calloc(hi, sizeof(struct mline))) == 0)
{
Msg(0, "No memory for history buffer - turned off");
hi = 0;
ty = he - 1;
}
}
#endif
/* special case: cursor is at magic margin position */
addone = 0;
if (p->w_width && p->w_x == p->w_width)
{
debug2("Special addone case: %d %d\n", p->w_x, p->w_y);
addone = 1;
p->w_x--;
}
/* handle the cursor and autoaka lines now if the widths are equal */
if (p->w_width == wi)
{
ncx = p->w_x + addone;
ncy = p->w_y + he - p->w_height;
/* never lose sight of the line with the cursor on it */
shift = -ncy;
for (yy = p->w_y + p->w_histheight - 1; yy >= 0 && ncy + shift < he; yy--)
{
ml = OLDWIN(yy);
if (!ml->image)
break;
if (ml->image[p->w_width] == ' ')
break;
shift++;
}
if (shift < 0)
shift = 0;
else
debug1("resize: cursor out of bounds, shifting %d\n", shift);
ncy += shift;
if (p->w_autoaka > 0)
{
naka = p->w_autoaka + he - p->w_height + shift;
if (naka < 1 || naka > he)
naka = 0;
}
while (shift-- > 0)
{
ml = OLDWIN(fy);
FreeMline(ml);
fy--;
}
}
debug2("fy %d ty %d\n", fy, ty);
if (fy >= 0)
mlf = OLDWIN(fy);
if (ty >= 0)
mlt = NEWWIN(ty);
while (fy >= 0 && ty >= 0)
{
if (p->w_width == wi)
{
/* here is a simple shortcut: just copy over */
*mlt = *mlf;
*mlf = mline_zero;
if (--fy >= 0)
mlf = OLDWIN(fy);
if (--ty >= 0)
mlt = NEWWIN(ty);
continue;
}
/* calculate lenght */
for (l = p->w_width - 1; l > 0; l--)
if (mlf->image[l] != ' ' || mlf->attr[l])
break;
if (fy == p->w_y + p->w_histheight && l < p->w_x)
l = p->w_x; /* cursor is non blank */
l++;
lf = l;
/* add wrapped lines to length */
for (yy = fy - 1; yy >= 0; yy--)
{
ml = OLDWIN(yy);
if (ml->image[p->w_width] == ' ')
break;
l += p->w_width;
}
/* rewrap lines */
lt = (l - 1) % wi + 1; /* lf is set above */
oty = ty;
while (l > 0 && fy >= 0 && ty >= 0)
{
lx = lt > lf ? lf : lt;
if (mlt->image == 0)
{
if (AllocMline(mlt, wi + 1))
goto nomem;
MakeBlankLine(mlt->image + lt, wi - lt);
mlt->image[wi] = ((oty == ty) ? ' ' : 0);
}
if (BcopyMline(mlf, lf - lx, mlt, lt - lx, lx, wi + 1))
goto nomem;
/* did we copy the cursor ? */
if (fy == p->w_y + p->w_histheight && lf - lx <= p->w_x && lf > p->w_x)
{
ncx = p->w_x + lt - lf + addone;
ncy = ty - hi;
shift = wi ? -ncy + (l - lx) / wi : 0;
if (ty + shift > hi + he - 1)
shift = hi + he - 1 - ty;
if (shift > 0)
{
debug3("resize: cursor out of bounds, shifting %d [%d/%d]\n", shift, lt - lx, wi);
for (y = hi + he - 1; y >= ty; y--)
{
mlt = NEWWIN(y);
FreeMline(mlt);
if (y - shift < ty)
continue;
ml = NEWWIN(y - shift);
*mlt = *ml;
*ml = mline_zero;
}
ncy += shift;
ty += shift;
mlt = NEWWIN(ty);
if (naka > 0)
naka = naka + shift > he ? 0 : naka + shift;
}
ASSERT(ncy >= 0);
}
/* did we copy autoaka line ? */
if (p->w_autoaka > 0 && fy == p->w_autoaka - 1 + p->w_histheight && lf - lx <= 0)
naka = ty - hi >= 0 ? 1 + ty - hi : 0;
lf -= lx;
lt -= lx;
l -= lx;
if (lf == 0)
{
FreeMline(mlf);
lf = p->w_width;
if (--fy >= 0)
mlf = OLDWIN(fy);
}
if (lt == 0)
{
lt = wi;
if (--ty >= 0)
mlt = NEWWIN(ty);
}
}
ASSERT(l != 0 || fy == yy);
}
while (fy >= 0)
{
FreeMline(mlf);
if (--fy >= 0)
mlf = OLDWIN(fy);
}
while (ty >= 0)
{
if (AllocMline(mlt, wi + 1))
goto nomem;
MakeBlankLine(mlt->image, wi + 1);
if (--ty >= 0)
mlt = NEWWIN(ty);
}
#ifdef DEBUG
if (nmlines != p->w_mlines)
for (fy = 0; fy < p->w_height + p->w_histheight; fy++)
{
ml = OLDWIN(fy);
ASSERT(ml->image == 0);
}
#endif
if (p->w_mlines && p->w_mlines != nmlines)
free((char *)p->w_mlines);
p->w_mlines = nmlines;
#ifdef COPY_PASTE
if (p->w_hlines && p->w_hlines != nhlines)
free((char *)p->w_hlines);
p->w_hlines = nhlines;
#endif
nmlines = nhlines = 0;
/* change tabs */
if (p->w_width != wi)
{
if (wi)
{
t = p->w_tabs ? p->w_width : 0;
p->w_tabs = xrealloc(p->w_tabs, wi + 1);
if (p->w_tabs == 0)
{
nomem:
if (nmlines)
{
for (ty = he + hi - 1; ty >= 0; ty--)
{
mlt = NEWWIN(ty);
FreeMline(mlt);
}
if (nmlines && p->w_mlines != nmlines)
free((char *)nmlines);
#ifdef COPY_PASTE
if (nhlines && p->w_hlines != nhlines)
free((char *)nhlines);
#endif
}
KillWindow(p);
Msg(0, "%s", strnomem);
return -1;
}
for (; t < wi; t++)
p->w_tabs[t] = t && !(t & 7) ? 1 : 0;
p->w_tabs[wi] = 0;
}
else
{
if (p->w_tabs)
free(p->w_tabs);
p->w_tabs = 0;
}
}
/* Change w_saved.y - this is only an estimate... */
p->w_saved.y += ncy - p->w_y;
p->w_x = ncx;
p->w_y = ncy;
if (p->w_autoaka > 0)
p->w_autoaka = naka;
/* do sanity checks */
if (p->w_x > wi)
p->w_x = wi;
if (p->w_y >= he)
p->w_y = he - 1;
if (p->w_saved.x > wi)
p->w_saved.x = wi;
if (p->w_saved.y < 0)
p->w_saved.y = 0;
if (p->w_saved.y >= he)
p->w_saved.y = he - 1;
if (p->w_alt.cursor.x > wi)
p->w_alt.cursor.x = wi;
if (p->w_alt.cursor.y >= he)
p->w_alt.cursor.y = he - 1;
/* reset scrolling region */
p->w_top = 0;
p->w_bot = he - 1;
/* signal new size to window */
#ifdef TIOCSWINSZ
if (wi && (p->w_width != wi || p->w_height != he)
&& p->w_width != 0 && p->w_height != 0 && p->w_ptyfd >= 0 && p->w_pid)
{
glwz.ws_col = wi;
glwz.ws_row = he;
debug("Setting pty winsize.\n");
if (ioctl(p->w_ptyfd, TIOCSWINSZ, (char *)&glwz))
debug2("SetPtySize: errno %d (fd:%d)\n", errno, p->w_ptyfd);
}
#endif /* TIOCSWINSZ */
/* store new size */
p->w_width = wi;
p->w_height = he;
#ifdef COPY_PASTE
p->w_histidx = 0;
p->w_histheight = hi;
#endif
#ifdef BUILTIN_TELNET
if (p->w_type == W_TYPE_TELNET)
TelWindowSize(p);
#endif
#ifdef DEBUG
/* Test if everything was ok */
for (fy = 0; fy < p->w_height + p->w_histheight; fy++)
{
ml = OLDWIN(fy);
ASSERT(ml->image);
# ifdef UTF8
if (p->w_encoding == UTF8)
{
for (l = 0; l < p->w_width; l++)
ASSERT(ml->image[l] >= ' ' || ml->font[l]);
}
else
#endif
for (l = 0; l < p->w_width; l++)
ASSERT(ml->image[l] >= ' ');
}
#endif
return 0;
}
void
FreeAltScreen(p)
struct win *p;
{
int i;
if (p->w_alt.mlines)
{
for (i = 0; i < p->w_alt.height; i++)
FreeMline(p->w_alt.mlines + i);
free(p->w_alt.mlines);
}
p->w_alt.mlines = 0;
p->w_alt.width = 0;
p->w_alt.height = 0;
#ifdef COPY_PASTE
if (p->w_alt.hlines)
{
for (i = 0; i < p->w_alt.histheight; i++)
FreeMline(p->w_alt.hlines + i);
free(p->w_alt.hlines);
}
p->w_alt.hlines = 0;
p->w_alt.histidx = 0;
p->w_alt.histheight = 0;
#endif
}
static void
SwapAltScreen(p)
struct win *p;
{
struct mline *ml;
int t;
#define SWAP(item, t) do { (t) = p->w_alt. item; p->w_alt. item = p->w_##item; p->w_##item = (t); } while (0)
SWAP(mlines, ml);
SWAP(width, t);
SWAP(height, t);
#ifdef COPY_PASTE
SWAP(histheight, t);
SWAP(hlines, ml);
SWAP(histidx, t);
#endif
#undef SWAP
}
void
EnterAltScreen(p)
struct win *p;
{
if (!p->w_alt.on)
{
/* If not already using the alternate screen buffer, then create
a new one and swap it with the 'real' screen buffer. */
FreeAltScreen(p);
SwapAltScreen(p);
}
else
{
/* Already using the alternate buffer. Just clear the screen. To do so, it
is only necessary to reset the height(s) without resetting the width. */
p->w_height = 0;
p->w_histheight = 0;
}
ChangeWindowSize(p, p->w_alt.width, p->w_alt.height, p->w_alt.histheight);
p->w_alt.on = 1;
}
void
LeaveAltScreen(p)
struct win *p;
{
if (!p->w_alt.on)
return;
SwapAltScreen(p);
ChangeWindowSize(p, p->w_alt.width, p->w_alt.height, p->w_alt.histheight);
FreeAltScreen(p);
p->w_alt.on = 0;
}
screen-4.1.0/display.h 0000644 0001750 0001750 00000026672 11642704565 012700 0 ustar abe abe /* 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
* http://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 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[20 + 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_mousetrack; /* set when user wants to use mouse even when the window
does not */
#ifdef RXVT_OSC
int d_xtermosc[4]; /* 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_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_ALWAYS (1<<2)
#endif /* SCREEN_DISPLAY_H */
screen-4.1.0/process.c 0000644 0001750 0001750 00000503712 11732171225 012666 0 ustar abe abe /* Copyright (c) 2010
* Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
* Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
* 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
* http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
****************************************************************
*/
#include "config.h"
#include
#include
#include
#include
#if !defined(sun) && !defined(B43) && !defined(ISC) && !defined(pyr) && !defined(_CX_UX)
# include
#endif
#include
#ifndef sun
#include
#endif
/* for solaris 2.1, Unixware (SVR4.2) and possibly others: */
#ifdef HAVE_STROPTS_H
# include
#endif
#include "screen.h"
#include "extern.h"
#include "logfile.h"
#include "layout.h"
#include "viewport.h"
#include "list_generic.h"
extern struct comm comms[];
extern char *rc_name;
extern char *RcFileName, *home;
extern char *BellString, *ActivityString, *ShellProg, *ShellArgs[];
extern char *hstatusstring, *captionstring, *timestring;
extern char *wliststr, *wlisttit;
extern int captionalways;
extern int queryflag;
extern char *hardcopydir, *screenlogfile, *logtstamp_string;
extern int log_flush, logtstamp_on, logtstamp_after;
extern char *VisualBellString;
extern int VBellWait, MsgWait, MsgMinWait, SilenceWait;
extern char SockPath[], *SockName;
extern int TtyMode, auto_detach, use_altscreen;
extern int iflag, maxwin;
extern int focusminwidth, focusminheight;
extern int use_hardstatus, visual_bell;
#ifdef COLOR
extern int attr2color[][4];
extern int nattr2color;
#endif
extern int hardstatusemu;
extern char *printcmd;
extern int default_startup;
extern int defobuflimit;
extern int defnonblock;
extern int defmousetrack;
extern int ZombieKey_destroy;
extern int ZombieKey_resurrect;
extern int ZombieKey_onerror;
#ifdef AUTO_NUKE
extern int defautonuke;
#endif
extern int separate_sids;
extern struct NewWindow nwin_default, nwin_undef;
#ifdef COPY_PASTE
extern int join_with_cr;
extern int compacthist;
extern int search_ic;
# ifdef FONT
extern int pastefont;
# endif
extern unsigned char mark_key_tab[];
extern char *BufferFile;
#endif
#ifdef POW_DETACH
extern char *BufferFile, *PowDetachString;
#endif
#ifdef MULTIUSER
extern struct acluser *EffectiveAclUser; /* acl.c */
#endif
extern struct term term[]; /* terminal capabilities */
#ifdef MAPKEYS
extern char *kmapdef[];
extern char *kmapadef[];
extern char *kmapmdef[];
#endif
extern struct mchar mchar_so, mchar_null;
extern int renditions[];
extern int VerboseCreate;
#ifdef UTF8
extern char *screenencodings;
#endif
#ifdef DW_CHARS
extern int cjkwidth;
#endif
static int CheckArgNum __P((int, char **));
static void ClearAction __P((struct action *));
static void SaveAction __P((struct action *, int, char **, int *));
static int NextWindow __P((void));
static int PreviousWindow __P((void));
static int MoreWindows __P((void));
static void LogToggle __P((int));
static void ShowInfo __P((void));
static void ShowDInfo __P((void));
static struct win *WindowByName __P((char *));
static int WindowByNumber __P((char *));
static int ParseOnOff __P((struct action *, int *));
static int ParseWinNum __P((struct action *, int *));
static int ParseBase __P((struct action *, char *, int *, int, char *));
static int ParseNum1000 __P((struct action *, int *));
static char **SaveArgs __P((char **));
static int IsNum __P((char *, int));
static void Colonfin __P((char *, int, char *));
static void InputSelect __P((void));
static void InputSetenv __P((char *));
static void InputAKA __P((void));
#ifdef MULTIUSER
static int InputSu __P((struct win *, struct acluser **, char *));
static void su_fin __P((char *, int, char *));
#endif
static void AKAfin __P((char *, int, char *));
#ifdef COPY_PASTE
static void copy_reg_fn __P((char *, int, char *));
static void ins_reg_fn __P((char *, int, char *));
#endif
static void process_fn __P((char *, int, char *));
#ifdef PASSWORD
static void pass1 __P((char *, int, char *));
static void pass2 __P((char *, int, char *));
#endif
#ifdef POW_DETACH
static void pow_detach_fn __P((char *, int, char *));
#endif
static void digraph_fn __P((char *, int, char *));
static int digraph_find __P((const char *buf));
static void confirm_fn __P((char *, int, char *));
static int IsOnDisplay __P((struct win *));
static void ResizeRegions __P((char *, int));
static void ResizeFin __P((char *, int, char *));
static struct action *FindKtab __P((char *, int));
static void SelectFin __P((char *, int, char *));
static void SelectLayoutFin __P((char *, int, char *));
extern struct layer *flayer;
extern struct display *display, *displays;
extern struct win *fore, *console_window, *windows;
extern struct acluser *users;
extern struct layout *layouts, *layout_attach, layout_last_marker;
extern struct layout *laytab[];
extern char screenterm[], HostName[], version[];
extern struct NewWindow nwin_undef, nwin_default;
extern struct LayFuncs WinLf, MarkLf;
extern int Z0width, Z1width;
extern int real_uid, real_gid;
#ifdef NETHACK
extern int nethackflag;
#endif
extern struct win **wtab;
#ifdef MULTIUSER
extern char *multi;
extern int maxusercount;
#endif
char NullStr[] = "";
struct plop plop_tab[MAX_PLOP_DEFS];
#ifndef PTYMODE
# define PTYMODE 0622
#endif
int TtyMode = PTYMODE;
int hardcopy_append = 0;
int all_norefresh = 0;
#ifdef ZMODEM
int zmodem_mode = 0;
char *zmodem_sendcmd;
char *zmodem_recvcmd;
static char *zmodes[4] = {"off", "auto", "catch", "pass"};
#endif
int idletimo;
struct action idleaction;
#ifdef BLANKER_PRG
char **blankerprg;
#endif
struct action ktab[256 + KMAP_KEYS]; /* command key translation table */
struct kclass {
struct kclass *next;
char *name;
struct action ktab[256 + KMAP_KEYS];
};
struct kclass *kclasses;
#ifdef MAPKEYS
struct action umtab[KMAP_KEYS+KMAP_AKEYS];
struct action dmtab[KMAP_KEYS+KMAP_AKEYS];
struct action mmtab[KMAP_KEYS+KMAP_AKEYS];
struct kmap_ext *kmap_exts;
int kmap_extn;
static int maptimeout = 300;
#endif
#ifndef MAX_DIGRAPH
#define MAX_DIGRAPH 512
#endif
struct digraph
{
unsigned char d[2];
int value;
};
/* digraph table taken from old vim and rfc1345 */
static struct digraph digraphs[MAX_DIGRAPH + 1] = {
{{' ', ' '}, 160}, /* */
{{'N', 'S'}, 160}, /* */
{{'~', '!'}, 161}, /* ¡ */
{{'!', '!'}, 161}, /* ¡ */
{{'!', 'I'}, 161}, /* ¡ */
{{'c', '|'}, 162}, /* ¢ */
{{'c', 't'}, 162}, /* ¢ */
{{'$', '$'}, 163}, /* £ */
{{'P', 'd'}, 163}, /* £ */
{{'o', 'x'}, 164}, /* ¤ */
{{'C', 'u'}, 164}, /* ¤ */
{{'C', 'u'}, 164}, /* ¤ */
{{'E', 'u'}, 164}, /* ¤ */
{{'Y', '-'}, 165}, /* ¥ */
{{'Y', 'e'}, 165}, /* ¥ */
{{'|', '|'}, 166}, /* ¦ */
{{'B', 'B'}, 166}, /* ¦ */
{{'p', 'a'}, 167}, /* § */
{{'S', 'E'}, 167}, /* § */
{{'"', '"'}, 168}, /* ¨ */
{{'\'', ':'}, 168}, /* ¨ */
{{'c', 'O'}, 169}, /* © */
{{'C', 'o'}, 169}, /* © */
{{'a', '-'}, 170}, /* ª */
{{'<', '<'}, 171}, /* « */
{{'-', ','}, 172}, /* ¬ */
{{'N', 'O'}, 172}, /* ¬ */
{{'-', '-'}, 173}, /* */
{{'r', 'O'}, 174}, /* ® */
{{'R', 'g'}, 174}, /* ® */
{{'-', '='}, 175}, /* ¯ */
{{'\'', 'm'}, 175}, /* ¯ */
{{'~', 'o'}, 176}, /* ° */
{{'D', 'G'}, 176}, /* ° */
{{'+', '-'}, 177}, /* ± */
{{'2', '2'}, 178}, /* ² */
{{'2', 'S'}, 178}, /* ² */
{{'3', '3'}, 179}, /* ³ */
{{'3', 'S'}, 179}, /* ³ */
{{'\'', '\''}, 180}, /* ´ */
{{'j', 'u'}, 181}, /* µ */
{{'M', 'y'}, 181}, /* µ */
{{'p', 'p'}, 182}, /* ¶ */
{{'P', 'I'}, 182}, /* ¶ */
{{'~', '.'}, 183}, /* · */
{{'.', 'M'}, 183}, /* · */
{{',', ','}, 184}, /* ¸ */
{{'\'', ','}, 184}, /* ¸ */
{{'1', '1'}, 185}, /* ¹ */
{{'1', 'S'}, 185}, /* ¹ */
{{'o', '-'}, 186}, /* º */
{{'>', '>'}, 187}, /* » */
{{'1', '4'}, 188}, /* ¼ */
{{'1', '2'}, 189}, /* ½ */
{{'3', '4'}, 190}, /* ¾ */
{{'~', '?'}, 191}, /* ¿ */
{{'?', '?'}, 191}, /* ¿ */
{{'?', 'I'}, 191}, /* ¿ */
{{'A', '`'}, 192}, /* À */
{{'A', '!'}, 192}, /* À */
{{'A', '\''}, 193}, /* Á */
{{'A', '^'}, 194}, /* Â */
{{'A', '>'}, 194}, /* Â */
{{'A', '~'}, 195}, /* Ã */
{{'A', '?'}, 195}, /* Ã */
{{'A', '"'}, 196}, /* Ä */
{{'A', ':'}, 196}, /* Ä */
{{'A', '@'}, 197}, /* Å */
{{'A', 'A'}, 197}, /* Å */
{{'A', 'E'}, 198}, /* Æ */
{{'C', ','}, 199}, /* Ç */
{{'E', '`'}, 200}, /* È */
{{'E', '!'}, 200}, /* È */
{{'E', '\''}, 201}, /* É */
{{'E', '^'}, 202}, /* Ê */
{{'E', '>'}, 202}, /* Ê */
{{'E', '"'}, 203}, /* Ë */
{{'E', ':'}, 203}, /* Ë */
{{'I', '`'}, 204}, /* Ì */
{{'I', '!'}, 204}, /* Ì */
{{'I', '\''}, 205}, /* Í */
{{'I', '^'}, 206}, /* Î */
{{'I', '>'}, 206}, /* Î */
{{'I', '"'}, 207}, /* Ï */
{{'I', ':'}, 207}, /* Ï */
{{'D', '-'}, 208}, /* Ð */
{{'N', '~'}, 209}, /* Ñ */
{{'N', '?'}, 209}, /* Ñ */
{{'O', '`'}, 210}, /* Ò */
{{'O', '!'}, 210}, /* Ò */
{{'O', '\''}, 211}, /* Ó */
{{'O', '^'}, 212}, /* Ô */
{{'O', '>'}, 212}, /* Ô */
{{'O', '~'}, 213}, /* Õ */
{{'O', '?'}, 213}, /* Õ */
{{'O', '"'}, 214}, /* Ö */
{{'O', ':'}, 214}, /* Ö */
{{'/', '\\'}, 215}, /* × */
{{'*', 'x'}, 215}, /* × */
{{'O', '/'}, 216}, /* Ø */
{{'U', '`'}, 217}, /* Ù */
{{'U', '!'}, 217}, /* Ù */
{{'U', '\''}, 218}, /* Ú */
{{'U', '^'}, 219}, /* Û */
{{'U', '>'}, 219}, /* Û */
{{'U', '"'}, 220}, /* Ü */
{{'U', ':'}, 220}, /* Ü */
{{'Y', '\''}, 221}, /* Ý */
{{'I', 'p'}, 222}, /* Þ */
{{'T', 'H'}, 222}, /* Þ */
{{'s', 's'}, 223}, /* ß */
{{'s', '"'}, 223}, /* ß */
{{'a', '`'}, 224}, /* à */
{{'a', '!'}, 224}, /* à */
{{'a', '\''}, 225}, /* á */
{{'a', '^'}, 226}, /* â */
{{'a', '>'}, 226}, /* â */
{{'a', '~'}, 227}, /* ã */
{{'a', '?'}, 227}, /* ã */
{{'a', '"'}, 228}, /* ä */
{{'a', ':'}, 228}, /* ä */
{{'a', 'a'}, 229}, /* å */
{{'a', 'e'}, 230}, /* æ */
{{'c', ','}, 231}, /* ç */
{{'e', '`'}, 232}, /* è */
{{'e', '!'}, 232}, /* è */
{{'e', '\''}, 233}, /* é */
{{'e', '^'}, 234}, /* ê */
{{'e', '>'}, 234}, /* ê */
{{'e', '"'}, 235}, /* ë */
{{'e', ':'}, 235}, /* ë */
{{'i', '`'}, 236}, /* ì */
{{'i', '!'}, 236}, /* ì */
{{'i', '\''}, 237}, /* í */
{{'i', '^'}, 238}, /* î */
{{'i', '>'}, 238}, /* î */
{{'i', '"'}, 239}, /* ï */
{{'i', ':'}, 239}, /* ï */
{{'d', '-'}, 240}, /* ð */
{{'n', '~'}, 241}, /* ñ */
{{'n', '?'}, 241}, /* ñ */
{{'o', '`'}, 242}, /* ò */
{{'o', '!'}, 242}, /* ò */
{{'o', '\''}, 243}, /* ó */
{{'o', '^'}, 244}, /* ô */
{{'o', '>'}, 244}, /* ô */
{{'o', '~'}, 245}, /* õ */
{{'o', '?'}, 245}, /* õ */
{{'o', '"'}, 246}, /* ö */
{{'o', ':'}, 246}, /* ö */
{{':', '-'}, 247}, /* ÷ */
{{'o', '/'}, 248}, /* ø */
{{'u', '`'}, 249}, /* ù */
{{'u', '!'}, 249}, /* ù */
{{'u', '\''}, 250}, /* ú */
{{'u', '^'}, 251}, /* û */
{{'u', '>'}, 251}, /* û */
{{'u', '"'}, 252}, /* ü */
{{'u', ':'}, 252}, /* ü */
{{'y', '\''}, 253}, /* ý */
{{'i', 'p'}, 254}, /* þ */
{{'t', 'h'}, 254}, /* þ */
{{'y', '"'}, 255}, /* ÿ */
{{'y', ':'}, 255}, /* ÿ */
{{'"', '['}, 196}, /* Ä */
{{'"', '\\'}, 214}, /* Ö */
{{'"', ']'}, 220}, /* Ü */
{{'"', '{'}, 228}, /* ä */
{{'"', '|'}, 246}, /* ö */
{{'"', '}'}, 252}, /* ü */
{{'"', '~'}, 223} /* ß */
};
#define RESIZE_FLAG_H 1
#define RESIZE_FLAG_V 2
#define RESIZE_FLAG_L 4
static char *resizeprompts[] = {
"resize # lines: ",
"resize -h # lines: ",
"resize -v # lines: ",
"resize -b # lines: ",
"resize -l # lines: ",
"resize -l -h # lines: ",
"resize -l -v # lines: ",
"resize -l -b # lines: ",
};
static int
parse_input_int(buf, len, val)
const char *buf;
int len;
int *val;
{
int x = 0, i;
if (len >= 1 && ((*buf == 'U' && buf[1] == '+') || (*buf == '0' && (buf[1] == 'x' || buf[1] == 'X'))))
{
x = 0;
for (i = 2; i < len; i++)
{
if (buf[i] >= '0' && buf[i] <= '9')
x = x * 16 | (buf[i] - '0');
else if (buf[i] >= 'a' && buf[i] <= 'f')
x = x * 16 | (buf[i] - ('a' - 10));
else if (buf[i] >= 'A' && buf[i] <= 'F')
x = x * 16 | (buf[i] - ('A' - 10));
else
return 0;
}
}
else if (buf[0] == '0')
{
x = 0;
for (i = 1; i < len; i++)
{
if (buf[i] < '0' || buf[i] > '7')
return 0;
x = x * 8 | (buf[i] - '0');
}
}
else
return 0;
*val = x;
return 1;
}
char *noargs[1];
void
InitKeytab()
{
register unsigned int i;
#ifdef MAPKEYS
char *argarr[2];
#endif
for (i = 0; i < sizeof(ktab)/sizeof(*ktab); i++)
{
ktab[i].nr = RC_ILLEGAL;
ktab[i].args = noargs;
ktab[i].argl = 0;
}
#ifdef MAPKEYS
for (i = 0; i < KMAP_KEYS+KMAP_AKEYS; i++)
{
umtab[i].nr = RC_ILLEGAL;
umtab[i].args = noargs;
umtab[i].argl = 0;
dmtab[i].nr = RC_ILLEGAL;
dmtab[i].args = noargs;
dmtab[i].argl = 0;
mmtab[i].nr = RC_ILLEGAL;
mmtab[i].args = noargs;
mmtab[i].argl = 0;
}
argarr[1] = 0;
for (i = 0; i < NKMAPDEF; i++)
{
if (i + KMAPDEFSTART < T_CAPS)
continue;
if (i + KMAPDEFSTART >= T_CAPS + KMAP_KEYS)
continue;
if (kmapdef[i] == 0)
continue;
argarr[0] = kmapdef[i];
SaveAction(dmtab + i + (KMAPDEFSTART - T_CAPS), RC_STUFF, argarr, 0);
}
for (i = 0; i < NKMAPADEF; i++)
{
if (i + KMAPADEFSTART < T_CURSOR)
continue;
if (i + KMAPADEFSTART >= T_CURSOR + KMAP_AKEYS)
continue;
if (kmapadef[i] == 0)
continue;
argarr[0] = kmapadef[i];
SaveAction(dmtab + i + (KMAPADEFSTART - T_CURSOR + KMAP_KEYS), RC_STUFF, argarr, 0);
}
for (i = 0; i < NKMAPMDEF; i++)
{
if (i + KMAPMDEFSTART < T_CAPS)
continue;
if (i + KMAPMDEFSTART >= T_CAPS + KMAP_KEYS)
continue;
if (kmapmdef[i] == 0)
continue;
argarr[0] = kmapmdef[i];
argarr[1] = 0;
SaveAction(mmtab + i + (KMAPMDEFSTART - T_CAPS), RC_STUFF, argarr, 0);
}
#endif
ktab['h'].nr = RC_HARDCOPY;
#ifdef BSDJOBS
ktab['z'].nr = ktab[Ctrl('z')].nr = RC_SUSPEND;
#endif
ktab['c'].nr = ktab[Ctrl('c')].nr = RC_SCREEN;
ktab[' '].nr = ktab[Ctrl(' ')].nr =
ktab['n'].nr = ktab[Ctrl('n')].nr = RC_NEXT;
ktab['N'].nr = RC_NUMBER;
ktab[Ctrl('h')].nr = ktab[0177].nr = ktab['p'].nr = ktab[Ctrl('p')].nr = RC_PREV;
ktab['k'].nr = ktab[Ctrl('k')].nr = RC_KILL;
ktab['l'].nr = ktab[Ctrl('l')].nr = RC_REDISPLAY;
ktab['w'].nr = ktab[Ctrl('w')].nr = RC_WINDOWS;
ktab['v'].nr = RC_VERSION;
ktab[Ctrl('v')].nr = RC_DIGRAPH;
ktab['q'].nr = ktab[Ctrl('q')].nr = RC_XON;
ktab['s'].nr = ktab[Ctrl('s')].nr = RC_XOFF;
ktab['t'].nr = ktab[Ctrl('t')].nr = RC_TIME;
ktab['i'].nr = ktab[Ctrl('i')].nr = RC_INFO;
ktab['m'].nr = ktab[Ctrl('m')].nr = RC_LASTMSG;
ktab['A'].nr = RC_TITLE;
#if defined(UTMPOK) && defined(LOGOUTOK)
ktab['L'].nr = RC_LOGIN;
#endif
ktab[','].nr = RC_LICENSE;
ktab['W'].nr = RC_WIDTH;
ktab['.'].nr = RC_DUMPTERMCAP;
ktab[Ctrl('\\')].nr = RC_QUIT;
#ifdef DETACH
ktab['d'].nr = ktab[Ctrl('d')].nr = RC_DETACH;
# ifdef POW_DETACH
ktab['D'].nr = RC_POW_DETACH;
# endif
#endif
ktab['r'].nr = ktab[Ctrl('r')].nr = RC_WRAP;
ktab['f'].nr = ktab[Ctrl('f')].nr = RC_FLOW;
ktab['C'].nr = RC_CLEAR;
ktab['Z'].nr = RC_RESET;
ktab['H'].nr = RC_LOG;
ktab['M'].nr = RC_MONITOR;
ktab['?'].nr = RC_HELP;
#ifdef MULTI
ktab['*'].nr = RC_DISPLAYS;
#endif
{
char *args[2];
args[0] = "-";
args[1] = NULL;
SaveAction(ktab + '-', RC_SELECT, args, 0);
}
for (i = 0; i < ((maxwin && maxwin < 10) ? maxwin : 10); i++)
{
char *args[2], arg1[10];
args[0] = arg1;
args[1] = 0;
sprintf(arg1, "%d", i);
SaveAction(ktab + '0' + i, RC_SELECT, args, 0);
}
ktab['\''].nr = RC_SELECT; /* calling a window by name */
{
char *args[2];
args[0] = "-b";
args[1] = 0;
SaveAction(ktab + '"', RC_WINDOWLIST, args, 0);
}
ktab[Ctrl('G')].nr = RC_VBELL;
ktab[':'].nr = RC_COLON;
#ifdef COPY_PASTE
ktab['['].nr = ktab[Ctrl('[')].nr = RC_COPY;
{
char *args[2];
args[0] = ".";
args[1] = 0;
SaveAction(ktab + ']', RC_PASTE, args, 0);
SaveAction(ktab + Ctrl(']'), RC_PASTE, args, 0);
}
ktab['{'].nr = RC_HISTORY;
ktab['}'].nr = RC_HISTORY;
ktab['>'].nr = RC_WRITEBUF;
ktab['<'].nr = RC_READBUF;
ktab['='].nr = RC_REMOVEBUF;
#endif
#ifdef POW_DETACH
ktab['D'].nr = RC_POW_DETACH;
#endif
#ifdef LOCK
ktab['x'].nr = ktab[Ctrl('x')].nr = RC_LOCKSCREEN;
#endif
ktab['b'].nr = ktab[Ctrl('b')].nr = RC_BREAK;
ktab['B'].nr = RC_POW_BREAK;
ktab['_'].nr = RC_SILENCE;
ktab['S'].nr = RC_SPLIT;
ktab['Q'].nr = RC_ONLY;
ktab['X'].nr = RC_REMOVE;
ktab['F'].nr = RC_FIT;
ktab['\t'].nr = RC_FOCUS;
{
char *args[2];
args[0] = "prev";
args[1] = 0;
SaveAction(ktab + T_BACKTAB - T_CAPS + 256, RC_FOCUS, args, 0);
}
{
char *args[2];
args[0] = "-v";
args[1] = 0;
SaveAction(ktab + '|', RC_SPLIT, args, 0);
}
/* These come last; they may want overwrite others: */
if (DefaultEsc >= 0)
{
ClearAction(&ktab[DefaultEsc]);
ktab[DefaultEsc].nr = RC_OTHER;
}
if (DefaultMetaEsc >= 0)
{
ClearAction(&ktab[DefaultMetaEsc]);
ktab[DefaultMetaEsc].nr = RC_META;
}
idleaction.nr = RC_BLANKER;
idleaction.args = noargs;
idleaction.argl = 0;
}
static struct action *
FindKtab(class, create)
char *class;
int create;
{
struct kclass *kp, **kpp;
int i;
if (class == 0)
return ktab;
for (kpp = &kclasses; (kp = *kpp) != 0; kpp = &kp->next)
if (!strcmp(kp->name, class))
break;
if (kp == 0)
{
if (!create)
return 0;
if (strlen(class) > 80)
{
Msg(0, "Command class name too long.");
return 0;
}
kp = malloc(sizeof(*kp));
if (kp == 0)
{
Msg(0, "%s", strnomem);
return 0;
}
kp->name = SaveStr(class);
for (i = 0; i < (int)(sizeof(kp->ktab)/sizeof(*kp->ktab)); i++)
{
kp->ktab[i].nr = RC_ILLEGAL;
kp->ktab[i].args = noargs;
kp->ktab[i].argl = 0;
kp->ktab[i].quiet = 0;
}
kp->next = 0;
*kpp = kp;
}
return kp->ktab;
}
static void
ClearAction(act)
struct action *act;
{
char **p;
if (act->nr == RC_ILLEGAL)
return;
act->nr = RC_ILLEGAL;
if (act->args == noargs)
return;
for (p = act->args; *p; p++)
free(*p);
free((char *)act->args);
act->args = noargs;
act->argl = 0;
}
/*
* ProcessInput: process input from display and feed it into
* the layer on canvas D_forecv.
*/
#ifdef MAPKEYS
/*
* This ProcessInput just does the keybindings and passes
* everything else on to ProcessInput2.
*/
void
ProcessInput(ibuf, ilen)
char *ibuf;
int ilen;
{
int ch, slen;
unsigned char *s, *q;
int i, l;
char *p;
debug1("ProcessInput: %d bytes\n", ilen);
if (display == 0 || ilen == 0)
return;
if (D_seql)
evdeq(&D_mapev);
slen = ilen;
s = (unsigned char *)ibuf;
while (ilen-- > 0)
{
ch = *s++;
if (D_dontmap || !D_nseqs)
{
D_dontmap = 0;
continue;
}
for (;;)
{
debug3("cmp %c %c[%d]\n", ch, *D_seqp, D_seqp - D_kmaps);
if (*D_seqp != ch)
{
l = D_seqp[D_seqp[-D_seql-1] + 1];
if (l)
{
D_seqp += l * 2 + 4;
debug1("miss %d\n", D_seqp - D_kmaps);
continue;
}
debug("complete miss\n");
D_mapdefault = 0;
l = D_seql;
p = (char *)D_seqp - l;
D_seql = 0;
D_seqp = D_kmaps + 3;
if (l == 0)
break;
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;
debug1("flush old %d\n", l);
ProcessInput(p, l);
if (display == 0)
return;
evdeq(&D_mapev);
continue;
}
if (D_seql++ == 0)
{
/* Finish old stuff */
slen -= ilen + 1;
debug1("finish old %d\n", slen);
if (slen)
ProcessInput2(ibuf, slen);
if (display == 0)
return;
D_seqh = 0;
}
ibuf = (char *)s;
slen = ilen;
D_seqp++;
l = D_seql;
debug2("length am %d, want %d\n", l, D_seqp[-l - 1]);
if (l == D_seqp[-l - 1])
{
if (D_seqp[l] != l)
{
q = D_seqp + 1 + l;
if (D_kmaps + D_nseqs > q && q[2] > l && !bcmp(D_seqp - l, q + 3, l))
{
debug1("have another mapping (%s), delay execution\n", q + 3);
D_seqh = D_seqp - 3 - l;
D_seqp = q + 3 + l;
break;
}
}
i = D_seqp[-l - 3] << 8 | D_seqp[-l - 2];
i &= ~KMAP_NOTIMEOUT;
debug1("Mapping #%d - ", i);
p = (char *)D_seqp - l;
debug2("%d(%s) - ", l, p);
D_seql = 0;
D_seqp = D_kmaps + 3;
D_seqh = 0;
if (StuffKey(i))
ProcessInput2(p, l);
if (display == 0)
return;
}
break;
}
}
if (D_seql)
{
debug("am in sequence -> check for timeout\n");
l = D_seql;
for (s = D_seqp; ; s += i * 2 + 4)
{
if (s[-l-3] & KMAP_NOTIMEOUT >> 8)
break;
if ((i = s[s[-l-1] + 1]) == 0)
{
SetTimeout(&D_mapev, maptimeout);
evenq(&D_mapev);
break;
}
}
}
ProcessInput2(ibuf, slen);
}
#else
# define ProcessInput2 ProcessInput
#endif
/*
* Here only the screen escape commands are handled.
*/
void
ProcessInput2(ibuf, ilen)
char *ibuf;
int ilen;
{
char *s;
int ch, slen;
struct action *ktabp;
debug1("ProcessInput2: %d bytes\n", ilen);
while (ilen && display)
{
debug1(" - ilen now %d bytes\n", ilen);
flayer = D_forecv->c_layer;
fore = D_fore;
slen = ilen;
s = ibuf;
if (!D_ESCseen)
{
while (ilen > 0)
{
if ((unsigned char)*s++ == D_user->u_Esc)
break;
ilen--;
}
slen -= ilen;
if (slen)
DoProcess(fore, &ibuf, &slen, 0);
if (--ilen == 0)
{
D_ESCseen = ktab;
WindowChanged(fore, 'E');
}
}
if (ilen <= 0)
return;
ktabp = D_ESCseen ? D_ESCseen : ktab;
if (D_ESCseen)
{
D_ESCseen = 0;
WindowChanged(fore, 'E');
}
ch = (unsigned char)*s;
/*
* As users have different esc characters, but a common ktab[],
* we fold back the users esc and meta-esc key to the Default keys
* that can be looked up in the ktab[]. grmbl. jw.
* XXX: make ktab[] a per user thing.
*/
if (ch == D_user->u_Esc)
ch = DefaultEsc;
else if (ch == D_user->u_MetaEsc)
ch = DefaultMetaEsc;
if (ch >= 0)
DoAction(&ktabp[ch], ch);
ibuf = (char *)(s + 1);
ilen--;
}
}
void
DoProcess(p, bufp, lenp, pa)
struct win *p;
char **bufp;
int *lenp;
struct paster *pa;
{
int oldlen;
struct display *d = display;
#ifdef COPY_PASTE
/* XXX -> PasteStart */
if (pa && *lenp > 1 && p && p->w_slowpaste)
{
/* schedule slowpaste event */
SetTimeout(&p->w_paster.pa_slowev, p->w_slowpaste);
evenq(&p->w_paster.pa_slowev);
return;
}
#endif
while (flayer && *lenp)
{
#ifdef COPY_PASTE
if (!pa && p && p->w_paster.pa_pastelen && flayer == p->w_paster.pa_pastelayer)
{
debug("layer is busy - beep!\n");
WBell(p, visual_bell);
*bufp += *lenp;
*lenp = 0;
display = d;
return;
}
#endif
oldlen = *lenp;
LayProcess(bufp, lenp);
#ifdef COPY_PASTE
if (pa && !pa->pa_pastelayer)
break; /* flush rest of paste */
#endif
if (*lenp == oldlen)
{
if (pa)
{
display = d;
return;
}
/* We're full, let's beep */
debug("layer is full - beep!\n");
WBell(p, visual_bell);
break;
}
}
*bufp += *lenp;
*lenp = 0;
display = d;
#ifdef COPY_PASTE
if (pa && pa->pa_pastelen == 0)
FreePaster(pa);
#endif
}
int
FindCommnr(str)
const char *str;
{
int x, m, l = 0, r = RC_LAST;
while (l <= r)
{
m = (l + r) / 2;
x = strcmp(str, comms[m].name);
if (x > 0)
l = m + 1;
else if (x < 0)
r = m - 1;
else
return m;
}
return RC_ILLEGAL;
}
static int
CheckArgNum(nr, args)
int nr;
char **args;
{
int i, n;
static char *argss[] = {"no", "one", "two", "three", "four", "OOPS"};
static char *orformat[] =
{
"%s: %s: %s argument%s required",
"%s: %s: %s or %s argument%s required",
"%s: %s: %s, %s or %s argument%s required",
"%s: %s: %s, %s, %s or %s argument%s required"
};
n = comms[nr].flags & ARGS_MASK;
for (i = 0; args[i]; i++)
;
if (comms[nr].flags & ARGS_ORMORE)
{
if (i < n)
{
Msg(0, "%s: %s: at least %s argument%s required",
rc_name, comms[nr].name, argss[n], n != 1 ? "s" : "");
return -1;
}
}
else if ((comms[nr].flags & ARGS_PLUS1) &&
(comms[nr].flags & ARGS_PLUS2) &&
(comms[nr].flags & ARGS_PLUS3))
{
if (i != n && i != n + 1 && i != n + 2 && i != n + 3)
{
Msg(0, orformat[3], rc_name, comms[nr].name, argss[n],
argss[n + 1], argss[n + 2], argss[n + 3], "");
return -1;
}
}
else if ((comms[nr].flags & ARGS_PLUS1) &&
(comms[nr].flags & ARGS_PLUS2))
{
if (i != n && i != n + 1 && i != n + 2)
{
Msg(0, orformat[2], rc_name, comms[nr].name, argss[n],
argss[n + 1], argss[n + 2], "");
return -1;
}
}
else if ((comms[nr].flags & ARGS_PLUS1) &&
(comms[nr].flags & ARGS_PLUS3))
{
if (i != n && i != n + 1 && i != n + 3)
{
Msg(0, orformat[2], rc_name, comms[nr].name, argss[n],
argss[n + 1], argss[n + 3], "");
return -1;
}
}
else if ((comms[nr].flags & ARGS_PLUS2) &&
(comms[nr].flags & ARGS_PLUS3))
{
if (i != n && i != n + 2 && i != n + 3)
{
Msg(0, orformat[2], rc_name, comms[nr].name, argss[n],
argss[n + 2], argss[n + 3], "");
return -1;
}
}
else if (comms[nr].flags & ARGS_PLUS1)
{
if (i != n && i != n + 1)
{
Msg(0, orformat[1], rc_name, comms[nr].name, argss[n],
argss[n + 1], n != 0 ? "s" : "");
return -1;
}
}
else if (comms[nr].flags & ARGS_PLUS2)
{
if (i != n && i != n + 2)
{
Msg(0, orformat[1], rc_name, comms[nr].name, argss[n],
argss[n + 2], "s");
return -1;
}
}
else if (comms[nr].flags & ARGS_PLUS3)
{
if (i != n && i != n + 3)
{
Msg(0, orformat[1], rc_name, comms[nr].name, argss[n],
argss[n + 3], "");
return -1;
}
}
else if (i != n)
{
Msg(0, orformat[0], rc_name, comms[nr].name, argss[n], n != 1 ? "s" : "");
return -1;
}
return i;
}
static void
StuffFin(buf, len, data)
char *buf;
int len;
char *data;
{
if (!flayer)
return;
while(len)
LayProcess(&buf, &len);
}
/* If the command is not 'quieted', then use Msg to output the message. If it's a remote
* query, then Msg takes care of also outputting the message to the querying client.
*
* If we want the command to be quiet, and it's a remote query, then use QueryMsg so that
* the response does go back to the querying client.
*
* If the command is quieted, and it's not a remote query, then just don't print the message.
*/
#define OutputMsg (!act->quiet ? Msg : queryflag >= 0 ? QueryMsg : Dummy)
/*ARGSUSED*/
void
DoAction(act, key)
struct action *act;
int key;
{
int nr = act->nr;
char **args = act->args;
int *argl = act->argl;
struct win *p;
int argc, i, n, msgok;
char *s;
char ch;
struct display *odisplay = display;
struct acluser *user;
user = display ? D_user : users;
if (nr == RC_ILLEGAL)
{
debug1("key '%c': No action\n", key);
return;
}
n = comms[nr].flags;
/* Commands will have a CAN_QUERY flag, depending on whether they have
* something to return on a query. For example, 'windows' can return a result,
* but 'other' cannot.
* If some command causes an error, then it should reset queryflag to -1, so that
* the process requesting the query can be notified that an error happened.
*/
if (!(n & CAN_QUERY) && queryflag >= 0)
{
/* Query flag is set, but this command cannot be queried. */
OutputMsg(0, "%s command cannot be queried.", comms[nr].name);
queryflag = -1;
return;
}
if ((n & NEED_DISPLAY) && display == 0)
{
OutputMsg(0, "%s: %s: display required", rc_name, comms[nr].name);
queryflag = -1;
return;
}
if ((n & NEED_FORE) && fore == 0)
{
OutputMsg(0, "%s: %s: window required", rc_name, comms[nr].name);
queryflag = -1;
return;
}
if ((n & NEED_LAYER) && flayer == 0)
{
OutputMsg(0, "%s: %s: display or window required", rc_name, comms[nr].name);
queryflag = -1;
return;
}
if ((argc = CheckArgNum(nr, args)) < 0)
return;
#ifdef MULTIUSER
if (display)
{
if (AclCheckPermCmd(D_user, ACL_EXEC, &comms[nr]))
{
OutputMsg(0, "%s: %s: permission denied (user %s)",
rc_name, comms[nr].name, (EffectiveAclUser ? EffectiveAclUser : D_user)->u_name);
queryflag = -1;
return;
}
}
#endif /* MULTIUSER */
msgok = display && !*rc_name;
switch(nr)
{
case RC_SELECT:
if (!*args)
InputSelect();
else if (args[0][0] == '-' && !args[0][1])
{
SetForeWindow((struct win *)0);
Activate(0);
}
else if (args[0][0] == '.' && !args[0][1])
{
if (!fore)
{
OutputMsg(0, "select . needs a window");
queryflag = -1;
}
else
{
SetForeWindow(fore);
Activate(0);
}
}
else if (ParseWinNum(act, &n) == 0)
SwitchWindow(n);
else if (queryflag >= 0)
queryflag = -1; /* ParseWinNum already prints out an appropriate error message. */
break;
#ifdef AUTO_NUKE
case RC_DEFAUTONUKE:
if (ParseOnOff(act, &defautonuke) == 0 && msgok)
OutputMsg(0, "Default autonuke turned %s", defautonuke ? "on" : "off");
if (display && *rc_name)
D_auto_nuke = defautonuke;
break;
case RC_AUTONUKE:
if (ParseOnOff(act, &D_auto_nuke) == 0 && msgok)
OutputMsg(0, "Autonuke turned %s", D_auto_nuke ? "on" : "off");
break;
#endif
case RC_DEFOBUFLIMIT:
if (ParseNum(act, &defobuflimit) == 0 && msgok)
OutputMsg(0, "Default limit set to %d", defobuflimit);
if (display && *rc_name)
{
D_obufmax = defobuflimit;
D_obuflenmax = D_obuflen - D_obufmax;
}
break;
case RC_OBUFLIMIT:
if (*args == 0)
OutputMsg(0, "Limit is %d, current buffer size is %d", D_obufmax, D_obuflen);
else if (ParseNum(act, &D_obufmax) == 0 && msgok)
OutputMsg(0, "Limit set to %d", D_obufmax);
D_obuflenmax = D_obuflen - D_obufmax;
break;
case RC_DUMPTERMCAP:
WriteFile(user, (char *)0, DUMP_TERMCAP);
break;
case RC_HARDCOPY:
{
int mode = DUMP_HARDCOPY;
char *file = NULL;
if (args[0])
{
if (!strcmp(*args, "-h"))
{
mode = DUMP_SCROLLBACK;
file = args[1];
}
else if (!strcmp(*args, "--") && args[1])
file = args[1];
else
file = args[0];
}
if (args[0] && file == args[0] && args[1])
{
OutputMsg(0, "%s: hardcopy: too many arguments", rc_name);
break;
}
WriteFile(user, file, mode);
}
break;
case RC_DEFLOG:
(void)ParseOnOff(act, &nwin_default.Lflag);
break;
case RC_LOG:
n = fore->w_log ? 1 : 0;
ParseSwitch(act, &n);
LogToggle(n);
break;
#ifdef BSDJOBS
case RC_SUSPEND:
Detach(D_STOP);
break;
#endif
case RC_NEXT:
if (MoreWindows())
SwitchWindow(NextWindow());
break;
case RC_PREV:
if (MoreWindows())
SwitchWindow(PreviousWindow());
break;
case RC_KILL:
{
char *name;
if (key >= 0)
{
#ifdef PSEUDOS
Input(fore->w_pwin ? "Really kill this filter [y/n]" : "Really kill this window [y/n]", 1, INP_RAW, confirm_fn, NULL, RC_KILL);
#else
Input("Really kill this window [y/n]", 1, INP_RAW, confirm_fn, NULL, RC_KILL);
#endif
break;
}
n = fore->w_number;
#ifdef PSEUDOS
if (fore->w_pwin)
{
FreePseudowin(fore);
OutputMsg(0, "Filter removed.");
break;
}
#endif
name = SaveStr(fore->w_title);
KillWindow(fore);
OutputMsg(0, "Window %d (%s) killed.", n, name);
if (name)
free(name);
break;
}
case RC_QUIT:
if (key >= 0)
{
Input("Really quit and kill all your windows [y/n]", 1, INP_RAW, confirm_fn, NULL, RC_QUIT);
break;
}
Finit(0);
/* NOTREACHED */
#ifdef DETACH
case RC_DETACH:
if (*args && !strcmp(*args, "-h"))
Hangup();
else
Detach(D_DETACH);
break;
# ifdef POW_DETACH
case RC_POW_DETACH:
if (key >= 0)
{
static char buf[2];
buf[0] = key;
Input(buf, 1, INP_RAW, pow_detach_fn, NULL, 0);
}
else
Detach(D_POWER); /* detach and kill Attacher's parent */
break;
# endif
#endif
case RC_DEBUG:
#ifdef DEBUG
if (!*args)
{
if (dfp)
OutputMsg(0, "debugging info is written to %s/", DEBUGDIR);
else
OutputMsg(0, "debugging is currently off. Use 'debug on' to enable.");
break;
}
if (dfp)
{
debug("debug: closing debug file.\n");
fflush(dfp);
fclose(dfp);
dfp = NULL;
}
if (strcmp("off", *args))
opendebug(0, 1);
# ifdef SIG_NODEBUG
else if (display)
kill(D_userpid, SIG_NODEBUG); /* a one shot item, but hey... */
# endif /* SIG_NODEBUG */
#else
if (*args == 0 || strcmp("off", *args))
OutputMsg(0, "Sorry, screen was compiled without -DDEBUG option.");
#endif
break;
#ifdef ZMODEM
case RC_ZMODEM:
if (*args && !strcmp(*args, "sendcmd"))
{
if (args[1])
{
free(zmodem_sendcmd);
zmodem_sendcmd = SaveStr(args[1]);
}
if (msgok)
OutputMsg(0, "zmodem sendcmd: %s", zmodem_sendcmd);
break;
}
if (*args && !strcmp(*args, "recvcmd"))
{
if (args[1])
{
free(zmodem_recvcmd);
zmodem_recvcmd = SaveStr(args[1]);
}
if (msgok)
OutputMsg(0, "zmodem recvcmd: %s", zmodem_recvcmd);
break;
}
if (*args)
{
for (i = 0; i < 4; i++)
if (!strcmp(zmodes[i], *args))
break;
if (i == 4 && !strcmp(*args, "on"))
i = 1;
if (i == 4)
{
OutputMsg(0, "usage: zmodem off|auto|catch|pass");
break;
}
zmodem_mode = i;
}
if (msgok)
OutputMsg(0, "zmodem mode is %s", zmodes[zmodem_mode]);
break;
#endif
case RC_UNBINDALL:
{
register unsigned int i;
for (i = 0; i < sizeof(ktab)/sizeof(*ktab); i++)
ClearAction(&ktab[i]);
OutputMsg(0, "Unbound all keys." );
break;
}
case RC_ZOMBIE:
{
if (!(s = *args))
{
ZombieKey_destroy = 0;
break;
}
if (*argl == 0 || *argl > 2)
{
OutputMsg(0, "%s:zombie: one or two characters expected.", rc_name);
break;
}
if (args[1])
{
if (!strcmp(args[1], "onerror"))
{
ZombieKey_onerror = 1;
} else {
OutputMsg(0, "usage: zombie [keys [onerror]]");
break;
}
} else
ZombieKey_onerror = 0;
ZombieKey_destroy = args[0][0];
ZombieKey_resurrect = *argl == 2 ? args[0][1] : 0;
}
break;
case RC_WALL:
#ifdef MULTIUSER
s = D_user->u_name;
#else
s = D_usertty;
#endif
{
struct display *olddisplay = display;
display = 0; /* no display will cause a broadcast */
OutputMsg(0, "%s: %s", s, *args);
display = olddisplay;
}
break;
case RC_AT:
/* where this AT command comes from: */
if (!user)
break;
#ifdef MULTIUSER
s = SaveStr(user->u_name);
/* DO NOT RETURN FROM HERE WITHOUT RESETTING THIS: */
EffectiveAclUser = user;
#else
s = SaveStr(display ? D_usertty : user->u_name);
#endif
n = strlen(args[0]);
if (n) n--;
/*
* the windows/displays loops are quite dangerous here, take extra
* care not to trigger landmines. Things may appear/disappear while
* we are walking along.
*/
switch (args[0][n])
{
case '*': /* user */
{
struct display *nd;
struct acluser *u;
if (!n)
u = user;
else
{
for (u = users; u; u = u->u_next)
{
debug3("strncmp('%s', '%s', %d)\n", *args, u->u_name, n);
if (!strncmp(*args, u->u_name, n))
break;
}
if (!u)
{
args[0][n] = '\0';
OutputMsg(0, "Did not find any user matching '%s'", args[0]);
break;
}
}
debug1("at all displays of user %s\n", u->u_name);
for (display = displays; display; display = nd)
{
nd = display->d_next;
if (D_forecv == 0)
continue;
flayer = D_forecv->c_layer;
fore = D_fore;
if (D_user != u)
continue;
debug1("AT display %s\n", D_usertty);
DoCommand(args + 1, argl + 1);
if (display)
OutputMsg(0, "command from %s: %s %s",
s, args[1], args[2] ? args[2] : "");
display = NULL;
flayer = 0;
fore = NULL;
}
break;
}
case '%': /* display */
{
struct display *nd;
debug1("at display matching '%s'\n", args[0]);
for (display = displays; display; display = nd)
{
nd = display->d_next;
if (D_forecv == 0)
continue;
fore = D_fore;
flayer = D_forecv->c_layer;
if (strncmp(args[0], D_usertty, n) &&
(strncmp("/dev/", D_usertty, 5) ||
strncmp(args[0], D_usertty + 5, n)) &&
(strncmp("/dev/tty", D_usertty, 8) ||
strncmp(args[0], D_usertty + 8, n)))
continue;
debug1("AT display %s\n", D_usertty);
DoCommand(args + 1, argl + 1);
if (display)
OutputMsg(0, "command from %s: %s %s",
s, args[1], args[2] ? args[2] : "");
display = NULL;
fore = NULL;
flayer = 0;
}
break;
}
case '#': /* window */
n--;
/* FALLTHROUGH */
default:
{
struct win *nw;
int ch;
n++;
ch = args[0][n];
args[0][n] = '\0';
if (!*args[0] || (i = WindowByNumber(args[0])) < 0)
{
args[0][n] = ch; /* must restore string in case of bind */
/* try looping over titles */
for (fore = windows; fore; fore = nw)
{
nw = fore->w_next;
if (strncmp(args[0], fore->w_title, n))
continue;
debug2("AT window %d(%s)\n", fore->w_number, fore->w_title);
/*
* consider this a bug or a feature:
* while looping through windows, we have fore AND
* display context. This will confuse users who try to
* set up loops inside of loops, but often allows to do
* what you mean, even when you adress your context wrong.
*/
i = 0;
/* XXX: other displays? */
if (fore->w_layer.l_cvlist)
display = fore->w_layer.l_cvlist->c_display;
flayer = fore->w_savelayer ? fore->w_savelayer : &fore->w_layer;
DoCommand(args + 1, argl + 1); /* may destroy our display */
if (fore && fore->w_layer.l_cvlist)
{
display = fore->w_layer.l_cvlist->c_display;
OutputMsg(0, "command from %s: %s %s",
s, args[1], args[2] ? args[2] : "");
}
}
display = NULL;
fore = NULL;
if (i < 0)
OutputMsg(0, "%s: at '%s': no such window.\n", rc_name, args[0]);
break;
}
else if (i < maxwin && (fore = wtab[i]))
{
args[0][n] = ch; /* must restore string in case of bind */
debug2("AT window %d (%s)\n", fore->w_number, fore->w_title);
if (fore->w_layer.l_cvlist)
display = fore->w_layer.l_cvlist->c_display;
flayer = fore->w_savelayer ? fore->w_savelayer : &fore->w_layer;
DoCommand(args + 1, argl + 1);
if (fore && fore->w_layer.l_cvlist)
{
display = fore->w_layer.l_cvlist->c_display;
OutputMsg(0, "command from %s: %s %s",
s, args[1], args[2] ? args[2] : "");
}
display = NULL;
fore = NULL;
}
else
OutputMsg(0, "%s: at [identifier][%%|*|#] command [args]", rc_name);
break;
}
}
free(s);
#ifdef MULTIUSER
EffectiveAclUser = NULL;
#endif
break;
#ifdef COPY_PASTE
case RC_READREG:
#ifdef ENCODINGS
i = fore ? fore->w_encoding : display ? display->d_encoding : 0;
if (args[0] && args[1] && !strcmp(args[0], "-e"))
{
i = FindEncoding(args[1]);
if (i == -1)
{
OutputMsg(0, "%s: readreg: unknown encoding", rc_name);
break;
}
args += 2;
}
#endif
/*
* Without arguments we prompt for a destination register.
* It will receive the copybuffer contents.
* This is not done by RC_PASTE, as we prompt for source
* (not dest) there.
*/
if ((s = *args) == NULL)
{
Input("Copy to register:", 1, INP_RAW, copy_reg_fn, NULL, 0);
break;
}
if (*argl != 1)
{
OutputMsg(0, "%s: copyreg: character, ^x, or (octal) \\032 expected.", rc_name);
break;
}
ch = args[0][0];
/*
* With two arguments we *really* read register contents from file
*/
if (args[1])
{
if (args[2])
{
OutputMsg(0, "%s: readreg: too many arguments", rc_name);
break;
}
if ((s = ReadFile(args[1], &n)))
{
struct plop *pp = plop_tab + (int)(unsigned char)ch;
if (pp->buf)
free(pp->buf);
pp->buf = s;
pp->len = n;
#ifdef ENCODINGS
pp->enc = i;
#endif
}
}
else
/*
* with one argument we copy the copybuffer into a specified register
* This could be done with RC_PASTE too, but is here to be consistent
* with the zero argument call.
*/
copy_reg_fn(&ch, 0, NULL);
break;
#endif
case RC_REGISTER:
#ifdef ENCODINGS
i = fore ? fore->w_encoding : display ? display->d_encoding : 0;
if (args[0] && args[1] && !strcmp(args[0], "-e"))
{
i = FindEncoding(args[1]);
if (i == -1)
{
OutputMsg(0, "%s: register: unknown encoding", rc_name);
break;
}
args += 2;
argc -= 2;
}
#endif
if (argc != 2)
{
OutputMsg(0, "%s: register: illegal number of arguments.", rc_name);
break;
}
if (*argl != 1)
{
OutputMsg(0, "%s: register: character, ^x, or (octal) \\032 expected.", rc_name);
break;
}
ch = args[0][0];
#ifdef COPY_PASTE
if (ch == '.')
{
if (user->u_plop.buf != NULL)
UserFreeCopyBuffer(user);
if (args[1] && args[1][0])
{
user->u_plop.buf = SaveStrn(args[1], argl[1]);
user->u_plop.len = argl[1];
#ifdef ENCODINGS
user->u_plop.enc = i;
#endif
}
}
else
#endif
{
struct plop *plp = plop_tab + (int)(unsigned char)ch;
if (plp->buf)
free(plp->buf);
plp->buf = SaveStrn(args[1], argl[1]);
plp->len = argl[1];
#ifdef ENCODINGS
plp->enc = i;
#endif
}
break;
case RC_PROCESS:
if ((s = *args) == NULL)
{
Input("Process register:", 1, INP_RAW, process_fn, NULL, 0);
break;
}
if (*argl != 1)
{
OutputMsg(0, "%s: process: character, ^x, or (octal) \\032 expected.", rc_name);
break;
}
ch = args[0][0];
process_fn(&ch, 0, NULL);
break;
case RC_STUFF:
s = *args;
if (!args[0])
{
Input("Stuff:", 100, INP_COOKED, StuffFin, NULL, 0);
break;
}
n = *argl;
if (args[1])
{
if (strcmp(s, "-k"))
{
OutputMsg(0, "%s: stuff: invalid option %s", rc_name, s);
break;
}
s = args[1];
for (i = T_CAPS; i < T_OCAPS; i++)
if (strcmp(term[i].tcname, s) == 0)
break;
if (i == T_OCAPS)
{
OutputMsg(0, "%s: stuff: unknown key '%s'", rc_name, s);
break;
}
#ifdef MAPKEYS
if (StuffKey(i - T_CAPS) == 0)
break;
#endif
s = display ? D_tcs[i].str : 0;
if (s == 0)
break;
n = strlen(s);
}
while(n)
LayProcess(&s, &n);
break;
case RC_REDISPLAY:
Activate(-1);
break;
case RC_WINDOWS:
ShowWindows(-1);
break;
case RC_VERSION:
OutputMsg(0, "screen %s", version);
break;
case RC_TIME:
if (*args)
{
timestring = SaveStr(*args);
break;
}
OutputMsg(0, "%s", MakeWinMsg(timestring, fore, '%'));
break;
case RC_INFO:
ShowInfo();
break;
case RC_DINFO:
ShowDInfo();
break;
case RC_COMMAND:
{
struct action *ktabp = ktab;
if (argc == 2 && !strcmp(*args, "-c"))
{
if ((ktabp = FindKtab(args[1], 0)) == 0)
{
OutputMsg(0, "Unknown command class '%s'", args[1]);
break;
}
}
if (D_ESCseen != ktab || ktabp != ktab)
{
if (D_ESCseen != ktabp)
{
D_ESCseen = ktabp;
WindowChanged(fore, 'E');
}
break;
}
if (D_ESCseen)
{
D_ESCseen = 0;
WindowChanged(fore, 'E');
}
}
/* FALLTHROUGH */
case RC_OTHER:
if (MoreWindows())
SwitchWindow(display && D_other ? D_other->w_number : NextWindow());
break;
case RC_META:
if (user->u_Esc == -1)
break;
ch = user->u_Esc;
s = &ch;
n = 1;
LayProcess(&s, &n);
break;
case RC_XON:
ch = Ctrl('q');
s = &ch;
n = 1;
LayProcess(&s, &n);
break;
case RC_XOFF:
ch = Ctrl('s');
s = &ch;
n = 1;
LayProcess(&s, &n);
break;
case RC_DEFBREAKTYPE:
case RC_BREAKTYPE:
{
static char *types[] = { "TIOCSBRK", "TCSBRK", "tcsendbreak", NULL };
extern int breaktype;
if (*args)
{
if (ParseNum(act, &n))
for (n = 0; n < (int)(sizeof(types)/sizeof(*types)); n++)
{
for (i = 0; i < 4; i++)
{
ch = args[0][i];
if (ch >= 'a' && ch <= 'z')
ch -= 'a' - 'A';
if (ch != types[n][i] && (ch + ('a' - 'A')) != types[n][i])
break;
}
if (i == 4)
break;
}
if (n < 0 || n >= (int)(sizeof(types)/sizeof(*types)))
OutputMsg(0, "%s invalid, chose one of %s, %s or %s", *args, types[0], types[1], types[2]);
else
{
breaktype = n;
OutputMsg(0, "breaktype set to (%d) %s", n, types[n]);
}
}
else
OutputMsg(0, "breaktype is (%d) %s", breaktype, types[breaktype]);
}
break;
case RC_POW_BREAK:
case RC_BREAK:
n = 0;
if (*args && ParseNum(act, &n))
break;
SendBreak(fore, n, nr == RC_POW_BREAK);
break;
#ifdef LOCK
case RC_LOCKSCREEN:
Detach(D_LOCK);
break;
#endif
case RC_WIDTH:
case RC_HEIGHT:
{
int w, h;
int what = 0;
i = 1;
if (*args && !strcmp(*args, "-w"))
what = 1;
else if (*args && !strcmp(*args, "-d"))
what = 2;
if (what)
args++;
if (what == 0 && flayer && !display)
what = 1;
if (what == 1)
{
if (!flayer)
{
OutputMsg(0, "%s: %s: window required", rc_name, comms[nr].name);
break;
}
w = flayer->l_width;
h = flayer->l_height;
}
else
{
if (!display)
{
OutputMsg(0, "%s: %s: display required", rc_name, comms[nr].name);
break;
}
w = D_width;
h = D_height;
}
if (*args && args[0][0] == '-')
{
OutputMsg(0, "%s: %s: unknown option %s", rc_name, comms[nr].name, *args);
break;
}
if (nr == RC_HEIGHT)
{
if (!*args)
{
#define H0height 42
#define H1height 24
if (h == H0height)
h = H1height;
else if (h == H1height)
h = H0height;
else if (h > (H0height + H1height) / 2)
h = H0height;
else
h = H1height;
}
else
{
h = atoi(*args);
if (args[1])
w = atoi(args[1]);
}
}
else
{
if (!*args)
{
if (w == Z0width)
w = Z1width;
else if (w == Z1width)
w = Z0width;
else if (w > (Z0width + Z1width) / 2)
w = Z0width;
else
w = Z1width;
}
else
{
w = atoi(*args);
if (args[1])
h = atoi(args[1]);
}
}
if (*args && args[1] && args[2])
{
OutputMsg(0, "%s: %s: too many arguments", rc_name, comms[nr].name);
break;
}
if (w <= 0)
{
OutputMsg(0, "Illegal width");
break;
}
if (h <= 0)
{
OutputMsg(0, "Illegal height");
break;
}
if (what == 1)
{
if (flayer->l_width == w && flayer->l_height == h)
break;
ResizeLayer(flayer, w, h, (struct display *)0);
break;
}
if (D_width == w && D_height == h)
break;
if (what == 2)
{
ChangeScreenSize(w, h, 1);
}
else
{
if (ResizeDisplay(w, h) == 0)
{
Activate(D_fore ? D_fore->w_norefresh : 0);
/* autofit */
ResizeLayer(D_forecv->c_layer, D_forecv->c_xe - D_forecv->c_xs + 1, D_forecv->c_ye - D_forecv->c_ys + 1, 0);
break;
}
if (h == D_height)
OutputMsg(0, "Your termcap does not specify how to change the terminal's width to %d.", w);
else if (w == D_width)
OutputMsg(0, "Your termcap does not specify how to change the terminal's height to %d.", h);
else
OutputMsg(0, "Your termcap does not specify how to change the terminal's resolution to %dx%d.", w, h);
}
}
break;
case RC_TITLE:
if (queryflag >= 0)
{
if (fore)
OutputMsg(0, "%s", fore->w_title);
else
queryflag = -1;
break;
}
if (*args == 0)
InputAKA();
else
ChangeAKA(fore, *args, strlen(*args));
break;
case RC_COLON:
Input(":", 100, INP_EVERY, Colonfin, NULL, 0);
if (*args && **args)
{
s = *args;
n = strlen(s);
LayProcess(&s, &n);
}
break;
case RC_LASTMSG:
if (D_status_lastmsg)
OutputMsg(0, "%s", D_status_lastmsg);
break;
case RC_SCREEN:
DoScreen("key", args);
break;
case RC_WRAP:
if (ParseSwitch(act, &fore->w_wrap) == 0 && msgok)
OutputMsg(0, "%cwrap", fore->w_wrap ? '+' : '-');
break;
case RC_FLOW:
if (*args)
{
if (args[0][0] == 'a')
{
fore->w_flow = (fore->w_flow & FLOW_AUTO) ? FLOW_AUTOFLAG |FLOW_AUTO|FLOW_NOW : FLOW_AUTOFLAG;
}
else
{
if (ParseOnOff(act, &n))
break;
fore->w_flow = (fore->w_flow & FLOW_AUTO) | n;
}
}
else
{
if (fore->w_flow & FLOW_AUTOFLAG)
fore->w_flow = (fore->w_flow & FLOW_AUTO) | FLOW_NOW;
else if (fore->w_flow & FLOW_NOW)
fore->w_flow &= ~FLOW_NOW;
else
fore->w_flow = fore->w_flow ? FLOW_AUTOFLAG|FLOW_AUTO|FLOW_NOW : FLOW_AUTOFLAG;
}
SetFlow(fore->w_flow & FLOW_NOW);
if (msgok)
OutputMsg(0, "%cflow%s", (fore->w_flow & FLOW_NOW) ? '+' : '-',
(fore->w_flow & FLOW_AUTOFLAG) ? "(auto)" : "");
break;
#ifdef MULTIUSER
case RC_DEFWRITELOCK:
if (args[0][0] == 'a')
nwin_default.wlock = WLOCK_AUTO;
else
{
if (ParseOnOff(act, &n))
break;
nwin_default.wlock = n ? WLOCK_ON : WLOCK_OFF;
}
break;
case RC_WRITELOCK:
if (*args)
{
if (args[0][0] == 'a')
{
fore->w_wlock = WLOCK_AUTO;
}
else
{
if (ParseOnOff(act, &n))
break;
fore->w_wlock = n ? WLOCK_ON : WLOCK_OFF;
}
/*
* user may have permission to change the writelock setting,
* but he may never aquire the lock himself without write permission
*/
if (!AclCheckPermWin(D_user, ACL_WRITE, fore))
fore->w_wlockuser = D_user;
}
OutputMsg(0, "writelock %s", (fore->w_wlock == WLOCK_AUTO) ? "auto" :
((fore->w_wlock == WLOCK_OFF) ? "off" : "on"));
break;
#endif
case RC_CLEAR:
ResetAnsiState(fore);
WriteString(fore, "\033[H\033[J", 6);
break;
case RC_RESET:
ResetAnsiState(fore);
#ifdef ZMODEM
if (fore->w_zdisplay)
zmodem_abort(fore, fore->w_zdisplay);
#endif
WriteString(fore, "\033c", 2);
break;
case RC_MONITOR:
n = fore->w_monitor != MON_OFF;
#ifdef MULTIUSER
if (display)
n = n && (ACLBYTE(fore->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id));
#endif
if (ParseSwitch(act, &n))
break;
if (n)
{
#ifdef MULTIUSER
if (display) /* we tell only this user */
ACLBYTE(fore->w_mon_notify, D_user->u_id) |= ACLBIT(D_user->u_id);
else
for (i = 0; i < maxusercount; i++)
ACLBYTE(fore->w_mon_notify, i) |= ACLBIT(i);
#endif
if (fore->w_monitor == MON_OFF)
fore->w_monitor = MON_ON;
OutputMsg(0, "Window %d (%s) is now being monitored for all activity.", fore->w_number, fore->w_title);
}
else
{
#ifdef MULTIUSER
if (display) /* we remove only this user */
ACLBYTE(fore->w_mon_notify, D_user->u_id)
&= ~ACLBIT(D_user->u_id);
else
for (i = 0; i < maxusercount; i++)
ACLBYTE(fore->w_mon_notify, i) &= ~ACLBIT(i);
for (i = maxusercount - 1; i >= 0; i--)
if (ACLBYTE(fore->w_mon_notify, i))
break;
if (i < 0)
#endif
fore->w_monitor = MON_OFF;
OutputMsg(0, "Window %d (%s) is no longer being monitored for activity.", fore->w_number, fore->w_title);
}
break;
#ifdef MULTI
case RC_DISPLAYS:
display_displays();
break;
#endif
case RC_WINDOWLIST:
if (!*args)
display_windows(0, WLIST_NUM, (struct win *)0);
else if (!strcmp(*args, "string"))
{
if (args[1])
{
if (wliststr)
free(wliststr);
wliststr = SaveStr(args[1]);
}
if (msgok)
OutputMsg(0, "windowlist string is '%s'", wliststr);
}
else if (!strcmp(*args, "title"))
{
if (args[1])
{
if (wlisttit)
free(wlisttit);
wlisttit = SaveStr(args[1]);
}
if (msgok)
OutputMsg(0, "windowlist title is '%s'", wlisttit);
}
else
{
int flag = 0;
int blank = 0;
for (i = 0; i < argc; i++)
if (!args[i])
continue;
else if (!strcmp(args[i], "-m"))
flag |= WLIST_MRU;
else if (!strcmp(args[i], "-b"))
blank = 1;
else if (!strcmp(args[i], "-g"))
flag |= WLIST_NESTED;
else
{
OutputMsg(0, "usage: windowlist [-b] [-g] [-m] [string [string] | title [title]]");
break;
}
if (i == argc)
display_windows(blank, flag, (struct win *)0);
}
break;
case RC_HELP:
if (argc == 2 && !strcmp(*args, "-c"))
{
struct action *ktabp;
if ((ktabp = FindKtab(args[1], 0)) == 0)
{
OutputMsg(0, "Unknown command class '%s'", args[1]);
break;
}
display_help(args[1], ktabp);
}
else
display_help((char *)0, ktab);
break;
case RC_LICENSE:
display_copyright();
break;
#ifdef COPY_PASTE
case RC_COPY:
if (flayer->l_layfn != &WinLf)
{
OutputMsg(0, "Must be on a window layer");
break;
}
MarkRoutine();
WindowChanged(fore, 'P');
break;
case RC_HISTORY:
{
static char *pasteargs[] = {".", 0};
static int pasteargl[] = {1};
if (flayer->l_layfn != &WinLf)
{
OutputMsg(0, "Must be on a window layer");
break;
}
if (GetHistory() == 0)
break;
if (user->u_plop.buf == NULL)
break;
args = pasteargs;
argl = pasteargl;
}
/*FALLTHROUGH*/
case RC_PASTE:
{
char *ss, *dbuf, dch;
int l = 0;
# ifdef ENCODINGS
int enc = -1;
# endif
/*
* without args we prompt for one(!) register to be pasted in the window
*/
if ((s = *args) == NULL)
{
Input("Paste from register:", 1, INP_RAW, ins_reg_fn, NULL, 0);
break;
}
if (args[1] == 0 && !fore) /* no window? */
break;
/*
* with two arguments we paste into a destination register
* (no window needed here).
*/
if (args[1] && argl[1] != 1)
{
OutputMsg(0, "%s: paste destination: character, ^x, or (octal) \\032 expected.",
rc_name);
break;
}
# ifdef ENCODINGS
else if (fore)
enc = fore->w_encoding;
# endif
/*
* measure length of needed buffer
*/
for (ss = s = *args; (ch = *ss); ss++)
{
if (ch == '.')
{
# ifdef ENCODINGS
if (enc == -1)
enc = user->u_plop.enc;
if (enc != user->u_plop.enc)
l += RecodeBuf((unsigned char *)user->u_plop.buf, user->u_plop.len, user->u_plop.enc, enc, (unsigned char *)0);
else
# endif
l += user->u_plop.len;
}
else
{
# ifdef ENCODINGS
if (enc == -1)
enc = plop_tab[(int)(unsigned char)ch].enc;
if (enc != plop_tab[(int)(unsigned char)ch].enc)
l += RecodeBuf((unsigned char *)plop_tab[(int)(unsigned char)ch].buf, plop_tab[(int)(unsigned char)ch].len, plop_tab[(int)(unsigned char)ch].enc, enc, (unsigned char *)0);
else
# endif
l += plop_tab[(int)(unsigned char)ch].len;
}
}
if (l == 0)
{
OutputMsg(0, "empty buffer");
break;
}
/*
* shortcut:
* if there is only one source and the destination is a window, then
* pass a pointer rather than duplicating the buffer.
*/
if (s[1] == 0 && args[1] == 0)
# ifdef ENCODINGS
if (enc == (*s == '.' ? user->u_plop.enc : plop_tab[(int)(unsigned char)*s].enc))
# endif
{
MakePaster(&fore->w_paster, *s == '.' ? user->u_plop.buf : plop_tab[(int)(unsigned char)*s].buf, l, 0);
break;
}
/*
* if no shortcut, we construct a buffer
*/
if ((dbuf = (char *)malloc(l)) == 0)
{
OutputMsg(0, "%s", strnomem);
break;
}
l = 0;
/*
* concatenate all sources into our own buffer, copy buffer is
* special and is skipped if no display exists.
*/
for (ss = s; (ch = *ss); ss++)
{
struct plop *pp = (ch == '.' ? &user->u_plop : &plop_tab[(int)(unsigned char)ch]);
#ifdef ENCODINGS
if (pp->enc != enc)
{
l += RecodeBuf((unsigned char *)pp->buf, pp->len, pp->enc, enc, (unsigned char *)dbuf + l);
continue;
}
#endif
bcopy(pp->buf, dbuf + l, pp->len);
l += pp->len;
}
/*
* when called with one argument we paste our buffer into the window
*/
if (args[1] == 0)
{
MakePaster(&fore->w_paster, dbuf, l, 1);
}
else
{
/*
* we have two arguments, the second is already in dch.
* use this as destination rather than the window.
*/
dch = args[1][0];
if (dch == '.')
{
if (user->u_plop.buf != NULL)
UserFreeCopyBuffer(user);
user->u_plop.buf = dbuf;
user->u_plop.len = l;
#ifdef ENCODINGS
user->u_plop.enc = enc;
#endif
}
else
{
struct plop *pp = plop_tab + (int)(unsigned char)dch;
if (pp->buf)
free(pp->buf);
pp->buf = dbuf;
pp->len = l;
#ifdef ENCODINGS
pp->enc = enc;
#endif
}
}
break;
}
case RC_WRITEBUF:
if (!user->u_plop.buf)
{
OutputMsg(0, "empty buffer");
break;
}
#ifdef ENCODINGS
{
struct plop oldplop;
oldplop = user->u_plop;
if (args[0] && args[1] && !strcmp(args[0], "-e"))
{
int enc, l;
char *newbuf;
enc = FindEncoding(args[1]);
if (enc == -1)
{
OutputMsg(0, "%s: writebuf: unknown encoding", rc_name);
break;
}
if (enc != oldplop.enc)
{
l = RecodeBuf((unsigned char *)oldplop.buf, oldplop.len, oldplop.enc, enc, (unsigned char *)0);
newbuf = malloc(l + 1);
if (!newbuf)
{
OutputMsg(0, "%s", strnomem);
break;
}
user->u_plop.len = RecodeBuf((unsigned char *)oldplop.buf, oldplop.len, oldplop.enc, enc, (unsigned char *)newbuf);
user->u_plop.buf = newbuf;
user->u_plop.enc = enc;
}
args += 2;
}
#endif
if (args[0] && args[1])
OutputMsg(0, "%s: writebuf: too many arguments", rc_name);
else
WriteFile(user, args[0], DUMP_EXCHANGE);
#ifdef ENCODINGS
if (user->u_plop.buf != oldplop.buf)
free(user->u_plop.buf);
user->u_plop = oldplop;
}
#endif
break;
case RC_READBUF:
#ifdef ENCODINGS
i = fore ? fore->w_encoding : display ? display->d_encoding : 0;
if (args[0] && args[1] && !strcmp(args[0], "-e"))
{
i = FindEncoding(args[1]);
if (i == -1)
{
OutputMsg(0, "%s: readbuf: unknown encoding", rc_name);
break;
}
args += 2;
}
#endif
if (args[0] && args[1])
{
OutputMsg(0, "%s: readbuf: too many arguments", rc_name);
break;
}
if ((s = ReadFile(args[0] ? args[0] : BufferFile, &n)))
{
if (user->u_plop.buf)
UserFreeCopyBuffer(user);
user->u_plop.len = n;
user->u_plop.buf = s;
#ifdef ENCODINGS
user->u_plop.enc = i;
#endif
}
break;
case RC_REMOVEBUF:
KillBuffers();
break;
case RC_IGNORECASE:
(void)ParseSwitch(act, &search_ic);
if (msgok)
OutputMsg(0, "Will %signore case in searches", search_ic ? "" : "not ");
break;
#endif /* COPY_PASTE */
case RC_ESCAPE:
if (*argl == 0)
SetEscape(user, -1, -1);
else if (*argl == 2)
SetEscape(user, (int)(unsigned char)args[0][0], (int)(unsigned char)args[0][1]);
else
{
OutputMsg(0, "%s: two characters required after escape.", rc_name);
break;
}
/* Change defescape if master user. This is because we only
* have one ktab.
*/
if (display && user != users)
break;
/* FALLTHROUGH */
case RC_DEFESCAPE:
if (*argl == 0)
SetEscape(NULL, -1, -1);
else if (*argl == 2)
SetEscape(NULL, (int)(unsigned char)args[0][0], (int)(unsigned char)args[0][1]);
else
{
OutputMsg(0, "%s: two characters required after defescape.", rc_name);
break;
}
#ifdef MAPKEYS
CheckEscape();
#endif
break;
case RC_CHDIR:
s = *args ? *args : home;
if (chdir(s) == -1)
OutputMsg(errno, "%s", s);
break;
case RC_SHELL:
case RC_DEFSHELL:
if (ParseSaveStr(act, &ShellProg) == 0)
ShellArgs[0] = ShellProg;
break;
case RC_HARDCOPYDIR:
if (*args)
(void)ParseSaveStr(act, &hardcopydir);
if (msgok)
OutputMsg(0, "hardcopydir is %s\n", hardcopydir && *hardcopydir ? hardcopydir : "");
break;
case RC_LOGFILE:
if (*args)
{
if (args[1] && !(strcmp(*args, "flush")))
{
log_flush = atoi(args[1]);
if (msgok)
OutputMsg(0, "log flush timeout set to %ds\n", log_flush);
break;
}
if (ParseSaveStr(act, &screenlogfile) || !msgok)
break;
}
OutputMsg(0, "logfile is '%s'", screenlogfile);
break;
case RC_LOGTSTAMP:
if (!*args || !strcmp(*args, "on") || !strcmp(*args, "off"))
{
if (ParseSwitch(act, &logtstamp_on) == 0 && msgok)
OutputMsg(0, "timestamps turned %s", logtstamp_on ? "on" : "off");
}
else if (!strcmp(*args, "string"))
{
if (args[1])
{
if (logtstamp_string)
free(logtstamp_string);
logtstamp_string = SaveStr(args[1]);
}
if (msgok)
OutputMsg(0, "logfile timestamp is '%s'", logtstamp_string);
}
else if (!strcmp(*args, "after"))
{
if (args[1])
{
logtstamp_after = atoi(args[1]);
if (!msgok)
break;
}
OutputMsg(0, "timestamp printed after %ds\n", logtstamp_after);
}
else
OutputMsg(0, "usage: logtstamp [after [n]|string [str]|on|off]");
break;
case RC_SHELLTITLE:
(void)ParseSaveStr(act, &nwin_default.aka);
break;
case RC_TERMCAP:
case RC_TERMCAPINFO:
case RC_TERMINFO:
if (!rc_name || !*rc_name)
OutputMsg(0, "Sorry, too late now. Place that in your .screenrc file.");
break;
case RC_SLEEP:
break; /* Already handled */
case RC_TERM:
s = NULL;
if (ParseSaveStr(act, &s))
break;
if (strlen(s) >= 20)
{
OutputMsg(0, "%s: term: argument too long ( < 20)", rc_name);
free(s);
break;
}
strcpy(screenterm, s);
free(s);
debug1("screenterm set to %s\n", screenterm);
MakeTermcap((display == 0));
debug("new termcap made\n");
break;
case RC_ECHO:
if (!msgok && (!rc_name || strcmp(rc_name, "-X")))
break;
/*
* user typed ^A:echo... well, echo isn't FinishRc's job,
* but as he wanted to test us, we show good will
*/
if (argc > 1 && !strcmp(*args, "-n"))
{
args++;
argc--;
}
s = *args;
if (argc > 1 && !strcmp(*args, "-p"))
{
args++;
argc--;
s = *args;
if (s)
s = MakeWinMsg(s, fore, '%');
}
if (s)
OutputMsg(0, "%s", s);
else
{
OutputMsg(0, "%s: 'echo [-n] [-p] \"string\"' expected.", rc_name);
queryflag = -1;
}
break;
case RC_BELL:
case RC_BELL_MSG:
if (*args == 0)
{
char buf[256];
AddXChars(buf, sizeof(buf), BellString);
OutputMsg(0, "bell_msg is '%s'", buf);
break;
}
(void)ParseSaveStr(act, &BellString);
break;
#ifdef COPY_PASTE
case RC_BUFFERFILE:
if (*args == 0)
BufferFile = SaveStr(DEFAULT_BUFFERFILE);
else if (ParseSaveStr(act, &BufferFile))
break;
if (msgok)
OutputMsg(0, "Bufferfile is now '%s'", BufferFile);
break;
#endif
case RC_ACTIVITY:
(void)ParseSaveStr(act, &ActivityString);
break;
#if defined(DETACH) && defined(POW_DETACH)
case RC_POW_DETACH_MSG:
if (*args == 0)
{
char buf[256];
AddXChars(buf, sizeof(buf), PowDetachString);
OutputMsg(0, "pow_detach_msg is '%s'", buf);
break;
}
(void)ParseSaveStr(act, &PowDetachString);
break;
#endif
#if defined(UTMPOK) && defined(LOGOUTOK)
case RC_LOGIN:
n = fore->w_slot != (slot_t)-1;
if (*args && !strcmp(*args, "always"))
{
fore->w_lflag = 3;
if (!displays && n)
SlotToggle(n);
break;
}
if (*args && !strcmp(*args, "attached"))
{
fore->w_lflag = 1;
if (!displays && n)
SlotToggle(0);
break;
}
if (ParseSwitch(act, &n) == 0)
SlotToggle(n);
break;
case RC_DEFLOGIN:
if (!strcmp(*args, "always"))
nwin_default.lflag |= 2;
else if (!strcmp(*args, "attached"))
nwin_default.lflag &= ~2;
else
(void)ParseOnOff(act, &nwin_default.lflag);
break;
#endif
case RC_DEFFLOW:
if (args[0] && args[1] && args[1][0] == 'i')
{
iflag = 1;
for (display = displays; display; display = display->d_next)
{
if (!D_flow)
continue;
#if defined(TERMIO) || defined(POSIX)
D_NewMode.tio.c_cc[VINTR] = D_OldMode.tio.c_cc[VINTR];
D_NewMode.tio.c_lflag |= ISIG;
#else /* TERMIO || POSIX */
D_NewMode.m_tchars.t_intrc = D_OldMode.m_tchars.t_intrc;
#endif /* TERMIO || POSIX */
SetTTY(D_userfd, &D_NewMode);
}
}
if (args[0] && args[0][0] == 'a')
nwin_default.flowflag = FLOW_AUTOFLAG;
else
(void)ParseOnOff(act, &nwin_default.flowflag);
break;
case RC_DEFWRAP:
(void)ParseOnOff(act, &nwin_default.wrap);
break;
case RC_DEFC1:
(void)ParseOnOff(act, &nwin_default.c1);
break;
#ifdef COLOR
case RC_DEFBCE:
(void)ParseOnOff(act, &nwin_default.bce);
break;
#endif
case RC_DEFGR:
(void)ParseOnOff(act, &nwin_default.gr);
break;
case RC_DEFMONITOR:
if (ParseOnOff(act, &n) == 0)
nwin_default.monitor = (n == 0) ? MON_OFF : MON_ON;
break;
case RC_DEFMOUSETRACK:
if (ParseOnOff(act, &n) == 0)
defmousetrack = (n == 0) ? 0 : 1000;
break;
case RC_MOUSETRACK:
if (!args[0])
{
OutputMsg(0, "Mouse tracking for this display is turned %s", D_mousetrack ? "on" : "off");
}
else if (ParseOnOff(act, &n) == 0)
{
D_mousetrack = n == 0 ? 0 : 1000;
if (D_fore)
MouseMode(D_fore->w_mouse);
}
break;
case RC_DEFSILENCE:
if (ParseOnOff(act, &n) == 0)
nwin_default.silence = (n == 0) ? SILENCE_OFF : SILENCE_ON;
break;
case RC_VERBOSE:
if (!*args)
OutputMsg(0, "W%s echo command when creating windows.",
VerboseCreate ? "ill" : "on't");
else if (ParseOnOff(act, &n) == 0)
VerboseCreate = n;
break;
case RC_HARDSTATUS:
if (display)
{
OutputMsg(0, "%s", ""); /* wait till mintime (keep gcc quiet) */
RemoveStatus();
}
if (args[0] && strcmp(args[0], "on") && strcmp(args[0], "off"))
{
struct display *olddisplay = display;
int old_use, new_use = -1;
s = args[0];
if (!strncmp(s, "always", 6))
s += 6;
if (!strcmp(s, "lastline"))
new_use = HSTATUS_LASTLINE;
else if (!strcmp(s, "ignore"))
new_use = HSTATUS_IGNORE;
else if (!strcmp(s, "message"))
new_use = HSTATUS_MESSAGE;
else if (!strcmp(args[0], "string"))
{
if (!args[1])
{
char buf[256];
AddXChars(buf, sizeof(buf), hstatusstring);
OutputMsg(0, "hardstatus string is '%s'", buf);
break;
}
}
else
{
OutputMsg(0, "%s: usage: hardstatus [always]lastline|ignore|message|string [string]", rc_name);
break;
}
if (new_use != -1)
{
hardstatusemu = new_use | (s == args[0] ? 0 : HSTATUS_ALWAYS);
for (display = displays; display; display = display->d_next)
{
RemoveStatus();
new_use = hardstatusemu & ~HSTATUS_ALWAYS;
if (D_HS && s == args[0])
new_use = HSTATUS_HS;
ShowHStatus((char *)0);
old_use = D_has_hstatus;
D_has_hstatus = new_use;
if ((new_use == HSTATUS_LASTLINE && old_use != HSTATUS_LASTLINE) || (new_use != HSTATUS_LASTLINE && old_use == HSTATUS_LASTLINE))
ChangeScreenSize(D_width, D_height, 1);
RefreshHStatus();
}
}
if (args[1])
{
if (hstatusstring)
free(hstatusstring);
hstatusstring = SaveStr(args[1]);
for (display = displays; display; display = display->d_next)
RefreshHStatus();
}
display = olddisplay;
break;
}
(void)ParseSwitch(act, &use_hardstatus);
if (msgok)
OutputMsg(0, "messages displayed on %s", use_hardstatus ? "hardstatus line" : "window");
break;
case RC_CAPTION:
if (strcmp(args[0], "always") == 0 || strcmp(args[0], "splitonly") == 0)
{
struct display *olddisplay = display;
captionalways = args[0][0] == 'a';
for (display = displays; display; display = display->d_next)
ChangeScreenSize(D_width, D_height, 1);
display = olddisplay;
}
else if (strcmp(args[0], "string") == 0)
{
if (!args[1])
{
char buf[256];
AddXChars(buf, sizeof(buf), captionstring);
OutputMsg(0, "caption string is '%s'", buf);
break;
}
}
else
{
OutputMsg(0, "%s: usage: caption always|splitonly|string ", rc_name);
break;
}
if (!args[1])
break;
if (captionstring)
free(captionstring);
captionstring = SaveStr(args[1]);
RedisplayDisplays(0);
break;
case RC_CONSOLE:
n = (console_window != 0);
if (ParseSwitch(act, &n))
break;
if (TtyGrabConsole(fore->w_ptyfd, n, rc_name))
break;
if (n == 0)
OutputMsg(0, "%s: releasing console %s", rc_name, HostName);
else if (console_window)
OutputMsg(0, "%s: stealing console %s from window %d (%s)", rc_name,
HostName, console_window->w_number, console_window->w_title);
else
OutputMsg(0, "%s: grabbing console %s", rc_name, HostName);
console_window = n ? fore : 0;
break;
case RC_ALLPARTIAL:
if (ParseOnOff(act, &all_norefresh))
break;
if (!all_norefresh && fore)
Activate(-1);
if (msgok)
OutputMsg(0, all_norefresh ? "No refresh on window change!\n" :
"Window specific refresh\n");
break;
case RC_PARTIAL:
(void)ParseSwitch(act, &n);
fore->w_norefresh = n;
break;
case RC_VBELL:
if (ParseSwitch(act, &visual_bell) || !msgok)
break;
if (visual_bell == 0)
OutputMsg(0, "switched to audible bell.");
else
OutputMsg(0, "switched to visual bell.");
break;
case RC_VBELLWAIT:
if (ParseNum1000(act, &VBellWait) == 0 && msgok)
OutputMsg(0, "vbellwait set to %.10g seconds", VBellWait/1000.);
break;
case RC_MSGWAIT:
if (ParseNum1000(act, &MsgWait) == 0 && msgok)
OutputMsg(0, "msgwait set to %.10g seconds", MsgWait/1000.);
break;
case RC_MSGMINWAIT:
if (ParseNum1000(act, &MsgMinWait) == 0 && msgok)
OutputMsg(0, "msgminwait set to %.10g seconds", MsgMinWait/1000.);
break;
case RC_SILENCEWAIT:
if (ParseNum(act, &SilenceWait))
break;
if (SilenceWait < 1)
SilenceWait = 1;
for (p = windows; p; p = p->w_next)
p->w_silencewait = SilenceWait;
if (msgok)
OutputMsg(0, "silencewait set to %d seconds", SilenceWait);
break;
case RC_NUMBER:
if (*args == 0)
OutputMsg(0, queryflag >= 0 ? "%d (%s)" : "This is window %d (%s).", fore->w_number, fore->w_title);
else
{
int old = fore->w_number;
int rel = 0, parse;
if (args[0][0] == '+')
rel = 1;
else if (args[0][0] == '-')
rel = -1;
if (rel)
++act->args[0];
parse = ParseNum(act, &n);
if (rel)
--act->args[0];
if (parse)
break;
if (rel > 0)
n += old;
else if (rel < 0)
n = old - n;
if (!WindowChangeNumber(fore, n))
{
/* Window number could not be changed. */
queryflag = -1;
return;
}
}
break;
case RC_SILENCE:
n = fore->w_silence != 0;
i = fore->w_silencewait;
if (args[0] && (args[0][0] == '-' || (args[0][0] >= '0' && args[0][0] <= '9')))
{
if (ParseNum(act, &i))
break;
n = i > 0;
}
else if (ParseSwitch(act, &n))
break;
if (n)
{
#ifdef MULTIUSER
if (display) /* we tell only this user */
ACLBYTE(fore->w_lio_notify, D_user->u_id) |= ACLBIT(D_user->u_id);
else
for (n = 0; n < maxusercount; n++)
ACLBYTE(fore->w_lio_notify, n) |= ACLBIT(n);
#endif
fore->w_silencewait = i;
fore->w_silence = SILENCE_ON;
SetTimeout(&fore->w_silenceev, fore->w_silencewait * 1000);
evenq(&fore->w_silenceev);
if (!msgok)
break;
OutputMsg(0, "The window is now being monitored for %d sec. silence.", fore->w_silencewait);
}
else
{
#ifdef MULTIUSER
if (display) /* we remove only this user */
ACLBYTE(fore->w_lio_notify, D_user->u_id)
&= ~ACLBIT(D_user->u_id);
else
for (n = 0; n < maxusercount; n++)
ACLBYTE(fore->w_lio_notify, n) &= ~ACLBIT(n);
for (i = maxusercount - 1; i >= 0; i--)
if (ACLBYTE(fore->w_lio_notify, i))
break;
if (i < 0)
#endif
{
fore->w_silence = SILENCE_OFF;
evdeq(&fore->w_silenceev);
}
if (!msgok)
break;
OutputMsg(0, "The window is no longer being monitored for silence.");
}
break;
#ifdef COPY_PASTE
case RC_DEFSCROLLBACK:
(void)ParseNum(act, &nwin_default.histheight);
break;
case RC_SCROLLBACK:
if (flayer->l_layfn == &MarkLf)
{
OutputMsg(0, "Cannot resize scrollback buffer in copy/scrollback mode.");
break;
}
(void)ParseNum(act, &n);
ChangeWindowSize(fore, fore->w_width, fore->w_height, n);
if (msgok)
OutputMsg(0, "scrollback set to %d", fore->w_histheight);
break;
#endif
case RC_SESSIONNAME:
if (*args == 0)
OutputMsg(0, "This session is named '%s'\n", SockName);
else
{
char buf[MAXPATHLEN];
s = 0;
if (ParseSaveStr(act, &s))
break;
if (!*s || strlen(s) + (SockName - SockPath) > MAXPATHLEN - 13 || index(s, '/'))
{
OutputMsg(0, "%s: bad session name '%s'\n", rc_name, s);
free(s);
break;
}
strncpy(buf, SockPath, SockName - SockPath);
sprintf(buf + (SockName - SockPath), "%d.%s", (int)getpid(), s);
free(s);
if ((access(buf, F_OK) == 0) || (errno != ENOENT))
{
OutputMsg(0, "%s: inappropriate path: '%s'.", rc_name, buf);
break;
}
if (rename(SockPath, buf))
{
OutputMsg(errno, "%s: failed to rename(%s, %s)", rc_name, SockPath, buf);
break;
}
debug2("rename(%s, %s) done\n", SockPath, buf);
strcpy(SockPath, buf);
MakeNewEnv();
WindowChanged((struct win *)0, 'S');
}
break;
case RC_SETENV:
if (!args[0] || !args[1])
{
debug1("RC_SETENV arguments missing: %s\n", args[0] ? args[0] : "");
InputSetenv(args[0]);
}
else
{
xsetenv(args[0], args[1]);
MakeNewEnv();
}
break;
case RC_UNSETENV:
unsetenv(*args);
MakeNewEnv();
break;
#ifdef COPY_PASTE
case RC_DEFSLOWPASTE:
(void)ParseNum(act, &nwin_default.slow);
break;
case RC_SLOWPASTE:
if (*args == 0)
OutputMsg(0, fore->w_slowpaste ?
"Slowpaste in window %d is %d milliseconds." :
"Slowpaste in window %d is unset.",
fore->w_number, fore->w_slowpaste);
else if (ParseNum(act, &fore->w_slowpaste) == 0 && msgok)
OutputMsg(0, fore->w_slowpaste ?
"Slowpaste in window %d set to %d milliseconds." :
"Slowpaste in window %d now unset.",
fore->w_number, fore->w_slowpaste);
break;
case RC_MARKKEYS:
if (CompileKeys(*args, *argl, mark_key_tab))
{
OutputMsg(0, "%s: markkeys: syntax error.", rc_name);
break;
}
debug1("markkeys %s\n", *args);
break;
# ifdef FONT
case RC_PASTEFONT:
if (ParseSwitch(act, &pastefont) == 0 && msgok)
OutputMsg(0, "Will %spaste font settings", pastefont ? "" : "not ");
break;
# endif
case RC_CRLF:
(void)ParseSwitch(act, &join_with_cr);
break;
case RC_COMPACTHIST:
if (ParseSwitch(act, &compacthist) == 0 && msgok)
OutputMsg(0, "%scompacting history lines", compacthist ? "" : "not ");
break;
#endif
#ifdef NETHACK
case RC_NETHACK:
(void)ParseOnOff(act, &nethackflag);
break;
#endif
case RC_HARDCOPY_APPEND:
(void)ParseOnOff(act, &hardcopy_append);
break;
case RC_VBELL_MSG:
if (*args == 0)
{
char buf[256];
AddXChars(buf, sizeof(buf), VisualBellString);
OutputMsg(0, "vbell_msg is '%s'", buf);
break;
}
(void)ParseSaveStr(act, &VisualBellString);
debug1(" new vbellstr '%s'\n", VisualBellString);
break;
case RC_DEFMODE:
if (ParseBase(act, *args, &n, 8, "octal"))
break;
if (n < 0 || n > 0777)
{
OutputMsg(0, "%s: mode: Invalid tty mode %o", rc_name, n);
break;
}
TtyMode = n;
if (msgok)
OutputMsg(0, "Ttymode set to %03o", TtyMode);
break;
case RC_AUTODETACH:
(void)ParseOnOff(act, &auto_detach);
break;
case RC_STARTUP_MESSAGE:
(void)ParseOnOff(act, &default_startup);
break;
#ifdef PASSWORD
case RC_PASSWORD:
if (*args)
{
n = (*user->u_password) ? 1 : 0;
if (user->u_password != NullStr) free((char *)user->u_password);
user->u_password = SaveStr(*args);
if (!strcmp(user->u_password, "none"))
{
if (n)
OutputMsg(0, "Password checking disabled");
free(user->u_password);
user->u_password = NullStr;
}
}
else
{
if (!fore)
{
OutputMsg(0, "%s: password: window required", rc_name);
break;
}
Input("New screen password:", 100, INP_NOECHO, pass1, display ? (char *)D_user : (char *)users, 0);
}
break;
#endif /* PASSWORD */
case RC_BIND:
{
struct action *ktabp = ktab;
int kflag = 0;
for (;;)
{
if (argc > 2 && !strcmp(*args, "-c"))
{
ktabp = FindKtab(args[1], 1);
if (ktabp == 0)
break;
args += 2;
argl += 2;
argc -= 2;
}
else if (argc > 1 && !strcmp(*args, "-k"))
{
kflag = 1;
args++;
argl++;
argc--;
}
else
break;
}
#ifdef MAPKEYS
if (kflag)
{
for (n = 0; n < KMAP_KEYS; n++)
if (strcmp(term[n + T_CAPS].tcname, *args) == 0)
break;
if (n == KMAP_KEYS)
{
OutputMsg(0, "%s: bind: unknown key '%s'", rc_name, *args);
break;
}
n += 256;
}
else
#endif
if (*argl != 1)
{
OutputMsg(0, "%s: bind: character, ^x, or (octal) \\032 expected.", rc_name);
break;
}
else
n = (unsigned char)args[0][0];
if (args[1])
{
if ((i = FindCommnr(args[1])) == RC_ILLEGAL)
{
OutputMsg(0, "%s: bind: unknown command '%s'", rc_name, args[1]);
break;
}
if (CheckArgNum(i, args + 2) < 0)
break;
ClearAction(&ktabp[n]);
SaveAction(ktabp + n, i, args + 2, argl + 2);
}
else
ClearAction(&ktabp[n]);
}
break;
#ifdef MAPKEYS
case RC_BINDKEY:
{
struct action *newact;
int newnr, fl = 0, kf = 0, af = 0, df = 0, mf = 0;
struct display *odisp = display;
int used = 0;
struct kmap_ext *kme = NULL;
for (; *args && **args == '-'; args++, argl++)
{
if (strcmp(*args, "-t") == 0)
fl = KMAP_NOTIMEOUT;
else if (strcmp(*args, "-k") == 0)
kf = 1;
else if (strcmp(*args, "-a") == 0)
af = 1;
else if (strcmp(*args, "-d") == 0)
df = 1;
else if (strcmp(*args, "-m") == 0)
mf = 1;
else if (strcmp(*args, "--") == 0)
{
args++;
argl++;
break;
}
else
{
OutputMsg(0, "%s: bindkey: invalid option %s", rc_name, *args);
return;
}
}
if (df && mf)
{
OutputMsg(0, "%s: bindkey: -d does not work with -m", rc_name);
break;
}
if (*args == 0)
{
if (mf)
display_bindkey("Edit mode", mmtab);
else if (df)
display_bindkey("Default", dmtab);
else
display_bindkey("User", umtab);
break;
}
if (kf == 0)
{
if (af)
{
OutputMsg(0, "%s: bindkey: -a only works with -k", rc_name);
break;
}
if (*argl == 0)
{
OutputMsg(0, "%s: bindkey: empty string makes no sense", rc_name);
break;
}
for (i = 0, kme = kmap_exts; i < kmap_extn; i++, kme++)
if (kme->str == 0)
{
if (args[1])
break;
}
else
if (*argl == (kme->fl & ~KMAP_NOTIMEOUT) && bcmp(kme->str, *args, *argl) == 0)
break;
if (i == kmap_extn)
{
if (!args[1])
{
OutputMsg(0, "%s: bindkey: keybinding not found", rc_name);
break;
}
kmap_extn += 8;
kmap_exts = (struct kmap_ext *)xrealloc((char *)kmap_exts, kmap_extn * sizeof(*kmap_exts));
kme = kmap_exts + i;
bzero((char *)kme, 8 * sizeof(*kmap_exts));
for (; i < kmap_extn; i++, kme++)
{
kme->str = 0;
kme->dm.nr = kme->mm.nr = kme->um.nr = RC_ILLEGAL;
kme->dm.args = kme->mm.args = kme->um.args = noargs;
kme->dm.argl = kme->mm.argl = kme->um.argl = 0;
}
i -= 8;
kme -= 8;
}
if (df == 0 && kme->dm.nr != RC_ILLEGAL)
used = 1;
if (mf == 0 && kme->mm.nr != RC_ILLEGAL)
used = 1;
if ((df || mf) && kme->um.nr != RC_ILLEGAL)
used = 1;
i += KMAP_KEYS + KMAP_AKEYS;
newact = df ? &kme->dm : mf ? &kme->mm : &kme->um;
}
else
{
for (i = T_CAPS; i < T_OCAPS; i++)
if (strcmp(term[i].tcname, *args) == 0)
break;
if (i == T_OCAPS)
{
OutputMsg(0, "%s: bindkey: unknown key '%s'", rc_name, *args);
break;
}
if (af && i >= T_CURSOR && i < T_OCAPS)
i -= T_CURSOR - KMAP_KEYS;
else
i -= T_CAPS;
newact = df ? &dmtab[i] : mf ? &mmtab[i] : &umtab[i];
}
if (args[1])
{
if ((newnr = FindCommnr(args[1])) == RC_ILLEGAL)
{
OutputMsg(0, "%s: bindkey: unknown command '%s'", rc_name, args[1]);
break;
}
if (CheckArgNum(newnr, args + 2) < 0)
break;
ClearAction(newact);
SaveAction(newact, newnr, args + 2, argl + 2);
if (kf == 0 && args[1])
{
if (kme->str)
free(kme->str);
kme->str = SaveStrn(*args, *argl);
kme->fl = fl | *argl;
}
}
else
ClearAction(newact);
for (display = displays; display; display = display->d_next)
remap(i, args[1] ? 1 : 0);
if (kf == 0 && !args[1])
{
if (!used && kme->str)
{
free(kme->str);
kme->str = 0;
kme->fl = 0;
}
}
display = odisp;
}
break;
case RC_MAPTIMEOUT:
if (*args)
{
if (ParseNum(act, &n))
break;
if (n < 0)
{
OutputMsg(0, "%s: maptimeout: illegal time %d", rc_name, n);
break;
}
maptimeout = n;
}
if (*args == 0 || msgok)
OutputMsg(0, "maptimeout is %dms", maptimeout);
break;
case RC_MAPNOTNEXT:
D_dontmap = 1;
break;
case RC_MAPDEFAULT:
D_mapdefault = 1;
break;
#endif
#ifdef MULTIUSER
case RC_ACLCHG:
case RC_ACLADD:
case RC_ADDACL:
case RC_CHACL:
UsersAcl(NULL, argc, args);
break;
case RC_ACLDEL:
if (UserDel(args[0], NULL))
break;
if (msgok)
OutputMsg(0, "%s removed from acl database", args[0]);
break;
case RC_ACLGRP:
/*
* modify a user to gain or lose rights granted to a group.
* This group is actually a normal user whose rights were defined
* with chacl in the usual way.
*/
if (args[1])
{
if (strcmp(args[1], "none")) /* link a user to another user */
{
if (AclLinkUser(args[0], args[1]))
break;
if (msgok)
OutputMsg(0, "User %s joined acl-group %s", args[0], args[1]);
}
else /* remove all groups from user */
{
struct acluser *u;
struct aclusergroup *g;
if (!(u = *FindUserPtr(args[0])))
break;
while ((g = u->u_group))
{
u->u_group = g->next;
free((char *)g);
}
}
}
else /* show all groups of user */
{
char buf[256], *p = buf;
int ngroups = 0;
struct acluser *u;
struct aclusergroup *g;
if (!(u = *FindUserPtr(args[0])))
{
if (msgok)
OutputMsg(0, "User %s does not exist.", args[0]);
break;
}
g = u->u_group;
while (g)
{
ngroups++;
sprintf(p, "%s ", g->u->u_name);
p += strlen(p);
if (p > buf+200)
break;
g = g->next;
}
if (ngroups)
*(--p) = '\0';
OutputMsg(0, "%s's group%s: %s.", args[0], (ngroups == 1) ? "" : "s",
(ngroups == 0) ? "none" : buf);
}
break;
case RC_ACLUMASK:
case RC_UMASK:
while ((s = *args++))
{
char *err = 0;
if (AclUmask(display ? D_user : users, s, &err))
OutputMsg(0, "umask: %s\n", err);
}
break;
case RC_MULTIUSER:
if (ParseOnOff(act, &n))
break;
multi = n ? "" : 0;
chsock();
if (msgok)
OutputMsg(0, "Multiuser mode %s", multi ? "enabled" : "disabled");
break;
#endif /* MULTIUSER */
#ifdef PSEUDOS
case RC_EXEC:
winexec(args);
break;
#endif
#ifdef MULTI
case RC_NONBLOCK:
i = D_nonblock >= 0;
if (*args && ((args[0][0] >= '0' && args[0][0] <= '9') || args[0][0] == '.'))
{
if (ParseNum1000(act, &i))
break;
}
else if (!ParseSwitch(act, &i))
i = i == 0 ? -1 : 1000;
else
break;
if (msgok && i == -1)
OutputMsg(0, "display set to blocking mode");
else if (msgok && i == 0)
OutputMsg(0, "display set to nonblocking mode, no timeout");
else if (msgok)
OutputMsg(0, "display set to nonblocking mode, %.10gs timeout", i/1000.);
D_nonblock = i;
if (D_nonblock <= 0)
evdeq(&D_blockedev);
break;
case RC_DEFNONBLOCK:
if (*args && ((args[0][0] >= '0' && args[0][0] <= '9') || args[0][0] == '.'))
{
if (ParseNum1000(act, &defnonblock))
break;
}
else if (!ParseOnOff(act, &defnonblock))
defnonblock = defnonblock == 0 ? -1 : 1000;
else
break;
if (display && *rc_name)
{
D_nonblock = defnonblock;
if (D_nonblock <= 0)
evdeq(&D_blockedev);
}
break;
#endif
case RC_GR:
#ifdef ENCODINGS
if (fore->w_gr == 2)
fore->w_gr = 0;
#endif
if (ParseSwitch(act, &fore->w_gr) == 0 && msgok)
OutputMsg(0, "Will %suse GR", fore->w_gr ? "" : "not ");
#ifdef ENCODINGS
if (fore->w_gr == 0 && fore->w_FontE)
fore->w_gr = 2;
#endif
break;
case RC_C1:
if (ParseSwitch(act, &fore->w_c1) == 0 && msgok)
OutputMsg(0, "Will %suse C1", fore->w_c1 ? "" : "not ");
break;
#ifdef COLOR
case RC_BCE:
if (ParseSwitch(act, &fore->w_bce) == 0 && msgok)
OutputMsg(0, "Will %serase with background color", fore->w_bce ? "" : "not ");
break;
#endif
#ifdef ENCODINGS
case RC_KANJI:
case RC_ENCODING:
#ifdef UTF8
if (*args && !strcmp(args[0], "-d"))
{
if (!args[1])
OutputMsg(0, "encodings directory is %s", screenencodings ? screenencodings : "");
else
{
free(screenencodings);
screenencodings = SaveStr(args[1]);
}
break;
}
if (*args && !strcmp(args[0], "-l"))
{
if (!args[1])
OutputMsg(0, "encoding: -l: argument required");
else if (LoadFontTranslation(-1, args[1]))
OutputMsg(0, "encoding: could not load utf8 encoding file");
else if (msgok)
OutputMsg(0, "encoding: utf8 encoding file loaded");
break;
}
#else
if (*args && (!strcmp(args[0], "-l") || !strcmp(args[0], "-d")))
{
if (msgok)
OutputMsg(0, "encoding: screen is not compiled for UTF-8.");
break;
}
#endif
for (i = 0; i < 2; i++)
{
if (args[i] == 0)
break;
if (!strcmp(args[i], "."))
continue;
n = FindEncoding(args[i]);
if (n == -1)
{
OutputMsg(0, "encoding: unknown encoding '%s'", args[i]);
break;
}
if (i == 0 && fore)
{
WinSwitchEncoding(fore, n);
ResetCharsets(fore);
}
else if (i && display)
D_encoding = n;
}
break;
case RC_DEFKANJI:
case RC_DEFENCODING:
n = FindEncoding(*args);
if (n == -1)
{
OutputMsg(0, "defencoding: unknown encoding '%s'", *args);
break;
}
nwin_default.encoding = n;
break;
#endif
#ifdef UTF8
case RC_DEFUTF8:
n = nwin_default.encoding == UTF8;
if (ParseSwitch(act, &n) == 0)
{
nwin_default.encoding = n ? UTF8 : 0;
if (msgok)
OutputMsg(0, "Will %suse UTF-8 encoding for new windows", n ? "" : "not ");
}
break;
case RC_UTF8:
for (i = 0; i < 2; i++)
{
if (i && args[i] == 0)
break;
if (args[i] == 0)
n = fore->w_encoding != UTF8;
else if (strcmp(args[i], "off") == 0)
n = 0;
else if (strcmp(args[i], "on") == 0)
n = 1;
else
{
OutputMsg(0, "utf8: illegal argument (%s)", args[i]);
break;
}
if (i == 0)
{
WinSwitchEncoding(fore, n ? UTF8 : 0);
if (msgok)
OutputMsg(0, "Will %suse UTF-8 encoding", n ? "" : "not ");
}
else if (display)
D_encoding = n ? UTF8 : 0;
if (args[i] == 0)
break;
}
break;
#endif
case RC_PRINTCMD:
if (*args)
{
if (printcmd)
free(printcmd);
printcmd = 0;
if (**args)
printcmd = SaveStr(*args);
}
if (*args == 0 || msgok)
{
if (printcmd)
OutputMsg(0, "using '%s' as print command", printcmd);
else
OutputMsg(0, "using termcap entries for printing");
break;
}
break;
case RC_DIGRAPH:
if (argl && argl[0] > 0 && argl[1] > 0)
{
if (argl[0] != 2)
{
OutputMsg(0, "Two characters expected to define a digraph");
break;
}
i = digraph_find(args[0]);
digraphs[i].d[0] = args[0][0];
digraphs[i].d[1] = args[0][1];
if (!parse_input_int(args[1], argl[1], &digraphs[i].value))
{
if (!(digraphs[i].value = atoi(args[1])))
{
if (!args[1][1])
digraphs[i].value = (int)args[1][0];
#ifdef UTF8
else
{
int t;
unsigned char *s = (unsigned char *)args[1];
digraphs[i].value = 0;
while (*s)
{
t = FromUtf8(*s++, &digraphs[i].value);
if (t == -1)
continue;
if (t == -2)
digraphs[i].value = 0;
else
digraphs[i].value = t;
break;
}
}
#endif
}
}
break;
}
Input("Enter digraph: ", 10, INP_EVERY, digraph_fn, NULL, 0);
if (*args && **args)
{
s = *args;
n = strlen(s);
LayProcess(&s, &n);
}
break;
case RC_DEFHSTATUS:
if (*args == 0)
{
char buf[256];
*buf = 0;
if (nwin_default.hstatus)
AddXChars(buf, sizeof(buf), nwin_default.hstatus);
OutputMsg(0, "default hstatus is '%s'", buf);
break;
}
(void)ParseSaveStr(act, &nwin_default.hstatus);
if (*nwin_default.hstatus == 0)
{
free(nwin_default.hstatus);
nwin_default.hstatus = 0;
}
break;
case RC_HSTATUS:
(void)ParseSaveStr(act, &fore->w_hstatus);
if (*fore->w_hstatus == 0)
{
free(fore->w_hstatus);
fore->w_hstatus = 0;
}
WindowChanged(fore, 'h');
break;
#ifdef FONT
case RC_DEFCHARSET:
case RC_CHARSET:
if (*args == 0)
{
char buf[256];
*buf = 0;
if (nwin_default.charset)
AddXChars(buf, sizeof(buf), nwin_default.charset);
OutputMsg(0, "default charset is '%s'", buf);
break;
}
n = strlen(*args);
if (n == 0 || n > 6)
{
OutputMsg(0, "%s: %s: string has illegal size.", rc_name, comms[nr].name);
break;
}
if (n > 4 && (
((args[0][4] < '0' || args[0][4] > '3') && args[0][4] != '.') ||
((args[0][5] < '0' || args[0][5] > '3') && args[0][5] && args[0][5] != '.')))
{
OutputMsg(0, "%s: %s: illegal mapping number.", rc_name, comms[nr].name);
break;
}
if (nr == RC_CHARSET)
{
SetCharsets(fore, *args);
break;
}
if (nwin_default.charset)
free(nwin_default.charset);
nwin_default.charset = SaveStr(*args);
break;
#endif
#ifdef COLOR
case RC_ATTRCOLOR:
s = args[0];
if (*s >= '0' && *s <= '9')
i = *s - '0';
else
for (i = 0; i < 8; i++)
if (*s == "dubrsBiI"[i])
break;
s++;
nr = 0;
if (*s && s[1] && !s[2])
{
if (*s == 'd' && s[1] == 'd')
nr = 3;
else if (*s == '.' && s[1] == 'd')
nr = 2;
else if (*s == 'd' && s[1] == '.')
nr = 1;
else if (*s != '.' || s[1] != '.')
s--;
s += 2;
}
if (*s || i < 0 || i >= 8)
{
OutputMsg(0, "%s: attrcolor: unknown attribute '%s'.", rc_name, args[0]);
break;
}
n = 0;
if (args[1])
n = ParseAttrColor(args[1], args[2], 1);
if (n == -1)
break;
attr2color[i][nr] = n;
n = 0;
for (i = 0; i < 8; i++)
if (attr2color[i][0] || attr2color[i][1] || attr2color[i][2] || attr2color[i][3])
n |= 1 << i;
nattr2color = n;
break;
#endif
case RC_RENDITION:
i = -1;
if (strcmp(args[0], "bell") == 0)
{
i = REND_BELL;
}
else if (strcmp(args[0], "monitor") == 0)
{
i = REND_MONITOR;
}
else if (strcmp(args[0], "silence") == 0)
{
i = REND_SILENCE;
}
else if (strcmp(args[0], "so") != 0)
{
OutputMsg(0, "Invalid option '%s' for rendition", args[0]);
break;
}
++args;
++argl;
if (i != -1)
{
renditions[i] = ParseAttrColor(args[0], args[1], 1);
WindowChanged((struct win *)0, 'w');
WindowChanged((struct win *)0, 'W');
WindowChanged((struct win *)0, 0);
break;
}
/* We are here, means we want to set the sorendition. */
/* FALLTHROUGH*/
case RC_SORENDITION:
i = 0;
if (*args)
{
i = ParseAttrColor(*args, args[1], 1);
if (i == -1)
break;
ApplyAttrColor(i, &mchar_so);
WindowChanged((struct win *)0, 0);
debug2("--> %x %x\n", mchar_so.attr, mchar_so.color);
}
if (msgok)
#ifdef COLOR
OutputMsg(0, "Standout attributes 0x%02x color 0x%02x", (unsigned char)mchar_so.attr, 0x99 ^ (unsigned char)mchar_so.color);
#else
OutputMsg(0, "Standout attributes 0x%02x ", (unsigned char)mchar_so.attr);
#endif
break;
case RC_SOURCE:
do_source(*args);
break;
#ifdef MULTIUSER
case RC_SU:
s = NULL;
if (!*args)
{
OutputMsg(0, "%s:%s screen login", HostName, SockPath);
InputSu(D_fore, &D_user, NULL);
}
else if (!args[1])
InputSu(D_fore, &D_user, args[0]);
else if (!args[2])
s = DoSu(&D_user, args[0], args[1], "\377");
else
s = DoSu(&D_user, args[0], args[1], args[2]);
if (s)
OutputMsg(0, "%s", s);
break;
#endif /* MULTIUSER */
case RC_SPLIT:
s = args[0];
if (s && !strcmp(s, "-v"))
AddCanvas(SLICE_HORI);
else
AddCanvas(SLICE_VERT);
Activate(-1);
break;
case RC_REMOVE:
RemCanvas();
Activate(-1);
break;
case RC_ONLY:
OneCanvas();
Activate(-1);
break;
case RC_FIT:
D_forecv->c_xoff = D_forecv->c_xs;
D_forecv->c_yoff = D_forecv->c_ys;
RethinkViewportOffsets(D_forecv);
ResizeLayer(D_forecv->c_layer, D_forecv->c_xe - D_forecv->c_xs + 1, D_forecv->c_ye - D_forecv->c_ys + 1, 0);
flayer = D_forecv->c_layer;
LaySetCursor();
break;
case RC_FOCUS:
{
struct canvas *cv = 0;
if (!*args || !strcmp(*args, "next"))
cv = D_forecv->c_next ? D_forecv->c_next : D_cvlist;
else if (!strcmp(*args, "prev"))
{
for (cv = D_cvlist; cv->c_next && cv->c_next != D_forecv; cv = cv->c_next)
;
}
else if (!strcmp(*args, "top"))
cv = D_cvlist;
else if (!strcmp(*args, "bottom"))
{
for (cv = D_cvlist; cv->c_next; cv = cv->c_next)
;
}
else if (!strcmp(*args, "up"))
cv = FindCanvas(D_forecv->c_xs, D_forecv->c_ys - 1);
else if (!strcmp(*args, "down"))
cv = FindCanvas(D_forecv->c_xs, D_forecv->c_ye + 2);
else if (!strcmp(*args, "left"))
cv = FindCanvas(D_forecv->c_xs - 1, D_forecv->c_ys);
else if (!strcmp(*args, "right"))
cv = FindCanvas(D_forecv->c_xe + 1, D_forecv->c_ys);
else
{
OutputMsg(0, "%s: usage: focus [next|prev|up|down|left|right|top|bottom]", rc_name);
break;
}
SetForeCanvas(display, cv);
}
break;
case RC_RESIZE:
i = 0;
if (D_forecv->c_slorient == SLICE_UNKN)
{
OutputMsg(0, "resize: need more than one region");
break;
}
for (; *args; args++)
{
if (!strcmp(*args, "-h"))
i |= RESIZE_FLAG_H;
else if (!strcmp(*args, "-v"))
i |= RESIZE_FLAG_V;
else if (!strcmp(*args, "-b"))
i |= RESIZE_FLAG_H | RESIZE_FLAG_V;
else if (!strcmp(*args, "-p"))
i |= D_forecv->c_slorient == SLICE_VERT ? RESIZE_FLAG_H : RESIZE_FLAG_V;
else if (!strcmp(*args, "-l"))
i |= RESIZE_FLAG_L;
else
break;
}
if (*args && args[1])
{
OutputMsg(0, "%s: usage: resize [-h] [-v] [-l] [num]\n", rc_name);
break;
}
if (*args)
ResizeRegions(*args, i);
else
Input(resizeprompts[i], 20, INP_EVERY, ResizeFin, (char*)0, i);
break;
case RC_SETSID:
(void)ParseSwitch(act, &separate_sids);
break;
case RC_EVAL:
args = SaveArgs(args);
for (i = 0; args[i]; i++)
{
if (args[i][0])
Colonfin(args[i], strlen(args[i]), (char *)0);
free(args[i]);
}
free(args);
break;
case RC_ALTSCREEN:
(void)ParseSwitch(act, &use_altscreen);
if (msgok)
OutputMsg(0, "Will %sdo alternate screen switching", use_altscreen ? "" : "not ");
break;
case RC_MAXWIN:
if (!args[0])
{
OutputMsg(0, "maximum windows allowed: %d", maxwin);
break;
}
if (ParseNum(act, &n))
break;
if (n < 1)
OutputMsg(0, "illegal maxwin number specified");
else if (n > 2048)
OutputMsg(0, "maximum 2048 windows allowed");
else if (n > maxwin && windows)
OutputMsg(0, "may increase maxwin only when there's no window");
else
{
if (!windows)
wtab = realloc(wtab, n * sizeof(struct win *));
maxwin = n;
}
break;
case RC_BACKTICK:
if (ParseBase(act, *args, &n, 10, "decimal"))
break;
if (!args[1])
setbacktick(n, 0, 0, (char **)0);
else
{
int lifespan, tick;
if (argc < 4)
{
OutputMsg(0, "%s: usage: backtick num [lifespan tick cmd args...]", rc_name);
break;
}
if (ParseBase(act, args[1], &lifespan, 10, "decimal"))
break;
if (ParseBase(act, args[2], &tick, 10, "decimal"))
break;
setbacktick(n, lifespan, tick, SaveArgs(args + 3));
}
WindowChanged(0, '`');
break;
case RC_BLANKER:
#ifdef BLANKER_PRG
if (blankerprg)
{
RunBlanker(blankerprg);
break;
}
#endif
ClearAll();
CursorVisibility(-1);
D_blocked = 4;
break;
#ifdef BLANKER_PRG
case RC_BLANKERPRG:
if (!args[0])
{
if (blankerprg)
{
char path[MAXPATHLEN];
char *p = path, **pp;
for (pp = blankerprg; *pp; pp++)
p += snprintf(p, sizeof(path) - (p - path) - 1, "%s ", *pp);
*(p - 1) = '\0';
OutputMsg(0, "blankerprg: %s", path);
}
else
OutputMsg(0, "No blankerprg set.");
break;
}
if (blankerprg)
{
char **pp;
for (pp = blankerprg; *pp; pp++)
free(*pp);
free(blankerprg);
blankerprg = 0;
}
if (args[0][0])
blankerprg = SaveArgs(args);
break;
#endif
case RC_IDLE:
if (*args)
{
struct display *olddisplay = display;
if (!strcmp(*args, "off"))
idletimo = 0;
else if (args[0][0])
idletimo = atoi(*args) * 1000;
if (argc > 1)
{
if ((i = FindCommnr(args[1])) == RC_ILLEGAL)
{
OutputMsg(0, "%s: idle: unknown command '%s'", rc_name, args[1]);
break;
}
if (CheckArgNum(i, args + 2) < 0)
break;
ClearAction(&idleaction);
SaveAction(&idleaction, i, args + 2, argl + 2);
}
for (display = displays; display; display = display->d_next)
ResetIdle();
display = olddisplay;
}
if (msgok)
{
if (idletimo)
OutputMsg(0, "idle timeout %ds, %s", idletimo / 1000, comms[idleaction.nr].name);
else
OutputMsg(0, "idle off");
}
break;
case RC_FOCUSMINSIZE:
for (i = 0; i < 2 && args[i]; i++)
{
if (!strcmp(args[i], "max") || !strcmp(args[i], "_"))
n = -1;
else
n = atoi(args[i]);
if (i == 0)
focusminwidth = n;
else
focusminheight = n;
}
if (msgok)
{
char b[2][20];
for (i = 0; i < 2; i++)
{
n = i == 0 ? focusminwidth : focusminheight;
if (n == -1)
strcpy(b[i], "max");
else
sprintf(b[i], "%d", n);
}
OutputMsg(0, "focus min size is %s %s\n", b[0], b[1]);
}
break;
case RC_GROUP:
if (*args)
{
fore->w_group = 0;
if (args[0][0])
{
fore->w_group = WindowByName(*args);
if (fore->w_group == fore || (fore->w_group && fore->w_group->w_type != W_TYPE_GROUP))
fore->w_group = 0;
}
WindowChanged((struct win *)0, 'w');
WindowChanged((struct win *)0, 'W');
WindowChanged((struct win *)0, 0);
}
if (msgok)
{
if (fore->w_group)
OutputMsg(0, "window group is %d (%s)\n", fore->w_group->w_number, fore->w_group->w_title);
else
OutputMsg(0, "window belongs to no group");
}
break;
case RC_LAYOUT:
// A number of the subcommands for "layout" are ignored, or not processed correctly when there
// is no attached display.
if (!strcmp(args[0], "title"))
{
if (!display)
{
if (!args[1]) // There is no display, and there is no new title. Ignore.
break;
if (!layout_attach || layout_attach == &layout_last_marker)
layout_attach = CreateLayout(args[1], 0);
else
RenameLayout(layout_attach, args[1]);
break;
}
if (!D_layout)
{
OutputMsg(0, "not on a layout");
break;
}
if (!args[1])
{
OutputMsg(0, "current layout is %d (%s)", D_layout->lay_number, D_layout->lay_title);
break;
}
RenameLayout(D_layout, args[1]);
}
else if (!strcmp(args[0], "number"))
{
if (!display)
{
if (args[1] && layout_attach && layout_attach != &layout_last_marker)
RenumberLayout(layout_attach, atoi(args[1]));
break;
}
if (!D_layout)
{
OutputMsg(0, "not on a layout");
break;
}
if (!args[1])
{
OutputMsg(0, "This is layout %d (%s).\n", D_layout->lay_number, D_layout->lay_title);
break;
}
RenumberLayout(D_layout, atoi(args[1]));
break;
}
else if (!strcmp(args[0], "autosave"))
{
if (!display)
{
if (args[1] && layout_attach && layout_attach != &layout_last_marker)
{
if (!strcmp(args[1], "on"))
layout_attach->lay_autosave = 1;
else if (!strcmp(args[1], "off"))
layout_attach->lay_autosave = 0;
}
break;
}
if (!D_layout)
{
OutputMsg(0, "not on a layout");
break;
}
if (args[1])
{
if (!strcmp(args[1], "on"))
D_layout->lay_autosave = 1;
else if (!strcmp(args[1], "off"))
D_layout->lay_autosave = 0;
else
{
OutputMsg(0, "invalid argument. Give 'on' or 'off");
break;
}
}
if (msgok)
OutputMsg(0, "autosave is %s", D_layout->lay_autosave ? "on" : "off");
}
else if (!strcmp(args[0], "new"))
{
char *t = args[1];
n = 0;
if (t)
{
while (*t >= '0' && *t <= '9')
t++;
if (t != args[1] && (!*t || *t == ':'))
{
n = atoi(args[1]);
if (*t)
t++;
}
else
t = args[1];
}
if (!t || !*t)
t = "layout";
NewLayout(t, n);
Activate(-1);
}
else if (!strcmp(args[0], "save"))
{
if (!args[1])
{
OutputMsg(0, "usage: layout save ");
break;
}
if (display)
SaveLayout(args[1], &D_canvas);
}
else if (!strcmp(args[0], "select"))
{
if (!display)
{
if (args[1])
layout_attach = FindLayout(args[1]);
break;
}
if (!args[1])
{
Input("Switch to layout: ", 20, INP_COOKED, SelectLayoutFin, NULL, 0);
break;
}
SelectLayoutFin(args[1], strlen(args[1]), (char *)0);
}
else if (!strcmp(args[0], "next"))
{
if (!display)
{
if (layout_attach && layout_attach != &layout_last_marker)
layout_attach = layout_attach->lay_next ? layout_attach->lay_next : layouts;;
break;
}
struct layout *lay = D_layout;
if (lay)
lay = lay->lay_next ? lay->lay_next : layouts;
else
lay = layouts;
if (!lay)
{
OutputMsg(0, "no layout defined");
break;
}
if (lay == D_layout)
break;
LoadLayout(lay, &D_canvas);
Activate(-1);
}
else if (!strcmp(args[0], "prev"))
{
struct layout *lay = display ? D_layout : layout_attach;
struct layout *target = lay;
if (lay)
{
for (lay = layouts; lay->lay_next && lay->lay_next != target; lay = lay->lay_next)
;
}
else
lay = layouts;
if (!display)
{
layout_attach = lay;
break;
}
if (!lay)
{
OutputMsg(0, "no layout defined");
break;
}
if (lay == D_layout)
break;
LoadLayout(lay, &D_canvas);
Activate(-1);
}
else if (!strcmp(args[0], "attach"))
{
if (!args[1])
{
if (!layout_attach)
OutputMsg(0, "no attach layout set");
else if (layout_attach == &layout_last_marker)
OutputMsg(0, "will attach to last layout");
else
OutputMsg(0, "will attach to layout %d (%s)", layout_attach->lay_number, layout_attach->lay_title);
break;
}
if (!strcmp(args[1], ":last"))
layout_attach = &layout_last_marker;
else if (!args[1][0])
layout_attach = 0;
else
{
struct layout *lay;
lay = FindLayout(args[1]);
if (!lay)
{
OutputMsg(0, "unknown layout '%s'", args[1]);
break;
}
layout_attach = lay;
}
}
else if (!strcmp(args[0], "show"))
{
ShowLayouts(-1);
}
else if (!strcmp(args[0], "remove"))
{
struct layout *lay = display ? D_layout : layouts;
if (args[1])
{
lay = layouts ? FindLayout(args[1]) : (struct layout *)0;
if (!lay)
{
OutputMsg(0, "unknown layout '%s'", args[1]);
break;
}
}
if (lay)
RemoveLayout(lay);
}
else if (!strcmp(args[0], "dump"))
{
if (!display)
OutputMsg(0, "Must have a display for 'layout dump'.");
else if (!LayoutDumpCanvas(&D_canvas, args[1] ? args[1] : "layout-dump"))
OutputMsg(errno, "Error dumping layout.");
else
OutputMsg(0, "Layout dumped to \"%s\"", args[1] ? args[1] : "layout-dump");
}
else
OutputMsg(0, "unknown layout subcommand");
break;
#ifdef DW_CHARS
case RC_CJKWIDTH:
if(ParseSwitch(act, &cjkwidth) == 0)
{
if(msgok)
OutputMsg(0, "Treat ambiguous width characters as %s width", cjkwidth ? "full" : "half");
}
break;
#endif
default:
#ifdef HAVE_BRAILLE
/* key == -2: input from braille keybord, msgok always 0 */
DoBrailleAction(act, key == -2 ? 0 : msgok);
#endif
break;
}
if (display != odisplay)
{
for (display = displays; display; display = display->d_next)
if (display == odisplay)
break;
}
}
#undef OutputMsg
void
DoCommand(argv, argl)
char **argv;
int *argl;
{
struct action act;
const char *cmd = *argv;
act.quiet = 0;
/* For now, we actually treat both 'supress error' and 'suppress normal message' as the
* same, and ignore all messages on either flag. If we wanted to do otherwise, we would
* need to change the definition of 'OutputMsg' slightly. */
if (*cmd == '@') /* Suppress error */
{
act.quiet |= 0x01;
cmd++;
}
if (*cmd == '-') /* Suppress normal message */
{
act.quiet |= 0x02;
cmd++;
}
if ((act.nr = FindCommnr(cmd)) == RC_ILLEGAL)
{
Msg(0, "%s: unknown command '%s'", rc_name, cmd);
return;
}
act.args = argv + 1;
act.argl = argl + 1;
DoAction(&act, -1);
}
static void
SaveAction(act, nr, args, argl)
struct action *act;
int nr;
char **args;
int *argl;
{
register int argc = 0;
char **pp;
int *lp;
if (args)
while (args[argc])
argc++;
if (argc == 0)
{
act->nr = nr;
act->args = noargs;
act->argl = 0;
return;
}
if ((pp = (char **)malloc((unsigned)(argc + 1) * sizeof(char **))) == 0)
Panic(0, "%s", strnomem);
if ((lp = (int *)malloc((unsigned)(argc) * sizeof(int *))) == 0)
Panic(0, "%s", strnomem);
act->nr = nr;
act->args = pp;
act->argl = lp;
while (argc--)
{
*lp = argl ? *argl++ : (int)strlen(*args);
*pp++ = SaveStrn(*args++, *lp++);
}
*pp = 0;
}
static char **
SaveArgs(args)
char **args;
{
register char **ap, **pp;
register int argc = 0;
while (args[argc])
argc++;
if ((pp = ap = (char **)malloc((unsigned)(argc + 1) * sizeof(char **))) == 0)
Panic(0, "%s", strnomem);
while (argc--)
*pp++ = SaveStr(*args++);
*pp = 0;
return ap;
}
/*
* buf is split into argument vector args.
* leading whitespace is removed.
* @!| abbreviations are expanded.
* the end of buffer is recognized by '\0' or an un-escaped '#'.
* " and ' are interpreted.
*
* argc is returned.
*/
int
Parse(buf, bufl, args, argl)
char *buf, **args;
int bufl, *argl;
{
register char *p = buf, **ap = args, *pp;
register int delim, argc;
int *lp = argl;
debug2("Parse %d %s\n", bufl, buf);
argc = 0;
pp = buf;
delim = 0;
for (;;)
{
*lp = 0;
while (*p && (*p == ' ' || *p == '\t'))
++p;
#ifdef PSEUDOS
if (argc == 0 && *p == '!')
{
*ap++ = "exec";
*lp++ = 4;
p++;
argc++;
continue;
}
#endif
if (*p == '\0' || *p == '#' || *p == '\n')
{
*p = '\0';
for (delim = 0; delim < argc; delim++)
debug1("-- %s\n", args[delim]);
args[argc] = 0;
return argc;
}
if (++argc >= MAXARGS)
{
Msg(0, "%s: too many tokens.", rc_name);
return 0;
}
*ap++ = pp;
debug1("- new arg %s\n", p);
while (*p)
{
if (*p == delim)
delim = 0;
else if (delim != '\'' && *p == '\\' && (p[1] == 'n' || p[1] == 'r' || p[1] == 't' || p[1] == '\'' || p[1] == '"' || p[1] == '\\' || p[1] == '$' || p[1] == '#' || p[1] == '^' || (p[1] >= '0' && p[1] <= '7')))
{
p++;
if (*p >= '0' && *p <= '7')
{
*pp = *p - '0';
if (p[1] >= '0' && p[1] <= '7')
{
p++;
*pp = (*pp << 3) | (*p - '0');
if (p[1] >= '0' && p[1] <= '7')
{
p++;
*pp = (*pp << 3) | (*p - '0');
}
}
pp++;
}
else
{
switch (*p)
{
case 'n': *pp = '\n'; break;
case 'r': *pp = '\r'; break;
case 't': *pp = '\t'; break;
default: *pp = *p; break;
}
pp++;
}
}
else if (delim != '\'' && *p == '$' && (p[1] == '{' || p[1] == ':' || (p[1] >= 'a' && p[1] <= 'z') || (p[1] >= 'A' && p[1] <= 'Z') || (p[1] >= '0' && p[1] <= '9') || p[1] == '_'))
{
char *ps, *pe, op, *v, xbuf[11], path[MAXPATHLEN];
int vl;
ps = ++p;
debug1("- var %s\n", ps);
p++;
while (*p)
{
if (*ps == '{' && *p == '}')
break;
if (*ps == ':' && *p == ':')
break;
if (*ps != '{' && *ps != ':' && (*p < 'a' || *p > 'z') && (*p < 'A' || *p > 'Z') && (*p < '0' || *p > '9') && *p != '_')
break;
p++;
}
pe = p;
if (*ps == '{' || *ps == ':')
{
if (!*p)
{
Msg(0, "%s: bad variable name.", rc_name);
return 0;
}
p++;
}
op = *pe;
*pe = 0;
debug1("- var is '%s'\n", ps);
if (*ps == ':')
v = gettermcapstring(ps + 1);
else
{
if (*ps == '{')
ps++;
v = xbuf;
if (!strcmp(ps, "TERM"))
v = display ? D_termname : "unknown";
else if (!strcmp(ps, "COLUMNS"))
sprintf(xbuf, "%d", display ? D_width : -1);
else if (!strcmp(ps, "LINES"))
sprintf(xbuf, "%d", display ? D_height : -1);
else if (!strcmp(ps, "PID"))
sprintf(xbuf, "%d", getpid());
else if (!strcmp(ps, "PWD"))
{
if (getcwd(path, sizeof(path) - 1) == 0)
v = "?";
else
v = path;
}
else if (!strcmp(ps, "STY"))
{
if ((v = strchr(SockName, '.'))) /* Skip the PID */
v++;
else
v = SockName;
}
else
v = getenv(ps);
}
*pe = op;
vl = v ? strlen(v) : 0;
if (vl)
{
debug1("- sub is '%s'\n", v);
if (p - pp < vl)
{
int right = buf + bufl - (p + strlen(p) + 1);
if (right > 0)
{
bcopy(p, p + right, strlen(p) + 1);
p += right;
}
}
if (p - pp < vl)
{
Msg(0, "%s: no space left for variable expansion.", rc_name);
return 0;
}
bcopy(v, pp, vl);
pp += vl;
}
continue;
}
else if (delim != '\'' && *p == '^' && p[1])
{
p++;
*pp++ = *p == '?' ? '\177' : *p & 0x1f;
}
else if (delim == 0 && (*p == '\'' || *p == '"'))
delim = *p;
else if (delim == 0 && (*p == ' ' || *p == '\t' || *p == '\n'))
break;
else
*pp++ = *p;
p++;
}
if (delim)
{
Msg(0, "%s: Missing %c quote.", rc_name, delim);
return 0;
}
if (*p)
p++;
*pp = 0;
debug2("- arg done, '%s' rest %s\n", ap[-1], p);
*lp++ = pp - ap[-1];
pp++;
}
}
void
SetEscape(u, e, me)
struct acluser *u;
int e, me;
{
if (u)
{
u->u_Esc = e;
u->u_MetaEsc = me;
}
else
{
if (users)
{
if (DefaultEsc >= 0)
ClearAction(&ktab[DefaultEsc]);
if (DefaultMetaEsc >= 0)
ClearAction(&ktab[DefaultMetaEsc]);
}
DefaultEsc = e;
DefaultMetaEsc = me;
if (users)
{
if (DefaultEsc >= 0)
{
ClearAction(&ktab[DefaultEsc]);
ktab[DefaultEsc].nr = RC_OTHER;
}
if (DefaultMetaEsc >= 0)
{
ClearAction(&ktab[DefaultMetaEsc]);
ktab[DefaultMetaEsc].nr = RC_META;
}
}
}
}
int
ParseSwitch(act, var)
struct action *act;
int *var;
{
if (*act->args == 0)
{
*var ^= 1;
return 0;
}
return ParseOnOff(act, var);
}
static int
ParseOnOff(act, var)
struct action *act;
int *var;
{
register int num = -1;
char **args = act->args;
if (args[1] == 0)
{
if (strcmp(args[0], "on") == 0)
num = 1;
else if (strcmp(args[0], "off") == 0)
num = 0;
}
if (num < 0)
{
Msg(0, "%s: %s: invalid argument. Give 'on' or 'off'", rc_name, comms[act->nr].name);
return -1;
}
*var = num;
return 0;
}
int
ParseSaveStr(act, var)
struct action *act;
char **var;
{
char **args = act->args;
if (*args == 0 || args[1])
{
Msg(0, "%s: %s: one argument required.", rc_name, comms[act->nr].name);
return -1;
}
if (*var)
free(*var);
*var = SaveStr(*args);
return 0;
}
int
ParseNum(act, var)
struct action *act;
int *var;
{
int i;
char *p, **args = act->args;
p = *args;
if (p == 0 || *p == 0 || args[1])
{
Msg(0, "%s: %s: invalid argument. Give one argument.",
rc_name, comms[act->nr].name);
return -1;
}
i = 0;
while (*p)
{
if (*p >= '0' && *p <= '9')
i = 10 * i + (*p - '0');
else
{
Msg(0, "%s: %s: invalid argument. Give numeric argument.",
rc_name, comms[act->nr].name);
return -1;
}
p++;
}
debug1("ParseNum got %d\n", i);
*var = i;
return 0;
}
static int
ParseNum1000(act, var)
struct action *act;
int *var;
{
int i;
char *p, **args = act->args;
int dig = 0;
p = *args;
if (p == 0 || *p == 0 || args[1])
{
Msg(0, "%s: %s: invalid argument. Give one argument.",
rc_name, comms[act->nr].name);
return -1;
}
i = 0;
while (*p)
{
if (*p >= '0' && *p <= '9')
{
if (dig < 4)
i = 10 * i + (*p - '0');
else if (dig == 4 && *p >= '5')
i++;
if (dig)
dig++;
}
else if (*p == '.' && !dig)
dig++;
else
{
Msg(0, "%s: %s: invalid argument. Give floating point argument.",
rc_name, comms[act->nr].name);
return -1;
}
p++;
}
if (dig == 0)
i *= 1000;
else
while (dig++ < 4)
i *= 10;
if (i < 0)
i = (int)((unsigned int)~0 >> 1);
debug1("ParseNum1000 got %d\n", i);
*var = i;
return 0;
}
static struct win *
WindowByName(s)
char *s;
{
struct win *p;
for (p = windows; p; p = p->w_next)
if (!strcmp(p->w_title, s))
return p;
for (p = windows; p; p = p->w_next)
if (!strncmp(p->w_title, s, strlen(s)))
return p;
return 0;
}
static int
WindowByNumber(str)
char *str;
{
int i;
char *s;
for (i = 0, s = str; *s; s++)
{
if (*s < '0' || *s > '9')
break;
i = i * 10 + (*s - '0');
}
return *s ? -1 : i;
}
/*
* Get window number from Name or Number string.
* Numbers are tried first, then names, a prefix match suffices.
* Be careful when assigning numeric strings as WindowTitles.
*/
int
WindowByNoN(str)
char *str;
{
int i;
struct win *p;
if ((i = WindowByNumber(str)) < 0 || i >= maxwin)
{
if ((p = WindowByName(str)))
return p->w_number;
return -1;
}
return i;
}
static int
ParseWinNum(act, var)
struct action *act;
int *var;
{
char **args = act->args;
int i = 0;
if (*args == 0 || args[1])
{
Msg(0, "%s: %s: one argument required.", rc_name, comms[act->nr].name);
return -1;
}
i = WindowByNoN(*args);
if (i < 0)
{
Msg(0, "%s: %s: invalid argument. Give window number or name.",
rc_name, comms[act->nr].name);
return -1;
}
debug1("ParseWinNum got %d\n", i);
*var = i;
return 0;
}
static int
ParseBase(act, p, var, base, bname)
struct action *act;
char *p;
int *var;
int base;
char *bname;
{
int i = 0;
int c;
if (*p == 0)
{
Msg(0, "%s: %s: empty argument.", rc_name, comms[act->nr].name);
return -1;
}
while ((c = *p++))
{
if (c >= 'a' && c <= 'z')
c -= 'a' - 'A';
if (c >= 'A' && c <= 'Z')
c -= 'A' - ('0' + 10);
c -= '0';
if (c < 0 || c >= base)
{
Msg(0, "%s: %s: argument is not %s.", rc_name, comms[act->nr].name, bname);
return -1;
}
i = base * i + c;
}
debug1("ParseBase got %d\n", i);
*var = i;
return 0;
}
static int
IsNum(s, base)
register char *s;
register int base;
{
for (base += '0'; *s; ++s)
if (*s < '0' || *s > base)
return 0;
return 1;
}
int
IsNumColon(s, base, p, psize)
int base, psize;
char *s, *p;
{
char *q;
if ((q = rindex(s, ':')) != 0)
{
strncpy(p, q + 1, psize - 1);
p[psize - 1] = '\0';
*q = '\0';
}
else
*p = '\0';
return IsNum(s, base);
}
void
SwitchWindow(n)
int n;
{
struct win *p;
debug1("SwitchWindow %d\n", n);
if (n < 0 || n >= maxwin)
{
ShowWindows(-1);
return;
}
if ((p = wtab[n]) == 0)
{
ShowWindows(n);
return;
}
if (display == 0)
{
fore = p;
return;
}
if (p == D_fore)
{
Msg(0, "This IS window %d (%s).", n, p->w_title);
return;
}
#ifdef MULTIUSER
if (AclCheckPermWin(D_user, ACL_READ, p))
{
Msg(0, "Access to window %d denied.", p->w_number);
return;
}
#endif
SetForeWindow(p);
Activate(fore->w_norefresh);
}
/*
* SetForeWindow changes the window in the input focus of the display.
* Puts window wi in canvas display->d_forecv.
*/
void
SetForeWindow(wi)
struct win *wi;
{
struct win *p;
if (display == 0)
{
fore = wi;
return;
}
p = Layer2Window(D_forecv->c_layer);
SetCanvasWindow(D_forecv, wi);
if (p)
WindowChanged(p, 'u');
if (wi)
WindowChanged(wi, 'u');
flayer = D_forecv->c_layer;
/* Activate called afterwards, so no RefreshHStatus needed */
}
/*****************************************************************/
/*
* Activate - make fore window active
* norefresh = -1 forces a refresh, disregard all_norefresh then.
*/
void
Activate(norefresh)
int norefresh;
{
debug1("Activate(%d)\n", norefresh);
if (display == 0)
return;
if (D_status)
{
Msg(0, "%s", ""); /* wait till mintime (keep gcc quiet) */
RemoveStatus();
}
if (MayResizeLayer(D_forecv->c_layer))
ResizeLayer(D_forecv->c_layer, D_forecv->c_xe - D_forecv->c_xs + 1, D_forecv->c_ye - D_forecv->c_ys + 1, display);
fore = D_fore;
if (fore)
{
/* XXX ? */
if (fore->w_monitor != MON_OFF)
fore->w_monitor = MON_ON;
fore->w_bell = BELL_ON;
WindowChanged(fore, 'f');
#if 0
if (ResizeDisplay(fore->w_width, fore->w_height))
{
debug2("Cannot resize from (%d,%d)", D_width, D_height);
debug2(" to (%d,%d) -> resize window\n", fore->w_width, fore->w_height);
DoResize(D_width, D_height);
}
#endif
}
Redisplay(norefresh + all_norefresh);
}
static int
NextWindow()
{
register struct win **pp;
int n = fore ? fore->w_number : maxwin;
struct win *group = fore ? fore->w_group : 0;
for (pp = fore ? wtab + n + 1 : wtab; pp != wtab + n; pp++)
{
if (pp == wtab + maxwin)
pp = wtab;
if (*pp)
{
if (!fore || group == (*pp)->w_group)
break;
}
}
if (pp == wtab + n)
return -1;
return pp - wtab;
}
static int
PreviousWindow()
{
register struct win **pp;
int n = fore ? fore->w_number : -1;
struct win *group = fore ? fore->w_group : 0;
for (pp = wtab + n - 1; pp != wtab + n; pp--)
{
if (pp == wtab - 1)
pp = wtab + maxwin - 1;
if (*pp)
{
if (!fore || group == (*pp)->w_group)
break;
}
}
if (pp == wtab + n)
return -1;
return pp - wtab;
}
static int
MoreWindows()
{
char *m = "No other window.";
if (windows && (fore == 0 || windows->w_next))
return 1;
if (fore == 0)
{
Msg(0, "No window available");
return 0;
}
Msg(0, m, fore->w_number); /* other arg for nethack */
return 0;
}
void
KillWindow(wi)
struct win *wi;
{
struct win **pp, *p;
struct canvas *cv;
int gotone;
struct layout *lay;
/*
* Remove window from linked list.
*/
for (pp = &windows; (p = *pp); pp = &p->w_next)
if (p == wi)
break;
ASSERT(p);
*pp = p->w_next;
wi->w_inlen = 0;
wtab[wi->w_number] = 0;
if (windows == 0)
{
FreeWindow(wi);
Finit(0);
}
/*
* switch to different window on all canvases
*/
for (display = displays; display; display = display->d_next)
{
gotone = 0;
for (cv = D_cvlist; cv; cv = cv->c_next)
{
if (Layer2Window(cv->c_layer) != wi)
continue;
/* switch to other window */
SetCanvasWindow(cv, FindNiceWindow(D_other, 0));
gotone = 1;
}
if (gotone)
{
#ifdef ZMODEM
if (wi->w_zdisplay == display)
{
D_blocked = 0;
D_readev.condpos = D_readev.condneg = 0;
}
#endif
Activate(-1);
}
}
/* do the same for the layouts */
for (lay = layouts; lay; lay = lay->lay_next)
UpdateLayoutCanvas(&lay->lay_canvas, wi);
FreeWindow(wi);
WindowChanged((struct win *)0, 'w');
WindowChanged((struct win *)0, 'W');
WindowChanged((struct win *)0, 0);
}
static void
LogToggle(on)
int on;
{
char buf[1024];
if ((fore->w_log != 0) == on)
{
if (display && !*rc_name)
Msg(0, "You are %s logging.", on ? "already" : "not");
return;
}
if (fore->w_log != 0)
{
Msg(0, "Logfile \"%s\" closed.", fore->w_log->name);
logfclose(fore->w_log);
fore->w_log = 0;
WindowChanged(fore, 'f');
return;
}
if (DoStartLog(fore, buf, sizeof(buf)))
{
Msg(errno, "Error opening logfile \"%s\"", buf);
return;
}
if (ftell(fore->w_log->fp) == 0)
Msg(0, "Creating logfile \"%s\".", fore->w_log->name);
else
Msg(0, "Appending to logfile \"%s\".", fore->w_log->name);
WindowChanged(fore, 'f');
}
char *
AddWindows(buf, len, flags, where)
char *buf;
int len;
int flags;
int where;
{
register char *s, *ss;
register struct win **pp, *p;
register char *cmd;
int l;
s = ss = buf;
if ((flags & 8) && where < 0)
{
*s = 0;
return ss;
}
for (pp = ((flags & 4) && where >= 0) ? wtab + where + 1: wtab; pp < wtab + maxwin; pp++)
{
int rend = -1;
if (pp - wtab == where && ss == buf)
ss = s;
if ((p = *pp) == 0)
continue;
if ((flags & 1) && display && p == D_fore)
continue;
if (display && D_fore && D_fore->w_group != p->w_group)
continue;
cmd = p->w_title;
l = strlen(cmd);
if (l > 20)
l = 20;
if (s - buf + l > len - 24)
break;
if (s > buf || (flags & 4))
{
*s++ = ' ';
*s++ = ' ';
}
if (p->w_number == where)
{
ss = s;
if (flags & 8)
break;
}
if (!(flags & 4) || where < 0 || ((flags & 4) && where < p->w_number))
{
if (p->w_monitor == MON_DONE && renditions[REND_MONITOR] != -1)
rend = renditions[REND_MONITOR];
else if ((p->w_bell == BELL_DONE || p->w_bell == BELL_FOUND) && renditions[REND_BELL] != -1)
rend = renditions[REND_BELL];
else if ((p->w_silence == SILENCE_FOUND || p->w_silence == SILENCE_DONE) && renditions[REND_SILENCE] != -1)
rend = renditions[REND_SILENCE];
}
if (rend != -1)
AddWinMsgRend(s, rend);
sprintf(s, "%d", p->w_number);
s += strlen(s);
if (display && p == D_fore)
*s++ = '*';
if (!(flags & 2))
{
if (display && p == D_other)
*s++ = '-';
s = AddWindowFlags(s, len, p);
}
*s++ = ' ';
strncpy(s, cmd, l);
s += l;
if (rend != -1)
AddWinMsgRend(s, -1);
}
*s = 0;
return ss;
}
char *
AddWindowFlags(buf, len, p)
char *buf;
int len;
struct win *p;
{
char *s = buf;
if (p == 0 || len < 12)
{
*s = 0;
return s;
}
#if 0
if (display && p == D_fore)
*s++ = '*';
if (display && p == D_other)
*s++ = '-';
#endif
if (p->w_layer.l_cvlist && p->w_layer.l_cvlist->c_lnext)
*s++ = '&';
if (p->w_monitor == MON_DONE
#ifdef MULTIUSER
&& (ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id))
#endif
)
*s++ = '@';
if (p->w_bell == BELL_DONE)
*s++ = '!';
#ifdef UTMPOK
if (p->w_slot != (slot_t) 0 && p->w_slot != (slot_t) -1)
*s++ = '$';
#endif
if (p->w_log != 0)
{
strcpy(s, "(L)");
s += 3;
}
if (p->w_ptyfd < 0 && p->w_type != W_TYPE_GROUP)
*s++ = 'Z';
*s = 0;
return s;
}
char *
AddOtherUsers(buf, len, p)
char *buf;
int len;
struct win *p;
{
struct display *d, *olddisplay = display;
struct canvas *cv;
char *s;
int l;
s = buf;
for (display = displays; display; display = display->d_next)
{
if (olddisplay && D_user == olddisplay->d_user)
continue;
for (cv = D_cvlist; cv; cv = cv->c_next)
if (Layer2Window(cv->c_layer) == p)
break;
if (!cv)
continue;
for (d = displays; d && d != display; d = d->d_next)
if (D_user == d->d_user)
break;
if (d && d != display)
continue;
if (len > 1 && s != buf)
{
*s++ = ',';
len--;
}
l = strlen(D_user->u_name);
if (l + 1 > len)
break;
strcpy(s, D_user->u_name);
s += l;
len -= l;
}
*s = 0;
display = olddisplay;
return s;
}
void
ShowWindows(where)
int where;
{
char buf[1024];
char *s, *ss;
if (display && where == -1 && D_fore)
where = D_fore->w_number;
ss = AddWindows(buf, sizeof(buf), 0, where);
s = buf + strlen(buf);
if (display && ss - buf > D_width / 2)
{
ss -= D_width / 2;
if (s - ss < D_width)
{
ss = s - D_width;
if (ss < buf)
ss = buf;
}
}
else
ss = buf;
Msg(0, "%s", ss);
}
static void
ShowInfo()
{
char buf[512], *p;
register struct win *wp = fore;
register int i;
if (wp == 0)
{
Msg(0, "(%d,%d)/(%d,%d) no window", D_x + 1, D_y + 1, D_width, D_height);
return;
}
p = buf;
if (buf < (p += GetAnsiStatus(wp, p)))
*p++ = ' ';
sprintf(p, "(%d,%d)/(%d,%d)",
wp->w_x + 1, wp->w_y + 1, wp->w_width, wp->w_height);
#ifdef COPY_PASTE
sprintf(p += strlen(p), "+%d", wp->w_histheight);
#endif
sprintf(p += strlen(p), " %c%sflow",
(wp->w_flow & FLOW_NOW) ? '+' : '-',
(wp->w_flow & FLOW_AUTOFLAG) ? "" :
((wp->w_flow & FLOW_AUTO) ? "(+)" : "(-)"));
if (!wp->w_wrap) sprintf(p += strlen(p), " -wrap");
if (wp->w_insert) sprintf(p += strlen(p), " ins");
if (wp->w_origin) sprintf(p += strlen(p), " org");
if (wp->w_keypad) sprintf(p += strlen(p), " app");
if (wp->w_log) sprintf(p += strlen(p), " log");
if (wp->w_monitor != MON_OFF
#ifdef MULTIUSER
&& (ACLBYTE(wp->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id))
#endif
)
sprintf(p += strlen(p), " mon");
if (wp->w_mouse) sprintf(p += strlen(p), " mouse");
#ifdef COLOR
if (wp->w_bce) sprintf(p += strlen(p), " bce");
#endif
if (!wp->w_c1) sprintf(p += strlen(p), " -c1");
if (wp->w_norefresh) sprintf(p += strlen(p), " nored");
p += strlen(p);
#ifdef FONT
# ifdef ENCODINGS
if (wp->w_encoding && (display == 0 || D_encoding != wp->w_encoding || EncodingDefFont(wp->w_encoding) <= 0))
{
*p++ = ' ';
strcpy(p, EncodingName(wp->w_encoding));
p += strlen(p);
}
# ifdef UTF8
if (wp->w_encoding != UTF8)
# endif
# endif
if (D_CC0 || (D_CS0 && *D_CS0))
{
if (wp->w_gr == 2)
{
sprintf(p, " G%c", wp->w_Charset + '0');
if (wp->w_FontE >= ' ')
p[3] = wp->w_FontE;
else
{
p[3] = '^';
p[4] = wp->w_FontE ^ 0x40;
p++;
}
p[4] = '[';
p++;
}
else if (wp->w_gr)
sprintf(p++, " G%c%c[", wp->w_Charset + '0', wp->w_CharsetR + '0');
else
sprintf(p, " G%c[", wp->w_Charset + '0');
p += 4;
for (i = 0; i < 4; i++)
{
if (wp->w_charsets[i] == ASCII)
*p++ = 'B';
else if (wp->w_charsets[i] >= ' ')
*p++ = wp->w_charsets[i];
else
{
*p++ = '^';
*p++ = wp->w_charsets[i] ^ 0x40;
}
}
*p++ = ']';
*p = 0;
}
#endif
if (wp->w_type == W_TYPE_PLAIN)
{
/* add info about modem control lines */
*p++ = ' ';
TtyGetModemStatus(wp->w_ptyfd, p);
}
#ifdef BUILTIN_TELNET
else if (wp->w_type == W_TYPE_TELNET)
{
*p++ = ' ';
TelStatus(wp, p, sizeof(buf) - 1 - (p - buf));
}
#endif
Msg(0, "%s %d(%s)", buf, wp->w_number, wp->w_title);
}
static void
ShowDInfo()
{
char buf[512], *p;
if (display == 0)
return;
p = buf;
sprintf(p, "(%d,%d)", D_width, D_height),
p += strlen(p);
#ifdef ENCODINGS
if (D_encoding)
{
*p++ = ' ';
strcpy(p, EncodingName(D_encoding));
p += strlen(p);
}
#endif
if (D_CXT)
{
strcpy(p, " xterm");
p += strlen(p);
}
#ifdef COLOR
if (D_hascolor)
{
strcpy(p, " color");
p += strlen(p);
}
#endif
#ifdef FONT
if (D_CG0)
{
strcpy(p, " iso2022");
p += strlen(p);
}
else if (D_CS0 && *D_CS0)
{
strcpy(p, " altchar");
p += strlen(p);
}
#endif
Msg(0, "%s", buf);
}
static void
AKAfin(buf, len, data)
char *buf;
int len;
char *data; /* dummy */
{
ASSERT(display);
if (len && fore)
ChangeAKA(fore, buf, strlen(buf));
}
static void
InputAKA()
{
char *s, *ss;
int n;
Input("Set window's title to: ", sizeof(fore->w_akabuf) - 1, INP_COOKED, AKAfin, NULL, 0);
s = fore->w_title;
if (!s)
return;
for (; *s; s++)
{
if ((*(unsigned char *)s & 0x7f) < 0x20 || *s == 0x7f)
continue;
ss = s;
n = 1;
LayProcess(&ss, &n);
}
}
static void
Colonfin(buf, len, data)
char *buf;
int len;
char *data; /* dummy */
{
char mbuf[256];
RemoveStatus();
if (buf[len] == '\t')
{
int m, x;
int l = 0, r = RC_LAST;
int showmessage = 0;
char *s = buf;
while (*s && s - buf < len)
if (*s++ == ' ')
return;
/* Showing a message when there's no hardstatus or caption cancels the input */
if (display &&
(captionalways || D_has_hstatus == HSTATUS_LASTLINE || (D_canvas.c_slperp && D_canvas.c_slperp->c_slnext)))
showmessage = 1;
while (l <= r)
{
m = (l + r) / 2;
x = strncmp(buf, comms[m].name, len);
if (x > 0)
l = m + 1;
else if (x < 0)
r = m - 1;
else
{
s = mbuf;
for (l = m - 1; l >= 0 && strncmp(buf, comms[l].name, len) == 0; l--)
;
for (m = ++l; m <= r && strncmp(buf, comms[m].name, len) == 0 && s - mbuf < sizeof(mbuf); m++)
s += snprintf(s, sizeof(mbuf) - (s - mbuf), " %s", comms[m].name);
if (l < m - 1)
{
if (showmessage)
Msg(0, "Possible commands:%s", mbuf);
}
else
{
s = mbuf;
len = snprintf(mbuf, sizeof(mbuf), "%s \t", comms[l].name + len);
if (len > 0 && len < sizeof(mbuf))
LayProcess(&s, &len);
}
break;
}
}
if (l > r && showmessage)
Msg(0, "No commands matching '%*s'", len, buf);
return;
}
if (!len || buf[len])
return;
len = strlen(buf) + 1;
if (len > (int)sizeof(mbuf))
RcLine(buf, len);
else
{
bcopy(buf, mbuf, len);
RcLine(mbuf, sizeof mbuf);
}
}
static void
SelectFin(buf, len, data)
char *buf;
int len;
char *data; /* dummy */
{
int n;
if (!len || !display)
return;
if (len == 1 && *buf == '-')
{
SetForeWindow((struct win *)0);
Activate(0);
return;
}
if ((n = WindowByNoN(buf)) < 0)
return;
SwitchWindow(n);
}
static void
SelectLayoutFin(buf, len, data)
char *buf;
int len;
char *data; /* dummy */
{
struct layout *lay;
if (!len || !display)
return;
if (len == 1 && *buf == '-')
{
LoadLayout((struct layout *)0, (struct canvas *)0);
Activate(0);
return;
}
lay = FindLayout(buf);
if (!lay)
Msg(0, "No such layout\n");
else if (lay == D_layout)
Msg(0, "This IS layout %d (%s).\n", lay->lay_number, lay->lay_title);
else
{
LoadLayout(lay, &D_canvas);
Activate(0);
}
}
static void
InputSelect()
{
Input("Switch to window: ", 20, INP_COOKED, SelectFin, NULL, 0);
}
static char setenv_var[31];
static void
SetenvFin1(buf, len, data)
char *buf;
int len;
char *data; /* dummy */
{
if (!len || !display)
return;
InputSetenv(buf);
}
static void
SetenvFin2(buf, len, data)
char *buf;
int len;
char *data; /* dummy */
{
if (!len || !display)
return;
debug2("SetenvFin2: setenv '%s' '%s'\n", setenv_var, buf);
xsetenv(setenv_var, buf);
MakeNewEnv();
}
static void
InputSetenv(arg)
char *arg;
{
static char setenv_buf[50 + sizeof(setenv_var)]; /* need to be static here, cannot be freed */
if (arg)
{
strncpy(setenv_var, arg, sizeof(setenv_var) - 1);
sprintf(setenv_buf, "Enter value for %s: ", setenv_var);
Input(setenv_buf, 30, INP_COOKED, SetenvFin2, NULL, 0);
}
else
Input("Setenv: Enter variable name: ", 30, INP_COOKED, SetenvFin1, NULL, 0);
}
/*
* the following options are understood by this parser:
* -f, -f0, -f1, -fy, -fa
* -t title, -T terminal-type, -h height-of-scrollback,
* -ln, -l0, -ly, -l1, -l
* -a, -M, -L
*/
void
DoScreen(fn, av)
char *fn, **av;
{
struct NewWindow nwin;
register int num;
char buf[20];
nwin = nwin_undef;
while (av && *av && av[0][0] == '-')
{
if (av[0][1] == '-')
{
av++;
break;
}
switch (av[0][1])
{
case 'f':
switch (av[0][2])
{
case 'n':
case '0':
nwin.flowflag = FLOW_NOW * 0;
break;
case 'y':
case '1':
case '\0':
nwin.flowflag = FLOW_NOW * 1;
break;
case 'a':
nwin.flowflag = FLOW_AUTOFLAG;
break;
default:
break;
}
break;
case 't': /* no more -k */
if (av[0][2])
nwin.aka = &av[0][2];
else if (*++av)
nwin.aka = *av;
else
--av;
break;
case 'T':
if (av[0][2])
nwin.term = &av[0][2];
else if (*++av)
nwin.term = *av;
else
--av;
break;
case 'h':
if (av[0][2])
nwin.histheight = atoi(av[0] + 2);
else if (*++av)
nwin.histheight = atoi(*av);
else
--av;
break;
#ifdef LOGOUTOK
case 'l':
switch (av[0][2])
{
case 'n':
case '0':
nwin.lflag = 0;
break;
case 'y':
case '1':
case '\0':
nwin.lflag = 1;
break;
case 'a':
nwin.lflag = 3;
break;
default:
break;
}
break;
#endif
case 'a':
nwin.aflag = 1;
break;
case 'M':
nwin.monitor = MON_ON;
break;
case 'L':
nwin.Lflag = 1;
break;
default:
Msg(0, "%s: screen: invalid option -%c.", fn, av[0][1]);
break;
}
++av;
}
num = 0;
if (av && *av && IsNumColon(*av, 10, buf, sizeof(buf)))
{
if (*buf != '\0')
nwin.aka = buf;
num = atoi(*av);
if (num < 0 || (maxwin && num > maxwin - 1) || (!maxwin && num > MAXWIN - 1))
{
Msg(0, "%s: illegal screen number %d.", fn, num);
num = 0;
}
nwin.StartAt = num;
++av;
}
if (av && *av)
{
nwin.args = av;
if (!nwin.aka)
nwin.aka = Filename(*av);
}
MakeWindow(&nwin);
}
#ifdef COPY_PASTE
/*
* CompileKeys must be called before Markroutine is first used.
* to initialise the keys with defaults, call CompileKeys(NULL, mark_key_tab);
*
* s is an ascii string in a termcap-like syntax. It looks like
* "j=u:k=d:l=r:h=l: =.:" and so on...
* this example rebinds the cursormovement to the keys u (up), d (down),
* l (left), r (right). placing a mark will now be done with ".".
*/
int
CompileKeys(s, sl, array)
char *s;
int sl;
unsigned char *array;
{
int i;
unsigned char key, value;
if (sl == 0)
{
for (i = 0; i < 256; i++)
array[i] = i;
return 0;
}
debug1("CompileKeys: '%s'\n", s);
while (sl)
{
key = *(unsigned char *)s++;
if (*s != '=' || sl < 3)
return -1;
sl--;
do
{
s++;
sl -= 2;
value = *(unsigned char *)s++;
array[value] = key;
}
while (*s == '=' && sl >= 2);
if (sl == 0)
break;
if (*s++ != ':')
return -1;
sl--;
}
return 0;
}
#endif /* COPY_PASTE */
/*
* Asynchronous input functions
*/
#if defined(DETACH) && defined(POW_DETACH)
static void
pow_detach_fn(buf, len, data)
char *buf;
int len;
char *data; /* dummy */
{
debug("pow_detach_fn called\n");
if (len)
{
*buf = 0;
return;
}
if (ktab[(int)(unsigned char)*buf].nr != RC_POW_DETACH)
{
if (display)
write(D_userfd, "\007", 1);
Msg(0, "Detach aborted.");
}
else
Detach(D_POWER);
}
#endif /* POW_DETACH */
#ifdef COPY_PASTE
static void
copy_reg_fn(buf, len, data)
char *buf;
int len;
char *data; /* dummy */
{
struct plop *pp = plop_tab + (int)(unsigned char)*buf;
if (len)
{
*buf = 0;
return;
}
if (pp->buf)
free(pp->buf);
pp->buf = 0;
pp->len = 0;
if (D_user->u_plop.len)
{
if ((pp->buf = (char *)malloc(D_user->u_plop.len)) == NULL)
{
Msg(0, "%s", strnomem);
return;
}
bcopy(D_user->u_plop.buf, pp->buf, D_user->u_plop.len);
}
pp->len = D_user->u_plop.len;
#ifdef ENCODINGS
pp->enc = D_user->u_plop.enc;
#endif
Msg(0, "Copied %d characters into register %c", D_user->u_plop.len, *buf);
}
static void
ins_reg_fn(buf, len, data)
char *buf;
int len;
char *data; /* dummy */
{
struct plop *pp = plop_tab + (int)(unsigned char)*buf;
if (len)
{
*buf = 0;
return;
}
if (!fore)
return; /* Input() should not call us w/o fore, but you never know... */
if (*buf == '.')
Msg(0, "ins_reg_fn: Warning: pasting real register '.'!");
if (pp->buf)
{
MakePaster(&fore->w_paster, pp->buf, pp->len, 0);
return;
}
Msg(0, "Empty register.");
}
#endif /* COPY_PASTE */
static void
process_fn(buf, len, data)
char *buf;
int len;
char *data; /* dummy */
{
struct plop *pp = plop_tab + (int)(unsigned char)*buf;
if (len)
{
*buf = 0;
return;
}
if (pp->buf)
{
ProcessInput(pp->buf, pp->len);
return;
}
Msg(0, "Empty register.");
}
static void
confirm_fn(buf, len, data)
char *buf;
int len;
char *data;
{
struct action act;
if (len || (*buf != 'y' && *buf != 'Y'))
{
*buf = 0;
return;
}
act.nr = *(int *)data;
act.args = noargs;
act.argl = 0;
act.quiet = 0;
DoAction(&act, -1);
}
#ifdef MULTIUSER
struct inputsu
{
struct acluser **up;
char name[24];
char pw1[130]; /* FreeBSD crypts to 128 bytes */
char pw2[130];
};
static void
su_fin(buf, len, data)
char *buf;
int len;
char *data;
{
struct inputsu *i = (struct inputsu *)data;
char *p;
int l;
if (!*i->name)
{ p = i->name; l = sizeof(i->name) - 1; }
else if (!*i->pw1)
{ strcpy(p = i->pw1, "\377"); l = sizeof(i->pw1) - 1; }
else
{ strcpy(p = i->pw2, "\377"); l = sizeof(i->pw2) - 1; }
if (buf && len)
strncpy(p, buf, 1 + (l < len) ? l : len);
if (!*i->name)
Input("Screen User: ", sizeof(i->name) - 1, INP_COOKED, su_fin, (char *)i, 0);
else if (!*i->pw1)
Input("User's UNIX Password: ", sizeof(i->pw1)-1, INP_COOKED|INP_NOECHO, su_fin, (char *)i, 0);
else if (!*i->pw2)
Input("User's Screen Password: ", sizeof(i->pw2)-1, INP_COOKED|INP_NOECHO, su_fin, (char *)i, 0);
else
{
if ((p = DoSu(i->up, i->name, i->pw2, i->pw1)))
Msg(0, "%s", p);
free((char *)i);
}
}
static int
InputSu(w, up, name)
struct win *w;
struct acluser **up;
char *name;
{
struct inputsu *i;
if (!(i = (struct inputsu *)calloc(1, sizeof(struct inputsu))))
return -1;
i->up = up;
if (name && *name)
su_fin(name, (int)strlen(name), (char *)i); /* can also initialise stuff */
else
su_fin((char *)0, 0, (char *)i);
return 0;
}
#endif /* MULTIUSER */
#ifdef PASSWORD
static void
pass1(buf, len, data)
char *buf;
int len;
char *data;
{
struct acluser *u = (struct acluser *)data;
if (!*buf)
return;
ASSERT(u);
if (u->u_password != NullStr)
free((char *)u->u_password);
u->u_password = SaveStr(buf);
bzero(buf, strlen(buf));
Input("Retype new password:", 100, INP_NOECHO, pass2, data, 0);
}
static void
pass2(buf, len, data)
char *buf;
int len;
char *data;
{
int st;
char salt[3];
struct acluser *u = (struct acluser *)data;
ASSERT(u);
if (!buf || strcmp(u->u_password, buf))
{
Msg(0, "[ Passwords don't match - checking turned off ]");
if (u->u_password != NullStr)
{
bzero(u->u_password, strlen(u->u_password));
free((char *)u->u_password);
}
u->u_password = NullStr;
}
else if (u->u_password[0] == '\0')
{
Msg(0, "[ No password - no secure ]");
if (buf)
bzero(buf, strlen(buf));
}
if (u->u_password != NullStr)
{
for (st = 0; st < 2; st++)
salt[st] = 'A' + (int)((time(0) >> 6 * st) % 26);
salt[2] = 0;
buf = crypt(u->u_password, salt);
bzero(u->u_password, strlen(u->u_password));
free((char *)u->u_password);
u->u_password = SaveStr(buf);
bzero(buf, strlen(buf));
#ifdef COPY_PASTE
if (u->u_plop.buf)
UserFreeCopyBuffer(u);
u->u_plop.len = strlen(u->u_password);
# ifdef ENCODINGS
u->u_plop.enc = 0;
#endif
if (!(u->u_plop.buf = SaveStr(u->u_password)))
{
Msg(0, "%s", strnomem);
D_user->u_plop.len = 0;
}
else
Msg(0, "[ Password moved into copybuffer ]");
#else /* COPY_PASTE */
Msg(0, "[ Crypted password is \"%s\" ]", u->u_password);
#endif /* COPY_PASTE */
}
}
#endif /* PASSWORD */
static int
digraph_find(buf)
const char *buf;
{
int i;
for (i = 0; i < MAX_DIGRAPH && digraphs[i].d[0]; i++)
if ((digraphs[i].d[0] == (unsigned char)buf[0] && digraphs[i].d[1] == (unsigned char)buf[1]) ||
(digraphs[i].d[0] == (unsigned char)buf[1] && digraphs[i].d[1] == (unsigned char)buf[0]))
break;
return i;
}
static void
digraph_fn(buf, len, data)
char *buf;
int len;
char *data; /* dummy */
{
int ch, i, x;
ch = buf[len];
if (ch)
{
buf[len + 1] = ch; /* so we can restore it later */
if (ch < ' ' || ch == '\177')
return;
if (len >= 1 && ((*buf == 'U' && buf[1] == '+') || (*buf == '0' && (buf[1] == 'x' || buf[1] == 'X'))))
{
if (len == 1)
return;
if ((ch < '0' || ch > '9') && (ch < 'a' || ch > 'f') && (ch < 'A' || ch > 'F'))
{
buf[len] = '\034'; /* ^] is ignored by Input() */
return;
}
if (len == (*buf == 'U' ? 5 : 3))
buf[len] = '\n';
return;
}
if (len && *buf == '0')
{
if (ch < '0' || ch > '7')
{
buf[len] = '\034'; /* ^] is ignored by Input() */
return;
}
if (len == 3)
buf[len] = '\n';
return;
}
if (len == 1)
buf[len] = '\n';
return;
}
if (len < 1)
return;
if (buf[len + 1])
{
buf[len] = buf[len + 1]; /* stored above */
len++;
}
if (len < 2)
return;
if (!parse_input_int(buf, len, &x))
{
i = digraph_find(buf);
if ((x = digraphs[i].value) <= 0)
{
Msg(0, "Unknown digraph");
return;
}
}
i = 1;
*buf = x;
#ifdef UTF8
if (flayer->l_encoding == UTF8)
i = ToUtf8(buf, x); /* buf is big enough for all UTF-8 codes */
#endif
while(i)
LayProcess(&buf, &i);
}
#ifdef MAPKEYS
int
StuffKey(i)
int i;
{
struct action *act;
int discard = 0;
debug1("StuffKey #%d", i);
#ifdef DEBUG
if (i < KMAP_KEYS)
debug1(" - %s", term[i + T_CAPS].tcname);
#endif
if (i < KMAP_KEYS && D_ESCseen)
{
struct action *act = &D_ESCseen[i + 256];
if (act->nr != RC_ILLEGAL)
{
D_ESCseen = 0;
WindowChanged(fore, 'E');
DoAction(act, i + 256);
return 0;
}
discard = 1;
}
if (i >= T_CURSOR - T_CAPS && i < T_KEYPAD - T_CAPS && D_cursorkeys)
i += T_OCAPS - T_CURSOR;
else if (i >= T_KEYPAD - T_CAPS && i < T_OCAPS - T_CAPS && D_keypad)
i += T_OCAPS - T_CURSOR;
debug1(" - action %d\n", i);
flayer = D_forecv->c_layer;
fore = D_fore;
act = 0;
#ifdef COPY_PASTE
if (flayer && flayer->l_mode == 1)
act = i < KMAP_KEYS+KMAP_AKEYS ? &mmtab[i] : &kmap_exts[i - (KMAP_KEYS+KMAP_AKEYS)].mm;
#endif
if ((!act || act->nr == RC_ILLEGAL) && !D_mapdefault)
act = i < KMAP_KEYS+KMAP_AKEYS ? &umtab[i] : &kmap_exts[i - (KMAP_KEYS+KMAP_AKEYS)].um;
if (!act || act->nr == RC_ILLEGAL)
act = i < KMAP_KEYS+KMAP_AKEYS ? &dmtab[i] : &kmap_exts[i - (KMAP_KEYS+KMAP_AKEYS)].dm;
if (discard && (!act || act->nr != RC_COMMAND))
{
if (D_ESCseen)
{
D_ESCseen = 0;
WindowChanged(fore, 'E');
}
return 0;
}
D_mapdefault = 0;
if (act == 0 || act->nr == RC_ILLEGAL)
return -1;
DoAction(act, 0);
return 0;
}
#endif
static int
IsOnDisplay(wi)
struct win *wi;
{
struct canvas *cv;
ASSERT(display);
for (cv = D_cvlist; cv; cv = cv->c_next)
if (Layer2Window(cv->c_layer) == wi)
return 1;
return 0;
}
struct win *
FindNiceWindow(wi, presel)
struct win *wi;
char *presel;
{
int i;
debug2("FindNiceWindow %d %s\n", wi ? wi->w_number : -1 , presel ? presel : "NULL");
if (presel)
{
i = WindowByNoN(presel);
if (i >= 0)
wi = wtab[i];
}
if (!display)
return wi;
#ifdef MULTIUSER
if (wi && AclCheckPermWin(D_user, ACL_READ, wi))
wi = 0;
#endif
if (!wi || (IsOnDisplay(wi) && !presel))
{
/* try to get another window */
wi = 0;
#ifdef MULTIUSER
for (wi = windows; wi; wi = wi->w_next)
if (!wi->w_layer.l_cvlist && !AclCheckPermWin(D_user, ACL_WRITE, wi))
break;
if (!wi)
for (wi = windows; wi; wi = wi->w_next)
if (wi->w_layer.l_cvlist && !IsOnDisplay(wi) && !AclCheckPermWin(D_user, ACL_WRITE, wi))
break;
if (!wi)
for (wi = windows; wi; wi = wi->w_next)
if (!wi->w_layer.l_cvlist && !AclCheckPermWin(D_user, ACL_READ, wi))
break;
if (!wi)
for (wi = windows; wi; wi = wi->w_next)
if (wi->w_layer.l_cvlist && !IsOnDisplay(wi) && !AclCheckPermWin(D_user, ACL_READ, wi))
break;
#endif
if (!wi)
for (wi = windows; wi; wi = wi->w_next)
if (!wi->w_layer.l_cvlist)
break;
if (!wi)
for (wi = windows; wi; wi = wi->w_next)
if (wi->w_layer.l_cvlist && !IsOnDisplay(wi))
break;
}
#ifdef MULTIUSER
if (wi && AclCheckPermWin(D_user, ACL_READ, wi))
wi = 0;
#endif
return wi;
}
#if 0
/* sorted list of all commands */
static struct comm **commtab;
static int ncommtab;
void
AddComms(cos, hand)
struct comm *cos;
void (*hand) __P((struct comm *, char **, int));
{
int n, i, j, r;
for (n = 0; cos[n].name; n++)
;
if (n == 0)
return;
if (commtab)
commtab = (struct commt *)realloc(commtab, sizeof(*commtab) * (ncommtab + n));
else
commtab = (struct commt *)malloc(sizeof(*commtab) * (ncommtab + n));
if (!commtab)
Panic(0, strnomem);
for (i = 0; i < n; i++)
{
for (j = 0; j < ncommtab; j++)
{
r = strcmp(cos[i].name, commtab[j]->name);
if (r == 0)
Panic(0, "Duplicate command: %s\n", cos[i].name);
if (r < 0)
break;
}
for (r = ncommtab; r > j; r--)
commtab[r] = commtab[r - 1];
commtab[j] = cos + i;
cos[i].handler = hand;
bzero(cos[i].userbits, sizeof(cos[i].userbits));
ncommtab++;
}
}
struct comm *
FindComm(str)
char *str;
{
int x, m, l = 0, r = ncommtab - 1;
while (l <= r)
{
m = (l + r) / 2;
x = strcmp(str, commtab[m]->name);
if (x > 0)
l = m + 1;
else if (x < 0)
r = m - 1;
else
return commtab[m];
}
return 0;
}
#endif
static int
CalcSlicePercent(cv, percent)
struct canvas *cv;
int percent;
{
int w, wsum, up;
if (!cv || !cv->c_slback)
return percent;
up = CalcSlicePercent(cv->c_slback->c_slback, percent);
w = cv->c_slweight;
for (cv = cv->c_slback->c_slperp, wsum = 0; cv; cv = cv->c_slnext)
wsum += cv->c_slweight;
if (wsum == 0)
return 0;
return (up * w) / wsum;
}
static int
ChangeCanvasSize(fcv, abs, diff, gflag, percent)
struct canvas *fcv; /* make this canvas bigger */
int abs; /* mode: 0:rel 1:abs 2:max */
int diff; /* change this much */
int gflag; /* go up if neccessary */
int percent;
{
struct canvas *cv;
int done, have, m, dir;
debug3("ChangeCanvasSize abs %d diff %d percent=%d\n", abs, diff, percent);
if (abs == 0 && diff == 0)
return 0;
if (abs == 2)
{
if (diff == 0)
fcv->c_slweight = 0;
else
{
for (cv = fcv->c_slback->c_slperp; cv; cv = cv->c_slnext)
cv->c_slweight = 0;
fcv->c_slweight = 1;
cv = fcv->c_slback->c_slback;
if (gflag && cv && cv->c_slback)
ChangeCanvasSize(cv, abs, diff, gflag, percent);
}
return diff;
}
if (abs)
{
if (diff < 0)
diff = 0;
if (percent && diff > percent)
diff = percent;
}
if (percent)
{
int wsum, up;
for (cv = fcv->c_slback->c_slperp, wsum = 0; cv; cv = cv->c_slnext)
wsum += cv->c_slweight;
if (wsum)
{
up = gflag ? CalcSlicePercent(fcv->c_slback->c_slback, percent) : percent;
debug3("up=%d, wsum=%d percent=%d\n", up, wsum, percent);
if (wsum < 1000)
{
int scale = wsum < 10 ? 1000 : 100;
for (cv = fcv->c_slback->c_slperp; cv; cv = cv->c_slnext)
cv->c_slweight *= scale;
wsum *= scale;
debug1("scaled wsum to %d\n", wsum);
}
for (cv = fcv->c_slback->c_slperp; cv; cv = cv->c_slnext)
{
if (cv->c_slweight)
{
cv->c_slweight = (cv->c_slweight * up) / percent;
if (cv->c_slweight == 0)
cv->c_slweight = 1;
}
debug1(" - weight %d\n", cv->c_slweight);
}
diff = (diff * wsum) / percent;
percent = wsum;
}
}
else
{
if (abs && diff == (fcv->c_slorient == SLICE_VERT ? fcv->c_ye - fcv->c_ys + 2 : fcv->c_xe - fcv->c_xs + 2))
return 0;
/* fix weights to real size (can't be helped, sorry) */
for (cv = fcv->c_slback->c_slperp; cv; cv = cv->c_slnext)
{
cv->c_slweight = cv->c_slorient == SLICE_VERT ? cv->c_ye - cv->c_ys + 2 : cv->c_xe - cv->c_xs + 2;
debug1(" - weight %d\n", cv->c_slweight);
}
}
if (abs)
diff = diff - fcv->c_slweight;
debug1("diff = %d\n", diff);
if (diff == 0)
return 0;
if (diff < 0)
{
cv = fcv->c_slnext ? fcv->c_slnext : fcv->c_slprev;
fcv->c_slweight += diff;
cv->c_slweight -= diff;
return diff;
}
done = 0;
dir = 1;
for (cv = fcv->c_slnext; diff > 0; cv = dir > 0 ? cv->c_slnext : cv->c_slprev)
{
if (!cv)
{
debug1("reached end, dir is %d\n", dir);
if (dir == -1)
break;
dir = -1;
cv = fcv;
continue;
}
if (percent)
m = 1;
else
m = cv->c_slperp ? CountCanvasPerp(cv) * 2 : 2;
debug2("min is %d, have %d\n", m, cv->c_slweight);
if (cv->c_slweight > m)
{
have = cv->c_slweight - m;
if (have > diff)
have = diff;
debug1("subtract %d\n", have);
cv->c_slweight -= have;
done += have;
diff -= have;
}
}
if (diff && gflag)
{
/* need more room! */
cv = fcv->c_slback->c_slback;
if (cv && cv->c_slback)
done += ChangeCanvasSize(fcv->c_slback->c_slback, 0, diff, gflag, percent);
}
fcv->c_slweight += done;
debug1("ChangeCanvasSize returns %d\n", done);
return done;
}
static void
ResizeRegions(arg, flags)
char *arg;
int flags;
{
struct canvas *cv;
int diff, l;
int gflag = 0, abs = 0, percent = 0;
int orient = 0;
ASSERT(display);
if (!*arg)
return;
if (D_forecv->c_slorient == SLICE_UNKN)
{
Msg(0, "resize: need more than one region");
return;
}
gflag = flags & RESIZE_FLAG_L ? 0 : 1;
orient |= flags & RESIZE_FLAG_H ? SLICE_HORI : 0;
orient |= flags & RESIZE_FLAG_V ? SLICE_VERT : 0;
if (orient == 0)
orient = D_forecv->c_slorient;
l = strlen(arg);
if (*arg == '=')
{
/* make all regions the same height */
struct canvas *cv = gflag ? &D_canvas : D_forecv->c_slback;
if (cv->c_slperp->c_slorient & orient)
EqualizeCanvas(cv->c_slperp, gflag);
/* can't use cv->c_slorient directly as it can be D_canvas */
if ((cv->c_slperp->c_slorient ^ (SLICE_HORI ^ SLICE_VERT)) & orient)
{
if (cv->c_slback)
{
cv = cv->c_slback;
EqualizeCanvas(cv->c_slperp, gflag);
}
else
EqualizeCanvas(cv, gflag);
}
ResizeCanvas(cv);
RecreateCanvasChain();
RethinkDisplayViewports();
ResizeLayersToCanvases();
return;
}
if (!strcmp(arg, "min") || !strcmp(arg, "0"))
{
abs = 2;
diff = 0;
}
else if (!strcmp(arg, "max") || !strcmp(arg, "_"))
{
abs = 2;
diff = 1;
}
else
{
if (l > 0 && arg[l - 1] == '%')
percent = 1000;
if (*arg == '+')
diff = atoi(arg + 1);
else if (*arg == '-')
diff = -atoi(arg + 1);
else
{
diff = atoi(arg); /* +1 because of caption line */
if (diff < 0)
diff = 0;
abs = diff == 0 ? 2 : 1;
}
}
if (!abs && !diff)
return;
if (percent)
diff = diff * percent / 100;
cv = D_forecv;
if (cv->c_slorient & orient)
ChangeCanvasSize(cv, abs, diff, gflag, percent);
if (cv->c_slback->c_slorient & orient)
ChangeCanvasSize(cv->c_slback, abs, diff, gflag, percent);
ResizeCanvas(&D_canvas);
RecreateCanvasChain();
RethinkDisplayViewports();
ResizeLayersToCanvases();
return;
#if 0
if (siz + diff < 1)
diff = 1 - siz;
if (siz + diff > dsize - (nreg - 1) * 2 - 1)
diff = dsize - (nreg - 1) * 2 - 1 - siz;
if (diff == 0 || siz + diff < 1)
return;
if (diff < 0)
{
if (D_forecv->c_next)
{
D_forecv->c_ye += diff;
D_forecv->c_next->c_ys += diff;
D_forecv->c_next->c_yoff += diff;
}
else
{
for (cv = D_cvlist; cv; cv = cv->c_next)
if (cv->c_next == D_forecv)
break;
ASSERT(cv);
cv->c_ye -= diff;
D_forecv->c_ys -= diff;
D_forecv->c_yoff -= diff;
}
}
else
{
int s, i = 0, found = 0, di = diff, d2;
s = dsize - (nreg - 1) * 2 - 1 - siz;
for (cv = D_cvlist; cv; i = cv->c_ye + 2, cv = cv->c_next)
{
if (cv == D_forecv)
{
cv->c_ye = i + (cv->c_ye - cv->c_ys) + diff;
cv->c_yoff -= cv->c_ys - i;
cv->c_ys = i;
found = 1;
continue;
}
s -= cv->c_ye - cv->c_ys;
if (!found)
{
if (s >= di)
continue;
d2 = di - s;
}
else
d2 = di > cv->c_ye - cv->c_ys ? cv->c_ye - cv->c_ys : di;
di -= d2;
cv->c_ye = i + (cv->c_ye - cv->c_ys) - d2;
cv->c_yoff -= cv->c_ys - i;
cv->c_ys = i;
}
}
RethinkDisplayViewports();
ResizeLayersToCanvases();
#endif
}
static void
ResizeFin(buf, len, data)
char *buf;
int len;
char *data;
{
int ch;
int flags = *(int *)data;
ch = ((unsigned char *)buf)[len];
if (ch == 0)
{
ResizeRegions(buf, flags);
return;
}
if (ch == 'h')
flags ^= RESIZE_FLAG_H;
else if (ch == 'v')
flags ^= RESIZE_FLAG_V;
else if (ch == 'b')
flags |= RESIZE_FLAG_H|RESIZE_FLAG_V;
else if (ch == 'p')
flags ^= D_forecv->c_slorient == SLICE_VERT ? RESIZE_FLAG_H : RESIZE_FLAG_V;
else if (ch == 'l')
flags ^= RESIZE_FLAG_L;
else
return;
inp_setprompt(resizeprompts[flags], NULL);
*(int *)data = flags;
buf[len] = '\034';
}
void
SetForeCanvas(d, cv)
struct display *d;
struct canvas *cv;
{
struct display *odisplay = display;
if (d->d_forecv == cv)
return;
display = d;
D_forecv = cv;
if ((focusminwidth && (focusminwidth < 0 || D_forecv->c_xe - D_forecv->c_xs + 1 < focusminwidth)) ||
(focusminheight && (focusminheight < 0 || D_forecv->c_ye - D_forecv->c_ys + 1 < focusminheight)))
{
ResizeCanvas(&D_canvas);
RecreateCanvasChain();
RethinkDisplayViewports();
ResizeLayersToCanvases(); /* redisplays */
}
fore = D_fore = Layer2Window(D_forecv->c_layer);
if (D_other == fore)
D_other = 0;
flayer = D_forecv->c_layer;
#ifdef RXVT_OSC
if (D_xtermosc[2] || D_xtermosc[3])
{
Activate(-1);
}
else
#endif
{
RefreshHStatus();
#ifdef RXVT_OSC
RefreshXtermOSC();
#endif
flayer = D_forecv->c_layer;
CV_CALL(D_forecv, LayRestore();LaySetCursor());
WindowChanged(0, 'F');
}
display = odisplay;
}
#ifdef RXVT_OSC
void
RefreshXtermOSC()
{
int i;
struct win *p;
p = Layer2Window(D_forecv->c_layer);
for (i = 3; i >=0; i--)
SetXtermOSC(i, p ? p->w_xtermosc[i] : 0);
}
#endif
int
ParseAttrColor(s1, s2, msgok)
char *s1, *s2;
int msgok;
{
int i, n;
char *s, *ss;
int r = 0;
s = s1;
while (*s == ' ')
s++;
ss = s;
while (*ss && *ss != ' ')
ss++;
while (*ss == ' ')
ss++;
if (*s && (s2 || *ss || !((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || *s == '.')))
{
int mode = 0, n = 0;
if (*s == '+')
{
mode = 1;
s++;
}
else if (*s == '-')
{
mode = -1;
s++;
}
else if (*s == '!')
{
mode = 2;
s++;
}
else if (*s == '=')
s++;
if (*s >= '0' && *s <= '9')
{
n = *s++ - '0';
if (*s >= '0' && *s <= '9')
n = n * 16 + (*s++ - '0');
else if (*s >= 'a' && *s <= 'f')
n = n * 16 + (*s++ - ('a' - 10));
else if (*s >= 'A' && *s <= 'F')
n = n * 16 + (*s++ - ('A' - 10));
else if (*s && *s != ' ')
{
if (msgok)
Msg(0, "Illegal attribute hexchar '%c'", *s);
return -1;
}
}
else
{
while (*s && *s != ' ')
{
if (*s == 'd')
n |= A_DI;
else if (*s == 'u')
n |= A_US;
else if (*s == 'b')
n |= A_BD;
else if (*s == 'r')
n |= A_RV;
else if (*s == 's')
n |= A_SO;
else if (*s == 'B')
n |= A_BL;
else
{
if (msgok)
Msg(0, "Illegal attribute specifier '%c'", *s);
return -1;
}
s++;
}
}
if (*s && *s != ' ')
{
if (msgok)
Msg(0, "junk after attribute description: '%c'", *s);
return -1;
}
if (mode == -1)
r = n << 8 | n;
else if (mode == 1)
r = n << 8;
else if (mode == 2)
r = n;
else if (mode == 0)
r = 0xffff ^ n;
}
while (*s && *s == ' ')
s++;
if (s2)
{
if (*s)
{
if (msgok)
Msg(0, "junk after description: '%c'", *s);
return -1;
}
s = s2;
while (*s && *s == ' ')
s++;
}
#ifdef COLOR
if (*s)
{
static char costr[] = "krgybmcw d i.01234567 9 f FKRGYBMCW I ";
int numco = 0, j;
n = 0;
if (*s == '.')
{
numco++;
n = 0x0f;
s++;
}
for (j = 0; j < 2 && *s && *s != ' '; j++)
{
for (i = 0; costr[i]; i++)
if (*s == costr[i])
break;
if (!costr[i])
{
if (msgok)
Msg(0, "illegal color descriptor: '%c'", *s);
return -1;
}
numco++;
n = n << 4 | (i & 15);
#ifdef COLORS16
if (i >= 48)
n = (n & 0x20ff) | 0x200;
#endif
s++;
}
if ((n & 0xf00) == 0xf00)
n ^= 0xf00; /* clear superflous bits */
#ifdef COLORS16
if (n & 0x2000)
n ^= 0x2400; /* shift bit into right position */
#endif
if (numco == 1)
n |= 0xf0; /* don't change bg color */
if (numco != 2 && n != 0xff)
n |= 0x100; /* special invert mode */
if (*s && *s != ' ')
{
if (msgok)
Msg(0, "junk after color description: '%c'", *s);
return -1;
}
n ^= 0xff;
r |= n << 16;
}
#endif
while (*s && *s == ' ')
s++;
if (*s)
{
if (msgok)
Msg(0, "junk after description: '%c'", *s);
return -1;
}
debug1("ParseAttrColor %06x\n", r);
return r;
}
/*
* Color coding:
* 0-7 normal colors
* 9 default color
* e just set intensity
* f don't change anything
* Intensity is encoded into bits 17(fg) and 18(bg).
*/
void
ApplyAttrColor(i, mc)
int i;
struct mchar *mc;
{
debug1("ApplyAttrColor %06x\n", i);
mc->attr |= i >> 8 & 255;
mc->attr ^= i & 255;
#ifdef COLOR
i = (i >> 16) ^ 0xff;
if ((i & 0x100) != 0)
{
i &= 0xeff;
if (mc->attr & (A_SO|A_RV))
# ifdef COLORS16
i = ((i & 0x0f) << 4) | ((i & 0xf0) >> 4) | ((i & 0x200) << 1) | ((i & 0x400) >> 1);
# else
i = ((i & 0x0f) << 4) | ((i & 0xf0) >> 4);
# endif
}
# ifdef COLORS16
if ((i & 0x0f) != 0x0f)
mc->attr = (mc->attr & 0xbf) | ((i >> 3) & 0x40);
if ((i & 0xf0) != 0xf0)
mc->attr = (mc->attr & 0x7f) | ((i >> 3) & 0x80);
# endif
mc->color = 0x99 ^ mc->color;
if ((i & 0x0e) == 0x0e)
i = (i & 0xf0) | (mc->color & 0x0f);
if ((i & 0xe0) == 0xe0)
i = (i & 0x0f) | (mc->color & 0xf0);
mc->color = 0x99 ^ i;
debug2("ApplyAttrColor - %02x %02x\n", mc->attr, i);
#endif
}
screen-4.1.0/patchlevel.h 0000644 0001750 0001750 00000101317 11642704565 013350 0 ustar abe abe /* 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
* http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
****************************************************************
* $Id$ GNU
*/
/****************************************************************
*
* patchlevel.h: Our life story.
*
* 08.07.91 -- 3.00.01 -wipe and a 'setenv TERM dumb' bugfix.
* 17.07.91 -- 3.00.02 another patchlevel by Wayne Davison
* 31.07.91 -- 3.00.03 E0, S0, C0 for flexible semi-graphics, nonblocking
* window title input and 'C-a :' command input.
* 10.08.91 -- 3.00.04 scrollback, markkeys and some bugfixes.
* 13.08.91 -- 3.00.05 mark routine improved, ansi prototypes added.
* 20.08.91 -- 3.00.06 screen -h, faster GotoPos in overlay, termcap %.
* instead of %c
* 28.08.91 -- 3.00.07 environment variable support. security. terminfo.
* pyramid and ultrix support.
* 07.09.91 -- 3.00.99 secopen(), MIPS support, SVR4 support.
* 09.09.91 -- 3.01.00 backspace bug fixed.
* 03.10.91 -- 3.01.01 ansi.c: null-ptr fixed, CLS now saves to scrollback.
* Using setresuid on hpux. Memory leak fixed.
* Better GotoPos(). Support for IC. Another resize bug.
* Detach() w/o fore crashed. -T and -A(dapt) option.
* GNU copyleft.
* 19.12.91 -- 3.01.02 flow now really automatic (autoflow killed).
* 7 bit restriction removed from WriteString().
* 09.01.92 -- 3.01.03 flow reattach bug fixed. VDISCARD bug fixed.
* 13.01.92 -- 3.01.04 new flow concept: ^Af toggles now three states
* 21.01.92 -- 3.01.05 '^A:screen 11' bug fixed. aflag in DoScreen().
* Some code cleanup. attach_tty and display_tty[]
* added.
* 26.01.92 -- 3.01.06 apollo support, "hardcopy_append on", "bufferfile",
* SECURITY PROBLEM cleared..
* 28.01.92 -- 3.01.07 screen after su allowed. Pid became part of
* SockName. sysvish 14 character restriction considered.
* 31.01.92 -- 3.02.00 Ultrix port, Irix 3.3 SGI port, shadow pw support,
* data loss on stdin overflow fixed. "refresh off".
* 12.02.92 -- 3.02.02 stripdev() moved, -S introduced, bufferfile improved,
* ShellProg coredump cleared. SVR4 bugfixes.
* I/O code speedup added.
* 24.04.92 -- 3.02.03 perfectly stackable overlays. One scrollback per window,
* not per display.
* 05.05.92 -- 3.02.04 very nasty initialisation bug fixed.
* 09.05.92 -- 3.02.05 parsing for $:cl: termcap strings and \012 octal notation
* in screenrc file. More structuring. Detached startup
* with 'screen -d -m -S...' bugfixed.
* 11.05.92 -- 3.02.06 setreuid() bugs cleared, C-a : setenv added.
* "xn" capability in TERMCAP needed since "am" is there.
* 25.06.92 -- 3.02.07 The multi display test version. Have merci.
* 15.07.92 -- 3.02.08 :B8: supports automatic charset switching for 8-bit
* 26.09.92 -- 3.02.09 Ported to linux. Ignoring bad files in $SCREENDIR
* 22.10.92 -- 3.02.10 screen.c/ansi.c splitted in several pieces.
* Better ISearch. Cleanup of loadav.c
* 29.10.92 -- 3.02.11 Key mechanism rewritten. New command names.
* New iscreenrc syntax.
* 02.11.92 -- 3.02.12 'bind g copy_reg' and 'bind x ins_reg' as suggested by
* stillson@tsfsrv.mitre.org (Ken Stillson).
* 03.11.92 -- 3.02.13 Ported to SunOs 4.1.2. Gulp. Some NULL ptrs fixed and
* misc. braindamage fixed.
* 03.11.92 -- 3.02.14 Argument number checking, AKA fixed.
* 05.11.92 -- 3.02.15 Memory leaks in Detach() and KillWindow() fixed.
* Lockprg powerdetaches on SIGHUP.
* 12.11.92 -- 3.02.16 Introduced two new termcaps: "CS" and "CE".
* (Switch cursorkeys in application mode)
* Tim's async output patch.
* Fixed an ugly bug in WriteString().
* New command: 'process'
* 16.11.92 -- 3.02.17 Nuking unsent tty output is now optional, (toxic
* ESC radiation).
* 30.11.92 -- 3.02.18 Lots of multi display bugs fixed. New layer
* function 'Restore'. MULTIUSER code cleanup.
* Rudimental acls added for multiuser.
* No more error output, when output gives write errors.
* 02.12.92 -- 3.02.19 BROKEN_PIPE and SOCK_NOT_IN_FS defines added for
* braindead systems. Bug in recover socket code fixed.
* Can create windows again from shell.
* 22.12.92 -- 3.02.20 Made a superb configure script. STY and break fixed.
* 01.02.93 -- 3.02.21 Coredump bug fixed: 8-bit output in background windows.
* Console grabbing somewhat more useable.
* 23.02.93 -- 3.02.22 Added ^:exec command, but not tested at all.
* 23.02.93 -- 3.02.23 Added 'hardcopydir' and 'logdir' commands.
* 11.03.93 -- 3.02.24 Prefixed display and window structure elements.
* Screen now handles autowrapped lines correctly
* in the redisplay and mark function.
* 19.03.93 -- 3.03.00 Patched for BSD386. pseudos work.
* 31.03.93 -- 3.03.01 Don't allocate so much empty attr and font lines.
* 04.04.93 -- 3.03.02 fixed :exec !..| less and :|less, patched BELL_DONE &
* ^B/^F. Fixed auto_nuke attribute resetting. Better linux
* configure. ^AW shows '&' when window has other attacher.
* MAXWIN > 10 patch. KEEP_UNDEF in config.h.in, shellaka
* bug fixed. dec alpha port. Solaris port.
* 02.05.93 -- 3.03.03 Configure tweaked for sgi. Update environment with
* setenv command. Silence on|off, silencewait ,
* defautonuke commands added. Manual page updated.
* 13.05.93 -- 3.03.04 exit in newsyntax script, finished _CX_UX port.
* Texinfo page added by Jason Merrill. Much longish debug
* output removed. Select window by title (or number).
* 16.06.93 -- 3.04.00 Replaced ^A- by ^A^H to be complementary to ^A SPACE.
* Moved into CVS. Yacc.
* 28.06.93 -- 3.04.01 Fixed selecting windows with numeric title. Silence
* now works without nethackoption set.
* 01.07.93 -- 3.04.02 Implementing real acls.
* 22.07.93 -- 3.05.00 Fixed SVR4, some multiuser bugs, -- DISTRIBUTED
* 05.08.93 -- 3.05.01 ${srcdir} feature added. Shellprog bug fixed.
* Motorola reattach bug fixed. Writelock bug fixed.
* Copybuffer moved into struct acluser. Configure.in
* uglified for Autoconf1.5. Paste may now have an
* argument. Interactive setenv. Right margin bug
* fixed. IRIX 5 patches. -- DISTRIBUTED
* 13.08.93 -- 3.05.02 ultrix support added. expand_vars removed from
* register function. Paste bug fixed.
* sysmacros.h now included in pty.c on sgis
* Strange hpux hack added for TTYCMP.
* Zombie feature improved.
* 08.09.93 -- 3.05.03 Makefile and OSF1 fine tuning. Eased attach to
* multi sessions. Writelock now obeys acl write
* permissions. UserDel() now preserves defaults.
* acladd/aclchg syntax improved. Updated
* documentation. Bug in at command fixed.
* MakeWindow() now obeys perm defaults.
* 03.10.93 -- 3.05.90 Multiple displays can now share the same window.
* Layers now belong to Windows. "Select -" yields a blank
* page. Debug output can be switched on/off at runtime.
* Number command changes window number and utmp-slot.
* 14.10.93 -- 3.05.91 Mechanism to find the "best" window added. Much
* debugging done and still needed. Slowpaste setting now
* per window. Preserving fore AND other window when
* detached. Per user settings for `monitor' and `silence'.
* New command `su'. BlankWindow performs now ColonInput.
* History command fixed, it depended on paste code that
* was previously changed to allow concatenation of buffers.
* Fixing umask problem with WriteFile() reported by Jamie
* Mason.
* 30.11.93 -- 3.05.04 Kanji support added. New keymap feature:
* bindkey and various map commands. GR and C1
* flags now define the character processing.
* 30.11.93 -- 3.05.92 Kanji support added. New keymap feature:
* bindkey and various map commands. GR and C1
* flags now define the character processing.
* 17.12.93 -- 3.05.93 Tcon authentification mechanism. AddChar macro broken up
* into 2 functions. Acl.c, network.c, misc.c, termcap.c
* and tty.c are now display free!
* 27.12.93 -- 3.05.94 Telnet login works, can export one window per mux.
* Acl-groups added. Export rights is an acl.
* connect works, can create shadow window thus import
* command channel and switch windows. Can detach sessions
* from each other. Can renumber imported and exported
* windows. DL_LOOP prevents now SEGV, sessions have names
* and can be connected to very flexible.
* 03.01.94 -- 3.05.95 Import and export of any number of channels works,
* except a small bug with margin handling when exporting.
* Window titles are forwarded, break is forwarded and
* tcon learned the mux protocoll. A few more bugs in
* network.c fixed. Tcon has Mux arrays, Protocoll is '\0'
* free.
* 08.01.94 -- 3.05.96 More tcon work. Solaris debugging.
*
* 14.01.94 -- 3.05.05 New FindSocket() code. Nicer socket handling.
* 20.01.94 -- 3.05.06 New attribute handling code. NeXT fixes.
* 04.02.94 -- 3.05.07 Ugly bug in ScrollH fixed. Keymap defaults.
* 10.03.94 -- 3.05.97 'defsilence' by Lloyd Zusman. Zombie resurrekt key.
* -b, -p options. Fixed Compilekeys(), gulp. config.h
* allows choice of VMIN/VTIME/NDELAY for ttys.
* 13.04.94 -- 3.05.08 Kanji bug fixed. POSIX sigaction support.
* os.h cleanup. UTNOKEEP splitted into
* UT_CLOSE and UT_UNSORTED. linux no longer
* implies UT_CLOSE. "struct display" name
* convention change: _d_ to d_ and d_ to D_.
* 20.04.94 -- 3.05.09 configure.in ptyrange bug fixed. Upgraded
* to autoconf-1.8
* 27.04.94 -- 3.05.10 97801 obscure code support. Linux long
* password workaround.
* 09.05.94 -- 3.05.11 seteuid() support added. Security fixes.
* _IBMR2 kludge makes call to sleep obsolete.
* Small fixes in uname() code. djm suggests replace of
* BSDI by __bsdi__.
* 27.07.94 -- 3.05.12 seteuid attacher bug fixed. ks/ke changed
* in termcap/info and termcap.c
* 01.09.94 -- 3.05.58 (There are a few more patches to come, subtracted 40)
* breaktype command to select how we cause breaks.
* Testing logfile.c. Manual and texinfo documentation
* updated to explain all new commands except keymapping.
* Added modem lines to info of plain ttys. Using
* cfset{i,o}speed() for POSIX is better, much better.
* 07.09.94 -- 3.05.59 Flushing logfiles every 5 seconds. Testing new
* resize linewrap code. Resize with care.
* Resurrect now displays its command string.
* 11.09.94 -- 3.05.60 Lines are now correctly wrapped, when window size
* changes; this prevents loss of text.
* 15.09.94 -- 3.05.61 umask renamed to aclumask, bell renamed to bell_msg.
* *_msg commands show message when called w/o parameter.
* Many changes in the manual, thanks to Sven Guckes.
* -O removed.
* 27.09.94 -- 3.05.13 defwlock stupidity fixed. MakeTermcap ks/ke
* ':' removed. Termcap entry improved.
* copy_reg and ins_reg obsoleted by better paste command:
* paste => paste .
* copy_reg r => paste . r
* ins_reg r => paste r
* ins_reg => paste
* 18.10.94 -- 3.05.62 breaktype changed. -L option added to enable logging
* from both command line and screenrc. SIGXFSZ ignored.
* 28.11.94 -- 3.05.63 ACL's sharpened. No user has any rights, unless
* explicitly given. Only two exceptions: 1) "acladd user"
* without further parameters. 2) Evry user has a default
* umask that gives himself all permissions on the windows
* he creates. Bug with colon input on shared windows fixed.
* The commad is now executed by the user typing the final
* Enter-Key rather by a random user.
* 30.11.94 -- 3.05.64 On demand grabbing of pending auto writelocks.
* Solaris troubles us with kill(pid, 0) ... not fixed...
* defbreaktype added. (breaktype is also global, still...)
* SIGARGHACK cleared, to pamper poor little Ultrix4.4 cfe.
* defescape added.
* w_status changed to w_hstatus, it's a #define in
* Domain/OS reported by ejackson@iastate.edu.
* 05.12.94 -- 3.05.17 SVR4 pty bug fixed, don't update window status
* line if not changed, onyx support. Manual
* pages updated.
* 14.12.94 -- 3.05.18 w_status changed to w_hstatus, it's a #define in
* Domain/OS reported by ejackson@iastate.edu.
* Screen manpage patches by larry.
* Ugly seteuid bug in ForkWindow() fixed.
* 20.12.94 -- 3.06.00 Solaris has broken seteuid().
* osf loadav patches. -- DISTRIBUTED
* 08.01.95 -- 3.05.65 Bug with LOGOUTOK and -ln fixed. redundant secfopen()
* avoided, when logfflush() looks at delayed nfs updates.
* Option parser now allowes condensed options. -A fixed.
* New commands 'preselect' and 'defpreselect'. New option
* -WdmS starts a detached screen session with watchdog.
* 16.01.95 -- 3.06.01 KANJI patch. doc/Makefile.in fixed.
* Install now calls doc/Makefile install.
* Don't use 'ds' too often, breaks MSkermit.
* undef'd LOGOUTOK logs in now.
* Ultrix is broken, too (seteuid).
* Use \r (not \n) to join lines in pastebuf.
* bindkey can now remove sequences.
* InitTTY fixed for PLAIN. -- DISTRIBUTED
* 06.02.95 -- 3.05.66 DisplayLine() does now clipping to reduce coredump-
* frequency. Window size is adapted to the display that
* types input, to reduce multi-user confusion. Scrollback
* mode still may get the wrong size.
* 07.02.95 -- 3.05.67 WinRewrite() also needs to clip. Users without unix
* account, but with screen password allowed. Chacl
* allows password change, su prompts for user name too.
* 01.03.95 -- 3.05.68 Leaving a window may now cause a resize to match a
* remaining display. Simple ESC-CR fix in the vt100
* state machine.
* 26.03.95 -- 3.05.69 Markroutine() needs to resize the window to avoid
* coredumps, just like WinProcess() already does.
* More NEED_ flags added in comm.c to avoid coredumps.
* 04.04.95 -- 3.06.02 Simple ESC-CR fix in the vt100 state machine.
* Now compiles again with all extras disabled.
* Zombie resurrect added. defc1, defgr, defkanji
* added. Screen now replies to a secondary DA
* query. Some missing NEED_XXX added in comm.c.
* Better default tty permissions/group test.
* More AUX support, now compiles with POSIX.
* Function keycodes xtermified (F11, F12).
* Paste bug fixed (only worked with KANJI).
* Check bcopy before memcpy/memmove.
* FindSocket code much fixed & improved.
* 10.04.95 -- 3.05.70 Display_displays() now shows the users permissions on
* the currently displayed window, this is easier than a
* full featured lsacl, but also helps. chacl can now copy
* users, but we need to copy windows.
* 26.04.95 -- 3.05.71 Multi digit screen numbers in utmp. MakeWindow() now
* echoes what it does, just like RemakeWindow() when
* 'verbose on' (default off).
* `screen -ls myname/' will find own(!) non-multi sessions.
* Command line option -q improved and documented.
* 'debug off' also shuts attacher debugging off now.
* 03.05.95 -- 3.05.72 'connect' command bugfixed. colon prompt improved.
* open(O_NONBLOCK) does not work on 4.1.3, using O_NDELAY.
* There are writes to exp_wid2chan[-1], XXX I just added
* fillers to the mux structure, please debug this ASAP!
* WindowByName() prefers exact matches now. Esc forwarding
* from Frontend to Backend suppressed unless specified.
* 26.06.95 -- 3.05.73 Improving the combination of dflag and rflag: If both
* are set, a screen session is reattached, regardless
* if it was previously detached or attached elseewhere.
* "screen -D -RR" is thus most powerful: Give me my screen
* session here and now. No matter, if this means creating
* a new session (due to the second R without warning
* message), detching the session first (and due to the
* capitalised D logging out), or simply attaching to it.
* 13.07.95 -- 3.05.74 Markroutine learned new join mode: comma seperated.
* Can disable the detach feature from config.h.
* Configure.in now calls etc/toolcheck. Password is no
* longer a static array.
* 30.07.95 -- 3.05.75 Karl Heuer wants to install with a relative symlink.
* Command line option -k removed (try -t).
* 31.08.95 -- 3.06.03 Color support, digraph command, code
* cleanup (struct mchar/mline added).
* 03.09.95 -- 3.06.04 SetRendition display check. Solaris unblock
* SIGHUP. w_curchar->w_rend, lintified.
* Shadow-password code moved and rewritten
* (includes sun C2).
* 06.09.95 -- 3.05.76 Added support for braille displays (by Hadi Bargi Rangin
* and Bill Barry, {bargy,barryb}@dots.physics.orst.edu
* Added baud, cs8, istrip, ixon, ixoff options for plain
* tty windows. Untested.
* 09.09.95 -- 3.07.99 Merged 3.7.76 back into the main distribution but
* without network, blankwindow and dlist support.
* Global variable Password removed.
* Resize code completely rewritten.
* New commands: sorendition, (def)charset.
* strftime-like message generation.
* 26.09.95 -- 3.06.05 resize code completely rewritten.
* Very visible cursor added. vbell sequence
* \Eb added. _delay for terminfo libs added
* to make the padding work for the first time.
* New ProcessInput() makes "command command"
* work. '[def]escape ""' clears escape chars.
* Changed logdir command to logfile. It now uses
* MakeWinMsg() to create the filename.
* Updated the manuals.
* 03.10.95 -- 3.08.00 Merged 3.07.99 back into my 3.06.05.
* pause removed. NONROOT now in config.h.
* sysV68 configure test. Socket protocol got
* revision number. New command termcapinfo.
* Screenrcs, docs & configure.in updated
* 18.10.95 -- 3.08.01 Info shows parser state.
* Very visible cursor added. vbell sequence
* \Eb added. _delay for terminfo libs added
* to make the padding work for the first time.
* Braille code almost completely reworked.
* New ProcessInput() makes "command command"
* work. '[def]escape ""' clears escape chars.
* Command logdir -> logfile and uses MakeWinMsg.
* Updated the manuals. New mark command 'x'.
* stuff has new "-k" parameter.
* 28.10.95 -- 3.06.06 Added new 'XC' capability to provide
* a method for character translation.
* 17.11.95 -- 3.07.00 Added CheckEscape() function.
* acl -> acls because of hpux10 nameclash
* /stand/vmunix added to kernel list (hpux10)
* stripdev changed to translate
* /dev/pts to /dev/pts/ (unixware)
* -lgen added to GETUTENT configure test.
* 20.11.95 -- 3.07.01 corrected vbell null ptr reference.
* -- DISTRIBUTED
* 27.11.95 -- 3.08.02 Added CheckEscape() function.
* acl -> acls because of hpux10 nameclash
* /stand/vmunix added to kernel list (hpux10)
* stripdev changed to translate
* /dev/pts to /dev/pts/ (unixware)
* -lgen added to GETUTENT configure test.
* corrected vbell null ptr reference
* -- 3.08.03 etc/toolcheck is now shouting louder.
* Solaris 2.5 has /kernel/genunix.
* Touching socket when detach/attach.
* FindWindow corrected. AIX4 patch
* from robert@mayday.cix.co.uk.
* 1.09.96 -- 3.07.02 added #ifdef MAPKEYS for CheckEscape.
* etc/toolcheck is now shouting louder.
* Touching socket when detach/attach.
* Linux tcflush hack. Linux md5 password suport.
* USE_SGR support for dumb (wyse) terminals.
* "at" and "reset" commands improved.
* Now sensitive to broken AIX4 TIOCPKT.
* tek patch unapplied.
* linux utmp patch: set slot to DEAD_PROCESS.
* include signal.h for killpg if hpux10.10.
* linux: elf, but no SVR4, check for utmpx.
* hpux10.10 libcurses breaks select()!
* -- DISTRIBUTED
* 27.09.96 -- 3.07.03 ncurses tgetflag returns -1 if cap not valid.
* autonuke fix: reset application mode. An init
* string may have cleared it.
* Small UTMPOK patch from Douglas B. Jones.
* 23.06.97 -- 3.07.04 Some bugfixes. Kanji fixed.
* Code update from 3.8.6: Colon line editing added.
* Digraph made line-editing resistant.
* DEC ALPHA ut_user[0] = 0;
* Added error messages to exit_with_usage.
* FindSocket code improved (the MULTIUSER part).
* Use utimes() if available. Power-detached start.
* vsnprintf() support. exit -> _exit in window.c.
* AddXChars now tolerates NULL string.
* -- DISTRIBUTED
* 10.11.98 -- 3.07.05 Wayne's resize.c patch to keep the cursor
* onscreen. FindWindow corrected. AIX4 patch
* from robert@mayday.cix.co.uk.
* Fixed kanji am & markcursor bugs.
* SCO support (Gert Doering).
* Wayne's statusline extra space. New flag nospc
* for MakeWinMsg. New TF capability: add missing
* termcap/info caps. screen-exchange security
* fix. Use kstat for Solaris' loadavg.
* Check for mode 775 if screen runs setgid.
* Fixed typo in silence command.
* Allow detach with no tty. HPUX10.20 ltchars
* hack. Use @1/@7 as replacement for kh/kH.
* Use seteuid before calling tgetent.
* Check for displays in Msg().
* Linux ncurses hack: redefine _nc_timed_wait.
* Configure check for ncurses (sigh).
* ITOH Yasufumi: kanji fixes, osf support,
* bug fixes. More secure Rewrite().
* Increased line buffer size to 2048 bytes.
* SCO-5 Fixes (Brian L. Gentry).
* 25.11.98 -- 3.07.06 resize code now handles tabs correctly.
* -- DISTRIBUTED
* -- 3.09.00 loads'o'bugs fixed, 3.8 merged with 3.7.
* new region clip code.
* Many new features (see NEWS file).
* 3.08.99 -- 3.09.02 -- DISTRIBUTED
* 11.08.99 -- 3.09.04 small utmp fix, BSD chflags fix, tty mode fix
* -- DISTRIBUTED
* 1.09.99 -- 3.09.05 linux SVR4_PTYS fixes, better ShowWindos(),
* Panic() displays bug fixed, strerror fixed,
* missing $srcdir & bad prefix in Makefiles.
* console grabbing with SRIOCSREDIR.
* linux utmp workaround added. Some KANJI bugs
* fixed. Stupid StuffKey() bug fixed.
* -- DISTRIBUTED
* 24.07.00 -- 3.09.06 kanji fixes. lock escape char fix.
* removed delayed message hack.
* resize command & focus subcomands by joze.
* fix for solaris' utmp_helper bug.
* linux tty handling fix.
* mousetracking, rxvt osc sequences.
* background color erase support.
* don't log user in after hangup.
* hstatus escape update bug fixed.
* 1.09.00 -- 3.09.08 -- DISTRIBUTED
* 25.05.01 -- 3.09.09 use $DESTDIR in Makefiles.
* renamed struct user to acluser to fix hpux
* problem for good. utf8 support. 16 color
* support. New -X option and escape sequence
* to send screen commands.
* New commands: attrcolor, defutf8, dinfo, utf8.
* Extended commands: width, height, readbuf,
* writebuf. Better nlist64 detection.
* New config.h option SOCKDIR_IS_LOCAL_TO_HOST.
* Don't call tputs with an empty string.
* New string escape %{ for attr/color changes.
* LRefreshAll bug fixed (0 instead of -1 as bce).
* New quote handling in Parse(). Workaround for
* some bad termcap/info entries added. Set BE
* for all xtermish emulators. Added '0x' and
* 'U+' prefix in digraph command. LOADAV_TYPE
* detection fixed.
* -- DISTRIBUTED
* 4.09.01 -- 3.09.10 '-R' security fix. Fixed array in MScrollV().
* -- DISTRIBUTED
* Reset charset after 'me'.
* 14.02.02 -- 3.09.11 Support for other encodings. Double width
* utf-8 chars. 'L' modifier for %D, %M, %w
* and %W. New %= (hfill), %H (hostname),
* %f (flags), %F (focus) escapes. New commands
* source, windowlist, deflog. Command key classes.
* New login state: always. Time format
* changeable. 256 color support.
* Updated digraph table. Ignorecase.
* Windowlist resizeable on blank window.
* Added encoding to paste buffers.
* Hpux loadtype detection fixed.
* Disabled im/ic warning. Fixed a bug that
* could cause window lockups in rare
* circumstances. Made hangup signal to the
* backend process detach all displays.
* New escapes %< %>, + and - flag for %w.
* Added eval command, added -h option to detach.
* Also set flayer when processing -X commands.
* use getpt() on linux systems. doc patches from
* Adam Lazur. tty flow/intr cleanup.
* -- DISTRIBUTED
* 29.08.2002 3.09.12 Port to POSIX 1003.1-2001 hosts (Paul Eggert).
* Fixed encoding for reattach password test.
* Fixed NUL characters when encodings are active.
* Fixed silly encodings bugs. Fixed bogus
* oldflayer when a window gets freed.
* Altscreen support by Gurusamy Sarathy.
* Reuse old password in builtin_screenlock.
* -- DISTRIBUTED
* 05.09.2002 3.09.13 added missing break statement that broke
* the eval command.
* -- DISTRIBUTED
* 13.03.2003 3.09.15 Console workaround for linux. Scrolling-region
* reset fix. GBK encoding added.
* support for unicode combining characters added.
* openpty() support added (thomas@xs4all.nl).
* preselect of blank window ('-') or window
* list ('=') added. Added %` string escape
* and backtick command.
* -- DISTRIBUTED
* 08.09.2003 4.00.00 rewrote nonblock command, added defnonblock.
* added zmodem support (catch & passthrough).
* added screenblanker.
* fixed a couple of GBK bugs.
* rewrote command parsing.
* added -m option to windowlist (Felix Rosencrantz)
* -- DISTRIBUTED
* 18.09.2003 4.00.01 fixed two small bugs in the new parser.
* -- DISTRIBUTED
* 05.12.2003 4.00.02 fixed a bug in the ansi parser. fixed execs
* on ttys. fixed hardstatus line on blanked screen.
* -- DISTRIBUTED
* 11.10.2004, 4.00.03jw handle StartRc errors nonfatal if due to a msg.
* 12.10.2004, 4.00.03jw1 let docu of 'split' 'focus' 'remove' and 'only' refer to each other.
* 01.11.2004, 4.00.03jw2 zombie command has new option 'onerror'
* 2005-12-19, 4.00.03jw3 syntax error.
*/
#define ORIGIN "GNU"
#define REV 4
#define VERS 1
#define PATCHLEVEL 0
#define DATE "2-May-06"
#define STATE "devel"
screen-4.1.0/utmp.c 0000644 0001750 0001750 00000045551 11642704565 012210 0 ustar abe abe /* 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
* http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
****************************************************************
*/
#include
#include
#include
#include "config.h"
#include "screen.h"
#include "extern.h"
#ifdef HAVE_UTEMPTER
#include
#endif
extern struct display *display;
#ifdef CAREFULUTMP
extern struct win *windows;
#endif
extern struct win *fore;
extern char *LoginName;
extern int real_uid, eff_uid;
/*
* UTNOKEEP: A (ugly) hack for apollo that does two things:
* 1) Always close and reopen the utmp file descriptor. (I don't know
* for what reason this is done...)
* 2) Implement an unsorted utmp file much like GETUTENT.
* (split into UT_CLOSE and UT_UNSORTED)
*/
#ifdef UTNOKEEP
# define UT_CLOSE
# define UT_UNSORTED
#endif
#ifdef UT_CLOSE
# undef UT_CLOSE
# define UT_CLOSE endutent()
#else
# define UT_CLOSE
#endif
/*
* we have a suid-root helper app that changes the utmp for us
* (won't work for login-slots)
*/
#if (defined(sun) && defined(SVR4) && defined(GETUTENT)) || defined(HAVE_UTEMPTER)
# define UTMP_HELPER
#endif
#ifdef UTMPOK
static slot_t TtyNameSlot __P((char *));
static void makeuser __P((struct utmp *, char *, char *, int));
static void makedead __P((struct utmp *));
static int pututslot __P((slot_t, struct utmp *, char *, struct win *));
static struct utmp *getutslot __P((slot_t));
#ifndef GETUTENT
static struct utmp *getutent __P((void));
static void endutent __P((void));
static int initutmp __P((void));
static void setutent __P((void));
#endif
#if defined(linux) && defined(GETUTENT)
static struct utmp *xpututline __P((struct utmp *utmp));
# define pututline xpututline
#endif
static int utmpok;
static char UtmpName[] = UTMPFILE;
#ifndef UTMP_HELPER
static int utmpfd = -1;
#endif
# if defined(GETUTENT) && (!defined(SVR4) || defined(__hpux)) && ! defined(__CYGWIN__)
# if defined(hpux) /* cruel hpux release 8.0 */
# define pututline _pututline
# endif /* hpux */
extern struct utmp *getutline(), *pututline();
# if defined(_SEQUENT_)
extern struct utmp *ut_add_user(), *ut_delete_user();
extern char *ut_find_host();
# ifndef UTHOST
# define UTHOST /* _SEQUENT_ has ut_find_host() */
# endif
# endif /* _SEQUENT_ */
# endif /* GETUTENT && !SVR4 */
# if !defined(GETUTENT) && !defined(UT_UNSORTED)
# ifdef GETTTYENT
# include
# else
struct ttyent { char *ty_name; };
static void setttyent __P((void));
static struct ttyent *getttyent __P((void));
# endif
# endif /* !GETUTENT && !UT_UNSORTED */
#ifndef _SEQUENT_
# undef D_loginhost
# define D_loginhost D_utmp_logintty.ut_host
#endif
#ifndef UTHOST
# undef D_loginhost
# define D_loginhost ((char *)0)
#endif
#endif /* UTMPOK */
/*
* SlotToggle - modify the utmp slot of the fore window.
*
* how > 0 do try to set a utmp slot.
* how = 0 try to withdraw a utmp slot.
*
* w_slot = -1 window not logged in.
* w_slot = 0 window not logged in, but should be logged in.
* (unable to write utmp, or detached).
*/
#ifndef UTMPOK
void
SlotToggle(how)
int how;
{
debug1("SlotToggle (!UTMPOK) %d\n", how);
# ifdef UTMPFILE
Msg(0, "Unable to modify %s.\n", UTMPFILE);
# else
Msg(0, "Unable to modify utmp-database.\n");
# endif
}
#endif
#ifdef UTMPOK
void
SlotToggle(how)
int how;
{
debug1("SlotToggle %d\n", how);
if (fore->w_type != W_TYPE_PTY)
{
Msg(0, "Can only work with normal windows.\n");
return;
}
if (how)
{
debug(" try to log in\n");
if ((fore->w_slot == (slot_t) -1) || (fore->w_slot == (slot_t) 0))
{
#ifdef USRLIMIT
if (CountUsers() >= USRLIMIT)
{
Msg(0, "User limit reached.");
return;
}
#endif
if (SetUtmp(fore) == 0)
Msg(0, "This window is now logged in.");
else
Msg(0, "This window should now be logged in.");
WindowChanged(fore, 'f');
}
else
Msg(0, "This window is already logged in.");
}
else
{
debug(" try to log out\n");
if (fore->w_slot == (slot_t) -1)
Msg(0, "This window is already logged out\n");
else if (fore->w_slot == (slot_t) 0)
{
debug("What a relief! In fact, it was not logged in\n");
Msg(0, "This window is not logged in.");
fore->w_slot = (slot_t) -1;
}
else
{
RemoveUtmp(fore);
if (fore->w_slot != (slot_t) -1)
Msg(0, "What? Cannot remove Utmp slot?");
else
Msg(0, "This window is no longer logged in.");
#ifdef CAREFULUTMP
CarefulUtmp();
#endif
WindowChanged(fore, 'f');
}
}
}
#ifdef CAREFULUTMP
/* CAREFULUTMP: goodie for paranoid sysadmins: always leave one
* window logged in
*/
void
CarefulUtmp()
{
struct win *p;
if (!windows) /* hopeless */
return;
debug("CarefulUtmp counting slots\n");
for (p = windows; p; p = p->w_next)
if (p->w_ptyfd >= 0 && p->w_slot != (slot_t)-1)
return; /* found one, nothing to do */
debug("CarefulUtmp: no slots, log one in again.\n");
for (p = windows; p; p = p->w_next)
if (p->w_ptyfd >= 0) /* no zombies please */
break;
if (!p)
return; /* really hopeless */
SetUtmp(p);
Msg(0, "Window %d is now logged in.\n", p->w_number);
}
#endif /* CAREFULUTMP */
void
InitUtmp()
{
debug1("InitUtmp testing '%s'...\n", UtmpName);
#ifndef UTMP_HELPER
if ((utmpfd = open(UtmpName, O_RDWR)) == -1)
{
if (errno != EACCES)
Msg(errno, "%s", UtmpName);
debug("InitUtmp failed.\n");
utmpok = 0;
return;
}
# ifdef GETUTENT
close(utmpfd); /* it was just a test */
utmpfd = -1;
# endif /* GETUTENT */
#endif /* UTMP_HELPER */
utmpok = 1;
}
#ifdef USRLIMIT
int
CountUsers()
{
struct utmp *ut;
int UserCount;
debug1("CountUsers() - utmpok=%d\n", utmpok);
if (!utmpok)
return 0;
UserCount = 0;
setutent();
while (ut = getutent())
if (SLOT_USED(ut))
UserCount++;
UT_CLOSE;
return UserCount;
}
#endif /* USRLIMIT */
/*
* the utmp entry for tty is located and removed.
* it is stored in D_utmp_logintty.
*/
void
RemoveLoginSlot()
{
struct utmp u, *uu;
ASSERT(display);
debug("RemoveLoginSlot: removing your logintty\n");
D_loginslot = TtyNameSlot(D_usertty);
if (D_loginslot == (slot_t)0 || D_loginslot == (slot_t)-1)
return;
#ifdef UTMP_HELPER
if (eff_uid) /* helpers can't do login slots. sigh. */
#else
if (!utmpok)
#endif
{
D_loginslot = 0;
debug("RemoveLoginSlot: utmpok == 0\n");
}
else
{
#ifdef _SEQUENT_
{
char *p;
if ((p = ut_find_host(D_loginslot)) != 0)
strncpy(D_loginhost, p, sizeof(D_loginhost) - 1);
D_loginhost[sizeof(D_loginhost) - 1] = 0;
}
#endif /* _SEQUENT_ */
if ((uu = getutslot(D_loginslot)) == 0)
{
debug("Utmp slot not found -> not removed");
D_loginslot = 0;
}
else
{
D_utmp_logintty = *uu;
u = *uu;
makedead(&u);
if (pututslot(D_loginslot, &u, (char *)0, (struct win *)0) == 0)
D_loginslot = 0;
}
UT_CLOSE;
}
debug1(" slot %d zapped\n", (int)D_loginslot);
if (D_loginslot == (slot_t)0)
{
/* couldn't remove slot, do a 'mesg n' at least. */
struct stat stb;
char *tty;
debug("couln't zap slot -> do mesg n\n");
D_loginttymode = 0;
if ((tty = ttyname(D_userfd)) && stat(tty, &stb) == 0 && (int)stb.st_uid == real_uid && ((int)stb.st_mode & 0777) != 0666)
{
D_loginttymode = (int)stb.st_mode & 0777;
chmod(D_usertty, stb.st_mode & 0600);
}
}
}
/*
* D_utmp_logintty is reinserted into utmp
*/
void
RestoreLoginSlot()
{
char *tty;
debug("RestoreLoginSlot()\n");
ASSERT(display);
if (utmpok && D_loginslot != (slot_t)0 && D_loginslot != (slot_t)-1)
{
debug1(" logging you in again (slot %#x)\n", (int)D_loginslot);
if (pututslot(D_loginslot, &D_utmp_logintty, D_loginhost, (struct win *)0) == 0)
Msg(errno,"Could not write %s", UtmpName);
}
UT_CLOSE;
D_loginslot = (slot_t)0;
if (D_loginttymode && (tty = ttyname(D_userfd)))
chmod(tty, D_loginttymode);
}
/*
* Construct a utmp entry for window wi.
* the hostname field reflects what we know about the user (display)
* location. If d_loginhost is not set, then he is local and we write
* down the name of his terminal line; else he is remote and we keep
* the hostname here. The letter S and the window id will be appended.
* A saved utmp entry in wi->w_savut serves as a template, usually.
*/
int
SetUtmp(wi)
struct win *wi;
{
register slot_t slot;
struct utmp u;
int saved_ut;
#ifdef UTHOST
char *p;
char host[sizeof(D_loginhost) + 15];
#else
char *host = 0;
#endif /* UTHOST */
wi->w_slot = (slot_t)0;
if (!utmpok || wi->w_type != W_TYPE_PTY)
return -1;
if ((slot = TtyNameSlot(wi->w_tty)) == (slot_t)0)
{
debug1("SetUtmp failed (tty %s).\n",wi->w_tty);
return -1;
}
debug2("SetUtmp %d will get slot %d...\n", wi->w_number, (int)slot);
bzero((char *)&u, sizeof(u));
if ((saved_ut = bcmp((char *) &wi->w_savut, (char *)&u, sizeof(u))))
/* restore original, of which we will adopt all fields but ut_host */
bcopy((char *)&wi->w_savut, (char *) &u, sizeof(u));
if (!saved_ut)
makeuser(&u, stripdev(wi->w_tty), LoginName, wi->w_pid);
#ifdef UTHOST
host[sizeof(host) - 15] = '\0';
if (display)
{
strncpy(host, D_loginhost, sizeof(host) - 15);
if (D_loginslot != (slot_t)0 && D_loginslot != (slot_t)-1 && host[0] != '\0')
{
/*
* we want to set our ut_host field to something like
* ":ttyhf:s.0" or
* "faui45:s.0" or
* "132.199.81.4:s.0" (even this may hurt..), but not
* "faui45.informati"......:s.0
* HPUX uses host:0.0, so chop at "." and ":" (Eric Backus)
*/
for (p = host; *p; p++)
if ((*p < '0' || *p > '9') && (*p != '.'))
break;
if (*p)
{
for (p = host; *p; p++)
if (*p == '.' || (*p == ':' && p != host))
{
*p = '\0';
break;
}
}
}
else
{
strncpy(host + 1, stripdev(D_usertty), sizeof(host) - 15 - 1);
host[0] = ':';
}
}
else
strncpy(host, "local", sizeof(host) - 15);
sprintf(host + strlen(host), ":S.%d", wi->w_number);
debug1("rlogin hostname: '%s'\n", host);
# if !defined(_SEQUENT_) && !defined(sequent)
strncpy(u.ut_host, host, sizeof(u.ut_host));
# endif
#endif /* UTHOST */
if (pututslot(slot, &u, host, wi) == 0)
{
Msg(errno,"Could not write %s", UtmpName);
UT_CLOSE;
return -1;
}
debug("SetUtmp successful\n");
wi->w_slot = slot;
bcopy((char *)&u, (char *)&wi->w_savut, sizeof(u));
UT_CLOSE;
return 0;
}
/*
* if slot could be removed or was 0, wi->w_slot = -1;
* else not changed.
*/
int
RemoveUtmp(wi)
struct win *wi;
{
struct utmp u, *uu;
slot_t slot;
slot = wi->w_slot;
debug1("RemoveUtmp slot=%#x\n", slot);
if (!utmpok)
return -1;
if (slot == (slot_t)0 || slot == (slot_t)-1)
{
wi->w_slot = (slot_t)-1;
return 0;
}
bzero((char *) &u, sizeof(u));
#ifdef sgi
bcopy((char *)&wi->w_savut, (char *)&u, sizeof(u));
uu = &u;
#else
if ((uu = getutslot(slot)) == 0)
{
Msg(0, "Utmp slot not found -> not removed");
return -1;
}
bcopy((char *)uu, (char *)&wi->w_savut, sizeof(wi->w_savut));
#endif
u = *uu;
makedead(&u);
if (pututslot(slot, &u, (char *)0, wi) == 0)
{
Msg(errno,"Could not write %s", UtmpName);
UT_CLOSE;
return -1;
}
debug("RemoveUtmp successfull\n");
wi->w_slot = (slot_t)-1;
UT_CLOSE;
return 0;
}
/*********************************************************************
*
* routines using the getut* api
*/
#ifdef GETUTENT
#define SLOT_USED(u) (u->ut_type == USER_PROCESS)
static struct utmp *
getutslot(slot)
slot_t slot;
{
struct utmp u;
bzero((char *)&u, sizeof(u));
strncpy(u.ut_line, slot, sizeof(u.ut_line));
setutent();
return getutline(&u);
}
static int
pututslot(slot, u, host, wi)
slot_t slot;
struct utmp *u;
char *host;
struct win *wi;
{
#ifdef _SEQUENT_
if (SLOT_USED(u) && host && *host)
return ut_add_user(u.ut_name, slot, u.ut_pid, host) != 0;
if (!SLOT_USED(u))
return ut_delete_user(slot, u.ut_pid, 0, 0) != 0;
#endif
#ifdef HAVE_UTEMPTER
if (eff_uid && wi->w_ptyfd != -1)
{
/* sigh, linux hackers made the helper functions void */
if (SLOT_USED(u))
addToUtmp(wi->w_tty, host, wi->w_ptyfd);
else
removeLineFromUtmp(wi->w_tty, wi->w_ptyfd);
return 1; /* pray for success */
}
#endif
setutent();
#ifndef __CYGWIN__
return pututline(u) != 0;
#else
return 1;
#endif
}
static void
makedead(u)
struct utmp *u;
{
u->ut_type = DEAD_PROCESS;
#if (!defined(linux) || defined(EMPTY)) && !defined(__CYGWIN__)
u->ut_exit.e_termination = 0;
u->ut_exit.e_exit = 0;
#endif
#if !defined(sun) || !defined(SVR4)
u->ut_user[0] = 0; /* for Digital UNIX, kilbi@rad.rwth-aachen.de */
#endif
}
static void
makeuser(u, line, user, pid)
struct utmp *u;
char *line, *user;
int pid;
{
time_t now;
u->ut_type = USER_PROCESS;
strncpy(u->ut_user, user, sizeof(u->ut_user));
/* Now the tricky part... guess ut_id */
#if defined(sgi) || defined(linux)
strncpy(u->ut_id, line + 3, sizeof(u->ut_id));
#else /* sgi */
# ifdef _IBMR2
strncpy(u->ut_id, line, sizeof(u->ut_id));
# else
strncpy(u->ut_id, line + strlen(line) - 2, sizeof(u->ut_id));
# endif
#endif /* sgi */
strncpy(u->ut_line, line, sizeof(u->ut_line));
u->ut_pid = pid;
/* must use temp variable because of NetBSD/sparc64, where
* ut_xtime is long(64) but time_t is int(32) */
(void)time(&now);
u->ut_time = now;
}
static slot_t
TtyNameSlot(nam)
char *nam;
{
return stripdev(nam);
}
#else /* GETUTENT */
/*********************************************************************
*
* getut emulation for systems lacking the api
*/
static struct utmp uent;
#define SLOT_USED(u) (u.ut_name[0] != 0)
static int
initutmp()
{
if (utmpfd >= 0)
return 1;
return (utmpfd = open(UtmpName, O_RDWR)) >= 0;
}
static void
setutent()
{
if (utmpfd >= 0)
(void)lseek(utmpfd, (off_t)0, 0);
}
static void
endutent()
{
if (utmpfd >= 0)
close(utmpfd);
utmpfd = -1;
}
static struct utmp *
getutent()
{
if (utmpfd < 0 && !initutmp())
return 0;
if (read(utmpfd, &uent, sizeof(uent)) != sizeof(uent))
return 0;
return &uent;
}
static struct utmp *
getutslot(slot)
slot_t slot;
{
if (utmpfd < 0 && !initutmp())
return 0;
lseek(utmpfd, (off_t)(slot * sizeof(struct utmp)), 0);
if (read(utmpfd, &uent, sizeof(uent)) != sizeof(uent))
return 0;
return &uent;
}
static int
pututslot(slot, u, host, wi)
slot_t slot;
struct utmp *u;
char *host;
struct win *wi;
{
#ifdef sequent
if (SLOT_USED(u))
return add_utmp(slot, u) != -1;
#endif
if (utmpfd < 0 && !initutmp())
return 0;
lseek(utmpfd, (off_t)(slot * sizeof(*u)), 0);
if (write(utmpfd, u, sizeof(*u)) != sizeof(*u))
return 0;
return 1;
}
static void
makedead(u)
struct utmp *u;
{
#ifdef UT_UNSORTED
bzero(u->ut_name, sizeof(u->ut_name));
# ifdef UTHOST
bzero(u->ut_host, sizeof(u->ut_host));
# endif
#else
bzero((char *)u, sizeof(*u));
#endif
}
static void
makeuser(u, line, user, pid)
struct utmp *u;
char *line, *user;
int pid;
{
time_t now;
strncpy(u->ut_line, line, sizeof(u->ut_line));
strncpy(u->ut_name, user, sizeof(u->ut_name));
(void)time(&now);
u->ut_time = now;
}
static slot_t
TtyNameSlot(nam)
char *nam;
{
slot_t slot;
char *line;
#ifndef UT_UNSORTED
struct ttyent *tp;
#endif
line = stripdev(nam);
#ifdef UT_UNSORTED
setutent();
if (utmpfd < 0)
return -1;
for (slot = 0; getutent(); slot++)
if (strcmp(uent.ut_line, line) == 0)
break;
UT_CLOSE;
#else
slot = 1;
setttyent();
while ((tp = getttyent()) != 0 && strcmp(line, tp->ty_name) != 0)
slot++;
#endif
return slot;
}
#endif /* GETUTENT */
/*********************************************************************
*
* Cheap plastic imitation of ttyent routines.
*/
#if !defined(GETTTYENT) && !defined(GETUTENT) && !defined(UT_UNSORTED)
static char *tt, *ttnext;
static char ttys[] = "/etc/ttys";
static void
setttyent()
{
if (ttnext == 0)
{
struct stat s;
register int f;
register char *p, *ep;
if ((f = open(ttys, O_RDONLY)) == -1 || fstat(f, &s) == -1)
Panic(errno, ttys);
if ((tt = malloc((unsigned) s.st_size + 1)) == 0)
Panic(0, strnomem);
if (read(f, tt, s.st_size) != s.st_size)
Panic(errno, ttys);
close(f);
for (p = tt, ep = p + s.st_size; p < ep; p++)
if (*p == '\n')
*p = '\0';
*p = '\0';
}
ttnext = tt;
}
static struct ttyent *
getttyent()
{
static struct ttyent t;
if (*ttnext == '\0')
return NULL;
t.ty_name = ttnext + 2;
ttnext += strlen(ttnext) + 1;
return &t;
}
#endif /* !GETTTYENT && !GETUTENT && !UT_UNSORTED*/
#endif /* UTMPOK */
/*********************************************************************
*
* getlogin() replacement (for SVR4 machines)
*/
# if defined(BUGGYGETLOGIN) && defined(UTMP_FILE)
char *
getlogin()
{
char *tty = NULL;
#ifdef utmp
# undef utmp
#endif
struct utmp u;
static char retbuf[sizeof(u.ut_user)+1];
int fd;
for (fd = 0; fd <= 2 && (tty = ttyname(fd)) == NULL; fd++)
;
if ((tty == NULL) || ((fd = open(UTMP_FILE, O_RDONLY)) < 0))
return NULL;
tty = stripdev(tty);
retbuf[0] = '\0';
while (read(fd, (char *)&u, sizeof(struct utmp)) == sizeof(struct utmp))
{
if (!strncmp(tty, u.ut_line, sizeof(u.ut_line)))
{
strncpy(retbuf, u.ut_user, sizeof(u.ut_user));
retbuf[sizeof(u.ut_user)] = '\0';
if (u.ut_type == USER_PROCESS)
break;
}
}
close(fd);
return *retbuf ? retbuf : NULL;
}
# endif /* BUGGYGETLOGIN */
#if defined(linux) && defined(GETUTENT)
# undef pututline
/* aargh, linux' pututline returns void! */
struct utmp *
xpututline(u)
struct utmp *u;
{
struct utmp *u2;
pututline(u);
setutent();
u2 = getutline(u);
if (u2 == 0)
return u->ut_type == DEAD_PROCESS ? u : 0;
return u->ut_type == u2->ut_type ? u : 0;
}
#endif
screen-4.1.0/nethack.c 0000644 0001750 0001750 00000011226 11642704565 012630 0 ustar abe abe /* 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
* http://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"
#ifdef NETHACK
extern int nethackflag;
#endif
struct nlstrans {
char *from;
char *to;
};
#ifdef NETHACK
static struct nlstrans nethacktrans[] = {
{"Cannot lock terminal - fork failed",
"Cannot fork terminal - lock failed"},
{"Got only %d bytes from %s",
"You choke on your food: %d bytes from %s"},
{"Copy mode - Column %d Line %d(+%d) (%d,%d)",
"Welcome to hacker's treasure zoo - Column %d Line %d(+%d) (%d,%d)"},
{"First mark set - Column %d Line %d",
"You drop a magic marker - Column %d Line %d"},
{"Copy mode aborted",
"You escaped the dungeon."},
{"Filter removed.",
"You have a sad feeling for a moment..."},
{"Window %d (%s) killed.",
"You destroy poor window %d (%s)."},
{"Window %d (%s) is now being monitored for all activity.",
"You feel like someone is watching you..."},
{"Window %d (%s) is no longer being monitored for activity.",
"You no longer sense the watcher's presence."},
{"empty buffer",
"Nothing happens."},
{"switched to audible bell.",
"Suddenly you can't see your bell!"},
{"switched to visual bell.",
"Your bell is no longer invisible."},
{"The window is now being monitored for %d sec. silence.",
"You feel like someone is waiting for %d sec. silence..."},
{"The window is no longer being monitored for silence.",
"You no longer sense the watcher's silence."},
{"No other window.",
"You cannot escape from window %d!"},
{"Logfile \"%s\" closed.",
"You put away your scroll of logging named \"%s\"." },
{"Error opening logfile \"%s\"",
"You don't seem to have a scroll of logging named \"%s\"."},
{"Creating logfile \"%s\".",
"You start writing on your scroll of logging named \"%s\"."},
{"Appending to logfile \"%s\".",
"You add to your scroll of logging named \"%s\"."},
{"Detach aborted.",
"The blast of disintegration whizzes by you!"},
{"Empty register.",
"Nothing happens."},
{"[ Passwords don't match - checking turned off ]",
"[ Passwords don't match - your armor crumbles away ]"},
{"Aborted because of window size change.",
"KAABLAMM!!! You triggered a land mine!"},
{"Out of memory.",
"Who was that Maude person anyway?"},
{"getpwuid() can't identify your account!",
"An alarm sounds through the dungeon...\nThe Keystone Kops are after you!"},
{"Must be connected to a terminal.",
"You must play from a terminal."},
{"No Sockets found in %s.\n",
"This room is empty (%s).\n"},
{"New screen...",
"Be careful! New screen tonight."},
{"Child has been stopped, restarting.",
"You regain consciousness."},
{"There are screens on:",
"Your inventory:"},
{"There is a screen on:",
"Your inventory:"},
{"There are several screens on:",
"Prove thyself worthy or perish:"},
{"There is a suitable screen on:",
"You see here a good looking screen:"},
{"There are several suitable screens on:",
"You may wish for a screen, what do you want?"},
{"%d socket%s wiped out.",
"You hear %d distant explosion%s."},
{"Remove dead screens with 'screen -wipe'.",
"The dead screen%s touch%s you. Try 'screen -wipe'."},
{"Illegal reattach attempt from terminal %s.",
"'%s' tries to touch your session, but fails."},
{"Could not write %s",
"%s is too hard to dig in"},
{0, 0}
};
#endif
const char *
DoNLS(from)
const char *from;
{
#ifdef NETHACK
struct nlstrans *t;
if (nethackflag)
{
for (t = nethacktrans; t->from; t++)
if (strcmp(from, t->from) == 0)
return t->to;
}
#endif
return from;
}
screen-4.1.0/termcap.c 0000644 0001750 0001750 00000075212 11642704565 012653 0 ustar abe abe /* 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
* http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
****************************************************************
*/
#include
#include "config.h"
#include "screen.h"
#include "extern.h"
extern struct display *display, *displays;
extern int real_uid, real_gid, eff_uid, eff_gid;
extern struct term term[]; /* terminal capabilities */
extern struct NewWindow nwin_undef, nwin_default, nwin_options;
extern int force_vt;
extern int Z0width, Z1width;
extern int hardstatusemu;
#ifdef MAPKEYS
extern struct action umtab[];
extern struct action mmtab[];
extern struct action dmtab[];
extern struct action ktab[];
extern struct kmap_ext *kmap_exts;
extern int kmap_extn;
extern int DefaultEsc;
#endif
static void AddCap __P((char *));
static void MakeString __P((char *, char *, int, char *));
static char *findcap __P((char *, char **, int));
static int copyarg __P((char **, char *));
static int e_tgetent __P((char *, char *));
static char *e_tgetstr __P((char *, char **));
static int e_tgetflag __P((char *));
static int e_tgetnum __P((char *));
#ifdef MAPKEYS
static int findseq_ge __P((char *, int, unsigned char **));
static void setseqoff __P((unsigned char *, int, int));
static int addmapseq __P((char *, int, int));
static int remmapseq __P((char *, int));
#ifdef DEBUGG
static void dumpmap __P((void));
#endif
#endif
char Termcap[TERMCAP_BUFSIZE + 8]; /* new termcap +8:"TERMCAP=" */
static int Termcaplen;
static int tcLineLen;
char Term[MAXSTR+5]; /* +5: "TERM=" */
char screenterm[20]; /* new $TERM, usually "screen" */
char *extra_incap, *extra_outcap;
static const char TermcapConst[] = "\\\n\
\t:DO=\\E[%dB:LE=\\E[%dD:RI=\\E[%dC:UP=\\E[%dA:bs:bt=\\E[Z:\\\n\
\t:cd=\\E[J:ce=\\E[K:cl=\\E[H\\E[J:cm=\\E[%i%d;%dH:ct=\\E[3g:\\\n\
\t:do=^J:nd=\\E[C:pt:rc=\\E8:rs=\\Ec:sc=\\E7:st=\\EH:up=\\EM:\\\n\
\t:le=^H:bl=^G:cr=^M:it#8:ho=\\E[H:nw=\\EE:ta=^I:is=\\E)0:";
char *
gettermcapstring(s)
char *s;
{
int i;
if (display == 0 || s == 0)
return 0;
for (i = 0; i < T_N; i++)
{
if (term[i].type != T_STR)
continue;
if (strcmp(term[i].tcname, s) == 0)
return D_tcs[i].str;
}
return 0;
}
/*
* Compile the terminal capabilities for a display.
* Input: tgetent(, D_termname) extra_incap, extra_outcap.
* Effect: display initialisation.
*/
int
InitTermcap(wi, he)
int wi;
int he;
{
register char *s;
int i;
char tbuf[TERMCAP_BUFSIZE], *tp;
int t, xue, xse, xme;
ASSERT(display);
bzero(tbuf, sizeof(tbuf));
debug1("InitTermcap: looking for tgetent('%s')\n", D_termname);
if (*D_termname == 0 || e_tgetent(tbuf, D_termname) != 1)
{
#ifdef TERMINFO
Msg(0, "Cannot find terminfo entry for '%s'.", D_termname);
#else
Msg(0, "Cannot find termcap entry for '%s'.", D_termname);
#endif
return -1;
}
debug1("got it:\n%s\n", tbuf);
#ifdef DEBUG
if (extra_incap)
debug1("Extra incap: %s\n", extra_incap);
if (extra_outcap)
debug1("Extra outcap: %s\n", extra_outcap);
#endif
if ((D_tentry = (char *)malloc(TERMCAP_BUFSIZE + (extra_incap ? strlen(extra_incap) + 1 : 0))) == 0)
{
Msg(0, "%s", strnomem);
return -1;
}
/*
* loop through all needed capabilities, record their values in the display
*/
tp = D_tentry;
for (i = 0; i < T_N; i++)
{
switch(term[i].type)
{
case T_FLG:
D_tcs[i].flg = e_tgetflag(term[i].tcname);
break;
case T_NUM:
D_tcs[i].num = e_tgetnum(term[i].tcname);
break;
case T_STR:
D_tcs[i].str = e_tgetstr(term[i].tcname, &tp);
/* no empty strings, please */
if (D_tcs[i].str && *D_tcs[i].str == 0)
D_tcs[i].str = 0;
break;
default:
Panic(0, "Illegal tc type in entry #%d", i);
/*NOTREACHED*/
}
}
/*
* Now a good deal of sanity checks on the retrieved capabilities.
*/
if (D_HC)
{
Msg(0, "You can't run screen on a hardcopy terminal.");
return -1;
}
if (D_OS)
{
Msg(0, "You can't run screen on a terminal that overstrikes.");
return -1;
}
if (!D_CL)
{
Msg(0, "Clear screen capability required.");
return -1;
}
if (!D_CM)
{
Msg(0, "Addressable cursor capability required.");
return -1;
}
if ((s = getenv("COLUMNS")) && (i = atoi(s)) > 0)
D_CO = i;
if ((s = getenv("LINES")) && (i = atoi(s)) > 0)
D_LI = i;
if (wi)
D_CO = wi;
if (he)
D_LI = he;
if (D_CO <= 0)
D_CO = 80;
if (D_LI <= 0)
D_LI = 24;
if (D_CTF)
{
/* standard fixes for xterms etc */
/* assume color for everything that looks ansi-compatible */
if (!D_CAF && D_ME && (InStr(D_ME, "\033[m") || InStr(D_ME, "\033[0m")))
{
#ifdef TERMINFO
D_CAF = "\033[3%p1%dm";
D_CAB = "\033[4%p1%dm";
#else
D_CAF = "\033[3%dm";
D_CAB = "\033[4%dm";
#endif
}
if (D_OP && InStr(D_OP, "\033[39;49m"))
D_CAX = 1;
if (D_OP && (InStr(D_OP, "\033[m") || InStr(D_OP, "\033[0m")))
D_OP = 0;
/* ISO2022 */
if ((D_EA && InStr(D_EA, "\033(B")) || (D_AS && InStr(D_AS, "\033(0")))
D_CG0 = 1;
if (InStr(D_termname, "xterm") || InStr(D_termname, "rxvt") ||
(D_CKM && InStr(D_CKM, "\033[M")))
D_CXT = 1;
/* "be" seems to be standard for xterms... */
if (D_CXT)
D_BE = 1;
}
if (nwin_options.flowflag == nwin_undef.flowflag)
nwin_default.flowflag = D_CNF ? FLOW_NOW * 0 :
D_NX ? FLOW_NOW * 1 :
FLOW_AUTOFLAG;
D_CLP |= (!D_AM || D_XV || D_XN);
if (!D_BL)
D_BL = "\007";
if (!D_BC)
{
if (D_BS)
D_BC = "\b";
else
D_BC = D_LE;
}
if (!D_CR)
D_CR = "\r";
if (!D_NL)
D_NL = "\n";
/*
* Set up attribute handling.
* This is rather complicated because termcap has different
* attribute groups.
*/
if (D_UG > 0)
D_US = D_UE = 0;
if (D_SG > 0)
D_SO = D_SE = 0;
/* Unfortunately there is no 'mg' capability.
* For now we think that mg > 0 if sg and ug > 0.
*/
if (D_UG > 0 && D_SG > 0)
D_MH = D_MD = D_MR = D_MB = D_ME = 0;
xue = ATYP_U;
xse = ATYP_S;
xme = ATYP_M;
if (D_SO && D_SE == 0)
{
Msg(0, "Warning: 'so' but no 'se' capability.");
if (D_ME)
xse = xme;
else
D_SO = 0;
}
if (D_US && D_UE == 0)
{
Msg(0, "Warning: 'us' but no 'ue' capability.");
if (D_ME)
xue = xme;
else
D_US = 0;
}
if ((D_MH || D_MD || D_MR || D_MB) && D_ME == 0)
{
Msg(0, "Warning: 'm?' but no 'me' capability.");
D_MH = D_MD = D_MR = D_MB = 0;
}
/*
* Does ME also reverse the effect of SO and/or US? This is not
* clearly specified by the termcap manual. Anyway, we should at
* least look whether ME and SE/UE are equal:
*/
if (D_UE && D_SE && strcmp(D_SE, D_UE) == 0)
xse = xue;
if (D_SE && D_ME && strcmp(D_ME, D_SE) == 0)
xse = xme;
if (D_UE && D_ME && strcmp(D_ME, D_UE) == 0)
xue = xme;
for (i = 0; i < NATTR; i++)
{
D_attrtab[i] = D_tcs[T_ATTR + i].str;
D_attrtyp[i] = i == ATTR_SO ? xse : (i == ATTR_US ? xue : xme);
}
/* Set up missing entries (attributes are priority ordered) */
s = 0;
t = 0;
for (i = 0; i < NATTR; i++)
if ((s = D_attrtab[i]))
{
t = D_attrtyp[i];
break;
}
for (i = 0; i < NATTR; i++)
{
if (D_attrtab[i] == 0)
{
D_attrtab[i] = s;
D_attrtyp[i] = t;
}
else
{
s = D_attrtab[i];
t = D_attrtyp[i];
}
}
if (D_CAF || D_CAB || D_CSF || D_CSB)
D_hascolor = 1;
if (D_UT)
D_BE = 1; /* screen erased with background color */
if (!D_DO)
D_DO = D_NL;
if (!D_SF)
D_SF = D_NL;
if (D_IN)
D_IC = D_IM = 0;
if (D_EI == 0)
D_IM = 0;
/* some strange termcap entries have IC == IM */
if (D_IC && D_IM && strcmp(D_IC, D_IM) == 0)
D_IC = 0;
if (D_KE == 0)
D_KS = 0;
if (D_CVN == 0)
D_CVR = 0;
if (D_VE == 0)
D_VI = D_VS = 0;
if (D_CCE == 0)
D_CCS = 0;
#ifdef FONT
if (D_CG0)
{
if (D_CS0 == 0)
#ifdef TERMINFO
D_CS0 = "\033(%p1%c";
#else
D_CS0 = "\033(%.";
#endif
if (D_CE0 == 0)
D_CE0 = "\033(B";
D_AC = 0;
D_EA = 0;
}
else if (D_AC || (D_AS && D_AE)) /* some kind of graphics */
{
D_CS0 = (D_AS && D_AE) ? D_AS : "";
D_CE0 = (D_AS && D_AE) ? D_AE : "";
D_CC0 = D_AC;
}
else
{
D_CS0 = D_CE0 = "";
D_CC0 = 0;
D_AC = ""; /* enable default string */
}
for (i = 0; i < 256; i++)
D_c0_tab[i] = i;
if (D_AC)
{
/* init with default string first */
s = "l+m+k+j+u+t+v+w+q-x|n+o~s_p\"r#`+a:f'g#~o.v-^+<,>h#I#0#y";
for (i = strlen(s) & ~1; i >= 0; i -= 2)
D_c0_tab[(int)(unsigned char)s[i]] = s[i + 1];
}
if (D_CC0)
for (i = strlen(D_CC0) & ~1; i >= 0; i -= 2)
D_c0_tab[(int)(unsigned char)D_CC0[i]] = D_CC0[i + 1];
debug1("ISO2022 = %d\n", D_CG0);
#endif /* FONT */
if (D_PF == 0)
D_PO = 0;
debug2("terminal size is %d, %d (says TERMCAP)\n", D_CO, D_LI);
#ifdef FONT
if (D_CXC)
if (CreateTransTable(D_CXC))
return -1;
#endif
/* Termcap fields Z0 & Z1 contain width-changing sequences. */
if (D_CZ1 == 0)
D_CZ0 = 0;
Z0width = 132;
Z1width = 80;
CheckScreenSize(0);
if (D_TS == 0 || D_FS == 0 || D_DS == 0)
D_HS = 0;
if (D_HS)
{
debug("oy! we have a hardware status line, says termcap\n");
if (D_WS < 0)
D_WS = 0;
}
D_has_hstatus = hardstatusemu & ~HSTATUS_ALWAYS;
if (D_HS && !(hardstatusemu & HSTATUS_ALWAYS))
D_has_hstatus = HSTATUS_HS;
#ifdef ENCODINGS
if (D_CKJ)
{
int enc = FindEncoding(D_CKJ);
if (enc != -1)
D_encoding = enc;
}
#endif
if (!D_tcs[T_NAVIGATE].str && D_tcs[T_NAVIGATE + 1].str)
D_tcs[T_NAVIGATE].str = D_tcs[T_NAVIGATE + 1].str; /* kh = @1 */
if (!D_tcs[T_NAVIGATE + 2].str && D_tcs[T_NAVIGATE + 3].str)
D_tcs[T_NAVIGATE + 2].str = D_tcs[T_NAVIGATE + 3].str; /* kH = @7 */
D_UPcost = CalcCost(D_UP);
D_DOcost = CalcCost(D_DO);
D_NLcost = CalcCost(D_NL);
D_LEcost = CalcCost(D_BC);
D_NDcost = CalcCost(D_ND);
D_CRcost = CalcCost(D_CR);
D_IMcost = CalcCost(D_IM);
D_EIcost = CalcCost(D_EI);
#ifdef AUTO_NUKE
if (D_CAN)
{
debug("termcap has AN, setting autonuke\n");
D_auto_nuke = 1;
}
#endif
if (D_COL > 0)
{
debug1("termcap has OL (%d), setting limit\n", D_COL);
D_obufmax = D_COL;
D_obuflenmax = D_obuflen - D_obufmax;
}
/* Some xterm entries set F0 and F10 to the same string. Nuke F0. */
if (D_tcs[T_CAPS].str && D_tcs[T_CAPS + 10].str && !strcmp(D_tcs[T_CAPS].str, D_tcs[T_CAPS + 10].str))
D_tcs[T_CAPS].str = 0;
/* Some xterm entries set kD to ^?. Nuke it. */
if (D_tcs[T_NAVIGATE_DELETE].str && !strcmp(D_tcs[T_NAVIGATE_DELETE].str, "\0177"))
D_tcs[T_NAVIGATE_DELETE].str = 0;
/* wyse52 entries have kcub1 == kb == ^H. Nuke... */
if (D_tcs[T_CURSOR + 3].str && !strcmp(D_tcs[T_CURSOR + 3].str, "\008"))
D_tcs[T_CURSOR + 3].str = 0;
#ifdef MAPKEYS
D_nseqs = 0;
for (i = 0; i < T_OCAPS - T_CAPS; i++)
remap(i, 1);
for (i = 0; i < kmap_extn; i++)
remap(i + (KMAP_KEYS+KMAP_AKEYS), 1);
D_seqp = D_kmaps + 3;
D_seql = 0;
D_seqh = 0;
#endif
D_tcinited = 1;
MakeTermcap(0);
#ifdef MAPKEYS
CheckEscape();
#endif
return 0;
}
#ifdef MAPKEYS
int
remap(n, map)
int n;
int map;
{
char *s = 0;
int fl = 0, domap = 0;
struct action *a1, *a2, *tab;
int l = 0;
struct kmap_ext *kme = 0;
a1 = 0;
if (n >= KMAP_KEYS+KMAP_AKEYS)
{
kme = kmap_exts + (n - (KMAP_KEYS+KMAP_AKEYS));
s = kme->str;
l = kme->fl & ~KMAP_NOTIMEOUT;
fl = kme->fl & KMAP_NOTIMEOUT;
a1 = &kme->um;
}
tab = umtab;
for (;;)
{
a2 = 0;
if (n < KMAP_KEYS+KMAP_AKEYS)
{
a1 = &tab[n];
if (n >= KMAP_KEYS)
n -= T_OCAPS-T_CURSOR;
s = D_tcs[n + T_CAPS].str;
l = s ? strlen(s) : 0;
if (n >= T_CURSOR-T_CAPS)
a2 = &tab[n + (T_OCAPS-T_CURSOR)];
}
if (s == 0 || l == 0)
return 0;
if (a1 && a1->nr == RC_ILLEGAL)
a1 = 0;
if (a2 && a2->nr == RC_ILLEGAL)
a2 = 0;
if (a1 && a1->nr == RC_STUFF && a1->args[0] && strcmp(a1->args[0], s) == 0)
a1 = 0;
if (a2 && a2->nr == RC_STUFF && a2->args[0] && strcmp(a2->args[0], s) == 0)
a2 = 0;
domap |= (a1 || a2);
if (tab == umtab)
{
tab = dmtab;
a1 = kme ? &kme->dm : 0;
}
else if (tab == dmtab)
{
tab = mmtab;
a1 = kme ? &kme->mm : 0;
}
else
break;
}
if (n < KMAP_KEYS)
domap = 1;
if (map == 0 && domap)
return 0;
if (map && !domap)
return 0;
debug3("%smapping %s %#x\n", map? "" :"un",s,n);
if (map)
return addmapseq(s, l, n | fl);
else
return remmapseq(s, l);
}
void
CheckEscape()
{
struct display *odisplay;
int i, nr;
if (DefaultEsc >= 0)
return;
odisplay = display;
for (display = displays; display; display = display->d_next)
{
for (i = 0; i < D_nseqs; i += D_kmaps[i + 2] * 2 + 4)
{
nr = (D_kmaps[i] << 8 | D_kmaps[i + 1]) & ~KMAP_NOTIMEOUT;
if (nr < KMAP_KEYS+KMAP_AKEYS)
{
if (umtab[nr].nr == RC_COMMAND)
break;
if (umtab[nr].nr == RC_ILLEGAL && dmtab[nr].nr == RC_COMMAND)
break;
}
else
{
struct kmap_ext *kme = kmap_exts + nr - (KMAP_KEYS+KMAP_AKEYS);
if (kme->um.nr == RC_COMMAND)
break;
if (kme->um.nr == RC_ILLEGAL && kme->dm.nr == RC_COMMAND)
break;
}
}
}
if (display == 0)
{
display = odisplay;
return;
}
SetEscape((struct acluser *)0, Ctrl('a'), 'a');
if (odisplay->d_user->u_Esc == -1)
odisplay->d_user->u_Esc = DefaultEsc;
if (odisplay->d_user->u_MetaEsc == -1)
odisplay->d_user->u_MetaEsc = DefaultMetaEsc;
display = 0;
Msg(0, "Warning: escape char set back to ^A");
display = odisplay;
}
static int
findseq_ge(seq, k, sp)
char *seq;
int k;
unsigned char **sp;
{
unsigned char *p;
int j, l;
p = D_kmaps;
while (p - D_kmaps < D_nseqs)
{
l = p[2];
p += 3;
for (j = 0; ; j++)
{
if (j == k || j == l)
j = l - k;
else if (p[j] != ((unsigned char *)seq)[j])
j = p[j] - ((unsigned char *)seq)[j];
else
continue;
break;
}
if (j >= 0)
{
*sp = p - 3;
return j;
}
p += 2 * l + 1;
}
*sp = p;
return -1;
}
static void
setseqoff(p, i, o)
unsigned char *p;
int i;
int o;
{
unsigned char *q;
int l, k;
k = p[2];
if (o < 256)
{
p[k + 4 + i] = o;
return;
}
/* go for the biggest offset */
for (q = p + k * 2 + 4; ; q += l * 2 + 4)
{
l = q[2];
if ((q + l * 2 - p) / 2 >= 256)
{
p[k + 4 + i] = (q - p - 4) / 2;
return;
}
}
}
static int
addmapseq(seq, k, nr)
char *seq;
int k;
int nr;
{
int i, j, l, mo, m;
unsigned char *p, *q;
if (k >= 254)
return -1;
j = findseq_ge(seq, k, &p);
if (j == 0)
{
p[0] = nr >> 8;
p[1] = nr;
return 0;
}
i = p - D_kmaps;
if (D_nseqs + 2 * k + 4 >= D_aseqs)
{
D_kmaps = (unsigned char *)xrealloc((char *)D_kmaps, D_aseqs + 256);
D_aseqs += 256;
p = D_kmaps + i;
}
D_seqp = D_kmaps + 3;
D_seql = 0;
D_seqh = 0;
evdeq(&D_mapev);
if (j > 0)
bcopy((char *)p, (char *)p + 2 * k + 4, D_nseqs - i);
p[0] = nr >> 8;
p[1] = nr;
p[2] = k;
bcopy(seq, (char *)p + 3, k);
bzero(p + k + 3, k + 1);
D_nseqs += 2 * k + 4;
if (j > 0)
{
q = p + 2 * k + 4;
l = q[2];
for (i = 0; i < k; i++)
{
if (p[3 + i] != q[3 + i])
{
p[k + 4 + i] = k;
break;
}
setseqoff(p, i, q[l + 4 + i] ? q[l + 4 + i] + k + 2: 0);
}
}
for (q = D_kmaps; q < p; q += 2 * l + 4)
{
l = q[2];
for (m = j = 0; j < l; j++)
{
mo = m;
if (!m && q[3 + j] != seq[j])
m = 1;
if (q[l + 4 + j] == 0)
{
if (!mo && m)
setseqoff(q, j, (p - q - 4) / 2);
}
else if (q + q[l + 4 + j] * 2 + 4 > p || (q + q[l + 4 + j] * 2 + 4 == p && !m))
setseqoff(q, j, q[l + 4 + j] + k + 2);
}
}
#ifdef DEBUGG
dumpmap();
#endif
return 0;
}
static int
remmapseq(seq, k)
char *seq;
int k;
{
int j, l;
unsigned char *p, *q;
if (k >= 254 || (j = findseq_ge(seq, k, &p)) != 0)
return -1;
for (q = D_kmaps; q < p; q += 2 * l + 4)
{
l = q[2];
for (j = 0; j < l; j++)
{
if (q + q[l + 4 + j] * 2 + 4 == p)
setseqoff(q, j, p[k + 4 + j] ? q[l + 4 + j] + p[k + 4 + j] - k : 0);
else if (q + q[l + 4 + j] * 2 + 4 > p)
q[l + 4 + j] -= k + 2;
}
}
if (D_kmaps + D_nseqs > p + 2 * k + 4)
bcopy((char *)p + 2 * k + 4, (char *)p, (D_kmaps + D_nseqs) - (p + 2 * k + 4));
D_nseqs -= 2 * k + 4;
D_seqp = D_kmaps + 3;
D_seql = 0;
D_seqh = 0;
evdeq(&D_mapev);
#ifdef DEBUGG
dumpmap();
#endif
return 0;
}
#ifdef DEBUGG
static void
dumpmap()
{
unsigned char *p;
int j, n, l, o, oo;
debug("Mappings:\n");
p = D_kmaps;
if (!p)
return;
while (p < D_kmaps + D_nseqs)
{
l = p[2];
debug1("%d: ", p - D_kmaps + 3);
for (j = 0; j < l; j++)
{
o = oo = p[l + 4 + j];
if (o)
o = 2 * o + 4 + (p + 3 + j - D_kmaps);
if (p[j + 3] > ' ' && p[j + 3] < 0177)
{
debug3("%c[%d:%d] ", p[j + 3], oo, o);
}
else
debug3("\\%03o[%d:%d] ", p[j + 3], oo, o);
}
n = p[0] << 8 | p[1];
debug2(" ==> %d%s\n", n & ~KMAP_NOTIMEOUT, (n & KMAP_NOTIMEOUT) ? " (no timeout)" : "");
p += 2 * l + 4;
}
}
#endif /* DEBUGG */
#endif /* MAPKEYS */
/*
* Appends to the static variable Termcap
*/
static void
AddCap(s)
char *s;
{
register int n;
if (tcLineLen + (n = strlen(s)) > 55 && Termcaplen < TERMCAP_BUFSIZE - 4 - 1)
{
strcpy(Termcap + Termcaplen, "\\\n\t:");
Termcaplen += 4;
tcLineLen = 0;
}
if (Termcaplen + n < TERMCAP_BUFSIZE - 1)
{
strcpy(Termcap + Termcaplen, s);
Termcaplen += n;
tcLineLen += n;
}
else
Panic(0, "TERMCAP overflow - sorry.");
}
/*
* Reads a displays capabilities and reconstructs a termcap entry in the
* global buffer "Termcap". A pointer to this buffer is returned.
*/
char *
MakeTermcap(aflag)
int aflag;
{
char buf[TERMCAP_BUFSIZE];
register char *p, *cp, *s, ch, *tname;
int i, wi, he;
#if 0
int found;
#endif
if (display)
{
wi = D_width;
he = D_height;
tname = D_termname;
}
else
{
wi = 80;
he = 24;
tname = "vt100";
}
debug1("MakeTermcap(%d)\n", aflag);
if ((s = getenv("SCREENCAP")) && strlen(s) < TERMCAP_BUFSIZE)
{
sprintf(Termcap, "TERMCAP=%s", s);
strcpy(Term, "TERM=screen");
debug("getenvSCREENCAP o.k.\n");
return Termcap;
}
Termcaplen = 0;
debug1("MakeTermcap screenterm='%s'\n", screenterm);
debug1("MakeTermcap termname='%s'\n", tname);
if (*screenterm == '\0' || strlen(screenterm) > MAXSTR - 3)
{
debug("MakeTermcap sets screenterm=screen\n");
strcpy(screenterm, "screen");
}
#if 0
found = 1;
#endif
do
{
strcpy(Term, "TERM=");
p = Term + 5;
if (!aflag && strlen(screenterm) + strlen(tname) < MAXSTR-1)
{
sprintf(p, "%s.%s", screenterm, tname);
if (e_tgetent(buf, p) == 1)
break;
}
#ifdef COLOR
if (nwin_default.bce)
{
sprintf(p, "%s-bce", screenterm);
if (e_tgetent(buf, p) == 1)
break;
}
#endif
#ifdef CHECK_SCREEN_W
if (wi >= 132)
{
sprintf(p, "%s-w", screenterm);
if (e_tgetent(buf, p) == 1)
break;
}
#endif
strcpy(p, screenterm);
if (e_tgetent(buf, p) == 1)
break;
strcpy(p, "vt100");
#if 0
found = 0;
#endif
}
while (0); /* Goto free programming... */
#if 0
#ifndef TERMINFO
/* check for compatibility problems, displays == 0 after fork */
if (found)
{
char xbuf[TERMCAP_BUFSIZE], *xbp = xbuf;
if (tgetstr("im", &xbp) && tgetstr("ic", &xbp) && displays)
{
Msg(0, "Warning: im and ic set in %s termcap entry", p);
}
}
#endif
#endif
tcLineLen = 100; /* Force NL */
if (strlen(Term) > TERMCAP_BUFSIZE - 40)
strcpy(Term, "too_long");
sprintf(Termcap, "TERMCAP=SC|%s|VT 100/ANSI X3.64 virtual terminal:", Term + 5);
Termcaplen = strlen(Termcap);
debug1("MakeTermcap decided '%s'\n", p);
if (extra_outcap && *extra_outcap)
{
for (cp = extra_outcap; (p = index(cp, ':')); cp = p)
{
ch = *++p;
*p = '\0';
AddCap(cp);
*p = ch;
}
tcLineLen = 100; /* Force NL */
}
debug1("MakeTermcap after outcap '%s'\n", (char *)TermcapConst);
if (Termcaplen + strlen(TermcapConst) < TERMCAP_BUFSIZE)
{
strcpy(Termcap + Termcaplen, (char *)TermcapConst);
Termcaplen += strlen(TermcapConst);
}
sprintf(buf, "li#%d:co#%d:", he, wi);
AddCap(buf);
AddCap("am:");
if (aflag || (force_vt && !D_COP) || D_CLP || !D_AM)
{
AddCap("xn:");
AddCap("xv:");
AddCap("LP:");
}
if (aflag || (D_CS && D_SR) || D_AL || D_CAL)
{
AddCap("sr=\\EM:");
AddCap("al=\\E[L:");
AddCap("AL=\\E[%dL:");
}
else if (D_SR)
AddCap("sr=\\EM:");
if (aflag || D_CS)
AddCap("cs=\\E[%i%d;%dr:");
if (aflag || D_CS || D_DL || D_CDL)
{
AddCap("dl=\\E[M:");
AddCap("DL=\\E[%dM:");
}
if (aflag || D_DC || D_CDC)
{
AddCap("dc=\\E[P:");
AddCap("DC=\\E[%dP:");
}
if (aflag || D_CIC || D_IC || D_IM)
{
AddCap("im=\\E[4h:");
AddCap("ei=\\E[4l:");
AddCap("mi:");
AddCap("IC=\\E[%d@:");
}
#ifdef MAPKEYS
AddCap("ks=\\E[?1h\\E=:");
AddCap("ke=\\E[?1l\\E>:");
#endif
AddCap("vi=\\E[?25l:");
AddCap("ve=\\E[34h\\E[?25h:");
AddCap("vs=\\E[34l:");
AddCap("ti=\\E[?1049h:");
AddCap("te=\\E[?1049l:");
if (display)
{
if (D_US)
{
AddCap("us=\\E[4m:");
AddCap("ue=\\E[24m:");
}
if (D_SO)
{
AddCap("so=\\E[3m:");
AddCap("se=\\E[23m:");
}
if (D_MB)
AddCap("mb=\\E[5m:");
if (D_MD)
AddCap("md=\\E[1m:");
if (D_MH)
AddCap("mh=\\E[2m:");
if (D_MR)
AddCap("mr=\\E[7m:");
if (D_MB || D_MD || D_MH || D_MR)
AddCap("me=\\E[m:ms:");
if (D_hascolor)
AddCap("Co#8:pa#64:AF=\\E[3%dm:AB=\\E[4%dm:op=\\E[39;49m:AX:");
if (D_VB)
AddCap("vb=\\Eg:");
#ifndef MAPKEYS
if (D_KS)
{
AddCap("ks=\\E=:");
AddCap("ke=\\E>:");
}
if (D_CCS)
{
AddCap("CS=\\E[?1h:");
AddCap("CE=\\E[?1l:");
}
#endif
if (D_CG0)
AddCap("G0:");
if (D_CC0 || (D_CS0 && *D_CS0))
{
AddCap("as=\\E(0:");
AddCap("ae=\\E(B:");
/* avoid `` because some shells dump core... */
AddCap("ac=\\140\\140aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~..--++,,hhII00:");
}
if (D_PO)
{
AddCap("po=\\E[5i:");
AddCap("pf=\\E[4i:");
}
if (D_CZ0)
{
AddCap("Z0=\\E[?3h:");
AddCap("Z1=\\E[?3l:");
}
if (D_CWS)
AddCap("WS=\\E[8;%d;%dt:");
}
for (i = T_CAPS; i < T_ECAPS; i++)
{
#ifdef MAPKEYS
struct action *act;
if (i < T_OCAPS)
{
if (i >= T_KEYPAD) /* don't put keypad codes in TERMCAP */
continue; /* - makes it too big */
if (i >= T_CURSOR && i < T_OCAPS)
{
act = &umtab[i - (T_CURSOR - T_OCAPS + T_CAPS)];
if (act->nr == RC_ILLEGAL)
act = &dmtab[i - (T_CURSOR - T_OCAPS + T_CAPS)];
}
else
{
act = &umtab[i - T_CAPS];
if (act->nr == RC_ILLEGAL)
act = &dmtab[i - T_CAPS];
}
if (act->nr == RC_ILLEGAL && (i == T_NAVIGATE + 1 || i == T_NAVIGATE + 3))
{
/* kh -> @1, kH -> @7 */
act = &umtab[i - T_CAPS - 1];
if (act->nr == RC_ILLEGAL)
act = &dmtab[i - T_CAPS - 1];
}
if (act->nr != RC_ILLEGAL)
{
if (act->nr == RC_STUFF)
{
MakeString(term[i].tcname, buf, sizeof(buf), act->args[0]);
AddCap(buf);
}
continue;
}
}
#endif
if (display == 0)
continue;
switch(term[i].type)
{
case T_STR:
if (D_tcs[i].str == 0)
break;
MakeString(term[i].tcname, buf, sizeof(buf), D_tcs[i].str);
AddCap(buf);
break;
case T_FLG:
if (D_tcs[i].flg == 0)
break;
sprintf(buf, "%s:", term[i].tcname);
AddCap(buf);
break;
default:
break;
}
}
debug("MakeTermcap: end\n");
return Termcap;
}
static void
MakeString(cap, buf, buflen, s)
char *cap, *buf;
int buflen;
char *s;
{
register char *p, *pmax;
register unsigned int c;
p = buf;
pmax = p + buflen - (3+4+2);
*p++ = *cap++;
*p++ = *cap;
*p++ = '=';
while ((c = *s++) && (p < pmax))
{
switch (c)
{
case '\033':
*p++ = '\\';
*p++ = 'E';
break;
case ':':
strcpy(p, "\\072");
p += 4;
break;
case '^':
case '\\':
*p++ = '\\';
*p++ = c;
break;
default:
if (c >= 200)
{
sprintf(p, "\\%03o", c & 0377);
p += 4;
}
else if (c < ' ')
{
*p++ = '^';
*p++ = c + '@';
}
else
*p++ = c;
}
}
*p++ = ':';
*p = '\0';
}
#undef QUOTES
#define QUOTES(p) \
(*p == '\\' && (p[1] == '\\' || p[1] == ',' || p[1] == '%'))
#ifdef FONT
int
CreateTransTable(s)
char *s;
{
int curchar;
char *templ, *arg;
int templlen;
int templnsub;
char *p, *sx;
char **ctable;
int l, c;
if ((D_xtable = (char ***)calloc(256, sizeof(char **))) == 0)
{
Msg(0, "%s", strnomem);
return -1;
}
while (*s)
{
if (QUOTES(s))
s++;
curchar = (unsigned char)*s++;
if (curchar == 'B')
curchar = 0; /* ASCII */
templ = s;
templlen = 0;
templnsub = 0;
if (D_xtable[curchar] == 0)
{
if ((D_xtable[curchar] = (char **)calloc(257, sizeof(char *))) == 0)
{
Msg(0, "%s", strnomem);
FreeTransTable();
return -1;
}
}
ctable = D_xtable[curchar];
for(; *s && *s != ','; s++)
{
if (QUOTES(s))
s++;
else if (*s == '%')
{
templnsub++;
continue;
}
templlen++;
}
if (*s++ == 0)
break;
while (*s && *s != ',')
{
c = (unsigned char)*s++;
if (QUOTES((s - 1)))
c = (unsigned char)*s++;
else if (c == '%')
c = 256;
if (ctable[c])
free(ctable[c]);
arg = s;
l = copyarg(&s, (char *)0);
if (c != 256)
l = l * templnsub + templlen;
if ((ctable[c] = (char *)malloc(l + 1)) == 0)
{
Msg(0, "%s", strnomem);
FreeTransTable();
return -1;
}
sx = ctable[c];
for (p = ((c == 256) ? "%" : templ); *p && *p != ','; p++)
{
if (QUOTES(p))
p++;
else if (*p == '%')
{
s = arg;
sx += copyarg(&s, sx);
continue;
}
*sx++ = *p;
}
*sx = 0;
ASSERT(ctable[c] + l * templnsub + templlen == sx);
debug3("XC: %c %c->%s\n", curchar, c, ctable[c]);
}
if (*s == ',')
s++;
}
return 0;
}
void
FreeTransTable()
{
char ***p, **q;
int i, j;
if ((p = D_xtable) == 0)
return;
for (i = 0; i < 256; i++, p++)
{
if (*p == 0)
continue;
q = *p;
for (j = 0; j < 257; j++, q++)
if (*q)
free(*q);
free(*p);
}
free(D_xtable);
}
#endif /* FONT */
static int
copyarg(pp, s)
char **pp, *s;
{
int l;
char *p;
for (l = 0, p = *pp; *p && *p != ','; p++)
{
if (QUOTES(p))
p++;
if (s)
*s++ = *p;
l++;
}
if (*p == ',')
p++;
*pp = p;
return l;
}
/*
**
** Termcap routines that use our extra_incap
**
*/
static int
e_tgetent(bp, name)
char *bp, *name;
{
int r;
#ifdef USE_SETEUID
xseteuid(real_uid);
xsetegid(real_gid);
#endif
r = tgetent(bp, name);
#ifdef USE_SETEUID
xseteuid(eff_uid);
xsetegid(eff_gid);
#endif
return r;
}
/* findcap:
* cap = capability we are looking for
* tepp = pointer to bufferpointer
* n = size of buffer (0 = infinity)
*/
static char *
findcap(cap, tepp, n)
char *cap;
char **tepp;
int n;
{
char *tep;
char c, *p, *cp;
int mode; /* mode: 0=LIT 1=^ 2=\x 3,4,5=\nnn */
int num = 0, capl;
if (!extra_incap)
return 0;
tep = *tepp;
capl = strlen(cap);
cp = 0;
mode = 0;
for (p = extra_incap; *p; )
{
if (strncmp(p, cap, capl) == 0)
{
p += capl;
c = *p;
if (c && c != ':' && c != '@')
p++;
if (c == 0 || c == '@' || c == '=' || c == ':' || c == '#')
cp = tep;
}
while ((c = *p))
{
p++;
if (mode == 0)
{
if (c == ':')
break;
if (c == '^')
mode = 1;
if (c == '\\')
mode = 2;
}
else if (mode == 1)
{
mode = 0;
c = c & 0x1f;
}
else if (mode == 2)
{
mode = 0;
switch(c)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
mode = 3;
num = 0;
break;
case 'E':
c = 27;
break;
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 't':
c = '\t';
break;
case 'b':
c = '\b';
break;
case 'f':
c = '\f';
break;
}
}
if (mode > 2)
{
num = num * 8 + (c - '0');
if (mode++ == 5 || (*p < '0' || *p > '9'))
{
c = num;
mode = 0;
}
}
if (mode)
continue;
if (cp && n != 1)
{
*cp++ = c;
n--;
}
}
if (cp)
{
*cp++ = 0;
*tepp = cp;
debug2("'%s' found in extra_incap -> %s\n", cap, tep);
return tep;
}
}
return 0;
}
static char *
e_tgetstr(cap, tepp)
char *cap;
char **tepp;
{
char *tep;
if ((tep = findcap(cap, tepp, 0)))
return (*tep == '@') ? 0 : tep;
return tgetstr(cap, tepp);
}
static int
e_tgetflag(cap)
char *cap;
{
char buf[2], *bufp;
char *tep;
bufp = buf;
if ((tep = findcap(cap, &bufp, 2)))
return (*tep == '@') ? 0 : 1;
return tgetflag(cap) > 0;
}
static int
e_tgetnum(cap)
char *cap;
{
char buf[20], *bufp;
char *tep, c;
int res, base = 10;
bufp = buf;
if ((tep = findcap(cap, &bufp, 20)))
{
c = *tep;
if (c == '@')
return -1;
if (c == '0')
base = 8;
res = 0;
while ((c = *tep++) >= '0' && c <= '9')
res = res * base + (c - '0');
return res;
}
return tgetnum(cap);
}
screen-4.1.0/window.c 0000644 0001750 0001750 00000146377 11720315605 012530 0 ustar abe abe /* Copyright (c) 2010
* Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
* Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
* 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
* http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
****************************************************************
*/
#include
#include
#include
#include
#ifndef sun
# include
#endif
#include "config.h"
#include "screen.h"
#include "extern.h"
#include "logfile.h" /* logfopen() */
extern struct display *displays, *display;
extern struct win *windows, *fore, *console_window;
extern char *ShellArgs[];
extern char *ShellProg;
extern char screenterm[];
extern char *screenlogfile;
extern char HostName[];
extern int TtyMode;
extern int SilenceWait;
extern int real_uid, real_gid, eff_uid, eff_gid;
extern char Termcap[];
extern char **NewEnv;
extern int visual_bell, maxwin;
extern struct event logflushev;
extern int log_flush, logtstamp_after;
extern int ZombieKey_destroy, ZombieKey_resurrect, ZombieKey_onerror;
extern struct layer *flayer;
extern int maxusercount;
extern int pty_preopen;
#ifdef ZMODEM
extern int zmodem_mode;
extern struct mchar mchar_blank;
extern char *zmodem_sendcmd;
extern char *zmodem_recvcmd;
#endif
#if defined(TIOCSWINSZ) || defined(TIOCGWINSZ)
extern struct winsize glwz;
#endif
#ifdef O_NOCTTY
extern int separate_sids;
#endif
static void WinProcess __P((char **, int *));
static void WinRedisplayLine __P((int, int, int, int));
static void WinClearLine __P((int, int, int, int));
static int WinRewrite __P((int, int, int, struct mchar *, int));
static int WinResize __P((int, int));
static void WinRestore __P((void));
static int DoAutolf __P((char *, int *, int));
static void ZombieProcess __P((char **, int *));
static void win_readev_fn __P((struct event *, char *));
static void win_writeev_fn __P((struct event *, char *));
static int muchpending __P((struct win *, struct event *));
#ifdef COPY_PASTE
static void paste_slowev_fn __P((struct event *, char *));
#endif
#ifdef PSEUDOS
static void pseu_readev_fn __P((struct event *, char *));
static void pseu_writeev_fn __P((struct event *, char *));
#endif
static void win_silenceev_fn __P((struct event *, char *));
static void win_destroyev_fn __P((struct event *, char *));
static int OpenDevice __P((char **, int, int *, char **));
static int ForkWindow __P((struct win *, char **, char *));
#ifdef ZMODEM
static void zmodem_found __P((struct win *, int, char *, int));
static void zmodem_fin __P((char *, int, char *));
static int zmodem_parse __P((struct win *, char *, int));
#endif
struct win **wtab; /* window table */
int VerboseCreate = 0; /* XXX move this to user.h */
char DefaultShell[] = "/bin/sh";
#ifndef HAVE_EXECVPE
static char DefaultPath[] = ":/usr/ucb:/bin:/usr/bin";
#endif
/* keep this in sync with the structure definition in window.h */
struct NewWindow nwin_undef =
{
-1, /* StartAt */
(char *)0, /* aka */
(char **)0, /* args */
(char *)0, /* dir */
(char *)0, /* term */
-1, /* aflag */
-1, /* flowflag */
-1, /* lflag */
-1, /* histheight */
-1, /* monitor */
-1, /* wlock */
-1, /* silence */
-1, /* wrap */
-1, /* logging */
-1, /* slowpaste */
-1, /* gr */
-1, /* c1 */
-1, /* bce */
-1, /* encoding */
(char *)0, /* hstatus */
(char *)0 /* charset */
};
struct NewWindow nwin_default =
{
0, /* StartAt */
0, /* aka */
ShellArgs, /* args */
0, /* dir */
screenterm, /* term */
0, /* aflag */
1*FLOW_NOW, /* flowflag */
LOGINDEFAULT, /* lflag */
DEFAULTHISTHEIGHT, /* histheight */
MON_OFF, /* monitor */
WLOCK_OFF, /* wlock */
0, /* silence */
1, /* wrap */
0, /* logging */
0, /* slowpaste */
0, /* gr */
1, /* c1 */
0, /* bce */
0, /* encoding */
(char *)0, /* hstatus */
(char *)0 /* charset */
};
struct NewWindow nwin_options;
static int const_IOSIZE = IOSIZE;
static int const_one = 1;
void
nwin_compose(def, new, res)
struct NewWindow *def, *new, *res;
{
#define COMPOSE(x) res->x = new->x != nwin_undef.x ? new->x : def->x
COMPOSE(StartAt);
COMPOSE(aka);
COMPOSE(args);
COMPOSE(dir);
COMPOSE(term);
COMPOSE(aflag);
COMPOSE(flowflag);
COMPOSE(lflag);
COMPOSE(histheight);
COMPOSE(monitor);
COMPOSE(wlock);
COMPOSE(silence);
COMPOSE(wrap);
COMPOSE(Lflag);
COMPOSE(slow);
COMPOSE(gr);
COMPOSE(c1);
COMPOSE(bce);
COMPOSE(encoding);
COMPOSE(hstatus);
COMPOSE(charset);
#undef COMPOSE
}
/*****************************************************************
*
* The window layer functions
*/
struct LayFuncs WinLf =
{
WinProcess,
0,
WinRedisplayLine,
WinClearLine,
WinRewrite,
WinResize,
WinRestore,
0
};
static int
DoAutolf(buf, lenp, fr)
char *buf;
int *lenp;
int fr;
{
char *p;
int len = *lenp;
int trunc = 0;
for (p = buf; len > 0; p++, len--)
{
if (*p != '\r')
continue;
if (fr-- <= 0)
{
trunc++;
len--;
}
if (len == 0)
break;
bcopy(p, p + 1, len++);
p[1] = '\n';
}
*lenp = p - buf;
return trunc;
}
static void
WinProcess(bufpp, lenp)
char **bufpp;
int *lenp;
{
int l2 = 0, f, *ilen, l = *lenp, trunc;
char *ibuf;
debug1("WinProcess: %d bytes\n", *lenp);
fore = (struct win *)flayer->l_data;
if (fore->w_type == W_TYPE_GROUP)
{
*bufpp += *lenp;
*lenp = 0;
return;
}
if (fore->w_ptyfd < 0) /* zombie? */
{
ZombieProcess(bufpp, lenp);
return;
}
#ifdef MULTIUSER
/* a pending writelock is this:
* fore->w_wlock == WLOCK_AUTO, fore->w_wlockuse = NULL
* The user who wants to use this window next, will get the lock, if he can.
*/
if (display && fore->w_wlock == WLOCK_AUTO &&
!fore->w_wlockuser && !AclCheckPermWin(D_user, ACL_WRITE, fore))
{
fore->w_wlockuser = D_user;
debug2("window %d: pending writelock grabbed by user %s\n",
fore->w_number, fore->w_wlockuser->u_name);
}
/* if w_wlock is set, only one user may write, else we check acls */
if (display && ((fore->w_wlock == WLOCK_OFF) ?
AclCheckPermWin(D_user, ACL_WRITE, fore) :
(D_user != fore->w_wlockuser)))
{
debug2("window %d, user %s: ", fore->w_number, D_user->u_name);
debug2("writelock %d (wlockuser %s)\n", fore->w_wlock,
fore->w_wlockuser ? fore->w_wlockuser->u_name : "NULL");
Msg(0, "write: permission denied (user %s)", D_user->u_name);
*bufpp += *lenp;
*lenp = 0;
return;
}
#endif /* MULTIUSER */
#ifdef BUILTIN_TELNET
if (fore->w_type == W_TYPE_TELNET && TelIsline(fore) && *bufpp != fore->w_telbuf)
{
TelProcessLine(bufpp, lenp);
return;
}
#endif
#ifdef PSEUDOS
if (W_UWP(fore))
{
/* we send the user input to our pseudowin */
ibuf = fore->w_pwin->p_inbuf; ilen = &fore->w_pwin->p_inlen;
f = sizeof(fore->w_pwin->p_inbuf) - *ilen;
}
else
#endif /* PSEUDOS */
{
/* we send the user input to the window */
ibuf = fore->w_inbuf; ilen = &fore->w_inlen;
f = sizeof(fore->w_inbuf) - *ilen;
}
if (l > f)
l = f;
#ifdef BUILTIN_TELNET
while (l > 0)
#else
if (l > 0)
#endif
{
l2 = l;
bcopy(*bufpp, ibuf + *ilen, l2);
if (fore->w_autolf && (trunc = DoAutolf(ibuf + *ilen, &l2, f - l2)))
l -= trunc;
#ifdef BUILTIN_TELNET
if (fore->w_type == W_TYPE_TELNET && (trunc = DoTelnet(ibuf + *ilen, &l2, f - l2)))
{
l -= trunc;
if (fore->w_autolf)
continue; /* need exact value */
}
#endif
*ilen += l2;
*bufpp += l;
*lenp -= l;
return;
}
}
static void
ZombieProcess(bufpp, lenp)
char **bufpp;
int *lenp;
{
int l = *lenp;
char *buf = *bufpp, b1[10], b2[10];
debug1("ZombieProcess: %d bytes\n", *lenp);
fore = (struct win *)flayer->l_data;
ASSERT(fore->w_ptyfd < 0);
*bufpp += *lenp;
*lenp = 0;
for (; l-- > 0; buf++)
{
if (*(unsigned char *)buf == ZombieKey_destroy)
{
debug1("Turning undead: %d\n", fore->w_number);
KillWindow(fore);
return;
}
if (*(unsigned char *)buf == ZombieKey_resurrect)
{
debug1("Resurrecting Zombie: %d\n", fore->w_number);
WriteString(fore, "\r\n", 2);
RemakeWindow(fore);
return;
}
}
b1[AddXChar(b1, ZombieKey_destroy)] = '\0';
b2[AddXChar(b2, ZombieKey_resurrect)] = '\0';
Msg(0, "Press %s to destroy or %s to resurrect window", b1, b2);
}
static void
WinRedisplayLine(y, from, to, isblank)
int y, from, to, isblank;
{
debug3("WinRedisplayLine %d %d %d\n", y, from, to);
if (y < 0)
return;
fore = (struct win *)flayer->l_data;
if (from == 0 && y > 0 && fore->w_mlines[y - 1].image[fore->w_width] == 0)
LCDisplayLineWrap(&fore->w_layer, &fore->w_mlines[y], y, from, to, isblank);
else
LCDisplayLine(&fore->w_layer, &fore->w_mlines[y], y, from, to, isblank);
}
static int
WinRewrite(y, x1, x2, rend, doit)
int y, x1, x2, doit;
struct mchar *rend;
{
register int cost, dx;
register unsigned char *p, *i;
#ifdef FONT
register unsigned char *f;
#endif
#ifdef COLOR
register unsigned char *c;
# ifdef COLORS256
register unsigned char *cx;
# endif
#endif
debug3("WinRewrite %d, %d-%d\n", y, x1, x2);
fore = (struct win *)flayer->l_data;
dx = x2 - x1 + 1;
if (doit)
{
i = fore->w_mlines[y].image + x1;
while (dx-- > 0)
PUTCHAR(*i++);
return 0;
}
p = fore->w_mlines[y].attr + x1;
#ifdef FONT
f = fore->w_mlines[y].font + x1;
# ifdef DW_CHARS
if (is_dw_font(rend->font))
return EXPENSIVE;
# endif
# ifdef UTF8
if (fore->w_encoding && fore->w_encoding != UTF8 && D_encoding == UTF8 && ContainsSpecialDeffont(fore->w_mlines + y, x1, x2, fore->w_encoding))
return EXPENSIVE;
# endif
#endif
#ifdef COLOR
c = fore->w_mlines[y].color + x1;
# ifdef COLORS256
cx = fore->w_mlines[y].colorx + x1;
# endif
#endif
cost = dx = x2 - x1 + 1;
while(dx-- > 0)
{
if (*p++ != rend->attr)
return EXPENSIVE;
#ifdef FONT
if (*f++ != rend->font)
return EXPENSIVE;
#endif
#ifdef COLOR
if (*c++ != rend->color)
return EXPENSIVE;
# ifdef COLORS256
if (*cx++ != rend->colorx)
return EXPENSIVE;
# endif
#endif
}
return cost;
}
static void
WinClearLine(y, xs, xe, bce)
int y, xs, xe, bce;
{
fore = (struct win *)flayer->l_data;
debug3("WinClearLine %d %d-%d\n", y, xs, xe);
LClearLine(flayer, y, xs, xe, bce, &fore->w_mlines[y]);
}
static int
WinResize(wi, he)
int wi, he;
{
fore = (struct win *)flayer->l_data;
ChangeWindowSize(fore, wi, he, fore->w_histheight);
return 0;
}
static void
WinRestore()
{
struct canvas *cv;
fore = (struct win *)flayer->l_data;
debug1("WinRestore: win %p\n", fore);
for (cv = flayer->l_cvlist; cv; cv = cv->c_next)
{
display = cv->c_display;
if (cv != D_forecv)
continue;
/* ChangeScrollRegion(fore->w_top, fore->w_bot); */
KeypadMode(fore->w_keypad);
CursorkeysMode(fore->w_cursorkeys);
SetFlow(fore->w_flow & FLOW_NOW);
InsertMode(fore->w_insert);
ReverseVideo(fore->w_revvid);
CursorVisibility(fore->w_curinv ? -1 : fore->w_curvvis);
MouseMode(fore->w_mouse);
}
}
/*****************************************************************/
/*
* DoStartLog constructs a path for the "want to be logfile" in buf and
* attempts logfopen.
*
* returns 0 on success.
*/
int
DoStartLog(w, buf, bufsize)
struct win *w;
char *buf;
int bufsize;
{
int n;
if (!w || !buf)
return -1;
strncpy(buf, MakeWinMsg(screenlogfile, w, '%'), bufsize - 1);
buf[bufsize - 1] = 0;
debug2("DoStartLog: win %d, file %s\n", w->w_number, buf);
if (w->w_log != NULL)
logfclose(w->w_log);
if ((w->w_log = logfopen(buf, islogfile(buf) ? NULL : secfopen(buf, "a"))) == NULL)
return -2;
if (!logflushev.queued)
{
n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
if (n)
{
SetTimeout(&logflushev, n * 1000);
evenq(&logflushev);
}
}
return 0;
}
/*
* Umask & wlock are set for the user of the display,
* The display d (if specified) switches to that window.
*/
int
MakeWindow(newwin)
struct NewWindow *newwin;
{
register struct win **pp, *p;
register int n, i;
int f = -1;
struct NewWindow nwin;
int type, startat;
char *TtyName;
#ifdef MULTIUSER
extern struct acluser *users;
#endif
if (!wtab)
{
if (!maxwin)
maxwin = MAXWIN;
wtab = calloc(maxwin, sizeof(struct win *));
}
debug1("NewWindow: StartAt %d\n", newwin->StartAt);
debug1("NewWindow: aka %s\n", newwin->aka?newwin->aka:"NULL");
debug1("NewWindow: dir %s\n", newwin->dir?newwin->dir:"NULL");
debug1("NewWindow: term %s\n", newwin->term?newwin->term:"NULL");
nwin_compose(&nwin_default, newwin, &nwin);
debug1("NWin: aka %s\n", nwin.aka ? nwin.aka : "NULL");
debug1("NWin: wlock %d\n", nwin.wlock);
debug1("NWin: Lflag %d\n", nwin.Lflag);
startat = nwin.StartAt < maxwin ? nwin.StartAt : 0;
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;
}
#if defined(USRLIMIT) && defined(UTMPOK)
/*
* Count current number of users, if logging windows in.
*/
if (nwin.lflag && CountUsers() >= USRLIMIT)
{
Msg(0, "User limit reached. Window will not be logged in.");
nwin.lflag = 0;
}
#endif
n = pp - wtab;
debug1("Makewin creating %d\n", n);
if ((f = OpenDevice(nwin.args, nwin.lflag, &type, &TtyName)) < 0)
return -1;
if (type == W_TYPE_GROUP)
f = -1;
if ((p = (struct win *)calloc(1, sizeof(struct win))) == 0)
{
close(f);
Msg(0, "%s", strnomem);
return -1;
}
#ifdef UTMPOK
if (type != W_TYPE_PTY)
nwin.lflag = 0;
#endif
p->w_type = type;
/* save the command line so that zombies can be resurrected */
for (i = 0; nwin.args[i] && i < MAXARGS - 1; i++)
p->w_cmdargs[i] = SaveStr(nwin.args[i]);
p->w_cmdargs[i] = 0;
if (nwin.dir)
p->w_dir = SaveStr(nwin.dir);
if (nwin.term)
p->w_term = SaveStr(nwin.term);
p->w_number = n;
p->w_group = 0;
if (fore && fore->w_type == W_TYPE_GROUP)
p->w_group = fore;
else if (fore && fore->w_group)
p->w_group = fore->w_group;
#ifdef MULTIUSER
/*
* This is dangerous: without a display we use creators umask
* This is intended to be useful for detached startup.
* But is still better than default bits with a NULL user.
*/
if (NewWindowAcl(p, display ? D_user : users))
{
free((char *)p);
close(f);
Msg(0, "%s", strnomem);
return -1;
}
#endif
p->w_layer.l_next = 0;
p->w_layer.l_bottom = &p->w_layer;
p->w_layer.l_layfn = &WinLf;
p->w_layer.l_data = (char *)p;
p->w_savelayer = &p->w_layer;
p->w_pdisplay = 0;
p->w_lastdisp = 0;
#ifdef MULTIUSER
if (display && !AclCheckPermWin(D_user, ACL_WRITE, p))
p->w_wlockuser = D_user;
p->w_wlock = nwin.wlock;
#endif
p->w_ptyfd = f;
p->w_aflag = nwin.aflag;
p->w_flow = nwin.flowflag | ((nwin.flowflag & FLOW_AUTOFLAG) ? (FLOW_AUTO|FLOW_NOW) : FLOW_AUTO);
if (!nwin.aka)
nwin.aka = Filename(nwin.args[0]);
strncpy(p->w_akabuf, nwin.aka, sizeof(p->w_akabuf) - 1);
if ((nwin.aka = rindex(p->w_akabuf, '|')) != NULL)
{
p->w_autoaka = 0;
*nwin.aka++ = 0;
p->w_title = nwin.aka;
p->w_akachange = nwin.aka + strlen(nwin.aka);
}
else
p->w_title = p->w_akachange = p->w_akabuf;
if (nwin.hstatus)
p->w_hstatus = SaveStr(nwin.hstatus);
p->w_monitor = nwin.monitor;
#ifdef MULTIUSER
if (p->w_monitor == MON_ON)
{
/* always tell all users */
for (i = 0; i < maxusercount; i++)
ACLBYTE(p->w_mon_notify, i) |= ACLBIT(i);
}
#endif
/*
* defsilence by Lloyd Zusman (zusman_lloyd@jpmorgan.com)
*/
p->w_silence = nwin.silence;
p->w_silencewait = SilenceWait;
#ifdef MULTIUSER
if (p->w_silence == SILENCE_ON)
{
/* always tell all users */
for (i = 0; i < maxusercount; i++)
ACLBYTE(p->w_lio_notify, i) |= ACLBIT(i);
}
#endif
#ifdef COPY_PASTE
p->w_slowpaste = nwin.slow;
#else
nwin.histheight = 0;
#endif
p->w_norefresh = 0;
strncpy(p->w_tty, TtyName, MAXSTR - 1);
#if 0
/* XXX Fixme display resize */
if (ChangeWindowSize(p, display ? D_defwidth : 80,
display ? D_defheight : 24,
nwin.histheight))
{
FreeWindow(p);
return -1;
}
#else
if (ChangeWindowSize(p, display ? D_forecv->c_xe - D_forecv->c_xs + 1: 80,
display ? D_forecv->c_ye - D_forecv->c_ys + 1 : 24,
nwin.histheight))
{
FreeWindow(p);
return -1;
}
#endif
p->w_encoding = nwin.encoding;
ResetWindow(p); /* sets w_wrap, w_c1, w_gr, w_bce */
#ifdef FONT
if (nwin.charset)
SetCharsets(p, nwin.charset);
#endif
if (VerboseCreate && type != W_TYPE_GROUP)
{
struct display *d = display; /* WriteString zaps display */
WriteString(p, ":screen (", 9);
WriteString(p, p->w_title, strlen(p->w_title));
WriteString(p, "):", 2);
for (f = 0; p->w_cmdargs[f]; f++)
{
WriteString(p, " ", 1);
WriteString(p, p->w_cmdargs[f], strlen(p->w_cmdargs[f]));
}
WriteString(p, "\r\n", 2);
display = d;
}
p->w_deadpid = 0;
p->w_pid = 0;
#ifdef PSEUDOS
p->w_pwin = 0;
#endif
#ifdef BUILTIN_TELNET
if (type == W_TYPE_TELNET)
{
if (TelConnect(p))
{
FreeWindow(p);
return -1;
}
}
else
#endif
if (type == W_TYPE_PTY)
{
p->w_pid = ForkWindow(p, nwin.args, TtyName);
if (p->w_pid < 0)
{
FreeWindow(p);
return -1;
}
}
/*
* Place the new window at the head of the most-recently-used list.
*/
if (display && D_fore)
D_other = D_fore;
*pp = p;
p->w_next = windows;
windows = p;
if (type == W_TYPE_GROUP)
{
SetForeWindow(p);
Activate(p->w_norefresh);
WindowChanged((struct win*)0, 'w');
WindowChanged((struct win*)0, 'W');
WindowChanged((struct win*)0, 0);
return n;
}
p->w_lflag = nwin.lflag;
#ifdef UTMPOK
p->w_slot = (slot_t)-1;
# ifdef LOGOUTOK
debug1("MakeWindow will %slog in.\n", nwin.lflag?"":"not ");
if (nwin.lflag & 1)
# else /* LOGOUTOK */
debug1("MakeWindow will log in, LOGOUTOK undefined in config.h%s.\n",
nwin.lflag?"":" (although lflag=0)");
# endif /* LOGOUTOK */
{
p->w_slot = (slot_t)0;
if (display || (p->w_lflag & 2))
SetUtmp(p);
}
# ifdef CAREFULUTMP
CarefulUtmp(); /* If all 've been zombies, we've had no slot */
# endif
#endif /* UTMPOK */
if (nwin.Lflag)
{
char buf[1024];
DoStartLog(p, buf, sizeof(buf));
}
p->w_readev.fd = p->w_writeev.fd = p->w_ptyfd;
p->w_readev.type = EV_READ;
p->w_writeev.type = EV_WRITE;
p->w_readev.data = p->w_writeev.data = (char *)p;
p->w_readev.handler = win_readev_fn;
p->w_writeev.handler = win_writeev_fn;
p->w_writeev.condpos = &p->w_inlen;
evenq(&p->w_readev);
evenq(&p->w_writeev);
#ifdef COPY_PASTE
p->w_paster.pa_slowev.type = EV_TIMEOUT;
p->w_paster.pa_slowev.data = (char *)&p->w_paster;
p->w_paster.pa_slowev.handler = paste_slowev_fn;
#endif
p->w_silenceev.type = EV_TIMEOUT;
p->w_silenceev.data = (char *)p;
p->w_silenceev.handler = win_silenceev_fn;
if (p->w_silence > 0)
{
debug("New window has silence enabled.\n");
SetTimeout(&p->w_silenceev, p->w_silencewait * 1000);
evenq(&p->w_silenceev);
}
p->w_destroyev.type = EV_TIMEOUT;
p->w_destroyev.data = 0;
p->w_destroyev.handler = win_destroyev_fn;
SetForeWindow(p);
Activate(p->w_norefresh);
WindowChanged((struct win*)0, 'w');
WindowChanged((struct win*)0, 'W');
WindowChanged((struct win*)0, 0);
return n;
}
/*
* Resurrect a window from Zombie state.
* The command vector is therefore stored in the window structure.
* Note: The terminaltype defaults to screenterm again, the current
* working directory is lost.
*/
int
RemakeWindow(p)
struct win *p;
{
char *TtyName;
int lflag, f;
lflag = nwin_default.lflag;
if ((f = OpenDevice(p->w_cmdargs, lflag, &p->w_type, &TtyName)) < 0)
return -1;
evdeq(&p->w_destroyev); /* no re-destroy of resurrected zombie */
strncpy(p->w_tty, *TtyName ? TtyName : p->w_title, MAXSTR - 1);
p->w_ptyfd = f;
p->w_readev.fd = f;
p->w_writeev.fd = f;
evenq(&p->w_readev);
evenq(&p->w_writeev);
if (VerboseCreate)
{
struct display *d = display; /* WriteString zaps display */
WriteString(p, ":screen (", 9);
WriteString(p, p->w_title, strlen(p->w_title));
WriteString(p, "):", 2);
for (f = 0; p->w_cmdargs[f]; f++)
{
WriteString(p, " ", 1);
WriteString(p, p->w_cmdargs[f], strlen(p->w_cmdargs[f]));
}
WriteString(p, "\r\n", 2);
display = d;
}
p->w_deadpid = 0;
p->w_pid = 0;
#ifdef BUILTIN_TELNET
if (p->w_type == W_TYPE_TELNET)
{
if (TelConnect(p))
return -1;
}
else
#endif
if (p->w_type == W_TYPE_PTY)
{
p->w_pid = ForkWindow(p, p->w_cmdargs, TtyName);
if (p->w_pid < 0)
return -1;
}
#ifdef UTMPOK
if (p->w_slot == (slot_t)0 && (display || (p->w_lflag & 2)))
SetUtmp(p);
# ifdef CAREFULUTMP
CarefulUtmp(); /* If all 've been zombies, we've had no slot */
# endif
#endif
WindowChanged(p, 'f');
return p->w_number;
}
void
CloseDevice(wp)
struct win *wp;
{
if (wp->w_ptyfd < 0)
return;
if (wp->w_type == W_TYPE_PTY)
{
/* pty 4 SALE */
(void)chmod(wp->w_tty, 0666);
(void)chown(wp->w_tty, 0, 0);
}
close(wp->w_ptyfd);
wp->w_ptyfd = -1;
wp->w_tty[0] = 0;
evdeq(&wp->w_readev);
evdeq(&wp->w_writeev);
#ifdef BUILTIN_TELNET
evdeq(&wp->w_telconnev);
#endif
wp->w_readev.fd = wp->w_writeev.fd = -1;
}
void
FreeWindow(wp)
struct win *wp;
{
struct display *d;
int i;
struct canvas *cv, *ncv;
struct layer *l;
debug1("FreeWindow %d\n", wp ? wp->w_number: -1);
#ifdef PSEUDOS
if (wp->w_pwin)
FreePseudowin(wp);
#endif
#ifdef UTMPOK
RemoveUtmp(wp);
#endif
CloseDevice(wp);
if (wp == console_window)
{
TtyGrabConsole(-1, -1, "free");
console_window = 0;
}
if (wp->w_log != NULL)
logfclose(wp->w_log);
ChangeWindowSize(wp, 0, 0, 0);
if (wp->w_type == W_TYPE_GROUP)
{
struct win *win;
for (win = windows; win; win = win->w_next)
if (win->w_group == wp)
win->w_group = wp->w_group;
}
if (wp->w_hstatus)
free(wp->w_hstatus);
for (i = 0; wp->w_cmdargs[i]; i++)
free(wp->w_cmdargs[i]);
if (wp->w_dir)
free(wp->w_dir);
if (wp->w_term)
free(wp->w_term);
for (d = displays; d; d = d->d_next)
{
if (d->d_other == wp)
d->d_other = d->d_fore && d->d_fore->w_next != wp ? d->d_fore->w_next : wp->w_next;
if (d->d_fore == wp)
d->d_fore = NULL;
for (cv = d->d_cvlist; cv; cv = cv->c_next)
{
for (l = cv->c_layer; l; l = l->l_next)
if (l->l_layfn == &WinLf)
break;
if (!l)
continue;
if ((struct win *)l->l_data != wp)
continue;
if (cv->c_layer == wp->w_savelayer)
wp->w_savelayer = 0;
KillLayerChain(cv->c_layer);
}
}
if (wp->w_savelayer)
KillLayerChain(wp->w_savelayer);
for (cv = wp->w_layer.l_cvlist; cv; cv = ncv)
{
ncv = cv->c_lnext;
cv->c_layer = &cv->c_blank;
cv->c_blank.l_cvlist = cv;
cv->c_lnext = 0;
cv->c_xoff = cv->c_xs;
cv->c_yoff = cv->c_ys;
RethinkViewportOffsets(cv);
}
wp->w_layer.l_cvlist = 0;
if (flayer == &wp->w_layer)
flayer = 0;
LayerCleanupMemory(&wp->w_layer);
#ifdef MULTIUSER
FreeWindowAcl(wp);
#endif /* MULTIUSER */
evdeq(&wp->w_readev); /* just in case */
evdeq(&wp->w_writeev); /* just in case */
evdeq(&wp->w_silenceev);
evdeq(&wp->w_destroyev);
#ifdef COPY_PASTE
FreePaster(&wp->w_paster);
#endif
free((char *)wp);
}
static int
OpenDevice(args, lflag, typep, namep)
char **args;
int lflag;
int *typep;
char **namep;
{
char *arg = args[0];
struct stat st;
int f;
if (!arg)
return -1;
if (strcmp(arg, "//group") == 0)
{
*typep = W_TYPE_GROUP;
*namep = "telnet";
return 0;
}
#ifdef BUILTIN_TELNET
if (strcmp(arg, "//telnet") == 0)
{
f = TelOpen(args + 1);
lflag = 0;
*typep = W_TYPE_TELNET;
*namep = "telnet";
}
else
#endif
if (strncmp(arg, "//", 2) == 0)
{
Msg(0, "Invalid argument '%s'", arg);
return -1;
}
else if ((stat(arg, &st)) == 0 && S_ISCHR(st.st_mode))
{
if (access(arg, R_OK | W_OK) == -1)
{
Msg(errno, "Cannot access line '%s' for R/W", arg);
return -1;
}
debug("OpenDevice: OpenTTY\n");
if ((f = OpenTTY(arg, args[1])) < 0)
return -1;
lflag = 0;
*typep = W_TYPE_PLAIN;
*namep = arg;
}
else
{
*typep = W_TYPE_PTY;
f = OpenPTY(namep);
if (f == -1)
{
Msg(0, "No more PTYs.");
return -1;
}
#ifdef TIOCPKT
{
int flag = 1;
if (ioctl(f, TIOCPKT, (char *)&flag))
{
Msg(errno, "TIOCPKT ioctl");
close(f);
return -1;
}
}
#endif /* TIOCPKT */
}
debug1("fcntl(%d, F_SETFL, FNBLOCK)\n", f);
(void) fcntl(f, F_SETFL, FNBLOCK);
#ifdef linux
/*
* Tenebreux (zeus@ns.acadiacom.net) has Linux 1.3.70 where select
* gets confused in the following condition:
* Open a pty-master side, request a flush on it, then set packet
* mode and call select(). Select will return a possible read, where
* the one byte response to the flush can be found. Select will
* thereafter return a possible read, which yields I/O error.
*
* If we request another flush *after* switching into packet mode,
* this I/O error does not occur. We receive a single response byte
* although we send two flush requests now.
*
* Maybe we should not flush at all.
*
* 10.5.96 jw.
*/
if (*typep == W_TYPE_PTY || *typep == W_TYPE_PLAIN)
tcflush(f, TCIOFLUSH);
#endif
if (*typep != W_TYPE_PTY)
return f;
#ifndef PTYROFS
#ifdef PTYGROUP
if (chown(*namep, real_uid, PTYGROUP) && !eff_uid)
#else
if (chown(*namep, real_uid, real_gid) && !eff_uid)
#endif
{
Msg(errno, "chown tty");
close(f);
return -1;
}
#ifdef UTMPOK
if (chmod(*namep, lflag ? TtyMode : (TtyMode & ~022)) && !eff_uid)
#else
if (chmod(*namep, TtyMode) && !eff_uid)
#endif
{
Msg(errno, "chmod tty");
close(f);
return -1;
}
#endif
return f;
}
/*
* Fields w_width, w_height, aflag, number (and w_tty)
* are read from struct win *win. No fields written.
* If pwin is nonzero, filedescriptors are distributed
* between win->w_tty and open(ttyn)
*
*/
static int
ForkWindow(win, args, ttyn)
struct win *win;
char **args, *ttyn;
{
int pid;
char tebuf[25];
char ebuf[20];
char shellbuf[7 + MAXPATHLEN];
char *proc;
#ifndef TIOCSWINSZ
char libuf[20], cobuf[20];
#endif
int newfd;
int w = win->w_width;
int h = win->w_height;
#ifdef PSEUDOS
int i, pat, wfdused;
struct pseudowin *pwin = win->w_pwin;
#endif
int slave = -1;
#ifdef O_NOCTTY
if (pty_preopen)
{
debug("pre-opening slave...\n");
if ((slave = open(ttyn, O_RDWR|O_NOCTTY)) == -1)
{
Msg(errno, "ttyn");
return -1;
}
}
#endif
debug("forking...\n");
proc = *args;
if (proc == 0)
{
args = ShellArgs;
proc = *args;
}
fflush(stdout);
fflush(stderr);
switch (pid = fork())
{
case -1:
Msg(errno, "fork");
break;
case 0:
signal(SIGHUP, SIG_DFL);
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
#ifdef BSDJOBS
signal(SIGTTIN, SIG_DFL);
signal(SIGTTOU, SIG_DFL);
#endif
#ifdef SIGPIPE
signal(SIGPIPE, SIG_DFL);
#endif
#ifdef SIGXFSZ
signal(SIGXFSZ, SIG_DFL);
#endif
displays = 0; /* beware of Panic() */
if (setgid(real_gid) || setuid(real_uid))
Panic(errno, "Setuid/gid");
eff_uid = real_uid;
eff_gid = real_gid;
#ifdef PSEUDOS
if (!pwin) /* ignore directory if pseudo */
#endif
if (win->w_dir && *win->w_dir && chdir(win->w_dir))
Panic(errno, "Cannot chdir to %s", win->w_dir);
if (display)
{
brktty(D_userfd);
freetty();
}
else
brktty(-1);
#ifdef DEBUG
if (dfp && dfp != stderr)
fclose(dfp);
#endif
if (slave != -1)
{
close(0);
dup(slave);
close(slave);
closeallfiles(win->w_ptyfd);
slave = dup(0);
}
else
closeallfiles(win->w_ptyfd);
#ifdef DEBUG
if (dfp) /* do not produce child debug, when debug is "off" */
{
char buf[256];
sprintf(buf, "%s/screen.child", DEBUGDIR);
if ((dfp = fopen(buf, "a")) == 0)
dfp = stderr;
else
(void) chmod(buf, 0666);
}
debug1("=== ForkWindow: pid %d\n", (int)getpid());
#endif
/* Close the three /dev/null descriptors */
close(0);
close(1);
close(2);
newfd = -1;
/*
* distribute filedescriptors between the ttys
*/
#ifdef PSEUDOS
pat = pwin ? pwin->p_fdpat :
((F_PFRONT<<(F_PSHIFT*2)) | (F_PFRONT<w_ptyfd);
wfdused = 1;
}
}
if (wfdused)
{
/*
* the pseudo window process should not be surprised with a
* nonblocking filedescriptor. Poor Backend!
*/
debug1("Clearing NBLOCK on window-fd(%d)\n", win->w_ptyfd);
if (fcntl(win->w_ptyfd, F_SETFL, 0))
Msg(errno, "Warning: clear NBLOCK fcntl failed");
}
#else /* PSEUDOS */
# ifdef O_NOCTTY
if (separate_sids)
newfd = open(ttyn, O_RDWR);
else
newfd = open(ttyn, O_RDWR|O_NOCTTY);
# else
newfd = open(ttyn, O_RDWR);
# endif
if (newfd != 0)
Panic(errno, "Cannot open %s", ttyn);
dup(0);
dup(0);
#endif /* PSEUDOS */
close(win->w_ptyfd);
if (slave != -1)
close(slave);
if (newfd >= 0)
{
struct mode fakemode, *modep;
InitPTY(newfd);
if (fgtty(newfd))
Msg(errno, "fgtty");
if (display)
{
debug("ForkWindow: using display tty mode for new child.\n");
modep = &D_OldMode;
}
else
{
debug("No display - creating tty setting\n");
modep = &fakemode;
InitTTY(modep, 0);
#ifdef DEBUG
DebugTTY(modep);
#endif
}
/* We only want echo if the users input goes to the pseudo
* and the pseudo's stdout is not send to the window.
*/
#ifdef PSEUDOS
if (pwin && (!(pat & F_UWP) || (pat & F_PBACK << F_PSHIFT)))
{
debug1("clearing echo on pseudywin fd (pat %x)\n", pat);
# if defined(POSIX) || defined(TERMIO)
modep->tio.c_lflag &= ~ECHO;
modep->tio.c_iflag &= ~ICRNL;
# else
modep->m_ttyb.sg_flags &= ~ECHO;
# endif
}
#endif
SetTTY(newfd, modep);
#ifdef TIOCSWINSZ
glwz.ws_col = w;
glwz.ws_row = h;
(void) ioctl(newfd, TIOCSWINSZ, (char *)&glwz);
#endif
/* Always turn off nonblocking mode */
(void)fcntl(newfd, F_SETFL, 0);
}
#ifndef TIOCSWINSZ
sprintf(libuf, "LINES=%d", h);
sprintf(cobuf, "COLUMNS=%d", w);
NewEnv[5] = libuf;
NewEnv[6] = cobuf;
#endif
#ifdef MAPKEYS
NewEnv[2] = MakeTermcap(display == 0 || win->w_aflag);
#else
if (win->w_aflag)
NewEnv[2] = MakeTermcap(1);
else
NewEnv[2] = Termcap;
#endif
strcpy(shellbuf, "SHELL=");
strncpy(shellbuf + 6, ShellProg + (*ShellProg == '-'), sizeof(shellbuf) - 7);
shellbuf[sizeof(shellbuf) - 1] = 0;
NewEnv[4] = shellbuf;
debug1("ForkWindow: NewEnv[4] = '%s'\n", shellbuf);
if (win->w_term && *win->w_term && strcmp(screenterm, win->w_term) &&
(strlen(win->w_term) < 20))
{
char *s1, *s2, tl;
sprintf(tebuf, "TERM=%s", win->w_term);
debug2("Makewindow %d with %s\n", win->w_number, tebuf);
tl = strlen(win->w_term);
NewEnv[1] = tebuf;
if ((s1 = index(NewEnv[2], '|')))
{
if ((s2 = index(++s1, '|')))
{
if (strlen(NewEnv[2]) - (s2 - s1) + tl < 1024)
{
bcopy(s2, s1 + tl, strlen(s2) + 1);
bcopy(win->w_term, s1, tl);
}
}
}
}
sprintf(ebuf, "WINDOW=%d", win->w_number);
NewEnv[3] = ebuf;
if (*proc == '-')
proc++;
if (!*proc)
proc = DefaultShell;
debug1("calling execvpe %s\n", proc);
execvpe(proc, args, NewEnv);
debug1("exec error: %d\n", errno);
Panic(errno, "Cannot exec '%s'", proc);
default:
break;
}
if (slave != -1)
close(slave);
return pid;
}
#ifndef HAVE_EXECVPE
void
execvpe(prog, args, env)
char *prog, **args, **env;
{
register char *path = NULL, *p;
char buf[1024];
char *shargs[MAXARGS + 1];
register int i, eaccess = 0;
if (rindex(prog, '/'))
path = "";
if (!path && !(path = getenv("PATH")))
path = DefaultPath;
do
{
for (p = buf; *path && *path != ':'; path++)
if (p - buf < (int)sizeof(buf) - 2)
*p++ = *path;
if (p > buf)
*p++ = '/';
if (p - buf + strlen(prog) >= sizeof(buf) - 1)
continue;
strcpy(p, prog);
execve(buf, args, env);
switch (errno)
{
case ENOEXEC:
shargs[0] = DefaultShell;
shargs[1] = buf;
for (i = 1; (shargs[i + 1] = args[i]) != NULL; ++i)
;
execve(DefaultShell, shargs, env);
return;
case EACCES:
eaccess = 1;
break;
case ENOMEM:
case E2BIG:
case ETXTBSY:
return;
}
} while (*path++);
if (eaccess)
errno = EACCES;
}
#endif
#ifdef PSEUDOS
int
winexec(av)
char **av;
{
char **pp;
char *p, *s, *t;
int i, r = 0, l = 0;
struct win *w;
extern struct display *display;
extern struct win *windows;
struct pseudowin *pwin;
int type;
if ((w = display ? fore : windows) == NULL)
return -1;
if (!*av || w->w_pwin)
{
Msg(0, "Filter running: %s", w->w_pwin ? w->w_pwin->p_cmd : "(none)");
return -1;
}
if (w->w_ptyfd < 0)
{
Msg(0, "You feel dead inside.");
return -1;
}
if (!(pwin = (struct pseudowin *)calloc(1, sizeof(struct pseudowin))))
{
Msg(0, "%s", strnomem);
return -1;
}
/* allow ^a:!!./ttytest as a short form for ^a:exec !.. ./ttytest */
for (s = *av; *s == ' '; s++)
;
for (p = s; *p == ':' || *p == '.' || *p == '!'; p++)
;
if (*p != '|')
while (*p && p > s && p[-1] == '.')
p--;
if (*p == '|')
{
l = F_UWP;
p++;
}
if (*p)
av[0] = p;
else
av++;
t = pwin->p_cmd;
for (i = 0; i < 3; i++)
{
*t = (s < p) ? *s++ : '.';
switch (*t++)
{
case '.':
case '|':
l |= F_PFRONT << (i * F_PSHIFT);
break;
case '!':
l |= F_PBACK << (i * F_PSHIFT);
break;
case ':':
l |= F_PBOTH << (i * F_PSHIFT);
break;
}
}
if (l & F_UWP)
{
*t++ = '|';
if ((l & F_PMASK) == F_PFRONT)
{
*pwin->p_cmd = '!';
l ^= F_PFRONT | F_PBACK;
}
}
if (!(l & F_PBACK))
l |= F_UWP;
*t++ = ' ';
pwin->p_fdpat = l;
debug1("winexec: '%#x'\n", pwin->p_fdpat);
l = MAXSTR - 4;
for (pp = av; *pp; pp++)
{
p = *pp;
while (*p && l-- > 0)
*t++ = *p++;
if (l <= 0)
break;
*t++ = ' ';
}
*--t = '\0';
debug1("%s\n", pwin->p_cmd);
if ((pwin->p_ptyfd = OpenDevice(av, 0, &type, &t)) < 0)
{
free((char *)pwin);
return -1;
}
strncpy(pwin->p_tty, t, MAXSTR - 1);
w->w_pwin = pwin;
if (type != W_TYPE_PTY)
{
FreePseudowin(w);
Msg(0, "Cannot only use commands as pseudo win.");
return -1;
}
if (!(pwin->p_fdpat & F_PFRONT))
evdeq(&w->w_readev);
#ifdef TIOCPKT
{
int flag = 0;
if (ioctl(pwin->p_ptyfd, TIOCPKT, (char *)&flag))
{
Msg(errno, "TIOCPKT pwin ioctl");
FreePseudowin(w);
return -1;
}
if (w->w_type == W_TYPE_PTY && !(pwin->p_fdpat & F_PFRONT))
{
if (ioctl(w->w_ptyfd, TIOCPKT, (char *)&flag))
{
Msg(errno, "TIOCPKT win ioctl");
FreePseudowin(w);
return -1;
}
}
}
#endif /* TIOCPKT */
pwin->p_readev.fd = pwin->p_writeev.fd = pwin->p_ptyfd;
pwin->p_readev.type = EV_READ;
pwin->p_writeev.type = EV_WRITE;
pwin->p_readev.data = pwin->p_writeev.data = (char *)w;
pwin->p_readev.handler = pseu_readev_fn;
pwin->p_writeev.handler = pseu_writeev_fn;
pwin->p_writeev.condpos = &pwin->p_inlen;
if (pwin->p_fdpat & (F_PFRONT << F_PSHIFT * 2 | F_PFRONT << F_PSHIFT))
evenq(&pwin->p_readev);
evenq(&pwin->p_writeev);
r = pwin->p_pid = ForkWindow(w, av, t);
if (r < 0)
FreePseudowin(w);
return r;
}
void
FreePseudowin(w)
struct win *w;
{
struct pseudowin *pwin = w->w_pwin;
ASSERT(pwin);
if (fcntl(w->w_ptyfd, F_SETFL, FNBLOCK))
Msg(errno, "Warning: FreePseudowin: NBLOCK fcntl failed");
#ifdef TIOCPKT
if (w->w_type == W_TYPE_PTY && !(pwin->p_fdpat & F_PFRONT))
{
int flag = 1;
if (ioctl(w->w_ptyfd, TIOCPKT, (char *)&flag))
Msg(errno, "Warning: FreePseudowin: TIOCPKT win ioctl");
}
#endif
/* should be able to use CloseDevice() here */
(void)chmod(pwin->p_tty, 0666);
(void)chown(pwin->p_tty, 0, 0);
if (pwin->p_ptyfd >= 0)
close(pwin->p_ptyfd);
evdeq(&pwin->p_readev);
evdeq(&pwin->p_writeev);
if (w->w_readev.condneg == &pwin->p_inlen)
w->w_readev.condpos = w->w_readev.condneg = 0;
evenq(&w->w_readev);
free((char *)pwin);
w->w_pwin = NULL;
}
#endif /* PSEUDOS */
#ifdef MULTIUSER
/*
* returns 0, if the lock really has been released
*/
int
ReleaseAutoWritelock(dis, w)
struct display *dis;
struct win *w;
{
debug2("ReleaseAutoWritelock: user %s, window %d\n",
dis->d_user->u_name, w->w_number);
/* release auto writelock when user has no other display here */
if (w->w_wlock == WLOCK_AUTO && w->w_wlockuser == dis->d_user)
{
struct display *d;
for (d = displays; d; d = d->d_next)
if (( d != dis) && (d->d_fore == w) && (d->d_user == dis->d_user))
break;
debug3("%s %s autolock on win %d\n",
dis->d_user->u_name, d ? "keeps" : "releases", w->w_number);
if (!d)
{
w->w_wlockuser = NULL;
return 0;
}
}
return 1;
}
/*
* returns 0, if the lock really could be obtained
*/
int
ObtainAutoWritelock(d, w)
struct display *d;
struct win *w;
{
if ((w->w_wlock == WLOCK_AUTO) &&
!AclCheckPermWin(d->d_user, ACL_WRITE, w) &&
!w->w_wlockuser)
{
debug2("%s obtained auto writelock for exported window %d\n",
d->d_user->u_name, w->w_number);
w->w_wlockuser = d->d_user;
return 0;
}
return 1;
}
#endif /* MULTIUSER */
/********************************************************************/
#ifdef COPY_PASTE
static void
paste_slowev_fn(ev, data)
struct event *ev;
char *data;
{
struct paster *pa = (struct paster *)data;
struct win *p;
int l = 1;
flayer = pa->pa_pastelayer;
if (!flayer)
pa->pa_pastelen = 0;
if (!pa->pa_pastelen)
return;
p = Layer2Window(flayer);
DoProcess(p, &pa->pa_pasteptr, &l, pa);
pa->pa_pastelen -= 1 - l;
if (pa->pa_pastelen > 0)
{
SetTimeout(&pa->pa_slowev, p->w_slowpaste);
evenq(&pa->pa_slowev);
}
}
#endif
static int
muchpending(p, ev)
struct win *p;
struct event *ev;
{
struct canvas *cv;
for (cv = p->w_layer.l_cvlist; cv; cv = cv->c_lnext)
{
display = cv->c_display;
if (D_status == STATUS_ON_WIN && !D_status_bell)
{
/* wait 'til status is gone */
debug("BLOCKING because of status\n");
ev->condpos = &const_one;
ev->condneg = &D_status;
return 1;
}
debug2("muchpending %s %d: ", D_usertty, D_blocked);
debug3("%d %d %d\n", D_obufp - D_obuf, D_obufmax, D_blocked_fuzz);
if (D_blocked)
continue;
if (D_obufp - D_obuf > D_obufmax + D_blocked_fuzz)
{
if (D_nonblock == 0)
{
debug1("obuf is full, stopping output to display %s\n", D_usertty);
D_blocked = 1;
continue;
}
debug("BLOCKING because of full obuf\n");
ev->condpos = &D_obuffree;
ev->condneg = &D_obuflenmax;
if (D_nonblock > 0 && !D_blockedev.queued)
{
debug1("created timeout of %g secs\n", D_nonblock/1000.);
SetTimeout(&D_blockedev, D_nonblock);
evenq(&D_blockedev);
}
return 1;
}
}
return 0;
}
static void
win_readev_fn(ev, data)
struct event *ev;
char *data;
{
struct win *p = (struct win *)data;
char buf[IOSIZE], *bp;
int size, len;
#ifdef PSEUDOS
int wtop;
#endif
bp = buf;
size = IOSIZE;
#ifdef PSEUDOS
wtop = p->w_pwin && W_WTOP(p);
if (wtop)
{
ASSERT(sizeof(p->w_pwin->p_inbuf) == IOSIZE);
size = IOSIZE - p->w_pwin->p_inlen;
if (size <= 0)
{
ev->condpos = &const_IOSIZE;
ev->condneg = &p->w_pwin->p_inlen;
return;
}
}
#endif
if (p->w_layer.l_cvlist && muchpending(p, ev))
return;
#ifdef ZMODEM
if (!p->w_zdisplay)
#endif
if (p->w_blocked)
{
ev->condpos = &const_one;
ev->condneg = &p->w_blocked;
return;
}
if (ev->condpos)
ev->condpos = ev->condneg = 0;
if ((len = p->w_outlen))
{
p->w_outlen = 0;
WriteString(p, p->w_outbuf, len);
return;
}
debug1("going to read from window fd %d\n", ev->fd);
if ((len = read(ev->fd, buf, size)) < 0)
{
if (errno == EINTR || errno == EAGAIN)
return;
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
if (errno == EWOULDBLOCK)
return;
#endif
debug2("Window %d: read error (errno %d) - killing window\n", p->w_number, errno);
#ifdef BSDWAIT
WindowDied(p, (union wait)0, 0);
#else
WindowDied(p, 0, 0);
#endif
return;
}
if (len == 0)
{
debug1("Window %d: EOF - killing window\n", p->w_number);
#ifdef BSDWAIT
WindowDied(p, (union wait)0, 0);
#else
WindowDied(p, 0, 0);
#endif
return;
}
debug1(" -> %d bytes\n", len);
#ifdef TIOCPKT
if (p->w_type == W_TYPE_PTY)
{
if (buf[0])
{
debug1("PAKET %x\n", buf[0]);
if (buf[0] & TIOCPKT_NOSTOP)
WNewAutoFlow(p, 0);
if (buf[0] & TIOCPKT_DOSTOP)
WNewAutoFlow(p, 1);
}
bp++;
len--;
}
#endif
#ifdef BUILTIN_TELNET
if (p->w_type == W_TYPE_TELNET)
len = TelIn(p, bp, len, buf + sizeof(buf) - (bp + len));
#endif
if (len == 0)
return;
#ifdef ZMODEM
if (zmodem_mode && zmodem_parse(p, bp, len))
return;
#endif
#ifdef PSEUDOS
if (wtop)
{
debug("sending input to pwin\n");
bcopy(bp, p->w_pwin->p_inbuf + p->w_pwin->p_inlen, len);
p->w_pwin->p_inlen += len;
}
#endif
LayPause(&p->w_layer, 1);
WriteString(p, bp, len);
LayPause(&p->w_layer, 0);
return;
}
static void
win_writeev_fn(ev, data)
struct event *ev;
char *data;
{
struct win *p = (struct win *)data;
int len;
if (p->w_inlen)
{
debug2("writing %d bytes to win %d\n", p->w_inlen, p->w_number);
if ((len = write(ev->fd, p->w_inbuf, p->w_inlen)) <= 0)
len = p->w_inlen; /* dead window */
if ((p->w_inlen -= len))
bcopy(p->w_inbuf + len, p->w_inbuf, p->w_inlen);
}
#ifdef COPY_PASTE
if (p->w_paster.pa_pastelen && !p->w_slowpaste)
{
struct paster *pa = &p->w_paster;
flayer = pa->pa_pastelayer;
if (flayer)
DoProcess(p, &pa->pa_pasteptr, &pa->pa_pastelen, pa);
}
#endif
return;
}
#ifdef PSEUDOS
static void
pseu_readev_fn(ev, data)
struct event *ev;
char *data;
{
struct win *p = (struct win *)data;
char buf[IOSIZE];
int size, ptow, len;
size = IOSIZE;
ptow = W_PTOW(p);
if (ptow)
{
ASSERT(sizeof(p->w_inbuf) == IOSIZE);
size = IOSIZE - p->w_inlen;
if (size <= 0)
{
ev->condpos = &const_IOSIZE;
ev->condneg = &p->w_inlen;
return;
}
}
if (p->w_layer.l_cvlist && muchpending(p, ev))
return;
if (p->w_blocked)
{
ev->condpos = &const_one;
ev->condneg = &p->w_blocked;
return;
}
if (ev->condpos)
ev->condpos = ev->condneg = 0;
if ((len = p->w_outlen))
{
p->w_outlen = 0;
WriteString(p, p->w_outbuf, len);
return;
}
if ((len = read(ev->fd, buf, size)) <= 0)
{
if (errno == EINTR || errno == EAGAIN)
return;
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
if (errno == EWOULDBLOCK)
return;
#endif
debug2("Window %d: pseudowin read error (errno %d) -- removing pseudowin\n", p->w_number, len ? errno : 0);
FreePseudowin(p);
return;
}
/* no packet mode on pseudos! */
if (ptow)
{
bcopy(buf, p->w_inbuf + p->w_inlen, len);
p->w_inlen += len;
}
WriteString(p, buf, len);
return;
}
static void
pseu_writeev_fn(ev, data)
struct event *ev;
char *data;
{
struct win *p = (struct win *)data;
struct pseudowin *pw = p->w_pwin;
int len;
ASSERT(pw);
if (pw->p_inlen == 0)
return;
if ((len = write(ev->fd, pw->p_inbuf, pw->p_inlen)) <= 0)
len = pw->p_inlen; /* dead pseudo */
if ((p->w_pwin->p_inlen -= len))
bcopy(p->w_pwin->p_inbuf + len, p->w_pwin->p_inbuf, p->w_pwin->p_inlen);
}
#endif /* PSEUDOS */
static void
win_silenceev_fn(ev, data)
struct event *ev;
char *data;
{
struct win *p = (struct win *)data;
struct canvas *cv;
debug1("FOUND silence win %d\n", p->w_number);
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)
continue; /* user already sees window */
#ifdef MULTIUSER
if (!(ACLBYTE(p->w_lio_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
continue;
#endif
Msg(0, "Window %d: silence for %d seconds", p->w_number, p->w_silencewait);
p->w_silence = SILENCE_FOUND;
WindowChanged(p, 'f');
}
}
static void
win_destroyev_fn(ev, data)
struct event *ev;
char *data;
{
struct win *p = (struct win *)ev->data;
WindowDied(p, p->w_exitstatus, 1);
}
#ifdef ZMODEM
static int
zmodem_parse(p, bp, len)
struct win *p;
char *bp;
int len;
{
int i;
char *b2 = bp;
for (i = 0; i < len; i++, b2++)
{
if (p->w_zauto == 0)
{
for (; i < len; i++, b2++)
if (*b2 == 030)
break;
if (i == len)
break;
if (i > 1 && b2[-1] == '*' && b2[-2] == '*')
p->w_zauto = 3;
continue;
}
if (p->w_zauto > 5 || *b2 == "**\030B00"[p->w_zauto] || (p->w_zauto == 5 && *b2 == '1') || (p->w_zauto == 5 && p->w_zdisplay && *b2 == '8'))
{
if (++p->w_zauto < 6)
continue;
if (p->w_zauto == 6)
p->w_zauto = 0;
if (!p->w_zdisplay)
{
if (i > 6)
WriteString(p, bp, i + 1 - 6);
WriteString(p, "\r\n", 2);
zmodem_found(p, *b2 == '1', b2 + 1, len - i - 1);
return 1;
}
else if (p->w_zauto == 7 || *b2 == '8')
{
int se = p->w_zdisplay->d_blocked == 2 ? 'O' : '\212';
for (; i < len; i++, b2++)
if (*b2 == se)
break;
if (i < len)
{
zmodem_abort(p, 0);
D_blocked = 0;
D_readev.condpos = D_readev.condneg = 0;
while (len-- > 0)
AddChar(*bp++);
Flush(0);
Activate(D_fore ? D_fore->w_norefresh : 0);
return 1;
}
p->w_zauto = 6;
}
}
else
p->w_zauto = *b2 == '*' ? (p->w_zauto == 2 ? 2 : 1) : 0;
}
if (p->w_zauto == 0 && bp[len - 1] == '*')
p->w_zauto = len > 1 && bp[len - 2] == '*' ? 2 : 1;
if (p->w_zdisplay)
{
display = p->w_zdisplay;
while (len-- > 0)
AddChar(*bp++);
return 1;
}
return 0;
}
static void
zmodem_fin(buf, len, data)
char *buf;
int len;
char *data;
{
char *s;
int n;
if (len)
RcLine(buf, strlen(buf) + 1);
else
{
s = "\030\030\030\030\030\030\030\030\030\030";
n = strlen(s);
LayProcess(&s, &n);
}
}
static void
zmodem_found(p, send, bp, len)
struct win *p;
int send;
char *bp;
int len;
{
char *s;
int i, n;
extern int zmodem_mode;
/* check for abort sequence */
n = 0;
for (i = 0; i < len ; i++)
if (bp[i] != 030)
n = 0;
else if (++n > 4)
return;
if (zmodem_mode == 3 || (zmodem_mode == 1 && p->w_type != W_TYPE_PLAIN))
{
struct display *d, *olddisplay;
olddisplay = display;
d = p->w_lastdisp;
if (!d || d->d_fore != p)
for (d = displays; d; d = d->d_next)
if (d->d_fore == p)
break;
if (!d && p->w_layer.l_cvlist)
d = p->w_layer.l_cvlist->c_display;
if (!d)
d = displays;
if (!d)
return;
display = d;
RemoveStatus();
p->w_zdisplay = display;
D_blocked = 2 + send;
flayer = &p->w_layer;
ZmodemPage();
display = d;
evdeq(&D_blockedev);
D_readev.condpos = &const_IOSIZE;
D_readev.condneg = &p->w_inlen;
ClearAll();
GotoPos(0, 0);
SetRendition(&mchar_blank);
AddStr("Zmodem active\r\n\r\n");
AddStr(send ? "**\030B01" : "**\030B00");
while (len-- > 0)
AddChar(*bp++);
display = olddisplay;
return;
}
flayer = &p->w_layer;
Input(":", 100, INP_COOKED, zmodem_fin, NULL, 0);
s = send ? zmodem_sendcmd : zmodem_recvcmd;
n = strlen(s);
LayProcess(&s, &n);
}
void
zmodem_abort(p, d)
struct win *p;
struct display *d;
{
struct display *olddisplay = display;
struct layer *oldflayer = flayer;
if (p)
{
if (p->w_savelayer && p->w_savelayer->l_next)
{
if (oldflayer == p->w_savelayer)
oldflayer = flayer->l_next;
flayer = p->w_savelayer;
ExitOverlayPage();
}
p->w_zdisplay = 0;
p->w_zauto = 0;
LRefreshAll(&p->w_layer, 0);
}
if (d)
{
display = d;
D_blocked = 0;
D_readev.condpos = D_readev.condneg = 0;
Activate(D_fore ? D_fore->w_norefresh : 0);
}
display = olddisplay;
flayer = oldflayer;
}
#endif
int
WindowChangeNumber(struct win *win, int n)
{
struct win *p;
int old = win->w_number;
if (n < 0 || n >= maxwin)
{
Msg(0, "Given window position is invalid.");
return 0;
}
p = wtab[n];
wtab[n] = win;
win->w_number = n;
wtab[old] = p;
if (p)
p->w_number = old;
#ifdef MULTIUSER
/* exchange the acls for these windows. */
AclWinSwap(old, n);
#endif
#ifdef UTMPOK
/* exchange the utmp-slots for these windows */
if ((win->w_slot != (slot_t) -1) && (win->w_slot != (slot_t) 0))
{
RemoveUtmp(win);
SetUtmp(win);
}
if (p && (p->w_slot != (slot_t) -1) && (p->w_slot != (slot_t) 0))
{
/* XXX: first display wins? */
#if 0
/* Does this make more sense? */
display = p->w_lastdisp ? p->w_lastdisp : p->w_layer.l_cvlist ? p->w_layer.l_cvlist->c_display : 0;
#else
display = win->w_layer.l_cvlist ? win->w_layer.l_cvlist->c_display : 0;
#endif
RemoveUtmp(p);
SetUtmp(p);
}
#endif
WindowChanged(win, 'n');
WindowChanged((struct win *)0, 'w');
WindowChanged((struct win *)0, 'W');
WindowChanged((struct win *)0, 0);
return 1;
}
screen-4.1.0/screen.c 0000644 0001750 0001750 00000226604 11732171225 012471 0 ustar abe abe /* Copyright (c) 2010
* Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
* Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
* 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
* Randy Lundquist randyl@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
* http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
****************************************************************
*/
#include
#include
#include
#ifdef sgi
# include
#endif
#include
#ifndef sun
# include
#endif
#ifndef SIGINT
# include
#endif
#include "config.h"
#ifdef HAVE_STROPTS_H
# include
#endif
#if defined(SYSV) && !defined(ISC)
# include
#endif
#if defined(sequent) || defined(SVR4)
# include
#endif /* sequent || SVR4 */
#ifdef ISC
# include
# include
# include
#endif /* ISC */
#if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
# include
#endif
#if defined(USE_LOCALE) || defined(ENCODINGS)
# include
#endif
#if defined(HAVE_NL_LANGINFO) && defined(ENCODINGS)
# include
#endif
#include "screen.h"
#ifdef HAVE_BRAILLE
# include "braille.h"
#endif
#include "patchlevel.h"
/*
* At the moment we only need the real password if the
* builtin lock is used. Therefore disable SHADOWPW if
* we do not really need it (kind of security thing).
*/
#ifndef LOCK
# undef SHADOWPW
#endif
#include
#ifdef SHADOWPW
# include
#endif /* SHADOWPW */
#include "logfile.h" /* islogfile, logfflush */
#ifdef DEBUG
FILE *dfp;
#endif
extern char Term[], screenterm[], **environ, Termcap[];
int force_vt = 1;
int VBellWait, MsgWait, MsgMinWait, SilenceWait;
extern struct acluser *users;
extern struct display *displays, *display;
extern struct LayFuncs MarkLf;
extern int visual_bell;
#ifdef COPY_PASTE
extern unsigned char mark_key_tab[];
#endif
extern char version[];
extern char DefaultShell[];
#ifdef ZMODEM
extern char *zmodem_sendcmd;
extern char *zmodem_recvcmd;
#endif
extern struct layout *layout_last;
char *ShellProg;
char *ShellArgs[2];
extern struct NewWindow nwin_undef, nwin_default, nwin_options;
struct backtick;
static struct passwd *getpwbyname __P((char *, struct passwd *));
static void SigChldHandler __P((void));
static sigret_t SigChld __P(SIGPROTOARG);
static sigret_t SigInt __P(SIGPROTOARG);
static sigret_t CoreDump __P(SIGPROTOARG);
static sigret_t FinitHandler __P(SIGPROTOARG);
static void DoWait __P((void));
static void serv_read_fn __P((struct event *, char *));
static void serv_select_fn __P((struct event *, char *));
static void logflush_fn __P((struct event *, char *));
static void backtick_filter __P((struct backtick *));
static void backtick_fn __P((struct event *, char *));
static char *runbacktick __P((struct backtick *, int *, time_t));
static int IsSymbol __P((char *, char *));
static char *ParseChar __P((char *, char *));
static int ParseEscape __P((char *));
static char *pad_expand __P((char *, char *, int, int));
#ifdef DEBUG
static void fds __P((void));
#endif
int nversion; /* numerical version, used for secondary DA */
/* the attacher */
struct passwd *ppp;
char *attach_tty;
int attach_fd = -1;
char *attach_term;
char *LoginName;
struct mode attach_Mode;
char SockPath[MAXPATHLEN + 2 * MAXSTR];
char *SockName; /* SockName is pointer in SockPath */
char *SockMatch = NULL; /* session id command line argument */
int ServerSocket = -1;
struct event serv_read;
struct event serv_select;
struct event logflushev;
char **NewEnv = NULL;
char *RcFileName = NULL;
char *home;
char *screenlogfile; /* filename layout */
int log_flush = 10; /* flush interval in seconds */
int logtstamp_on = 0; /* tstamp disabled */
char *logtstamp_string; /* stamp layout */
int logtstamp_after = 120; /* first tstamp after 120s */
char *hardcopydir = NULL;
char *BellString;
char *VisualBellString;
char *ActivityString;
#ifdef COPY_PASTE
char *BufferFile;
#endif
#ifdef POW_DETACH
char *PowDetachString;
#endif
char *hstatusstring;
char *captionstring;
char *timestring;
char *wliststr;
char *wlisttit;
int auto_detach = 1;
int iflag, rflag, dflag, lsflag, quietflag, wipeflag, xflag;
int cmdflag;
int queryflag = -1;
int adaptflag;
#ifdef MULTIUSER
char *multi;
char *multi_home;
int multi_uid;
int own_uid;
int multiattach;
int tty_mode;
int tty_oldmode = -1;
#endif
char HostName[MAXSTR];
int MasterPid, PanicPid;
int real_uid, real_gid, eff_uid, eff_gid;
int default_startup;
int ZombieKey_destroy, ZombieKey_resurrect, ZombieKey_onerror;
char *preselect = NULL; /* only used in Attach() */
#ifdef UTF8
char *screenencodings;
#endif
#ifdef DW_CHARS
int cjkwidth;
#endif
#ifdef NETHACK
int nethackflag = 0;
#endif
int maxwin;
struct layer *flayer;
struct win *fore;
struct win *windows;
struct win *console_window;
/*
* Do this last
*/
#include "extern.h"
char strnomem[] = "Out of memory.";
static int InterruptPlease;
static int GotSigChld;
static int
lf_secreopen(name, wantfd, l)
char *name;
int wantfd;
struct logfile *l;
{
int got_fd;
close(wantfd);
if (((got_fd = secopen(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) ||
lf_move_fd(got_fd, wantfd) < 0)
{
logfclose(l);
debug1("lf_secreopen: failed for %s\n", name);
return -1;
}
l->st->st_ino = l->st->st_dev = 0;
debug2("lf_secreopen: %d = %s\n", wantfd, name);
return 0;
}
/********************************************************************/
/********************************************************************/
/********************************************************************/
static struct passwd *
getpwbyname(name, ppp)
char *name;
struct passwd *ppp;
{
int n;
#ifdef SHADOWPW
struct spwd *sss = NULL;
static char *spw = NULL;
#endif
if (!ppp && !(ppp = getpwnam(name)))
return NULL;
/* Do password sanity check..., allow ##user for SUN_C2 security */
#ifdef SHADOWPW
pw_try_again:
#endif
n = 0;
if (ppp->pw_passwd[0] == '#' && ppp->pw_passwd[1] == '#' &&
strcmp(ppp->pw_passwd + 2, ppp->pw_name) == 0)
n = 13;
for (; n < 13; n++)
{
char c = ppp->pw_passwd[n];
if (!(c == '.' || c == '/' || c == '$' ||
(c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z')))
break;
}
#ifdef SHADOWPW
/* try to determine real password */
if (n < 13 && sss == 0)
{
sss = getspnam(ppp->pw_name);
if (sss)
{
if (spw)
free(spw);
ppp->pw_passwd = spw = SaveStr(sss->sp_pwdp);
endspent(); /* this should delete all buffers ... */
goto pw_try_again;
}
endspent(); /* this should delete all buffers ... */
}
#endif
if (n < 13)
ppp->pw_passwd = 0;
#ifdef linux
if (ppp->pw_passwd && strlen(ppp->pw_passwd) == 13 + 11)
ppp->pw_passwd[13] = 0; /* beware of linux's long passwords */
#endif
return ppp;
}
static char *
locale_name(void)
{
static char *s;
if (!s)
{
s = getenv("LC_ALL");
if (s == NULL)
s = getenv("LC_CTYPE");
if (s == NULL)
s = getenv("LANG");
}
return s;
}
int
main(ac, av)
int ac;
char **av;
{
register int n;
char *ap;
char *av0;
char socknamebuf[2 * MAXSTR];
int mflag = 0;
char *myname = (ac == 0) ? "screen" : av[0];
char *SockDir;
struct stat st;
#ifdef _MODE_T /* (jw) */
mode_t oumask;
#else
int oumask;
#endif
#if defined(SYSV) && !defined(ISC)
struct utsname utsnam;
#endif
struct NewWindow nwin;
int detached = 0; /* start up detached */
#ifdef MULTIUSER
char *sockp;
#endif
char *sty = 0;
#if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */
#endif
#if defined(sun) && defined(SVR4)
{
/* Solaris' login blocks SIGHUP! This is _very bad_ */
sigset_t sset;
sigemptyset(&sset);
sigprocmask(SIG_SETMASK, &sset, 0);
}
#endif
/*
* First, close all unused descriptors
* (otherwise, we might have problems with the select() call)
*/
closeallfiles(0);
#ifdef DEBUG
opendebug(1, 0);
#endif
snprintf(version, 59, "%d.%.2d.%.2d%s (%s%s) %s", REV, VERS,
PATCHLEVEL, STATE, ORIGIN, GIT_REV, DATE);
nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
debug2("-- screen debug started %s (%s)\n", *av, version);
#ifdef POSIX
debug("POSIX\n");
#endif
#ifdef TERMIO
debug("TERMIO\n");
#endif
#ifdef SYSV
debug("SYSV\n");
#endif
#ifdef SYSVSIGS
debug("SYSVSIGS\n");
#endif
#ifdef NAMEDPIPE
debug("NAMEDPIPE\n");
#endif
#if defined(SIGWINCH) && defined(TIOCGWINSZ)
debug("Window size changing enabled\n");
#endif
#ifdef HAVE_SETREUID
debug("SETREUID\n");
#endif
#ifdef HAVE_SETEUID
debug("SETEUID\n");
#endif
#ifdef hpux
debug("hpux\n");
#endif
#ifdef USEBCOPY
debug("USEBCOPY\n");
#endif
#ifdef UTMPOK
debug("UTMPOK\n");
#endif
#ifdef LOADAV
debug("LOADAV\n");
#endif
#ifdef NETHACK
debug("NETHACK\n");
#endif
#ifdef TERMINFO
debug("TERMINFO\n");
#endif
#ifdef SHADOWPW
debug("SHADOWPW\n");
#endif
#ifdef NAME_MAX
debug1("NAME_MAX = %d\n", NAME_MAX);
#endif
BellString = SaveStr("Bell in window %n");
VisualBellString = SaveStr(" Wuff, Wuff!! ");
ActivityString = SaveStr("Activity in window %n");
screenlogfile = SaveStr("screenlog.%n");
logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
hstatusstring = SaveStr("%h");
captionstring = SaveStr("%4n %t");
timestring = SaveStr("%c:%s %M %d %H%? %l%?");
wlisttit = SaveStr(" Num Name%=Flags");
wliststr = SaveStr("%4n %t%=%f");
#ifdef COPY_PASTE
BufferFile = SaveStr(DEFAULT_BUFFERFILE);
#endif
ShellProg = NULL;
#ifdef POW_DETACH
PowDetachString = 0;
#endif
default_startup = (ac > 1) ? 0 : 1;
adaptflag = 0;
VBellWait = VBELLWAIT * 1000;
MsgWait = MSGWAIT * 1000;
MsgMinWait = MSGMINWAIT * 1000;
SilenceWait = SILENCEWAIT;
#ifdef HAVE_BRAILLE
InitBraille();
#endif
#ifdef ZMODEM
zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
#endif
#ifdef COPY_PASTE
CompileKeys((char *)0, 0, mark_key_tab);
#endif
#ifdef UTF8
InitBuiltinTabs();
screenencodings = SaveStr(SCREENENCODINGS);
#endif
#ifdef DW_CHARS
cjkwidth = 0;
#endif
nwin = nwin_undef;
nwin_options = nwin_undef;
strcpy(screenterm, "screen");
logreopen_register(lf_secreopen);
av0 = *av;
/* if this is a login screen, assume -RR */
if (*av0 == '-')
{
rflag = 4;
#ifdef MULTI
xflag = 1;
#else
dflag = 1;
#endif
ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */
}
while (ac > 0)
{
ap = *++av;
if (--ac > 0 && *ap == '-')
{
if (ap[1] == '-' && ap[2] == 0)
{
av++;
ac--;
break;
}
if (ap[1] == '-' && !strcmp(ap, "--version"))
Panic(0, "Screen version %s", version);
if (ap[1] == '-' && !strcmp(ap, "--help"))
exit_with_usage(myname, NULL, NULL);
while (ap && *ap && *++ap)
{
switch (*ap)
{
case 'a':
nwin_options.aflag = 1;
break;
case 'A':
adaptflag = 1;
break;
case 'p': /* preselect */
if (*++ap)
preselect = ap;
else
{
if (!--ac)
exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
preselect = *++av;
}
ap = NULL;
break;
#ifdef HAVE_BRAILLE
case 'B':
bd.bd_start_braille = 1;
break;
#endif
case 'c':
if (*++ap)
RcFileName = ap;
else
{
if (--ac == 0)
exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
RcFileName = *++av;
}
ap = NULL;
break;
case 'e':
if (!*++ap)
{
if (--ac == 0)
exit_with_usage(myname, "Specify command characters with -e", NULL);
ap = *++av;
}
if (ParseEscape(ap))
Panic(0, "Two characters are required with -e option, not '%s'.", ap);
ap = NULL;
break;
case 'f':
ap++;
switch (*ap++)
{
case 'n':
case '0':
nwin_options.flowflag = FLOW_NOW * 0;
break;
case '\0':
ap--;
/* FALLTHROUGH */
case 'y':
case '1':
nwin_options.flowflag = FLOW_NOW * 1;
break;
case 'a':
nwin_options.flowflag = FLOW_AUTOFLAG;
break;
default:
exit_with_usage(myname, "Unknown flow option -%s", --ap);
}
break;
case 'h':
if (--ac == 0)
exit_with_usage(myname, NULL, NULL);
nwin_options.histheight = atoi(*++av);
if (nwin_options.histheight < 0)
exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
break;
case 'i':
iflag = 1;
break;
case 't': /* title, the former AkA == -k */
if (--ac == 0)
exit_with_usage(myname, "Specify a new window-name with -t", NULL);
nwin_options.aka = *++av;
break;
case 'l':
ap++;
switch (*ap++)
{
case 'n':
case '0':
nwin_options.lflag = 0;
break;
case '\0':
ap--;
/* FALLTHROUGH */
case 'y':
case '1':
nwin_options.lflag = 1;
break;
case 'a':
nwin_options.lflag = 3;
break;
case 's': /* -ls */
case 'i': /* -list */
lsflag = 1;
if (ac > 1 && !SockMatch)
{
SockMatch = *++av;
ac--;
}
ap = NULL;
break;
default:
exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
}
break;
case 'w':
lsflag = 1;
wipeflag = 1;
if (ac > 1 && !SockMatch)
{
SockMatch = *++av;
ac--;
}
break;
case 'L':
nwin_options.Lflag = 1;
break;
case 'm':
mflag = 1;
break;
case 'O': /* to be (or not to be?) deleted. jw. */
force_vt = 0;
break;
case 'T':
if (--ac == 0)
exit_with_usage(myname, "Specify terminal-type with -T", NULL);
if (strlen(*++av) < 20)
strcpy(screenterm, *av);
else
Panic(0, "-T: terminal name too long. (max. 20 char)");
nwin_options.term = screenterm;
break;
case 'q':
quietflag = 1;
break;
case 'Q':
queryflag = 1;
cmdflag = 1;
break;
case 'r':
case 'R':
#ifdef MULTI
case 'x':
#endif
if (ac > 1 && *av[1] != '-' && !SockMatch)
{
SockMatch = *++av;
ac--;
debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
}
#ifdef MULTI
if (*ap == 'x')
xflag = 1;
#endif
if (rflag)
rflag = 2;
rflag += (*ap == 'R') ? 2 : 1;
break;
#ifdef REMOTE_DETACH
case 'd':
dflag = 1;
/* FALLTHROUGH */
case 'D':
if (!dflag)
dflag = 2;
if (ac == 2)
{
if (*av[1] != '-' && !SockMatch)
{
SockMatch = *++av;
ac--;
debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
}
}
break;
#endif
case 's':
if (--ac == 0)
exit_with_usage(myname, "Specify shell with -s", NULL);
if (ShellProg)
free(ShellProg);
ShellProg = SaveStr(*++av);
debug1("ShellProg: '%s'\n", ShellProg);
break;
case 'S':
if (!SockMatch)
{
if (--ac == 0)
exit_with_usage(myname, "Specify session-name with -S", NULL);
SockMatch = *++av;
}
if (!*SockMatch)
exit_with_usage(myname, "Empty session-name?", NULL);
break;
case 'X':
cmdflag = 1;
break;
case 'v':
Panic(0, "Screen version %s", version);
/* NOTREACHED */
#ifdef UTF8
case 'U':
nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
break;
#endif
default:
exit_with_usage(myname, "Unknown option %s", --ap);
}
}
}
else
break;
}
real_uid = getuid();
real_gid = getgid();
eff_uid = geteuid();
eff_gid = getegid();
#ifdef SIGBUS /* OOPS, linux has no bus errors! */
signal(SIGBUS, CoreDump);
#endif /* SIGBUS */
signal(SIGSEGV, CoreDump);
#ifdef USE_LOCALE
setlocale(LC_ALL, "");
#endif
#ifdef ENCODINGS
if (nwin_options.encoding == -1)
{
/* ask locale if we should start in UTF-8 mode */
# ifdef HAVE_NL_LANGINFO
# ifndef USE_LOCALE
setlocale(LC_CTYPE, "");
# endif
nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
debug1("locale says encoding = %d\n", nwin_options.encoding);
# else
# ifdef UTF8
char *s;
if ((s = locale_name()) && InStr(s, "UTF-8"))
nwin_options.encoding = UTF8;
# endif
debug1("environment says encoding=%d\n", nwin_options.encoding);
#endif
}
# ifdef DW_CHARS
{
char *s;
if ((s = locale_name()))
{
if(!strncmp(s, "zh_", 3) || !strncmp(s, "ja_", 3) || !strncmp(s, "ko_", 3))
{
cjkwidth = 1;
}
}
}
#endif
#endif
if (nwin_options.aka)
{
#ifdef ENCODINGS
if (nwin_options.encoding > 0)
{
size_t len = strlen(nwin_options.aka);
size_t newsz;
char *newbuf = malloc(3 * len);
if (!newbuf)
Panic(0, "%s", strnomem);
newsz = RecodeBuf((unsigned char *)nwin_options.aka, len,
nwin_options.encoding, 0, (unsigned char *)newbuf);
newbuf[newsz] = '\0';
nwin_options.aka = newbuf;
}
else
#endif
{
/* If we just use the original value from av,
subsequent shelltitle invocations will attempt to free
space we don't own... */
nwin_options.aka = SaveStr(nwin_options.aka);
}
}
if (SockMatch && strlen(SockMatch) >= MAXSTR)
Panic(0, "Ridiculously long socketname - try again.");
if (cmdflag && !rflag && !dflag && !xflag)
xflag = 1;
if (!cmdflag && dflag && mflag && !(rflag || xflag))
detached = 1;
nwin = nwin_options;
#ifdef ENCODINGS
nwin.encoding = nwin_undef.encoding; /* let screenrc overwrite it */
#endif
if (ac)
nwin.args = av;
/* make the write() calls return -1 on all errors */
#ifdef SIGXFSZ
/*
* Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
* It appears that in System V Release 4, UNIX, if you are writing
* an output file and you exceed the currently set file size limit,
* you _don't_ just get the call to `write' returning with a
* failure code. Rather, you get a signal called `SIGXFSZ' which,
* if neither handled nor ignored, will cause your program to crash
* with a core dump.
*/
signal(SIGXFSZ, SIG_IGN);
#endif /* SIGXFSZ */
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
#endif
if (!ShellProg)
{
register char *sh;
sh = getenv("SHELL");
ShellProg = SaveStr(sh ? sh : DefaultShell);
}
ShellArgs[0] = ShellProg;
home = getenv("HOME");
if (!mflag && !SockMatch)
{
sty = getenv("STY");
if (sty && *sty == 0)
sty = 0;
}
#ifdef NETHACK
if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL)))
{
char nethackrc[MAXPATHLEN];
if (home && (strlen(home) < (MAXPATHLEN - 20)))
{
sprintf(nethackrc,"%s/.nethackrc", home);
nethackflag = !access(nethackrc, F_OK);
}
}
#endif
#ifdef MULTIUSER
own_uid = multi_uid = real_uid;
if (SockMatch && (sockp = index(SockMatch, '/')))
{
*sockp = 0;
multi = SockMatch;
SockMatch = sockp + 1;
if (*multi)
{
struct passwd *mppp;
if ((mppp = getpwnam(multi)) == (struct passwd *)0)
Panic(0, "Cannot identify account '%s'.", multi);
multi_uid = mppp->pw_uid;
multi_home = SaveStr(mppp->pw_dir);
if (strlen(multi_home) > MAXPATHLEN - 10)
Panic(0, "home directory path too long");
# ifdef MULTI
/* always fake multi attach mode */
if (rflag || lsflag)
xflag = 1;
# endif /* MULTI */
detached = 0;
multiattach = 1;
}
/* Special case: effective user is multiuser. */
if (eff_uid && (multi_uid != eff_uid))
Panic(0, "Must run suid root for multiuser support.");
}
if (SockMatch && *SockMatch == 0)
SockMatch = 0;
#endif /* MULTIUSER */
if ((LoginName = getlogin()) && LoginName[0] != '\0')
{
if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
if ((int)ppp->pw_uid != real_uid)
ppp = (struct passwd *) 0;
}
if (ppp == 0)
{
if ((ppp = getpwuid(real_uid)) == 0)
{
Panic(0, "getpwuid() can't identify your account!");
exit(1);
}
LoginName = ppp->pw_name;
}
LoginName = SaveStr(LoginName);
ppp = getpwbyname(LoginName, ppp);
#if !defined(SOCKDIR) && defined(MULTIUSER)
if (multi && !multiattach)
{
if (home && strcmp(home, ppp->pw_dir))
Panic(0, "$HOME must match passwd entry for multiuser screens.");
}
#endif
#define SET_GUID() do \
{ \
setgid(real_gid); \
setuid(real_uid); \
eff_uid = real_uid; \
eff_gid = real_gid; \
} while (0)
#define SET_TTYNAME(fatal) do \
{ \
if (!(attach_tty = ttyname(0))) \
{ \
if (fatal) \
Panic(0, "Must be connected to a terminal."); \
else \
attach_tty = ""; \
} \
else if (stat(attach_tty, &st)) \
Panic(errno, "Cannot access '%s'", attach_tty); \
if (strlen(attach_tty) >= MAXPATHLEN) \
Panic(0, "TtyName too long - sorry."); \
} while (0)
if (home == 0 || *home == '\0')
home = ppp->pw_dir;
if (strlen(LoginName) > 20)
Panic(0, "LoginName too long - sorry.");
#ifdef MULTIUSER
if (multi && strlen(multi) > 20)
Panic(0, "Screen owner name too long - sorry.");
#endif
if (strlen(home) > MAXPATHLEN - 25)
Panic(0, "$HOME too long - sorry.");
attach_tty = "";
if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag) && !(!mflag && !SockMatch && sty && !xflag))
{
#ifndef NAMEDPIPE
int fl;
#endif
/* ttyname implies isatty */
SET_TTYNAME(1);
#ifdef MULTIUSER
tty_mode = (int)st.st_mode & 0777;
#endif
#ifndef NAMEDPIPE
fl = fcntl(0, F_GETFL, 0);
if (fl != -1 && (fl & (O_RDWR|O_RDONLY|O_WRONLY)) == O_RDWR)
attach_fd = 0;
#endif
if (attach_fd == -1)
{
if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
close(n);
}
debug2("attach_tty is %s, attach_fd is %d\n", attach_tty, attach_fd);
if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
Panic(0, "Please set a terminal type.");
if (strlen(attach_term) > sizeof(D_termname) - 1)
Panic(0, "$TERM too long - sorry.");
GetTTY(0, &attach_Mode);
#ifdef DEBUGGGGGGGGGGGGGGG
DebugTTY(&attach_Mode);
#endif /* DEBUG */
}
#ifdef _MODE_T
oumask = umask(0); /* well, unsigned never fails? jw. */
#else
if ((oumask = (int)umask(0)) == -1)
Panic(errno, "Cannot change umask to zero");
#endif
SockDir = getenv("SCREENDIR");
if (SockDir)
{
if (strlen(SockDir) >= MAXPATHLEN - 1)
Panic(0, "Ridiculously long $SCREENDIR - try again.");
#ifdef MULTIUSER
if (multi)
Panic(0, "No $SCREENDIR with multi screens, please.");
#endif
}
#ifdef MULTIUSER
if (multiattach)
{
# ifndef SOCKDIR
sprintf(SockPath, "%s/.screen", multi_home);
SockDir = SockPath;
# else
SockDir = SOCKDIR;
sprintf(SockPath, "%s/S-%s", SockDir, multi);
# endif
}
else
#endif
{
#ifndef SOCKDIR
if (SockDir == 0)
{
sprintf(SockPath, "%s/.screen", home);
SockDir = SockPath;
}
#endif
if (SockDir)
{
if (access(SockDir, F_OK))
{
debug1("SockDir '%s' missing ...\n", SockDir);
if (UserContext() > 0)
{
if (mkdir(SockDir, 0700))
UserReturn(0);
UserReturn(1);
}
if (UserStatus() <= 0)
Panic(0, "Cannot make directory '%s'.", SockDir);
}
if (SockDir != SockPath)
strcpy(SockPath, SockDir);
}
#ifdef SOCKDIR
else
{
SockDir = SOCKDIR;
if (stat(SockDir, &st))
{
n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
(eff_gid != real_gid) ? 0775 :
#ifdef S_ISVTX
0777|S_ISVTX;
#else
0777;
#endif
if (mkdir(SockDir, n) == -1)
Panic(errno, "Cannot make directory '%s'", SockDir);
}
else
{
if (!S_ISDIR(st.st_mode))
Panic(0, "'%s' must be a directory.", SockDir);
if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
Panic(0, "Directory '%s' must be owned by root.", SockDir);
n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
(eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 :
0777;
if (((int)st.st_mode & 0777) != n)
Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
}
sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
if (access(SockPath, F_OK))
{
if (mkdir(SockPath, 0700) == -1)
Panic(errno, "Cannot make directory '%s'", SockPath);
(void) chown(SockPath, real_uid, real_gid);
}
}
#endif
}
if (stat(SockPath, &st) == -1)
Panic(errno, "Cannot access %s", SockPath);
else
if (!S_ISDIR(st.st_mode))
Panic(0, "%s is not a directory.", SockPath);
#ifdef MULTIUSER
if (multi)
{
if ((int)st.st_uid != multi_uid)
Panic(0, "%s is not the owner of %s.", multi, SockPath);
}
else
#endif
{
if ((int)st.st_uid != real_uid)
Panic(0, "You are not the owner of %s.", SockPath);
}
if ((st.st_mode & 0777) != 0700)
Panic(0, "Directory %s must have mode 700.", SockPath);
if (SockMatch && index(SockMatch, '/'))
Panic(0, "Bad session name '%s'", SockMatch);
SockName = SockPath + strlen(SockPath) + 1;
*SockName = 0;
(void) umask(oumask);
debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
#if defined(SYSV) && !defined(ISC)
if (uname(&utsnam) == -1)
Panic(errno, "uname");
strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
#else
(void) gethostname(HostName, MAXSTR);
HostName[MAXSTR - 1] = '\0';
#endif
if ((ap = index(HostName, '.')) != NULL)
*ap = '\0';
if (lsflag)
{
int i, fo, oth;
#ifdef MULTIUSER
if (multi)
real_uid = multi_uid;
#endif
SET_GUID();
i = FindSocket((int *)NULL, &fo, &oth, SockMatch);
if (quietflag)
exit(8 + (fo ? ((oth || i) ? 2 : 1) : 0) + i);
if (fo == 0)
Panic(0, "No Sockets found in %s.\n", SockPath);
Panic(0, "%d Socket%s in %s.\n", fo, fo > 1 ? "s" : "", SockPath);
/* NOTREACHED */
}
signal(SIG_BYE, AttacherFinit); /* prevent races */
if (cmdflag)
{
/* attach_tty is not mandatory */
SET_TTYNAME(0);
if (!*av)
Panic(0, "Please specify a command.");
SET_GUID();
SendCmdMessage(sty, SockMatch, av, queryflag >= 0);
exit(0);
}
else if (rflag || xflag)
{
debug("screen -r: - is there anybody out there?\n");
if (Attach(MSG_ATTACH))
{
Attacher();
/* NOTREACHED */
}
#ifdef MULTIUSER
if (multiattach)
Panic(0, "Can't create sessions of other users.");
#endif
debug("screen -r: backend not responding -- still crying\n");
}
else if (dflag && !mflag)
{
SET_TTYNAME(0);
Attach(MSG_DETACH);
Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
eexit(0);
/* NOTREACHED */
}
if (!SockMatch && !mflag && sty)
{
/* attach_tty is not mandatory */
SET_TTYNAME(0);
SET_GUID();
nwin_options.args = av;
SendCreateMsg(sty, &nwin);
exit(0);
/* NOTREACHED */
}
nwin_compose(&nwin_default, &nwin_options, &nwin_default);
if (!detached || dflag != 2)
MasterPid = fork();
else
MasterPid = 0;
switch (MasterPid)
{
case -1:
Panic(errno, "fork");
/* NOTREACHED */
case 0:
break;
default:
if (detached)
exit(0);
if (SockMatch)
sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
else
sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
for (ap = socknamebuf; *ap; ap++)
if (*ap == '/')
*ap = '-';
#ifdef NAME_MAX
if (strlen(socknamebuf) > NAME_MAX)
socknamebuf[NAME_MAX] = 0;
#endif
sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
SET_GUID();
Attacher();
/* NOTREACHED */
}
if (!detached)
PanicPid = getppid();
if (DefaultEsc == -1)
DefaultEsc = Ctrl('a');
if (DefaultMetaEsc == -1)
DefaultMetaEsc = 'a';
ap = av0 + strlen(av0) - 1;
while (ap >= av0)
{
if (!strncmp("screen", ap, 6))
{
strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
break;
}
ap--;
}
if (ap < av0)
*av0 = 'S';
#ifdef DEBUG
{
char buf[256];
if (dfp && dfp != stderr)
fclose(dfp);
sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
if ((dfp = fopen(buf, "w")) == NULL)
dfp = stderr;
else
(void) chmod(buf, 0666);
}
#endif
if (!detached)
{
if (attach_fd == -1)
{
if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
}
else
n = dup(attach_fd);
}
else
n = -1;
freopen("/dev/null", "r", stdin);
freopen("/dev/null", "w", stdout);
#ifdef DEBUG
if (dfp != stderr)
#endif
freopen("/dev/null", "w", stderr);
debug("-- screen.back debug started\n");
/*
* This guarantees that the session owner is listed, even when we
* start detached. From now on we should not refer to 'LoginName'
* any more, use users->u_name instead.
*/
if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
Panic(0, "Could not create user info");
if (!detached)
{
if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
Panic(0, "Could not alloc display");
PanicPid = 0;
#ifdef ENCODINGS
D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
debug1("D_encoding = %d\n", D_encoding);
#endif
}
if (SockMatch)
{
/* user started us with -S option */
sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
}
else
{
sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty),
HostName);
}
for (ap = socknamebuf; *ap; ap++)
if (*ap == '/')
*ap = '-';
#ifdef NAME_MAX
if (strlen(socknamebuf) > NAME_MAX)
{
debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
socknamebuf[NAME_MAX] = 0;
}
#endif
sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
ServerSocket = MakeServerSocket();
InitKeytab();
#ifdef ETCSCREENRC
# ifdef ALLOW_SYSSCREENRC
if ((ap = getenv("SYSSCREENRC")))
(void)StartRc(ap, 0);
else
# endif
(void)StartRc(ETCSCREENRC, 0);
#endif
(void)StartRc(RcFileName, 0);
# ifdef UTMPOK
# ifndef UTNOKEEP
InitUtmp();
# endif /* UTNOKEEP */
# endif /* UTMPOK */
if (display)
{
if (InitTermcap(0, 0))
{
debug("Could not init termcap - exiting\n");
fcntl(D_userfd, F_SETFL, 0); /* Flush sets FNBLOCK */
freetty();
if (D_userpid)
Kill(D_userpid, SIG_BYE);
eexit(1);
}
MakeDefaultCanvas();
InitTerm(0);
#ifdef UTMPOK
RemoveLoginSlot();
#endif
}
else
MakeTermcap(1);
#ifdef LOADAV
InitLoadav();
#endif /* LOADAV */
MakeNewEnv();
signal(SIGHUP, SigHup);
signal(SIGINT, FinitHandler);
signal(SIGQUIT, FinitHandler);
signal(SIGTERM, FinitHandler);
#ifdef BSDJOBS
signal(SIGTTIN, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
#endif
if (display)
{
brktty(D_userfd);
SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
/* Note: SetMode must be called _before_ FinishRc. */
SetTTY(D_userfd, &D_NewMode);
if (fcntl(D_userfd, F_SETFL, FNBLOCK))
Msg(errno, "Warning: NBLOCK fcntl failed");
}
else
brktty(-1); /* just try */
signal(SIGCHLD, SigChld);
#ifdef ETCSCREENRC
# ifdef ALLOW_SYSSCREENRC
if ((ap = getenv("SYSSCREENRC")))
FinishRc(ap);
else
# endif
FinishRc(ETCSCREENRC);
#endif
FinishRc(RcFileName);
debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
if (windows == NULL)
{
debug("We open one default window, as screenrc did not specify one.\n");
if (MakeWindow(&nwin) == -1)
{
fd_set rfd;
struct timeval tv = { MsgWait/1000, 1000*(MsgWait%1000) };
FD_SET(0, &rfd);
Msg(0, "Sorry, could not find a PTY or TTY.");
// allow user to exit early by pressing any key.
select(1, &rfd, NULL, NULL, &tv);
Finit(0);
/* NOTREACHED */
}
}
else if (ac) /* Screen was invoked with a command */
{
MakeWindow(&nwin);
}
#ifdef HAVE_BRAILLE
StartBraille();
#endif
if (display && default_startup)
display_copyright();
signal(SIGINT, SigInt);
if (rflag && (rflag & 1) == 0 && !quietflag)
{
Msg(0, "New screen...");
rflag = 0;
}
serv_read.type = EV_READ;
serv_read.fd = ServerSocket;
serv_read.handler = serv_read_fn;
evenq(&serv_read);
serv_select.pri = -10;
serv_select.type = EV_ALWAYS;
serv_select.handler = serv_select_fn;
evenq(&serv_select);
logflushev.type = EV_TIMEOUT;
logflushev.handler = logflush_fn;
sched();
/* NOTREACHED */
return 0;
}
void
WindowDied(p, wstat, wstat_valid)
struct win *p;
#ifdef BSDWAIT
union wait wstat;
#else
int wstat;
#endif
int wstat_valid;
{
int killit = 0;
if (p->w_destroyev.data == (char *)p)
{
wstat = p->w_exitstatus;
wstat_valid = 1;
evdeq(&p->w_destroyev);
p->w_destroyev.data = 0;
}
#if defined(BSDJOBS) && !defined(BSDWAIT)
if (!wstat_valid && p->w_pid > 0)
{
/* EOF on file descriptor. The process is probably also dead.
* try a waitpid */
if (waitpid(p->w_pid, &wstat, WNOHANG | WUNTRACED) == p->w_pid)
{
p->w_pid = 0;
wstat_valid = 1;
}
}
#endif
if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid &&
WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0)
killit = 1;
if (ZombieKey_destroy && !killit)
{
char buf[100], *s, reason[100];
time_t now;
if (wstat_valid) {
if (WIFEXITED(wstat))
if (WEXITSTATUS(wstat))
sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat));
else
sprintf(reason, "terminated normally");
else if (WIFSIGNALED(wstat))
sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat),
#ifdef WCOREDUMP
WCOREDUMP(wstat) ? " (core file generated)" : "");
#else
"");
#endif
} else
sprintf(reason, "detached from window");
(void) time(&now);
s = ctime(&now);
if (s && *s)
s[strlen(s) - 1] = '\0';
debug3("window %d (%s) going into zombie state fd %d",
p->w_number, p->w_title, p->w_ptyfd);
#ifdef UTMPOK
if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1)
{
RemoveUtmp(p);
p->w_slot = 0; /* "detached" */
}
#endif
CloseDevice(p);
p->w_deadpid = p->w_pid;
p->w_pid = 0;
ResetWindow(p);
/* p->w_y = p->w_bot; */
p->w_y = MFindUsedLine(p, p->w_bot, 1);
sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?");
WriteString(p, buf, strlen(buf));
WindowChanged(p, 'f');
}
else
KillWindow(p);
#ifdef UTMPOK
CarefulUtmp();
#endif
}
static void
SigChldHandler()
{
struct stat st;
#ifdef DEBUG
fds();
#endif
while (GotSigChld)
{
GotSigChld = 0;
DoWait();
#ifdef SYSVSIGS
signal(SIGCHLD, SigChld);
#endif
}
if (stat(SockPath, &st) == -1)
{
debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
if (!RecoverSocket())
{
debug("SCREEN cannot recover from corrupt Socket, bye\n");
Finit(1);
}
else
debug1("'%s' reconstructed\n", SockPath);
}
else
debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
}
static sigret_t
SigChld SIGDEFARG
{
debug("SigChld()\n");
GotSigChld = 1;
SIGRETURN;
}
sigret_t
SigHup SIGDEFARG
{
/* Hangup all displays */
while ((display = displays) != 0)
Hangup();
SIGRETURN;
}
/*
* the backend's Interrupt handler
* we cannot insert the intrc directly, as we never know
* if fore is valid.
*/
static sigret_t
SigInt SIGDEFARG
{
#if HAZARDOUS
char ibuf;
debug("SigInt()\n");
if (fore && displays)
{
# if defined(TERMIO) || defined(POSIX)
ibuf = displays->d_OldMode.tio.c_cc[VINTR];
# else
ibuf = displays->d_OldMode.m_tchars.t_intrc;
# endif
fore->w_inlen = 0;
write(fore->w_ptyfd, &ibuf, 1);
}
#else
signal(SIGINT, SigInt);
debug("SigInt() careful\n");
InterruptPlease = 1;
#endif
SIGRETURN;
}
static sigret_t
CoreDump SIGDEFARG
{
/* if running with s-bit, we must reset the s-bit, so that we get a
* core file anyway.
*/
struct display *disp;
char buf[80];
char *dump_msg = " (core dumped)";
int running_w_s_bit = getuid() != geteuid();
#if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
if (running_w_s_bit)
dump_msg = "";
#endif
#if defined(SYSVSIGS) && defined(SIGHASARG)
signal(sigsig, SIG_IGN);
#endif
setgid(getgid());
setuid(getuid());
unlink("core");
#ifdef SIGHASARG
sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig, dump_msg);
#else
sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n", dump_msg);
#endif
for (disp = displays; disp; disp = disp->d_next)
{
if (disp->d_nonblock < -1 || disp->d_nonblock > 1000000)
continue;
fcntl(disp->d_userfd, F_SETFL, 0);
SetTTY(disp->d_userfd, &D_OldMode);
write(disp->d_userfd, buf, strlen(buf));
Kill(disp->d_userpid, SIG_BYE);
}
if (running_w_s_bit)
{
#if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
Kill(getpid(), SIGKILL);
eexit(11);
#else /* SHADOWPW && !DEBUG */
abort();
#endif /* SHADOWPW && !DEBUG */
}
else
abort();
SIGRETURN;
}
static void
DoWait()
{
register int pid;
struct win *p, *next;
#ifdef BSDWAIT
union wait wstat;
#else
int wstat;
#endif
#ifdef BSDJOBS
# ifndef BSDWAIT
while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
# else
# ifdef USE_WAIT2
/*
* From: rouilj@sni-usa.com (John Rouillard)
* note that WUNTRACED is not documented to work, but it is defined in
* /usr/include/sys/wait.h, so it may work
*/
while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
# else /* USE_WAIT2 */
while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
# endif /* USE_WAIT2 */
# endif
#else /* BSDJOBS */
while ((pid = wait(&wstat)) < 0)
if (errno != EINTR)
break;
if (pid > 0)
#endif /* BSDJOBS */
{
for (p = windows; p; p = next)
{
next = p->w_next;
if ( (p->w_pid && pid == p->w_pid) ||
(p->w_deadpid && pid == p->w_deadpid) )
{
/* child has ceased to exist */
p->w_pid = 0;
#ifdef BSDJOBS
if (WIFSTOPPED(wstat))
{
debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, pid, WSTOPSIG(wstat));
#ifdef SIGTTIN
if (WSTOPSIG(wstat) == SIGTTIN)
{
Msg(0, "Suspended (tty input)");
continue;
}
#endif
#ifdef SIGTTOU
if (WSTOPSIG(wstat) == SIGTTOU)
{
Msg(0, "Suspended (tty output)");
continue;
}
#endif
/* Try to restart process */
Msg(0, "Child has been stopped, restarting.");
if (killpg(pid, SIGCONT))
kill(pid, SIGCONT);
}
else
#endif
{
/* Screen will detect the window has died when the window's
* file descriptor signals EOF (which it will do when the process in
* the window terminates). So do this in a timeout of 10 seconds.
* (not doing this at all might also work)
* See #27061 for more details.
*/
p->w_destroyev.data = (char *)p;
p->w_exitstatus = wstat;
SetTimeout(&p->w_destroyev, 10 * 1000);
evenq(&p->w_destroyev);
}
break;
}
#ifdef PSEUDOS
if (p->w_pwin && pid == p->w_pwin->p_pid)
{
debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
FreePseudowin(p);
break;
}
#endif
}
if (p == 0)
{
debug1("pid %d not found - hope that's ok\n", pid);
}
}
}
static sigret_t
FinitHandler SIGDEFARG
{
#ifdef SIGHASARG
debug1("FinitHandler called, sig %d.\n", sigsig);
#else
debug("FinitHandler called.\n");
#endif
Finit(1);
SIGRETURN;
}
void
Finit(i)
int i;
{
signal(SIGCHLD, SIG_DFL);
signal(SIGHUP, SIG_IGN);
debug1("Finit(%d);\n", i);
while (windows)
{
struct win *p = windows;
windows = windows->w_next;
FreeWindow(p);
}
if (ServerSocket != -1)
{
debug1("we unlink(%s)\n", SockPath);
#ifdef USE_SETEUID
xseteuid(real_uid);
xsetegid(real_gid);
#endif
(void) unlink(SockPath);
#ifdef USE_SETEUID
xseteuid(eff_uid);
xsetegid(eff_gid);
#endif
}
for (display = displays; display; display = display->d_next)
{
if (D_status)
RemoveStatus();
FinitTerm();
#ifdef UTMPOK
RestoreLoginSlot();
#endif
AddStr("[screen is terminating]\r\n");
Flush(3);
SetTTY(D_userfd, &D_OldMode);
fcntl(D_userfd, F_SETFL, 0);
freetty();
Kill(D_userpid, SIG_BYE);
}
/*
* we _cannot_ call eexit(i) here,
* instead of playing with the Socket above. Sigh.
*/
exit(i);
}
void
eexit(e)
int e;
{
debug("eexit\n");
if (ServerSocket != -1)
{
debug1("we unlink(%s)\n", SockPath);
setgid(real_gid);
setuid(real_uid);
(void) unlink(SockPath);
}
exit(e);
}
void
Hangup()
{
if (display == 0)
return;
debug1("Hangup %x\n", display);
if (D_userfd >= 0)
{
close(D_userfd);
D_userfd = -1;
}
if (auto_detach || displays->d_next)
Detach(D_HANGUP);
else
Finit(0);
}
/*
* Detach now has the following modes:
*D_DETACH SIG_BYE detach backend and exit attacher
*D_HANGUP SIG_BYE detach backend and exit attacher
*D_STOP SIG_STOP stop attacher (and detach backend)
*D_REMOTE SIG_BYE remote detach -- reattach to new attacher
*D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
*D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
*D_LOCK SIG_LOCK lock the attacher
* (jw)
* we always remove our utmp slots. (even when "lock" or "stop")
* Note: Take extra care here, we may be called by interrupt!
*/
void
Detach(mode)
int mode;
{
int sign = 0, pid;
struct canvas *cv;
struct win *p;
if (display == 0)
return;
#define AddStrSock(msg) do { \
if (SockName) \
{ \
AddStr("[" msg " from "); \
AddStr(SockName); \
AddStr("]\r\n"); \
} \
else \
AddStr("[" msg "]\r\n"); \
} while (0)
signal(SIGHUP, SIG_IGN);
debug1("Detach(%d)\n", mode);
if (D_status)
RemoveStatus();
FinitTerm();
if (!display)
return;
switch (mode)
{
case D_HANGUP:
sign = SIG_BYE;
break;
case D_DETACH:
AddStrSock("detached");
sign = SIG_BYE;
break;
#ifdef BSDJOBS
case D_STOP:
sign = SIG_STOP;
break;
#endif
#ifdef REMOTE_DETACH
case D_REMOTE:
AddStrSock("remote detached");
sign = SIG_BYE;
break;
#endif
#ifdef POW_DETACH
case D_POWER:
AddStrSock("power detached");
if (PowDetachString)
{
AddStr(PowDetachString);
AddStr("\r\n");
}
sign = SIG_POWER_BYE;
break;
#ifdef REMOTE_DETACH
case D_REMOTE_POWER:
AddStrSock("remote power detached");
if (PowDetachString)
{
AddStr(PowDetachString);
AddStr("\r\n");
}
sign = SIG_POWER_BYE;
break;
#endif
#endif
case D_LOCK:
ClearAll();
sign = SIG_LOCK;
/* tell attacher to lock terminal with a lockprg. */
break;
}
#ifdef UTMPOK
if (displays->d_next == 0)
{
for (p = windows; p; p = p->w_next)
{
if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2))
{
RemoveUtmp(p);
/*
* Set the slot to 0 to get the window
* logged in again.
*/
p->w_slot = (slot_t) 0;
}
}
}
if (mode != D_HANGUP)
RestoreLoginSlot();
#endif
if (displays->d_next == 0 && console_window)
{
if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach"))
{
debug("could not release console - killing window\n");
KillWindow(console_window);
display = displays; /* restore display */
}
}
if (D_fore)
{
#ifdef MULTIUSER
ReleaseAutoWritelock(display, D_fore);
#endif
D_user->u_detachwin = D_fore->w_number;
D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
}
AutosaveLayout(D_layout);
layout_last = D_layout;
for (cv = D_cvlist; cv; cv = cv->c_next)
{
p = Layer2Window(cv->c_layer);
SetCanvasWindow(cv, 0);
if (p)
WindowChanged(p, 'u');
}
pid = D_userpid;
debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
FreeDisplay();
if (displays == 0)
/* Flag detached-ness */
(void) chsock();
/*
* tell father what to do. We do that after we
* freed the tty, thus getty feels more comfortable on hpux
* if it was a power detach.
*/
Kill(pid, sign);
debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
debug("Detach returns, we are successfully detached.\n");
signal(SIGHUP, SigHup);
#undef AddStrSock
}
static int
IsSymbol(e, s)
char *e, *s;
{
register int l;
l = strlen(s);
return strncmp(e, s, l) == 0 && e[l] == '=';
}
void
MakeNewEnv()
{
register char **op, **np;
static char stybuf[MAXSTR];
for (op = environ; *op; ++op)
;
if (NewEnv)
free((char *)NewEnv);
NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
if (!NewEnv)
Panic(0, "%s", strnomem);
sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
*np++ = stybuf; /* NewEnv[0] */
*np++ = Term; /* NewEnv[1] */
np++; /* room for SHELL */
#ifdef TIOCSWINSZ
np += 2; /* room for TERMCAP and WINDOW */
#else
np += 4; /* room for TERMCAP WINDOW LINES COLUMNS */
#endif
for (op = environ; *op; ++op)
{
if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
&& !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
&& !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
&& !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
)
*np++ = *op;
}
*np = 0;
}
#if defined(USEVARARGS) && defined(__STDC__)
#define DEFINE_VARARGS_FN(fnname) void fnname (int err, const char *fmt, VA_DOTS)
#else
#define DEFINE_VARARGS_FN(fnname) void fnname(err, fmt, VA_DOTS) \
int err; \
const char *fmt; \
VA_DECL
#endif
#define PROCESS_MESSAGE(B) do { \
char *p = B; \
VA_LIST(ap) \
VA_START(ap, fmt); \
fmt = DoNLS(fmt); \
(void)vsnprintf(p, sizeof(B) - 100, fmt, VA_ARGS(ap)); \
VA_END(ap); \
if (err) \
{ \
p += strlen(p); \
*p++ = ':'; \
*p++ = ' '; \
strncpy(p, strerror(err), B + sizeof(B) - p - 1); \
B[sizeof(B) - 1] = 0; \
} \
} while (0)
DEFINE_VARARGS_FN(Msg)
{
char buf[MAXPATHLEN*2];
PROCESS_MESSAGE(buf);
debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
if (display && displays)
MakeStatus(buf);
else if (displays)
{
for (display = displays; display; display = display->d_next)
MakeStatus(buf);
}
else if (display)
{
/* no displays but a display - must have forked.
* send message to backend!
*/
char *tty = D_usertty;
struct display *olddisplay = display;
display = 0; /* only send once */
SendErrorMsg(tty, buf);
display = olddisplay;
}
else
printf("%s\r\n", buf);
if (queryflag >= 0)
write(queryflag, buf, strlen(buf));
}
/*
* Call FinitTerm for all displays, write a message to each and call eexit();
*/
DEFINE_VARARGS_FN(Panic)
{
char buf[MAXPATHLEN*2];
PROCESS_MESSAGE(buf);
debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
if (displays == 0 && display == 0)
{
printf("%s\r\n", buf);
if (PanicPid)
Kill(PanicPid, SIG_BYE);
}
else if (displays == 0)
{
/* no displays but a display - must have forked.
* send message to backend!
*/
char *tty = D_usertty;
display = 0;
SendErrorMsg(tty, buf);
sleep(2);
_exit(1);
}
else
for (display = displays; display; display = display->d_next)
{
if (D_status)
RemoveStatus();
FinitTerm();
Flush(3);
#ifdef UTMPOK
RestoreLoginSlot();
#endif
SetTTY(D_userfd, &D_OldMode);
fcntl(D_userfd, F_SETFL, 0);
write(D_userfd, buf, strlen(buf));
write(D_userfd, "\n", 1);
freetty();
if (D_userpid)
Kill(D_userpid, SIG_BYE);
}
#ifdef MULTIUSER
if (tty_oldmode >= 0)
{
# ifdef USE_SETEUID
if (setuid(own_uid))
xseteuid(own_uid); /* may be a loop. sigh. */
# else
setuid(own_uid);
# endif
debug1("Panic: changing back modes from %s\n", attach_tty);
chmod(attach_tty, tty_oldmode);
}
#endif
eexit(1);
}
DEFINE_VARARGS_FN(QueryMsg)
{
char buf[MAXPATHLEN*2];
if (queryflag < 0)
return;
PROCESS_MESSAGE(buf);
write(queryflag, buf, strlen(buf));
}
DEFINE_VARARGS_FN(Dummy)
{}
#undef PROCESS_MESSAGE
#undef DEFINE_VARARGS_FN
/*
* '^' is allowed as an escape mechanism for control characters. jw.
*
* Added time insertion using ideas/code from /\ndy Jones
* (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
*
*/
#ifndef USE_LOCALE
static const char days[] = "SunMonTueWedThuFriSat";
static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
#endif
static char winmsg_buf[MAXSTR];
#define MAX_WINMSG_REND 256 /* rendition changes */
static int winmsg_rend[MAX_WINMSG_REND];
static int winmsg_rendpos[MAX_WINMSG_REND];
static int winmsg_numrend;
static char *
pad_expand(buf, p, numpad, padlen)
char *buf;
char *p;
int numpad;
int padlen;
{
char *pn, *pn2;
int i, r;
padlen = padlen - (p - buf); /* space for rent */
if (padlen < 0)
padlen = 0;
pn2 = pn = p + padlen;
r = winmsg_numrend;
while (p >= buf)
{
if (r && *p != 127 && p - buf == winmsg_rendpos[r - 1])
{
winmsg_rendpos[--r] = pn - buf;
continue;
}
*pn-- = *p;
if (*p-- == 127)
{
pn[1] = ' ';
i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
padlen -= i;
while (i-- > 0)
*pn-- = ' ';
numpad--;
if (r && p - buf == winmsg_rendpos[r - 1])
winmsg_rendpos[--r] = pn - buf;
}
}
return pn2;
}
struct backtick {
struct backtick *next;
int num;
int tick;
int lifespan;
time_t bestbefore;
char result[MAXSTR];
char **cmdv;
struct event ev;
char *buf;
int bufi;
};
struct backtick *backticks;
static void
backtick_filter(bt)
struct backtick *bt;
{
char *p, *q;
int c;
for (p = q = bt->result; (c = (unsigned char)*p++) != 0;)
{
if (c == '\t')
c = ' ';
if (c >= ' ' || c == '\005')
*q++ = c;
}
*q = 0;
}
static void
backtick_fn(ev, data)
struct event *ev;
char *data;
{
struct backtick *bt;
int i, j, k, l;
bt = (struct backtick *)data;
debug1("backtick_fn for #%d\n", bt->num);
i = bt->bufi;
l = read(ev->fd, bt->buf + i, MAXSTR - i);
if (l <= 0)
{
debug1("EOF on backtick #%d\n", bt->num);
evdeq(ev);
close(ev->fd);
ev->fd = -1;
return;
}
debug1("read %d bytes\n", l);
i += l;
for (j = 0; j < l; j++)
if (bt->buf[i - j - 1] == '\n')
break;
if (j < l)
{
for (k = i - j - 2; k >= 0; k--)
if (bt->buf[k] == '\n')
break;
k++;
bcopy(bt->buf + k, bt->result, i - j - k);
bt->result[i - j - k - 1] = 0;
backtick_filter(bt);
WindowChanged(0, '`');
}
if (j == l && i == MAXSTR)
{
j = MAXSTR/2;
l = j + 1;
}
if (j < l)
{
if (j)
bcopy(bt->buf + i - j, bt->buf, j);
i = j;
}
bt->bufi = i;
}
void
setbacktick(num, lifespan, tick, cmdv)
int num;
int lifespan;
int tick;
char **cmdv;
{
struct backtick **btp, *bt;
char **v;
debug1("setbacktick called for backtick #%d\n", num);
for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
if (bt->num == num)
break;
if (!bt && !cmdv)
return;
if (bt)
{
for (v = bt->cmdv; *v; v++)
free(*v);
free(bt->cmdv);
if (bt->buf)
free(bt->buf);
if (bt->ev.fd >= 0)
close(bt->ev.fd);
evdeq(&bt->ev);
}
if (bt && !cmdv)
{
*btp = bt->next;
free(bt);
return;
}
if (!bt)
{
bt = (struct backtick *)malloc(sizeof *bt);
if (!bt)
{
Msg(0, "%s", strnomem);
return;
}
bzero(bt, sizeof(*bt));
bt->next = 0;
*btp = bt;
}
bt->num = num;
bt->tick = tick;
bt->lifespan = lifespan;
bt->bestbefore = 0;
bt->result[0] = 0;
bt->buf = 0;
bt->bufi = 0;
bt->cmdv = cmdv;
bt->ev.fd = -1;
if (bt->tick == 0 && bt->lifespan == 0)
{
debug("setbacktick: continuous mode\n");
bt->buf = (char *)malloc(MAXSTR);
if (bt->buf == 0)
{
Msg(0, "%s", strnomem);
setbacktick(num, 0, 0, (char **)0);
return;
}
bt->ev.type = EV_READ;
bt->ev.fd = readpipe(bt->cmdv);
bt->ev.handler = backtick_fn;
bt->ev.data = (char *)bt;
if (bt->ev.fd >= 0)
evenq(&bt->ev);
}
}
static char *
runbacktick(bt, tickp, now)
struct backtick *bt;
int *tickp;
time_t now;
{
int f, i, l, j;
time_t now2;
debug1("runbacktick called for backtick #%d\n", bt->num);
if (bt->tick && (!*tickp || bt->tick < *tickp))
*tickp = bt->tick;
if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore)
{
debug1("returning old result (%d)\n", bt->lifespan);
return bt->result;
}
f = readpipe(bt->cmdv);
if (f == -1)
return bt->result;
i = 0;
while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0)
{
debug1("runbacktick: read %d bytes\n", l);
i += l;
for (j = 1; j < l; j++)
if (bt->result[i - j - 1] == '\n')
break;
if (j == l && i == sizeof(bt->result))
{
j = sizeof(bt->result) / 2;
l = j + 1;
}
if (j < l)
{
bcopy(bt->result + i - j, bt->result, j);
i = j;
}
}
close(f);
bt->result[sizeof(bt->result) - 1] = '\n';
if (i && bt->result[i - 1] == '\n')
i--;
debug1("runbacktick: finished, %d bytes\n", i);
bt->result[i] = 0;
backtick_filter(bt);
(void)time(&now2);
bt->bestbefore = now2 + bt->lifespan;
return bt->result;
}
int
AddWinMsgRend(str, r)
const char *str;
int r;
{
if (winmsg_numrend >= MAX_WINMSG_REND || str < winmsg_buf ||
str >= winmsg_buf + MAXSTR)
return -1;
winmsg_rend[winmsg_numrend] = r;
winmsg_rendpos[winmsg_numrend] = str - winmsg_buf;
winmsg_numrend++;
return 0;
}
char *
MakeWinMsgEv(str, win, esc, padlen, ev, rec)
char *str;
struct win *win;
int esc;
int padlen;
struct event *ev;
int rec;
{
static int tick;
char *s = str;
register char *p = winmsg_buf;
register int ctrl;
struct timeval now;
struct tm *tm;
int l, i, r;
int num;
int zeroflg;
int longflg;
int minusflg;
int plusflg;
int qmflag = 0, omflag = 0, qmnumrend = 0;
char *qmpos = 0;
int numpad = 0;
int lastpad = 0;
int truncpos = -1;
int truncper = 0;
int trunclong = 0;
struct backtick *bt = NULL;
if (winmsg_numrend >= 0)
winmsg_numrend = 0;
else
winmsg_numrend = -winmsg_numrend;
tick = 0;
tm = 0;
ctrl = 0;
gettimeofday(&now, NULL);
for (; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
{
*p = *s;
if (ctrl)
{
ctrl = 0;
if (*s != '^' && *s >= 64)
*p &= 0x1f;
continue;
}
if (*s != esc)
{
if (esc == '%')
{
switch (*s)
{
#if 0
case '~':
*p = BELL;
break;
#endif
case '^':
ctrl = 1;
*p-- = '^';
break;
default:
break;
}
}
continue;
}
if (*++s == esc) /* double escape ? */
continue;
if ((plusflg = *s == '+') != 0)
s++;
if ((minusflg = *s == '-') != 0)
s++;
if ((zeroflg = *s == '0') != 0)
s++;
num = 0;
while(*s >= '0' && *s <= '9')
num = num * 10 + (*s++ - '0');
if ((longflg = *s == 'L') != 0)
s++;
switch (*s)
{
case '?':
p--;
if (qmpos)
{
if ((!qmflag && !omflag) || omflag == 1)
{
p = qmpos;
if (qmnumrend < winmsg_numrend)
winmsg_numrend = qmnumrend;
}
qmpos = 0;
break;
}
qmpos = p;
qmnumrend = winmsg_numrend;
qmflag = omflag = 0;
break;
case ':':
p--;
if (!qmpos)
break;
if (qmflag && omflag != 1)
{
omflag = 1;
qmpos = p;
qmnumrend = winmsg_numrend;
}
else
{
p = qmpos;
if (qmnumrend < winmsg_numrend)
winmsg_numrend = qmnumrend;
omflag = -1;
}
break;
case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
case 'a': case 'A': case 's': case 'c': case 'C':
if (l < 4)
break;
if (tm == 0)
{
time_t nowsec = now.tv_sec;
tm = localtime(&nowsec);
}
qmflag = 1;
if (!tick || tick > 3600)
tick = 3600;
switch (*s)
{
case 'd':
sprintf(p, "%02d", tm->tm_mday % 100);
break;
case 'D':
#ifdef USE_LOCALE
strftime(p, l, (longflg ? "%A" : "%a"), tm);
#else
sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
#endif
break;
case 'm':
sprintf(p, "%02d", tm->tm_mon + 1);
break;
case 'M':
#ifdef USE_LOCALE
strftime(p, l, (longflg ? "%B" : "%b"), tm);
#else
sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
#endif
break;
case 'y':
sprintf(p, "%02d", tm->tm_year % 100);
break;
case 'Y':
sprintf(p, "%04d", tm->tm_year + 1900);
break;
case 'a':
sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
break;
case 'A':
sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
break;
case 's':
sprintf(p, "%02d", tm->tm_sec);
tick = 1;
break;
case 'c':
sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
if (!tick || tick > 60)
tick = 60;
break;
case 'C':
sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
if (!tick || tick > 60)
tick = 60;
break;
default:
break;
}
p += strlen(p) - 1;
break;
case 'l':
#ifdef LOADAV
*p = 0;
if (l > 20)
AddLoadav(p);
if (*p)
{
qmflag = 1;
p += strlen(p) - 1;
}
else
*p = '?';
if (!tick || tick > 60)
tick = 60;
#else
*p = '?';
#endif
p += strlen(p) - 1;
break;
case '`':
case 'h':
if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0)))
{
p--;
break;
}
if (*s == '`')
{
for (bt = backticks; bt; bt = bt->next)
if (bt->num == num)
break;
if (bt == 0)
{
p--;
break;
}
}
{
char savebuf[sizeof(winmsg_buf)];
int oldtick = tick;
int oldnumrend = winmsg_numrend;
*p = 0;
strcpy(savebuf, winmsg_buf);
winmsg_numrend = -winmsg_numrend;
MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
debug2("oldtick=%d tick=%d\n", oldtick, tick);
if (!tick || oldtick < tick)
tick = oldtick;
if ((int)strlen(winmsg_buf) < l)
strcat(savebuf, winmsg_buf);
strcpy(winmsg_buf, savebuf);
while (oldnumrend < winmsg_numrend)
winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
if (*p)
qmflag = 1;
p += strlen(p) - 1;
}
break;
case 'w':
case 'W':
{
struct win *oldfore = 0;
char *ss;
if (display)
{
oldfore = D_fore;
D_fore = win;
}
ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0) | (minusflg ? 8 : 0), win ? win->w_number : -1);
if (display)
D_fore = oldfore;
}
if (*p)
qmflag = 1;
p += strlen(p) - 1;
break;
case 'u':
*p = 0;
if (win)
AddOtherUsers(p, l - 1, win);
if (*p)
qmflag = 1;
p += strlen(p) - 1;
break;
case 'f':
*p = 0;
if (win)
AddWindowFlags(p, l - 1, win);
if (*p)
qmflag = 1;
p += strlen(p) - 1;
break;
case 't':
*p = 0;
if (win && (int)strlen(win->w_title) < l)
{
strcpy(p, win->w_title);
if (*p)
qmflag = 1;
}
p += strlen(p) - 1;
break;
case '{':
{
char rbuf[128];
s++;
for (i = 0; i < 127; i++)
if (s[i] && s[i] != '}')
rbuf[i] = s[i];
else
break;
if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND)
{
r = -1;
rbuf[i] = 0;
debug1("MakeWinMsg attrcolor %s\n", rbuf);
if (i != 1 || rbuf[0] != '-')
r = ParseAttrColor(rbuf, (char *)0, 0);
if (r != -1 || (i == 1 && rbuf[0] == '-'))
{
winmsg_rend[winmsg_numrend] = r;
winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
winmsg_numrend++;
}
}
s += i;
p--;
}
break;
case 'H':
*p = 0;
if ((int)strlen(HostName) < l)
{
strcpy(p, HostName);
if (*p)
qmflag = 1;
}
p += strlen(p) - 1;
break;
case 'S':
{
char *session_name;
*p = 0;
session_name = strchr(SockName, '.') + 1;
if ((int)strlen(session_name) < l)
{
strcpy(p, session_name);
if (*p)
qmflag = 1;
}
p += strlen(p) - 1;
}
break;
case 'p':
{
sprintf(p, "%d", (plusflg && display) ? D_userpid : getpid());
p += strlen(p) - 1;
}
break;
case 'F':
p--;
/* small hack */
if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
minusflg = !minusflg;
if (minusflg)
qmflag = 1;
break;
case 'P':
p--;
#ifdef COPY_PASTE
if (display && ev && ev != &D_hstatusev) /* Hack */
{
/* Is the layer in the current canvas in copy mode? */
struct canvas *cv = (struct canvas *)ev->data;
if (ev == &cv->c_captev && cv->c_layer->l_layfn == &MarkLf)
qmflag = 1;
}
#endif
break;
case 'E':
p--;
if (display && D_ESCseen)
qmflag = 1;
break;
case '>':
truncpos = p - winmsg_buf;
truncper = num > 100 ? 100 : num;
trunclong = longflg;
p--;
break;
case '=':
case '<':
*p = ' ';
if (num || zeroflg || plusflg || longflg || (*s != '='))
{
/* expand all pads */
if (minusflg)
{
num = (plusflg ? lastpad : padlen) - num;
if (!plusflg && padlen == 0)
num = p - winmsg_buf;
plusflg = 0;
}
else if (!zeroflg)
{
if (*s != '=' && num == 0 && !plusflg)
num = 100;
if (num > 100)
num = 100;
if (padlen == 0)
num = p - winmsg_buf;
else
num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
}
if (num < 0)
num = 0;
if (plusflg)
num += lastpad;
if (num > MAXSTR - 1)
num = MAXSTR - 1;
if (numpad)
p = pad_expand(winmsg_buf, p, numpad, num);
numpad = 0;
if (p - winmsg_buf > num && !longflg)
{
int left, trunc;
if (truncpos == -1)
{
truncpos = lastpad;
truncper = 0;
}
trunc = lastpad + truncper * (num - lastpad) / 100;
if (trunc > num)
trunc = num;
if (trunc < lastpad)
trunc = lastpad;
left = truncpos - trunc;
if (left > p - winmsg_buf - num)
left = p - winmsg_buf - num;
debug1("lastpad = %d, ", lastpad);
debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
if (left > 0)
{
if (left + lastpad > p - winmsg_buf)
left = p - winmsg_buf - lastpad;
if (p - winmsg_buf - lastpad - left > 0)
bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left);
p -= left;
r = winmsg_numrend;
while (r && winmsg_rendpos[r - 1] > lastpad)
{
r--;
winmsg_rendpos[r] -= left;
if (winmsg_rendpos[r] < lastpad)
winmsg_rendpos[r] = lastpad;
}
if (trunclong)
{
if (p - winmsg_buf > lastpad)
winmsg_buf[lastpad] = '.';
if (p - winmsg_buf > lastpad + 1)
winmsg_buf[lastpad + 1] = '.';
if (p - winmsg_buf > lastpad + 2)
winmsg_buf[lastpad + 2] = '.';
}
}
if (p - winmsg_buf > num)
{
p = winmsg_buf + num;
if (trunclong)
{
if (num - 1 >= lastpad)
p[-1] = '.';
if (num - 2 >= lastpad)
p[-2] = '.';
if (num - 3 >= lastpad)
p[-3] = '.';
}
r = winmsg_numrend;
while (r && winmsg_rendpos[r - 1] > num)
winmsg_rendpos[--r] = num;
}
truncpos = -1;
trunclong = 0;
if (lastpad > p - winmsg_buf)
lastpad = p - winmsg_buf;
debug1("lastpad now %d\n", lastpad);
}
if (*s == '=')
{
while (p - winmsg_buf < num)
*p++ = ' ';
lastpad = p - winmsg_buf;
truncpos = -1;
trunclong = 0;
debug1("lastpad2 now %d\n", lastpad);
}
p--;
}
else if (padlen)
{
*p = 127; /* internal pad representation */
numpad++;
}
break;
case 'n':
s++;
/* FALLTHROUGH */
default:
s--;
if (l > 10 + num)
{
if (num == 0)
num = 1;
if (!win)
sprintf(p, "%*s", num, num > 1 ? "--" : "-");
else
sprintf(p, "%*d", num, win->w_number);
qmflag = 1;
p += strlen(p) - 1;
}
break;
}
}
if (qmpos && !qmflag)
p = qmpos + 1;
*p = '\0';
if (numpad)
{
if (padlen > MAXSTR - 1)
padlen = MAXSTR - 1;
p = pad_expand(winmsg_buf, p, numpad, padlen);
}
if (ev)
{
evdeq(ev); /* just in case */
ev->timeout.tv_sec = 0;
ev->timeout.tv_usec = 0;
}
if (ev && tick)
{
now.tv_usec = 100000;
if (tick == 1)
now.tv_sec++;
else
now.tv_sec += tick - (now.tv_sec % tick);
ev->timeout = now;
debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
}
return winmsg_buf;
}
char *
MakeWinMsg(s, win, esc)
char *s;
struct win *win;
int esc;
{
return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
}
void
PutWinMsg(s, start, max)
char *s;
int start, max;
{
int i, p, l, r, n;
struct mchar rend;
struct mchar rendstack[MAX_WINMSG_REND];
int rendstackn = 0;
if (s != winmsg_buf)
{
/* sorry, no fancy coloring available */
debug1("PutWinMsg %s plain\n", s);
l = strlen(s);
if (l > max)
l = max;
l -= start;
s += start;
while (l-- > 0)
PUTCHARLP(*s++);
return;
}
rend = D_rend;
p = 0;
l = strlen(s);
debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
for (i = 0; i < winmsg_numrend && max > 0; i++)
{
if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
break;
if (p < winmsg_rendpos[i])
{
n = winmsg_rendpos[i] - p;
if (n > max)
n = max;
max -= n;
p += n;
while(n-- > 0)
{
if (start-- > 0)
s++;
else
PUTCHARLP(*s++);
}
}
r = winmsg_rend[i];
if (r == -1)
{
if (rendstackn > 0)
rend = rendstack[--rendstackn];
}
else
{
rendstack[rendstackn++] = rend;
ApplyAttrColor(r, &rend);
}
SetRendition(&rend);
}
if (p < l)
{
n = l - p;
if (n > max)
n = max;
while(n-- > 0)
{
if (start-- > 0)
s++;
else
PUTCHARLP(*s++);
}
}
}
#ifdef DEBUG
static void
fds1(i, j)
int i, j;
{
while (i < j)
{
debug1("%d ", i);
i++;
}
if ((j = open("/dev/null", 0)) >= 0)
{
fds1(i + 1, j);
close(j);
}
else
{
while (dup(++i) < 0 && errno != EBADF)
debug1("%d ", i);
debug1(" [%d]\n", i);
}
}
static void
fds()
{
debug("fds: ");
fds1(-1, -1);
}
#endif
static void
serv_read_fn(ev, data)
struct event *ev;
char *data;
{
debug("Knock - knock!\n");
ReceiveMsg();
}
static void
serv_select_fn(ev, data)
struct event *ev;
char *data;
{
struct win *p;
debug("serv_select_fn called\n");
/* XXX: messages?? */
if (GotSigChld)
{
SigChldHandler();
}
if (InterruptPlease)
{
debug("Backend received interrupt\n");
/* This approach is rather questionable in a multi-display
* environment */
if (fore && displays)
{
#if defined(TERMIO) || defined(POSIX)
char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
#else
char ibuf = displays->d_OldMode.m_tchars.t_intrc;
#endif
#ifdef PSEUDOS
write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd,
&ibuf, 1);
debug1("Backend wrote interrupt to %d", fore->w_number);
debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
#else
write(fore->w_ptyfd, &ibuf, 1);
debug1("Backend wrote interrupt to %d\n", fore->w_number);
#endif
}
InterruptPlease = 0;
}
for (p = windows; p; p = p->w_next)
{
if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL)
{
struct canvas *cv;
int visual = p->w_bell == BELL_VISUAL || visual_bell;
p->w_bell = BELL_ON;
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 == 0)
{
p->w_bell = BELL_DONE;
Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
}
else if (visual && !D_VB && (!D_status || !D_status_bell))
{
Msg(0, "%s", VisualBellString);
if (D_status)
{
D_status_bell = 1;
debug1("using vbell timeout %d\n", VBellWait);
SetTimeout(&D_statusev, VBellWait );
}
}
}
/* don't annoy the user with two messages */
if (p->w_monitor == MON_FOUND)
p->w_monitor = MON_DONE;
WindowChanged(p, 'f');
}
if (p->w_monitor == MON_FOUND)
{
struct canvas *cv;
p->w_monitor = MON_ON;
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)
continue; /* user already sees window */
#ifdef MULTIUSER
if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
continue; /* user doesn't care */
#endif
Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
p->w_monitor = MON_DONE;
}
WindowChanged(p, 'f');
}
if (p->w_silence == SILENCE_FOUND)
{
/* Unset the flag if the user switched to this window. */
if (p->w_layer.l_cvlist)
{
p->w_silence = SILENCE_ON;
WindowChanged(p, 'f');
}
}
}
for (display = displays; display; display = display->d_next)
{
struct canvas *cv;
if (D_status == STATUS_ON_WIN)
continue;
/* XXX: should use display functions! */
for (cv = D_cvlist; cv; cv = cv->c_next)
{
int lx, ly;
/* normalize window, see resize.c */
lx = cv->c_layer->l_x;
ly = cv->c_layer->l_y;
if (lx == cv->c_layer->l_width)
lx--;
if (ly + cv->c_yoff < cv->c_ys)
{
int i, n = cv->c_ys - (ly + cv->c_yoff);
cv->c_yoff = cv->c_ys - ly;
RethinkViewportOffsets(cv);
if (n > cv->c_layer->l_height)
n = cv->c_layer->l_height;
CV_CALL(cv,
LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
LayRedisplayLine(-1, -1, -1, 1);
for (i = 0; i < n; i++)
LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
if (cv == cv->c_display->d_forecv)
LaySetCursor();
);
}
else if (ly + cv->c_yoff > cv->c_ye)
{
int i, n = ly + cv->c_yoff - cv->c_ye;
cv->c_yoff = cv->c_ye - ly;
RethinkViewportOffsets(cv);
if (n > cv->c_layer->l_height)
n = cv->c_layer->l_height;
CV_CALL(cv,
LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
LayRedisplayLine(-1, -1, -1, 1);
for (i = 0; i < n; i++)
LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
if (cv == cv->c_display->d_forecv)
LaySetCursor();
);
}
if (lx + cv->c_xoff < cv->c_xs)
{
int i, n = cv->c_xs - (lx + cv->c_xoff);
if (n < (cv->c_xe - cv->c_xs + 1) / 2)
n = (cv->c_xe - cv->c_xs + 1) / 2;
if (cv->c_xoff + n > cv->c_xs)
n = cv->c_xs - cv->c_xoff;
cv->c_xoff += n;
RethinkViewportOffsets(cv);
if (n > cv->c_layer->l_width)
n = cv->c_layer->l_width;
CV_CALL(cv,
LayRedisplayLine(-1, -1, -1, 1);
for (i = 0; i < flayer->l_height; i++)
{
LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0);
LayRedisplayLine(i, 0, n - 1, 1);
}
if (cv == cv->c_display->d_forecv)
LaySetCursor();
);
}
else if (lx + cv->c_xoff > cv->c_xe)
{
int i, n = lx + cv->c_xoff - cv->c_xe;
if (n < (cv->c_xe - cv->c_xs + 1) / 2)
n = (cv->c_xe - cv->c_xs + 1) / 2;
if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
cv->c_xoff -= n;
RethinkViewportOffsets(cv);
if (n > cv->c_layer->l_width)
n = cv->c_layer->l_width;
CV_CALL(cv,
LayRedisplayLine(-1, -1, -1, 1);
for (i = 0; i < flayer->l_height; i++)
{
LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
}
if (cv == cv->c_display->d_forecv)
LaySetCursor();
);
}
}
}
for (display = displays; display; display = display->d_next)
{
if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
continue;
debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
CV_CALL(D_forecv, LayRestore();LaySetCursor());
}
}
static void
logflush_fn(ev, data)
struct event *ev;
char *data;
{
struct win *p;
char *buf;
int n;
if (!islogfile(NULL))
return; /* no more logfiles */
logfflush(NULL);
n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
if (n)
{
SetTimeout(ev, n * 1000);
evenq(ev); /* re-enqueue ourself */
}
if (!logtstamp_on)
return;
/* write fancy time-stamp */
for (p = windows; p; p = p->w_next)
{
if (!p->w_log)
continue;
p->w_logsilence += n;
if (p->w_logsilence < logtstamp_after)
continue;
if (p->w_logsilence - n >= logtstamp_after)
continue;
buf = MakeWinMsg(logtstamp_string, p, '%');
logfwrite(p->w_log, buf, strlen(buf));
}
}
/*
* Interprets ^?, ^@ and other ^-control-char notation.
* Interprets \ddd octal notation
*
* The result is placed in *cp, p is advanced behind the parsed expression and
* returned.
*/
static char *
ParseChar(p, cp)
char *p, *cp;
{
if (*p == 0)
return 0;
if (*p == '^' && p[1])
{
if (*++p == '?')
*cp = '\177';
else if (*p >= '@')
*cp = Ctrl(*p);
else
return 0;
++p;
}
else if (*p == '\\' && *++p <= '7' && *p >= '0')
{
*cp = 0;
do
*cp = *cp * 8 + *p - '0';
while (*++p <= '7' && *p >= '0');
}
else
*cp = *p++;
return p;
}
static int
ParseEscape(p)
char *p;
{
unsigned char buf[2];
if (*p == 0)
SetEscape((struct acluser *)0, -1, -1);
else
{
if ((p = ParseChar(p, (char *)buf)) == NULL ||
(p = ParseChar(p, (char *)buf+1)) == NULL || *p)
return -1;
SetEscape((struct acluser *)0, buf[0], buf[1]);
}
return 0;
}
screen-4.1.0/HACKING 0000644 0001750 0001750 00000003430 11642704565 012034 0 ustar abe abe 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 procesing 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().
screen-4.1.0/utf8encodings/ 0000755 0001750 0001750 00000000000 11642704565 013625 5 ustar abe abe screen-4.1.0/utf8encodings/03 0000644 0001750 0001750 00000100236 11642704565 013774 0 ustar abe abe ScreenI2UTF8 KSC 5601-1992 !!0 !"0!#0!$ ·!% %!& &!' ¨!(0!) !* !+"%!,ÿ