+
+static unsigned char str_lm_mode[] = {
+ IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE
+};
+
+ void
+lm_mode(cmd, len, init)
+ unsigned char *cmd;
+ int len, init;
+{
+ if (len != 1)
+ return;
+ if ((linemode&MODE_MASK&~MODE_ACK) == *cmd)
+ return;
+ if (*cmd&MODE_ACK)
+ return;
+ linemode = *cmd&(MODE_MASK&~MODE_ACK);
+ str_lm_mode[4] = linemode;
+ if (!init)
+ str_lm_mode[4] |= MODE_ACK;
+ if (NETROOM() > sizeof(str_lm_mode)) {
+ ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode));
+ printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2);
+ }
+/*@*/ else printf("lm_mode: not enough room in buffer\n");
+ setconnmode(0); /* set changed mode */
+}
+
+\f
+
+/*
+ * slc()
+ * Handle special character suboption of LINEMODE.
+ */
+
+struct spc {
+ cc_t val;
+ cc_t *valp;
+ char flags; /* Current flags & level */
+ char mylevel; /* Maximum level & flags */
+} spc_data[NSLC+1];
+
+#define SLC_IMPORT 0
+#define SLC_EXPORT 1
+#define SLC_RVALUE 2
+static int slc_mode = SLC_EXPORT;
+
+ void
+slc_init()
+{
+ register struct spc *spcp;
+
+ localchars = 1;
+ for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) {
+ spcp->val = 0;
+ spcp->valp = 0;
+ spcp->flags = spcp->mylevel = SLC_NOSUPPORT;
+ }
+
+#define initfunc(func, flags) { \
+ spcp = &spc_data[func]; \
+ if (spcp->valp = tcval(func)) { \
+ spcp->val = *spcp->valp; \
+ spcp->mylevel = SLC_VARIABLE|flags; \
+ } else { \
+ spcp->val = 0; \
+ spcp->mylevel = SLC_DEFAULT; \
+ } \
+ }
+
+ initfunc(SLC_SYNCH, 0);
+ /* No BRK */
+ initfunc(SLC_AO, 0);
+ initfunc(SLC_AYT, 0);
+ /* No EOR */
+ initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT);
+ initfunc(SLC_EOF, 0);
+#ifndef SYSV_TERMIO
+ initfunc(SLC_SUSP, SLC_FLUSHIN);
+#endif
+ initfunc(SLC_EC, 0);
+ initfunc(SLC_EL, 0);
+#ifndef SYSV_TERMIO
+ initfunc(SLC_EW, 0);
+ initfunc(SLC_RP, 0);
+ initfunc(SLC_LNEXT, 0);
+#endif
+ initfunc(SLC_XON, 0);
+ initfunc(SLC_XOFF, 0);
+#ifdef SYSV_TERMIO
+ spc_data[SLC_XON].mylevel = SLC_CANTCHANGE;
+ spc_data[SLC_XOFF].mylevel = SLC_CANTCHANGE;
+#endif
+ initfunc(SLC_FORW1, 0);
+#ifdef USE_TERMIO
+ initfunc(SLC_FORW2, 0);
+ /* No FORW2 */
+#endif
+
+ initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT);
+#undef initfunc
+
+ if (slc_mode == SLC_EXPORT)
+ slc_export();
+ else
+ slc_import(1);
+
+}
+
+ void
+slcstate()
+{
+ printf("Special characters are %s values\n",
+ slc_mode == SLC_IMPORT ? "remote default" :
+ slc_mode == SLC_EXPORT ? "local" :
+ "remote");
+}
+
+ void
+slc_mode_export()
+{
+ slc_mode = SLC_EXPORT;
+ if (my_state_is_will(TELOPT_LINEMODE))
+ slc_export();
+}
+
+ void
+slc_mode_import(def)
+ int def;
+{
+ slc_mode = def ? SLC_IMPORT : SLC_RVALUE;
+ if (my_state_is_will(TELOPT_LINEMODE))
+ slc_import(def);
+}
+
+unsigned char slc_import_val[] = {
+ IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE
+};
+unsigned char slc_import_def[] = {
+ IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE
+};
+
+ void
+slc_import(def)
+ int def;
+{
+ if (NETROOM() > sizeof(slc_import_val)) {
+ if (def) {
+ ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def));
+ printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2);
+ } else {
+ ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val));
+ printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2);
+ }
+ }
+/*@*/ else printf("slc_import: not enough room\n");
+}
+
+ void
+slc_export()
+{
+ register struct spc *spcp;
+
+ TerminalDefaultChars();
+
+ slc_start_reply();
+ for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
+ if (spcp->mylevel != SLC_NOSUPPORT) {
+ if (spcp->val == (cc_t)(_POSIX_VDISABLE))
+ spcp->flags = SLC_NOSUPPORT;
+ else
+ spcp->flags = spcp->mylevel;
+ if (spcp->valp)
+ spcp->val = *spcp->valp;
+ slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
+ }
+ }
+ slc_end_reply();
+ (void)slc_update();
+ setconnmode(1); /* Make sure the character values are set */
+}
+
+ void
+slc(cp, len)
+ register unsigned char *cp;
+ int len;
+{
+ register struct spc *spcp;
+ register int func,level;
+
+ slc_start_reply();
+
+ for (; len >= 3; len -=3, cp +=3) {
+
+ func = cp[SLC_FUNC];
+
+ if (func == 0) {
+ /*
+ * Client side: always ignore 0 function.
+ */
+ continue;
+ }
+ if (func > NSLC) {
+ if ((cp[SLC_FLAGS] & SLC_LEVELBITS) != SLC_NOSUPPORT)
+ slc_add_reply(func, SLC_NOSUPPORT, 0);
+ continue;
+ }
+
+ spcp = &spc_data[func];
+
+ level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK);
+
+ if ((cp[SLC_VALUE] == (unsigned char)spcp->val) &&
+ ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) {
+ continue;
+ }
+
+ if (level == (SLC_DEFAULT|SLC_ACK)) {
+ /*
+ * This is an error condition, the SLC_ACK
+ * bit should never be set for the SLC_DEFAULT
+ * level. Our best guess to recover is to
+ * ignore the SLC_ACK bit.
+ */
+ cp[SLC_FLAGS] &= ~SLC_ACK;
+ }
+
+ if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) {
+ spcp->val = (cc_t)cp[SLC_VALUE];
+ spcp->flags = cp[SLC_FLAGS]; /* include SLC_ACK */
+ continue;
+ }
+
+ level &= ~SLC_ACK;
+
+ if (level <= (spcp->mylevel&SLC_LEVELBITS)) {
+ spcp->flags = cp[SLC_FLAGS]|SLC_ACK;
+ spcp->val = (cc_t)cp[SLC_VALUE];
+ }
+ if (level == SLC_DEFAULT) {
+ if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT)
+ spcp->flags = spcp->mylevel;
+ else
+ spcp->flags = SLC_NOSUPPORT;
+ }
+ slc_add_reply(func, spcp->flags, spcp->val);
+ }
+ slc_end_reply();
+ if (slc_update())
+ setconnmode(1); /* set the new character values */
+}
+
+ void
+slc_check()
+{
+ register struct spc *spcp;
+
+ slc_start_reply();
+ for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
+ if (spcp->valp && spcp->val != *spcp->valp) {
+ spcp->val = *spcp->valp;
+ if (spcp->val == (cc_t)(_POSIX_VDISABLE))
+ spcp->flags = SLC_NOSUPPORT;
+ else
+ spcp->flags = spcp->mylevel;
+ slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
+ }
+ }
+ slc_end_reply();
+ setconnmode(1);
+}
+
+
+unsigned char slc_reply[128];
+unsigned char *slc_replyp;
+
+ void
+slc_start_reply()
+{
+ slc_replyp = slc_reply;
+ *slc_replyp++ = IAC;
+ *slc_replyp++ = SB;
+ *slc_replyp++ = TELOPT_LINEMODE;
+ *slc_replyp++ = LM_SLC;
+}
+
+ void
+slc_add_reply(func, flags, value)
+ unsigned char func;
+ unsigned char flags;
+ cc_t value;
+{
+ if ((*slc_replyp++ = func) == IAC)
+ *slc_replyp++ = IAC;
+ if ((*slc_replyp++ = flags) == IAC)
+ *slc_replyp++ = IAC;
+ if ((*slc_replyp++ = (unsigned char)value) == IAC)
+ *slc_replyp++ = IAC;
+}
+
+ void
+slc_end_reply()
+{
+ register int len;
+
+ *slc_replyp++ = IAC;
+ *slc_replyp++ = SE;
+ len = slc_replyp - slc_reply;
+ if (len <= 6)
+ return;
+ if (NETROOM() > len) {
+ ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply);
+ printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2);
+ }
+/*@*/else printf("slc_end_reply: not enough room\n");
+}
+
+ int
+slc_update()
+{
+ register struct spc *spcp;
+ int need_update = 0;
+
+ for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
+ if (!(spcp->flags&SLC_ACK))
+ continue;
+ spcp->flags &= ~SLC_ACK;
+ if (spcp->valp && (*spcp->valp != spcp->val)) {
+ *spcp->valp = spcp->val;
+ need_update = 1;
+ }
+ }
+ return(need_update);
+}
+
+ void
+env_opt(buf, len)
+ register unsigned char *buf;
+ register int len;
+{
+ register unsigned char *ep = 0, *epc = 0;
+ register int i;
+
+ switch(buf[0]&0xff) {
+ case TELQUAL_SEND:
+ env_opt_start();
+ if (len == 1) {
+ env_opt_add(NULL);
+ } else for (i = 1; i < len; i++) {
+ switch (buf[i]&0xff) {
+ case ENV_VALUE:
+ if (ep) {
+ *epc = 0;
+ env_opt_add(ep);
+ }
+ ep = epc = &buf[i+1];
+ break;
+ case ENV_ESC:
+ i++;
+ /*FALL THROUGH*/
+ default:
+ if (epc)
+ *epc++ = buf[i];
+ break;
+ }
+ if (ep) {
+ *epc = 0;
+ env_opt_add(ep);
+ }
+ }
+ env_opt_end(1);
+ break;
+
+ case TELQUAL_IS:
+ case TELQUAL_INFO:
+ /* Ignore for now. We shouldn't get it anyway. */
+ break;
+
+ default:
+ break;
+ }
+}
+
+#define OPT_REPLY_SIZE 256
+unsigned char *opt_reply;
+unsigned char *opt_replyp;
+unsigned char *opt_replyend;
+
+ void
+env_opt_start()
+{
+ if (opt_reply)
+ opt_reply = (unsigned char *)realloc(opt_reply, OPT_REPLY_SIZE);
+ else
+ opt_reply = (unsigned char *)malloc(OPT_REPLY_SIZE);
+ if (opt_reply == NULL) {
+/*@*/ printf("env_opt_start: malloc()/realloc() failed!!!\n");
+ opt_reply = opt_replyp = opt_replyend = NULL;
+ return;
+ }
+ opt_replyp = opt_reply;
+ opt_replyend = opt_reply + OPT_REPLY_SIZE;
+ *opt_replyp++ = IAC;
+ *opt_replyp++ = SB;
+ *opt_replyp++ = TELOPT_ENVIRON;
+ *opt_replyp++ = TELQUAL_IS;
+}
+
+ void
+env_opt_start_info()
+{
+ env_opt_start();
+ if (opt_replyp)
+ opt_replyp[-1] = TELQUAL_INFO;
+}
+
+ void
+env_opt_add(ep)
+ register unsigned char *ep;
+{
+ register unsigned char *vp, c;
+
+ if (opt_reply == NULL) /*XXX*/
+ return; /*XXX*/
+
+ if (ep == NULL || *ep == '\0') {
+ env_default(1);
+ while (ep = env_default(0))
+ env_opt_add(ep);
+ return;
+ }
+ vp = env_getvalue(ep);
+ if (opt_replyp + (vp ? strlen((char *)vp) : 0) +
+ strlen((char *)ep) + 6 > opt_replyend)
+ {
+ register int len;
+ opt_replyend += OPT_REPLY_SIZE;
+ len = opt_replyend - opt_reply;
+ opt_reply = (unsigned char *)realloc(opt_reply, len);
+ if (opt_reply == NULL) {
+/*@*/ printf("env_opt_add: realloc() failed!!!\n");
+ opt_reply = opt_replyp = opt_replyend = NULL;
+ return;
+ }
+ opt_replyp = opt_reply + len - (opt_replyend - opt_replyp);
+ opt_replyend = opt_reply + len;
+ }
+ *opt_replyp++ = ENV_VAR;
+ for (;;) {
+ while (c = *ep++) {
+ switch(c&0xff) {
+ case IAC:
+ *opt_replyp++ = IAC;
+ break;
+ case ENV_VALUE:
+ case ENV_VAR:
+ case ENV_ESC:
+ *opt_replyp++ = ENV_ESC;
+ break;
+ }
+ *opt_replyp++ = c;
+ }
+ if (ep = vp) {
+ *opt_replyp++ = ENV_VALUE;
+ vp = NULL;
+ } else
+ break;
+ }
+}
+
+ void
+env_opt_end(emptyok)
+ register int emptyok;
+{
+ register int len;
+
+ len = opt_replyp - opt_reply + 2;
+ if (emptyok || len > 6) {
+ *opt_replyp++ = IAC;
+ *opt_replyp++ = SE;
+ if (NETROOM() > len) {
+ ring_supply_data(&netoring, opt_reply, len);
+ printsub('>', &opt_reply[2], len - 2);
+ }
+/*@*/ else printf("slc_end_reply: not enough room\n");
+ }
+ if (opt_reply) {
+ free(opt_reply);
+ opt_reply = opt_replyp = opt_replyend = NULL;
+ }
+}
+