1
0
mirror of https://github.com/moparisthebest/socat synced 2024-12-22 06:58:47 -05:00

Merge /home/gerhard/Develop/socat-escape into socat-1.7.0.0

This commit is contained in:
Gerhard Rieger 2008-09-22 22:05:08 +02:00
commit bd3810642b
12 changed files with 152 additions and 26 deletions

View File

@ -1,4 +1,8 @@
new features:
new address option "escape" allows to break a socat instance even when
raw terminal mode prevents ^C etc.
corrections: corrections:
some raw IP and UNIX datagram modes failed on BSD systems some raw IP and UNIX datagram modes failed on BSD systems

View File

@ -1 +1 @@
"1.6.0.1+ip4bind+recvfromfork+x64+execstderr+execspaces+cosmetics+poll+udplistencont+ignoreeofunblock" "1.6.0.1+ip4bind+recvfromfork+x64+execstderr+execspaces+cosmetics+poll+udplistencont+ignoreeofunblock+escape"

View File

@ -10,7 +10,7 @@ def(Filan)(0)(bf(Filan))
def(procan)(0)(bf(procan)) def(procan)(0)(bf(procan))
def(Procan)(0)(bf(Procan)) def(Procan)(0)(bf(Procan))
manpage(socat)(1)(Feb 2008)(socat)() manpage(socat)(1)(Jul 2008)(socat)()
whenhtml( whenhtml(
label(CONTENTS) label(CONTENTS)
@ -1516,6 +1516,10 @@ label(OPTION_LOCKFILE)dit(bf(tt(lockfile=<filename>)))
label(OPTION_WAITLOCK)dit(bf(tt(waitlock=<filename>))) label(OPTION_WAITLOCK)dit(bf(tt(waitlock=<filename>)))
If lockfile exists, waits until it disappears. When lockfile does not exist, If lockfile exists, waits until it disappears. When lockfile does not exist,
creates it and continues, unlinks lockfile on exit. creates it and continues, unlinks lockfile on exit.
label(OPTION_ESCAPE)dit(bf(tt(escape=<int>)))
Specifies the numeric code of a character that triggers EOF on the input
stream. It is useful with a terminal in raw mode
(link(example)(EXAMPLE_OPTION_ESCAPE)).
enddit() enddit()
startdit()enddit()nl() startdit()enddit()nl()
@ -2630,13 +2634,15 @@ at most 512 data bytes per packet (link(mss)(OPTION_MSS)).
label(EXAMPLE_ADDRESS_GOPEN) label(EXAMPLE_ADDRESS_GOPEN)
label(EXAMPLE_OPTION_RAW) label(EXAMPLE_OPTION_RAW)
label(EXAMPLE_OPTION_ECHO) label(EXAMPLE_OPTION_ECHO)
dit(bf(tt(socat - /dev/ttyS0,raw,echo=0,crnl))) label(EXAMPLE_OPTION_ESCAPE)
dit(bf(tt(socat -,raw,echo=0,escape=0x0f /dev/ttyS0,raw,echo=0,crnl)))
Opens an interactive connection via the serial line, e.g. for talking with a Opens an interactive connection via the serial line, e.g. for talking with a
modem. link(raw)(OPTION_RAW) and link(echo)(OPTION_ECHO) set ttyS0's terminal modem. link(raw)(OPTION_RAW) and link(echo)(OPTION_ECHO) set the console's and
parameters to practicable values, link(crnl)(OPTION_CRNL) ttyS0's terminal parameters to practicable values, link(crnl)(OPTION_CRNL)
converts to correct newline characters. Consider using converts to correct newline characters. link(escape)(OPTION_ESCAPE) allows to
link(READLINE)(ADDRESS_READLINE) instead of `-'. terminate the socat process with character control-O.
Consider using link(READLINE)(ADDRESS_READLINE) instead of the first address.
label(EXAMPLE_ADDRESS_UNIX_LISTEN) label(EXAMPLE_ADDRESS_UNIX_LISTEN)

70
socat.c
View File

@ -948,6 +948,10 @@ int _socat(void) {
/* avoid idle when all readbytes already there */ /* avoid idle when all readbytes already there */
mayrd1 = true; mayrd1 = true;
} }
/* escape char occurred? */
if (XIO_RDSTREAM(sock1)->actescape) {
bytes1 = 0; /* indicate EOF */
}
} }
/* (bytes1 == 0) handled later */ /* (bytes1 == 0) handled later */
} else { } else {
@ -976,6 +980,10 @@ int _socat(void) {
/* avoid idle when all readbytes already there */ /* avoid idle when all readbytes already there */
mayrd2 = true; mayrd2 = true;
} }
/* escape char occurred? */
if (XIO_RDSTREAM(sock2)->actescape) {
bytes2 = 0; /* indicate EOF */
}
} }
/* (bytes2 == 0) handled later */ /* (bytes2 == 0) handled later */
} else { } else {
@ -988,7 +996,8 @@ int _socat(void) {
bytes1, XIO_RDSTREAM(sock1)->eof, XIO_RDSTREAM(sock1)->ignoreeof, bytes1, XIO_RDSTREAM(sock1)->eof, XIO_RDSTREAM(sock1)->ignoreeof,
closing);*/ closing);*/
if (bytes1 == 0 || XIO_RDSTREAM(sock1)->eof >= 2) { if (bytes1 == 0 || XIO_RDSTREAM(sock1)->eof >= 2) {
if (XIO_RDSTREAM(sock1)->ignoreeof && !closing) { if (XIO_RDSTREAM(sock1)->ignoreeof &&
!XIO_RDSTREAM(sock1)->actescape && !closing) {
Debug1("socket 1 (fd %d) is at EOF, ignoring", Debug1("socket 1 (fd %d) is at EOF, ignoring",
XIO_RDSTREAM(sock1)->fd); /*! */ XIO_RDSTREAM(sock1)->fd); /*! */
mayrd1 = true; mayrd1 = true;
@ -996,16 +1005,20 @@ int _socat(void) {
} else { } else {
Notice1("socket 1 (fd %d) is at EOF", XIO_GETRDFD(sock1)); Notice1("socket 1 (fd %d) is at EOF", XIO_GETRDFD(sock1));
xioshutdown(sock2, SHUT_WR); xioshutdown(sock2, SHUT_WR);
XIO_RDSTREAM(sock1)->eof = 2;
XIO_RDSTREAM(sock1)->ignoreeof = false;
if (socat_opts.lefttoright) { if (socat_opts.lefttoright) {
break; break;
} }
closing = 1;
} }
} else if (polling && XIO_RDSTREAM(sock1)->ignoreeof) { } else if (polling && XIO_RDSTREAM(sock1)->ignoreeof) {
polling = 0; polling = 0;
} }
if (bytes2 == 0 || XIO_RDSTREAM(sock2)->eof >= 2) { if (bytes2 == 0 || XIO_RDSTREAM(sock2)->eof >= 2) {
if (XIO_RDSTREAM(sock2)->ignoreeof && !closing) { if (XIO_RDSTREAM(sock2)->ignoreeof &&
!XIO_RDSTREAM(sock2)->actescape && !closing) {
Debug1("socket 2 (fd %d) is at EOF, ignoring", Debug1("socket 2 (fd %d) is at EOF, ignoring",
XIO_RDSTREAM(sock2)->fd); XIO_RDSTREAM(sock2)->fd);
mayrd2 = true; mayrd2 = true;
@ -1013,9 +1026,12 @@ int _socat(void) {
} else { } else {
Notice1("socket 2 (fd %d) is at EOF", XIO_GETRDFD(sock2)); Notice1("socket 2 (fd %d) is at EOF", XIO_GETRDFD(sock2));
xioshutdown(sock1, SHUT_WR); xioshutdown(sock1, SHUT_WR);
XIO_RDSTREAM(sock2)->eof = 2;
XIO_RDSTREAM(sock2)->ignoreeof = false;
if (socat_opts.righttoleft) { if (socat_opts.righttoleft) {
break; break;
} }
closing = 1;
} }
} else if (polling && XIO_RDSTREAM(sock2)->ignoreeof) { } else if (polling && XIO_RDSTREAM(sock2)->ignoreeof) {
polling = 0; polling = 0;
@ -1102,8 +1118,9 @@ static int
/* inpipe is suspected to have read data available; read at most bufsiz bytes /* inpipe is suspected to have read data available; read at most bufsiz bytes
and transfer them to outpipe. Perform required data conversions. and transfer them to outpipe. Perform required data conversions.
buff should be at least twice as large as bufsiz, to allow all standard buff must be a malloc()'ed storage and might be realloc()'ed in this
conversions. Returns the number of bytes written, or 0 on EOF or <0 if an function if more space is required after conversions.
Returns the number of bytes written, or 0 on EOF or <0 if an
error occurred or when data was read but none written due to conversions error occurred or when data was read but none written due to conversions
(with EAGAIN). EAGAIN also occurs when reading from a nonblocking FD where (with EAGAIN). EAGAIN also occurs when reading from a nonblocking FD where
the file has a mandatory lock. the file has a mandatory lock.
@ -1113,9 +1130,9 @@ static int
/* inpipe, outpipe must be single descriptors (not dual!) */ /* inpipe, outpipe must be single descriptors (not dual!) */
int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe, int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe,
unsigned char **buff, size_t bufsiz, bool righttoleft) { unsigned char **buff, size_t bufsiz, bool righttoleft) {
ssize_t bytes, writt; ssize_t bytes, writt = 0;
bytes = xioread(inpipe, *buff, socat_opts.bufsiz); bytes = xioread(inpipe, *buff, bufsiz);
if (bytes < 0) { if (bytes < 0) {
if (errno != EAGAIN) if (errno != EAGAIN)
XIO_RDSTREAM(inpipe)->eof = 2; XIO_RDSTREAM(inpipe)->eof = 2;
@ -1123,14 +1140,35 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe,
return -1; return -1;
} }
if (bytes == 0 && XIO_RDSTREAM(inpipe)->ignoreeof && !closing) { if (bytes == 0 && XIO_RDSTREAM(inpipe)->ignoreeof && !closing) {
writt = 0; ;
} else if (bytes == 0) { } else if (bytes == 0) {
XIO_RDSTREAM(inpipe)->eof = 2; XIO_RDSTREAM(inpipe)->eof = 2;
closing = MAX(closing, 1); closing = MAX(closing, 1);
writt = 0;
} }
else /* if (bytes > 0)*/ { if (bytes > 0) {
/* handle escape char */
if (XIO_RDSTREAM(inpipe)->escape != -1) {
/* check input data for escape char */
unsigned char *ptr = *buff;
size_t ctr = 0;
while (ctr < bytes) {
if (*ptr == XIO_RDSTREAM(inpipe)->escape) {
/* found: set flag, truncate input data */
XIO_RDSTREAM(inpipe)->actescape = true;
bytes = ctr;
Info("escape char found in input");
break;
}
++ptr; ++ctr;
}
if (ctr != bytes) {
XIO_RDSTREAM(inpipe)->eof = 2;
}
}
}
if (bytes > 0) {
if (XIO_RDSTREAM(inpipe)->lineterm != if (XIO_RDSTREAM(inpipe)->lineterm !=
XIO_WRSTREAM(outpipe)->lineterm) { XIO_WRSTREAM(outpipe)->lineterm) {
@ -1248,8 +1286,14 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe,
#define LF '\n' #define LF '\n'
int cv_newline(unsigned char **buff, ssize_t *bytes, /* converts the newline characters (or character sequences) from the one
specified in lineterm1 to that of lineterm2. Possible values are
LINETERM_CR, LINETERM_CRNL, LINETERM_RAW.
buff points to the malloc()'ed data, input and output. It may be subject to
realloc(). bytes specifies the number of bytes input and output */
int cv_newline(unsigned char **buff, ssize_t *bufsiz,
int lineterm1, int lineterm2) { int lineterm1, int lineterm2) {
ssize_t *bytes = bufsiz;
/* must perform newline changes */ /* must perform newline changes */
if (lineterm1 <= LINETERM_CR && lineterm2 <= LINETERM_CR) { if (lineterm1 <= LINETERM_CR && lineterm2 <= LINETERM_CR) {
/* no change in data length */ /* no change in data length */
@ -1287,7 +1331,7 @@ int cv_newline(unsigned char **buff, ssize_t *bytes,
*t++ = *s++; *t++ = *s++;
} }
} }
*bytes = t - *buff; *bufsiz = t - *buff;
} else { } else {
/* buffer becomes longer, must alloc another space */ /* buffer becomes longer, must alloc another space */
unsigned char *buf2; unsigned char *buf2;
@ -1297,7 +1341,7 @@ int cv_newline(unsigned char **buff, ssize_t *bytes,
} else { } else {
from = '\r'; from = '\r';
} }
if ((buf2 = Malloc(2*socat_opts.bufsiz+1)) == NULL) { if ((buf2 = Malloc(2*socat_opts.bufsiz/*sic!*/+1)) == NULL) {
return -1; return -1;
} }
s = *buff; t = buf2; z = *buff + *bytes; s = *buff; t = buf2; z = *buff + *bytes;
@ -1312,7 +1356,7 @@ int cv_newline(unsigned char **buff, ssize_t *bytes,
} }
free(*buff); free(*buff);
*buff = buf2; *buff = buf2;
*bytes = t - buf2;; *bufsiz = t - buf2;;
} }
return 0; return 0;
} }

63
test.sh
View File

@ -8450,6 +8450,69 @@ esac
N=$((N+1)) N=$((N+1))
# test the escape option
NAME=ESCAPE
case "$TESTS" in
*%functions%*|*%escape%*|*%$NAME%*)
TEST="$NAME: escape character triggers EOF"
# idea: start socat just echoing input, but apply escape option. send a string
# containing the escape character and check if the output is truncated
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
CMD="$SOCAT $opts -,escape=27 pipe"
printf "test $F_n $TEST... " $N
$ECHO "$da\n\x1bXYZ" |$CMD >"$tf" 2>"$te"
if [ $? -ne 0 ]; then
$PRINTF "$FAILED: $SOCAT:\n"
echo "$CMD"
cat "$te"
numFAIL=$((numFAIL+1))
elif ! echo "$da" |diff - "$tf" >"$tdiff"; then
$PRINTF "$FAILED: diff:\n"
cat "$tdiff"
numFAIL=$((numFAIL+1))
else
$PRINTF "$OK\n"
if [ -n "$debug" ]; then cat $te; fi
numOK=$((numOK+1))
fi
esac
N=$((N+1))
# test the escape option combined with ignoreeof
NAME=ESCAPE_IGNOREEOF
case "$TESTS" in
*%functions%*|*%ignoreeof%*|*%escape%*|*%$NAME%*)
TEST="$NAME: escape character triggers EOF"
# idea: start socat just echoing input, but apply escape option. send a string
# containing the escape character and check if the output is truncated
ti="$td/test$N.file"
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
CMD="$SOCAT -T 5 $opts file:$ti,ignoreeof,escape=27!!- pipe"
printf "test $F_n $TEST... " $N
>"$ti"
$CMD >"$tf" 2>"$te" &
$ECHO "$da\n\x1bXYZ" >>"$ti"
sleep 1
if ! echo "$da" |diff - "$tf" >"$tdiff"; then
$PRINTF "$FAILED: diff:\n"
cat "$tdiff"
cat "$te"
numFAIL=$((numFAIL+1))
else
$PRINTF "$OK\n"
if [ -n "$debug" ]; then cat $te; fi
numOK=$((numOK+1))
fi
esac
N=$((N+1))
echo "summary: $((N-1)) tests; $numOK ok, $numFAIL failed, $numCANT could not be performed" echo "summary: $((N-1)) tests; $numOK ok, $numFAIL failed, $numCANT could not be performed"
if [ "$numFAIL" -gt 0 ]; then if [ "$numFAIL" -gt 0 ]; then

View File

@ -1,5 +1,5 @@
/* source: xio-tun.c */ /* source: xio-tun.c */
/* Copyright Gerhard Rieger 2007 */ /* Copyright Gerhard Rieger 2007-2008 */
/* Published under the GNU General Public License V.2, see file COPYING */ /* Published under the GNU General Public License V.2, see file COPYING */
/* this file contains the source for opening addresses of tun/tap type */ /* this file contains the source for opening addresses of tun/tap type */
@ -17,8 +17,6 @@
static int xioopen_tun(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, unsigned groups, int dummy1, int dummy2, int dummy3); static int xioopen_tun(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, unsigned groups, int dummy1, int dummy2, int dummy3);
#define XIO_OFFSETOF(x) ((size_t)&((xiosingle_t *)0)->x)
/****** TUN addresses ******/ /****** TUN addresses ******/
const struct optdesc opt_tun_device = { "tun-device", NULL, OPT_TUN_DEVICE, GROUP_TUN, PH_OPEN, TYPE_FILENAME, OFUNC_SPEC }; const struct optdesc opt_tun_device = { "tun-device", NULL, OPT_TUN_DEVICE, GROUP_TUN, PH_OPEN, TYPE_FILENAME, OFUNC_SPEC };
const struct optdesc opt_tun_name = { "tun-name", NULL, OPT_TUN_NAME, GROUP_INTERFACE, PH_FD, TYPE_STRING, OFUNC_SPEC }; const struct optdesc opt_tun_name = { "tun-name", NULL, OPT_TUN_NAME, GROUP_INTERFACE, PH_FD, TYPE_STRING, OFUNC_SPEC };

2
xio.h
View File

@ -167,6 +167,8 @@ typedef struct single {
const char *name; /* only with END_UNLINK */ const char *name; /* only with END_UNLINK */
int (*sigchild)(struct single *); /* callback after sigchild */ int (*sigchild)(struct single *); /* callback after sigchild */
pid_t ppid; /* parent pid, only if we send it signals */ pid_t ppid; /* parent pid, only if we send it signals */
int escape; /* escape character; -1 for no escape */
bool actescape; /* escape character found in input data */
union { union {
struct { struct {
int fdout; /* use fd for output */ int fdout; /* use fd for output */

View File

@ -16,6 +16,7 @@ const struct optdesc opt_crnl = { "crnl", NULL, OPT_CRNL, GROUP_A
const struct optdesc opt_readbytes = { "readbytes", "bytes", OPT_READBYTES, GROUP_APPL, PH_LATE, TYPE_SIZE_T, OFUNC_EXT, (int)(&((struct single *)0)->readbytes), sizeof(((struct single *)0)->readbytes) }; const struct optdesc opt_readbytes = { "readbytes", "bytes", OPT_READBYTES, GROUP_APPL, PH_LATE, TYPE_SIZE_T, OFUNC_EXT, (int)(&((struct single *)0)->readbytes), sizeof(((struct single *)0)->readbytes) };
const struct optdesc opt_lockfile = { "lockfile", NULL, OPT_LOCKFILE, GROUP_APPL, PH_INIT, TYPE_FILENAME, OFUNC_EXT, 0, 0 }; const struct optdesc opt_lockfile = { "lockfile", NULL, OPT_LOCKFILE, GROUP_APPL, PH_INIT, TYPE_FILENAME, OFUNC_EXT, 0, 0 };
const struct optdesc opt_waitlock = { "waitlock", NULL, OPT_WAITLOCK, GROUP_APPL, PH_INIT, TYPE_FILENAME, OFUNC_EXT, 0, 0 }; const struct optdesc opt_waitlock = { "waitlock", NULL, OPT_WAITLOCK, GROUP_APPL, PH_INIT, TYPE_FILENAME, OFUNC_EXT, 0, 0 };
const struct optdesc opt_escape = { "escape", NULL, OPT_ESCAPE, GROUP_APPL, PH_INIT, TYPE_INT, OFUNC_OFFSET, XIO_OFFSETOF(escape), sizeof(((xiosingle_t *)0)->escape) };
/****** APPL addresses ******/ /****** APPL addresses ******/
#if WITH_RETRY #if WITH_RETRY
const struct optdesc opt_forever = { "forever", NULL, OPT_FOREVER, GROUP_RETRY, PH_INIT, TYPE_BOOL, OFUNC_EXT, (int)(&((struct single *)0)->forever), sizeof(((struct single *)0)->forever) }; const struct optdesc opt_forever = { "forever", NULL, OPT_FOREVER, GROUP_RETRY, PH_INIT, TYPE_BOOL, OFUNC_EXT, (int)(&((struct single *)0)->forever), sizeof(((struct single *)0)->forever) };

View File

@ -1,5 +1,5 @@
/* source: xiolayer.h */ /* source: xiolayer.h */
/* Copyright Gerhard Rieger 2001-2005 */ /* Copyright Gerhard Rieger 2001-2008 */
/* Published under the GNU General Public License V.2, see file COPYING */ /* Published under the GNU General Public License V.2, see file COPYING */
#ifndef __xiolayer_h_included #ifndef __xiolayer_h_included
@ -11,6 +11,7 @@ extern const struct optdesc opt_crnl;
extern const struct optdesc opt_readbytes; extern const struct optdesc opt_readbytes;
extern const struct optdesc opt_lockfile; extern const struct optdesc opt_lockfile;
extern const struct optdesc opt_waitlock; extern const struct optdesc opt_waitlock;
extern const struct optdesc opt_escape;
extern const struct optdesc opt_forever; extern const struct optdesc opt_forever;
extern const struct optdesc opt_intervall; extern const struct optdesc opt_intervall;
extern const struct optdesc opt_retry; extern const struct optdesc opt_retry;

View File

@ -1,5 +1,5 @@
/* source: xioopen.c */ /* source: xioopen.c */
/* Copyright Gerhard Rieger 2001-2007 */ /* Copyright Gerhard Rieger 2001-2008 */
/* Published under the GNU General Public License V.2, see file COPYING */ /* Published under the GNU General Public License V.2, see file COPYING */
/* this is the source file of the extended open function */ /* this is the source file of the extended open function */
@ -301,6 +301,7 @@ static xiofile_t *xioallocfd(void) {
#endif /* WITH_SOCKET */ #endif /* WITH_SOCKET */
fd->stream.howtoend = END_UNSPEC; fd->stream.howtoend = END_UNSPEC;
/* fd->stream.name = NULL; */ /* fd->stream.name = NULL; */
fd->stream.escape = -1;
/* fd->stream.para.exec.pid = 0; */ /* fd->stream.para.exec.pid = 0; */
fd->stream.lineterm = LINETERM_RAW; fd->stream.lineterm = LINETERM_RAW;

View File

@ -397,6 +397,7 @@ const struct optname optionnames[] = {
IF_TERMIOS("eol2", &opt_veol2) IF_TERMIOS("eol2", &opt_veol2)
IF_TERMIOS("erase", &opt_verase) IF_TERMIOS("erase", &opt_verase)
IF_SOCKET ("error", &opt_so_error) IF_SOCKET ("error", &opt_so_error)
IF_ANY ("escape", &opt_escape)
IF_OPEN ("excl", &opt_o_excl) IF_OPEN ("excl", &opt_o_excl)
#if WITH_EXT2 && defined(EXT2_APPEND_FL) #if WITH_EXT2 && defined(EXT2_APPEND_FL)
IF_ANY ("ext2-append", &opt_ext2_append) IF_ANY ("ext2-append", &opt_ext2_append)
@ -3339,6 +3340,8 @@ static int applyopt_offset(struct single *xfd, struct opt *opt) {
switch (opt->desc->type) { switch (opt->desc->type) {
case TYPE_BOOL: case TYPE_BOOL:
*(bool *)ptr = opt->value.u_bool; break; *(bool *)ptr = opt->value.u_bool; break;
case TYPE_INT:
*(int *)ptr = opt->value.u_int; break;
case TYPE_DOUBLE: case TYPE_DOUBLE:
*(double *)ptr = opt->value.u_double; break; *(double *)ptr = opt->value.u_double; break;
case TYPE_TIMEVAL: case TYPE_TIMEVAL:

View File

@ -9,6 +9,8 @@
#define ODESC_DONE ((void *)-1) /* indicates that option has been applied */ #define ODESC_DONE ((void *)-1) /* indicates that option has been applied */
#define ODESC_ERROR ODESC_DONE /* maybe later */ #define ODESC_ERROR ODESC_DONE /* maybe later */
#define XIO_OFFSETOF(x) ((size_t)&((xiosingle_t *)0)->x)
/* atomic structure for use in the option search table; keep compatible with /* atomic structure for use in the option search table; keep compatible with
struct wordent! */ struct wordent! */
struct optname { struct optname {
@ -250,6 +252,7 @@ enum e_optcode {
OPT_ECHOPRT, /* termios.c_lflag */ OPT_ECHOPRT, /* termios.c_lflag */
#endif #endif
OPT_END_CLOSE, /* xfd.stream.howtoend = END_CLOSE */ OPT_END_CLOSE, /* xfd.stream.howtoend = END_CLOSE */
OPT_ESCAPE,
OPT_EXT2_SECRM, OPT_EXT2_SECRM,
OPT_EXT2_UNRM, OPT_EXT2_UNRM,
OPT_EXT2_COMPR, OPT_EXT2_COMPR,