root/src/dps8/uvutil.c

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

DEFINITIONS

This source file includes following definitions.
  1. alloc_buffer
  2. accessWriteCallback
  3. accessReadCallback
  4. accessReadStart
  5. accessStartWriteActual
  6. accessStartWrite
  7. accessPutCharForce
  8. accessPutStrForce
  9. accessStartWriteStr
  10. accessLogon
  11. accessCloseCallback
  12. accessCloseConnection
  13. accessProcessInput
  14. accessTelnetReadCallback
  15. evHandler
  16. accessTelnetConnect
  17. onNewAccess
  18. uv_open_access
  19. accessPutChar
  20. accessGetChar
  21. accessPutStr

   1 /*
   2  * vim: filetype=c:tabstop=4:ai:expandtab
   3  * SPDX-License-Identifier: ICU
   4  * scspell-id: 6315eaa3-f630-11ec-96fc-80ee73e9b8e7
   5  *
   6  * ---------------------------------------------------------------------------
   7  *
   8  * Copyright (c) 2013-2019 Charles Anthony
   9  * Copyright (c) 2021 Jeffrey H. Johnson
  10  * Copyright (c) 2021-2024 The DPS8M Development Team
  11  *
  12  * This software is made available under the terms of the ICU License.
  13  * See the LICENSE.md file at the top-level directory of this distribution.
  14  *
  15  * ---------------------------------------------------------------------------
  16  */
  17 
  18 #include <uv.h>
  19 #include <ctype.h>
  20 #include <signal.h>
  21 #include "dps8.h"
  22 #include "dps8_sys.h"
  23 #include "dps8_cpu.h"
  24 #include "dps8_utils.h"
  25 #include "libtelnet.h"
  26 #include "uvutil.h"
  27 
  28 #if defined(FREE)
  29 # undef FREE
  30 #endif /* if defined(FREE) */
  31 #define FREE(p) do  \
  32   {                 \
  33     free((p));      \
  34     (p) = NULL;     \
  35   } while(0)
  36 
  37 #define USE_REQ_DATA
  38 
  39 static void accessTelnetReadCallback (uv_tcp_t * client,
  40                             ssize_t nread,
  41                             unsigned char * buf);
  42 
  43 //
  44 // alloc_buffer: libuv callback handler to allocate buffers for incoming data.
  45 //
  46 
  47 static void alloc_buffer (UNUSED uv_handle_t * handle, size_t suggested_size,
     /* [previous][next][first][last][top][bottom][index][help] */
  48                           uv_buf_t * buf)
  49   {
  50 /* Suppress Clang Analyzer's possible memory leak warning */
  51 #if !defined(__clang_analyzer__)
  52     * buf = uv_buf_init ((char *) malloc (suggested_size),
  53                          (uint) suggested_size);
  54 #endif /* if !defined(__clang_analyzer__) */
  55   }
  56 
  57 static void accessWriteCallback (uv_write_t * req, int status)
     /* [previous][next][first][last][top][bottom][index][help] */
  58   {
  59     if (status < 0)
  60       {
  61         if (status == -ECONNRESET || status == -ECANCELED ||
  62             status == -EPIPE)
  63           {
  64             // This occurs when the other end disconnects; not an "error"
  65           }
  66         else
  67           {
  68             sim_warn ("accessWriteCallback status %d (%s)\n", -status,
  69                       xstrerror_l(-status));
  70           }
  71 
  72         // connection reset by peer
  73         accessCloseConnection (req->handle);
  74       }
  75 
  76 #if defined(USE_REQ_DATA)
  77     FREE (req->data);
  78 #else
  79     unsigned int nbufs = req->nbufs;
  80     uv_buf_t * bufs = req->bufs;
  81     for (unsigned int i = 0; i < nbufs; i ++)
  82       {
  83         if (bufs && bufs[i].base)
  84           {
  85             FREE (bufs[i].base);
  86             //bufp->base = NULL;
  87           }
  88         if (req->bufsml[i].base)
  89           {
  90             FREE (req->bufsml[i].base);
  91           }
  92       }
  93 #endif /* if defined(USE_REQ_DATA) */
  94 
  95     // the buf structure is copied; do not free.
  96     //sim_printf ("freeing req %p\n", req);
  97     FREE (req);
  98   }
  99 
 100 //
 101 // accessReadCallback: libuv read complete callback
 102 //
 103 //   Cleanup on read error.
 104 //   Forward data to appropriate handler.
 105 
 106 static void accessReadCallback (uv_stream_t* stream,
     /* [previous][next][first][last][top][bottom][index][help] */
 107                              ssize_t nread,
 108                              const uv_buf_t* buf)
 109   {
 110     uv_access * access = (uv_access *) stream->data;
 111     if (nread < 0)
 112       {
 113         //if (nread == UV_EOF)
 114           {
 115             accessCloseConnection (stream);
 116           }
 117       }
 118     else if (nread > 0)
 119       {
 120         if (access->telnetp)
 121           {
 122             telnet_recv (access->telnetp, buf->base, (size_t) nread);
 123           }
 124         else
 125           {
 126             accessTelnetReadCallback ((uv_tcp_t *) stream,
 127                                       (ssize_t) nread,
 128                                       (unsigned char *) buf->base);
 129           }
 130       }
 131     if (buf->base)
 132         free (buf->base); /* X-LINTED: FREE */
 133   }
 134 
 135 //
 136 // Enable reading on connection
 137 //
 138 
 139 static void accessReadStart (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 140   {
 141     if (! client || uv_is_closing ((uv_handle_t *) client))
 142       return;
 143     uv_read_start ((uv_stream_t *) client, alloc_buffer, accessReadCallback);
 144   }
 145 
 146 // Create and start a write request
 147 
 148 static void accessStartWriteActual (uv_tcp_t * client, char * data,
     /* [previous][next][first][last][top][bottom][index][help] */
 149                                         ssize_t datalen)
 150   {
 151     if (! client || uv_is_closing ((uv_handle_t *) client))
 152       return;
 153 /* Suppress Clang Analyzer's possible memory leak warning */
 154 #if !defined(__clang_analyzer__)
 155     uv_write_t * req = (uv_write_t *) malloc (sizeof (uv_write_t));
 156     if (!req)
 157       {
 158         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 159                        __func__, __FILE__, __LINE__);
 160 # if defined(USE_BACKTRACE)
 161 #  if defined(SIGUSR2)
 162         (void)raise(SIGUSR2);
 163         /*NOTREACHED*/ /* unreachable */
 164 #  endif /* if defined(SIGUSR2) */
 165 # endif /* if defined(USE_BACKTRACE) */
 166         abort();
 167       }
 168     // This makes sure that bufs*.base and bufsml*.base are NULL
 169     (void)memset (req, 0, sizeof (uv_write_t));
 170     uv_buf_t buf = uv_buf_init ((char *) malloc ((unsigned long) datalen),
 171                                                  (uint) datalen);
 172 # if defined(USE_REQ_DATA)
 173     req->data = buf.base;
 174 # endif /* if defined(USE_REQ_DATA) */
 175     memcpy (buf.base, data, (unsigned long) datalen);
 176 //sim_printf ("write %u<%s>\n", datalen, data);
 177     int ret = uv_write (req, (uv_stream_t *) client, & buf, 1,
 178                         accessWriteCallback);
 179 // There seems to be a race condition when Multics signals a disconnect_line;
 180 // We close the socket, but Multics is still writing its goodbye text trailing
 181 // NULs.
 182 // If the socket has been closed, write will return BADF; just ignore it.
 183     if (ret < 0 && ret != -EBADF)
 184       sim_printf ("uv_write returns %d\n", ret);
 185 #endif /* if !defined(__clang_analyzer__) */
 186   }
 187 
 188 void accessStartWrite (uv_tcp_t * client, char * data, ssize_t datalen)
     /* [previous][next][first][last][top][bottom][index][help] */
 189   {
 190     if ((client == NULL) || uv_is_closing ((uv_handle_t *) client))
 191       return;
 192     uv_access * access = (uv_access *) client->data;
 193     if (access->telnetp)
 194       telnet_send (access->telnetp, data, (size_t) datalen);
 195     else
 196       accessStartWriteActual (client, data, (ssize_t) datalen);
 197   }
 198 
 199 static void accessPutCharForce (uv_access * access, char ch)
     /* [previous][next][first][last][top][bottom][index][help] */
 200   {
 201     //sim_putchar (ch);
 202     accessStartWrite (access->client, & ch, 1);
 203   }
 204 
 205 static void accessPutStrForce (uv_access * access, char * str)
     /* [previous][next][first][last][top][bottom][index][help] */
 206   {
 207     size_t l = strlen (str);
 208     accessStartWrite (access->client, str, (ssize_t) l);
 209   }
 210 
 211 void accessStartWriteStr (uv_tcp_t * client, char * data)
     /* [previous][next][first][last][top][bottom][index][help] */
 212   {
 213     accessStartWrite (client, data, (ssize_t) strlen (data));
 214   }
 215 
 216 static void accessLogon (uv_access * access, unsigned char * buf, ssize_t nread)
     /* [previous][next][first][last][top][bottom][index][help] */
 217   {
 218     for (ssize_t nchar = 0; nchar < nread; nchar ++)
 219       {
 220         unsigned char kar = buf[nchar];
 221         // buffer too full for anything more?
 222         if ((unsigned long) access->pwPos >= sizeof (access->pwBuffer) - 1)
 223           {
 224             // yes. Only allow \n, \r, ^H, ^R
 225             switch (kar)
 226               {
 227                 case '\b':  // backspace
 228                 case 127:   // delete
 229                   {
 230                     // remove char from line
 231                     accessPutStrForce (access, "\b \b");
 232                     access->pwBuffer[access->pwPos] = 0;     // remove char from buffer
 233                     if (access->pwPos > 0)
 234                       access->pwPos -= 1;             // back up buffer pointer
 235                   }
 236                   break;
 237 
 238                 case '\n':
 239                 case '\r':
 240                   {
 241                     access->pwBuffer[access->pwPos] = 0;
 242                     goto check;
 243                   }
 244 
 245                 case 0x12:  // ^R
 246                   {
 247                     accessPutStrForce (access, "^R\r\n"); // echo ^R
 248                     access->connectPrompt (access->client);
 249                     accessPutStrForce (access, access->pwBuffer);
 250                   }
 251                  break;
 252 
 253                 default:
 254                   break;
 255               } // switch kar
 256             continue; // process next character in buffer
 257           } // if buffer full
 258 
 259         if (isprint (kar))   // printable?
 260           {
 261             accessPutCharForce (access, '*');
 262             access->pwBuffer[access->pwPos++] = (char) kar;
 263             access->pwBuffer[access->pwPos] = 0;
 264           }
 265         else
 266           {
 267             switch (kar)
 268               {
 269                 case '\b':  // backspace
 270                 case 127:   // delete
 271                   {
 272                     // remove char from line
 273                     accessPutStrForce (access, "\b \b");
 274                    // remove char from buffer
 275                     access->pwBuffer[access->pwPos] = 0;
 276                     if (access->pwPos > 0)
 277                       access->pwPos -= 1;   // back up buffer pointer
 278                   }
 279                   break;
 280 
 281                 case '\n':
 282                 case '\r':
 283                   {
 284                     access->pwBuffer[access->pwPos] = 0;
 285                     goto check;
 286                   }
 287 
 288                 case 0x12:  // ^R
 289                   {
 290                     accessPutStrForce (access, "^R\r\n"); // echo ^R
 291                     access->connectPrompt (access->client);
 292                     accessPutStrForce (access, access->pwBuffer);
 293                   }
 294                   break;
 295 
 296                 default:
 297                   break;
 298               } // switch kar
 299           } // not printable
 300       } // for nchar
 301     return;
 302 
 303 check:;
 304     char cpy[access->pwPos + 1];
 305     memcpy (cpy, access->pwBuffer, (unsigned long) access->pwPos);
 306     cpy[access->pwPos] = 0;
 307     trim (cpy);
 308     //sim_printf ("<%s>", cpy);
 309     access->pwPos = 0;
 310     accessPutStrForce (access, "\r\n");
 311 
 312     if (strcmp (cpy, access->pw) == 0)
 313       {
 314         accessPutStrForce (access, "ok\r\n");
 315         sim_printf ("\r[OPC emulation: ACCESS GRANTED]\r\n");
 316         goto associate;
 317       }
 318     else
 319       {
 320         //accessPutStrForce ("<");
 321         //accessPutStrForce (access->pwBuffer);
 322         //accessPutStrForce (">\r\n");
 323         accessPutStrForce (access, "nope\r\n");
 324         sim_printf ("\r[OPC emulation: INVALID PASSWORD]\r\n");
 325         goto reprompt;
 326       }
 327 
 328 reprompt:;
 329     access->connectPrompt (access->client);
 330     return;
 331 
 332 associate:;
 333     access->loggedOn = true;
 334     if (access->connected)
 335       access->connected (access->client);
 336   }
 337 
 338 static void accessCloseCallback (uv_handle_t * stream)
     /* [previous][next][first][last][top][bottom][index][help] */
 339   {
 340     FREE (stream);
 341     //access->client = NULL;
 342   }
 343 
 344 void accessCloseConnection (uv_stream_t* stream)
     /* [previous][next][first][last][top][bottom][index][help] */
 345   {
 346     uv_access * access = (uv_access *) stream->data;
 347     sim_printf ("\r[OPC emulation: DISCONNECT]\r\n");
 348     // Clean up allocated data
 349     if (access->telnetp)
 350       {
 351         telnet_free (access->telnetp);
 352         access->telnetp = NULL;
 353       }
 354     if (! uv_is_closing ((uv_handle_t *) stream))
 355       uv_close ((uv_handle_t *) stream, accessCloseCallback);
 356     access->client = NULL;
 357   }
 358 
 359 static void accessProcessInput (uv_access * access, unsigned char * buf,
     /* [previous][next][first][last][top][bottom][index][help] */
 360                                  ssize_t nread)
 361   {
 362     if (access->inBuffer)
 363       {
 364         //sim_warn ("inBuffer overrun\n");
 365         unsigned char * new =
 366           realloc (access->inBuffer,
 367                    (unsigned long) (access->inSize + nread));
 368         if (! new)
 369           {
 370             (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 371                            __func__, __FILE__, __LINE__);
 372 #if defined(USE_BACKTRACE)
 373 # if defined(SIGUSR2)
 374             (void)raise(SIGUSR2);
 375             /*NOTREACHED*/ /* unreachable */
 376 # endif /* if defined(SIGUSR2) */
 377 #endif /* if defined(USE_BACKTRACE) */
 378             abort();
 379           }
 380         memcpy (new + access->inSize, buf, (unsigned long) nread);
 381         access->inSize += nread;
 382         access->inBuffer = new;
 383       }
 384     else
 385       {
 386         access->inBuffer = malloc ((unsigned long) nread);
 387         if (! access->inBuffer)
 388           {
 389             (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 390                            __func__, __FILE__, __LINE__);
 391 #if defined(USE_BACKTRACE)
 392 # if defined(SIGUSR2)
 393             (void)raise(SIGUSR2);
 394             /*NOTREACHED*/ /* unreachable */
 395 # endif /* if defined(SIGUSR2) */
 396 #endif /* if defined(USE_BACKTRACE) */
 397             abort();
 398           }
 399         memcpy (access->inBuffer, buf, (unsigned long) nread);
 400         access->inSize = (uint) nread;
 401         access->inUsed = 0;
 402       }
 403 
 404     // Prevent further reading until this buffer is consumed
 405     //fnpuv_read_stop (client);
 406     //if (! client || uv_is_closing ((uv_handle_t *) client))
 407       //return;
 408     //uv_read_stop ((uv_stream_t *) client);
 409   }
 410 
 411 static void accessTelnetReadCallback (uv_tcp_t * client,
     /* [previous][next][first][last][top][bottom][index][help] */
 412                             ssize_t nread,
 413                             unsigned char * buf)
 414   {
 415     uv_access * access = (uv_access *) client->data;
 416     if (access->loggedOn)
 417       accessProcessInput (access, buf, nread);
 418     else
 419       accessLogon (access, buf, nread);
 420   }
 421 
 422 static void evHandler (UNUSED telnet_t *telnet, telnet_event_t *event,
     /* [previous][next][first][last][top][bottom][index][help] */
 423                        void *user_data)
 424   {
 425     uv_tcp_t * client = (uv_tcp_t *) user_data;
 426 
 427     switch (event->type)
 428       {
 429         case TELNET_EV_DATA:
 430           {
 431             accessTelnetReadCallback (client, (ssize_t) event->data.size,
 432                             (unsigned char *)event->data.buffer);
 433           }
 434           break;
 435 
 436         case TELNET_EV_SEND:
 437           {
 438             accessStartWriteActual (client, (char *) event->data.buffer,
 439                                         (ssize_t) event->data.size);
 440           }
 441           break;
 442 
 443         case TELNET_EV_DO:
 444           {
 445             if (event->neg.telopt == TELNET_TELOPT_BINARY)
 446               {
 447                 // DO Binary
 448               }
 449             else if (event->neg.telopt == TELNET_TELOPT_SGA)
 450               {
 451                 // DO Suppress Go Ahead
 452               }
 453             else if (event->neg.telopt == TELNET_TELOPT_ECHO)
 454               {
 455                 // DO Suppress Echo
 456               }
 457             else
 458               {
 459                 sim_printf ("evHandler DO %d\n", event->neg.telopt);
 460               }
 461           }
 462           break;
 463 
 464         case TELNET_EV_DONT:
 465           {
 466             sim_printf ("evHandler DONT %d\n", event->neg.telopt);
 467           }
 468           break;
 469 
 470         case TELNET_EV_WILL:
 471           {
 472             if (event->neg.telopt != 3)
 473               sim_printf ("evHandler WILL %d\n", event->neg.telopt);
 474           }
 475           break;
 476 
 477         case TELNET_EV_WONT:
 478           {
 479             sim_printf ("evHandler WONT %d\n", event->neg.telopt);
 480           }
 481           break;
 482 
 483         case TELNET_EV_ERROR:
 484           {
 485             sim_warn ("libtelnet evHandler error <%s>\n", event->error.msg);
 486           }
 487           break;
 488 
 489         case TELNET_EV_IAC:
 490           {
 491             if (event->iac.cmd == 243 || // BRK
 492                 event->iac.cmd == 244)   // IP
 493               {
 494                 sim_warn ("libtelnet dropping unassociated BRK/IP\n");
 495               }
 496             else
 497               if ((!sim_quiet) || (event->iac.cmd != 241))
 498                 sim_warn ("libtelnet unhandled IAC event %d\n", event->iac.cmd);
 499           }
 500           break;
 501 
 502         default:
 503           sim_printf ("evHandler: unhandled event %d\n", event->type);
 504           break;
 505       }
 506 
 507   }
 508 
 509 static const telnet_telopt_t my_telopts[] =
 510   {
 511     { TELNET_TELOPT_SGA,       TELNET_WILL, TELNET_DO   },
 512     { TELNET_TELOPT_ECHO,      TELNET_WILL, TELNET_DONT },
 513   //{ TELNET_TELOPT_TTYPE,     TELNET_WONT, TELNET_DONT },
 514  //   { TELNET_TELOPT_BINARY,    TELNET_WILL, TELNET_DO   },
 515     { TELNET_TELOPT_BINARY,    TELNET_WONT, TELNET_DONT   },
 516   //{ TELNET_TELOPT_NAWS,      TELNET_WONT, TELNET_DONT },
 517     { -1, 0, 0 }
 518   };
 519 
 520 static void * accessTelnetConnect (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 521   {
 522     void * p = (void *) telnet_init (my_telopts, evHandler, 0, client);
 523     if (!p)
 524       {
 525         (void)fprintf(stderr, "\rtelnet_init failed at %s[%s:%d]\r\n",
 526                       __func__, __FILE__, __LINE__);
 527         return NULL;
 528       }
 529     const telnet_telopt_t * q = my_telopts;
 530     while (q->telopt != -1)
 531       {
 532         telnet_negotiate (p, q->us, (unsigned char) q->telopt);
 533         q ++;
 534       }
 535     return p;
 536   }
 537 
 538 //
 539 // Connection callback handler
 540 //
 541 
 542 static void onNewAccess (uv_stream_t * server, int status)
     /* [previous][next][first][last][top][bottom][index][help] */
 543   {
 544     if (status < 0)
 545       {
 546         (void)fprintf (stderr, "\r[OPC emulation: new connection error %s]\r\n", uv_strerror(status));
 547         // error!
 548         return;
 549       }
 550 
 551     uv_access * access = (uv_access *) server->data;
 552 
 553     uv_tcp_t * client = (uv_tcp_t *) malloc (sizeof (uv_tcp_t));
 554     if (!client)
 555       {
 556         (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 557                       __func__, __FILE__, __LINE__);
 558 #if defined(USE_BACKTRACE)
 559 # if defined(SIGUSR2)
 560         (void)raise(SIGUSR2);
 561         /*NOTREACHED*/ /* unreachable */
 562 # endif /* if defined(SIGUSR2) */
 563 #endif /* if defined(USE_BACKTRACE) */
 564         abort();
 565       }
 566     uv_tcp_init (access->loop, client);
 567     client->data = server->data;
 568     if (uv_accept (server, (uv_stream_t *) client) == 0)
 569       {
 570         // Only a single connection at a time
 571         if (access->client)
 572           {
 573             sim_printf ("\r\n[OPC emulation: BUMPED]\r\n\r\n");
 574             accessPutStrForce (access, "\r[OPC emulation: BUMPED]\r\n");
 575             access->loggedOn = false;
 576             sim_printf ("\rMultics has disconnected you\r\n");
 577             accessCloseConnection ((uv_stream_t *) access->client);
 578           }
 579         access->client = client;
 580         uv_tcp_nodelay (client, 1);
 581         struct sockaddr name;
 582         int namelen = sizeof (name);
 583         int ret = uv_tcp_getpeername (access->client, & name, & namelen);
 584         if (ret < 0)
 585           {
 586             sim_printf ("\r[OPC emulation: CONNECT; addr err %d]\r\n", ret);
 587           }
 588         else
 589           {
 590             struct sockaddr_in * p = (struct sockaddr_in *) & name;
 591             sim_printf ("\r[OPC emulation: CONNECT %s]\r\n", inet_ntoa (p -> sin_addr));
 592           }
 593 
 594         if (access->useTelnet)
 595           {
 596             access->telnetp = accessTelnetConnect (access->client);
 597             if (!access->telnetp)
 598               {
 599                  sim_warn ("ltnConnect failed\n");
 600                  return;
 601               }
 602           }
 603         else
 604           {
 605             access->telnetp = NULL;
 606           }
 607         access->loggedOn =
 608           ! strlen (access->pw);
 609         if (access->loggedOn)
 610           access->connected (access->client);
 611         else
 612           access->connectPrompt (access->client);
 613         accessReadStart (access->client);
 614       }
 615     else
 616       {
 617         uv_close ((uv_handle_t *) client, accessCloseCallback);
 618       }
 619   }
 620 
 621 void uv_open_access (uv_access * access)
     /* [previous][next][first][last][top][bottom][index][help] */
 622   {
 623     if (access->open == true)
 624       {
 625         sim_printf ("\r[OPC emulation: OPC already initialized]\r\n");
 626       }
 627 
 628     if (! access->port)
 629       {
 630         //sim_printf ("access port disabled\n");
 631         return;
 632       }
 633 
 634     if (! access->loop)
 635       access->loop = uv_default_loop ();
 636 
 637     // Do we already have a listening port (ie not first time)?
 638     if (access->open)
 639       return;
 640 
 641     uv_tcp_init (access->loop, & access->server);
 642     access->server.data = (void *) access;
 643     struct sockaddr_in addr;
 644     uv_ip4_addr (access->address, access->port, & addr);
 645     uv_tcp_bind (& access->server, (const struct sockaddr *) & addr, 0);
 646 #define DEFAULT_BACKLOG 1024
 647     int r = uv_listen ((uv_stream_t *) & access->server,
 648                        DEFAULT_BACKLOG,
 649                        onNewAccess);
 650     if (r)
 651      {
 652         (void)fprintf (stderr, "\r[OPC emulation: listen error: %s:%ld: %s]\r\n",
 653                        access->address, (long) access->port, uv_strerror(r));
 654       }
 655     access->open = true;
 656     if (access->address != NULL)
 657       sim_printf ("\r[OPC emulation: TELNET server listening on %s:%ld]\r\n",
 658                   access->address, (long) access->port);
 659     else
 660       sim_printf ("\r[OPC emulation: TELNET server listening on 0.0.0.0:%ld]\r\n",
 661                   (long) access->port);
 662   }
 663 
 664 #if !defined(QUIET_UNUSED)
 665 void accessPutChar (uv_access * access, char ch)
     /* [previous][next][first][last][top][bottom][index][help] */
 666   {
 667     //sim_putchar (ch);
 668     if (access->loggedOn)
 669       accessStartWrite (access->client, & ch, 1);
 670   }
 671 #endif /* if !defined(QUIET_UNUSED) */
 672 
 673 int accessGetChar (uv_access * access)
     /* [previous][next][first][last][top][bottom][index][help] */
 674   {
 675     // The connection could have closed when we were not looking
 676     if (! access->client)
 677       {
 678         if (access->inBuffer)
 679           FREE (access->inBuffer);
 680         access->inBuffer = NULL;
 681         access->inSize = 0;
 682         access->inUsed = 0;
 683         return SCPE_OK;
 684       }
 685 
 686     if (access->inBuffer && access->inUsed < access->inSize)
 687        {
 688          unsigned char c = access->inBuffer[access->inUsed ++];
 689          if (access->inUsed >= access->inSize)
 690            {
 691              FREE (access->inBuffer);
 692              access->inBuffer = NULL;
 693              access->inSize = 0;
 694              access->inUsed = 0;
 695              // The connection could have been closed when we weren't looking
 696              //if (access->client)
 697                //fnpuv_read_start (access->client);
 698            }
 699          return (int) c + SCPE_KFLAG;
 700        }
 701     return SCPE_OK;
 702 
 703   }
 704 
 705 #if !defined(QUIET_UNUSED)
 706 void accessPutStr (uv_access * access, char * str)
     /* [previous][next][first][last][top][bottom][index][help] */
 707   {
 708     size_t l = strlen (str);
 709     //for (size_t i = 0; i < l; i ++)
 710       //sim_putchar (str[i]);
 711     if (access->loggedOn)
 712       accessStartWrite (access->client, str, (ssize_t) l);
 713   }
 714 #endif /* if !defined(QUIET_UNUSED) */

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