root/src/simh/sim_tmxr.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tmxr_init_line
  2. tmxr_report_connection
  3. tmxr_report_disconnection
  4. loop_write_ex
  5. loop_write
  6. loop_read_ex
  7. loop_read
  8. tmxr_read
  9. tmxr_write
  10. tmxr_rmvrc
  11. tmxr_find_ldsc
  12. tmxr_get_ldsc
  13. growstring
  14. tmxr_mux_attach_string
  15. tmxr_line_attach_string
  16. tmxr_poll_conn
  17. tmxr_reset_ln_ex
  18. tmxr_close_ln
  19. tmxr_reset_ln
  20. tmxr_set_get_modem_bits
  21. tmxr_set_line_loopback
  22. tmxr_get_line_loopback
  23. tmxr_set_line_halfduplex
  24. tmxr_get_line_halfduplex
  25. tmxr_input_pending_ln
  26. tmxr_getc_ln
  27. tmxr_get_packet_ln
  28. tmxr_get_packet_ln_ex
  29. tmxr_poll_rx
  30. tmxr_rqln_bare
  31. tmxr_rqln
  32. tmxr_putc_ln
  33. tmxr_put_packet_ln
  34. tmxr_put_packet_ln_ex
  35. tmxr_poll_tx
  36. tmxr_send_buffered_data
  37. tmxr_tqln
  38. tmxr_tpqln
  39. tmxr_tpbusyln
  40. _mux_detach_line
  41. tmxr_detach_ln
  42. _tmln_speed_delta
  43. tmxr_set_line_speed
  44. tmxr_open_master
  45. tmxr_set_line_unit
  46. tmxr_set_line_output_unit
  47. tmxr_start_poll
  48. tmxr_stop_poll
  49. tmxr_add_to_open_list
  50. _tmxr_remove_from_open_list
  51. _tmxr_locate_line_send_expect
  52. tmxr_locate_line_send
  53. tmxr_locate_line_expect
  54. tmxr_change_async
  55. tmxr_attach_ex
  56. tmxr_startup
  57. tmxr_shutdown
  58. tmxr_show_open_devices
  59. tmxr_close_master
  60. tmxr_detach
  61. tmxr_activate
  62. tmxr_activate_after
  63. tmxr_attach_help
  64. tmxr_ex
  65. tmxr_dep
  66. tmxr_msg
  67. tmxr_linemsg
  68. tmxr_linemsgf
  69. tmxr_linemsgvf
  70. tmxr_fconns
  71. tmxr_fstats
  72. tmxr_dscln
  73. tmxr_set_log
  74. tmxr_set_nolog
  75. tmxr_show_log
  76. tmxr_set_lnorder
  77. tmxr_show_lnorder
  78. tmxr_show_summ
  79. tmxr_show_cstat
  80. tmxr_show_lines

   1 /*
   2  * sim_tmxr.c: Telnet terminal multiplexer library
   3  *
   4  * vim: filetype=c:tabstop=4:ai:expandtab
   5  * SPDX-License-Identifier: X11
   6  * scspell-id: e76cc98d-f62a-11ec-967b-80ee73e9b8e7
   7  *
   8  * ---------------------------------------------------------------------------
   9  *
  10  * Copyright (c) 2001-2011 Robert M. Supnik
  11  * Copyright (c) 2021-2022 The DPS8M Development Team
  12  *
  13  * Permission is hereby granted, free of charge, to any person obtaining a
  14  * copy of this software and associated documentation files (the "Software"),
  15  * to deal in the Software without restriction, including without limitation
  16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  17  * and/or sell copies of the Software, and to permit persons to whom the
  18  * Software is furnished to do so, subject to the following conditions:
  19  *
  20  * The above copyright notice and this permission notice shall be included
  21  * in all copies or substantial portions of the Software.
  22  *
  23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  24  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  26  * IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR
  27  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  28  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  29  * OTHER DEALINGS IN THE SOFTWARE.
  30  *
  31  * Except as contained in this notice, the name of Robert M. Supnik shall
  32  * not be used in advertising or otherwise to promote the sale, use or
  33  * other dealings in this Software without prior written authorization from
  34  * Robert M. Supnik.
  35  *
  36  * ---------------------------------------------------------------------------
  37  */
  38 
  39 //-V::701
  40 
  41 /*
  42  * Based on the original DZ11 simulator by Thord Nilson, as updated by
  43  * Arthur Krewat.
  44  *
  45  * This library includes:
  46  *
  47  * tmxr_poll_conn -                     poll for connection
  48  * tmxr_reset_ln -                      reset line (drops connections)
  49  * tmxr_detach_ln -                     reset line and close per line listener and outgoing destination
  50  * tmxr_getc_ln -                       get character for line
  51  * tmxr_get_packet_ln -                 get packet from line
  52  * tmxr_get_packet_ln_ex -              get packet from line with separator byte
  53  * tmxr_poll_rx -                       poll receive
  54  * tmxr_putc_ln -                       put character for line
  55  * tmxr_put_packet_ln -                 put packet on line
  56  * tmxr_put_packet_ln_ex -              put packet on line with separator byte
  57  * tmxr_poll_tx -                       poll transmit
  58  * tmxr_send_buffered_data -            transmit buffered data
  59  * tmxr_set_get_modem_bits -            set and/or get a line modem bits
  60  * tmxr_set_line_loopback -             enable or disable loopback mode on a line
  61  * tmxr_get_line_loopback -             returns the current loopback status of a line
  62  * tmxr_set_line_halfduplex -           enable or disable halfduplex mode on a line
  63  * tmxr_get_line_halfduplex -           returns the current halfduplex status of a line
  64  * tmxr_open_master -                   open master connection
  65  * tmxr_close_master -                  close master connection
  66  * tmxr_attach  -                       attach terminal multiplexor to listening port
  67  * tmxr_detach  -                       detach terminal multiplexor to listening port
  68  * tmxr_attach_help  -                  help routine for attaching multiplexer devices
  69  * tmxr_set_line_unit -                 set the unit which polls for input for a given line
  70  * tmxr_ex      -                       (null) examine
  71  * tmxr_dep     -                       (null) deposit
  72  * tmxr_msg     -                       send message to socket
  73  * tmxr_linemsg -                       send message to line
  74  * tmxr_linemsgf -                      send formatted message to line
  75  * tmxr_fconns  -                       output connection status
  76  * tmxr_fstats  -                       output connection statistics
  77  * tmxr_set_log -                       enable logging for line
  78  * tmxr_set_nolog -                     disable logging for line
  79  * tmxr_show_log -                      show logging status for line
  80  * tmxr_dscln   -                       disconnect line (SET routine)
  81  * tmxr_rqln    -                       number of available characters for line
  82  * tmxr_tqln    -                       number of buffered characters for line
  83  * tmxr_tpqln    -                      number of buffered packet characters for line
  84  * tmxr_tpbusyln -                      transmit packet busy status for line
  85  * tmxr_set_lnorder -                   set line connection order
  86  * tmxr_show_lnorder -                  show line connection order
  87  * tmxr_show_summ -                     show connection summary
  88  * tmxr_show_cstat -                    show line connections or status
  89  * tmxr_show_lines -                    show number of lines
  90  * tmxr_show_open_devices -             show info about all open tmxr devices
  91  */
  92 
  93 #include "sim_defs.h"
  94 #include "sim_sock.h"
  95 #include "sim_timer.h"
  96 #include "sim_tmxr.h"
  97 #include "scp.h"
  98 
  99 #define DBG_CTR 0
 100 
 101 #include "../dps8/dps8.h"
 102 
 103 #include <ctype.h>
 104 #include <time.h>
 105 #include <sys/time.h>
 106 #include <signal.h>
 107 #include <unistd.h>
 108 #include <math.h>
 109 
 110 #ifdef TESTING
 111 # include "../dps8/dps8_cpu.h"
 112 # undef realloc
 113 # undef FREE
 114 # define FREE(p) free(p)
 115 # define realloc trealloc
 116 #endif /* ifdef TESTING */
 117 
 118 /* Telnet protocol constants - negatives are for init'ing signed char data */
 119 
 120 /* Commands */
 121 #define TN_IAC          0xFFu /* -1 */                  /* protocol delim */
 122 #define TN_DONT         0xFEu /* -2 */                  /* dont */
 123 #define TN_DO           0xFDu /* -3 */                  /* do */
 124 #define TN_WONT         0xFCu /* -4 */                  /* wont */
 125 #define TN_WILL         0xFBu /* -5 */                  /* will */
 126 #define TN_SB           0xFAu /* -6 */                  /* sub-option negotiation */
 127 #define TN_GA           0xF9u /* -7 */                  /* go ahead */
 128 #define TN_EL           0xF8u /* -8 */                  /* erase line */
 129 #define TN_EC           0xF7u /* -9 */                  /* erase character */
 130 #define TN_AYT          0xF6u /* -10 */                 /* are you there */
 131 #define TN_AO           0xF5u /* -11 */                 /* abort output */
 132 #define TN_IP           0xF4u /* -12 */                 /* interrupt process */
 133 #define TN_BRK          0xF3u /* -13 */                 /* break */
 134 #define TN_DATAMK       0xF2u /* -14 */                 /* data mark */
 135 #define TN_NOP          0xF1u /* -15 */                 /* no operation */
 136 #define TN_SE           0xF0u /* -16 */                 /* end sub-option negot */
 137 
 138 /* Options */
 139 
 140 #define TN_BIN            0                             /* bin */
 141 #define TN_ECHO           1                             /* echo */
 142 #define TN_SGA            3                             /* sga */
 143 #define TN_STATUS         5                             /* option status query */
 144 #define TN_TIMING         6                             /* Timing Mark */
 145 #define TN_NAOCRD        10                             /* Output Carriage-Return Disposition */
 146 #define TN_NAOHTS        11                             /* Output Horizontal Tab Stops */
 147 #define TN_NAOHTD        12                             /* Output Horizontal Tab Stop Disposition */
 148 #define TN_NAOFFD        13                             /* Output Forfeed Disposition */
 149 #define TN_NAOVTS        14                             /* Output Vertical Tab Stop */
 150 #define TN_NAOVTD        15                             /* Output Vertical Tab Stop Disposition */
 151 #define TN_NAOLFD        16                             /* Output Linefeed Disposition */
 152 #define TN_EXTEND        17                             /* Extended Ascii */
 153 #define TN_LOGOUT        18                             /* Logout */
 154 #define TN_BM            19                             /* Byte Macro */
 155 #define TN_DET           20                             /* Data Entry Terminal */
 156 #define TN_SENDLO        23                             /* Send Location */
 157 #define TN_TERMTY        24                             /* Terminal Type */
 158 #define TN_ENDREC        25                             /* Terminal Type */
 159 #define TN_TUID          26                             /* TACACS User Identification */
 160 #define TN_OUTMRK        27                             /* Output Marking */
 161 #define TN_TTYLOC        28                             /* Terminal Location Number */
 162 #define TN_3270          29                             /* 3270 Regime */
 163 #define TN_X3PAD         30                             /* X.3 PAD */
 164 #define TN_NAWS          31                             /* Negotiate About Window Size */
 165 #define TN_TERMSP        32                             /* Terminal Speed */
 166 #define TN_TOGFLO        33                             /* Remote Flow Control */
 167 #define TN_LINE          34                             /* line mode */
 168 #define TN_XDISPL        35                             /* X Display Location */
 169 #define TN_ENVIRO        36                             /* Environment */
 170 #define TN_AUTH          37                             /* Authentication */
 171 #define TN_ENCRYP        38                             /* Data Encryption */
 172 #define TN_NEWENV        39                             /* New Environment */
 173 #define TN_TN3270        40                             /* TN3270 Enhancements */
 174 #define TN_CHARST        42                             /* CHARSET */
 175 #define TN_COMPRT        44                             /* Com Port Control */
 176 #define TN_KERMIT        47                             /* KERMIT */
 177 
 178 #define TN_CR           015                             /* carriage return */
 179 #define TN_LF           012                             /* line feed */
 180 #define TN_NUL          000                             /* null */
 181 
 182 /* Telnet line states */
 183 
 184 #define TNS_NORM        000                             /* normal */
 185 #define TNS_IAC         001                             /* IAC seen */
 186 #define TNS_WILL        002                             /* WILL seen */
 187 #define TNS_WONT        003                             /* WONT seen */
 188 #define TNS_SKIP        004                             /* skip next cmd */
 189 #define TNS_CRPAD       005                             /* CR padding */
 190 #define TNS_DO          006                             /* DO request pending rejection */
 191 
 192 /* Telnet Option Sent Flags */
 193 
 194 #define TNOS_DONT       001                             /* Don't has been sent */
 195 #define TNOS_WONT       002                             /* Won't has been sent */
 196 
 197 static BITFIELD tmxr_modem_bits[] = {
 198   BIT(DTR),                                 /* Data Terminal Ready */
 199   BIT(RTS),                                 /* Request To Send     */
 200   BIT(DCD),                                 /* Data Carrier Detect */
 201   BIT(RNG),                                 /* Ring Indicator      */
 202   BIT(CTS),                                 /* Clear To Send       */
 203   BIT(DSR),                                 /* Data Set Ready      */
 204   ENDBITS
 205 };
 206 
 207 static u_char mantra[] = {                  /* Telnet Option Negotiation Mantra */
 208     TN_IAC, TN_WILL, TN_LINE,
 209     TN_IAC, TN_WILL, TN_SGA,
 210     TN_IAC, TN_WILL, TN_ECHO,
 211     TN_IAC, TN_WILL, TN_BIN,
 212     TN_IAC, TN_DO,   TN_BIN
 213     };
 214 
 215 #define TMXR_GUARD  ((sizeof(mantra))) /* buffer guard */
 216 
 217 /* Local routines */
 218 
 219 static void tmxr_add_to_open_list (TMXR* mux);
 220 
 221 /* Initialize the line state.
 222 
 223    Reset the line state to represent an idle line.  Note that we do not clear
 224    all of the line structure members, so a connected line remains connected
 225    after this call.
 226 
 227    Because a line break is represented by a flag in the "receive break status"
 228    array, we must zero that array in order to clear any pending break
 229    indications.
 230 */
 231 
 232 static void tmxr_init_line (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
 233 {
 234 lp->tsta           = 0;                                           /* init telnet state */
 235 lp->xmte           = 1;                                           /* enable transmit */
 236 lp->dstb           = 0;                                           /* default bin mode */
 237 lp->rxbpr          = lp->rxbpi = lp->rxcnt = lp->rxpcnt = 0;      /* init receive indexes */
 238 if (!lp->txbfd || lp->notelnet)                                   /* if not buffered telnet */
 239     lp->txbpr      = lp->txbpi = lp->txcnt = lp->txpcnt = 0;      /*   init transmit indexes */
 240 lp->txdrp          = 0;
 241 tmxr_set_get_modem_bits (lp, 0, 0, NULL);
 242 if ((!lp->mp->buffered) && (!lp->txbfd)) {
 243     lp->txbfd      = 0; //-V1048
 244     lp->txbsz      = TMXR_MAXBUF;
 245     lp->txb        = (char *)realloc (lp->txb, lp->txbsz);
 246     if (!lp->txb)
 247       {
 248         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 249                  __func__, __FILE__, __LINE__);
 250 #if defined(USE_BACKTRACE)
 251 # ifdef SIGUSR2
 252         (void)raise(SIGUSR2);
 253         /*NOTREACHED*/ /* unreachable */
 254 # endif /* ifdef SIGUSR2 */
 255 #endif /* if defined(USE_BACKTRACE) */
 256         abort();
 257       }
 258     lp->rxbsz      = TMXR_MAXBUF;
 259     lp->rxb        = (char *)realloc(lp->rxb, lp->rxbsz);
 260     if (!lp->rxb)
 261       {
 262         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 263                  __func__, __FILE__, __LINE__);
 264 #if defined(USE_BACKTRACE)
 265 # ifdef SIGUSR2
 266         (void)raise(SIGUSR2);
 267         /*NOTREACHED*/ /* unreachable */
 268 # endif /* ifdef SIGUSR2 */
 269 #endif /* if defined(USE_BACKTRACE) */
 270         abort();
 271       }
 272     lp->rbr        = (char *)realloc(lp->rbr, lp->rxbsz);
 273     if (!lp->rbr)
 274       {
 275         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 276                  __func__, __FILE__, __LINE__);
 277 #if defined(USE_BACKTRACE)
 278 # ifdef SIGUSR2
 279         (void)raise(SIGUSR2);
 280         /*NOTREACHED*/ /* unreachable */
 281 # endif /* ifdef SIGUSR2 */
 282 #endif /* if defined(USE_BACKTRACE) */
 283         abort();
 284       }
 285     }
 286 if (lp->loopback) {
 287     lp->lpbsz      = lp->rxbsz;
 288     lp->lpb        = (char *)realloc(lp->lpb, lp->lpbsz);
 289     if (!lp->lpb)
 290       {
 291         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 292                  __func__, __FILE__, __LINE__);
 293 #if defined(USE_BACKTRACE)
 294 # ifdef SIGUSR2
 295         (void)raise(SIGUSR2);
 296         /*NOTREACHED*/ /* unreachable */
 297 # endif /* ifdef SIGUSR2 */
 298 #endif /* if defined(USE_BACKTRACE) */
 299         abort();
 300       }
 301     lp->lpbcnt     = lp->lpbpi = lp->lpbpr = 0;
 302     }
 303 if (lp->rxpb) {
 304     lp->rxpboffset = lp->rxpbsize = 0;
 305     FREE (lp->rxpb);
 306     lp->rxpb       = NULL;
 307     }
 308 if (lp->txpb) {
 309     lp->txpbsize   = lp->txppsize = lp->txppoffset = 0;
 310     FREE (lp->txpb);
 311     lp->txpb       = NULL;
 312     }
 313 memset (lp->rbr, 0, lp->rxbsz);                         /* clear break status array */
 314 return;
 315 }
 316 
 317 /* Report a connection to a line.
 318 
 319    If the indicated line (lp) is speaking the telnet wire protocol, a
 320    notification of the form:
 321 
 322       Connected to the <sim> simulator <dev> device, line <n>
 323 
 324    is sent to the newly connected line.  If the device has only one line, the
 325    "line <n>" part is omitted.  If the device has not been defined, the "<dev>
 326    device" part is omitted.
 327 
 328 */
 329 
 330 static void tmxr_report_connection (TMXR *mp, TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
 331 {
 332 int32 unwritten, psave;
 333 char  cmsg[160];
 334 char   dmsg[80]  = "";
 335 char   lmsg[80]  = "";
 336 char msgbuf[512] = "";
 337 
 338 if ((!lp->notelnet) || (sim_switches & SWMASK ('V'))) {
 339     sprintf (cmsg, "\n\r\nConnected to the %s simulator ", sim_name);
 340 
 341     if (mp->dptr) {                                     /* device defined? */
 342         sprintf (dmsg, "%s device",                     /* report device name */
 343                        sim_dname (mp->dptr));
 344 
 345         if (mp->lines > 1)                              /* more than one line? */
 346             sprintf (lmsg, ", line %d", (int)(lp-mp->ldsc));/* report the line number */
 347         }
 348 
 349     sprintf (msgbuf, "%s%s%s\r\n\n", cmsg, dmsg, lmsg);
 350     }
 351 
 352 if (!mp->buffered) {
 353     lp->txbpi  = 0;                                      /* init buf pointers */
 354     lp->txbpr  = (int32)(lp->txbsz - strlen (msgbuf));
 355     lp->rxcnt  = lp->txcnt = lp->txdrp = 0;              /* init counters */
 356     lp->rxpcnt = lp->txpcnt = 0;
 357     }
 358 else
 359     if (lp->txcnt > lp->txbsz)
 360         lp->txbpr = (lp->txbpi + 1) % lp->txbsz;
 361     else
 362         lp->txbpr = (int32)(lp->txbsz - strlen (msgbuf));
 363 
 364 psave     = lp->txbpi;                                  /* save insertion pointer */
 365 lp->txbpi = lp->txbpr;                                  /* insert connection message */
 366 tmxr_linemsg (lp, msgbuf);                              /* beginning of buffer */
 367 lp->txbpi = psave;                                      /* restore insertion pointer */
 368 
 369 unwritten = tmxr_send_buffered_data (lp);               /* send the message */
 370 
 371 if (unwritten == 0)                                     /* buffer now empty? */
 372     lp->xmte = 1;                                       /* reenable transmission if paused */
 373 
 374 lp->txcnt -= (int32)strlen (msgbuf);                    /* adjust statistics */
 375 return;
 376 }
 377 
 378 /* Report a disconnection to a line.
 379 
 380    A notification of the form:
 381 
 382       Disconnected from the <sim> simulator
 383 
 384    is sent to the line about to be disconnected.  We do not flush the buffer
 385    here, because the disconnect routines will do that just after calling us.
 386 */
 387 
 388 static void tmxr_report_disconnection (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
 389 {
 390 if (lp->notelnet)
 391     return;
 392 tmxr_linemsgf (lp, "\r\nDisconnected from the %s simulator\r\n\n", sim_name);/* report disconnection */
 393 return;
 394 }
 395 
 396 static int32 loop_write_ex (TMLN *lp, char *buf, int32 length, t_bool prefix_datagram)
     /* [previous][next][first][last][top][bottom][index][help] */
 397 {
 398 int32 written = 0;
 399 int32 loopfree = lp->lpbsz - lp->lpbcnt;
 400 
 401 if (lp->datagram && prefix_datagram) {
 402     if ((size_t)loopfree < (size_t)(length + sizeof(length)))
 403         return written;
 404     loop_write_ex (lp, (char *)&length, sizeof(length), FALSE);
 405     }
 406 while (length) {
 407     int32 chunksize;
 408 
 409     loopfree = lp->lpbsz - lp->lpbcnt;
 410     if (loopfree == 0)
 411         break;
 412     if (loopfree < length)
 413         length = loopfree;
 414     if (lp->lpbpi >= lp->lpbpr)
 415         chunksize = lp->lpbsz - lp->lpbpi;
 416     else
 417         chunksize = lp->lpbpr - lp->lpbpi;
 418     if (chunksize > length)
 419         chunksize = length;
 420     memcpy (&lp->lpb[lp->lpbpi], buf, chunksize);
 421     buf += chunksize;
 422     length -= chunksize;
 423     written += chunksize;
 424     lp->lpbpi = (lp->lpbpi + chunksize) % lp->lpbsz;
 425     }
 426 lp->lpbcnt += written;
 427 return written;
 428 }
 429 
 430 static int32 loop_write (TMLN *lp, char *buf, int32 length)
     /* [previous][next][first][last][top][bottom][index][help] */
 431 {
 432 return loop_write_ex (lp, buf, length, TRUE);
 433 }
 434 
 435 static int32 loop_read_ex (TMLN *lp, char *buf, int32 bufsize)
     /* [previous][next][first][last][top][bottom][index][help] */
 436 {
 437 int32 bytesread = 0;
 438 
 439 while (bufsize > 0) {
 440     int32 chunksize;
 441     int32 loopused = lp->lpbcnt;
 442 
 443     if (loopused < bufsize)
 444         bufsize = loopused;
 445     if (loopused == 0)
 446         break;
 447     if (lp->lpbpi > lp->lpbpr)
 448         chunksize = lp->lpbpi - lp->lpbpr;
 449     else
 450         chunksize = lp->lpbsz - lp->lpbpr;
 451     if (chunksize > bufsize)
 452         chunksize = bufsize;
 453     memcpy (buf, &lp->lpb[lp->lpbpr], chunksize);
 454     buf += chunksize;
 455     bufsize -= chunksize;
 456     bytesread += chunksize;
 457     lp->lpbpr = (lp->lpbpr + chunksize) % lp->lpbsz;
 458     }
 459 lp->lpbcnt -= bytesread;
 460 return bytesread;
 461 }
 462 
 463 static int32 loop_read (TMLN *lp, char *buf, int32 bufsize)
     /* [previous][next][first][last][top][bottom][index][help] */
 464 {
 465 if (lp->datagram) {
 466     int32 pktsize;
 467 
 468     if (lp->lpbcnt < (int32)sizeof(pktsize))
 469         return 0;
 470     if ((sizeof(pktsize) != loop_read_ex (lp, (char *)&pktsize, sizeof(pktsize))) ||
 471         (pktsize > bufsize))
 472         return -1;
 473     bufsize = pktsize;
 474     }
 475 return loop_read_ex (lp, buf, bufsize);
 476 }
 477 
 478 /* Read from a line.
 479 
 480    Up to "length" characters are read into the character buffer associated with
 481    line "lp".  The actual number of characters read is returned.  If no
 482    characters are available, 0 is returned.  If an error occurred while reading,
 483    -1 is returned.
 484 */
 485 
 486 static int32 tmxr_read (TMLN *lp, int32 length)
     /* [previous][next][first][last][top][bottom][index][help] */
 487 {
 488 int32 i = lp->rxbpi;
 489 
 490 if (lp->loopback)
 491     return loop_read (lp, &(lp->rxb[i]), length);
 492 else                                                    /* Telnet connection */
 493     return sim_read_sock (lp->sock, &(lp->rxb[i]), length);
 494 }
 495 
 496 /* Write to a line.
 497 
 498    Up to "length" characters are written from the character buffer associated
 499    with "lp".  The actual number of characters written is returned.  If an error
 500    occurred while writing, -1 is returned.
 501 */
 502 
 503 static int32 tmxr_write (TMLN *lp, int32 length)
     /* [previous][next][first][last][top][bottom][index][help] */
 504 {
 505 int32 written;
 506 int32 i = lp->txbpr;
 507 
 508 if (lp->loopback)
 509   return loop_write (lp, &(lp->txb[i]), length);
 510 
 511 written = sim_write_sock (lp->sock, &(lp->txb[i]), length);
 512 
 513 if (written == SOCKET_ERROR)                        /* did an error occur? */
 514   if (lp->datagram)
 515     return written;                             /* ignore errors on datagram sockets */
 516   else
 517     return -1;                                  /* return error indication */
 518 else
 519   return written;
 520 }
 521 
 522 /* Remove a character from the read buffer.
 523 
 524    The character at position "p" in the read buffer associated with line "lp" is
 525    removed by moving all of the following received characters down one position.
 526    The receive break status array is adjusted accordingly.
 527 */
 528 
 529 static void tmxr_rmvrc (TMLN *lp, int32 p)
     /* [previous][next][first][last][top][bottom][index][help] */
 530 {
 531 for ( ; p < lp->rxbpi; p++) {                           /* work from "p" through end of buffer */
 532     lp->rxb[p] = lp->rxb[p + 1];                        /* slide following character down */
 533     lp->rbr[p] = lp->rbr[p + 1];                        /* adjust break status too */
 534     }
 535 
 536 lp->rbr[p] = 0;                                         /* clear potential break from vacated slot */
 537 lp->rxbpi = lp->rxbpi - 1;                              /* drop buffer insert index */
 538 return;
 539 }
 540 
 541 /* Find a line descriptor indicated by unit or number.
 542 
 543    If "uptr" is NULL, then the line descriptor is determined by the line number
 544    passed in "val".  If "uptr" is not NULL, then it must point to a unit
 545    associated with a line, and the line descriptor is determined by the unit
 546    number, which is derived by the position of the unit in the device's unit
 547    array.
 548 
 549    Note: This routine may be called with a UNIT that does not belong to the
 550    device indicated in the TMXR structure.  That is, the multiplexer lines may
 551    belong to a device other than the one attached to the socket (the HP 2100 MUX
 552    device is one example).  Therefore, we must look up the device from the unit
 553    at each call, rather than depending on the DEVICE pointer stored in the TMXR.
 554 */
 555 
 556 static TMLN *tmxr_find_ldsc (UNIT *uptr, int32 val, const TMXR *mp)
     /* [previous][next][first][last][top][bottom][index][help] */
 557 {
 558 if (mp == NULL)                                         /* invalid multiplexer descriptor? */
 559     return NULL;                                        /* programming error! */
 560 if (uptr) {                                             /* called from SET? */
 561     DEVICE *dptr = find_dev_from_unit (uptr);           /* find device */
 562     if (dptr == NULL)                                   /* what?? */
 563         return NULL;
 564     val = (int32) (uptr - dptr->units);                 /* implicit line # */
 565     }
 566 if ((val < 0) || (val >= mp->lines))                    /* invalid line? */
 567     return NULL;
 568 return mp->ldsc + val;                                  /* line descriptor */
 569 }
 570 
 571 /* Get a line descriptor indicated by a string or unit.
 572 
 573    A pointer to the line descriptor associated with multiplexer "mp" and unit
 574    "uptr" or specified by string "cptr" is returned.  If "uptr" is non-null,
 575    then the unit number within its associated device implies the line number.
 576    If "uptr" is null, then the string "cptr" is parsed for a decimal line
 577    number.  If the line number is missing, malformed, or outside of the range of
 578    line numbers associated with "mp", then NULL is returned with status set to
 579    SCPE_ARG.
 580 
 581    Implementation note:
 582 
 583     1. A return status of SCPE_IERR implies a programming error (passing an
 584        invalid pointer or an invalid unit).
 585 */
 586 
 587 static TMLN *tmxr_get_ldsc (UNIT *uptr, const char *cptr, TMXR *mp, t_stat *status)
     /* [previous][next][first][last][top][bottom][index][help] */
 588 {
 589 t_value  ln;
 590 TMLN    *lp = NULL;
 591 t_stat   code = SCPE_OK;
 592 
 593 if (mp == NULL)                                         /* missing mux descriptor? */
 594     code = SCPE_IERR;                                   /* programming error! */
 595 
 596 else if (uptr) {                                        /* implied line form? */
 597     lp = tmxr_find_ldsc (uptr, mp->lines, mp);          /* determine line from unit */
 598 
 599     if (lp == NULL)                                     /* invalid line number? */
 600         code = SCPE_IERR;                               /* programming error! */
 601     }
 602 
 603 else if (cptr == NULL)                                  /* named line form, parameter supplied? */
 604     code = SCPE_MISVAL;                                 /* no, so report missing */
 605 
 606 else {
 607     ln = get_uint (cptr, 10, mp->lines - 1, &code);     /* get line number */
 608 
 609     if (code == SCPE_OK)                                /* line number OK? */
 610         lp = mp->ldsc + (int32) ln;                     /* use as index to determine line */
 611     }
 612 
 613 if (status)                                             /* return value pointer supplied? */
 614     *status = code;                                     /* store return status value */
 615 
 616 return lp;                                              /* return pointer to line descriptor */
 617 }
 618 
 619 /* Generate the Attach string which will fully configure the multiplexer
 620 
 621    Inputs:
 622         old     =       pointer to the original configuration string which will be replaced
 623         *mp     =       pointer to multiplexer
 624 
 625    Output:
 626         a complete attach string for the current state of the multiplexer
 627 
 628 */
 629 static char *growstring(char **string, size_t growth)
     /* [previous][next][first][last][top][bottom][index][help] */
 630 {
 631 if (!*string)
 632   {
 633     fprintf(stderr, "\rFATAL: Bugcheck! Aborting at %s[%s:%d]\r\n",
 634             __func__, __FILE__, __LINE__);
 635 #if defined(USE_BACKTRACE)
 636 # ifdef SIGUSR2
 637         (void)raise(SIGUSR2);
 638         /*NOTREACHED*/ /* unreachable */
 639 # endif /* ifdef SIGUSR2 */
 640 #endif /* if defined(USE_BACKTRACE) */
 641     abort();
 642   }
 643 *string = (char *)realloc (*string, 1 + (*string ? strlen (*string) : 0) + growth);
 644 if (!*string)
 645   {
 646     fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 647             __func__, __FILE__, __LINE__);
 648 #if defined(USE_BACKTRACE)
 649 # ifdef SIGUSR2
 650         (void)raise(SIGUSR2);
 651         /*NOTREACHED*/ /* unreachable */
 652 # endif /* ifdef SIGUSR2 */
 653 #endif /* if defined(USE_BACKTRACE) */
 654     abort();
 655   }
 656 return *string + strlen(*string);
 657 }
 658 
 659 static char *tmxr_mux_attach_string(char *old, TMXR *mp)
     /* [previous][next][first][last][top][bottom][index][help] */
 660 {
 661 char* tptr = NULL;
 662 int32 i;
 663 TMLN *lp;
 664 
 665 FREE (old);
 666 tptr = (char *) calloc (1, 1);
 667 if (tptr == NULL)                                       /* no more mem? */
 668     return tptr;
 669 
 670 if (mp->port)                                           /* copy port */
 671     sprintf (growstring(&tptr, 33 + strlen (mp->port)), "%s%s", mp->port, mp->notelnet ? ";notelnet" : "");
 672 if (mp->logfiletmpl[0])                                 /* logfile info */
 673     sprintf (growstring(&tptr, 7 + strlen (mp->logfiletmpl)), ",Log=%s", mp->logfiletmpl);
 674 while ((*tptr == ',') || (*tptr == ' '))
 675     memcpy (tptr, tptr+1, strlen(tptr+1)+1);
 676 for (i=0; i<mp->lines; ++i) {
 677     char *lptr;
 678     lp = mp->ldsc + i;
 679 
 680     lptr = tmxr_line_attach_string(lp);
 681     if (lptr) {
 682         sprintf (growstring(&tptr, 10+strlen(lptr)), "%s%s", *tptr ? "," : "", lptr);
 683         FREE (lptr);
 684         }
 685     }
 686 if (mp->lines == 1)
 687     while ((*tptr == ',') || (*tptr == ' '))
 688         memcpy (tptr, tptr+1, strlen(tptr+1)+1);
 689 if (*tptr == '\0') {
 690     FREE (tptr);
 691     tptr = NULL;
 692     }
 693 return tptr;
 694 }
 695 
 696 /* Global routines */
 697 
 698 /* Return the Line specific attach setup currently configured for a given line
 699 
 700    Inputs:
 701         *lp     =       pointer to terminal line descriptor
 702    Outputs:
 703         a string which can be used to reconfigure the line,
 704         NULL if the line isn't configured
 705 
 706    Note: The returned string is dynamically allocated memory and must be freed
 707          when it is no longer needed by calling free
 708 
 709 */
 710 
 711 char *tmxr_line_attach_string(TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
 712 {
 713 char* tptr = NULL;
 714 
 715 tptr = (char *) calloc (1, 1);
 716 if (tptr == NULL)                                       /* no more mem? */
 717     return tptr;
 718 
 719 if (lp->destination || lp->port || lp->txlogname) {
 720     if ((lp->mp->lines > 1) || (lp->port))
 721         sprintf (growstring(&tptr, 32), "Line=%d", (int)(lp-lp->mp->ldsc));
 722     if (lp->modem_control != lp->mp->modem_control)
 723         sprintf (growstring(&tptr, 32), ",%s", lp->modem_control ? "Modem" : "NoModem");
 724     if (lp->txbfd && (lp->txbsz != lp->mp->buffered))
 725         sprintf (growstring(&tptr, 32), ",Buffered=%d", lp->txbsz);
 726     if (!lp->txbfd && (lp->mp->buffered > 0))
 727         sprintf (growstring(&tptr, 32), ",UnBuffered");
 728     if (lp->mp->datagram != lp->datagram)
 729         sprintf (growstring(&tptr, 8), ",%s", lp->datagram ? "UDP" : "TCP");
 730     if (lp->mp->packet != lp->packet)
 731         sprintf (growstring(&tptr, 8), ",Packet");
 732     if (lp->port)
 733         sprintf (growstring(&tptr, 32 + strlen (lp->port)), ",%s%s", lp->port, ((lp->mp->notelnet != lp->notelnet) && (!lp->datagram)) ? (lp->notelnet ? ";notelnet" : ";telnet") : "");
 734     if (lp->destination) {
 735             sprintf (growstring(&tptr, 25 + strlen (lp->destination)), ",Connect=%s%s", lp->destination, ((lp->mp->notelnet != lp->notelnet) && (!lp->datagram)) ? (lp->notelnet ? ";notelnet" : ";telnet") : "");
 736         }
 737     if (lp->txlogname)
 738         sprintf (growstring(&tptr, 12 + strlen (lp->txlogname)), ",Log=%s", lp->txlogname);
 739     if (lp->loopback)
 740         sprintf (growstring(&tptr, 12 ), ",Loopback");
 741     }
 742 if (*tptr == '\0') {
 743     FREE (tptr);
 744     tptr = NULL;
 745     }
 746 return tptr;
 747 }
 748 
 749 /* Poll for new connection
 750 
 751    Called from unit service routine to test for new connection
 752 
 753    Inputs:
 754         *mp     =       pointer to terminal multiplexer descriptor
 755    Outputs:
 756         line number activated, -1 if none
 757 
 758    If a connection order is defined for the descriptor, and the first value is
 759    not -1 (indicating default order), then the order array is used to find an
 760    open line.  Otherwise, a search is made of all lines in numerical sequence.
 761 */
 762 
 763 int32 tmxr_poll_conn (TMXR *mp)
     /* [previous][next][first][last][top][bottom][index][help] */
 764 {
 765 SOCKET newsock;
 766 TMLN *lp = NULL;
 767 int32 *op;
 768 int32 i, j;
 769 int st1ret;
 770 char *address;
 771 char msg[512];
 772 uint32 poll_time = sim_os_msec ();
 773 struct timespec ts;
 774 
 775 (void)lp;
 776 memset (msg, 0, sizeof (msg));
 777 if (mp->last_poll_time == 0) {                          /* first poll initializations */
 778     UNIT *uptr = mp->uptr;
 779 
 780     if (!uptr)                                          /* Attached ? */
 781         return -1;                                      /* No connections are possinle! */
 782 
 783     if (mp->poll_interval == 0)                         /* Assure reasonable polling interval */
 784         mp->poll_interval = TMXR_DEFAULT_CONNECT_POLL_INTERVAL;
 785 
 786     if (!(uptr->dynflags & TMUF_NOASYNCH)) {            /* if asynch not disabled */
 787         uptr->dynflags |= UNIT_TM_POLL;                 /* tag as polling unit */
 788         sim_cancel (uptr);
 789         }
 790     for (i=0; i < mp->lines; i++) {
 791         uptr = mp->ldsc[i].uptr ? mp->ldsc[i].uptr : mp->uptr;
 792 
 793         if (!(mp->uptr->dynflags & TMUF_NOASYNCH)) {    /* if asynch not disabled */
 794             uptr->dynflags |= UNIT_TM_POLL;             /* tag as polling unit */
 795             sim_cancel (uptr);
 796             }
 797         }
 798     }
 799 
 800 if ((poll_time - mp->last_poll_time) < mp->poll_interval*1000)
 801     return -1;                                          /* too soon to try */
 802 
 803 #ifdef USE_MONOTONIC
 804   st1ret = clock_gettime(CLOCK_MONOTONIC, &ts);
 805 #else
 806   st1ret = clock_gettime(CLOCK_REALTIME, &ts);
 807 #endif /*ifdef USE_MONOTONIC */
 808   if (st1ret != 0)
 809     {
 810       fprintf (stderr, "\rFATAL: clock_gettime failure! Aborting at %s[%s:%d]\r\n",
 811                __func__, __FILE__, __LINE__);
 812 #if defined(USE_BACKTRACE)
 813 # ifdef SIGUSR2
 814       (void)raise(SIGUSR2);
 815       /*NOTREACHED*/ /* unreachable */
 816 # endif /* ifdef SIGUSR2 */
 817 #endif /* if defined(USE_BACKTRACE) */
 818       abort();
 819     }
 820 
 821 srand((unsigned int)((poll_time + getpid()) ^ (long)((1LL + (long long)ts.tv_nsec) * (1LL + (long long)ts.tv_sec))));
 822 
 823 mp->last_poll_time = poll_time;
 824 
 825 /* Check for a pending Telnet/tcp connection */
 826 
 827 if (mp->master) {
 828     if (mp->ring_sock != INVALID_SOCKET) {  /* Use currently 'ringing' socket if one is active */
 829         newsock       = mp->ring_sock;
 830         mp->ring_sock = INVALID_SOCKET;
 831         address       = mp->ring_ipad;
 832         mp->ring_ipad = NULL;
 833         }
 834     else
 835         newsock = sim_accept_conn_ex (mp->master, &address, (mp->packet ? SIM_SOCK_OPT_NODELAY : 0));/* poll connect */
 836 
 837     if (newsock != INVALID_SOCKET) {                    /* got a live one? */
 838         snprintf (msg, sizeof (msg)-1, "tmxr_poll_conn() - Connection from %s", address);
 839         op = mp->lnorder;                               /* get line connection order list pointer */
 840         i = mp->lines;                                  /* play it safe in case lines == 0 */
 841         ++mp->sessions;                                 /* count the new session */
 842 
 843         for (j = 0; j < mp->lines; j++, i++) {          /* find next avail line */
 844             if (op && (*op >= 0) && (*op < mp->lines))  /* order list present and valid? */
 845                 i = *op++;                              /* get next line in list to try */
 846             else                                        /* no list or not used or range error */
 847                 i = j;                                  /* get next sequential line */
 848 
 849             lp = mp->ldsc + i;                          /* get pointer to line descriptor */
 850             if ((lp->conn == FALSE) &&                  /* is the line available? */
 851                 (lp->destination == NULL) &&
 852                 (lp->master == 0) &&
 853                 (lp->ser_connect_pending == FALSE) &&
 854                 (lp->modem_control ? ((lp->modembits & TMXR_MDM_DTR) != 0) : TRUE))
 855                 break;                                  /* yes, so stop search */
 856             }
 857 
 858         if (i >= mp->lines) {                           /* all busy? */
 859             int32 ringable_count = 0;
 860 
 861             for (j = 0; j < mp->lines; j++, i++) {      /* find next avail line */
 862                 lp = mp->ldsc + j;                      /* get pointer to line descriptor */
 863                 if ((lp->conn == FALSE) &&              /* is the line available? */
 864                     (lp->destination == NULL) &&
 865                     (lp->master == 0) &&
 866                     (lp->ser_connect_pending == FALSE) &&
 867                     ((lp->modembits & TMXR_MDM_DTR) == 0)) {
 868                     ++ringable_count;
 869                     tmxr_set_get_modem_bits (lp, TMXR_MDM_RNG, 0, NULL);
 870                     }
 871                 }
 872             if (ringable_count > 0) {
 873                 if (mp->ring_start_time == 0) {
 874                     mp->ring_start_time = poll_time;
 875                     mp->ring_sock = newsock;
 876                     mp->ring_ipad = address;
 877                     }
 878                 else {
 879                     if ((poll_time - mp->ring_start_time) < TMXR_MODEM_RING_TIME*1000) {
 880                         mp->ring_sock = newsock;
 881                         mp->ring_ipad = address;
 882                         }
 883                     else {                                      /* Timeout waiting for DTR */
 884                         int ln;
 885 
 886                         /* turn off pending ring signals */
 887                         for (ln = 0; ln < lp->mp->lines; ln++) {
 888                             TMLN *tlp = lp->mp->ldsc + ln;
 889                             if (((tlp->destination == NULL) && (tlp->master == 0)) &&
 890                                 (tlp->modembits & TMXR_MDM_RNG) && (tlp->conn == FALSE))
 891                                 tlp->modembits &= ~TMXR_MDM_RNG;
 892                             }
 893                         mp->ring_start_time = 0;
 894                         tmxr_msg (newsock, "No answer on any connection\r\n");
 895                         sim_close_sock (newsock);
 896                         FREE (address);
 897                         }
 898                     }
 899                 }
 900             else {
 901                 tmxr_msg (newsock, "All connections busy\r\n");
 902                 sim_close_sock (newsock);
 903                 FREE (address);
 904                 }
 905             }
 906         else {
 907             lp = mp->ldsc + i;                          /* get line desc */
 908             lp->conn = TRUE;                            /* record connection */
 909             lp->sock = newsock;                         /* save socket */
 910             lp->ipad = address;                         /* ip address */
 911             tmxr_init_line (lp);                        /* init line */
 912             lp->notelnet = mp->notelnet;                /* apply mux default telnet setting */
 913             if (!lp->notelnet) {
 914                 sim_write_sock (newsock, (char *)mantra, sizeof(mantra));
 915                 lp->telnet_sent_opts = (uint8 *)realloc (lp->telnet_sent_opts, 256);
 916                 if (!lp->telnet_sent_opts)
 917                   {
 918                     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 919                              __func__, __FILE__, __LINE__);
 920 #if defined(USE_BACKTRACE)
 921 # ifdef SIGUSR2
 922                     (void)raise(SIGUSR2);
 923                     /*NOTREACHED*/ /* unreachable */
 924 # endif /* ifdef SIGUSR2 */
 925 #endif /* if defined(USE_BACKTRACE) */
 926                     abort();
 927                   }
 928                 memset (lp->telnet_sent_opts, 0, 256);
 929                 }
 930             tmxr_report_connection (mp, lp);
 931             lp->cnms = sim_os_msec ();                  /* time of connection */
 932             return i;
 933             }
 934         }                                               /* end if newsock */
 935     }
 936 
 937 /* Look for per line listeners or outbound connecting sockets */
 938 for (i = 0; i < mp->lines; i++) {                       /* check each line in sequence */
 939     int j, r = rand();
 940     lp = mp->ldsc + i;                                  /* get pointer to line descriptor */
 941 
 942     if (lp->ser_connect_pending) {
 943         lp->ser_connect_pending = FALSE;
 944         lp->conn = TRUE;
 945         return i;
 946         }
 947 
 948     /* Don't service network connections for loopbacked lines */
 949 
 950     if (lp->loopback)
 951         continue;
 952 
 953     /* If two simulators are configured with symmetric virtual null modem
 954        cables pointing at each other, there may be a problem establishing
 955        a connection if both systems happen to be checking for the success
 956        of their connections in the exact same order.  They can each observe
 957        success in their respective outgoing connections, which haven't
 958        actually been 'accept'ed on the peer end of the connection.
 959        We address this issue by checking for the success of an outgoing
 960        connection and the arrival of an incoming one in a random order.
 961      */
 962     for (j=0; j<2; j++)
 963         switch (((unsigned)j+(unsigned)r)&1) {
 964             case 0:
 965                 if (lp->connecting) {                           /* connecting? */
 966                     char *sockname, *peername;
 967 
 968                     switch (sim_check_conn(lp->connecting, FALSE))
 969                         {
 970                         case 1:                                 /* successful connection */
 971                             lp->conn = TRUE;                    /* record connection */
 972                             lp->sock = lp->connecting;          /* it now looks normal */
 973                             lp->connecting = 0;
 974                             int lpdlen = 1;
 975                             if (lp->destination != NULL)
 976                                 lpdlen = 1+strlen (lp->destination);
 977                             lp->ipad = (char *)realloc (lp->ipad, lpdlen);
 978                             if (!lp->ipad)
 979                               {
 980                                 fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 981                                         __func__, __FILE__, __LINE__);
 982 #if defined(USE_BACKTRACE)
 983 # ifdef SIGUSR2
 984                                 (void)raise(SIGUSR2);
 985                                 /*NOTREACHED*/ /* unreachable */
 986 # endif /* ifdef SIGUSR2 */
 987 #endif /* if defined(USE_BACKTRACE) */
 988                                 abort();
 989                               }
 990                             if (lp->destination != NULL)
 991                                 strcpy (lp->ipad, lp->destination);
 992                             lp->cnms = sim_os_msec ();
 993                             sim_getnames_sock (lp->sock, &sockname, &peername);
 994                             if (lp->destination)
 995                               snprintf (msg, sizeof(msg)-1, "tmxr_poll_conn() - Outgoing Line Connection to %s (%s->%s) established", lp->destination, sockname, peername);
 996                             FREE (sockname);
 997                             FREE (peername);
 998                             return i;
 999                         case -1:                                /* failed connection */
1000                             snprintf (msg, sizeof(msg)-1, "tmxr_poll_conn() - Outgoing Line Connection to %s failed", lp->destination);
1001                             tmxr_reset_ln (lp);                 /* retry */
1002                             break;
1003                         }
1004                     }
1005                 break;
1006             case 1:
1007                 if (lp->master) {                                   /* Check for a pending Telnet/tcp connection */
1008                     while (INVALID_SOCKET != (newsock = sim_accept_conn_ex (lp->master, &address, (lp->packet ? SIM_SOCK_OPT_NODELAY : 0)))) {/* got a live one? */
1009                         char *sockname, *peername;
1010 
1011                         sim_getnames_sock (newsock, &sockname, &peername);
1012                         snprintf (msg, sizeof(msg)-1, "tmxr_poll_conn() - Incoming Line Connection from %s (%s->%s)", address, peername, sockname);
1013                         FREE (sockname);
1014                         FREE (peername);
1015                         ++mp->sessions;                             /* count the new session */
1016 
1017                         if (lp->destination) {                      /* Virtual Null Modem Cable? */
1018                             char host[sizeof(msg) - 64];
1019 
1020                             if (sim_parse_addr (lp->destination, host, sizeof(host), NULL, NULL, 0, NULL, address)) {
1021                                 tmxr_msg (newsock, "Rejecting connection from unexpected source\r\n");
1022                                 snprintf (msg, sizeof(msg)-1, "tmxr_poll_conn() - Rejecting line connection from: %s, Expected: %s", address, host);
1023                                 sim_close_sock (newsock);
1024                                 FREE (address);
1025                                 continue;                           /* Try for another connection */
1026                                 }
1027                             if (lp->connecting) {
1028                                 snprintf (msg, sizeof(msg)-1, "tmxr_poll_conn() - aborting outgoing line connection attempt to: %s", lp->destination);
1029                                 sim_close_sock (lp->connecting);    /* abort our as yet unconnnected socket */
1030                                 lp->connecting = 0;
1031                                 }
1032                             }
1033                         if (lp->conn == FALSE) {                    /* is the line available? */
1034                             if ((!lp->modem_control) || (lp->modembits & TMXR_MDM_DTR)) {
1035                                 lp->conn = TRUE;                    /* record connection */
1036                                 lp->sock = newsock;                 /* save socket */
1037                                 lp->ipad = address;                 /* ip address */
1038                                 tmxr_init_line (lp);                /* init line */
1039                                 if (!lp->notelnet) {
1040                                     sim_write_sock (newsock, (char *)mantra, sizeof(mantra));
1041                                     lp->telnet_sent_opts = (uint8 *)realloc (lp->telnet_sent_opts, 256);
1042                                     if (!lp->telnet_sent_opts)
1043                                       {
1044                                         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1045                                                  __func__, __FILE__, __LINE__);
1046 #if defined(USE_BACKTRACE)
1047 # ifdef SIGUSR2
1048                                         (void)raise(SIGUSR2);
1049                                         /*NOTREACHED*/ /* unreachable */
1050 # endif /* ifdef SIGUSR2 */
1051 #endif /* if defined(USE_BACKTRACE) */
1052                                         abort();
1053                                       }
1054                                     memset (lp->telnet_sent_opts, 0, 256);
1055                                     }
1056                                 tmxr_report_connection (mp, lp);
1057                                 lp->cnms = sim_os_msec ();          /* time of connection */
1058                                 return i;
1059                                 }
1060                             else {
1061                                 tmxr_msg (newsock, "Line connection not available\r\n");
1062                                 sim_close_sock (newsock);
1063                                 FREE (address);
1064                                 }
1065                             }
1066                         else {
1067                             tmxr_msg (newsock, "Line connection busy\r\n");
1068                             sim_close_sock (newsock);
1069                             FREE (address);
1070                             }
1071                         }
1072                     }
1073                 break;
1074             }
1075 
1076     /* Check for needed outgoing connection initiation */
1077 
1078     if (lp->destination && (!lp->sock) && (!lp->connecting) &&
1079         (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR))) {
1080         snprintf (msg, sizeof(msg)-1, "tmxr_poll_conn() - establishing outgoing connection to: %s", lp->destination);
1081         lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL, lp->destination, "localhost", NULL, (lp->datagram ? SIM_SOCK_OPT_DATAGRAM : 0)  |
1082                                                                                                                   (lp->mp->packet ? SIM_SOCK_OPT_NODELAY : 0) |
1083                                                                                                                   SIM_SOCK_OPT_BLOCKING);
1084         }
1085 
1086     }
1087 
1088 return -1;                                              /* no new connections made */
1089 }
1090 
1091 /* Reset a line. */
1092 
1093 static t_stat tmxr_reset_ln_ex (TMLN *lp, t_bool closeserial)
     /* [previous][next][first][last][top][bottom][index][help] */
