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

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