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

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