1094 {
1095 char msg[512];
1096 
1097 if (lp->txlog)
1098     fflush (lp->txlog);                                 /* flush log */
1099 
1100 tmxr_send_buffered_data (lp);                           /* send any buffered data */
1101 
1102 snprintf (msg, sizeof(msg)-1, "tmxr_reset_ln_ex(%s)", closeserial ? "TRUE" : "FALSE");
1103 
1104     if (lp->sock) {
1105         sim_close_sock (lp->sock);                      /* close socket */
1106         FREE (lp->telnet_sent_opts);
1107         lp->telnet_sent_opts = NULL;
1108         lp->sock = 0;
1109         lp->conn = FALSE;
1110         lp->cnms = 0;
1111         lp->xmte = 1;
1112         }
1113 FREE(lp->ipad);
1114 lp->ipad = NULL;
1115 if ((lp->destination)
1116 ) {
1117     if (lp->connecting) {
1118         sim_close_sock (lp->connecting);
1119         lp->connecting = 0;
1120         }
1121     if ((!lp->modem_control) || (lp->modembits & TMXR_MDM_DTR)) {
1122         snprintf (msg, sizeof(msg)-1, "tmxr_reset_ln_ex() - connecting to %s", lp->destination);
1123         lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL, lp->destination, "localhost", NULL, (lp->datagram ? SIM_SOCK_OPT_DATAGRAM : 0) |
1124                                                                                                                   (lp->packet ? SIM_SOCK_OPT_NODELAY : 0)    |
1125                                                                                                                   SIM_SOCK_OPT_BLOCKING);
1126         }
1127     }
1128 tmxr_init_line (lp);                                /* initialize line state */
1129 return SCPE_OK;
1130 }
1131 
1132 t_stat tmxr_close_ln (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1133 {
1134 return tmxr_reset_ln_ex (lp, TRUE);
1135 }
1136 
1137 t_stat tmxr_reset_ln (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1138 {
1139 return tmxr_reset_ln_ex (lp, FALSE);
1140 }
1141 
1142 /* Manipulate the modem control bits of a specific line
1143 
1144    Inputs:
1145         *lp     =       pointer to terminal line descriptor
1146         bits_to_set     TMXR_MDM_DTR and/or TMXR_MDM_RTS as desired
1147         bits_to_clear   TMXR_MDM_DTR and/or TMXR_MDM_RTS as desired
1148 
1149    Output:
1150         incoming_bits   if non NULL, returns the current stat of DCD,
1151                         RNG, CTS and DSR along with the current state
1152                         of DTR and RTS
1153 */
1154 t_stat tmxr_set_get_modem_bits (TMLN *lp, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits)
     /* [previous][next][first][last][top][bottom][index][help] */
1155 {
1156 int32 before_modem_bits, incoming_state;
1157 DEVICE *dptr;
1158 
1159 if ((bits_to_set & ~(TMXR_MDM_OUTGOING)) ||         /* Assure only settable bits */
1160     (bits_to_clear & ~(TMXR_MDM_OUTGOING)) ||
1161     (bits_to_set & bits_to_clear))                  /* and can't set and clear the same bits */
1162     return SCPE_ARG;
1163 before_modem_bits = lp->modembits;
1164 lp->modembits |= bits_to_set;
1165 lp->modembits &= ~(bits_to_clear | TMXR_MDM_INCOMING);
1166 if ((lp->sock)
1167    || (lp->loopback)) {
1168     if (lp->modembits & TMXR_MDM_DTR) {
1169         incoming_state = TMXR_MDM_DSR;
1170         if (lp->modembits & TMXR_MDM_RTS)
1171             incoming_state |= TMXR_MDM_CTS;
1172         if (lp->halfduplex) {
1173             if (incoming_state & TMXR_MDM_CTS)
1174                 incoming_state |= TMXR_MDM_DCD;
1175             }
1176         else
1177             incoming_state |= TMXR_MDM_DCD;
1178         }
1179     else
1180         incoming_state = TMXR_MDM_DCD | TMXR_MDM_DSR | ((lp->modembits & TMXR_MDM_DTR) ? 0 : TMXR_MDM_RNG);
1181     }
1182 else {
1183     if (((before_modem_bits & TMXR_MDM_DTR) == 0) &&    /* Upward transition of DTR? */
1184         ((lp->modembits & TMXR_MDM_DTR) != 0)     &&
1185         (lp->conn == FALSE)                       &&    /* Not connected */
1186         (lp->modembits & TMXR_MDM_RNG)) {               /* and Ring Signal Present */
1187         if ((lp->destination == NULL) &&
1188             (lp->master == 0) &&
1189             (lp->mp && (lp->mp->ring_sock))) {
1190             int ln;
1191 
1192             lp->conn = TRUE;                            /* record connection */
1193             lp->sock = lp->mp->ring_sock;               /* save socket */
1194             lp->mp->ring_sock = INVALID_SOCKET;
1195             lp->ipad = lp->mp->ring_ipad;               /* ip address */
1196             lp->mp->ring_ipad = NULL;
1197             lp->mp->ring_start_time = 0;
1198             tmxr_init_line (lp);                        /* init line */
1199             lp->notelnet = lp->mp->notelnet;            /* apply mux default telnet setting */
1200             if (!lp->notelnet) {
1201                 sim_write_sock (lp->sock, (char *)mantra, sizeof(mantra));
1202                 lp->telnet_sent_opts = (uint8 *)realloc (lp->telnet_sent_opts, 256);
1203                 if (!lp->telnet_sent_opts)
1204                   {
1205                     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1206                              __func__, __FILE__, __LINE__);
1207 #if defined(USE_BACKTRACE)
1208 # ifdef SIGUSR2
1209                     (void)raise(SIGUSR2);
1210                     /*NOTREACHED*/ /* unreachable */
1211 # endif /* ifdef SIGUSR2 */
1212 #endif /* if defined(USE_BACKTRACE) */
1213                     abort();
1214                   }
1215                 memset (lp->telnet_sent_opts, 0, 256);
1216                 }
1217             tmxr_report_connection (lp->mp, lp);
1218             lp->cnms = sim_os_msec ();                  /* time of connection */
1219             lp->modembits &= ~TMXR_MDM_RNG;             /* turn off ring on this line*/
1220             /* turn off other pending ring signals */
1221             for (ln = 0; ln < lp->mp->lines; ln++) {
1222                 TMLN *tlp = lp->mp->ldsc + ln;
1223                 if (((tlp->destination == NULL) && (tlp->master == 0)) &&
1224                     (tlp->modembits & TMXR_MDM_RNG) && (tlp->conn == FALSE))
1225                     tlp->modembits &= ~TMXR_MDM_RNG;
1226                 }
1227             }
1228         }
1229     if ((lp->master) || (lp->mp && lp->mp->master) ||
1230         (lp->port && lp->destination))
1231         incoming_state = TMXR_MDM_DSR;
1232     else
1233         incoming_state = 0;
1234     }
1235 lp->modembits |= incoming_state;
1236 dptr = (lp->dptr ? lp->dptr : (lp->mp ? lp->mp->dptr : NULL));
1237 if ((lp->modembits != before_modem_bits) && (sim_deb && lp->mp && dptr)) {
1238     sim_debug_bits (TMXR_DBG_MDM, dptr, tmxr_modem_bits, before_modem_bits, lp->modembits, FALSE);
1239     sim_debug (TMXR_DBG_MDM, dptr, " - Line %d - %p\n", (int)(lp-lp->mp->ldsc), lp->txb);
1240     }
1241 if (incoming_bits)
1242     *incoming_bits = lp->modembits;
1243 if (lp->mp && lp->modem_control) {                  /* This API ONLY works on modem_control enabled multiplexer lines */
1244     if (bits_to_set | bits_to_clear) {              /* Anything to do? */
1245         if (lp->loopback) {
1246             if ((lp->modembits ^ before_modem_bits) & TMXR_MDM_DTR) { /* DTR changed? */
1247                 lp->ser_connect_pending = (lp->modembits & TMXR_MDM_DTR);
1248                 lp->conn = !(lp->modembits & TMXR_MDM_DTR);
1249                 }
1250             return SCPE_OK;
1251             }
1252         if ((lp->sock) || (lp->connecting)) {
1253             if ((before_modem_bits & bits_to_clear & TMXR_MDM_DTR) != 0) { /* drop DTR? */
1254                 if (lp->sock)
1255                     tmxr_report_disconnection (lp);     /* report closure */
1256                 tmxr_reset_ln (lp);
1257                 }
1258             }
1259         else {
1260             if ((lp->destination) &&                    /* Virtual Null Modem Cable */
1261                 (bits_to_set & ~before_modem_bits &     /* and DTR being Raised */
1262                  TMXR_MDM_DTR)) {
1263                 char msg[512];
1264 
1265                 snprintf (msg, sizeof(msg)-1, "tmxr_set_get_modem_bits() - establishing outgoing connection to: %s", lp->destination);
1266                 lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL, lp->destination, "localhost", NULL, (lp->datagram ? SIM_SOCK_OPT_DATAGRAM : 0) |
1267                                                                                                                           (lp->packet ? SIM_SOCK_OPT_NODELAY : 0)    |
1268                                                                                                                           SIM_SOCK_OPT_BLOCKING);
1269                 }
1270             }
1271         }
1272     return SCPE_OK;
1273     }
1274 if ((lp->sock) || (lp->connecting)) {
1275     if ((before_modem_bits & bits_to_clear & TMXR_MDM_DTR) != 0) { /* drop DTR? */
1276         if (lp->sock)
1277             tmxr_report_disconnection (lp);     /* report closure */
1278         tmxr_reset_ln (lp);
1279         }
1280     }
1281 return SCPE_INCOMP;
1282 }
1283 
1284 /* Enable or Disable loopback mode on a line
1285 
1286    Inputs:
1287         lp -                the line to change
1288         enable_loopback -   enable or disable flag
1289 
1290    Output:
1291         none
1292 */
1293 t_stat tmxr_set_line_loopback (TMLN *lp, t_bool enable_loopback)
     /* [previous][next][first][last][top][bottom][index][help] */
