1 /*
2  * tkSend.c --
3  *
4  *	This file provides procedures that implement the "send"
5  *	command, allowing commands to be passed from interpreter
6  *	to interpreter.
7  *
8  * Copyright (c) 1989-1994 The Regents of the University of California.
9  * Copyright (c) 1994-1996 Sun Microsystems, Inc.
10  *
11  * See the file "license.terms" for information on usage and redistribution
12  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13  *
14  * SCCS: @(#) tkSend.c 1.64 96/07/20 17:38:32
15  */
16 
17 #include "tkInt.h"
18 
19 /*
20  * The following structure is used to keep track of the interpreters
21  * registered by this process.
22  */
23 
24 typedef struct RegisteredInterp {
25     char *name;			/* Interpreter's name (malloc-ed). */
26     Tcl_Interp *interp;		/* Interpreter associated with name.  NULL
27 				 * means that the application was unregistered
28 				 * or deleted while a send was in progress
29 				 * to it. */
30     TkDisplay *dispPtr;		/* Display for the application.  Needed
31 				 * because we may need to unregister the
32 				 * interpreter after its main window has
33 				 * been deleted. */
34     struct RegisteredInterp *nextPtr;
35 				/* Next in list of names associated
36 				 * with interps in this process.
37 				 * NULL means end of list. */
38 } RegisteredInterp;
39 
40 static RegisteredInterp *registry = NULL;
41 				/* List of all interpreters
42 				 * registered by this process. */
43 
44 /*
45  * A registry of all interpreters for a display is kept in a
46  * property "InterpRegistry" on the root window of the display.
47  * It is organized as a series of zero or more concatenated strings
48  * (in no particular order), each of the form
49  * 	window space name '\0'
50  * where "window" is the hex id of the comm. window to use to talk
51  * to an interpreter named "name".
52  *
53  * When the registry is being manipulated by an application (e.g. to
54  * add or remove an entry), it is loaded into memory using a structure
55  * of the following type:
56  */
57 
58 typedef struct NameRegistry {
59     TkDisplay *dispPtr;		/* Display from which the registry was
60 				 * read. */
61     int locked;			/* Non-zero means that the display was
62 				 * locked when the property was read in. */
63     int modified;		/* Non-zero means that the property has
64 				 * been modified, so it needs to be written
65 				 * out when the NameRegistry is closed. */
66     unsigned long propLength;	/* Length of the property, in bytes. */
67     char *property;		/* The contents of the property, or NULL
68 				 * if none.  See format description above;
69 				 * this is *not* terminated by the first
70 				 * null character.  Dynamically allocated. */
71     int allocedByX;		/* Non-zero means must free property with
72 				 * XFree;  zero means use ckfree. */
73 } NameRegistry;
74 
75 /*
76  * When a result is being awaited from a sent command, one of
77  * the following structures is present on a list of all outstanding
78  * sent commands.  The information in the structure is used to
79  * process the result when it arrives.  You're probably wondering
80  * how there could ever be multiple outstanding sent commands.
81  * This could happen if interpreters invoke each other recursively.
82  * It's unlikely, but possible.
83  */
84 
85 typedef struct PendingCommand {
86     int serial;			/* Serial number expected in
87 				 * result. */
88     TkDisplay *dispPtr;		/* Display being used for communication. */
89     char *target;		/* Name of interpreter command is
90 				 * being sent to. */
91     Window commWindow;		/* Target's communication window. */
92     Tcl_Interp *interp;		/* Interpreter from which the send
93 				 * was invoked. */
94     int code;			/* Tcl return code for command
95 				 * will be stored here. */
96     char *result;		/* String result for command (malloc'ed),
97 				 * or NULL. */
98     char *errorInfo;		/* Information for "errorInfo" variable,
99 				 * or NULL (malloc'ed). */
100     char *errorCode;		/* Information for "errorCode" variable,
101 				 * or NULL (malloc'ed). */
102     int gotResponse;		/* 1 means a response has been received,
103 				 * 0 means the command is still outstanding. */
104     struct PendingCommand *nextPtr;
105 				/* Next in list of all outstanding
106 				 * commands.  NULL means end of
107 				 * list. */
108 } PendingCommand;
109 
110 static PendingCommand *pendingCommands = NULL;
111 				/* List of all commands currently
112 				 * being waited for. */
113 
114 /*
115  * The information below is used for communication between processes
116  * during "send" commands.  Each process keeps a private window, never
117  * even mapped, with one property, "Comm".  When a command is sent to
118  * an interpreter, the command is appended to the comm property of the
119  * communication window associated with the interp's process.  Similarly,
120  * when a result is returned from a sent command, it is also appended
121  * to the comm property.
122  *
123  * Each command and each result takes the form of ASCII text.  For a
124  * command, the text consists of a zero character followed by several
125  * null-terminated ASCII strings.  The first string consists of the
126  * single letter "c".  Subsequent strings have the form "option value"
127  * where the following options are supported:
128  *
129  * -r commWindow serial
130  *
131  *	This option means that a response should be sent to the window
132  *	whose X identifier is "commWindow" (in hex), and the response should
133  *	be identified with the serial number given by "serial" (in decimal).
134  *	If this option isn't specified then the send is asynchronous and
135  *	no response is sent.
136  *
137  * -n name
138  *	"Name" gives the name of the application for which the command is
139  *	intended.  This option must be present.
140  *
141  * -s script
142  *
143  *	"Script" is the script to be executed.  This option must be present.
144  *
145  * The options may appear in any order.  The -n and -s options must be
146  * present, but -r may be omitted for asynchronous RPCs.  For compatibility
147  * with future releases that may add new features, there may be additional
148  * options present;  as long as they start with a "-" character, they will
149  * be ignored.
150  *
151  * A result also consists of a zero character followed by several null-
152  * terminated ASCII strings.  The first string consists of the single
153  * letter "r".  Subsequent strings have the form "option value" where
154  * the following options are supported:
155  *
156  * -s serial
157  *
158  *	Identifies the command for which this is the result.  It is the
159  *	same as the "serial" field from the -s option in the command.  This
160  *	option must be present.
161  *
162  * -c code
163  *
164  *	"Code" is the completion code for the script, in decimal.  If the
165  *	code is omitted it defaults to TCL_OK.
166  *
167  * -r result
168  *
169  *	"Result" is the result string for the script, which may be either
170  *	a result or an error message.  If this field is omitted then it
171  *	defaults to an empty string.
172  *
173  * -i errorInfo
174  *
175  *	"ErrorInfo" gives a string with which to initialize the errorInfo
176  *	variable.  This option may be omitted;  it is ignored unless the
177  *	completion code is TCL_ERROR.
178  *
179  * -e errorCode
180  *
181  *	"ErrorCode" gives a string with with to initialize the errorCode
182  *	variable.  This option may be omitted;  it is ignored  unless the
183  *	completion code is TCL_ERROR.
184  *
185  * Options may appear in any order, and only the -s option must be
186  * present.  As with commands, there may be additional options besides
187  * these;  unknown options are ignored.
188  */
189 
190 /*
191  * The following variable is the serial number that was used in the
192  * last "send" command.  It is exported only for testing purposes.
193  */
194 
195 int tkSendSerial = 0;
196 
197 /*
198  * Maximum size property that can be read at one time by
199  * this module:
200  */
201 
202 #define MAX_PROP_WORDS 100000
203 
204 /*
205  * The following variable can be set while debugging to do things like
206  * skip locking the server.
207  */
208 
209 static int sendDebug = 0;
210 
211 /*
212  * Forward declarations for procedures defined later in this file:
213  */
214 
215 static int		AppendErrorProc _ANSI_ARGS_((ClientData clientData,
216 				XErrorEvent *errorPtr));
217 static void		AppendPropCarefully _ANSI_ARGS_((Display *display,
218 			    Window window, Atom property, char *value,
219 			    int length, PendingCommand *pendingPtr));
220 static void		DeleteProc _ANSI_ARGS_((ClientData clientData));
221 static void		RegAddName _ANSI_ARGS_((NameRegistry *regPtr,
222 			    char *name, Window commWindow));
223 static void		RegClose _ANSI_ARGS_((NameRegistry *regPtr));
224 static void		RegDeleteName _ANSI_ARGS_((NameRegistry *regPtr,
225 			    char *name));
226 static Window		RegFindName _ANSI_ARGS_((NameRegistry *regPtr,
227 			    char *name));
228 static NameRegistry *	RegOpen _ANSI_ARGS_((Tcl_Interp *interp,
229 			    TkDisplay *dispPtr, int lock));
230 static void		SendEventProc _ANSI_ARGS_((ClientData clientData,
231 			    XEvent *eventPtr));
232 static int		SendInit _ANSI_ARGS_((Tcl_Interp *interp,
233 			    TkDisplay *dispPtr));
234 static Tk_RestrictAction SendRestrictProc _ANSI_ARGS_((ClientData clientData,
235 			    XEvent *eventPtr));
236 static int		ServerSecure _ANSI_ARGS_((TkDisplay *dispPtr));
237 static void		TimeoutProc _ANSI_ARGS_((ClientData clientData));
238 static void		UpdateCommWindow _ANSI_ARGS_((TkDisplay *dispPtr));
239 static int		ValidateName _ANSI_ARGS_((TkDisplay *dispPtr,
240 			    char *name, Window commWindow, int oldOK));
241 
242 /*
243  *----------------------------------------------------------------------
244  *
245  * RegOpen --
246  *
247  *	This procedure loads the name registry for a display into
248  *	memory so that it can be manipulated.
249  *
250  * Results:
251  *	The return value is a pointer to the loaded registry.
252  *
253  * Side effects:
254  *	If "lock" is set then the server will be locked.  It is the
255  *	caller's responsibility to call RegClose when finished with
256  *	the registry, so that we can write back the registry if
257  *	neeeded, unlock the server if needed, and free memory.
258  *
259  *----------------------------------------------------------------------
260  */
261 
262 static NameRegistry *
RegOpen(interp,dispPtr,lock)263 RegOpen(interp, dispPtr, lock)
264     Tcl_Interp *interp;		/* Interpreter to use for error reporting
265 				 * (errors cause a panic so in fact no
266 				 * error is ever returned, but the interpreter
267 				 * is needed anyway). */
268     TkDisplay *dispPtr;		/* Display whose name registry is to be
269 				 * opened. */
270     int lock;			/* Non-zero means lock the window server
271 				 * when opening the registry, so no-one
272 				 * else can use the registry until we
273 				 * close it. */
274 {
275     NameRegistry *regPtr;
276     int result, actualFormat;
277     unsigned long bytesAfter;
278     Atom actualType;
279 
280     if (dispPtr->commTkwin == NULL) {
281 	SendInit(interp, dispPtr);
282     }
283 
284     regPtr = (NameRegistry *) ckalloc(sizeof(NameRegistry));
285     regPtr->dispPtr = dispPtr;
286     regPtr->locked = 0;
287     regPtr->modified = 0;
288     regPtr->allocedByX = 1;
289 
290     if (lock && !sendDebug) {
291 	XGrabServer(dispPtr->display);
292 	regPtr->locked = 1;
293     }
294 
295     /*
296      * Read the registry property.
297      */
298 
299     result = XGetWindowProperty(dispPtr->display,
300 	    RootWindow(dispPtr->display, 0),
301 	    dispPtr->registryProperty, 0, MAX_PROP_WORDS,
302 	    False, XA_STRING, &actualType, &actualFormat,
303 	    &regPtr->propLength, &bytesAfter,
304 	    (unsigned char **) &regPtr->property);
305 
306     if (actualType == None) {
307 	regPtr->propLength = 0;
308 	regPtr->property = NULL;
309     } else if ((result != Success) || (actualFormat != 8)
310 	    || (actualType != XA_STRING)) {
311 	/*
312 	 * The property is improperly formed;  delete it.
313 	 */
314 
315 	if (regPtr->property != NULL) {
316 	    XFree(regPtr->property);
317 	    regPtr->propLength = 0;
318 	    regPtr->property = NULL;
319 	}
320 	XDeleteProperty(dispPtr->display,
321 		RootWindow(dispPtr->display, 0),
322 		dispPtr->registryProperty);
323     }
324 
325     /*
326      * Xlib placed an extra null byte after the end of the property, just
327      * to make sure that it is always NULL-terminated.  Be sure to include
328      * this byte in our count if it's needed to ensure null termination
329      * (note: as of 8/95 I'm no longer sure why this code is needed;  seems
330      * like it shouldn't be).
331      */
332 
333     if ((regPtr->propLength > 0)
334 	    && (regPtr->property[regPtr->propLength-1] != 0)) {
335 	regPtr->propLength++;
336     }
337     return regPtr;
338 }
339 
340 /*
341  *----------------------------------------------------------------------
342  *
343  * RegFindName --
344  *
345  *	Given an open name registry, this procedure finds an entry
346  *	with a given name, if there is one, and returns information
347  *	about that entry.
348  *
349  * Results:
350  *	The return value is the X identifier for the comm window for
351  *	the application named "name", or None if there is no such
352  *	entry in the registry.
353  *
354  * Side effects:
355  *	None.
356  *
357  *----------------------------------------------------------------------
358  */
359 
360 static Window
RegFindName(regPtr,name)361 RegFindName(regPtr, name)
362     NameRegistry *regPtr;	/* Pointer to a registry opened with a
363 				 * previous call to RegOpen. */
364     char *name;			/* Name of an application. */
365 {
366     char *p, *entry;
367     Window commWindow;
368 
369     commWindow = None;
370     for (p = regPtr->property; (p-regPtr->property) < regPtr->propLength; ) {
371 	entry = p;
372 	while ((*p != 0) && (!isspace(UCHAR(*p)))) {
373 	    p++;
374 	}
375 	if ((*p != 0) && (strcmp(name, p+1) == 0)) {
376 	    if (sscanf(entry, "%x", (unsigned int *) &commWindow) == 1) {
377 		return commWindow;
378 	    }
379 	}
380 	while (*p != 0) {
381 	    p++;
382 	}
383 	p++;
384     }
385     return None;
386 }
387 
388 /*
389  *----------------------------------------------------------------------
390  *
391  * RegDeleteName --
392  *
393  *	This procedure deletes the entry for a given name from
394  *	an open registry.
395  *
396  * Results:
397  *	None.
398  *
399  * Side effects:
400  *	If there used to be an entry named "name" in the registry,
401  *	then it is deleted and the registry is marked as modified
402  *	so it will be written back when closed.
403  *
404  *----------------------------------------------------------------------
405  */
406 
407 static void
RegDeleteName(regPtr,name)408 RegDeleteName(regPtr, name)
409     NameRegistry *regPtr;	/* Pointer to a registry opened with a
410 				 * previous call to RegOpen. */
411     char *name;			/* Name of an application. */
412 {
413     char *p, *entry, *entryName;
414     int count;
415 
416     for (p = regPtr->property; (p-regPtr->property) < regPtr->propLength; ) {
417 	entry = p;
418 	while ((*p != 0) && (!isspace(UCHAR(*p)))) {
419 	    p++;
420 	}
421 	if (*p != 0) {
422 	    p++;
423 	}
424 	entryName = p;
425 	while (*p != 0) {
426 	    p++;
427 	}
428 	p++;
429 	if ((strcmp(name, entryName) == 0)) {
430 	    /*
431 	     * Found the matching entry.  Copy everything after it
432 	     * down on top of it.
433 	     */
434 
435 	    count = regPtr->propLength - (p - regPtr->property);
436 	    if (count > 0)  {
437 		memmove((VOID *) entry, (VOID *) p, (size_t) count);
438 	    }
439 	    regPtr->propLength -=  p - entry;
440 	    regPtr->modified = 1;
441 	    return;
442 	}
443     }
444 }
445 
446 /*
447  *----------------------------------------------------------------------
448  *
449  * RegAddName --
450  *
451  *	Add a new entry to an open registry.
452  *
453  * Results:
454  *	None.
455  *
456  * Side effects:
457  *	The open registry is expanded;  it is marked as modified so that
458  *	it will be written back when closed.
459  *
460  *----------------------------------------------------------------------
461  */
462 
463 static void
RegAddName(regPtr,name,commWindow)464 RegAddName(regPtr, name, commWindow)
465     NameRegistry *regPtr;	/* Pointer to a registry opened with a
466 				 * previous call to RegOpen. */
467     char *name;			/* Name of an application.  The caller
468 				 * must ensure that this name isn't
469 				 * already registered. */
470     Window commWindow;		/* X identifier for comm. window of
471 				 * application.  */
472 {
473     char id[30];
474     char *newProp;
475     int idLength, newBytes;
476 
477     sprintf(id, "%x ", (unsigned int) commWindow);
478     idLength = strlen(id);
479     newBytes = idLength + strlen(name) + 1;
480     newProp = (char *) ckalloc((unsigned) (regPtr->propLength + newBytes));
481     strcpy(newProp, id);
482     strcpy(newProp+idLength, name);
483     if (regPtr->property != NULL) {
484 	memcpy((VOID *) (newProp + newBytes), (VOID *) regPtr->property,
485 		regPtr->propLength);
486 	if (regPtr->allocedByX) {
487 	    XFree(regPtr->property);
488 	} else {
489 	    ckfree(regPtr->property);
490 	}
491     }
492     regPtr->modified = 1;
493     regPtr->propLength += newBytes;
494     regPtr->property = newProp;
495     regPtr->allocedByX = 0;
496 }
497 
498 /*
499  *----------------------------------------------------------------------
500  *
501  * RegClose --
502  *
503  *	This procedure is called to end a series of operations on
504  *	a name registry.
505  *
506  * Results:
507  *	None.
508  *
509  * Side effects:
510  *	The registry is written back if it has been modified, and the
511  *	X server is unlocked if it was locked.  Memory for the
512  *	registry is freed, so the caller should never use regPtr
513  *	again.
514  *
515  *----------------------------------------------------------------------
516  */
517 
518 static void
RegClose(regPtr)519 RegClose(regPtr)
520     NameRegistry *regPtr;	/* Pointer to a registry opened with a
521 				 * previous call to RegOpen. */
522 {
523     if (regPtr->modified) {
524 	if (!regPtr->locked && !sendDebug) {
525 	    panic("The name registry was modified without being locked!");
526 	}
527 	XChangeProperty(regPtr->dispPtr->display,
528 		RootWindow(regPtr->dispPtr->display, 0),
529 		regPtr->dispPtr->registryProperty, XA_STRING, 8,
530 		PropModeReplace, (unsigned char *) regPtr->property,
531 		(int) regPtr->propLength);
532     }
533 
534     if (regPtr->locked) {
535 	XUngrabServer(regPtr->dispPtr->display);
536     }
537     XFlush(regPtr->dispPtr->display);
538 
539     if (regPtr->property != NULL) {
540 	if (regPtr->allocedByX) {
541 	    XFree(regPtr->property);
542 	} else {
543 	    ckfree(regPtr->property);
544 	}
545     }
546     ckfree((char *) regPtr);
547 }
548 
549 /*
550  *----------------------------------------------------------------------
551  *
552  * ValidateName --
553  *
554  *	This procedure checks to see if an entry in the registry
555  *	is still valid.
556  *
557  * Results:
558  *	The return value is 1 if the given commWindow exists and its
559  *	name is "name".  Otherwise 0 is returned.
560  *
561  * Side effects:
562  *	None.
563  *
564  *----------------------------------------------------------------------
565  */
566 
567 static int
ValidateName(dispPtr,name,commWindow,oldOK)568 ValidateName(dispPtr, name, commWindow, oldOK)
569     TkDisplay *dispPtr;		/* Display for which to perform the
570 				 * validation. */
571     char *name;			/* The name of an application. */
572     Window commWindow;		/* X identifier for the application's
573 				 * comm. window. */
574     int oldOK;			/* Non-zero means that we should consider
575 				 * an application to be valid even if it
576 				 * looks like an old-style (pre-4.0) one;
577 				 * 0 means consider these invalid. */
578 {
579     int result, actualFormat, argc, i;
580     unsigned long length, bytesAfter;
581     Atom actualType;
582     char *property;
583     Tk_ErrorHandler handler;
584     char **argv;
585 
586     property = NULL;
587 
588     /*
589      * Ignore X errors when reading the property (e.g., the window
590      * might not exist).  If an error occurs, result will be some
591      * value other than Success.
592      */
593 
594     handler = Tk_CreateErrorHandler(dispPtr->display, -1, -1, -1,
595 	    (Tk_ErrorProc *) NULL, (ClientData) NULL);
596     result = XGetWindowProperty(dispPtr->display, commWindow,
597 	    dispPtr->appNameProperty, 0, MAX_PROP_WORDS,
598 	    False, XA_STRING, &actualType, &actualFormat,
599 	    &length, &bytesAfter, (unsigned char **) &property);
600 
601     if ((result == Success) && (actualType == None)) {
602 	XWindowAttributes atts;
603 
604 	/*
605 	 * The comm. window exists but the property we're looking for
606 	 * doesn't exist.  This probably means that the application
607 	 * comes from an older version of Tk (< 4.0) that didn't set the
608 	 * property;  if this is the case, then assume for compatibility's
609 	 * sake that everything's OK.  However, it's also possible that
610 	 * some random application has re-used the window id for something
611 	 * totally unrelated.  Check a few characteristics of the window,
612 	 * such as its dimensions and mapped state, to be sure that it
613 	 * still "smells" like a commWindow.
614 	 */
615 
616 	if (!oldOK
617 		|| !XGetWindowAttributes(dispPtr->display, commWindow, &atts)
618 		|| (atts.width != 1) || (atts.height != 1)
619 		|| (atts.map_state != IsUnmapped)) {
620 	    result = 0;
621 	} else {
622 	    result = 1;
623 	}
624     } else if ((result == Success) && (actualFormat == 8)
625 	   && (actualType == XA_STRING)) {
626 	result = 0;
627 	if (Tcl_SplitList((Tcl_Interp *) NULL, property, &argc, &argv)
628 		== TCL_OK) {
629 	    for (i = 0; i < argc; i++) {
630 		if (strcmp(argv[i], name) == 0) {
631 		    result = 1;
632 		    break;
633 		}
634 	    }
635 	    ckfree((char *) argv);
636 	}
637     } else {
638        result = 0;
639     }
640     Tk_DeleteErrorHandler(handler);
641     if (property != NULL) {
642 	XFree(property);
643     }
644     return result;
645 }
646 
647 /*
648  *----------------------------------------------------------------------
649  *
650  * ServerSecure --
651  *
652  *	Check whether a server is secure enough for us to trust
653  *	Tcl scripts arriving via that server.
654  *
655  * Results:
656  *	The return value is 1 if the server is secure, which means
657  *	that host-style authentication is turned on but there are
658  *	no hosts in the enabled list.  This means that some other
659  *	form of authorization (presumably more secure, such as xauth)
660  *	is in use.
661  *
662  * Side effects:
663  *	None.
664  *
665  *----------------------------------------------------------------------
666  */
667 
668 static int
ServerSecure(dispPtr)669 ServerSecure(dispPtr)
670     TkDisplay *dispPtr;		/* Display to check. */
671 {
672 #ifdef TK_NO_SECURITY
673     return 1;
674 #else
675     XHostAddress *addrPtr;
676     int numHosts, secure;
677     Bool enabled;
678 
679     secure = 0;
680     addrPtr = XListHosts(dispPtr->display, &numHosts, &enabled);
681     if (enabled && (numHosts == 0)) {
682 	secure = 1;
683     }
684     if (addrPtr != NULL) {
685 	XFree((char *) addrPtr);
686     }
687     return secure;
688 #endif /* TK_NO_SECURITY */
689 }
690 
691 /*
692  *--------------------------------------------------------------
693  *
694  * Tk_SetAppName --
695  *
696  *	This procedure is called to associate an ASCII name with a Tk
697  *	application.  If the application has already been named, the
698  *	name replaces the old one.
699  *
700  * Results:
701  *	The return value is the name actually given to the application.
702  *	This will normally be the same as name, but if name was already
703  *	in use for an application then a name of the form "name #2" will
704  *	be chosen,  with a high enough number to make the name unique.
705  *
706  * Side effects:
707  *	Registration info is saved, thereby allowing the "send" command
708  *	to be used later to invoke commands in the application.  In
709  *	addition, the "send" command is created in the application's
710  *	interpreter.  The registration will be removed automatically
711  *	if the interpreter is deleted or the "send" command is removed.
712  *
713  *--------------------------------------------------------------
714  */
715 
716 char *
Tk_SetAppName(tkwin,name)717 Tk_SetAppName(tkwin, name)
718     Tk_Window tkwin;		/* Token for any window in the application
719 				 * to be named:  it is just used to identify
720 				 * the application and the display.  */
721     char *name;			/* The name that will be used to
722 				 * refer to the interpreter in later
723 				 * "send" commands.  Must be globally
724 				 * unique. */
725 {
726     RegisteredInterp *riPtr, *riPtr2;
727     Window w;
728     TkWindow *winPtr = (TkWindow *) tkwin;
729     TkDisplay *dispPtr;
730     NameRegistry *regPtr;
731     Tcl_Interp *interp;
732     char *actualName;
733     Tcl_DString dString;
734     int offset, i;
735 
736 #ifdef WIN_TCL
737     return name;
738 #endif /* WIN_TCL */
739 
740     dispPtr = winPtr->dispPtr;
741     interp = winPtr->mainPtr->interp;
742     if (dispPtr->commTkwin == NULL) {
743 	SendInit(interp, winPtr->dispPtr);
744     }
745 
746     /*
747      * See if the application is already registered;  if so, remove its
748      * current name from the registry.
749      */
750 
751     regPtr = RegOpen(interp, winPtr->dispPtr, 1);
752     for (riPtr = registry; ; riPtr = riPtr->nextPtr) {
753 	if (riPtr == NULL) {
754 	    /*
755 	     * This interpreter isn't currently registered;  create
756 	     * the data structure that will be used to register it locally,
757 	     * plus add the "send" command to the interpreter.
758 	     */
759 
760 	    riPtr = (RegisteredInterp *) ckalloc(sizeof(RegisteredInterp));
761 	    riPtr->interp = interp;
762 	    riPtr->dispPtr = winPtr->dispPtr;
763 	    riPtr->nextPtr = registry;
764 	    registry = riPtr;
765 	    Tcl_CreateCommand(interp, "send", Tk_SendCmd, (ClientData) riPtr,
766 		    DeleteProc);
767 	    break;
768 	}
769 	if (riPtr->interp == interp) {
770 	    /*
771 	     * The interpreter is currently registered;  remove it from
772 	     * the name registry.
773 	     */
774 
775 	    RegDeleteName(regPtr, riPtr->name);
776 	    ckfree(riPtr->name);
777 	    break;
778 	}
779     }
780 
781     /*
782      * Pick a name to use for the application.  Use "name" if it's not
783      * already in use.  Otherwise add a suffix such as " #2", trying
784      * larger and larger numbers until we eventually find one that is
785      * unique.
786      */
787 
788     actualName = name;
789     offset = 0;				/* Needed only to avoid "used before
790 					 * set" compiler warnings. */
791     for (i = 1; ; i++) {
792 	if (i > 1) {
793 	    if (i == 2) {
794 		Tcl_DStringInit(&dString);
795 		Tcl_DStringAppend(&dString, name, -1);
796 		Tcl_DStringAppend(&dString, " #", 2);
797 		offset = Tcl_DStringLength(&dString);
798 		Tcl_DStringSetLength(&dString, offset+10);
799 		actualName = Tcl_DStringValue(&dString);
800 	    }
801 	    sprintf(actualName + offset, "%d", i);
802 	}
803 	w = RegFindName(regPtr, actualName);
804 	if (w == None) {
805 	    break;
806 	}
807 
808 	/*
809 	 * The name appears to be in use already, but double-check to
810 	 * be sure (perhaps the application died without removing its
811 	 * name from the registry?).
812 	 */
813 
814 	if (w == Tk_WindowId(dispPtr->commTkwin)) {
815 	    for (riPtr2 = registry; riPtr2 != NULL; riPtr2 = riPtr2->nextPtr) {
816 		if ((riPtr2->interp != interp) &&
817 			(strcmp(riPtr2->name, actualName) == 0)) {
818 		    goto nextSuffix;
819 		}
820 	    }
821 	    RegDeleteName(regPtr, actualName);
822 	    break;
823 	} else if (!ValidateName(winPtr->dispPtr, actualName, w, 1)) {
824 	    RegDeleteName(regPtr, actualName);
825 	    break;
826 	}
827 	nextSuffix:
828 	continue;
829     }
830 
831     /*
832      * We've now got a name to use.  Store it in the name registry and
833      * in the local entry for this application, plus put it in a property
834      * on the commWindow.
835      */
836 
837     RegAddName(regPtr, actualName, Tk_WindowId(dispPtr->commTkwin));
838     RegClose(regPtr);
839     riPtr->name = (char *) ckalloc((unsigned) (strlen(actualName) + 1));
840     strcpy(riPtr->name, actualName);
841     if (actualName != name) {
842 	Tcl_DStringFree(&dString);
843     }
844     UpdateCommWindow(dispPtr);
845 
846     return riPtr->name;
847 }
848 
849 /*
850  *--------------------------------------------------------------
851  *
852  * Tk_SendCmd --
853  *
854  *	This procedure is invoked to process the "send" Tcl command.
855  *	See the user documentation for details on what it does.
856  *
857  * Results:
858  *	A standard Tcl result.
859  *
860  * Side effects:
861  *	See the user documentation.
862  *
863  *--------------------------------------------------------------
864  */
865 
866 int
Tk_SendCmd(clientData,interp,argc,argv)867 Tk_SendCmd(clientData, interp, argc, argv)
868     ClientData clientData;		/* Information about sender (only
869 					 * dispPtr field is used). */
870     Tcl_Interp *interp;			/* Current interpreter. */
871     int argc;				/* Number of arguments. */
872     char **argv;			/* Argument strings. */
873 {
874     TkWindow *winPtr;
875     Window commWindow;
876     PendingCommand pending;
877     register RegisteredInterp *riPtr;
878     char *destName, buffer[30];
879     int result, c, async, i, firstArg;
880     size_t length;
881     Tk_RestrictProc *prevRestrictProc;
882     ClientData prevArg;
883     TkDisplay *dispPtr;
884     NameRegistry *regPtr;
885     Tcl_DString request;
886     Tcl_Interp *localInterp;		/* Used when the interpreter to
887                                          * send the command to is within
888                                          * the same process. */
889 
890     /*
891      * Process options, if any.
892      */
893 
894     async = 0;
895     winPtr = (TkWindow *) Tk_MainWindow(interp);
896     if (winPtr == NULL) {
897 	return TCL_ERROR;
898     }
899     for (i = 1; i < (argc-1); ) {
900 	if (argv[i][0] != '-') {
901 	    break;
902 	}
903 	c = argv[i][1];
904 	length = strlen(argv[i]);
905 	if ((c == 'a') && (strncmp(argv[i], "-async", length) == 0)) {
906 	    async = 1;
907 	    i++;
908 	} else if ((c == 'd') && (strncmp(argv[i], "-displayof",
909 		length) == 0)) {
910 	    winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[i+1],
911 		    (Tk_Window) winPtr);
912 	    if (winPtr == NULL) {
913 		return TCL_ERROR;
914 	    }
915 	    i += 2;
916 	} else if (strcmp(argv[i], "--") == 0) {
917 	    i++;
918 	    break;
919 	} else {
920 	    Tcl_AppendResult(interp, "bad option \"", argv[i],
921 		    "\": must be -async, -displayof, or --", (char *) NULL);
922 	    return TCL_ERROR;
923 	}
924     }
925 
926     if (argc < (i+2)) {
927 	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
928 		" ?options? interpName arg ?arg ...?\"", (char *) NULL);
929 	return TCL_ERROR;
930     }
931     destName = argv[i];
932     firstArg = i+1;
933 
934     dispPtr = winPtr->dispPtr;
935     if (dispPtr->commTkwin == NULL) {
936 	SendInit(interp, winPtr->dispPtr);
937     }
938 
939     /*
940      * See if the target interpreter is local.  If so, execute
941      * the command directly without going through the X server.
942      * The only tricky thing is passing the result from the target
943      * interpreter to the invoking interpreter.  Watch out:  they
944      * could be the same!
945      */
946 
947     for (riPtr = registry; riPtr != NULL; riPtr = riPtr->nextPtr) {
948 	if ((riPtr->dispPtr != dispPtr)
949 		|| (strcmp(riPtr->name, destName) != 0)) {
950 	    continue;
951 	}
952 	Tcl_Preserve((ClientData) riPtr);
953         localInterp = riPtr->interp;
954         Tcl_Preserve((ClientData) localInterp);
955 	if (firstArg == (argc-1)) {
956 	    result = Tcl_GlobalEval(localInterp, argv[firstArg]);
957 	} else {
958 	    Tcl_DStringInit(&request);
959 	    Tcl_DStringAppend(&request, argv[firstArg], -1);
960 	    for (i = firstArg+1; i < argc; i++) {
961 		Tcl_DStringAppend(&request, " ", 1);
962 		Tcl_DStringAppend(&request, argv[i], -1);
963 	    }
964 	    result = Tcl_GlobalEval(localInterp, Tcl_DStringValue(&request));
965 	    Tcl_DStringFree(&request);
966 	}
967 	if (interp != localInterp) {
968 	    if (result == TCL_ERROR) {
969 
970 		/*
971 		 * An error occurred, so transfer error information from the
972 		 * destination interpreter back to our interpreter.  Must clear
973 		 * interp's result before calling Tcl_AddErrorInfo, since
974 		 * Tcl_AddErrorInfo will store the interp's result in errorInfo
975 		 * before appending riPtr's $errorInfo;  we've already got
976 		 * everything we need in riPtr's $errorInfo.
977 		 */
978 
979 		Tcl_ResetResult(interp);
980 		Tcl_AddErrorInfo(interp, Tcl_GetVar2(localInterp,
981 			"errorInfo", (char *) NULL, TCL_GLOBAL_ONLY));
982 		Tcl_SetVar2(interp, "errorCode", (char *) NULL,
983 			Tcl_GetVar2(localInterp, "errorCode", (char *) NULL,
984 			TCL_GLOBAL_ONLY), TCL_GLOBAL_ONLY);
985 	    }
986             if (localInterp->freeProc != TCL_STATIC) {
987                 interp->result = localInterp->result;
988                 interp->freeProc = localInterp->freeProc;
989                 localInterp->freeProc = TCL_STATIC;
990             } else {
991                 Tcl_SetResult(interp, localInterp->result, TCL_VOLATILE);
992             }
993             Tcl_ResetResult(localInterp);
994 	}
995 	Tcl_Release((ClientData) riPtr);
996         Tcl_Release((ClientData) localInterp);
997 	return result;
998     }
999 
1000     /*
1001      * Bind the interpreter name to a communication window.
1002      */
1003 
1004     regPtr = RegOpen(interp, winPtr->dispPtr, 0);
1005     commWindow = RegFindName(regPtr, destName);
1006     RegClose(regPtr);
1007     if (commWindow == None) {
1008 	Tcl_AppendResult(interp, "no application named \"",
1009 		destName, "\"", (char *) NULL);
1010 	return TCL_ERROR;
1011     }
1012 
1013     /*
1014      * Send the command to the target interpreter by appending it to the
1015      * comm window in the communication window.
1016      */
1017 
1018     tkSendSerial++;
1019     Tcl_DStringInit(&request);
1020     Tcl_DStringAppend(&request, "\0c\0-n ", 6);
1021     Tcl_DStringAppend(&request, destName, -1);
1022     if (!async) {
1023 	sprintf(buffer, "%x %d",
1024 		(unsigned int) Tk_WindowId(dispPtr->commTkwin),
1025 		tkSendSerial);
1026 	Tcl_DStringAppend(&request, "\0-r ", 4);
1027 	Tcl_DStringAppend(&request, buffer, -1);
1028     }
1029     Tcl_DStringAppend(&request, "\0-s ", 4);
1030     Tcl_DStringAppend(&request, argv[firstArg], -1);
1031     for (i = firstArg+1; i < argc; i++) {
1032 	Tcl_DStringAppend(&request, " ", 1);
1033 	Tcl_DStringAppend(&request, argv[i], -1);
1034     }
1035     (void) AppendPropCarefully(dispPtr->display, commWindow,
1036 	    dispPtr->commProperty, Tcl_DStringValue(&request),
1037 	    Tcl_DStringLength(&request) + 1,
1038 	    (async) ? (PendingCommand *) NULL : &pending);
1039     Tcl_DStringFree(&request);
1040     if (async) {
1041 	/*
1042 	 * This is an asynchronous send:  return immediately without
1043 	 * waiting for a response.
1044 	 */
1045 
1046 	return TCL_OK;
1047     }
1048 
1049     /*
1050      * Register the fact that we're waiting for a command to complete
1051      * (this is needed by SendEventProc and by AppendErrorProc to pass
1052      * back the command's results).  Set up a timeout handler so that
1053      * we can check during long sends to make sure that the destination
1054      * application is still alive.
1055      */
1056 
1057     pending.serial = tkSendSerial;
1058     pending.dispPtr = dispPtr;
1059     pending.target = destName;
1060     pending.commWindow = commWindow;
1061     pending.interp = interp;
1062     pending.result = NULL;
1063     pending.errorInfo = NULL;
1064     pending.errorCode = NULL;
1065     pending.gotResponse = 0;
1066     pending.nextPtr = pendingCommands;
1067     pendingCommands = &pending;
1068 
1069     /*
1070      * Enter a loop processing X events until the result comes
1071      * in or the target is declared to be dead.  While waiting
1072      * for a result, look only at send-related events so that
1073      * the send is synchronous with respect to other events in
1074      * the application.
1075      */
1076 
1077     prevRestrictProc = Tk_RestrictEvents(SendRestrictProc,
1078 	    (ClientData) NULL, &prevArg);
1079     Tcl_CreateModalTimeout(1000, TimeoutProc, (ClientData) &pending);
1080     while (!pending.gotResponse) {
1081 	Tcl_DoOneEvent(TCL_WINDOW_EVENTS);
1082     }
1083     Tcl_DeleteModalTimeout(TimeoutProc, (ClientData) &pending);
1084     (void) Tk_RestrictEvents(prevRestrictProc, prevArg, &prevArg);
1085 
1086     /*
1087      * Unregister the information about the pending command
1088      * and return the result.
1089      */
1090 
1091     if (pendingCommands == &pending) {
1092 	pendingCommands = pending.nextPtr;
1093     } else {
1094 	PendingCommand *pcPtr;
1095 
1096 	for (pcPtr = pendingCommands; pcPtr != NULL;
1097 		pcPtr = pcPtr->nextPtr) {
1098 	    if (pcPtr->nextPtr == &pending) {
1099 		pcPtr->nextPtr = pending.nextPtr;
1100 		break;
1101 	    }
1102 	}
1103     }
1104     if (pending.errorInfo != NULL) {
1105 	/*
1106 	 * Special trick: must clear the interp's result before calling
1107 	 * Tcl_AddErrorInfo, since Tcl_AddErrorInfo will store the interp's
1108 	 * result in errorInfo before appending pending.errorInfo;  we've
1109 	 * already got everything we need in pending.errorInfo.
1110 	 */
1111 
1112 	Tcl_ResetResult(interp);
1113 	Tcl_AddErrorInfo(interp, pending.errorInfo);
1114 	ckfree(pending.errorInfo);
1115     }
1116     if (pending.errorCode != NULL) {
1117 	Tcl_SetVar2(interp, "errorCode", (char *) NULL, pending.errorCode,
1118 		TCL_GLOBAL_ONLY);
1119 	ckfree(pending.errorCode);
1120     }
1121     Tcl_SetResult(interp, pending.result, TCL_DYNAMIC);
1122     return pending.code;
1123 }
1124 
1125 /*
1126  *----------------------------------------------------------------------
1127  *
1128  * TkGetInterpNames --
1129  *
1130  *	This procedure is invoked to fetch a list of all the
1131  *	interpreter names currently registered for the display
1132  *	of a particular window.
1133  *
1134  * Results:
1135  *	A standard Tcl return value.  Interp->result will be set
1136  *	to hold a list of all the interpreter names defined for
1137  *	tkwin's display.  If an error occurs, then TCL_ERROR
1138  *	is returned and interp->result will hold an error message.
1139  *
1140  * Side effects:
1141  *	None.
1142  *
1143  *----------------------------------------------------------------------
1144  */
1145 
1146 int
TkGetInterpNames(interp,tkwin)1147 TkGetInterpNames(interp, tkwin)
1148     Tcl_Interp *interp;		/* Interpreter for returning a result. */
1149     Tk_Window tkwin;		/* Window whose display is to be used
1150 				 * for the lookup. */
1151 {
1152     TkWindow *winPtr = (TkWindow *) tkwin;
1153     char *p, *entry, *entryName;
1154     NameRegistry *regPtr;
1155     Window commWindow;
1156     int count;
1157 
1158     /*
1159      * Read the registry property, then scan through all of its entries.
1160      * Validate each entry to be sure that its application still exists.
1161      */
1162 
1163     regPtr = RegOpen(interp, winPtr->dispPtr, 1);
1164     for (p = regPtr->property; (p-regPtr->property) < regPtr->propLength; ) {
1165 	entry = p;
1166 	if (sscanf(p,  "%x",(unsigned int *) &commWindow) != 1) {
1167 	    commWindow =  None;
1168 	}
1169 	while ((*p != 0) && (!isspace(UCHAR(*p)))) {
1170 	    p++;
1171 	}
1172 	if (*p != 0) {
1173 	    p++;
1174 	}
1175 	entryName = p;
1176 	while (*p != 0) {
1177 	    p++;
1178 	}
1179 	p++;
1180 	if (ValidateName(winPtr->dispPtr, entryName, commWindow, 1)) {
1181 	    /*
1182 	     * The application still exists; add its name to the result.
1183 	     */
1184 
1185 	    Tcl_AppendElement(interp, entryName);
1186 	} else {
1187 	    /*
1188 	     * This name is bogus (perhaps the application died without
1189 	     * cleaning up its entry in the registry?).  Delete the name.
1190 	     */
1191 
1192 	    count = regPtr->propLength - (p - regPtr->property);
1193 	    if (count > 0)  {
1194 		memmove((VOID *) entry, (VOID *) p, (size_t) count);
1195 	    }
1196 	    regPtr->propLength -= p - entry;
1197 	    regPtr->modified = 1;
1198 	    p = entry;
1199 	}
1200     }
1201     RegClose(regPtr);
1202     return TCL_OK;
1203 }
1204 
1205 /*
1206  *--------------------------------------------------------------
1207  *
1208  * SendInit --
1209  *
1210  *	This procedure is called to initialize the
1211  *	communication channels for sending commands and
1212  *	receiving results.
1213  *
1214  * Results:
1215  *	None.
1216  *
1217  * Side effects:
1218  *	Sets up various data structures and windows.
1219  *
1220  *--------------------------------------------------------------
1221  */
1222 
1223 static int
SendInit(interp,dispPtr)1224 SendInit(interp, dispPtr)
1225     Tcl_Interp *interp;		/* Interpreter to use for error reporting
1226 				 * (no errors are ever returned, but the
1227 				 * interpreter is needed anyway). */
1228     TkDisplay *dispPtr;		/* Display to initialize. */
1229 {
1230     XSetWindowAttributes atts;
1231 
1232     /*
1233      * Create the window used for communication, and set up an
1234      * event handler for it.
1235      */
1236 
1237     dispPtr->commTkwin = Tk_CreateWindow(interp, (Tk_Window) NULL,
1238 	    "_comm", DisplayString(dispPtr->display));
1239     if (dispPtr->commTkwin == NULL) {
1240 	panic("Tk_CreateWindow failed in SendInit!");
1241     }
1242     atts.override_redirect = True;
1243     Tk_ChangeWindowAttributes(dispPtr->commTkwin,
1244 	    CWOverrideRedirect, &atts);
1245     Tk_CreateEventHandler(dispPtr->commTkwin, PropertyChangeMask,
1246 	    SendEventProc, (ClientData) dispPtr);
1247     Tk_MakeWindowExist(dispPtr->commTkwin);
1248 
1249     /*
1250      * Get atoms used as property names.
1251      */
1252 
1253     dispPtr->commProperty = Tk_InternAtom(dispPtr->commTkwin, "Comm");
1254     dispPtr->registryProperty = Tk_InternAtom(dispPtr->commTkwin,
1255 	    "InterpRegistry");
1256     dispPtr->appNameProperty = Tk_InternAtom(dispPtr->commTkwin,
1257 	    "TK_APPLICATION");
1258 
1259     return TCL_OK;
1260 }
1261 
1262 /*
1263  *--------------------------------------------------------------
1264  *
1265  * SendEventProc --
1266  *
1267  *	This procedure is invoked automatically by the toolkit
1268  *	event manager when a property changes on the communication
1269  *	window.  This procedure reads the property and handles
1270  *	command requests and responses.
1271  *
1272  * Results:
1273  *	None.
1274  *
1275  * Side effects:
1276  *	If there are command requests in the property, they
1277  *	are executed.  If there are responses in the property,
1278  *	their information is saved for the (ostensibly waiting)
1279  *	"send" commands. The property is deleted.
1280  *
1281  *--------------------------------------------------------------
1282  */
1283 
1284 static void
SendEventProc(clientData,eventPtr)1285 SendEventProc(clientData, eventPtr)
1286     ClientData clientData;	/* Display information. */
1287     XEvent *eventPtr;		/* Information about event. */
1288 {
1289     TkDisplay *dispPtr = (TkDisplay *) clientData;
1290     char *propInfo;
1291     register char *p;
1292     int result, actualFormat;
1293     unsigned long numItems, bytesAfter;
1294     Atom actualType;
1295     Tcl_Interp *remoteInterp;	/* Interp in which to execute the command. */
1296 
1297     if ((eventPtr->xproperty.atom != dispPtr->commProperty)
1298 	    || (eventPtr->xproperty.state != PropertyNewValue)) {
1299 	return;
1300     }
1301 
1302     /*
1303      * Read the comm property and delete it.
1304      */
1305 
1306     propInfo = NULL;
1307     result = XGetWindowProperty(dispPtr->display,
1308 	    Tk_WindowId(dispPtr->commTkwin),
1309 	    dispPtr->commProperty, 0, MAX_PROP_WORDS, True,
1310 	    XA_STRING, &actualType, &actualFormat,
1311 	    &numItems, &bytesAfter, (unsigned char **) &propInfo);
1312 
1313     /*
1314      * If the property doesn't exist or is improperly formed
1315      * then ignore it.
1316      */
1317 
1318     if ((result != Success) || (actualType != XA_STRING)
1319 	    || (actualFormat != 8)) {
1320 	if (propInfo != NULL) {
1321 	    XFree(propInfo);
1322 	}
1323 	return;
1324     }
1325 
1326     /*
1327      * Several commands and results could arrive in the property at
1328      * one time;  each iteration through the outer loop handles a
1329      * single command or result.
1330      */
1331 
1332     for (p = propInfo; (p-propInfo) < numItems; ) {
1333 	/*
1334 	 * Ignore leading NULLs; each command or result starts with a
1335 	 * NULL so that no matter how badly formed a preceding command
1336 	 * is, we'll be able to tell that a new command/result is
1337 	 * starting.
1338 	 */
1339 
1340 	if (*p == 0) {
1341 	    p++;
1342 	    continue;
1343 	}
1344 
1345 	if ((*p == 'c') && (p[1] == 0)) {
1346 	    Window commWindow;
1347 	    char *interpName, *script, *serial, *end;
1348 	    Tcl_DString reply;
1349 	    RegisteredInterp *riPtr;
1350 
1351 	    /*
1352 	     *----------------------------------------------------------
1353 	     * This is an incoming command from some other application.
1354 	     * Iterate over all of its options.  Stop when we reach
1355 	     * the end of the property or something that doesn't look
1356 	     * like an option.
1357 	     *----------------------------------------------------------
1358 	     */
1359 
1360 	    p += 2;
1361 	    interpName = NULL;
1362 	    commWindow = None;
1363 	    serial = "";
1364 	    script = NULL;
1365 	    while (((p-propInfo) < numItems) && (*p == '-')) {
1366 		switch (p[1]) {
1367 		    case 'r':
1368 			commWindow = (Window) strtoul(p+2, &end, 16);
1369 			if ((end == p+2) || (*end != ' ')) {
1370 			    commWindow = None;
1371 			} else {
1372 			    p = serial = end+1;
1373 			}
1374 			break;
1375 		    case 'n':
1376 			if (p[2] == ' ') {
1377 			    interpName = p+3;
1378 			}
1379 			break;
1380 		    case 's':
1381 			if (p[2] == ' ') {
1382 			    script = p+3;
1383 			}
1384 			break;
1385 		}
1386 		while (*p != 0) {
1387 		    p++;
1388 		}
1389 		p++;
1390 	    }
1391 
1392 	    if ((script == NULL) || (interpName == NULL)) {
1393 		continue;
1394 	    }
1395 
1396 	    /*
1397 	     * Initialize the result property, so that we're ready at any
1398 	     * time if we need to return an error.
1399 	     */
1400 
1401 	    if (commWindow != None) {
1402 		Tcl_DStringInit(&reply);
1403 		Tcl_DStringAppend(&reply, "\0r\0-s ", 6);
1404 		Tcl_DStringAppend(&reply, serial, -1);
1405 		Tcl_DStringAppend(&reply, "\0-r ", 4);
1406 	    }
1407 
1408 	    if (!ServerSecure(dispPtr)) {
1409 		if (commWindow != None) {
1410 		    Tcl_DStringAppend(&reply, "X server insecure (must use xauth-style authorization); command ignored", -1);
1411 		}
1412 		result = TCL_ERROR;
1413 		goto returnResult;
1414 	    }
1415 
1416 	    /*
1417 	     * Locate the application, then execute the script.
1418 	     */
1419 
1420 	    for (riPtr = registry; ; riPtr = riPtr->nextPtr) {
1421 		if (riPtr == NULL) {
1422 		    if (commWindow != None) {
1423 			Tcl_DStringAppend(&reply,
1424 				"receiver never heard of interpreter \"", -1);
1425 			Tcl_DStringAppend(&reply, interpName, -1);
1426 			Tcl_DStringAppend(&reply, "\"", 1);
1427 		    }
1428 		    result = TCL_ERROR;
1429 		    goto returnResult;
1430 		}
1431 		if (strcmp(riPtr->name, interpName) == 0) {
1432 		    break;
1433 		}
1434 	    }
1435 	    Tcl_Preserve((ClientData) riPtr);
1436 
1437             /*
1438              * We must protect the interpreter because the script may
1439              * enter another event loop, which might call Tcl_DeleteInterp.
1440              */
1441 
1442             remoteInterp = riPtr->interp;
1443             Tcl_Preserve((ClientData) remoteInterp);
1444 
1445             result = Tcl_GlobalEval(remoteInterp, script);
1446 
1447             /*
1448              * The call to Tcl_Release may have released the interpreter
1449              * which will cause the "send" command for that interpreter
1450              * to be deleted. The command deletion callback will set the
1451              * riPtr->interp field to NULL, hence the check below for NULL.
1452              */
1453 
1454 	    if (commWindow != None) {
1455 		Tcl_DStringAppend(&reply, remoteInterp->result, -1);
1456 		if (result == TCL_ERROR) {
1457 		    char *varValue;
1458 
1459 		    varValue = Tcl_GetVar2(remoteInterp, "errorInfo",
1460 			    (char *) NULL, TCL_GLOBAL_ONLY);
1461 		    if (varValue != NULL) {
1462 			Tcl_DStringAppend(&reply, "\0-i ", 4);
1463 			Tcl_DStringAppend(&reply, varValue, -1);
1464 		    }
1465 		    varValue = Tcl_GetVar2(remoteInterp, "errorCode",
1466 			    (char *) NULL, TCL_GLOBAL_ONLY);
1467 		    if (varValue != NULL) {
1468 			Tcl_DStringAppend(&reply, "\0-e ", 4);
1469 			Tcl_DStringAppend(&reply, varValue, -1);
1470 		    }
1471 		}
1472 	    }
1473             Tcl_Release((ClientData) remoteInterp);
1474 	    Tcl_Release((ClientData) riPtr);
1475 
1476 	    /*
1477 	     * Return the result to the sender if a commWindow was
1478 	     * specified (if none was specified then this is an asynchronous
1479 	     * call).  Right now reply has everything but the completion
1480 	     * code, but it needs the NULL to terminate the current option.
1481 	     */
1482 
1483 	    returnResult:
1484 	    if (commWindow != None) {
1485 		if (result != TCL_OK) {
1486 		    char buffer[20];
1487 
1488 		    sprintf(buffer, "%d", result);
1489 		    Tcl_DStringAppend(&reply, "\0-c ", 4);
1490 		    Tcl_DStringAppend(&reply, buffer, -1);
1491 		}
1492 		(void) AppendPropCarefully(dispPtr->display, commWindow,
1493 			dispPtr->commProperty, Tcl_DStringValue(&reply),
1494 			Tcl_DStringLength(&reply) + 1,
1495 			(PendingCommand *) NULL);
1496 		XFlush(dispPtr->display);
1497 		Tcl_DStringFree(&reply);
1498 	    }
1499 	} else if ((*p == 'r') && (p[1] == 0)) {
1500 	    int serial, code, gotSerial;
1501 	    char *errorInfo, *errorCode, *resultString;
1502 	    PendingCommand *pcPtr;
1503 
1504 	    /*
1505 	     *----------------------------------------------------------
1506 	     * This is a reply to some command that we sent out.  Iterate
1507 	     * over all of its options.  Stop when we reach the end of the
1508 	     * property or something that doesn't look like an option.
1509 	     *----------------------------------------------------------
1510 	     */
1511 
1512 	    p += 2;
1513 	    code = TCL_OK;
1514 	    gotSerial = 0;
1515 	    errorInfo = NULL;
1516 	    errorCode = NULL;
1517 	    resultString = "";
1518 	    while (((p-propInfo) < numItems) && (*p == '-')) {
1519 		switch (p[1]) {
1520 		    case 'c':
1521 			if (sscanf(p+2, " %d", &code) != 1) {
1522 			    code = TCL_OK;
1523 			}
1524 			break;
1525 		    case 'e':
1526 			if (p[2] == ' ') {
1527 			    errorCode = p+3;
1528 			}
1529 			break;
1530 		    case 'i':
1531 			if (p[2] == ' ') {
1532 			    errorInfo = p+3;
1533 			}
1534 			break;
1535 		    case 'r':
1536 			if (p[2] == ' ') {
1537 			    resultString = p+3;
1538 			}
1539 			break;
1540 		    case 's':
1541 			if (sscanf(p+2, " %d", &serial) == 1) {
1542 			    gotSerial = 1;
1543 			}
1544 			break;
1545 		}
1546 		while (*p != 0) {
1547 		    p++;
1548 		}
1549 		p++;
1550 	    }
1551 
1552 	    if (!gotSerial) {
1553 		continue;
1554 	    }
1555 
1556 	    /*
1557 	     * Give the result information to anyone who's
1558 	     * waiting for it.
1559 	     */
1560 
1561 	    for (pcPtr = pendingCommands; pcPtr != NULL;
1562 		    pcPtr = pcPtr->nextPtr) {
1563 		if ((serial != pcPtr->serial) || (pcPtr->result != NULL)) {
1564 		    continue;
1565 		}
1566 		pcPtr->code = code;
1567 		if (resultString != NULL) {
1568 		    pcPtr->result = (char *) ckalloc((unsigned)
1569 			    (strlen(resultString) + 1));
1570 		    strcpy(pcPtr->result, resultString);
1571 		}
1572 		if (code == TCL_ERROR) {
1573 		    if (errorInfo != NULL) {
1574 			pcPtr->errorInfo = (char *) ckalloc((unsigned)
1575 				(strlen(errorInfo) + 1));
1576 			strcpy(pcPtr->errorInfo, errorInfo);
1577 		    }
1578 		    if (errorCode != NULL) {
1579 			pcPtr->errorCode = (char *) ckalloc((unsigned)
1580 				(strlen(errorCode) + 1));
1581 			strcpy(pcPtr->errorCode, errorCode);
1582 		    }
1583 		}
1584 		pcPtr->gotResponse = 1;
1585 		break;
1586 	    }
1587 	} else {
1588 	    /*
1589 	     * Didn't recognize this thing.  Just skip through the next
1590 	     * null character and try again.
1591 	     */
1592 
1593 	    while (*p != 0) {
1594 		p++;
1595 	    }
1596 	    p++;
1597 	}
1598     }
1599     XFree(propInfo);
1600 }
1601 
1602 /*
1603  *--------------------------------------------------------------
1604  *
1605  * AppendPropCarefully --
1606  *
1607  *	Append a given property to a given window, but set up
1608  *	an X error handler so that if the append fails this
1609  *	procedure can return an error code rather than having
1610  *	Xlib panic.
1611  *
1612  * Results:
1613  *	None.
1614  *
1615  * Side effects:
1616  *	The given property on the given window is appended to.
1617  *	If this operation fails and if pendingPtr is non-NULL,
1618  *	then the pending operation is marked as complete with
1619  *	an error.
1620  *
1621  *--------------------------------------------------------------
1622  */
1623 
1624 static void
AppendPropCarefully(display,window,property,value,length,pendingPtr)1625 AppendPropCarefully(display, window, property, value, length, pendingPtr)
1626     Display *display;		/* Display on which to operate. */
1627     Window window;		/* Window whose property is to
1628 				 * be modified. */
1629     Atom property;		/* Name of property. */
1630     char *value;		/* Characters to append to property. */
1631     int length;			/* Number of bytes to append. */
1632     PendingCommand *pendingPtr;	/* Pending command to mark complete
1633 				 * if an error occurs during the
1634 				 * property op.  NULL means just
1635 				 * ignore the error. */
1636 {
1637     Tk_ErrorHandler handler;
1638 
1639     handler = Tk_CreateErrorHandler(display, -1, -1, -1, AppendErrorProc,
1640 	(ClientData) pendingPtr);
1641     XChangeProperty(display, window, property, XA_STRING, 8,
1642 	    PropModeAppend, (unsigned char *) value, length);
1643     Tk_DeleteErrorHandler(handler);
1644 }
1645 
1646 /*
1647  * The procedure below is invoked if an error occurs during
1648  * the XChangeProperty operation above.
1649  */
1650 
1651 	/* ARGSUSED */
1652 static int
AppendErrorProc(clientData,errorPtr)1653 AppendErrorProc(clientData, errorPtr)
1654     ClientData clientData;	/* Command to mark complete, or NULL. */
1655     XErrorEvent *errorPtr;	/* Information about error. */
1656 {
1657     PendingCommand *pendingPtr = (PendingCommand *) clientData;
1658     register PendingCommand *pcPtr;
1659 
1660     if (pendingPtr == NULL) {
1661 	return 0;
1662     }
1663 
1664     /*
1665      * Make sure this command is still pending.
1666      */
1667 
1668     for (pcPtr = pendingCommands; pcPtr != NULL;
1669 	    pcPtr = pcPtr->nextPtr) {
1670 	if ((pcPtr == pendingPtr) && (pcPtr->result == NULL)) {
1671 	    pcPtr->result = (char *) ckalloc((unsigned)
1672 		    (strlen(pcPtr->target) + 50));
1673 	    sprintf(pcPtr->result, "no application named \"%s\"",
1674 		    pcPtr->target);
1675 	    pcPtr->code = TCL_ERROR;
1676 	    pcPtr->gotResponse = 1;
1677 	    break;
1678 	}
1679     }
1680     return 0;
1681 }
1682 
1683 /*
1684  *--------------------------------------------------------------
1685  *
1686  * TimeoutProc --
1687  *
1688  *	This procedure is invoked when an unusually long amout of
1689  *	time has elapsed during the processing of a sent command.
1690  *	It checks to make sure that the target application still
1691  *	exists, and reschedules itself to check again later.
1692  *
1693  * Results:
1694  *	None.
1695  *
1696  * Side effects:
1697  *	If the target application has gone away abort the send
1698  *	operation with an error.
1699  *
1700  *--------------------------------------------------------------
1701  */
1702 
1703 static void
TimeoutProc(clientData)1704 TimeoutProc(clientData)
1705     ClientData clientData;	/* Information about command that
1706 				 * has been sent but not yet
1707 				 * responded to. */
1708 {
1709     PendingCommand *pcPtr = (PendingCommand *) clientData;
1710     register PendingCommand *pcPtr2;
1711 
1712     /*
1713      * Make sure that the command is still in the pending list
1714      * and that it hasn't already completed.  Then validate the
1715      * existence of the target application.
1716      */
1717 
1718     for (pcPtr2 = pendingCommands; pcPtr2 != NULL;
1719 	    pcPtr2 = pcPtr2->nextPtr) {
1720 	char *msg;
1721 	if ((pcPtr2 != pcPtr) || (pcPtr2->result != NULL)) {
1722 	    continue;
1723 	}
1724 	if (!ValidateName(pcPtr2->dispPtr, pcPtr2->target,
1725 		pcPtr2->commWindow, 0)) {
1726 	    if (ValidateName(pcPtr2->dispPtr, pcPtr2->target,
1727 		    pcPtr2->commWindow, 1)) {
1728 		msg =
1729                     "target application died or uses a Tk version before 4.0";
1730 	    } else {
1731 		msg = "target application died";
1732 	    }
1733 	    pcPtr2->code = TCL_ERROR;
1734 	    pcPtr2->result = (char *) ckalloc((unsigned) (strlen(msg) + 1));
1735 	    strcpy(pcPtr2->result, msg);
1736 	    pcPtr2->gotResponse = 1;
1737 	} else {
1738 	    Tcl_DeleteModalTimeout(TimeoutProc, clientData);
1739 	    Tcl_CreateModalTimeout(2000, TimeoutProc, clientData);
1740 	}
1741     }
1742 }
1743 
1744 /*
1745  *--------------------------------------------------------------
1746  *
1747  * DeleteProc --
1748  *
1749  *	This procedure is invoked by Tcl when the "send" command
1750  *	is deleted in an interpreter.  It unregisters the interpreter.
1751  *
1752  * Results:
1753  *	None.
1754  *
1755  * Side effects:
1756  *	The interpreter given by riPtr is unregistered.
1757  *
1758  *--------------------------------------------------------------
1759  */
1760 
1761 static void
DeleteProc(clientData)1762 DeleteProc(clientData)
1763     ClientData clientData;	/* Info about registration, passed
1764 				 * as ClientData. */
1765 {
1766     RegisteredInterp *riPtr = (RegisteredInterp *) clientData;
1767     register RegisteredInterp *riPtr2;
1768     NameRegistry *regPtr;
1769 
1770     regPtr = RegOpen(riPtr->interp, riPtr->dispPtr, 1);
1771     RegDeleteName(regPtr, riPtr->name);
1772     RegClose(regPtr);
1773 
1774     if (registry == riPtr) {
1775 	registry = riPtr->nextPtr;
1776     } else {
1777 	for (riPtr2 = registry; riPtr2 != NULL;
1778 		riPtr2 = riPtr2->nextPtr) {
1779 	    if (riPtr2->nextPtr == riPtr) {
1780 		riPtr2->nextPtr = riPtr->nextPtr;
1781 		break;
1782 	    }
1783 	}
1784     }
1785     ckfree((char *) riPtr->name);
1786     riPtr->interp = NULL;
1787     UpdateCommWindow(riPtr->dispPtr);
1788     Tcl_EventuallyFree((ClientData) riPtr, TCL_DYNAMIC);
1789 }
1790 
1791 /*
1792  *----------------------------------------------------------------------
1793  *
1794  * SendRestrictProc --
1795  *
1796  *	This procedure filters incoming events when a "send" command
1797  *	is outstanding.  It defers all events except those containing
1798  *	send commands and results.
1799  *
1800  * Results:
1801  *	False is returned except for property-change events on a
1802  *	commWindow.
1803  *
1804  * Side effects:
1805  *	None.
1806  *
1807  *----------------------------------------------------------------------
1808  */
1809 
1810     /* ARGSUSED */
1811 static Tk_RestrictAction
SendRestrictProc(clientData,eventPtr)1812 SendRestrictProc(clientData, eventPtr)
1813     ClientData clientData;		/* Not used. */
1814     register XEvent *eventPtr;		/* Event that just arrived. */
1815 {
1816     TkDisplay *dispPtr;
1817 
1818     if (eventPtr->type != PropertyNotify) {
1819 	return TK_DEFER_EVENT;
1820     }
1821     for (dispPtr = tkDisplayList; dispPtr != NULL; dispPtr = dispPtr->nextPtr) {
1822 	if ((eventPtr->xany.display == dispPtr->display)
1823 		&& (eventPtr->xproperty.window
1824 		== Tk_WindowId(dispPtr->commTkwin))) {
1825 	    return TK_PROCESS_EVENT;
1826 	}
1827     }
1828     return TK_DEFER_EVENT;
1829 }
1830 
1831 /*
1832  *----------------------------------------------------------------------
1833  *
1834  * UpdateCommWindow --
1835  *
1836  *	This procedure updates the list of application names stored
1837  *	on our commWindow.  It is typically called when interpreters
1838  *	are registered and unregistered.
1839  *
1840  * Results:
1841  *	None.
1842  *
1843  * Side effects:
1844  *	The TK_APPLICATION property on the comm window is updated.
1845  *
1846  *----------------------------------------------------------------------
1847  */
1848 
1849 static void
UpdateCommWindow(dispPtr)1850 UpdateCommWindow(dispPtr)
1851     TkDisplay *dispPtr;		/* Display whose commWindow is to be
1852 				 * updated. */
1853 {
1854     Tcl_DString names;
1855     RegisteredInterp *riPtr;
1856 
1857     Tcl_DStringInit(&names);
1858     for (riPtr = registry; riPtr != NULL; riPtr = riPtr->nextPtr) {
1859 	Tcl_DStringAppendElement(&names, riPtr->name);
1860     }
1861     XChangeProperty(dispPtr->display, Tk_WindowId(dispPtr->commTkwin),
1862 	    dispPtr->appNameProperty, XA_STRING, 8, PropModeReplace,
1863 	    (unsigned char *) Tcl_DStringValue(&names),
1864 	    Tcl_DStringLength(&names));
1865     Tcl_DStringFree(&names);
1866 }
1867