* Copyright (c) 1989 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
static char sccsid
[] = "@(#)slc.c 5.1 (Berkeley) %G%";
static char *def_slcbuf
= (char *)0;;
static int def_slclen
= 0;
static int slcchange
; /* change to slc is requested */
static char *slcptr
; /* pointer into slc buffer */
static char slcbuf
[NSLC
*6]; /* buffer for slc negotiation */
* Write out the current special characters to the client.
* Send out list of triplets of special characters
* to client. We only send info on the characters
* that are currently supported.
for (i
= 1; i
<= NSLC
; i
++) {
if ((slctab
[i
].current
.flag
& SLC_LEVELBITS
) != SLC_NOSUPPORT
) {
add_slc((unsigned char)i
, slctab
[i
].current
.flag
,
* Set pty special characters to all the defaults.
for (i
= 1; i
<= NSLC
; i
++) {
slctab
[i
].current
.flag
= slctab
[i
].defset
.flag
;
slctab
[i
].current
.val
= slctab
[i
].defset
.val
;
*(slctab
[i
].sptr
) = slctab
[i
].defset
.val
;
} /* end of default_slc */
* Initialize the slc mapping table.
for (i
= 1; i
<= NSLC
; i
++) {
spcset(i
, &slctab
[i
].defset
.val
, &slctab
[i
].sptr
);
slctab
[i
].current
.flag
= SLC_NOSUPPORT
;
slctab
[i
].current
.val
= 0;
} /* end of get_slc_defaults */
* Add an slc triplet to the slc buffer.
register unsigned char func
, flag
, val
;
* Get ready to process incoming slc's and respond to them.
* The parameter getit is non-zero if it is necessary to grab a copy
* of the terminal control structures.
(void) sprintf(slcbuf
, "%c%c%c%c", IAC
, SB
, TELOPT_LINEMODE
, LM_SLC
);
* Finish up the slc negotiation. If something to send, then send it.
* If a change has occured, store the new terminal control
* structures back to the terminal driver.
* If the pty state has not yet been fully processed and there is a
* deferred slc request from the client, then do not send any
* sort of slc negotiation now. We will respond to the client's
if (def_slcbuf
&& (terminit() == 0)) {
if (slcptr
> (slcbuf
+ 4)) {
return(slcptr
- slcbuf
- 4);
(void) sprintf(slcptr
, "%c%c", IAC
, SE
);
netflush(); /* force it out immediately */
* Figure out what to do about the client's slc
process_slc(func
, flag
, val
)
register unsigned char func
, flag
, val
;
register int hislevel
, mylevel
, ack
;
* Ensure that we know something about this function
add_slc(func
, SLC_NOSUPPORT
, 0);
* Process the special case requests of 0 SLC_DEFAULT 0
* and 0 SLC_VARIABLE 0. Be a little forgiving here, don't
* worry about whether the value is actually 0 or not.
if ((flag
= flag
& SLC_LEVELBITS
) == SLC_DEFAULT
) {
if (flag
== SLC_DEFAULT
|| flag
== SLC_VARIABLE
) {
* Appears to be a function that we know something about. So
* get on with it and see what we know.
hislevel
= flag
& SLC_LEVELBITS
;
mylevel
= slctab
[func
].current
.flag
& SLC_LEVELBITS
;
* the function value and level are the same as what we already have;
* or the level is the same and the ack bit is set
if (hislevel
== mylevel
&& (val
== slctab
[func
].current
.val
|| ack
)) {
change_slc(func
, flag
, val
);
} /* end of process_slc */
* Process a request to change one of our special characters.
* Compare client's request with what we are capable of supporting.
change_slc(func
, flag
, val
)
register unsigned char func
, flag
, val
;
register int hislevel
, mylevel
;
hislevel
= flag
& SLC_LEVELBITS
;
mylevel
= slctab
[func
].defset
.flag
& SLC_LEVELBITS
;
* If client is setting a function to NOSUPPORT
* or DEFAULT, then we can easily and directly
* accomodate the request.
if (hislevel
== SLC_NOSUPPORT
) {
slctab
[func
].current
.flag
= flag
;
slctab
[func
].current
.val
= val
;
add_slc(func
, flag
, val
);
if (hislevel
== SLC_DEFAULT
) {
* Special case here. If client tells us to use
* the default on a function we don't support, then
* return NOSUPPORT instead of what we may have as a
* default level of DEFAULT.
if (mylevel
== SLC_DEFAULT
) {
slctab
[func
].current
.flag
= SLC_NOSUPPORT
;
slctab
[func
].current
.flag
= slctab
[func
].defset
.flag
;
slctab
[func
].current
.val
= slctab
[func
].defset
.val
;
add_slc(func
, slctab
[func
].current
.flag
,
slctab
[func
].current
.val
);
* Client wants us to change to a new value or he
* is telling us that he can't change to our value.
* Some of the slc's we support and can change,
* some we do support but can't change,
* and others we don't support at all.
* If we can change it then we have a pointer to
* the place to put the new value, so change it,
* otherwise, continue the negotiation.
* We can change this one.
slctab
[func
].current
.val
= val
;
*(slctab
[func
].sptr
) = val
;
slctab
[func
].current
.flag
= flag
;
add_slc(func
, flag
, val
);
* It is not possible for us to support this
* If our level is DEFAULT, then just ack whatever was
* If he can't change and we can't change,
* then degenerate to NOSUPPORT.
* Otherwise we send our level back to him, (CANTCHANGE
* or NOSUPPORT) and if CANTCHANGE, send
if (mylevel
== SLC_DEFAULT
) {
slctab
[func
].current
.flag
= flag
;
slctab
[func
].current
.val
= val
;
} else if (hislevel
== SLC_CANTCHANGE
&&
mylevel
== SLC_CANTCHANGE
) {
slctab
[func
].current
.flag
= flag
;
slctab
[func
].current
.flag
= flag
;
if (mylevel
== SLC_CANTCHANGE
) {
slctab
[func
].current
.val
=
val
= slctab
[func
].current
.val
;
add_slc(func
, flag
, val
);
} /* end of change_slc */
* Check the special characters in use and notify the client if any have
* changed. Only those characters that are capable of being changed are
* likely to have changed. If a local change occurs, kick the support level
* and flags up to the defaults.
for (i
= 1; i
<= NSLC
; i
++) {
#if defined(USE_TERMIO) && defined(SYSV_TERMIO)
* In a perfect world this would be a neat little
* function. But in this world, we should not notify
* client of changes to the VEOF char when
* ICANON is off, because it is not representing
if (!tty_isediting() && i
== SLC_EOF
)
#endif /* defined(USE_TERMIO) && defined(SYSV_TERMIO) */
(*(slctab
[i
].sptr
) != slctab
[i
].current
.val
)) {
slctab
[i
].current
.val
= *(slctab
[i
].sptr
);
slctab
[i
].current
.flag
= slctab
[i
].defset
.flag
;
add_slc((unsigned char)i
, slctab
[i
].current
.flag
,
* Process an slc option buffer. Defer processing of incoming slc's
* until after the terminal state has been processed. Save the first slc
* request that comes along, but discard all others.
* ptr points to the beginning of the buffer, len is the length.
register unsigned char func
, flag
, val
;
register char *end
= (char *)(ptr
+ len
);
if (terminit()) { /* go ahead */
process_slc(func
, flag
, val
);
* save this slc buffer if it is the first, otherwise dump
if (def_slcbuf
== (char *)0) {
def_slcbuf
= malloc((unsigned)len
);
if (def_slcbuf
== (char *)0)
bcopy(ptr
, def_slcbuf
, len
);
} /* end of do_opt_slc */
* Do slc stuff that was deferred.
do_opt_slc(def_slcbuf
, def_slclen
);