1294 {
1295 if (lp->loopback == (enable_loopback != FALSE))
1296     return SCPE_OK;                 /* Nothing to do */
1297 lp->loopback = (enable_loopback != FALSE);
1298 if (lp->loopback) {
1299     lp->lpbsz = lp->rxbsz;
1300     lp->lpb = (char *)realloc(lp->lpb, lp->lpbsz);
1301     if (!lp->lpb)
1302       {
1303         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1304                  __func__, __FILE__, __LINE__);
1305 #if defined(USE_BACKTRACE)
1306 # ifdef SIGUSR2
1307         (void)raise(SIGUSR2);
1308         /*NOTREACHED*/ /* unreachable */
1309 # endif /* ifdef SIGUSR2 */
1310 #endif /* if defined(USE_BACKTRACE) */
1311         abort();
1312       }
1313     lp->lpbcnt = lp->lpbpi = lp->lpbpr = 0;
1314     if (!lp->conn)
1315         lp->ser_connect_pending = TRUE;
1316     }
1317 else {
1318     FREE (lp->lpb);
1319     lp->lpb = NULL;
1320     lp->lpbsz = 0;
1321     }
1322 return SCPE_OK;
1323 }
1324 
1325 t_bool tmxr_get_line_loopback (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1326 {
1327 return (lp->loopback != FALSE);
1328 }
1329 
1330 /* Enable or Disable halfduplex mode on a line
1331 
1332    Inputs:
1333         lp -                the line to change
1334         enable_halfduplex - enable or disable flag
1335 
1336    Output:
1337         none
1338 
1339    When a network connected line is in halfduplex mode, DCD modem signal
1340    track with CTS.  When not in halfduplex mode the DCD modem signal for
1341    network connected lines tracks with DSR.
1342 
1343 */
1344 t_stat tmxr_set_line_halfduplex (TMLN *lp, t_bool enable_halfduplex)
     /* [previous][next][first][last][top][bottom][index][help] */
1345 {
1346 if (lp->halfduplex == (enable_halfduplex != FALSE))
1347     return SCPE_OK;                 /* Nothing to do */
1348 lp->halfduplex = (enable_halfduplex != FALSE);
1349 return SCPE_OK;
1350 }
1351 
1352 t_bool tmxr_get_line_halfduplex (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1353 {
1354 return (lp->halfduplex != FALSE);
1355 }
1356 
1357 /* Get character from specific line
1358 
1359    Inputs:
1360         *lp     =       pointer to terminal line descriptor
1361    Output:
1362         valid + char, 0 if line
1363 
1364    Implementation note:
1365 
1366     1. If a line break was detected coincident with the current character, the
1367        receive break status associated with the character is cleared, and
1368        SCPE_BREAK is ORed into the return value.
1369 */
1370 
1371 int32 tmxr_input_pending_ln (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1372 {
1373 return (lp->rxbpi - lp->rxbpr);
1374 }
1375 
1376 int32 tmxr_getc_ln (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1377 {
1378 int32 j;
1379 t_stat val = 0;
1380 uint32 tmp;
1381 
1382 if ((lp->conn && lp->rcve) &&                           /* conn & enb & */
1383     ((!lp->rxbps) ||                                    /* (!rate limited || enough time passed)? */
1384      (sim_gtime () >= lp->rxnexttime))) {
1385     if (!sim_send_poll_data (&lp->send, &val)) {        /* injected input characters available? */
1386         j = lp->rxbpi - lp->rxbpr;                      /* # input chrs */
1387         if (j) {                                        /* any? */
1388             tmp = lp->rxb[lp->rxbpr];                   /* get char */
1389             val = TMXR_VALID | (tmp & 0377);            /* valid + chr */
1390             if (lp->rbr[lp->rxbpr]) {                   /* break? */
1391                 lp->rbr[lp->rxbpr] = 0;                 /* clear status */
1392                 val = val | SCPE_BREAK;                 /* indicate to caller */
1393                 }
1394             lp->rxbpr = lp->rxbpr + 1;                  /* adv pointer */
1395             }
1396         }
1397     }                                                   /* end if conn */
1398 if (lp->rxbpi == lp->rxbpr)                             /* empty? zero ptrs */
1399     lp->rxbpi = lp->rxbpr = 0;
1400 if (lp->rxbps) {
1401     if (val)
1402         lp->rxnexttime = floor (sim_gtime () + ((lp->rxdelta * sim_timer_inst_per_sec ())/lp->rxbpsfactor));
1403     }
1404 return val;
1405 }
1406 
1407 /* Get packet from specific line
1408 
1409    Inputs:
1410         *lp     =       pointer to terminal line descriptor
1411         **pbuf  =       pointer to pointer of packet contents
1412         *psize  =       pointer to packet size
1413         frame_byte -    byte which separates packets in the tcp stream
1414                         (0 means no separation character)
1415 
1416    Output:
1417         SCPE_LOST       link state lost
1418         SCPE_OK         Packet returned OR no packet available
1419 
1420    Implementation notes:
1421 
1422     1. If a packet is not yet available, then the pbuf address returned is
1423        NULL, but success (SCPE_OK) is returned
1424 */
1425 
1426 t_stat tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, size_t *psize)
     /* [previous][next][first][last][top][bottom][index][help] */
1427 {
1428 return tmxr_get_packet_ln_ex (lp, pbuf, psize, 0);
1429 }
1430 
1431 t_stat tmxr_get_packet_ln_ex (TMLN *lp, const uint8 **pbuf, size_t *psize, uint8 frame_byte)
     /* [previous][next][first][last][top][bottom][index][help] */
1432 {
1433 int32 c;
1434 size_t pktsize;
1435 size_t fc_size = (frame_byte ? 1 : 0);
1436 
1437 while (TMXR_VALID & (c = tmxr_getc_ln (lp))) {
1438     if (lp->rxpboffset + 3 > lp->rxpbsize) {
1439         lp->rxpbsize += 512;
1440         lp->rxpb = (uint8 *)realloc (lp->rxpb, lp->rxpbsize);
1441         if (!lp->rxpb)
1442           {
1443             fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1444                      __func__, __FILE__, __LINE__);
1445 #if defined(USE_BACKTRACE)
1446 # ifdef SIGUSR2
1447             (void)raise(SIGUSR2);
1448             /*NOTREACHED*/ /* unreachable */
1449 # endif /* ifdef SIGUSR2 */
1450 #endif /* if defined(USE_BACKTRACE) */
1451             abort();
1452           }
1453         }
1454     if ((lp->rxpboffset == 0) && (fc_size) && (c != frame_byte)) {
1455         continue;
1456         }
1457     if ((lp->datagram) && (lp->rxpboffset == fc_size)) {
1458         /* Datagram packet length is provided as a part of the natural datagram
1459            delivery, for TCP lines, we read the packet length from the data stream.
1460            So, here we stuff packet size into head of packet buffer so it looks like
1461            it was delivered by TCP and the below return logic doesn't have to worry */
1462         lp->rxpb[lp->rxpboffset++] = (uint8)(((1 + lp->rxbpi - lp->rxbpr) >> 8) & 0xFF);
1463         lp->rxpb[lp->rxpboffset++] = (uint8)((1 + lp->rxbpi - lp->rxbpr) & 0xFF);
1464         }
1465     lp->rxpb[lp->rxpboffset++] = c & 0xFF;
1466     if (lp->rxpboffset >= (2 + fc_size)) {
1467         pktsize = (lp->rxpb[0+fc_size] << 8) | lp->rxpb[1+fc_size];
1468         if (pktsize == (lp->rxpboffset - 2)) {
1469             ++lp->rxpcnt;
1470             *pbuf = &lp->rxpb[2+fc_size];
1471             *psize = pktsize;
1472             lp->rxpboffset = 0;
1473             return SCPE_OK;
1474             }
1475         }
1476     }
1477 *pbuf = NULL;
1478 *psize = 0;
1479 if (lp->conn)
1480     return SCPE_OK;
1481 return SCPE_LOST;
1482 }
1483 
1484 /* Poll for input
1485 
1486    Inputs:
1487         *mp     =       pointer to terminal multiplexer descriptor
1488    Outputs:     none
1489 */
1490 
1491 void tmxr_poll_rx (TMXR *mp)
     /* [previous][next][first][last][top][bottom][index][help] */
1492 {
1493 int32 i, nbytes, j;
1494 TMLN *lp;
1495 
1496 for (i = 0; i < mp->lines; i++) {                       /* loop thru lines */
1497     lp = mp->ldsc + i;                                  /* get line desc */
1498     if (!(lp->sock
1499         || lp->loopback) ||
1500         !(lp->rcve))                                    /* skip if not connected */
1501         continue;
1502 
1503     nbytes = 0;
1504     if (lp->rxbpi == 0)                                 /* need input? */
1505         nbytes = tmxr_read (lp,                         /* yes, read */
1506             lp->rxbsz - TMXR_GUARD);                    /* leave spc for Telnet cruft */
1507     else if (lp->tsta)                                  /* in Telnet seq? */
1508         nbytes = tmxr_read (lp,                         /* yes, read to end */
1509             lp->rxbsz - lp->rxbpi);
1510 
1511     if (nbytes < 0) {                                   /* line error? */
1512         if (!lp->datagram) {                            /* ignore errors reading UDP sockets */
1513             if (!lp->txbfd || lp->notelnet)
1514                 lp->txbpi = lp->txbpr = 0;              /* Drop the data we already know we can't send */
1515             tmxr_close_ln (lp);                         /* disconnect line */
1516             }
1517         }
1518 
1519     else if (nbytes > 0) {                              /* if data rcvd */
1520 
1521         j = lp->rxbpi;                                  /* start of data */
1522         lp->rxbpi = lp->rxbpi + nbytes;                 /* adv pointers */
1523         lp->rxcnt = lp->rxcnt + nbytes;
1524 
1525 /* Examine new data, remove TELNET cruft before making input available */
1526 
1527         if (!lp->notelnet) {                            /* Are we looking for telnet interpretation? */
1528             for (; j < lp->rxbpi; ) {                   /* loop thru char */
1529                 u_char tmp = (u_char)lp->rxb[j];        /* get char */
1530                 switch (lp->tsta) {                     /* case tlnt state */
1531 
1532                 case TNS_NORM:                          /* normal */
1533                     if (tmp == TN_IAC) {                /* IAC? */
1534                         lp->tsta = TNS_IAC;             /* change state */
1535                         tmxr_rmvrc (lp, j);             /* remove char */
1536                         break;
1537                         }
1538                     if ((tmp == TN_CR) && lp->dstb)     /* CR, no bin */
1539                         lp->tsta = TNS_CRPAD;           /* skip pad char */
1540                     j = j + 1;                          /* advance j */
1541                     break;
1542 
1543                 case TNS_IAC:                           /* IAC prev */
1544                     if (tmp == TN_IAC) {                /* IAC + IAC */
1545                         lp->tsta = TNS_NORM;            /* treat as normal */
1546                         j = j + 1;                      /* advance j */
1547                         break;                          /* keep IAC */
1548                         }
1549                     if (tmp == TN_BRK) {                /* IAC + BRK? */
1550                         lp->tsta = TNS_NORM;            /* treat as normal */
1551                         lp->rxb[j] = 0;                 /* char is null */
1552                         lp->rbr[j] = 1;                 /* flag break */
1553                         j = j + 1;                      /* advance j */
1554                         break;
1555                         }
1556                     switch (tmp) {
1557                     case TN_WILL:                       /* IAC + WILL? */
1558                         lp->tsta = TNS_WILL;
1559                         break;
1560                     case TN_WONT:                       /* IAC + WONT? */
1561                         lp->tsta = TNS_WONT;
1562                         break;
1563                     case TN_DO:                         /* IAC + DO? */
1564                         lp->tsta = TNS_DO;
1565                         break;
1566                     case TN_DONT:                       /* IAC + DONT? */
1567                         lp->tsta = TNS_SKIP;            /* IAC + other */
1568                         break;
1569                     case TN_GA: case TN_EL:             /* IAC + other 2 byte types */
1570                     case TN_EC: case TN_AYT:
1571                     case TN_AO: case TN_IP:
1572                     case TN_NOP:
1573                         lp->tsta = TNS_NORM; //-V1037   /* ignore */
1574                         break;
1575                     case TN_SB:                         /* IAC + SB sub-opt negotiation */
1576                     case TN_DATAMK:                     /* IAC + data mark */
1577                     case TN_SE:                         /* IAC + SE sub-opt end */
1578                         lp->tsta = TNS_NORM; //-V1037   /* ignore */
1579                         break;
1580                         }
1581                     tmxr_rmvrc (lp, j);                 /* remove char */
1582                     break;
1583 
1584                 case TNS_WILL:                          /* IAC+WILL prev */
1585                     if ((tmp == TN_STATUS) ||
1586                         (tmp == TN_TIMING) ||
1587                         (tmp == TN_NAOCRD) ||
1588                         (tmp == TN_NAOHTS) ||
1589                         (tmp == TN_NAOHTD) ||
1590                         (tmp == TN_NAOFFD) ||
1591                         (tmp == TN_NAOVTS) ||
1592                         (tmp == TN_NAOVTD) ||
1593                         (tmp == TN_NAOLFD) ||
1594                         (tmp == TN_EXTEND) ||
1595                         (tmp == TN_LOGOUT) ||
1596                         (tmp == TN_BM)     ||
1597                         (tmp == TN_DET)    ||
1598                         (tmp == TN_SENDLO) ||
1599                         (tmp == TN_TERMTY) ||
1600                         (tmp == TN_ENDREC) ||
1601                         (tmp == TN_TUID)   ||
1602                         (tmp == TN_OUTMRK) ||
1603                         (tmp == TN_TTYLOC) ||
1604                         (tmp == TN_3270)   ||
1605                         (tmp == TN_X3PAD)  ||
1606                         (tmp == TN_NAWS)   ||
1607                         (tmp == TN_TERMSP) ||
1608                         (tmp == TN_TOGFLO) ||
1609                         (tmp == TN_XDISPL) ||
1610                         (tmp == TN_ENVIRO) ||
1611                         (tmp == TN_AUTH)   ||
1612                         (tmp == TN_ENCRYP) ||
1613                         (tmp == TN_NEWENV) ||
1614                         (tmp == TN_TN3270) ||
1615                         (tmp == TN_CHARST) ||
1616                         (tmp == TN_COMPRT) ||
1617                         (tmp == TN_KERMIT)) {
1618                         /* Reject (DONT) these 'uninteresting' options only one time to avoid loops */
1619                         if (0 == (lp->telnet_sent_opts[tmp] & TNOS_DONT)) {
1620                             lp->notelnet = TRUE;                /* Temporarily disable so */
1621                             tmxr_putc_ln (lp, TN_IAC);          /* IAC gets injected bare */
1622                             lp->notelnet = FALSE;
1623                             tmxr_putc_ln (lp, TN_DONT);
1624                             tmxr_putc_ln (lp, tmp);
1625                             lp->telnet_sent_opts[tmp] |= TNOS_DONT;/* Record DONT sent */
1626                             }
1627                         }
1628                 /*FALLTHRU*/ /* fall through */ /* fallthrough */
1629                 case TNS_WONT:           /* IAC+WILL/WONT prev */
1630                     if (tmp == TN_BIN) {                /* BIN? */
1631                         if (lp->tsta == TNS_WILL) {
1632                             lp->dstb = 0;
1633                             }
1634                         else {
1635                             lp->dstb = 1;
1636                             }
1637                         }
1638                     tmxr_rmvrc (lp, j);                 /* remove it */
1639                     lp->tsta = TNS_NORM;                /* next normal */
1640                     break;
1641 
1642                 /* Negotiation with the HP terminal emulator "QCTerm" is not working.
1643                    QCTerm says "WONT BIN" but sends bare CRs.  RFC 854 says:
1644 
1645                      Note that "CR LF" or "CR NUL" is required in both directions
1646                      (in the default ASCII mode), to preserve the symmetry of the
1647                      NVT model.  ...The protocol requires that a NUL be inserted
1648                      following a CR not followed by a LF in the data stream.
1649 
1650                    Until full negotiation is implemented, we work around the problem
1651                    by checking the character following the CR in non-BIN mode and
1652                    strip it only if it is LF or NUL.  This should not affect
1653                    conforming clients.
1654                 */
1655 
1656                 case TNS_CRPAD:                         /* only LF or NUL should follow CR */
1657                     lp->tsta = TNS_NORM;                /* next normal */
1658                     if ((tmp == TN_LF) ||               /* CR + LF ? */
1659                         (tmp == TN_NUL))                /* CR + NUL? */
1660                         tmxr_rmvrc (lp, j);             /* remove it */
1661                     break;
1662 
1663                 case TNS_DO:                            /* pending DO request */
1664                     if ((tmp == TN_STATUS) ||
1665                         (tmp == TN_TIMING) ||
1666                         (tmp == TN_NAOCRD) ||
1667                         (tmp == TN_NAOHTS) ||
1668                         (tmp == TN_NAOHTD) ||
1669                         (tmp == TN_NAOFFD) ||
1670                         (tmp == TN_NAOVTS) ||
1671                         (tmp == TN_NAOVTD) ||
1672                         (tmp == TN_NAOLFD) ||
1673                         (tmp == TN_EXTEND) ||
1674                         (tmp == TN_LOGOUT) ||
1675                         (tmp == TN_BM)     ||
1676                         (tmp == TN_DET)    ||
1677                         (tmp == TN_SENDLO) ||
1678                         (tmp == TN_TERMTY) ||
1679                         (tmp == TN_ENDREC) ||
1680                         (tmp == TN_TUID)   ||
1681                         (tmp == TN_OUTMRK) ||
1682                         (tmp == TN_TTYLOC) ||
1683                         (tmp == TN_3270)   ||
1684                         (tmp == TN_X3PAD)  ||
1685                         (tmp == TN_NAWS)   ||
1686                         (tmp == TN_TERMSP) ||
1687                         (tmp == TN_TOGFLO) ||
1688                         (tmp == TN_XDISPL) ||
1689                         (tmp == TN_ENVIRO) ||
1690                         (tmp == TN_AUTH)   ||
1691                         (tmp == TN_ENCRYP) ||
1692                         (tmp == TN_NEWENV) ||
1693                         (tmp == TN_TN3270) ||
1694                         (tmp == TN_CHARST) ||
1695                         (tmp == TN_COMPRT) ||
1696                         (tmp == TN_KERMIT)) {
1697                         /* Reject (WONT) these 'uninteresting' options only one time to avoid loops */
1698                         if (0 == (lp->telnet_sent_opts[tmp] & TNOS_WONT)) {
1699                             lp->notelnet = TRUE;                /* Temporarily disable so */
1700                             tmxr_putc_ln (lp, TN_IAC);          /* IAC gets injected bare */
1701                             lp->notelnet = FALSE;
1702                             tmxr_putc_ln (lp, TN_WONT);
1703                             tmxr_putc_ln (lp, tmp);
1704                             lp->telnet_sent_opts[tmp] |= TNOS_WONT;/* Record WONT sent */
1705                             }
1706                         }
1707                 /*FALLTHRU*/ /* fall through */ /* fallthrough */
1708                 case TNS_SKIP: default:                 /* skip char */
1709                     tmxr_rmvrc (lp, j);                 /* remove char */
1710                     lp->tsta = TNS_NORM;                /* next normal */
1711                     break;
1712                     }                                   /* end case state */
1713                 }                                       /* end for char */
1714             }
1715         }                                               /* end else nbytes */
1716     }                                                   /* end for lines */
1717 for (i = 0; i < mp->lines; i++) {                       /* loop thru lines */
1718     lp = mp->ldsc + i;                                  /* get line desc */
1719     if (lp->rxbpi == lp->rxbpr)                         /* if buf empty, */
1720         lp->rxbpi = lp->rxbpr = 0;                      /* reset pointers */
1721     }                                                   /* end for */
1722 return;
1723 }
1724 
1725 /* Return count of available characters for line */
1726 
1727 int32 tmxr_rqln_bare (const TMLN *lp, t_bool speed)
     /* [previous][next][first][last][top][bottom][index][help] */
1728 {
1729 if ((speed) && (lp->rxbps)) {                   /* consider speed and rate limiting? */
1730     if (sim_gtime () < lp->rxnexttime)          /* too soon? */
1731         return 0;
1732     else
1733         return (lp->rxbpi - lp->rxbpr + ((lp->rxbpi < lp->rxbpr)? lp->rxbsz : 0)) ? 1 : 0;
1734     }
1735 return (lp->rxbpi - lp->rxbpr + ((lp->rxbpi < lp->rxbpr)? lp->rxbsz: 0));
1736 }
1737 
1738 int32 tmxr_rqln (const TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1739 {
1740 return tmxr_rqln_bare (lp, TRUE);
1741 }
1742 
1743 /* Store character in line buffer
1744 
1745    Inputs:
1746         *lp     =       pointer to line descriptor
1747         chr     =       character
1748    Outputs:
1749         status  =       ok, connection lost, or stall
1750 
1751    Implementation note:
1752 
1753     1. If the line is not connected, SCPE_LOST is returned.
1754 */
1755 
1756 t_stat tmxr_putc_ln (TMLN *lp, int32 chr)
     /* [previous][next][first][last][top][bottom][index][help] */
1757 {
1758 if ((lp->conn == FALSE) &&                              /* no conn & not buffered telnet? */
1759     (!lp->txbfd || lp->notelnet)) {
1760     ++lp->txdrp;                                        /* lost */
1761     return SCPE_LOST;
1762     }
1763 
1764 #define TXBUF_AVAIL(lp) (lp->txbsz - tmxr_tqln (lp))
1765 
1766 #define TXBUF_CHAR(lp, c) {                               \
1767     lp->txb[lp->txbpi++] = (char)(c);                     \
1768     lp->txbpi %= lp->txbsz;                               \
1769     if (lp->txbpi == lp->txbpr)                           \
1770         lp->txbpr = (1+lp->txbpr)%lp->txbsz, ++lp->txdrp; \
1771     }
1772 
1773 if ((lp->txbfd && !lp->notelnet) || (TXBUF_AVAIL(lp) > 1)) {/* room for char (+ IAC)? */
1774     if ((TN_IAC == (u_char) chr) && (!lp->notelnet))    /* char == IAC in telnet session? */
1775         TXBUF_CHAR (lp, TN_IAC);                        /* stuff extra IAC char */
1776     TXBUF_CHAR (lp, chr);                               /* buffer char & adv pointer */
1777     if ((!lp->txbfd) && ((unsigned long int)TXBUF_AVAIL (lp) <= TMXR_GUARD)) /* near full? */
1778         lp->xmte = 0;                                   /* disable line */
1779     if (lp->txlog)                                      /* log if available */
1780         fputc (chr, lp->txlog);
1781     sim_exp_check (&lp->expect, chr);                   /* process expect rules as needed */
1782     return SCPE_OK;                                     /* char sent */
1783     }
1784 ++lp->txdrp; lp->xmte = 0;                              /* no room, dsbl line */
1785 return SCPE_STALL;                                      /* char not sent */
1786 }
1787 
1788 /* Store packet in line buffer
1789 
1790    Inputs:
1791         *lp     =       pointer to line descriptor
1792         *buf    =       pointer to packet data
1793         size    =       size of packet
1794         frame_char =    inter-packet framing character (0 means no frame character)
1795 
1796    Outputs:
1797         status  =       ok, connection lost, or stall
1798 
1799    Implementation notes:
1800 
1801     1. If the line is not connected, SCPE_LOST is returned.
1802     2. If prior packet transmission still in progress, SCPE_STALL is
1803        returned and no packet data is stored.  The caller must retry later.
1804 */
1805 t_stat tmxr_put_packet_ln (TMLN *lp, const uint8 *buf, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
1806 {
1807 return tmxr_put_packet_ln_ex (lp, buf, size, 0);
1808 }
1809 
1810 t_stat tmxr_put_packet_ln_ex (TMLN *lp, const uint8 *buf, size_t size, uint8 frame_byte)
     /* [previous][next][first][last][top][bottom][index][help] */
1811 {
1812 t_stat r;
1813 size_t fc_size = (frame_byte ? 1 : 0);
1814 size_t pktlen_size = (lp->datagram ? 0 : 2);
1815 
1816 if ((!lp->conn) && (!lp->loopback))
1817     return SCPE_LOST;
1818 if (lp->txppoffset < lp->txppsize) {
1819     return SCPE_STALL;
1820     }
1821 if (lp->txpbsize < size + pktlen_size + fc_size) {
1822     lp->txpbsize = size + pktlen_size + fc_size;
1823     lp->txpb = (uint8 *)realloc (lp->txpb, lp->txpbsize);
1824     if (!lp->txpb)
1825       {
1826         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1827                  __func__, __FILE__, __LINE__);
1828 #if defined(USE_BACKTRACE)
1829 # ifdef SIGUSR2
1830         (void)raise(SIGUSR2);
1831         /*NOTREACHED*/ /* unreachable */
1832 # endif /* ifdef SIGUSR2 */
1833 #endif /* if defined(USE_BACKTRACE) */
1834         abort();
1835       }
1836     }
1837 lp->txpb[0] = frame_byte;
1838 if (!lp->datagram) {
1839     lp->txpb[0+fc_size] = (size >> 8) & 0xFF;
1840     lp->txpb[1+fc_size] = size & 0xFF;
1841     }
1842 memcpy (lp->txpb + pktlen_size + fc_size, buf, size);
1843 lp->txppsize = size + pktlen_size + fc_size;
1844 lp->txppoffset = 0;
1845 ++lp->txpcnt;
1846 while ((lp->txppoffset < lp->txppsize) &&
1847        (SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset]))))
1848    ++lp->txppoffset;
1849 (void)r;
1850 tmxr_send_buffered_data (lp);
1851 return (lp->conn || lp->loopback) ? SCPE_OK : SCPE_LOST;
1852 }
1853 
1854 /* Poll for output
1855 
1856    Inputs:
1857         *mp     =       pointer to terminal multiplexer descriptor
1858    Outputs:
1859         none
1860 */
1861 
1862 void tmxr_poll_tx (TMXR *mp)
     /* [previous][next][first][last][top][bottom][index][help] */
