PLplot 5.15.0
Loading...
Searching...
No Matches
tcpip.c
Go to the documentation of this file.
1// Maurice LeBrun
2// 6-Jan-94
3//
4// Functions to handle a variety of TPC-IP related chores, in particular
5// socket i/o for data transfer to the Tcl-DP driver. For the latter, the
6// Tcl-DP routines were modified to use binary records; copyright follows:
7//
8// Copyright 1992 Telecom Finland
9//
10// Permission to use, copy, modify, and distribute this
11// software and its documentation for any purpose and without
12// fee is hereby granted, provided that this copyright
13// notice appears in all copies. Telecom Finland
14// makes no representations about the suitability of this
15// software for any purpose. It is provided "as is" without
16// express or implied warranty.
17//
18// Copyright (c) 1993 The Regents of the University of California.
19// All rights reserved.
20//
21// Permission to use, copy, modify, and distribute this software and its
22// documentation for any purpose, without fee, and without written agreement is
23// hereby granted, provided that the above copyright notice and the following
24// two paragraphs appear in all copies of this software.
25//
26// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
27// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
28// OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
29// CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30//
31// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
32// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
33// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
34// ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
35// PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
36//
37//
38// Copyright (C) 2004 Joao Cardoso
39//
40// This file is part of PLplot.
41//
42// PLplot is free software; you can redistribute it and/or modify
43// it under the terms of the GNU Library General Public License as published
44// by the Free Software Foundation; either version 2 of the License, or
45// (at your option) any later version.
46//
47// PLplot is distributed in the hope that it will be useful,
48// but WITHOUT ANY WARRANTY; without even the implied warranty of
49// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
50// GNU Library General Public License for more details.
51//
52// You should have received a copy of the GNU Library General Public License
53// along with PLplot; if not, write to the Free Software
54// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
55//
56
57//
58// #define DEBUG
59//
60
61#include "plDevs.h"
62#include "plConfig.h"
63
64#if defined ( PLD_tk ) || defined ( ENABLE_tkX )
65
66// This file is meant to be compiled with non-ANSI compilers ("cc").
67// The reason for doing it this way is to ensure that the full C
68// environment of the target machine is visible (at the time of writing
69// this, parts of this code are not covered by any international
70// standard). ANSI compilers are required to omit these extra symbols,
71// and at the moment there is no way to get them back except for by
72// vendor-specific defines, e.g. _HPUX_SOURCE (HP), _ALL_SOURCE (AIX),
73// _DGUX_SOURCE (DGUX). This is an omission in the POSIX standard more
74// than anything else, and will probably be rectified at some point. So
75// for now, instead of relying on a hodgepodge of vendor specific symbols
76// I forego the ANSI compiler here and go with good (bad) old "cc".
77//
78
79#ifdef _POSIX_SOURCE
80#undef _POSIX_SOURCE
81#endif
82#ifdef caddr_t
83#undef caddr_t
84#endif
85
86#include <stdio.h>
87#include <stdlib.h>
88#include <math.h>
89#include <string.h>
90#if defined ( __sgi ) && !defined ( SVR3 )
91#include <sys/select.h>
92#endif
93#ifdef PL_HAVE_UNISTD_H
94#include <unistd.h>
95#endif
96
97#include "plplot.h"
98#include "tcpip.h"
99#include <tcl.h>
100#include <tk.h>
101
102#include <sys/stat.h>
103#include <sys/types.h>
104#include <fcntl.h>
105#include <ctype.h>
106#if !defined ( _WIN32 )
107#include <sys/uio.h>
108#endif
109#include <errno.h>
110
111#if defined ( _WIN32 )
112// This is the source of the WSAEWOULDBLOCK macro on Windows platforms.
113 #include <winerror.h>
114#endif
115
116#if defined ( EWOULDBLOCK )
117 #define PLPLOT_EWOULDBLOCK EWOULDBLOCK
118#elif defined ( WSAEWOULDBLOCK )
119 #define PLPLOT_EWOULDBLOCK WSAEWOULDBLOCK
120#else
121 #error broken system where neither EWOULDBLOCK nor WSAEWOULDBLOCK macros are #defined
122#endif
123
124// Supply dummy macros for the low-level I/O functions - Windows
125#if defined ( _WIN32 )
126#define read( a, b, c ) 0
127#define close( a )
128#define write( a, b, c ) 0
129#endif
130
131//extern int errno;
132
133#ifndef MIN
134#define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) )
135#endif
136
137//
138// This is a "magic number" prepended to the beginning of the packet
139// Used to help resync the packet machanism in the event of errors.
140//
141#define PACKET_MAGIC 0x6feeddcc
142
143//
144// For TCP, it's possible to get a line in pieces. In case everything we
145// want isn't there, we need a place to store partial results when we're
146// in non-blocking mode. The partial buffers below are created
147// dynamically to store incomplete data in these cases.
148//
149
150typedef struct PartialRead
151{
152 char *buffer; // Buffer of characters
153 int bufSize; // Size of buffer
154 int offset; // Offset of current character within buffer
155 struct PartialRead *next; // Next buffer in chain
156} PartialRead;
157
158#define MAX_OPEN_FILES 128
159
160static PartialRead *partial[MAX_OPEN_FILES];
161
162static void pl_FreeReadBuffer( int fd );
163static void pl_Unread( int fd, char *buffer, int numBytes, int copy );
164static int pl_Read( int fd, char *buffer, int numReq );
165
166int pl_PacketReceive( Tcl_Interp * interp, PLiodev *iodev, PDFstrm *pdfs );
167int pl_PacketSend( Tcl_Interp * interp, PLiodev *iodev, PDFstrm *pdfs );
168
169//
170//--------------------------------------------------------------------------
171//
172// pl_FreeReadBuffer --
173//
174// This function is called to free up all the memory associated
175// with a file once the file is closed.
176//
177// Results:
178// None.
179//
180// Side effects:
181// Any data buffered locally will be lost.
182//
183//--------------------------------------------------------------------------
184//
185
186static void
187pl_FreeReadBuffer( int fd )
188{
189 PartialRead *readList;
190
191 while ( partial[fd] != NULL )
192 {
193 readList = partial[fd];
194 partial[fd] = readList->next;
195 free( readList->buffer );
196 free( readList );
197 }
198}
199
200//
201//--------------------------------------------------------------------------
202//
203// pl_Unread --
204//
205// This function puts data back into the read chain on a
206// file descriptor. It's basically an extended "ungetc".
207//
208// Results:
209// None.
210//
211// Side effects:
212// Subsequent calls to pl_Read on the fd will get this data.
213//
214//--------------------------------------------------------------------------
215//
216
217static void
218pl_Unread( int fd, char *buffer, int numBytes, int copy )
219//int fd; // File descriptor
220//char *buffer; // Data to unget
221//int numBytes; // Number of bytes to unget
222//int copy; // Should we copy the data, or use this
223// buffer?
224{
225 PartialRead *new;
226
227 new = (PartialRead *) malloc( sizeof ( PartialRead ) );
228 if ( copy )
229 {
230 new->buffer = (char *) malloc( (size_t) numBytes );
231 memcpy( new->buffer, buffer, (size_t) numBytes );
232 }
233 else
234 {
235 new->buffer = buffer;
236 }
237 new->bufSize = numBytes;
238 new->offset = 0;
239 new->next = partial[fd];
240 partial[fd] = new;
241}
242
243//
244//--------------------------------------------------------------------------
245//
246// pl_Read --
247//
248// This function implements a "read"-like command, but
249// buffers partial reads. The semantics are the same as
250// with read.
251//
252// Results:
253// Number of bytes read, or -1 on error (with errno set).
254//
255// Side effects:
256// All available data is read from the file descriptor.
257//
258//--------------------------------------------------------------------------
259//
260
261static int
262pl_Read( int fd, char *buffer, int numReq )
263//int fd; // File descriptor to read from
264//char *buffer; // Place to put the data
265//int numReq; // Number of bytes to get
266{
267 PartialRead *readList;
268 PartialRead *tmp;
269 int numRead;
270 int numToCopy;
271
272 readList = partial[fd];
273
274 //
275 // If there's no data left over from a previous read, then just do a read
276 // This is the common case.
277 //
278 if ( readList == NULL )
279 {
280 numRead = (int) read( fd, buffer, (size_t) numReq );
281#ifdef DEBUG
282 {
283 int j;
284 fprintf( stderr, "received %d bytes starting with:", numRead );
285 for ( j = 0; j < MIN( 8, numRead ); j++ )
286 fprintf( stderr, " %x", 0x000000FF & (unsigned long) buffer[j] );
287 fprintf( stderr, "\n" );
288 }
289#endif
290 return numRead;
291 }
292
293 //
294 // There's data left over from a previous read. Yank it in and
295 // only call read() if we didn't get enough data (this keeps the fd
296 // readable if they only request as much data as is in the buffers).
297 //
298 numRead = 0;
299 while ( ( readList != NULL ) && ( numRead < numReq ) )
300 {
301 numToCopy = readList->bufSize - readList->offset;
302 if ( numToCopy + numRead > numReq )
303 {
304 numToCopy = numReq - numRead;
305 }
306 memcpy( buffer + numRead, readList->buffer + readList->offset, (size_t) numToCopy );
307
308 //
309 // Consume the data
310 //
311 tmp = readList;
312 readList = readList->next;
313 tmp->offset += numToCopy;
314 if ( tmp->offset == tmp->bufSize )
315 {
316 free( tmp->buffer );
317 free( tmp );
318 partial[fd] = readList;
319 }
320 numRead += numToCopy;
321 }
322
323 //
324 // Only call read if at the end of a previously incomplete packet.
325 //
326 if ( ( numRead < numReq ) )
327 {
328 numToCopy = numReq - numRead;
329 numRead += (int) read( fd, buffer + numRead, (size_t) numToCopy );
330 }
331
332 return numRead;
333}
334
335//--------------------------------------------------------------------------
336// This part for Tcl-DP only
337//--------------------------------------------------------------------------
338
339#ifdef PLD_dp
340
341#include <dp.h>
342
343#include <arpa/inet.h>
344#include <netdb.h>
345#include <netinet/in.h>
346#include <sys/socket.h>
347
348//--------------------------------------------------------------------------
349// plHost_ID
350//
351// Tcl command -- return the IP address for the current host.
352//
353// Derived from source code in "UNIX Network Programming" by W. Richard
354// Stevens, Prentice Hall, 1990.
355//--------------------------------------------------------------------------
356
357static char *
358get_inet( char ** listptr, int length )
359{
360 struct in_addr *ptr;
361
362 while ( ( ptr = (struct in_addr *) *listptr++ ) == NULL )
363 continue;
364
365 return inet_ntoa( *ptr );
366}
367
368int
369plHost_ID( ClientData clientData, Tcl_Interp *interp, int argc, char **argv )
370{
371 register struct hostent *hostptr;
372 char hostname[100];
373
374 if ( gethostname( hostname, 100 ) )
375 {
376 Tcl_AppendResult( interp, "Error -- cannot get host name",
377 (char *) NULL );
378 return TCL_ERROR;
379 }
380
381 if ( ( hostptr = gethostbyname( hostname ) ) == NULL )
382 {
383 Tcl_AppendResult( interp, "Error -- cannot get host info for node ",
384 hostname, (char *) NULL );
385 return TCL_ERROR;
386 }
387
388 Tcl_SetResult( interp,
389 get_inet( hostptr->h_addr_list, hostptr->h_length ),
390 TCL_VOLATILE );
391
392 return TCL_OK;
393}
394
395#endif // PLD_dp
396
397//
398//--------------------------------------------------------------------------
399//
400// pl_PacketReceive --
401//
402// This procedure is a modified version of Tdp_PacketReceive,
403// from the Tcl-DP distribution. It reads the socket,
404// returning a complete packet. If the entire packet cannot
405// be read, the partial packet is buffered until the rest is
406// available. Some capabilities have been removed from the
407// original, such as the check for a non-server TCP socket,
408// since there's no access to the optFlags array from here,
409// and the peek capability, since I don't need it.
410//
411// Results:
412// Packet contents stored in pdfs->buffer and pdfs->bp set
413// to the number of bytes read (zero if incomplete).
414//
415// Side effects:
416// The file descriptor passed in is read.
417//
418//--------------------------------------------------------------------------
419//
420int
421pl_PacketReceive( Tcl_Interp *interp, PLiodev *iodev, PDFstrm *pdfs )
422{
423 int j, numRead;
424 unsigned int packetLen, header[2];
425 int headerSize;
426 unsigned char hbuf[8];
427 const char *errMsg;
428
429 pdfs->bp = 0;
430
431 //
432 // Read in the header (8 bytes)
433 //
434 headerSize = 8;
435 numRead = pl_Read( iodev->fd, (char *) hbuf, headerSize );
436
437 if ( numRead <= 0 )
438 {
439#ifdef DEBUG
440 fprintf( stderr, "Incorrect header read, numRead = %d\n", numRead );
441#endif
442 goto readError;
443 }
444
445 //
446 // Check for incomplete read. If so, put it back and return.
447 //
448 if ( numRead < headerSize )
449 {
450#ifdef DEBUG
451 fprintf( stderr, "Incomplete header read, numRead = %d\n", numRead );
452#endif
453 pl_Unread( iodev->fd, (char *) hbuf, numRead, 1 );
454 Tcl_ResetResult( interp );
455 return TCL_OK;
456 }
457
458 //
459 // Convert header character stream into ints. This works when the
460 // connecting machine has a different size int and takes care of the
461 // endian problem to boot. It is also mostly backward compatible since
462 // network byte ordering (big endian) is used.
463 //
464
465 j = 0;
466
467 header[0] = 0;
468 header[0] |= (unsigned int) ( hbuf[j++] << 24 );
469 header[0] |= (unsigned int) ( hbuf[j++] << 16 );
470 header[0] |= (unsigned int) ( hbuf[j++] << 8 );
471 header[0] |= hbuf[j++];
472
473 header[1] = 0;
474 header[1] |= (unsigned int) ( hbuf[j++] << 24 );
475 header[1] |= (unsigned int) ( hbuf[j++] << 16 );
476 header[1] |= (unsigned int) ( hbuf[j++] << 8 );
477 header[1] |= hbuf[j++];
478
479 //
480 // Format of each packet:
481 //
482 // First 4 bytes are PACKET_MAGIC.
483 // Next 4 bytes are packetLen.
484 // Next packetLen-headerSize is zero terminated string
485 //
486 if ( header[0] != PACKET_MAGIC )
487 {
488 fprintf( stderr, "Badly formatted packet, numRead = %d\n", numRead );
489 Tcl_AppendResult( interp, "Error reading from ", iodev->typeName,
490 ": badly formatted packet", (char *) NULL );
491 return TCL_ERROR;
492 }
493 packetLen = header[1] - (unsigned int) headerSize;
494
495 //
496 // Expand the size of the buffer, as needed.
497 //
498
499 if ( header[1] > (unsigned) pdfs->bufmax )
500 {
501 free( (void *) pdfs->buffer );
502 pdfs->bufmax = header[1] + 32;
503 pdfs->buffer = (unsigned char *) malloc( pdfs->bufmax );
504 }
505
506 //
507 // Read in the packet, and if it's not all there, put it back.
508 //
509 // We have to be careful here, because we could block if just the
510 // header came in (making the file readable at the beginning of this
511 // function) but the rest of the packet is still out on the network.
512 //
513
514 if ( iodev->type == 0 )
515 {
516 numRead = pl_Read( iodev->fd, (char *) pdfs->buffer, (int) packetLen );
517 }
518 else
519 {
520#ifdef PLD_dp
521 if ( Tdp_FDIsReady( iodev->fd ) & TCL_FILE_READABLE )
522 {
523 numRead = pl_Read( iodev->fd, (char *) pdfs->buffer, packetLen );
524 }
525 else
526 {
527#ifdef DEBUG
528 fprintf( stderr, "Packet not ready, putting back header\n" );
529#endif
530 pl_Unread( iodev->fd, (char *) hbuf, headerSize, 1 );
531 Tcl_ResetResult( interp );
532 return TCL_OK;
533 }
534#endif
535 }
536
537 if ( numRead <= 0 )
538 {
539 goto readError;
540 }
541
542 if ( (unsigned) numRead != packetLen )
543 {
544#ifdef DEBUG
545 fprintf( stderr, "Incomplete packet read, numRead = %d\n", numRead );
546#endif
547 pl_Unread( iodev->fd, (char *) pdfs->buffer, numRead, 1 );
548 pl_Unread( iodev->fd, (char *) hbuf, headerSize, 1 );
549 return TCL_OK;
550 }
551
552 pdfs->bp = (size_t) numRead;
553#ifdef DEBUG
554 fprintf( stderr, "received %d byte packet starting with:", numRead );
555 for ( j = 0; j < 4; j++ )
556 {
557 fprintf( stderr, " %x", 0x000000FF & (unsigned long) pdfs->buffer[j] );
558 }
559 fprintf( stderr, "\n" );
560#endif
561
562 return TCL_OK;
563
564readError:
565 //
566 //
567 // If we're in non-blocking mode, and this would block, return.
568 // If the connection is closed (numRead == 0), don't return an
569 // error message. Otherwise, return one.
570 //
571 // In either case, we close the file, delete the file handler, and
572 // return a null string.
573 //
574
575 if ( errno == PLPLOT_EWOULDBLOCK || errno == EAGAIN )
576 {
577 Tcl_ResetResult( interp );
578 return TCL_OK;
579 }
580
581 // Record the error before closing the file
582 if ( numRead != 0 )
583 {
584 errMsg = Tcl_PosixError( interp );
585 }
586 else
587 {
588 errMsg = NULL; // Suppresses spurious compiler warning
589 }
590
591 //
592 // Remove the file handler and close the file.
593 //
594 if ( iodev->type == 0 )
595 {
596// Exclude UNIX-only feature
597#if !defined ( MAC_TCL ) && !defined ( _WIN32 ) && !defined ( __CYGWIN__ )
598 Tk_DeleteFileHandler( iodev->fd );
599#endif
600 close( iodev->fd );
601 }
602 pl_FreeReadBuffer( iodev->fd );
603
604 Tcl_ResetResult( interp );
605 if ( numRead == 0 )
606 {
607 return TCL_OK;
608 }
609 else
610 {
611 Tcl_AppendResult( interp, "pl_PacketReceive -- error reading from ",
612 iodev->typeName, ": ", errMsg, (char *) NULL );
613 return TCL_ERROR;
614 }
615}
616
617//
618//--------------------------------------------------------------------------
619//
620// pl_PacketSend --
621//
622// This procedure is a modified version of Tdp_PacketSend,
623// from the Tcl-DP distribution. It writes a complete packet
624// to a socket or file-oriented device.
625//
626// Results:
627// A standard tcl result.
628//
629// Side effects:
630// The specified buffer is written to the file descriptor passed
631// in.
632//
633//--------------------------------------------------------------------------
634//
635
636int
637pl_PacketSend( Tcl_Interp * interp, PLiodev *iodev, PDFstrm *pdfs )
638{
639 int j, numSent;
640 unsigned char hbuf[8];
641 unsigned int packetLen, header[2];
642 size_t len;
643 char *buffer, tmp[256];
644
645 //
646 // Format up the packet:
647 // First 4 bytes are PACKET_MAGIC.
648 // Next 4 bytes are packetLen.
649 // Next packetLen-8 bytes are buffer contents.
650 //
651
652 packetLen = (unsigned int) pdfs->bp + 8;
653
654 header[0] = PACKET_MAGIC;
655 header[1] = packetLen;
656
657 //
658 // Convert header ints to character stream.
659 // Network byte ordering (big endian) is used.
660 //
661
662 j = 0;
663
664 hbuf[j++] = (unsigned char) ( ( header[0] & (unsigned long) 0xFF000000 ) >> 24 );
665 hbuf[j++] = (unsigned char) ( ( header[0] & (unsigned long) 0x00FF0000 ) >> 16 );
666 hbuf[j++] = (unsigned char) ( ( header[0] & (unsigned long) 0x0000FF00 ) >> 8 );
667 hbuf[j++] = (unsigned char) ( header[0] & (unsigned long) 0x000000FF );
668
669 hbuf[j++] = (unsigned char) ( ( header[1] & (unsigned long) 0xFF000000 ) >> 24 );
670 hbuf[j++] = (unsigned char) ( ( header[1] & (unsigned long) 0x00FF0000 ) >> 16 );
671 hbuf[j++] = (unsigned char) ( ( header[1] & (unsigned long) 0x0000FF00 ) >> 8 );
672 hbuf[j++] = (unsigned char) ( header[1] & (unsigned long) 0x000000FF );
673
674 //
675 // Send it off, with error checking.
676 // Simulate writev using memcpy to put together
677 // the msg so it can go out in a single write() call.
678 //
679
680 len = pdfs->bp + 8;
681 buffer = (char *) malloc( len );
682
683 memcpy( buffer, (char *) hbuf, 8 );
684 memcpy( buffer + 8, (char *) pdfs->buffer, pdfs->bp );
685
686#ifdef DEBUG
687 fprintf( stderr, "sending %z byte packet starting with:", len );
688 for ( j = 0; j < 12; j++ )
689 {
690 fprintf( stderr, " %x", 0x000000FF & (unsigned long) buffer[j] );
691 }
692 fprintf( stderr, "\n" );
693#endif
694 numSent = (int) write( iodev->fd, buffer, len );
695
696 free( buffer );
697
698 if ( (unsigned) numSent != packetLen )
699 {
700 if ( ( errno == 0 ) || ( errno == PLPLOT_EWOULDBLOCK || errno == EAGAIN ) )
701 {
702 //
703 // Non-blocking I/O: return number of bytes actually sent.
704 //
705 Tcl_ResetResult( interp );
706 sprintf( tmp, "%d", numSent - 8 );
707 Tcl_SetResult( interp, tmp, TCL_VOLATILE );
708 return TCL_OK;
709 }
710 else if ( errno == EPIPE )
711 {
712 //
713 // Got a broken pipe signal, which means the far end closed
714 // the connection. Close the file and return 0 bytes sent.
715 //
716 if ( iodev->type == 0 )
717 {
718 close( iodev->fd );
719 }
720 sprintf( tmp, "0" );
721 Tcl_SetResult( interp, tmp, TCL_VOLATILE );
722 return TCL_OK;
723 }
724 else
725 {
726 Tcl_AppendResult( interp, "pl_PacketSend -- error writing to ",
727 iodev->typeName, ": ",
728 Tcl_PosixError( interp ), (char *) NULL );
729 }
730
731 return TCL_ERROR;
732 }
733
734 //
735 // Return the number of bytes sent (minus the header).
736 //
737 sprintf( tmp, "%d", numSent - 8 );
738 Tcl_SetResult( interp, tmp, TCL_VOLATILE );
739 return TCL_OK;
740}
741
742#else
743int
745{
746 return 0;
747}
748
749#endif // defined(PLD_tk) || defined (ENABLE_tkX)
const char header[]
Definition: deltaT-gen.c:41
#define MIN(a, b)
Definition: dsplint.c:29
static PLINT * buffer
Definition: plfill.c:74
int plHost_ID(ClientData clientData, Tcl_Interp *interp, int argc, const char **argv)
static int argc
Definition: qt.cpp:48
static char ** argv
Definition: qt.cpp:49
Definition: pdf.h:50
size_t bufmax
Definition: pdf.h:56
unsigned char * buffer
Definition: pdf.h:52
size_t bp
Definition: pdf.h:56
Definition: pdf.h:62
int fd
Definition: pdf.h:63
int type
Definition: pdf.h:67
const char * typeName
Definition: pdf.h:68
int pldummy_tcpip()
Definition: tcpip.c:744
PLDLLIMPEXP_TCLTK int pl_PacketReceive(Tcl_Interp *interp, PLiodev *iodev, PDFstrm *pdfs)
PLDLLIMPEXP_TCLTK int pl_PacketSend(Tcl_Interp *interp, PLiodev *iodev, PDFstrm *pdfs)
static Tcl_Interp * interp
Definition: tkMain.c:120