1863 {
1864 int32 i, nbytes;
1865 TMLN *lp;
1866 
1867 for (i = 0; i < mp->lines; i++) {                       /* loop thru lines */
1868     lp = mp->ldsc + i;                                  /* get line desc */
1869     if (!lp->conn)                                      /* skip if !conn */
1870         continue;
1871     nbytes = tmxr_send_buffered_data (lp);              /* buffered bytes */
1872     if (nbytes == 0) {                                  /* buf empty? enab line */
1873         lp->xmte = 1;                                   /* enable line transmit */
1874         }
1875     }                                                   /* end for */
1876 return;
1877 }
1878 
1879 /* Send buffered data across network
1880 
1881    Inputs:
1882         *lp     =       pointer to line descriptor
1883    Outputs:
1884         returns number of bytes still buffered
1885 */
1886 
1887 int32 tmxr_send_buffered_data (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1888 {
1889 int32 nbytes, sbytes;
1890 t_stat r;
1891 
1892 nbytes = tmxr_tqln(lp);                                 /* avail bytes */
1893 if (nbytes) {                                           /* >0? write */
1894     if (lp->txbpr < lp->txbpi)                          /* no wrap? */
1895         sbytes = tmxr_write (lp, nbytes);               /* write all data */
1896     else
1897         sbytes = tmxr_write (lp, lp->txbsz - lp->txbpr);/* write to end buf */
1898     if (sbytes >= 0) {                                  /* ok? */
1899         lp->txbpr = (lp->txbpr + sbytes);               /* update remove ptr */
1900         if (lp->txbpr >= lp->txbsz)                     /* wrap? */
1901             lp->txbpr = 0;
1902         lp->txcnt = lp->txcnt + sbytes;                 /* update counts */
1903         nbytes = nbytes - sbytes;
1904         if ((nbytes == 0) && (lp->datagram))            /* if Empty buffer on datagram line */
1905             lp->txbpi = lp->txbpr = 0;                  /* Start next packet at beginning of buffer */
1906         }
1907     if (sbytes < 0) {                                   /* I/O Error? */
1908         lp->txbpi = lp->txbpr = 0;                      /* Drop the data we already know we can't send */
1909         lp->rxpboffset = lp->txppoffset = lp->txppsize = 0;/* Drop the data we already know we can't send */
1910         tmxr_close_ln (lp);                             /*  close line/port on error */
1911         return nbytes;                                  /*  done now. */
1912         }
1913     if (nbytes && (lp->txbpr == 0))     {               /* more data and wrap? */
1914         sbytes = tmxr_write (lp, nbytes);
1915         if (sbytes > 0) {                               /* ok */
1916             lp->txbpr = (lp->txbpr + sbytes);           /* update remove ptr */
1917             if (lp->txbpr >= lp->txbsz)                 /* wrap? */
1918                 lp->txbpr = 0;
1919             lp->txcnt = lp->txcnt + sbytes;             /* update counts */
1920             nbytes = nbytes - sbytes;
1921             }
1922         }
1923     }                                                   /* end if nbytes */
1924 while ((lp->txppoffset < lp->txppsize) &&               /* buffered packet data? */
1925        (lp->txbsz > nbytes) &&                          /* and room in xmt buffer */
1926        (SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset]))))
1927    ++lp->txppoffset;
1928 (void)r;
1929 if ((nbytes == 0) && (tmxr_tqln(lp) > 0))
1930     return tmxr_send_buffered_data (lp);
1931 return tmxr_tqln(lp) + tmxr_tpqln(lp);
1932 }
1933 
1934 /* Return count of buffered characters for line */
1935 
1936 int32 tmxr_tqln (const TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1937 {
1938 return (lp->txbpi - lp->txbpr + ((lp->txbpi < lp->txbpr)? lp->txbsz: 0));
1939 }
1940 
1941 /* Return count of buffered packet characters for line */
1942 
1943 int32 tmxr_tpqln (const TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1944 {
1945 return (lp->txppsize - lp->txppoffset);
1946 }
1947 
1948 /* Return transmit packet busy status for line */
1949 
1950 t_bool tmxr_tpbusyln (const TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1951 {
1952 return (0 != (lp->txppsize - lp->txppoffset));
1953 }
1954 
1955 static void _mux_detach_line (TMLN *lp, t_bool close_listener, t_bool close_connecting)
     /* [previous][next][first][last][top][bottom][index][help] */
1956 {
1957 if (close_listener && lp->master) {
1958     sim_close_sock (lp->master);
1959     lp->master = 0;
1960     FREE (lp->port);
1961     lp->port = NULL;
1962     }
1963 if (lp->sock) {                             /* if existing tcp, drop it */
1964     tmxr_report_disconnection (lp);         /* report disconnection */
1965     tmxr_reset_ln (lp);
1966     }
1967 if (close_connecting) {
1968     FREE (lp->destination);
1969     lp->destination = NULL;
1970     if (lp->connecting) {                   /* if existing outgoing tcp, drop it */
1971         lp->sock = lp->connecting;
1972         lp->connecting = 0;
1973         tmxr_reset_ln (lp);
1974         }
1975     }
1976 tmxr_set_line_loopback (lp, FALSE);
1977 }
1978 
1979 t_stat tmxr_detach_ln (TMLN *lp)
     /* [previous][next][first][last][top][bottom][index][help] */
1980 {
1981 UNIT *uptr = NULL;
1982 
1983 _mux_detach_line (lp, TRUE, TRUE);
1984 if (lp->mp) {
1985     if (lp->uptr)
1986         uptr = lp->uptr;
1987     else
1988         uptr = lp->mp->uptr;
1989     }
1990 if (uptr && uptr->filename) {
1991     /* Revise the unit's connect string to reflect the current attachments */
1992     uptr->filename = tmxr_mux_attach_string (uptr->filename, lp->mp);
1993     /* No connections or listeners exist, then we're equivalent to being fully detached.  We should reflect that */
1994     if (uptr->filename == NULL)
1995         tmxr_detach (lp->mp, uptr);
1996     }
1997 return SCPE_OK;
1998 }
1999 
2000 static int32 _tmln_speed_delta (CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2001 {
2002 struct {
2003     const char *bps;
2004     int32 delta;
2005     } *spd,     speeds[] = {
2006     { "50",     TMLN_SPD_50_BPS     },
2007     { "75",     TMLN_SPD_75_BPS     },
2008     { "110",    TMLN_SPD_110_BPS    },
2009     { "134",    TMLN_SPD_134_BPS    },
2010     { "150",    TMLN_SPD_150_BPS    },
2011     { "300",    TMLN_SPD_300_BPS    },
2012     { "600",    TMLN_SPD_600_BPS    },
2013     { "1200",   TMLN_SPD_1200_BPS   },
2014     { "1800",   TMLN_SPD_1800_BPS   },
2015     { "2000",   TMLN_SPD_2000_BPS   },
2016     { "2400",   TMLN_SPD_2400_BPS   },
2017     { "3600",   TMLN_SPD_3600_BPS   },
2018     { "4800",   TMLN_SPD_4800_BPS   },
2019     { "7200",   TMLN_SPD_7200_BPS   },
2020     { "9600",   TMLN_SPD_9600_BPS   },
2021     { "19200",  TMLN_SPD_19200_BPS  },
2022     { "38400",  TMLN_SPD_38400_BPS  },
2023     { "57600",  TMLN_SPD_57600_BPS  },
2024     { "76800",  TMLN_SPD_76800_BPS  },
2025     { "115200", TMLN_SPD_115200_BPS },
2026     { "0",      0 } };                /* End of List, last valid value */
2027 int nspeed;
2028 char speed[24];
2029 
2030 nspeed = (uint32)strtotv (cptr, &cptr, 10);
2031 if ((*cptr != '\0') && (*cptr != '-') && (*cptr != '*'))
2032     return -1;
2033 sprintf (speed, "%d", nspeed);
2034 
2035 spd = speeds;
2036 while (1) {
2037     if (0 == strcmp(spd->bps, speed))
2038         return spd->delta;
2039     if (spd->delta == 0)
2040         break;
2041     ++spd;
2042     }
2043 return -1;
2044 }
2045 
2046 t_stat tmxr_set_line_speed (TMLN *lp, CONST char *speed)
     /* [previous][next][first][last][top][bottom][index][help] */
2047 {
2048 UNIT *uptr;
2049 CONST char *cptr;
2050 t_stat r;
2051 
2052 if (!speed || !*speed)
2053     return SCPE_2FARG;
2054 if (_tmln_speed_delta (speed) < 0)
2055     return SCPE_ARG;
2056 if (lp == NULL)
2057     return SCPE_ARG;
2058 lp->rxbps = (uint32)strtotv (speed, &cptr, 10);
2059 if (*cptr == '*') {
2060     uint32 rxbpsfactor = (uint32) get_uint (cptr+1, 10, 32, &r);
2061     if (r != SCPE_OK)
2062         return r;
2063     lp->rxbpsfactor = TMXR_RX_BPS_UNIT_SCALE * rxbpsfactor;
2064     }
2065 lp->rxdelta = _tmln_speed_delta (speed);
2066 lp->rxnexttime = 0.0;
2067 uptr = lp->uptr;
2068 if ((!uptr) && (lp->mp))
2069     uptr = lp->mp->uptr;
2070 if (uptr)
2071     uptr->wait = lp->rxdelta;
2072 if (lp->rxbpsfactor == 0.0)
2073     lp->rxbpsfactor = TMXR_RX_BPS_UNIT_SCALE;
2074 lp->txbps = lp->rxbps;
2075 lp->txdelta = lp->rxdelta;
2076 lp->txnexttime = lp->rxnexttime;
2077 return SCPE_OK;
2078 }
2079 
2080 /* Open a master listening socket (and all of the other variances of connections).
2081 
2082    A listening socket for the port number described by "cptr" is opened for the
2083    multiplexer associated with descriptor "mp".  If the open is successful, all
2084    lines not currently otherwise connected.
2085 */
2086 
2087 t_stat tmxr_open_master (TMXR *mp, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2088 {
2089 int32 i, line, nextline = -1;
2090 char tbuf[CBUFSIZE], listen[CBUFSIZE], destination[CBUFSIZE],
2091      logfiletmpl[CBUFSIZE], buffered[CBUFSIZE], hostport[CBUFSIZE*2],
2092      port[CBUFSIZE], option[CBUFSIZE], speed[CBUFSIZE];
2093 SOCKET sock;
2094 CONST char *tptr = cptr;
2095 t_bool nolog, notelnet, listennotelnet, modem_control, loopback, datagram, packet;
2096 TMLN *lp = NULL;
2097 t_stat r = SCPE_OK;
2098 
2099 if (*tptr == '\0')
2100     return SCPE_ARG;
2101 for (i = 0; i < mp->lines; i++) {               /* initialize lines */
2102     lp = mp->ldsc + i;
2103     lp->mp = mp;                                /* set the back pointer */
2104     lp->modem_control = mp->modem_control;
2105     if (lp->rxbpsfactor == 0.0)
2106         lp->rxbpsfactor = TMXR_RX_BPS_UNIT_SCALE;
2107     }
2108 mp->ring_sock = INVALID_SOCKET;
2109 FREE (mp->ring_ipad);
2110 mp->ring_ipad = NULL;
2111 mp->ring_start_time = 0;
2112 while (*tptr) {
2113     line = nextline;
2114     memset(logfiletmpl, '\0', sizeof(logfiletmpl));
2115     memset(listen,      '\0', sizeof(listen));
2116     memset(destination, '\0', sizeof(destination));
2117     memset(buffered,    '\0', sizeof(buffered));
2118     memset(port,        '\0', sizeof(port));
2119     memset(option,      '\0', sizeof(option));
2120     memset(speed,       '\0', sizeof(speed));
2121     nolog = notelnet = listennotelnet = loopback = FALSE;
2122     datagram = mp->datagram;
2123     packet = mp->packet;
2124     if (mp->buffered)
2125         sprintf(buffered, "%d", mp->buffered);
2126     if (line != -1)
2127         notelnet = listennotelnet = mp->notelnet;
2128     modem_control = mp->modem_control;
2129     while (*tptr) {
2130         tptr = get_glyph_nc (tptr, tbuf, ',');
2131         if (!tbuf[0])
2132             break;
2133         cptr = tbuf;
2134         if (!isdigit((unsigned char)*cptr)) {
2135             char gbuf[CBUFSIZE];
2136             CONST char *init_cptr = cptr;
2137 
2138             cptr = get_glyph (cptr, gbuf, '=');
2139             if (0 == MATCH_CMD (gbuf, "LINE")) {
2140                 if ((NULL == cptr) || ('\0' == *cptr))
2141                     return sim_messagef (SCPE_2FARG, "Missing Line Specifier\n");
2142                 nextline = (int32) get_uint (cptr, 10, mp->lines-1, &r);
2143                 if (r)
2144                     return sim_messagef (SCPE_ARG, "Invalid Line Specifier: %s\n", cptr);
2145                 break;
2146                 }
2147             if (0 == MATCH_CMD (gbuf, "LOG")) {
2148                 if ((NULL == cptr) || ('\0' == *cptr))
2149                     return sim_messagef (SCPE_2FARG, "Missing Log Specifier\n");
2150                 strncpy(logfiletmpl, cptr, sizeof(logfiletmpl)-1);
2151                 continue;
2152                 }
2153              if (0 == MATCH_CMD (gbuf, "LOOPBACK")) {
2154                 if ((NULL != cptr) && ('\0' != *cptr))
2155                     return sim_messagef (SCPE_2MARG, "Unexpected Loopback Specifier: %s\n", cptr);
2156                 loopback = TRUE;
2157                 continue;
2158                 }
2159            if ((0 == MATCH_CMD (gbuf, "NOBUFFERED")) || (0 == MATCH_CMD (gbuf, "UNBUFFERED"))) { //-V600
2160                 if ((NULL != cptr) && ('\0' != *cptr))
2161                     return sim_messagef (SCPE_2MARG, "Unexpected Unbuffered Specifier: %s\n", cptr);
2162                 buffered[0] = '\0';
2163                 continue;
2164                 }
2165             if (0 == MATCH_CMD (gbuf, "BUFFERED")) {
2166                 if ((NULL == cptr) || ('\0' == *cptr))
2167                     strcpy (buffered, "32768");
2168                 else {
2169                     i = (int32) get_uint (cptr, 10, 1024*1024, &r);
2170                     if (r || (i == 0))
2171                         return sim_messagef (SCPE_ARG, "Invalid Buffered Specifier: %s\n", cptr);
2172                     sprintf(buffered, "%d", i);
2173                     }
2174                 continue;
2175                 }
2176             if (0 == MATCH_CMD (gbuf, "NOLOG")) {
2177                 if ((NULL != cptr) && ('\0' != *cptr))
2178                     return sim_messagef (SCPE_2MARG, "Unexpected NoLog Specifier: %s\n", cptr);
2179                 nolog = TRUE;
2180                 continue;
2181                 }
2182             if (0 == MATCH_CMD (gbuf, "NOMODEM")) {
2183                 if ((NULL != cptr) && ('\0' != *cptr))
2184                     return sim_messagef (SCPE_2MARG, "Unexpected NoModem Specifier: %s\n", cptr);
2185                 modem_control = FALSE;
2186                 continue;
2187                 }
2188             if (0 == MATCH_CMD (gbuf, "MODEM")) {
2189                 if ((NULL != cptr) && ('\0' != *cptr))
2190                     return sim_messagef (SCPE_2MARG, "Unexpected Modem Specifier: %s\n", cptr);
2191                 modem_control = TRUE;
2192                 continue;
2193                 }
2194             if ((0 == MATCH_CMD (gbuf, "DATAGRAM")) || (0 == MATCH_CMD (gbuf, "UDP"))) { //-V600
2195                 if ((NULL != cptr) && ('\0' != *cptr))
2196                     return sim_messagef (SCPE_2MARG, "Unexpected Datagram Specifier: %s\n", cptr);
2197                 notelnet = datagram = TRUE;
2198                 continue;
2199                 }
2200             if (0 == MATCH_CMD (gbuf, "PACKET")) {
2201                 if ((NULL != cptr) && ('\0' != *cptr))
2202                     return sim_messagef (SCPE_2MARG, "Unexpected Packet Specifier: %s\n", cptr);
2203                 packet = TRUE;
2204                 continue;
2205                 }
2206             if ((0 == MATCH_CMD (gbuf, "STREAM")) || (0 == MATCH_CMD (gbuf, "TCP"))) { //-V600
2207                 if ((NULL != cptr) && ('\0' != *cptr))
2208                     return sim_messagef (SCPE_2MARG, "Unexpected Stream Specifier: %s\n", cptr);
2209                 datagram = FALSE;
2210                 continue;
2211                 }
2212             if (0 == MATCH_CMD (gbuf, "CONNECT")) {
2213                 if ((NULL == cptr) || ('\0' == *cptr))
2214                     return sim_messagef (SCPE_2FARG, "Missing Connect Specifier\n");
2215                 strcpy (destination, cptr);
2216                 continue;
2217                 }
2218             if (0 == MATCH_CMD (gbuf, "SPEED")) {
2219                 if ((NULL == cptr) || ('\0' == *cptr) ||
2220                     (_tmln_speed_delta (cptr) < 0))
2221                     return sim_messagef (SCPE_ARG, "Invalid Speed Specifier: %s\n", (cptr ? cptr : ""));
2222                 strcpy (speed, cptr);
2223                 continue;
2224                 }
2225             cptr = get_glyph (gbuf, port, ';');
2226             if (sim_parse_addr (port, NULL, 0, NULL, NULL, 0, NULL, NULL))
2227                 return sim_messagef (SCPE_ARG, "Invalid Port Specifier: %s\n", port);
2228             if (cptr) {
2229                 char *tptr = gbuf + (cptr - gbuf);
2230                 (void)get_glyph (cptr, tptr, 0);             /* upcase this string */
2231                 if (0 == MATCH_CMD (cptr, "NOTELNET"))
2232                     listennotelnet = TRUE;
2233                 else
2234                     if (0 == MATCH_CMD (cptr, "TELNET"))
2235                         listennotelnet = FALSE;
2236                     else
2237                         return sim_messagef (SCPE_ARG, "Invalid Specifier: %s\n", tptr);
2238                 }
2239             cptr = init_cptr;
2240             }
2241         cptr = get_glyph_nc (cptr, port, ';');
2242         sock = sim_master_sock (port, &r);                      /* make master socket to validate port */
2243         if (r)
2244             return sim_messagef (SCPE_ARG, "Invalid Port Specifier: %s\n", port);
2245         if (sock == INVALID_SOCKET)                             /* open error */
2246             return sim_messagef (SCPE_OPENERR, "Can't open network port: %s\n", port);
2247         sim_close_sock (sock);
2248         sim_os_ms_sleep (2);                                    /* let the close finish (required on some platforms) */
2249         strcpy (listen, port);
2250         cptr = get_glyph (cptr, option, ';');
2251         (void)cptr;
2252         if (option[0]) {
2253             if (0 == MATCH_CMD (option, "NOTELNET"))
2254                 listennotelnet = TRUE;
2255             else
2256                 if (0 == MATCH_CMD (option, "TELNET"))
2257                     listennotelnet = FALSE;
2258             else {
2259                 if (*tptr)
2260                     return sim_messagef (SCPE_ARG, "Invalid Specifier: %s\n", tptr);
2261                 }
2262             }
2263         }
2264     if (destination[0]) {
2265         /* Validate destination */
2266             char *eptr;
2267 
2268             memset (hostport, '\0', sizeof(hostport));
2269             strncpy (hostport, destination, sizeof(hostport));
2270             if ((eptr = strchr (hostport, ';')))
2271                 *(eptr++) = '\0';
2272             if (eptr) {
2273                 (void)get_glyph (eptr, eptr, 0);     /* upcase this string */
2274                 if (0 == MATCH_CMD (eptr, "NOTELNET"))
2275                     notelnet = TRUE;
2276                 else
2277                     if (0 == MATCH_CMD (eptr, "TELNET"))
2278                         if (datagram)
2279                             return sim_messagef (SCPE_ARG, "Telnet invalid on Datagram socket\n");
2280                         else
2281                             notelnet = FALSE;
2282                     else
2283                         return sim_messagef (SCPE_ARG, "Unexpected specifier: %s\n", eptr);
2284                 }
2285             sock = sim_connect_sock_ex (NULL, hostport, "localhost", NULL, (datagram ? SIM_SOCK_OPT_DATAGRAM : 0) |
2286                                                                            (packet ? SIM_SOCK_OPT_NODELAY : 0)    |
2287                                                                            SIM_SOCK_OPT_BLOCKING);
2288             if (sock != INVALID_SOCKET)
2289                 sim_close_sock (sock);
2290             else
2291                 return sim_messagef (SCPE_ARG, "Invalid destination: %s\n", hostport);
2292         }
2293     if (line == -1) {
2294         if (modem_control != mp->modem_control)
2295             return SCPE_ARG;
2296         if (logfiletmpl[0]) {
2297 #ifdef __GNUC__
2298 # ifndef __clang_version__
2299 #  ifndef __INTEL_COMPILER
2300 #   if __GNUC__ > 7
2301 #    pragma GCC diagnostic push
2302 #    pragma GCC diagnostic ignored "-Wstringop-truncation"
2303 #   endif /* if __GNUC__ > 7 */
2304 #  endif /* ifndef __INTEL_COMPILER */
2305 # endif /* ifndef __clang_version__ */
2306 #endif /* ifdef __GNUC__ */
2307             strncpy(mp->logfiletmpl, logfiletmpl, sizeof(mp->logfiletmpl)-1);
2308 #ifdef __GNUC__
2309 # ifndef __clang_version__
2310 #  ifndef __INTEL_COMPILER
2311 #   if __GNUC__ > 7
2312 #    pragma GCC diagnostic pop
2313 #   endif /* if __GNUC__ > 7 */
2314 #  endif /* ifndef __INTEL_COMPILER */
2315 # endif /* ifndef __clang_version__ */
2316 #endif /* ifdef __GNUC__ */
2317             for (i = 0; i < mp->lines; i++) {
2318                 lp = mp->ldsc + i;
2319                 sim_close_logfile (&lp->txlogref);
2320                 lp->txlog = NULL;
2321                 lp->txlogname = (char *)realloc(lp->txlogname, CBUFSIZE);
2322                 if (!lp->txlogname)
2323                   {
2324                     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2325                              __func__, __FILE__, __LINE__);
2326 #if defined(USE_BACKTRACE)
2327 # ifdef SIGUSR2
2328                     (void)raise(SIGUSR2);
2329                     /*NOTREACHED*/ /* unreachable */
2330 # endif /* ifdef SIGUSR2 */
2331 #endif /* if defined(USE_BACKTRACE) */
2332                     abort();
2333                   }
2334                 if (mp->lines > 1)
2335                     sprintf(lp->txlogname, "%s_%d", mp->logfiletmpl, i);
2336                 else
2337                     strcpy (lp->txlogname, mp->logfiletmpl);
2338                 r = sim_open_logfile (lp->txlogname, TRUE, &lp->txlog, &lp->txlogref);
2339                 if (r == SCPE_OK)
2340                     setvbuf (lp->txlog, NULL, _IOFBF, 65536);
2341                 else {
2342                     FREE (lp->txlogname);
2343                     lp->txlogname = NULL;
2344                     break;
2345                     }
2346                 }
2347             }
2348         mp->buffered = atoi(buffered);
2349         for (i = 0; i < mp->lines; i++) { /* initialize line buffers */
2350             lp = mp->ldsc + i;
2351             if (mp->buffered) {
2352                 lp->txbsz = mp->buffered;
2353                 lp->txbfd = 1;
2354                 lp->rxbsz = mp->buffered;
2355                 }
2356             else {
2357                 lp->txbsz = TMXR_MAXBUF;
2358                 lp->txbfd = 0;
2359                 lp->rxbsz = TMXR_MAXBUF;
2360                 }
2361             lp->txbpi = lp->txbpr = 0;
2362             lp->txb = (char *)realloc(lp->txb, lp->txbsz);
2363             if (!lp->txb)
2364               {
2365                 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2366                          __func__, __FILE__, __LINE__);
2367 #if defined(USE_BACKTRACE)
2368 # ifdef SIGUSR2
2369                 (void)raise(SIGUSR2);
2370                 /*NOTREACHED*/ /* unreachable */
2371 # endif /* ifdef SIGUSR2 */
2372 #endif /* if defined(USE_BACKTRACE) */
2373                 abort();
2374               }
2375             lp->rxb = (char *)realloc(lp->rxb, lp->rxbsz);
2376             if (!lp->rxb)
2377               {
2378                 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2379                          __func__, __FILE__, __LINE__);
2380 #if defined(USE_BACKTRACE)
2381 # ifdef SIGUSR2
2382                 (void)raise(SIGUSR2);
2383                 /*NOTREACHED*/ /* unreachable */
2384 # endif /* ifdef SIGUSR2 */
2385 #endif /* if defined(USE_BACKTRACE) */
2386                 abort();
2387               }
2388             lp->rbr = (char *)realloc(lp->rbr, lp->rxbsz);
2389             if (!lp->rbr)
2390               {
2391                 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2392                          __func__, __FILE__, __LINE__);
2393 #if defined(USE_BACKTRACE)
2394 # ifdef SIGUSR2
2395                 (void)raise(SIGUSR2);
2396                 /*NOTREACHED*/ /* unreachable */
2397 # endif /* ifdef SIGUSR2 */
2398 #endif /* if defined(USE_BACKTRACE) */
2399                 abort();
2400               }
2401             }
2402         if (nolog) {
2403             mp->logfiletmpl[0] = '\0';
2404             for (i = 0; i < mp->lines; i++) { /* close line logs */
2405                 lp = mp->ldsc + i;
2406                 FREE(lp->txlogname);
2407                 lp->txlogname = NULL;
2408                 if (lp->txlog) {
2409                     sim_close_logfile (&lp->txlogref);
2410                     lp->txlog = NULL;
2411                     }
2412                 }
2413             }
2414         if ((listen[0]) && (!datagram)) {
2415             sock = sim_master_sock (listen, &r);            /* make master socket */
2416             if (r)
2417                 return sim_messagef (SCPE_ARG, "Invalid network listen port: %s\n", listen);
2418             if (sock == INVALID_SOCKET)                     /* open error */
2419                 return sim_messagef (SCPE_OPENERR, "Can't open network socket for listen port: %s\n", listen);
2420             if (mp->port) {                                 /* close prior listener */
2421                 sim_close_sock (mp->master);
2422                 mp->master = 0;
2423                 FREE (mp->port);
2424                 mp->port = NULL;
2425                 }
2426             sim_printf ("Listening on port %s\n", listen);
2427             mp->port = (char *)realloc (mp->port, 1 + strlen (listen));
2428             if (!mp->port)
2429               {
2430                 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2431                          __func__, __FILE__, __LINE__);
2432 #if defined(USE_BACKTRACE)
2433 # ifdef SIGUSR2
2434                 (void)raise(SIGUSR2);
2435                 /*NOTREACHED*/ /* unreachable */
2436 # endif /* ifdef SIGUSR2 */
2437 #endif /* if defined(USE_BACKTRACE) */
2438                 abort();
2439               }
2440             strcpy (mp->port, listen);                      /* save port */
2441             mp->master = sock;                              /* save master socket */
2442             mp->ring_sock = INVALID_SOCKET;
2443             if (mp->ring_ipad) FREE (mp->ring_ipad);
2444             mp->ring_ipad = NULL;
2445             mp->ring_start_time = 0;
2446             mp->notelnet = listennotelnet;                  /* save desired telnet behavior flag */
2447             for (i = 0; i < mp->lines; i++) {               /* initialize lines */
2448                 lp = mp->ldsc + i;
2449                 lp->mp = mp;                                /* set the back pointer */
2450                 lp->packet = mp->packet;
2451                     if (speed[0])
2452                         tmxr_set_line_speed (lp, speed);
2453                 tmxr_init_line (lp);                        /* initialize line state */
2454                 lp->sock = 0;                               /* clear the socket */
2455                 }
2456             }
2457         if (loopback) {
2458             if (mp->lines > 1)
2459                 return sim_messagef (SCPE_ARG, "Ambiguous Loopback specification\n");
2460             sim_printf ("Operating in loopback mode\n");
2461             for (i = 0; i < mp->lines; i++) { //-V1008
2462                 lp = mp->ldsc + i;
2463                 tmxr_set_line_loopback (lp, loopback);
2464                 if (speed[0])
2465                     tmxr_set_line_speed (lp, speed);
2466                 }
2467             }
2468         if (destination[0]) {
2469             if (mp->lines > 1)
2470                 return sim_messagef (SCPE_ARG, "Ambiguous Destination specification\n");
2471             lp = &mp->ldsc[0];
2472                 lp->datagram = datagram;
2473                 if (datagram) {
2474                     if (listen[0]) {
2475                         lp->port = (char *)realloc (lp->port, 1 + strlen (listen));
2476                         if (!lp->port)
2477                           {
2478                             fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2479                                     __func__, __FILE__, __LINE__);
2480 #if defined(USE_BACKTRACE)
2481 # ifdef SIGUSR2
2482                             (void)raise(SIGUSR2);
2483                             /*NOTREACHED*/ /* unreachable */
2484 # endif /* ifdef SIGUSR2 */
2485 #endif /* if defined(USE_BACKTRACE) */
2486                             abort();
2487                           }
2488                         strcpy (lp->port, listen);           /* save port */
2489                         }
2490                     else
2491                         return sim_messagef (SCPE_ARG, "Missing listen port for Datagram socket\n");
2492                     }
2493                 lp->packet = packet;
2494                 sock = sim_connect_sock_ex (datagram ? listen : NULL, hostport, "localhost", NULL, (datagram ? SIM_SOCK_OPT_DATAGRAM : 0) |
2495                                                                                                    (packet ? SIM_SOCK_OPT_NODELAY : 0)    |
2496                                                                                                    SIM_SOCK_OPT_BLOCKING);
2497                 if (sock != INVALID_SOCKET) {
2498                     _mux_detach_line (lp, FALSE, TRUE);
2499                     lp->destination = (char *)malloc(1+strlen(hostport));
2500                     if (!lp->destination)
2501                       {
2502                         fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2503                                 __func__, __FILE__, __LINE__);
2504 #if defined(USE_BACKTRACE)
2505 # ifdef SIGUSR2
2506                         (void)raise(SIGUSR2);
2507                         /*NOTREACHED*/ /* unreachable */
2508 # endif /* ifdef SIGUSR2 */
2509 #endif /* if defined(USE_BACKTRACE) */
2510                         abort();
2511                       }
2512                     strcpy (lp->destination, hostport);
2513                     lp->mp = mp;
2514                     if (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR)) {
2515                         lp->connecting = sock;
2516                         lp->ipad = (char *)malloc (1 + strlen (lp->destination));
2517                         if (!lp->ipad)
2518                           {
2519                             fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2520                                     __func__, __FILE__, __LINE__);
2521 #if defined(USE_BACKTRACE)
2522 # ifdef SIGUSR2
2523                             (void)raise(SIGUSR2);
2524                             /*NOTREACHED*/ /* unreachable */
2525 # endif /* ifdef SIGUSR2 */
2526 #endif /* if defined(USE_BACKTRACE) */
2527                             abort();
2528                           }
2529                         strcpy (lp->ipad, lp->destination);
2530                         }
2531                     else
2532                         sim_close_sock (sock);
2533                     lp->notelnet = notelnet;
2534                     tmxr_init_line (lp);                    /* init the line state */
2535                     if (speed[0] && (!datagram))
2536                         tmxr_set_line_speed (lp, speed);
2537                     return SCPE_OK;
2538                     }
2539                 else
2540                     return sim_messagef (SCPE_ARG, "Can't open %s socket on %s%s%s\n", datagram ? "Datagram" : "Stream", datagram ? listen : "", datagram ? "<->" : "", hostport);
2541                 }
2542         }
2543     else {                                                  /* line specific attach */
2544         lp = &mp->ldsc[line];
2545         lp->mp = mp;
2546         if (logfiletmpl[0]) {
2547             sim_close_logfile (&lp->txlogref);
2548             lp->txlog = NULL;
2549             lp->txlogname = (char *)realloc (lp->txlogname, 1 + strlen (logfiletmpl));
2550             if (!lp->txlogname)
2551               {
2552                 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2553                          __func__, __FILE__, __LINE__);
2554 #if defined(USE_BACKTRACE)
2555 # ifdef SIGUSR2
2556                 (void)raise(SIGUSR2);
2557                 /*NOTREACHED*/ /* unreachable */
2558 # endif /* ifdef SIGUSR2 */
2559 #endif /* if defined(USE_BACKTRACE) */
2560                 abort();
2561               }
2562             strcpy (lp->txlogname, logfiletmpl);
2563             r = sim_open_logfile (lp->txlogname, TRUE, &lp->txlog, &lp->txlogref);
2564             if (r == SCPE_OK)
2565                 setvbuf(lp->txlog, NULL, _IOFBF, 65536);
2566             else {
2567                 FREE (lp->txlogname);
2568                 lp->txlogname = NULL;
2569                 return sim_messagef (r, "Can't open log file: %s\n", logfiletmpl);
2570                 }
2571             }
2572         if (buffered[0] == '\0') {
2573             lp->rxbsz = lp->txbsz = TMXR_MAXBUF;
2574             lp->txbfd = 0;
2575             }
2576         else {
2577             lp->rxbsz = lp->txbsz = atoi(buffered);
2578             lp->txbfd = 1;
2579             }
2580         lp->txbpi = lp->txbpr = 0;
2581         lp->txb = (char *)realloc (lp->txb, lp->txbsz);
2582         if (!lp->txb)
2583           {
2584             fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2585                      __func__, __FILE__, __LINE__);
2586 #if defined(USE_BACKTRACE)
2587 # ifdef SIGUSR2
2588             (void)raise(SIGUSR2);
2589             /*NOTREACHED*/ /* unreachable */
2590 # endif /* ifdef SIGUSR2 */
2591 #endif /* if defined(USE_BACKTRACE) */
2592             abort();
2593           }
2594         lp->rxb = (char *)realloc (lp->rxb, lp->rxbsz);
2595         if (!lp->rxb)
2596           {
2597             fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2598                      __func__, __FILE__, __LINE__);
2599 #if defined(USE_BACKTRACE)
2600 # ifdef SIGUSR2
2601             (void)raise(SIGUSR2);
2602             /*NOTREACHED*/ /* unreachable */
2603 # endif /* ifdef SIGUSR2 */
2604 #endif /* if defined(USE_BACKTRACE) */
2605             abort();
2606           }
2607         lp->rbr = (char *)realloc (lp->rbr, lp->rxbsz);
2608         if (!lp->rbr)
2609           {
2610             fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2611                      __func__, __FILE__, __LINE__);
2612 #if defined(USE_BACKTRACE)
2613 # ifdef SIGUSR2
2614             (void)raise(SIGUSR2);
2615             /*NOTREACHED*/ /* unreachable */
2616 # endif /* ifdef SIGUSR2 */
2617 #endif /* if defined(USE_BACKTRACE) */
2618             abort();
2619           }
2620         lp->packet = packet;
2621         if (nolog) {
2622             FREE(lp->txlogname);
2623             lp->txlogname = NULL;
2624             if (lp->txlog) {
2625                 sim_close_logfile (&lp->txlogref);
2626                 lp->txlog = NULL;
2627                 }
2628             }
2629         if ((listen[0]) && (!datagram)) {
2630             if ((mp->lines == 1) && (mp->master))
2631                 return sim_messagef (SCPE_ARG, "Single Line MUX can have either line specific OR MUS listener but NOT both\n");
2632             sock = sim_master_sock (listen, &r);            /* make master socket */
2633             if (r)
2634                 return sim_messagef (SCPE_ARG, "Invalid Listen Specification: %s\n", listen);
2635             if (sock == INVALID_SOCKET)                     /* open error */
2636                 return sim_messagef (SCPE_OPENERR, "Can't listen on port: %s\n", listen);
2637             _mux_detach_line (lp, TRUE, FALSE);
2638             sim_printf ("Line %d Listening on port %s\n", line, listen);
2639             lp->port = (char *)realloc (lp->port, 1 + strlen (listen));
2640             if (!lp->port)
2641               {
2642                 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2643                          __func__, __FILE__, __LINE__);
2644 #if defined(USE_BACKTRACE)
2645 # ifdef SIGUSR2
2646                 (void)raise(SIGUSR2);
2647                 /*NOTREACHED*/ /* unreachable */
2648 # endif /* ifdef SIGUSR2 */
2649 #endif /* if defined(USE_BACKTRACE) */
2650                 abort();
2651               }
2652             strcpy (lp->port, listen);                       /* save port */
2653             lp->master = sock;                              /* save master socket */
2654             if (listennotelnet != mp->notelnet)
2655                 lp->notelnet = listennotelnet;
2656             else
2657                 lp->notelnet = mp->notelnet;
2658             }
2659         if (destination[0]) {
2660                 lp->datagram = datagram;
2661                 if (datagram) {
2662                     if (listen[0]) {
2663                         lp->port = (char *)realloc (lp->port, 1 + strlen (listen));
2664                         if (!lp->port)
2665                           {
2666                             fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2667                                      __func__, __FILE__, __LINE__);
2668 #if defined(USE_BACKTRACE)
2669 # ifdef SIGUSR2
2670                             (void)raise(SIGUSR2);
2671                             /*NOTREACHED*/ /* unreachable */
2672 # endif /* ifdef SIGUSR2 */
2673 #endif /* if defined(USE_BACKTRACE) */
2674                             abort();
2675                           }
2676                         strcpy (lp->port, listen);          /* save port */
2677                         }
2678                     else
2679                         return sim_messagef (SCPE_ARG, "Missing listen port for Datagram socket\n");
2680                     }
2681                 sock = sim_connect_sock_ex (datagram ? listen : NULL, hostport, "localhost", NULL, (datagram ? SIM_SOCK_OPT_DATAGRAM : 0) | (packet ? SIM_SOCK_OPT_NODELAY : 0));
2682                 if (sock != INVALID_SOCKET) {
2683                     _mux_detach_line (lp, FALSE, TRUE);
2684                     lp->destination = (char *)malloc(1+strlen(hostport));
2685                     if (!lp->destination)
2686                       {
2687                         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2688                                  __func__, __FILE__, __LINE__);
2689 #if defined(USE_BACKTRACE)
2690 # ifdef SIGUSR2
2691                         (void)raise(SIGUSR2);
2692                         /*NOTREACHED*/ /* unreachable */
2693 # endif /* ifdef SIGUSR2 */
2694 #endif /* if defined(USE_BACKTRACE) */
2695                         abort();
2696                       }
2697                     strcpy (lp->destination, hostport);
2698                     if (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR)) {
2699                         lp->connecting = sock;
2700                         lp->ipad = (char *)malloc (1 + strlen (lp->destination));
2701                         if (!lp->ipad)
2702                           {
2703                             fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2704                                      __func__, __FILE__, __LINE__);
2705 #if defined(USE_BACKTRACE)
2706 # ifdef SIGUSR2
2707                             (void)raise(SIGUSR2);
2708                             /*NOTREACHED*/ /* unreachable */
2709 # endif /* ifdef SIGUSR2 */
2710 #endif /* if defined(USE_BACKTRACE) */
2711                             abort();
2712                           }
2713                         strcpy (lp->ipad, lp->destination);
2714                         }
2715                     else
2716                         sim_close_sock (sock);
2717                     lp->notelnet = notelnet;
2718                     tmxr_init_line (lp);                    /* init the line state */
2719                     }
2720                 else
2721                     return sim_messagef (SCPE_ARG, "Can't open %s socket on %s%s%s\n", datagram ? "Datagram" : "Stream", datagram ? listen : "", datagram ? "<->" : "", hostport);
2722                 }
2723             }
2724         if (loopback) {
2725             if (lp != NULL) {
2726               tmxr_set_line_loopback (lp, loopback);
2727               sim_printf ("Line %d operating in loopback mode\n", line);
2728               }
2729             }
2730         if (lp != NULL) lp->modem_control = modem_control;
2731         if (speed[0] && (!datagram)
2732         )
2733             tmxr_set_line_speed (lp, speed);
2734         r = SCPE_OK;
2735         }
2736 if (r == SCPE_OK)
2737     tmxr_add_to_open_list (mp);
2738 return r;
2739 }
2740 
2741 /* Declare which unit polls for input
2742 
2743    Inputs:
2744         *mp     =       the mux
2745         line    =       the line number
2746         *uptr_poll =    the unit which polls
2747 
2748    Outputs:
2749         none
2750 
2751    Implementation note:
2752 
2753         Only devices which poll on a unit different from the unit provided
2754         at MUX attach time need call this function.  Calling this API is
2755         necessary for asynchronous multiplexer support and unnecessary
2756         otherwise.
2757 
2758 */
2759 
2760 t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll)
     /* [previous][next][first][last][top][bottom][index][help] */
2761 {
2762 if ((line < 0) || (line >= mp->lines))
2763     return SCPE_ARG;
2764 mp->ldsc[line].uptr = uptr_poll;
2765 return SCPE_OK;
2766 }
2767 
2768 /* Declare which unit polls for output
2769 
2770    Inputs:
2771         *mp     =       the mux
2772         line    =       the line number
2773         *uptr_poll =    the unit which polls for output
2774 
2775    Outputs:
2776         none
2777 
2778    Implementation note:
2779 
2780         Only devices which poll on a unit different from the unit provided
2781         at MUX attach time need call this function ABD different from the
2782         unit which polls for input.  Calling this API is necessary for
2783         asynchronous multiplexer support and unnecessary otherwise.
2784 
2785 */
2786 
2787 t_stat tmxr_set_line_output_unit (TMXR *mp, int line, UNIT *uptr_poll)
     /* [previous][next][first][last][top][bottom][index][help] */
2788 {
2789 if ((line < 0) || (line >= mp->lines))
2790     return SCPE_ARG;
2791 mp->ldsc[line].o_uptr = uptr_poll;
2792 return SCPE_OK;
2793 }
2794 
2795 static TMXR **tmxr_open_devices = NULL;
2796 static int tmxr_open_device_count = 0;
2797 
2798 t_stat tmxr_start_poll (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2799 {
2800 return SCPE_OK;
2801 }
2802 
2803 t_stat tmxr_stop_poll (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2804 {
2805 return SCPE_OK;
2806 }
2807 
2808 static void tmxr_add_to_open_list (TMXR* mux)
     /* [previous][next][first][last][top][bottom][index][help] */
2809 {
2810 int i;
2811 t_bool found = FALSE;
2812 
2813 for (i=0; i<tmxr_open_device_count; ++i)
2814     if (tmxr_open_devices[i] == mux) {
2815         found = TRUE;
2816         break;
2817         }
2818 if (!found) {
2819     tmxr_open_devices = (TMXR **)realloc(tmxr_open_devices, (tmxr_open_device_count+1)*sizeof(*tmxr_open_devices));
2820     if (!tmxr_open_devices)
2821       {
2822         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2823                  __func__, __FILE__, __LINE__);
2824 #if defined(USE_BACKTRACE)
2825 # ifdef SIGUSR2
2826         (void)raise(SIGUSR2);
2827         /*NOTREACHED*/ /* unreachable */
2828 # endif /* ifdef SIGUSR2 */
2829 #endif /* if defined(USE_BACKTRACE) */
2830         abort();
2831       }
2832     tmxr_open_devices[tmxr_open_device_count++] = mux;
2833     for (i=0; i<mux->lines; i++)
2834         if (0 == mux->ldsc[i].send.delay)
2835             mux->ldsc[i].send.delay = SEND_DEFAULT_DELAY;
2836     }
2837 }
2838 
2839 static void _tmxr_remove_from_open_list (const TMXR* mux)
     /* [previous][next][first][last][top][bottom][index][help] */
2840 {
2841 int i, j;
2842 
2843 for (i=0; i<tmxr_open_device_count; ++i)
2844     if (tmxr_open_devices[i] == mux) {
2845         for (j=i+1; j<tmxr_open_device_count; ++j)
2846             tmxr_open_devices[j-1] = tmxr_open_devices[j];
2847         --tmxr_open_device_count;
2848         break;
2849         }
2850 }
2851 
2852 static t_stat _tmxr_locate_line_send_expect (const char *cptr, SEND **snd, EXPECT **exp)
     /* [previous][next][first][last][top][bottom][index][help] */
2853 {
2854 char gbuf[CBUFSIZE];
2855 DEVICE *dptr;
2856 int i;
2857 t_stat r;
2858 
2859 if (snd)
2860     *snd = NULL;
2861 if (exp)
2862     *exp = NULL;
2863 cptr = get_glyph(cptr, gbuf, ':');
2864 dptr = find_dev (gbuf);                 /* device match? */
2865 if (!dptr)
2866     return SCPE_ARG;
2867 
2868 for (i=0; i<tmxr_open_device_count; ++i)
2869     if (tmxr_open_devices[i]->dptr == dptr) {
2870         int line = (int)get_uint (cptr, 10, tmxr_open_devices[i]->lines, &r);
2871         if (r != SCPE_OK)
2872             return r;
2873         if (snd)
2874             *snd = &tmxr_open_devices[i]->ldsc[line].send;
2875         if (exp)
2876             *exp = &tmxr_open_devices[i]->ldsc[line].expect;
2877         return SCPE_OK;
2878         }
2879 return SCPE_ARG;
2880 }
2881 
2882 t_stat tmxr_locate_line_send (const char *cptr, SEND **snd)
     /* [previous][next][first][last][top][bottom][index][help] */
2883 {
2884 return _tmxr_locate_line_send_expect (cptr, snd, NULL);
2885 }
2886 
2887 t_stat tmxr_locate_line_expect (const char *cptr, EXPECT **exp)
     /* [previous][next][first][last][top][bottom][index][help] */
2888 {
2889 return _tmxr_locate_line_send_expect (cptr, NULL, exp);
2890 }
2891 
2892 t_stat tmxr_change_async (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2893 {
2894 return SCPE_OK;
2895 }
2896 
2897 /* Attach unit to master socket */
2898 
2899 t_stat tmxr_attach_ex (TMXR *mp, UNIT *uptr, CONST char *cptr, t_bool async)
     /* [previous][next][first][last][top][bottom][index][help] */
2900 {
2901 t_stat r;
2902 int32 i;
2903 
2904 r = tmxr_open_master (mp, cptr);                        /* open master socket */
2905 if (r != SCPE_OK)                                       /* error? */
2906     return r;
2907 mp->uptr = uptr;                                        /* save unit for polling */
2908 uptr->filename = tmxr_mux_attach_string (uptr->filename, mp);/* save */
2909 uptr->flags = uptr->flags | UNIT_ATT;                   /* no more errors */
2910 uptr->tmxr = (void *)mp;
2911 if ((mp->lines > 1) ||
2912     ((mp->master == 0) &&
2913      (mp->ldsc[0].connecting == 0)
2914 ))
2915     uptr->dynflags = uptr->dynflags | UNIT_ATTMULT;     /* allow multiple attach commands */
2916 
2917 uptr->dynflags |= TMUF_NOASYNCH;                        /* tag as no asynch */
2918 
2919 if (mp->dptr == NULL)                                   /* has device been set? */
2920     mp->dptr = find_dev_from_unit (uptr);               /* no, so set device now */
2921 
2922 if (mp->dptr) {
2923     for (i=0; i<mp->lines; i++) {
2924         mp->ldsc[i].expect.dptr = mp->dptr;
2925         mp->ldsc[i].expect.dbit = TMXR_DBG_EXP;
2926         mp->ldsc[i].send.dptr   = mp->dptr;
2927         mp->ldsc[i].send.dbit   = TMXR_DBG_SEND;
2928         }
2929     }
2930 tmxr_add_to_open_list (mp);
2931 return SCPE_OK;
2932 }
2933 
2934 t_stat tmxr_startup (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2935 {
2936 return SCPE_OK;
2937 }
2938 
2939 t_stat tmxr_shutdown (void)
     /* [previous][next][first][last][top][bottom][index][help] */
2940 {
2941 if (tmxr_open_device_count)
2942     return SCPE_IERR;
2943 return SCPE_OK;
2944 }
2945 
2946 t_stat tmxr_show_open_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc)
     /* [previous][next][first][last][top][bottom][index][help] */
2947 {
2948 int i, j;
2949 
2950 if (0 == tmxr_open_device_count)
2951     fprintf(st, "No Attached Multiplexer Devices\n");
2952 else {
2953     for (i=0; i<tmxr_open_device_count; ++i) {
2954         TMXR *mp = tmxr_open_devices[i];
2955         TMLN *lp;
2956         char *attach;
2957 
2958         fprintf(st, "Multiplexer device: %s", (mp->dptr ? sim_dname (mp->dptr) : ""));
2959         if (mp->lines > 1) {
2960             fprintf(st, ", ");
2961             tmxr_show_lines(st, NULL, 0, mp);
2962             }
2963         if (mp->packet)
2964             fprintf(st, ", Packet");
2965         if (mp->datagram)
2966             fprintf(st, ", UDP");
2967         if (mp->notelnet)
2968             fprintf(st, ", Telnet=disabled");
2969         if (mp->modem_control)
2970             fprintf(st, ", ModemControl=enabled");
2971         if (mp->buffered)
2972             fprintf(st, ", Buffered=%d", mp->buffered);
2973         attach = tmxr_mux_attach_string (NULL, mp);
2974         if (attach)
2975             fprintf(st, ",\n    attached to %s, ", attach);
2976         FREE (attach);
2977         tmxr_show_summ(st, NULL, 0, mp);
2978         fprintf(st, ", sessions=%d", mp->sessions);
2979         if (mp->lines == 1) {
2980             if (mp->ldsc->rxbps) {
2981                 fprintf(st, ", Speed=%lu", (unsigned long)mp->ldsc->rxbps);
2982                 if (mp->ldsc->rxbpsfactor != TMXR_RX_BPS_UNIT_SCALE)
2983                     fprintf(st, "*%.0f", mp->ldsc->rxbpsfactor/TMXR_RX_BPS_UNIT_SCALE);
2984                 fprintf(st, " bps");
2985                 }
2986             }
2987         fprintf(st, "\n");
2988         if (mp->ring_start_time) {
2989             fprintf (st, "    incoming Connection from: %s ringing for %lu milliseconds\n", mp->ring_ipad, (unsigned long)sim_os_msec () - (unsigned long)mp->ring_start_time);
2990             }
2991         for (j = 0; j < mp->lines; j++) {
2992             lp = mp->ldsc + j;
2993             if (mp->lines > 1) {
2994                 if (lp->dptr && (mp->dptr != lp->dptr))
2995                     fprintf (st, "Device: %s ", sim_dname(lp->dptr));
2996                 fprintf (st, "Line: %d", j);
2997                 if (mp->notelnet != lp->notelnet)
2998                     fprintf (st, " - %stelnet", lp->notelnet ? "no" : "");
2999                 if (lp->uptr && (lp->uptr != lp->mp->uptr))
3000                     fprintf (st, " - Unit: %s", sim_uname (lp->uptr));
3001                 if (mp->modem_control != lp->modem_control)
3002                     fprintf(st, ", ModemControl=%s", lp->modem_control ? "enabled" : "disabled");
3003                 if (lp->loopback)
3004                     fprintf(st, ", Loopback");
3005                 if (lp->rxbps) {
3006                     fprintf(st, ", Speed=%lu", (unsigned long)lp->rxbps);
3007                     if (lp->rxbpsfactor != TMXR_RX_BPS_UNIT_SCALE)
3008                         fprintf(st, "*%.0f", lp->rxbpsfactor/TMXR_RX_BPS_UNIT_SCALE);
3009                     fprintf(st, " bps");
3010                     }
3011                 fprintf (st, "\n");
3012                 }
3013             if ((!lp->sock) && (!lp->connecting)
3014             && (!lp->master)) {
3015                 if (lp->modem_control)
3016                     tmxr_fconns (st, lp, -1);
3017                 continue;
3018                 }
3019             tmxr_fconns (st, lp, -1);
3020             tmxr_fstats (st, lp, -1);
3021             }
3022         }
3023     }
3024 return SCPE_OK;
3025 }
3026 
3027 /* Close a master listening socket.
3028 
3029    The listening socket associated with multiplexer descriptor "mp" is closed
3030    and deallocated.  In addition, all current Telnet sessions are disconnected.
3031    Serial and outgoing sessions are also disconnected.
3032 */
3033 
3034 t_stat tmxr_close_master (TMXR *mp)
     /* [previous][next][first][last][top][bottom][index][help] */
3035 {
3036 int32 i;
3037 TMLN *lp;
3038 
3039 for (i = 0; i < mp->lines; i++) {  /* loop thru conn */
3040     lp = mp->ldsc + i;
3041 
3042     if (!lp->destination && lp->sock) {                 /* not serial and is connected? */
3043         tmxr_report_disconnection (lp);                 /* report disconnection */
3044         tmxr_reset_ln (lp);                             /* disconnect line */
3045         }
3046     else {
3047         if (lp->sock) {
3048             tmxr_report_disconnection (lp);             /* report disconnection */
3049             tmxr_reset_ln (lp);
3050             }
3051         FREE (lp->destination);
3052         lp->destination = NULL;
3053         if (lp->connecting) {
3054             lp->sock = lp->connecting;
3055             lp->connecting = 0;
3056             tmxr_reset_ln (lp);
3057             }
3058         lp->conn = FALSE;
3059         }
3060     if (lp->master) {
3061         sim_close_sock (lp->master);                    /* close master socket */
3062         lp->master = 0;
3063         FREE (lp->port);
3064         lp->port = NULL;
3065         }
3066     lp->txbfd = 0;
3067     FREE (lp->txb);
3068     lp->txb = NULL;
3069     FREE (lp->rxb);
3070     lp->rxb = NULL;
3071     FREE (lp->rbr);
3072     lp->rbr = NULL;
3073     lp->modembits = 0;
3074     }
3075 
3076 if (mp->master)
3077     sim_close_sock (mp->master);                        /* close master socket */
3078 mp->master = 0;
3079 FREE (mp->port);
3080 mp->port = NULL;
3081 if (mp->ring_sock != INVALID_SOCKET) {
3082     sim_close_sock (mp->ring_sock);
3083     mp->ring_sock = INVALID_SOCKET;
3084     FREE (mp->ring_ipad);
3085     mp->ring_ipad = NULL;
3086     mp->ring_start_time = 0;
3087     }
3088 _tmxr_remove_from_open_list (mp);
3089 return SCPE_OK;
3090 }
3091 
3092 /* Detach unit from master socket and close all active network connections.
3093    Note that we return SCPE_OK, regardless of whether a listening socket was
3094    attached.
3095 */
3096 
3097 t_stat tmxr_detach (TMXR *mp, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3098 {
3099 int32 i;
3100 
3101 if (!(uptr->flags & UNIT_ATT))                          /* attached? */
3102     return SCPE_OK;
3103 tmxr_close_master (mp);                                 /* close master socket */
3104 FREE (uptr->filename);                                  /* free setup string */
3105 uptr->filename = NULL;
3106 uptr->tmxr = NULL;
3107 mp->last_poll_time = 0;
3108 for (i=0; i < mp->lines; i++) {
3109     UNIT *uptr = mp->ldsc[i].uptr ? mp->ldsc[i].uptr : mp->uptr;
3110     UNIT *o_uptr = mp->ldsc[i].o_uptr ? mp->ldsc[i].o_uptr : mp->uptr;
3111 
3112     uptr->dynflags &= ~UNIT_TM_POLL;                    /* no polling */
3113     o_uptr->dynflags &= ~UNIT_TM_POLL;                  /* no polling */
3114     }
3115 uptr->flags &= ~(UNIT_ATT);                             /* not attached */
3116 uptr->dynflags &= ~(UNIT_TM_POLL|TMUF_NOASYNCH);        /* no polling, not asynch disabled  */
3117 return SCPE_OK;
3118 }
3119 
3120 t_stat tmxr_activate (UNIT *uptr, int32 interval)
     /* [previous][next][first][last][top][bottom][index][help] */
3121 {
3122 if (uptr->dynflags & UNIT_TMR_UNIT)
3123     return sim_timer_activate (uptr, interval);
3124 return _sim_activate (uptr, interval);
3125 }
3126 
3127 t_stat tmxr_activate_after (UNIT *uptr, uint32 usecs_walltime)
     /* [previous][next][first][last][top][bottom][index][help] */
3128 {
3129 return _sim_activate_after (uptr, usecs_walltime);
3130 }
3131 
3132 /* Generic Multiplexer attach help */
3133 
3134 t_stat tmxr_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3135 {
3136 TMXR *mux = (TMXR *)dptr->help_ctx;
3137 t_bool single_line = FALSE;               /* default to Multi-Line help */
3138 
3139 if (mux)
3140    single_line = (mux->lines == 1);
3141 
3142 if (!flag)
3143     fprintf (st, "%s Multiplexer Attach Help\n\n", dptr->name);
3144 if (single_line) {          /* Single Line Multiplexer */
3145     fprintf (st, "The %s multiplexer may be connected to terminal emulators supporting the\n", dptr->name);
3146     fprintf (st, "Telnet protocol via sockets.\n\n");
3147     if (mux->modem_control) {
3148         fprintf (st, "The %s device is a full modem control device and therefore is capable of\n", dptr->name);
3149         fprintf (st, "passing port configuration information and modem signals.\n");
3150         }
3151     fprintf (st, "A Telnet listening port can be configured with:\n\n");
3152     fprintf (st, "   sim> ATTACH %s {interface:}port\n\n", dptr->name);
3153     fprintf (st, "Line buffering can be enabled for the %s device with:\n\n", dptr->name);
3154     fprintf (st, "   sim> ATTACH %s Buffer{=bufsize}\n\n", dptr->name);
3155     fprintf (st, "Line buffering can be disabled for the %s device with:\n\n", dptr->name);
3156     fprintf (st, "   sim> ATTACH %s NoBuffer\n\n", dptr->name);
3157     fprintf (st, "The default buffer size is 32k bytes, the max buffer size is 1024k bytes\n\n");
3158     fprintf (st, "The outbound traffic the %s device can be logged to a file with:\n", dptr->name);
3159     fprintf (st, "   sim> ATTACH %s Log=LogFileName\n\n", dptr->name);
3160     fprintf (st, "File logging can be disabled for the %s device with:\n\n", dptr->name);
3161     fprintf (st, "   sim> ATTACH %s NoLog\n\n", dptr->name);
3162     fprintf (st, "The %s device may be connected to a serial port on the host system.\n", dptr->name);
3163     }
3164 else {
3165     fprintf (st, "%s multiplexer lines may be connected to terminal emulators supporting the\n", dptr->name);
3166     fprintf (st, "Telnet protocol via sockets, or to hardware terminals via host serial\n");
3167     fprintf (st, "ports.  Concurrent Telnet and serial connections may be mixed on a given\n");
3168     fprintf (st, "multiplexer.\n\n");
3169     if (mux && mux->modem_control) {
3170         fprintf (st, "The %s device is a full modem control device and therefore is capable of\n", dptr->name);
3171         fprintf (st, "passing port configuration information and modem signals on all lines.\n");
3172         }
3173     fprintf (st, "Modem Control signalling behaviors can be enabled/disabled on a specific\n");
3174     fprintf (st, "multiplexer line with:\n\n");
3175     fprintf (st, "   sim> ATTACH %s Line=n,Modem\n", dptr->name);
3176     fprintf (st, "   sim> ATTACH %s Line=n,NoModem\n\n", dptr->name);
3177     fprintf (st, "A Telnet listening port can be configured with:\n\n");
3178     fprintf (st, "   sim> ATTACH %s {interface:}port\n\n", dptr->name);
3179     if (mux)
3180         fprintf (st, "Line buffering for all %d lines on the %s device can be configured with:\n\n", mux->lines, dptr->name);
3181     else
3182         fprintf (st, "Line buffering for all lines on the %s device can be configured with:\n\n", dptr->name);
3183     fprintf (st, "   sim> ATTACH %s Buffer{=bufsize}\n\n", dptr->name);
3184     if (mux)
3185         fprintf (st, "Line buffering for all %d lines on the %s device can be disabled with:\n\n", mux->lines, dptr->name);
3186     else
3187         fprintf (st, "Line buffering for all lines on the %s device can be disabled with:\n\n", dptr->name);
3188     fprintf (st, "   sim> ATTACH %s NoBuffer\n\n", dptr->name);
3189     fprintf (st, "The default buffer size is 32k bytes, the max buffer size is 1024k bytes\n\n");
3190     fprintf (st, "The outbound traffic for the lines of the %s device can be logged to files\n", dptr->name);
3191     fprintf (st, "with:\n\n");
3192     fprintf (st, "   sim> ATTACH %s Log=LogFileName\n\n", dptr->name);
3193     fprintf (st, "The log file name for each line uses the above LogFileName as a template\n");
3194     fprintf (st, "for the actual file name which will be LogFileName_n where n is the line\n");
3195     fprintf (st, "number.\n\n");
3196     fprintf (st, "Multiplexer lines may be connected to serial ports on the host system.\n");
3197     }
3198 fprintf (st, "Serial ports may be specified as an operating system specific device names\n");
3199 fprintf (st, "or using simh generic serial names.  simh generic names are of the form\n");
3200 fprintf (st, "serN, where N is from 0 thru one less than the maximum number of serial\n");
3201 fprintf (st, "ports on the local system.  The mapping of simh generic port names to OS \n");
3202 fprintf (st, "specific names can be displayed using the following command:\n\n");
3203 fprintf (st, "   sim> SHOW SERIAL\n");
3204 fprintf (st, "   Serial devices:\n");
3205 fprintf (st, "    ser0   COM1 (\\Device\\Serial0)\n");
3206 fprintf (st, "    ser1   COM3 (Winachcf0)\n\n");
3207 if (single_line) {          /* Single Line Multiplexer */
3208     fprintf (st, "   sim> ATTACH %s Connect=ser0\n\n", dptr->name);
3209     fprintf (st, "or equivalently:\n\n");
3210     fprintf (st, "   sim> ATTACH %s Connect=COM1\n\n", dptr->name);
3211     }
3212 else {
3213     fprintf (st, "   sim> ATTACH %s Line=n,Connect=ser0\n\n", dptr->name);
3214     fprintf (st, "or equivalently:\n\n");
3215     fprintf (st, "   sim> ATTACH %s Line=n,Connect=COM1\n\n", dptr->name);
3216     if (mux)
3217         fprintf (st, "Valid line numbers are from 0 thru %d\n\n", mux->lines-1);
3218     }
3219 if (single_line) {          /* Single Line Multiplexer */
3220     fprintf (st, "The input data rate for the %s device can be controlled by\n", dptr->name);
3221     fprintf (st, "specifying SPEED=nnn{*fac} on the ATTACH command.\n");
3222     }
3223 else {
3224     fprintf (st, "The input data rate for all lines or a particular line of a the %s\n", dptr->name);
3225     fprintf (st, "device can be controlled by specifying SPEED=nnn{*fac} on the ATTACH command.\n");
3226     }
3227 fprintf (st, "SPEED values can be any one of:\n\n");
3228 fprintf (st, "    0 50 75 110 134 150 300 600 1200 1800 2000 2400\n");
3229 fprintf (st, "    3600 4800 7200 9600 19200 38400 57600 76800 115200\n\n");
3230 fprintf (st, "A SPEED value of 0 causes input data to be delivered to the simulated\n");
3231 fprintf (st, "port as fast as it arrives.\n\n");
3232 fprintf (st, "If a simulated multiplexor devices can programmatically set a serial\n");
3233 fprintf (st, "port line speed, the programmatically specified speed will take precedence\n");
3234 fprintf (st, "over any input speed specified on an attach command.\n");
3235 fprintf (st, "Some simulated systems run very much faster than the original system\n");
3236 fprintf (st, "which is being simulated.  To accommodate this, the speed specified may\n");
3237 fprintf (st, "include a factor which will increase the input data delivery rate by\n");
3238 fprintf (st, "the specified factor.  A factor is specified with a speed value of the\n");
3239 fprintf (st, "form \"speed*factor\".  Factor values can range from 1 thru 32.\n");
3240 fprintf (st, "Example:\n\n");
3241 fprintf (st, "   sim> ATTACH %s 1234,SPEED=2400\n", dptr->name);
3242 fprintf (st, "   sim> ATTACH %s 1234,SPEED=9600*8\n", dptr->name);
3243 if (!single_line)
3244     fprintf (st, "   sim> ATTACH %s Line=2,SPEED=2400\n", dptr->name);
3245 fprintf (st, "\n");
3246 fprintf (st, "The SPEED parameter only influences the rate at which data is delivered\n");
3247 fprintf (st, "into the simulated multiplexor port.  Output data rates are unaffected\n");
3248 fprintf (st, "If an attach command specifies a speed multiply factor, that value will\n");
3249 fprintf (st, "persist independent of any programmatic action by the simulated system to\n");
3250 fprintf (st, "change the port speed.\n\n");
3251 fprintf (st, "An optional serial port configuration string may be present after the port\n");
3252 fprintf (st, "name.  If present, it must be separated from the port name with a semicolon\n");
3253 fprintf (st, "and has this form:\n\n");
3254 fprintf (st, "   <rate>-<charsize><parity><stopbits>\n\n");
3255 fprintf (st, "where:\n");
3256 fprintf (st, "   rate     = communication rate in bits per second\n");
3257 fprintf (st, "   charsize = character size in bits (5-8, including optional parity)\n");
3258 fprintf (st, "   parity   = parity designator (N/E/O/M/S for no/even/odd/mark/space parity)\n");
3259 fprintf (st, "   stopbits = number of stop bits (1, 1.5, or 2)\n\n");
3260 fprintf (st, "As an example:\n\n");
3261 fprintf (st, "   9600-8n1\n\n");
3262 fprintf (st, "The supported rates, sizes, and parity options are host-specific.  If\n");
3263 fprintf (st, "a configuration string is not supplied, then the default of 9600-8N1\n");
3264 fprintf (st, "is used.\n");
3265 fprintf (st, "Note: The serial port configuration option is only available on multiplexer\n");
3266 fprintf (st, "      lines which are not operating with full modem control behaviors enabled.\n");
3267 fprintf (st, "      Lines with full modem control behaviors enabled have all of their\n");
3268 fprintf (st, "      configuration managed by the Operating System running within the\n");
3269 fprintf (st, "      simulator.\n\n");
3270 fprintf (st, "An attachment to a serial port with the '-V' switch will cause a\n");
3271 fprintf (st, "connection message to be output to the connected serial port.\n");
3272 fprintf (st, "This will help to confirm the correct port has been connected and\n");
3273 fprintf (st, "that the port settings are reasonable for the connected device.\n");
3274 fprintf (st, "This would be done as:\n\n");
3275 if (single_line)            /* Single Line Multiplexer */
3276     fprintf (st, "   sim> ATTACH -V %s Connect=SerN\n", dptr->name);
3277 else {
3278     fprintf (st, "   sim> ATTACH -V %s Line=n,Connect=SerN\n\n", dptr->name);
3279     fprintf (st, "Line specific tcp listening ports are supported.  These are configured\n");
3280     fprintf (st, "using commands of the form:\n\n");
3281     fprintf (st, "   sim> ATTACH %s Line=n,{interface:}port{;notelnet}\n\n", dptr->name);
3282     }
3283 fprintf (st, "Direct computer to computer connections (Virtual Null Modem cables) may\n");
3284 fprintf (st, "be established using the telnet protocol or via raw tcp sockets.\n\n");
3285 fprintf (st, "   sim> ATTACH %s Line=n,Connect=host:port{;notelnet}\n\n", dptr->name);
3286 fprintf (st, "Computer to computer virtual connections can be one way (as illustrated\n");
3287 fprintf (st, "above) or symmetric.  A symmetric connection is configured by combining\n");
3288 if (single_line) {          /* Single Line Multiplexer */
3289     fprintf (st, "a one way connection with a tcp listening port on the same line:\n\n");
3290     fprintf (st, "   sim> ATTACH %s listenport,Connect=host:port\n\n", dptr->name);
3291     }
3292 else {
3293     fprintf (st, "a one way connection with a tcp listening port on the same line:\n\n");
3294     fprintf (st, "   sim> ATTACH %s Line=n,listenport,Connect=host:port\n\n", dptr->name);
3295     }
3296 fprintf (st, "When symmetric virtual connections are configured, incoming connections\n");
3297 fprintf (st, "on the specified listening port are checked to assure that they actually\n");
3298 fprintf (st, "come from the specified connection destination host system.\n\n");
3299 if (single_line) {          /* Single Line Multiplexer */
3300     fprintf (st, "The %s device can be attached in LOOPBACK mode:\n\n", dptr->name);
3301     fprintf (st, "   sim> ATTACH %s Loopback\n\n", dptr->name);
3302     }
3303 else {
3304     fprintf (st, "A line on the %s device can be attached in LOOPBACK mode:\n\n", dptr->name);
3305     fprintf (st, "   sim> ATTACH %s Line=n,Loopback\n\n", dptr->name);
3306     }
3307 fprintf (st, "When operating in LOOPBACK mode, all outgoing data arrives as input and\n");
3308 fprintf (st, "outgoing modem signals (if enabled) (DTR and RTS) are reflected in the\n");
3309 fprintf (st, "incoming modem signals (DTR->(DCD and DSR), RTS->CTS)\n\n");
3310 if (single_line)            /* Single Line Multiplexer */
3311     fprintf (st, "The connection configured for the %s device is unconfigured by:\n\n", dptr->name);
3312 else
3313     fprintf (st, "All connections configured for the %s device are unconfigured by:\n\n", dptr->name);
3314 fprintf (st, "   sim> DETACH %s\n\n", dptr->name);
3315 if (dptr->modifiers) {
3316     MTAB *mptr;
3317 
3318     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++)
3319         if (mptr->valid == &tmxr_dscln) {
3320             fprintf (st, "A specific line on the %s device can be disconnected with:\n\n", dptr->name);
3321             fprintf (st, "   sim> SET %s %s=n\n\n", dptr->name, mptr->mstring);
3322             fprintf (st, "This will cause a telnet connection to be closed, but a serial port will\n");
3323             fprintf (st, "normally have DTR dropped for 500ms and raised again (thus hanging up a\n");
3324             fprintf (st, "modem on that serial port).\n\n");
3325             fprintf (st, "A line which is connected to a serial port can be manually closed by\n");
3326             fprintf (st, "adding the -C switch to a %s command.\n\n", mptr->mstring);
3327             fprintf (st, "   sim> SET -C %s %s=n\n\n", dptr->name, mptr->mstring);
3328             }
3329     }
3330 return SCPE_OK;
3331 }
3332 
3333 /* Stub examine and deposit */
3334 
3335 t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
3336 {
3337 return SCPE_NOFNC;
3338 }
3339 
3340 t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
3341 {
3342 return SCPE_NOFNC;
3343 }
3344 
3345 /* Write a message directly to a socket */
3346 
3347 void tmxr_msg (SOCKET sock, const char *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
3348 {
3349 if ((sock) && (sock != INVALID_SOCKET))
3350     sim_write_sock (sock, msg, (int32)strlen (msg));
3351 return;
3352 }
3353 
3354 /* Write a message to a line */
3355 
3356 void tmxr_linemsg (TMLN *lp, const char *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
3357 {
3358 while (*msg) {
3359     while (SCPE_STALL == tmxr_putc_ln (lp, (int32)(*msg)))
3360         if (lp->txbsz == tmxr_send_buffered_data (lp))
3361             sim_os_ms_sleep (10);
3362     ++msg;
3363     }
3364 return;
3365 }
3366 
3367 /* Write a formatted message to a line */
3368 
3369 void tmxr_linemsgf (TMLN *lp, const char *fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
3370 {
3371 va_list arglist;
3372 
3373 va_start (arglist, fmt);
3374 tmxr_linemsgvf (lp, fmt, arglist);
3375 va_end (arglist);
3376 }
3377 
3378 void tmxr_linemsgvf (TMLN *lp, const char *fmt, va_list arglist)
     /* [previous][next][first][last][top][bottom][index][help] */
3379 {
3380 char stackbuf[STACKBUFSIZE];
3381 int32 bufsize = sizeof(stackbuf);
3382 char *buf = stackbuf;
3383 int32 i, len;
3384 
3385 buf[bufsize-1] = '\0';
3386 while (1) {                                         /* format passed string, args */
3387     len = vsnprintf (buf, bufsize-1, fmt, arglist);
3388 
3389 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
3390 
3391     if ((len < 0) || (len >= bufsize-1)) {
3392         if (buf != stackbuf)
3393             FREE (buf);
3394         bufsize = bufsize * 2;
3395         if (bufsize < len + 2)
3396             bufsize = len + 2;
3397         buf = (char *) malloc (bufsize);
3398         if (!buf)                            /* out of memory */
3399           {
3400             fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
3401                     __func__, __FILE__, __LINE__);
3402 #if defined(USE_BACKTRACE)
3403 # ifdef SIGUSR2
3404             (void)raise(SIGUSR2);
3405             /*NOTREACHED*/ /* unreachable */
3406 # endif /* ifdef SIGUSR2 */
3407 #endif /* if defined(USE_BACKTRACE) */
3408             abort();
3409           }
3410         buf[bufsize-1] = '\0';
3411         continue;
3412         }
3413     break;
3414     }
3415 
3416 /* Output the formatted data expanding newlines where they exist */
3417 
3418 for (i = 0; i < len; ++i) {
3419     if (('\n' == buf[i]) && ((i == 0) || ('\r' != buf[i-1]))) {
3420         while (SCPE_STALL == tmxr_putc_ln (lp, '\r'))
3421             if (lp->txbsz == tmxr_send_buffered_data (lp))
3422                 sim_os_ms_sleep (10);
3423         }
3424     while (SCPE_STALL == tmxr_putc_ln (lp, buf[i]))
3425         if (lp->txbsz == tmxr_send_buffered_data (lp))
3426             sim_os_ms_sleep (10);
3427     }
3428 if (buf != stackbuf)
3429     FREE (buf);
3430 return;
3431 }
3432 
3433 /* Print connections - used only in named SHOW command */
3434 
3435 void tmxr_fconns (FILE *st, const TMLN *lp, int32 ln)
     /* [previous][next][first][last][top][bottom][index][help] */
3436 {
3437 int32 hr, mn, sc;
3438 uint32 tctime;
3439 
3440 if (ln >= 0)
3441     fprintf (st, "line %d: ", ln);
3442 
3443 if ((lp->sock) || (lp->connecting)) {                   /* tcp connection? */
3444     if (lp->destination)                                /* remote connection? */
3445         if (lp->datagram)
3446             fprintf (st, "Datagram Connection from %s to remote port %s\n", lp->port, lp->destination);/* print port name */
3447         else
3448             fprintf (st, "Connection to remote port %s\n", lp->destination);/* print port name */
3449     else                                                /* incoming connection */
3450         fprintf (st, "Connection from IP address %s\n", lp->ipad);
3451     }
3452 else
3453     if (lp->destination)                                /* remote connection? */
3454         fprintf (st, "Connecting to remote port %s\n", lp->destination);/* print port name */
3455 if (lp->sock) {
3456     char *sockname, *peername;
3457 
3458     sim_getnames_sock (lp->sock, &sockname, &peername);
3459     fprintf (st, "Connection %s->%s\n", sockname, peername);
3460     FREE (sockname);
3461     FREE (peername);
3462     }
3463 
3464 if ((lp->port) && (!lp->datagram))
3465     fprintf (st, "Listening on port %s\n", lp->port);   /* print port name */
3466 if (lp->cnms) {
3467     tctime = (sim_os_msec () - lp->cnms) / 1000;
3468     hr = tctime / 3600;
3469     mn = (tctime / 60) % 60;
3470     sc = tctime % 60;
3471     if (tctime)
3472         fprintf (st, " %s %02d:%02d:%02d\n", lp->connecting ? "Connecting for" : "Connected", hr, mn, sc);
3473     }
3474 else
3475     fprintf (st, " Line disconnected\n");
3476 
3477 if (lp->modem_control) {
3478     fprintf (st, " Modem Bits: %s%s%s%s%s%s\n", (lp->modembits & TMXR_MDM_DTR) ? "DTR " : "",
3479                                                 (lp->modembits & TMXR_MDM_RTS) ? "RTS " : "",
3480                                                 (lp->modembits & TMXR_MDM_DCD) ? "DCD " : "",
3481                                                 (lp->modembits & TMXR_MDM_RNG) ? "RNG " : "",
3482                                                 (lp->modembits & TMXR_MDM_CTS) ? "CTS " : "",
3483                                                 (lp->modembits & TMXR_MDM_DSR) ? "DSR " : "");
3484     }
3485 
3486 if (
3487   (lp->sock) && (!lp->datagram))
3488     fprintf (st, " %s\n", (lp->notelnet) ? "Telnet disabled (RAW data)" : "Telnet protocol");
3489 if (lp->send.buffer)
3490     sim_show_send_input (st, &lp->send);
3491 if (lp->expect.buf)
3492     sim_exp_showall (st, &lp->expect);
3493 if (lp->txlog)
3494     fprintf (st, " Logging to %s\n", lp->txlogname);
3495 return;
3496 }
3497 
3498 /* Print statistics - used only in named SHOW command */
3499 
3500 void tmxr_fstats (FILE *st, const TMLN *lp, int32 ln)
     /* [previous][next][first][last][top][bottom][index][help] */
3501 {
3502 static const char *enab = "on";
3503 static const char *dsab = "off";
3504 
3505 if (ln >= 0)
3506     fprintf (st, "Line %d:", ln);
3507 if ((!lp->sock) && (!lp->connecting)
3508    )
3509     fprintf (st, " not connected\n");
3510 else {
3511     if (ln >= 0)
3512         fprintf (st, "\n");
3513     fprintf (st, "  input (%s)", (lp->rcve? enab: dsab));
3514     if (lp->rxcnt)
3515         fprintf (st, " queued/total = %d/%d", tmxr_rqln (lp), lp->rxcnt);
3516     if (lp->rxpcnt)
3517         fprintf (st, " packets = %d", lp->rxpcnt);
3518     fprintf (st, "\n  output (%s)", (lp->xmte? enab: dsab));
3519     if (lp->txcnt || lp->txbpi)
3520         fprintf (st, " queued/total = %d/%d", tmxr_tqln (lp), lp->txcnt);
3521     if (lp->txpcnt || tmxr_tpqln (lp))
3522         fprintf (st, " packet data queued/packets sent = %d/%d",
3523             tmxr_tpqln (lp), lp->txpcnt);
3524     fprintf (st, "\n");
3525     }
3526 if (lp->txbfd)
3527     fprintf (st, "  output buffer size = %d\n", lp->txbsz);
3528 if (lp->txcnt || lp->txbpi)
3529     fprintf (st, "  bytes in buffer = %d\n",
3530                ((lp->txcnt > 0) && (lp->txcnt > lp->txbsz)) ? lp->txbsz : lp->txbpi);
3531 if (lp->txdrp)
3532     fprintf (st, "  dropped = %d\n", lp->txdrp);
3533 return;
3534 }
3535 
3536 /* Disconnect a line.
3537 
3538    Disconnect a line of the multiplexer associated with descriptor "desc" from a
3539    tcp session or a serial port.  Two calling sequences are supported:
3540 
3541     1. If "val" is zero, then "uptr" is implicitly associated with the line
3542        number corresponding to the position of the unit in the zero-based array
3543        of units belonging to the associated device, and "cptr" is ignored.  For
3544        example, if "uptr" points to unit 3 in a given device, then line 3 will
3545        be disconnected.
3546 
3547     2. If "val" is non-zero, then "cptr" points to a string that is parsed for
3548        an explicit line number, and "uptr" is ignored.  For example, if "cptr"
3549        points to the string "3", then line 3 will be disconnected.
3550 
3551    If the line was connected to a tcp session, the socket associated with the
3552    line will be closed.  If the line was connected to a serial port, the port
3553    will NOT be closed, but DTR will be dropped.  After a 500ms delay DTR will
3554    be raised again.  If the sim_switches -C flag is set, then a serial port
3555    connection will be closed.
3556 
3557    Implementation notes:
3558 
3559     1. This function is usually called as an MTAB processing routine.
3560 */
3561 
3562 t_stat tmxr_dscln (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3563 {
3564 TMXR *mp = (TMXR *) desc;
3565 TMLN *lp;
3566 t_stat status;
3567 
3568 if (val)                                                        /* explicit line? */
3569     uptr = NULL;                                                /* indicate to get routine */
3570 
3571 lp = tmxr_get_ldsc (uptr, cptr, mp, &status);                   /* get referenced line */
3572 
3573 if (lp == NULL)                                                 /* bad line number? */
3574     return status;                                              /* report it */
3575 
3576 if ((lp->sock)
3577    ) {                                                          /* connection active? */
3578     if (!lp->notelnet)
3579         tmxr_linemsg (lp, "\r\nOperator disconnected line\r\n\n");/* report closure */
3580     tmxr_reset_ln_ex (lp, (sim_switches & SWMASK ('C')));       /* drop the line */
3581     }
3582 
3583 return SCPE_OK;
3584 }
3585 
3586 /* Enable logging for line */
3587 
3588 t_stat tmxr_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3589 {
3590 TMXR *mp = (TMXR *) desc;
3591 TMLN *lp;
3592 
3593 if (cptr == NULL)                                       /* no file name? */
3594     return SCPE_2FARG;
3595 lp = tmxr_find_ldsc (uptr, val, mp);                    /* find line desc */
3596 if (lp == NULL)
3597     return SCPE_IERR;
3598 if (lp->txlog)                                          /* close existing log */
3599     tmxr_set_nolog (NULL, val, NULL, desc);
3600 lp->txlogname = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc namebuf */
3601 if (lp->txlogname == NULL)                              /* can't? */
3602     return SCPE_MEM;
3603 strncpy (lp->txlogname, cptr, CBUFSIZE-1);              /* save file name */
3604 sim_open_logfile (cptr, TRUE, &lp->txlog, &lp->txlogref);/* open log */
3605 if (lp->txlog == NULL) {                                /* error? */
3606     FREE (lp->txlogname);                               /* free buffer */
3607     return SCPE_OPENERR;
3608     }
3609 if (mp->uptr)                                           /* attached?, then update attach string */
3610     lp->mp->uptr->filename = tmxr_mux_attach_string (lp->mp->uptr->filename, lp->mp);
3611 return SCPE_OK;
3612 }
3613 
3614 /* Disable logging for line */
3615 
3616 t_stat tmxr_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3617 {
3618 TMXR *mp = (TMXR *) desc;
3619 TMLN *lp;
3620 
3621 if (cptr)                                               /* no arguments */
3622     return SCPE_2MARG;
3623 lp = tmxr_find_ldsc (uptr, val, mp);                    /* find line desc */
3624 if (lp == NULL)
3625     return SCPE_IERR;
3626 if (lp->txlog) {                                        /* logging? */
3627     sim_close_logfile (&lp->txlogref);                  /* close log */
3628     FREE (lp->txlogname);                               /* free namebuf */
3629     lp->txlog = NULL;
3630     lp->txlogname = NULL;
3631     }
3632 if (mp->uptr)
3633     lp->mp->uptr->filename = tmxr_mux_attach_string (lp->mp->uptr->filename, lp->mp);
3634 return SCPE_OK;
3635 }
3636 
3637 /* Show logging status for line */
3638 
3639 t_stat tmxr_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3640 {
3641 const TMXR *mp = (const TMXR *) desc;
3642 TMLN *lp;
3643 
3644 lp = tmxr_find_ldsc (uptr, val, mp);                    /* find line desc */
3645 if (lp == NULL)
3646     return SCPE_IERR;
3647 if (lp->txlog)
3648     fprintf (st, "logging to %s", lp->txlogname);
3649 else fprintf (st, "no logging");
3650 return SCPE_OK;
3651 }
3652 
3653 /* Set the line connection order.
3654 
3655    Example command for eight-line multiplexer:
3656 
3657       SET <dev> LINEORDER=1;5;2-4;7
3658 
3659    Resulting connection order: 1,5,2,3,4,7,0,6.
3660 
3661    Parameters:
3662     - uptr = (not used)
3663     - val  = (not used)
3664     - cptr = pointer to first character of range specification
3665     - desc = pointer to multiplexer's TMXR structure
3666 
3667    On entry, cptr points to the value portion of the command string, which may
3668    be either a semicolon-separated list of line ranges or the keyword ALL.
3669 
3670    If a line connection order array is not defined in the multiplexer
3671    descriptor, the command is rejected.  If the specified range encompasses all
3672    of the lines, the first value of the connection order array is set to -1 to
3673    indicate sequential connection order.  Otherwise, the line values in the
3674    array are set to the order specified by the command string.  All values are
3675    populated, first with those explicitly specified in the command string, and
3676    then in ascending sequence with those not specified.
3677 
3678    If an error occurs, the original line order is not disturbed.
3679 */
3680 
3681 t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, CONST char *carg, void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3682 {
3683 TMXR *mp = (TMXR *) desc;
3684 char *tbuf;
3685 char *tptr;
3686 CONST char *cptr;
3687 t_addr low, high, max = (t_addr) mp->lines - 1;
3688 int32 *list;
3689 t_bool *set;
3690 uint32 line, idx = 0;
3691 t_stat result = SCPE_OK;
3692 
3693 if (mp->lnorder == NULL)                                /* line connection order undefined? */
3694     return SCPE_NXPAR;                                  /* "Non-existent parameter" error */
3695 
3696 else if ((carg == NULL) || (*carg == '\0'))             /* line range not supplied? */
3697     return SCPE_MISVAL;                                 /* "Missing value" error */
3698 
3699 list = (int32 *) calloc (mp->lines, sizeof (int32));    /* allocate new line order array */
3700 if (list == NULL)                                       /* allocation failed? */
3701     return SCPE_MEM;                                    /* report it */
3702 
3703 set = (t_bool *) calloc (mp->lines, sizeof (t_bool));   /* allocate line set tracking array */
3704 if (set == NULL) {                                      /* allocation failed? */
3705     FREE (list);                                        /* free successful list allocation */
3706     return SCPE_MEM;                                    /* report it */
3707     }
3708 
3709 tbuf = (char *) calloc (strlen(carg)+2, sizeof(*carg));
3710 if (tbuf == NULL) {
3711   FREE(set);
3712   FREE(list);
3713   return SCPE_MEM;
3714 }
3715 strcpy (tbuf, carg);
3716 tptr = tbuf + strlen (tbuf);                            /* append a semicolon */
3717 *tptr++ = ';';                                          /*   to the command string */
3718 *tptr = '\0';                                           /*   to make parsing easier for get_range */
3719 cptr = tbuf;
3720 
3721 while (*cptr) {                                         /* parse command string */
3722     cptr = get_range (NULL, cptr, &low, &high, 10, max, ';');/* get a line range */
3723 
3724     if (cptr == NULL) {                                 /* parsing error? */
3725         result = SCPE_ARG;                              /* "Invalid argument" error */
3726         break;
3727         }
3728 
3729     else if ((low > max) || (high > max)) {             /* line out of range? */
3730         result = SCPE_SUB;                              /* "Subscript out of range" error */
3731         break;
3732         }
3733 
3734     else if ((low == 0) && (high == max)) {             /* entire line range specified? */
3735         list [0] = -1;                                  /* set sequential order flag */
3736         idx = (uint32) max + 1;                         /* indicate no fill-in needed */
3737         break;
3738         }
3739 
3740     else
3741         for (line = (uint32) low; line <= (uint32) high; line++) /* see if previously specified */
3742             if (set [line] == FALSE) {                  /* not already specified? */
3743                 set [line] = TRUE;                      /* now it is */
3744                 list [idx] = line;                      /* add line to connection order */
3745                 idx = idx + 1;                          /* bump "specified" count */
3746                 }
3747     }
3748 
3749 if (result == SCPE_OK) {                                /* assignment successful? */
3750     if (idx <= max)                                     /* any lines not specified? */
3751         for (line = 0; line <= max; line++)             /* fill them in sequentially */
3752             if (set [line] == FALSE) {                  /* specified? */
3753                 list [idx] = line;                      /* no, so add it */
3754                 idx = idx + 1;
3755                 }
3756 
3757     memcpy (mp->lnorder, list, mp->lines * sizeof (int32)); /* copy working array to connection array */
3758     }
3759 
3760 FREE (list);                                            /* free list allocation */
3761 FREE (set);                                             /* free set allocation */
3762 FREE (tbuf);                                            /* free arg copy with ; */
3763 
3764 return result;
3765 }
3766 
3767 /* Show line connection order.
3768 
3769    Parameters:
3770     - st   = stream on which output is to be written
3771     - uptr = (not used)
3772     - val  = (not used)
3773     - desc = pointer to multiplexer's TMXR structure
3774 
3775    If a connection order array is not defined in the multiplexer descriptor, the
3776    command is rejected.  If the first value of the connection order array is set
3777    to -1, then the connection order is sequential.  Otherwise, the line values
3778    in the array are printed as a semicolon-separated list.  Ranges are printed
3779    where possible to shorten the output.
3780 */
3781 
3782 t_stat tmxr_show_lnorder (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3783 {
3784 int32 i, j, low, last;
3785 const TMXR *mp = (const TMXR *) desc;
3786 int32 *iptr = mp->lnorder;
3787 t_bool first = TRUE;
3788 
3789 if (iptr == NULL)                                       /* connection order undefined? */
3790     return SCPE_NXPAR;                                  /* "Non-existent parameter" error */
3791 
3792 if (*iptr < 0)                                          /* sequential order indicated? */
3793     fprintf (st, "Order=0-%d\n", mp->lines - 1);        /* print full line range */
3794 
3795 else {
3796     low = last = *iptr++;                               /* set first line value */
3797 
3798     for (j = 1; j <= mp->lines; j++) {                  /* print remaining lines in order list */
3799         if (j < mp->lines)                              /* more lines to process? */
3800             i = *iptr++;                                /* get next line in list */
3801         else                                            /* final iteration */
3802             i = -1;                                     /* get "tie-off" value */
3803 
3804         if (i != last + 1) {                            /* end of a range? */
3805             if (first) {                                /* first line to print? */
3806                 fputs ("Order=", st);                   /* print header */
3807                 first = FALSE;
3808                 }
3809 
3810             else                                        /* not first line printed */
3811                 fputc (';', st);                        /* print separator */
3812 
3813             if (low == last)                            /* range null? */
3814                 fprintf (st, "%d", last);               /* print single line value */
3815 
3816             else                                        /* range established */
3817                 fprintf (st, "%d-%d", low, last);       /* print start and end line */
3818 
3819             low = i;                                    /* start new range */
3820             }
3821 
3822         last = i;                                       /* note value for range check */
3823         }
3824     }
3825 
3826 if (first == FALSE)                                     /* sanity check for lines == 0 */
3827     fputc ('\n', st);
3828 
3829 return SCPE_OK;
3830 }
3831 
3832 /* Show summary processor */
3833 
3834 t_stat tmxr_show_summ (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3835 {
3836 const TMXR *mp = (const TMXR *) desc;
3837 int32 i, t;
3838 
3839 if (mp == NULL)
3840     return SCPE_IERR;
3841 for (i = t = 0; i < mp->lines; i++)
3842     if ((mp->ldsc[i].sock != 0)
3843     )
3844         t = t + 1;
3845 if (mp->lines > 1)
3846     fprintf (st, "%d current connection%s", t, (t != 1) ? "s" : "");
3847 else
3848     fprintf (st, "%s", (t == 1) ? "connected" : "disconnected");
3849 return SCPE_OK;
3850 }
3851 
3852 /* Show conn/stat processor */
3853 
3854 t_stat tmxr_show_cstat (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3855 {
3856 const TMXR *mp = (const TMXR *) desc;
3857 int32 i, any;
3858 
3859 if (mp == NULL)
3860     return SCPE_IERR;
3861 for (i = any = 0; i < mp->lines; i++) {
3862     if ((mp->ldsc[i].sock != 0)
3863       || mp->ldsc[i].modem_control) {
3864         if ((mp->ldsc[i].sock != 0)
3865       )
3866             any++;
3867         if (val)
3868             tmxr_fconns (st, &mp->ldsc[i], i);
3869         else
3870             if ((mp->ldsc[i].sock != 0)
3871           )
3872                 tmxr_fstats (st, &mp->ldsc[i], i);
3873         }
3874     }
3875 if (any == 0)
3876     fprintf (st, (mp->lines == 1? "disconnected\n": "all disconnected\n"));
3877 return SCPE_OK;
3878 }
3879 
3880 /* Show number of lines */
3881 
3882 t_stat tmxr_show_lines (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
     /* [previous][next][first][last][top][bottom][index][help] */
3883 {
3884 const TMXR *mp = (const TMXR *) desc;
3885 
3886 if (mp == NULL)
3887     return SCPE_IERR;
3888 fprintf (st, "lines=%d", mp->lines);
3889 return SCPE_OK;
3890 }

/* [previous][next][first][last][top][bottom][index][help] */