]> cvs.zerfleddert.de Git - micropolis/blob - src/tk/tksend.c
Fixes for compilation with gcc 15
[micropolis] / src / tk / tksend.c
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 1989-1992 Regents of the University of California
9 * Permission to use, copy, modify, and distribute this
10 * software and its documentation for any purpose and without
11 * fee is hereby granted, provided that the above copyright
12 * notice appear in all copies. The University of California
13 * makes no representations about the suitability of this
14 * software for any purpose. It is provided "as is" without
15 * express or implied warranty.
16 */
17
18 #ifndef lint
19 static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkSend.c,v 1.26 92/08/13 10:29:26 ouster Exp $ SPRITE (Berkeley)";
20 #endif
21
22 #include "tkconfig.h"
23 #include "tkint.h"
24
25 /*
26 * The following structure is used to keep track of the
27 * interpreters registered by this process.
28 */
29
30 typedef struct RegisteredInterp {
31 char *name; /* Interpreter's name (malloc-ed). */
32 Tcl_Interp *interp; /* Interpreter associated with
33 * name. */
34 TkDisplay *dispPtr; /* Display associated with name. */
35 TkWindow *winPtr; /* Window associated with name. */
36 struct RegisteredInterp *nextPtr;
37 /* Next in list of names associated
38 * with interps in this process.
39 * NULL means end of list. */
40 } RegisteredInterp;
41
42 static RegisteredInterp *registry = NULL;
43 /* List of all interpreters
44 * registered by this process. */
45
46 /*
47 * When a result is being awaited from a sent command, one of
48 * the following structures is present on a list of all outstanding
49 * sent commands. The information in the structure is used to
50 * process the result when it arrives. You're probably wondering
51 * how there could ever be multiple outstanding sent commands.
52 * This could happen if interpreters invoke each other recursively.
53 * It's unlikely, but possible.
54 */
55
56 typedef struct PendingCommand {
57 int serial; /* Serial number expected in
58 * result. */
59 char *target; /* Name of interpreter command is
60 * being sent to. */
61 Tcl_Interp *interp; /* Interpreter from which the send
62 * was invoked. */
63 int code; /* Tcl return code for command
64 * will be stored here. */
65 char *result; /* String result for command (malloc'ed).
66 * NULL means command still pending. */
67 struct PendingCommand *nextPtr;
68 /* Next in list of all outstanding
69 * commands. NULL means end of
70 * list. */
71 } PendingCommand;
72
73 static PendingCommand *pendingCommands = NULL;
74 /* List of all commands currently
75 * being waited for. */
76
77 /*
78 * The information below is used for communication between
79 * processes during "send" commands. Each process keeps a
80 * private window, never even mapped, with one property,
81 * "Comm". When a command is sent to an interpreter, the
82 * command is appended to the comm property of the communication
83 * window associated with the interp's process. Similarly, when a
84 * result is returned from a sent command, it is also appended
85 * to the comm property. In each case, the property information
86 * is in the form of an ASCII string. The exact syntaxes are:
87 *
88 * Command:
89 * 'C' space window space serial space interpName '|' command '\0'
90 * The 'C' character indicates that this is a command and not
91 * a response. Window is the hex identifier for the comm
92 * window on which to append the response. Serial is a hex
93 * integer containing an identifying number assigned by the
94 * sender; it may be used by the sender to sort out concurrent
95 * responses. InterpName is the ASCII name of the desired
96 * interpreter, which must not contain any vertical bar characters
97 * The interpreter name is delimited by a vertical bar (this
98 * allows the name to include blanks), and is followed by
99 * the command to execute. The command is terminated by a
100 * NULL character.
101 *
102 * Response:
103 * 'R' space serial space code space result '\0'
104 * The 'R' character indicates that this is a response. Serial
105 * gives the identifier for the command (same value as in the
106 * command message). The code field is a decimal integer giving
107 * the Tcl return code from the command, and result is the string
108 * result. The result is terminated by a NULL character.
109 *
110 * The register of interpreters is kept in a property
111 * "InterpRegistry" on the root window of the display. It is
112 * organized as a series of zero or more concatenated strings
113 * (in no particular order), each of the form
114 * window space name '\0'
115 * where "window" is the hex id of the comm. window to use to talk
116 * to an interpreter named "name".
117 */
118
119 /*
120 * Maximum size property that can be read at one time by
121 * this module:
122 */
123
124 #define MAX_PROP_WORDS 100000
125
126 /*
127 * Forward declarations for procedures defined later in this file:
128 */
129
130 static int AppendErrorProc _ANSI_ARGS_((ClientData clientData,
131 XErrorEvent *errorPtr));
132 static void AppendPropCarefully _ANSI_ARGS_((Display *display,
133 Window window, Atom property, char *value,
134 PendingCommand *pendingPtr));
135 static void DeleteProc _ANSI_ARGS_((ClientData clientData));
136 static Window LookupName _ANSI_ARGS_((TkDisplay *dispPtr, char *name,
137 int delete));
138 static void SendEventProc _ANSI_ARGS_((ClientData clientData,
139 XEvent *eventPtr));
140 static int SendInit _ANSI_ARGS_((Tcl_Interp *interp, TkDisplay *dispPtr));
141 static Bool SendRestrictProc _ANSI_ARGS_((Display *display,
142 XEvent *eventPtr, char *arg));
143 static void TimeoutProc _ANSI_ARGS_((ClientData clientData));
144 \f
145 /*
146 *--------------------------------------------------------------
147 *
148 * Tk_RegisterInterp --
149 *
150 * This procedure is called to associate an ASCII name
151 * with an interpreter. Tk_InitSend must previously
152 * have been called to set up communication channels
153 * and specify a display.
154 *
155 * Results:
156 * Zero is returned if the name was registered successfully.
157 * Non-zero means the name was already in use.
158 *
159 * Side effects:
160 * Registration info is saved, thereby allowing the
161 * "send" command to be used later to invoke commands
162 * in the interpreter. The registration will be removed
163 * automatically when the interpreter is deleted.
164 *
165 *--------------------------------------------------------------
166 */
167
168 int
169 Tk_RegisterInterp (
170 Tcl_Interp *interp, /* Interpreter associated with name. */
171 char *name, /* The name that will be used to
172 * refer to the interpreter in later
173 * "send" commands. Must be globally
174 * unique. */
175 Tk_Window tkwin /* Token for window associated with
176 * interp; used to identify display
177 * for communication. */
178 )
179 {
180 #define TCL_MAX_NAME_LENGTH 1000
181 char propInfo[TCL_MAX_NAME_LENGTH + 20];
182 register RegisteredInterp *riPtr;
183 Window w;
184 TkWindow *winPtr = (TkWindow *) tkwin;
185 TkDisplay *dispPtr;
186
187 if (strchr(name, '|') != NULL) {
188 interp->result =
189 "interpreter name cannot contain '|' character";
190 return TCL_ERROR;
191 }
192
193 dispPtr = winPtr->dispPtr;
194 if (dispPtr->commWindow == NULL) {
195 int result;
196
197 result = SendInit(interp, dispPtr);
198 if (result != TCL_OK) {
199 return result;
200 }
201 }
202
203 /*
204 * Make sure the name is unique, and append info about it to
205 * the registry property. Eventually, it would probably be
206 * a good idea to lock the server here to prevent conflicting
207 * changes to the registry property. But that would make
208 * testing more difficult, and probably isn't necessary
209 * anyway because new windows don't get created all that often.
210 */
211
212 w = LookupName(dispPtr, name, 0);
213 if (w != (Window) 0) {
214 Tcl_Interp *tmpInterp;
215 RegisteredInterp tmpRi;
216 int result;
217 char *argv[3];
218
219 /*
220 * Name already exists. Ping the interpreter with a
221 * NULL command to see if it already exists. If not,
222 * unregister the old name (this could happen if an
223 * application dies without cleaning up the registry).
224 */
225
226 tmpInterp = Tcl_CreateInterp();
227 argv[0] = "send";
228 argv[1] = name;
229 argv[2] = "";
230 tmpRi.dispPtr = dispPtr;
231 tmpRi.winPtr = winPtr;
232 result = Tk_SendCmd((ClientData) &tmpRi, tmpInterp, 3, argv);
233 Tcl_DeleteInterp(tmpInterp);
234 if (result == TCL_OK) {
235 Tcl_AppendResult(interp, "interpreter name \"", name,
236 "\" is already in use", (char *) NULL);
237 return TCL_ERROR;
238 }
239 (void) LookupName(winPtr->dispPtr, name, 1);
240 }
241 sprintf(propInfo, "%x %.*s", Tk_WindowId(dispPtr->commWindow),
242 TCL_MAX_NAME_LENGTH, name);
243 XChangeProperty(dispPtr->display,
244 Tk_DefaultRootWindow(dispPtr->display),
245 dispPtr->registryProperty, XA_STRING, 8, PropModeAppend,
246 (unsigned char *) propInfo, strlen(propInfo)+1);
247
248 /*
249 * Add an entry in the local registry of names owned by this
250 * process.
251 */
252
253 riPtr = (RegisteredInterp *) ckalloc(sizeof(RegisteredInterp));
254 riPtr->name = (char *) ckalloc((unsigned) (strlen(name) + 1));
255 strcpy(riPtr->name, name);
256 riPtr->interp = interp;
257 riPtr->dispPtr = dispPtr;
258 riPtr->winPtr = winPtr;
259 riPtr->nextPtr = registry;
260 registry = riPtr;
261
262 /*
263 * Add the "send" command to this interpreter, and arrange for
264 * us to be notified when the interpreter is deleted (actually,
265 * when the "send" command is deleted).
266 */
267
268 Tcl_CreateCommand(interp, "send", Tk_SendCmd, (ClientData) riPtr,
269 DeleteProc);
270
271 return TCL_OK;
272 }
273 \f
274 /*
275 *--------------------------------------------------------------
276 *
277 * Tk_SendCmd --
278 *
279 * This procedure is invoked to process the "send" Tcl command.
280 * See the user documentation for details on what it does.
281 *
282 * Results:
283 * A standard Tcl result.
284 *
285 * Side effects:
286 * See the user documentation.
287 *
288 *--------------------------------------------------------------
289 */
290
291 int
292 Tk_SendCmd (
293 ClientData clientData, /* Information about sender (only
294 * dispPtr field is used). */
295 Tcl_Interp *interp, /* Current interpreter. */
296 int argc, /* Number of arguments. */
297 char **argv /* Argument strings. */
298 )
299 {
300 RegisteredInterp *senderRiPtr = (RegisteredInterp *) clientData;
301 Window w;
302 #define STATIC_PROP_SPACE 100
303 char *property, staticSpace[STATIC_PROP_SPACE];
304 int length;
305 int quick = 0;
306 static int serial = 0; /* Running count of sent commands.
307 * Used to give each command a
308 * different serial number. */
309 PendingCommand pending;
310 Tk_TimerToken timeout;
311 register RegisteredInterp *riPtr;
312 char *cmd;
313 int result;
314 Bool (*prevRestrictProc)(Display *, XEvent *, char *);
315 char *prevArg;
316 TkWindow *winPtr = senderRiPtr->winPtr;
317 TkDisplay *dispPtr = senderRiPtr->dispPtr;
318 int to_server = 0;
319
320 if (argc >= 2) {
321 Tk_Window tkwin = NULL;
322
323 if ((argv[1][0] == '-') &&
324 (strncmp(argv[1], "-quick", strlen(argv[1])) == 0)) {
325 quick = 1;
326 argv += 1; argc -= 1;
327 }
328 }
329
330 if (argc >= 3) {
331 Tk_Window tkwin = NULL;
332
333 if ((argv[1][0] == '-') &&
334 (strncmp(argv[1], "-server", strlen(argv[1])) == 0)) {
335 to_server = 1;
336 tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) winPtr);
337 if (tkwin == NULL) {
338 Tcl_AppendResult(interp, "bad server arg, should be window name: ",
339 argv[2], (char *) NULL);
340 return TCL_ERROR;
341 }
342 winPtr = (TkWindow *) tkwin;
343 dispPtr = winPtr->dispPtr;
344 argv += 2; argc -= 2;
345 }
346 }
347
348 if (dispPtr->commWindow == NULL) {
349 result = SendInit(interp, dispPtr);
350 if (result != TCL_OK) {
351 return result;
352 }
353 }
354
355 if (argc < 3) {
356 badargs:
357 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
358 " interpName arg ?arg ...?\"", (char *) NULL);
359 return TCL_ERROR;
360 }
361
362 if (argc == 3) {
363 cmd = argv[2];
364 } else {
365 cmd = Tcl_Concat(argc-2, argv+2);
366 }
367
368 /*
369 * See if the target interpreter is local. If so, execute
370 * the command directly without going through the X server.
371 * The only tricky thing is passing the result from the target
372 * interpreter to the invoking interpreter. Watch out: they
373 * could be the same!
374 */
375
376 for (riPtr = registry; riPtr != NULL; riPtr = riPtr->nextPtr) {
377 if (strcmp(riPtr->name, argv[1]) != 0) {
378 continue;
379 }
380 if (interp == riPtr->interp) {
381 result = Tcl_GlobalEval(interp, cmd);
382 } else {
383 result = Tcl_GlobalEval(riPtr->interp, cmd);
384 interp->result = riPtr->interp->result;
385 interp->freeProc = riPtr->interp->freeProc;
386 riPtr->interp->freeProc = 0;
387 Tcl_ResetResult(riPtr->interp);
388 }
389 if (cmd != argv[2]) {
390 ckfree(cmd);
391 }
392 return result;
393 }
394
395 /*
396 * Bind the interpreter name to a communication window.
397 */
398
399 w = LookupName(dispPtr, argv[1], 0);
400 if (w == 0) {
401 Tcl_AppendResult(interp, "no registered interpeter named \"",
402 argv[1], "\"", (char *) NULL);
403 if (cmd != argv[2]) {
404 ckfree(cmd);
405 }
406 return TCL_ERROR;
407 }
408
409 if (!quick) {
410 /*
411 * Register the fact that we're waiting for a command to
412 * complete (this is needed by SendEventProc and by
413 * AppendErrorProc to pass back the command's results).
414 */
415
416 serial++;
417 pending.serial = serial;
418 pending.target = argv[1];
419 pending.interp = interp;
420 pending.result = NULL;
421 pending.nextPtr = pendingCommands;
422 pendingCommands = &pending;
423 }
424
425 /*
426 * Send the command to target interpreter by appending it to the
427 * comm window in the communication window.
428 */
429
430 length = strlen(argv[1]) + strlen(cmd) + 30;
431 if (length <= STATIC_PROP_SPACE) {
432 property = staticSpace;
433 } else {
434 property = (char *) ckalloc((unsigned) length);
435 }
436 sprintf(property, "C %x %x %s|%s",
437 Tk_WindowId(dispPtr->commWindow), serial, argv[1], cmd);
438 (void) AppendPropCarefully(dispPtr->display, w, dispPtr->commProperty,
439 property, &pending);
440 if (length > STATIC_PROP_SPACE) {
441 ckfree(property);
442 }
443 if (cmd != argv[2]) {
444 ckfree(cmd);
445 }
446
447 if (quick) {
448 sprintf(interp->result, "NoReturnValue");
449 return TCL_OK;
450 }
451
452 /*
453 * Enter a loop processing X events until the result comes
454 * in. If no response is received within a few seconds,
455 * then timeout. While waiting for a result, look only at
456 * send-related events (otherwise it would be possible for
457 * additional input events, such as mouse motion, to cause
458 * other sends, leading eventually to such a large number
459 * of nested Tcl_Eval calls that the Tcl interpreter panics).
460 */
461
462 prevRestrictProc = Tk_RestrictEvents(SendRestrictProc,
463 (char *) dispPtr->commWindow, &prevArg);
464 timeout = Tk_CreateTimerHandler(5000, TimeoutProc,
465 (ClientData) &pending);
466 while (pending.result == NULL) {
467 Tk_DoOneEvent(0);
468 }
469 Tk_DeleteTimerHandler(timeout);
470 (void) Tk_RestrictEvents(prevRestrictProc, prevArg, &prevArg);
471
472 /*
473 * Unregister the information about the pending command
474 * and return the result.
475 */
476
477 if (pendingCommands == &pending) {
478 pendingCommands = pending.nextPtr;
479 } else {
480 PendingCommand *pcPtr;
481
482 for (pcPtr = pendingCommands; pcPtr != NULL;
483 pcPtr = pcPtr->nextPtr) {
484 if (pcPtr->nextPtr == &pending) {
485 pcPtr->nextPtr = pending.nextPtr;
486 break;
487 }
488 }
489 }
490 Tcl_SetResult(interp, pending.result, TCL_DYNAMIC);
491 return pending.code;
492
493 }
494 \f
495 /*
496 *----------------------------------------------------------------------
497 *
498 * TkGetInterpNames --
499 *
500 * This procedure is invoked to fetch a list of all the
501 * interpreter names currently registered for the display
502 * of a particular window.
503 *
504 * Results:
505 * A standard Tcl return value. Interp->result will be set
506 * to hold a list of all the interpreter names defined for
507 * tkwin's display. If an error occurs, then TCL_ERROR
508 * is returned and interp->result will hold an error message.
509 *
510 * Side effects:
511 * None.
512 *
513 *----------------------------------------------------------------------
514 */
515
516 int
517 TkGetInterpNames (
518 Tcl_Interp *interp, /* Interpreter for returning a result. */
519 Tk_Window tkwin /* Window whose display is to be used
520 * for the lookup. */
521 )
522 {
523 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
524 char *regProp, *separator, *name;
525 register char *p;
526 int result, actualFormat;
527 unsigned long numItems, bytesAfter;
528 Atom actualType;
529
530 /*
531 * Read the registry property.
532 */
533
534 regProp = NULL;
535 result = XGetWindowProperty(dispPtr->display,
536 Tk_DefaultRootWindow(dispPtr->display),
537 dispPtr->registryProperty, 0, MAX_PROP_WORDS,
538 False, XA_STRING, &actualType, &actualFormat,
539 &numItems, &bytesAfter, (unsigned char **) &regProp);
540
541 if (actualType == None) {
542 sprintf(interp->result, "couldn't read intepreter registry property");
543 return TCL_ERROR;
544 }
545
546 /*
547 * If the property is improperly formed, then delete it.
548 */
549
550 if ((result != Success) || (actualFormat != 8)
551 || (actualType != XA_STRING)) {
552 if (regProp != NULL) {
553 XFree(regProp);
554 }
555 sprintf(interp->result, "intepreter registry property is badly formed");
556 return TCL_ERROR;
557 }
558
559 /*
560 * Scan all of the names out of the property.
561 */
562
563 separator = "";
564 for (p = regProp; (p-regProp) < numItems; p++) {
565 name = p;
566 while ((*p != 0) && (!isspace(*p))) {
567 p++;
568 }
569 if (*p != 0) {
570 name = p+1;
571 name = Tcl_Merge(1, &name);
572 Tcl_AppendResult(interp, separator, name, (char *) NULL);
573 while (*p != 0) {
574 p++;
575 }
576 separator = " ";
577 }
578 }
579 XFree(regProp);
580 return TCL_OK;
581 }
582 \f
583 /*
584 *--------------------------------------------------------------
585 *
586 * SendInit --
587 *
588 * This procedure is called to initialize the
589 * communication channels for sending commands and
590 * receiving results.
591 *
592 * Results:
593 * The result is a standard Tcl return value, which is
594 * normally TCL_OK. If an error occurs then an error
595 * message is left in interp->result and TCL_ERROR is
596 * returned.
597 *
598 * Side effects:
599 * Sets up various data structures and windows.
600 *
601 *--------------------------------------------------------------
602 */
603
604 static int
605 SendInit (
606 Tcl_Interp *interp, /* Interpreter to use for error
607 * reporting. */
608 register TkDisplay *dispPtr/* Display to initialize. */
609 )
610
611 {
612 XSetWindowAttributes atts;
613
614 /*
615 * Create the window used for communication, and set up an
616 * event handler for it.
617 */
618
619 dispPtr->commWindow = Tk_CreateWindow(interp, (Tk_Window) NULL,
620 "_comm", DisplayString(dispPtr->display));
621 if (dispPtr->commWindow == NULL) {
622 return TCL_ERROR;
623 }
624 atts.override_redirect = True;
625 Tk_ChangeWindowAttributes(dispPtr->commWindow,
626 CWOverrideRedirect, &atts);
627 Tk_CreateEventHandler(dispPtr->commWindow, PropertyChangeMask,
628 SendEventProc, (ClientData) dispPtr);
629 Tk_MakeWindowExist(dispPtr->commWindow);
630
631 /*
632 * Get atoms used as property names.
633 */
634
635 dispPtr->commProperty = XInternAtom(dispPtr->display,
636 "Comm", False);
637 dispPtr->registryProperty = XInternAtom(dispPtr->display,
638 "InterpRegistry", False);
639 return TCL_OK;
640 }
641 \f
642 /*
643 *--------------------------------------------------------------
644 *
645 * LookupName --
646 *
647 * Given an interpreter name, see if the name exists in
648 * the interpreter registry for a particular display.
649 *
650 * Results:
651 * If the given name is registered, return the ID of
652 * the window associated with the name. If the name
653 * isn't registered, then return 0.
654 *
655 * Side effects:
656 * If the registry property is improperly formed, then
657 * it is deleted. If "delete" is non-zero, then if the
658 * named interpreter is found it is removed from the
659 * registry property.
660 *
661 *--------------------------------------------------------------
662 */
663
664 static Window
665 LookupName (
666 register TkDisplay *dispPtr,
667 char *name, /* Name of an interpreter. */
668 int delete /* If non-zero, delete info about name. */
669 )
670 {
671 char *regProp, *entry;
672 register char *p;
673 int result, actualFormat;
674 unsigned long numItems, bytesAfter;
675 Atom actualType;
676 Window returnValue;
677
678 /*
679 * Read the registry property.
680 */
681
682 regProp = NULL;
683 result = XGetWindowProperty(dispPtr->display,
684 Tk_DefaultRootWindow(dispPtr->display),
685 dispPtr->registryProperty, 0, MAX_PROP_WORDS,
686 False, XA_STRING, &actualType, &actualFormat,
687 &numItems, &bytesAfter, (unsigned char **) &regProp);
688
689 if (actualType == None) {
690 return 0;
691 }
692
693 /*
694 * If the property is improperly formed, then delete it.
695 */
696
697 if ((result != Success) || (actualFormat != 8)
698 || (actualType != XA_STRING)) {
699 if (regProp != NULL) {
700 XFree(regProp);
701 }
702 XDeleteProperty(dispPtr->display,
703 Tk_DefaultRootWindow(dispPtr->display),
704 dispPtr->registryProperty);
705 return 0;
706 }
707
708 /*
709 * Scan the property for the desired name.
710 */
711
712 returnValue = (Window) 0;
713 entry = NULL; /* Not needed, but eliminates compiler warning. */
714 for (p = regProp; (p-regProp) < numItems; ) {
715 entry = p;
716 while ((*p != 0) && (!isspace(*p))) {
717 p++;
718 }
719 if ((*p != 0) && (strcmp(name, p+1) == 0)) {
720 sscanf(entry, "%x", &returnValue);
721 break;
722 }
723 while (*p != 0) {
724 p++;
725 }
726 p++;
727 }
728
729 /*
730 * Delete the property, if that is desired (copy down the
731 * remainder of the registry property to overlay the deleted
732 * info, then rewrite the property).
733 */
734
735 if ((delete) && (returnValue != 0)) {
736 int count;
737
738 while (*p != 0) {
739 p++;
740 }
741 p++;
742 count = numItems - (p-regProp);
743 if (count > 0) {
744 memcpy((VOID *) entry, (VOID *) p, count);
745 }
746 XChangeProperty(dispPtr->display,
747 Tk_DefaultRootWindow(dispPtr->display),
748 dispPtr->registryProperty, XA_STRING, 8,
749 PropModeReplace, (unsigned char *) regProp,
750 (int) (numItems - (p-entry)));
751 XSync(dispPtr->display, False);
752 }
753
754 XFree(regProp);
755 return returnValue;
756 }
757 \f
758 /*
759 *--------------------------------------------------------------
760 *
761 * SendEventProc --
762 *
763 * This procedure is invoked automatically by the toolkit
764 * event manager when a property changes on the communication
765 * window. This procedure reads the property and handles
766 * command requests and responses.
767 *
768 * Results:
769 * None.
770 *
771 * Side effects:
772 * If there are command requests in the property, they
773 * are executed. If there are responses in the property,
774 * their information is saved for the (ostensibly waiting)
775 * "send" commands. The property is deleted.
776 *
777 *--------------------------------------------------------------
778 */
779
780 static void
781 SendEventProc (
782 ClientData clientData, /* Display information. */
783 XEvent *eventPtr /* Information about event. */
784 )
785 {
786 TkDisplay *dispPtr = (TkDisplay *) clientData;
787 char *propInfo;
788 register char *p;
789 int result, actualFormat;
790 unsigned long numItems, bytesAfter;
791 Atom actualType;
792
793 if ((eventPtr->xproperty.atom != dispPtr->commProperty)
794 || (eventPtr->xproperty.state != PropertyNewValue)) {
795 return;
796 }
797
798 /*
799 * Read the comm property and delete it.
800 */
801
802 propInfo = NULL;
803 result = XGetWindowProperty(dispPtr->display,
804 Tk_WindowId(dispPtr->commWindow),
805 dispPtr->commProperty, 0, MAX_PROP_WORDS, True,
806 XA_STRING, &actualType, &actualFormat,
807 &numItems, &bytesAfter, (unsigned char **) &propInfo);
808
809 /*
810 * If the property doesn't exist or is improperly formed
811 * then ignore it.
812 */
813
814 if ((result != Success) || (actualType != XA_STRING)
815 || (actualFormat != 8)) {
816 if (propInfo != NULL) {
817 XFree(propInfo);
818 }
819 return;
820 }
821
822 /*
823 * The property is divided into records separated by null
824 * characters. Each record represents one command request
825 * or response. Scan through the property one record at a
826 * time.
827 */
828
829 for (p = propInfo; (p-propInfo) < numItems; ) {
830 if (*p == 'C') {
831 Window window;
832 int serial, resultSize;
833 char *resultString, *interpName, *returnProp, *end;
834 register RegisteredInterp *riPtr;
835 char errorMsg[100];
836 #define STATIC_RESULT_SPACE 100
837 char staticSpace[STATIC_RESULT_SPACE];
838
839 /*
840 *-----------------------------------------------------
841 * This is an incoming command sent by another window.
842 * Parse the fields of the command string. If the command
843 * string isn't properly formed, send back an error message
844 * if there's enough well-formed information to generate
845 * a proper reply; otherwise just ignore the message.
846 *-----------------------------------------------------
847 */
848
849 p++;
850 window = (Window) strtol(p, &end, 16);
851 if (end == p) {
852 goto nextRecord;
853 }
854 p = end;
855 if (*p != ' ') {
856 goto nextRecord;
857 }
858 p++;
859 serial = strtol(p, &end, 16);
860 if (end == p) {
861 goto nextRecord;
862 }
863 p = end;
864 if (*p != ' ') {
865 goto nextRecord;
866 }
867 p++;
868 interpName = p;
869 while ((*p != 0) && (*p != '|')) {
870 p++;
871 }
872 if (*p != '|') {
873 result = TCL_ERROR;
874 resultString = "bad property format for sent command";
875 goto returnResult;
876 }
877 *p = 0;
878 p++;
879
880 /*
881 * Locate the interpreter for the command, then
882 * execute the command.
883 */
884
885 for (riPtr = registry; ; riPtr = riPtr->nextPtr) {
886 if (riPtr == NULL) {
887 result = TCL_ERROR;
888 sprintf(errorMsg,
889 "receiver never heard of interpreter \"%.40s\"",
890 interpName);
891 resultString = errorMsg;
892 goto returnResult;
893 }
894 if (strcmp(riPtr->name, interpName) == 0) {
895 break;
896 }
897 }
898 result = Tcl_GlobalEval(riPtr->interp, p);
899 resultString = riPtr->interp->result;
900
901 /*
902 * Return the result to the sender.
903 */
904
905 returnResult:
906 resultSize = strlen(resultString) + 30;
907 if (resultSize <= STATIC_RESULT_SPACE) {
908 returnProp = staticSpace;
909 } else {
910 returnProp = (char *) ckalloc((unsigned) resultSize);
911 }
912 sprintf(returnProp, "R %x %d %s", serial, result,
913 resultString);
914 (void) AppendPropCarefully(dispPtr->display, window,
915 dispPtr->commProperty, returnProp,
916 (PendingCommand *) NULL);
917 if (returnProp != staticSpace) {
918 ckfree(returnProp);
919 }
920 } else if (*p == 'R') {
921 int serial, code;
922 char *end;
923 register PendingCommand *pcPtr;
924
925 /*
926 *-----------------------------------------------------
927 * This record in the property is a result being
928 * returned for a command sent from here. First
929 * parse the fields.
930 *-----------------------------------------------------
931 */
932
933 p++;
934 serial = strtol(p, &end, 16);
935 if (end == p) {
936 goto nextRecord;
937 }
938 p = end;
939 if (*p != ' ') {
940 goto nextRecord;
941 }
942 p++;
943 code = strtol(p, &end, 10);
944 if (end == p) {
945 goto nextRecord;
946 }
947 p = end;
948 if (*p != ' ') {
949 goto nextRecord;
950 }
951 p++;
952
953 /*
954 * Give the result information to anyone who's
955 * waiting for it.
956 */
957
958 for (pcPtr = pendingCommands; pcPtr != NULL;
959 pcPtr = pcPtr->nextPtr) {
960 if ((serial != pcPtr->serial) || (pcPtr->result != NULL)) {
961 continue;
962 }
963 pcPtr->code = code;
964 pcPtr->result = ckalloc((unsigned) (strlen(p) + 1));
965 strcpy(pcPtr->result, p);
966 break;
967 }
968 }
969
970 nextRecord:
971 while (*p != 0) {
972 p++;
973 }
974 p++;
975 }
976 XFree(propInfo);
977 }
978 \f
979 /*
980 *--------------------------------------------------------------
981 *
982 * AppendPropCarefully --
983 *
984 * Append a given property to a given window, but set up
985 * an X error handler so that if the append fails this
986 * procedure can return an error code rather than having
987 * Xlib panic.
988 *
989 * Results:
990 * None.
991 *
992 * Side effects:
993 * The given property on the given window is appended to.
994 * If this operation fails and if pendingPtr is non-NULL,
995 * then the pending operation is marked as complete with
996 * an error.
997 *
998 *--------------------------------------------------------------
999 */
1000
1001 static void
1002 AppendPropCarefully (
1003 Display *display, /* Display on which to operate. */
1004 Window window, /* Window whose property is to
1005 * be modified. */
1006 Atom property, /* Name of property. */
1007 char *value, /* Characters (null-terminated) to
1008 * append to property. */
1009 PendingCommand *pendingPtr /* Pending command to mark complete
1010 * if an error occurs during the
1011 * property op. NULL means just
1012 * ignore the error. */
1013 )
1014 {
1015 Tk_ErrorHandler handler;
1016
1017 handler = Tk_CreateErrorHandler(display, -1, -1, -1, AppendErrorProc,
1018 (ClientData) pendingPtr);
1019 XChangeProperty(display, window, property, XA_STRING, 8,
1020 PropModeAppend, (unsigned char *) value, strlen(value)+1);
1021 Tk_DeleteErrorHandler(handler);
1022 }
1023
1024 /*
1025 * The procedure below is invoked if an error occurs during
1026 * the XChangeProperty operation above.
1027 */
1028
1029 /* ARGSUSED */
1030 static int
1031 AppendErrorProc (
1032 ClientData clientData, /* Command to mark complete, or NULL. */
1033 XErrorEvent *errorPtr /* Information about error. */
1034 )
1035 {
1036 PendingCommand *pendingPtr = (PendingCommand *) clientData;
1037 register PendingCommand *pcPtr;
1038
1039 if (pendingPtr == NULL) {
1040 return 0;
1041 }
1042
1043 /*
1044 * Make sure this command is still pending.
1045 */
1046
1047 for (pcPtr = pendingCommands; pcPtr != NULL;
1048 pcPtr = pcPtr->nextPtr) {
1049 if ((pcPtr == pendingPtr) && (pcPtr->result == NULL)) {
1050 pcPtr->result = ckalloc((unsigned) (strlen(pcPtr->target) + 50));
1051 sprintf(pcPtr->result,
1052 "send to \"%s\" failed (no communication window)",
1053 pcPtr->target);
1054 pcPtr->code = TCL_ERROR;
1055 break;
1056 }
1057 }
1058 return 0;
1059 }
1060 \f
1061 /*
1062 *--------------------------------------------------------------
1063 *
1064 * TimeoutProc --
1065 *
1066 * This procedure is invoked when too much time has elapsed
1067 * during the processing of a sent command.
1068 *
1069 * Results:
1070 * None.
1071 *
1072 * Side effects:
1073 * Mark the pending command as complete, with an error
1074 * message signalling the timeout.
1075 *
1076 *--------------------------------------------------------------
1077 */
1078
1079 static void
1080 TimeoutProc (
1081 ClientData clientData /* Information about command that
1082 * has been sent but not yet
1083 * responded to. */
1084 )
1085 {
1086 PendingCommand *pcPtr = (PendingCommand *) clientData;
1087 register PendingCommand *pcPtr2;
1088
1089 /*
1090 * Make sure that the command is still in the pending list
1091 * and that it hasn't already completed. Then register the
1092 * error.
1093 */
1094
1095 for (pcPtr2 = pendingCommands; pcPtr2 != NULL;
1096 pcPtr2 = pcPtr2->nextPtr) {
1097 static char msg[] = "remote interpreter did not respond";
1098 if ((pcPtr2 != pcPtr) || (pcPtr2->result != NULL)) {
1099 continue;
1100 }
1101 pcPtr2->code = TCL_ERROR;
1102 pcPtr2->result = ckalloc((unsigned) (sizeof(msg) + 1));
1103 strcpy(pcPtr2->result, msg);
1104 return;
1105 }
1106 }
1107 \f
1108 /*
1109 *--------------------------------------------------------------
1110 *
1111 * DeleteProc --
1112 *
1113 * This procedure is invoked by Tcl when a registered
1114 * interpreter is about to be deleted. It unregisters
1115 * the interpreter.
1116 *
1117 * Results:
1118 * None.
1119 *
1120 * Side effects:
1121 * The interpreter given by riPtr is unregistered.
1122 *
1123 *--------------------------------------------------------------
1124 */
1125
1126 static void
1127 DeleteProc (
1128 ClientData clientData /* Info about registration, passed
1129 * as ClientData. */
1130 )
1131 {
1132 RegisteredInterp *riPtr = (RegisteredInterp *) clientData;
1133 register RegisteredInterp *riPtr2;
1134
1135 (void) LookupName(riPtr->dispPtr, riPtr->name, 1);
1136 if (registry == riPtr) {
1137 registry = riPtr->nextPtr;
1138 } else {
1139 for (riPtr2 = registry; riPtr2 != NULL;
1140 riPtr2 = riPtr2->nextPtr) {
1141 if (riPtr2->nextPtr == riPtr) {
1142 riPtr2->nextPtr = riPtr->nextPtr;
1143 break;
1144 }
1145 }
1146 }
1147 ckfree((char *) riPtr->name);
1148 ckfree((char *) riPtr);
1149 }
1150 \f
1151 /*
1152 *----------------------------------------------------------------------
1153 *
1154 * SendRestrictProc --
1155 *
1156 * This procedure filters incoming events when a "send" command
1157 * is outstanding. It defers all events except those containing
1158 * send commands and results.
1159 *
1160 * Results:
1161 * False is returned except for property-change events on the
1162 * given commWindow.
1163 *
1164 * Side effects:
1165 * None.
1166 *
1167 *----------------------------------------------------------------------
1168 */
1169
1170 /* ARGSUSED */
1171 static Bool
1172 SendRestrictProc (
1173 Display *display, /* Display from which event arrived. */
1174 register XEvent *eventPtr, /* Event that just arrived. */
1175 char *arg /* Comunication window in which
1176 * we're interested. */
1177 )
1178 {
1179 register Tk_Window comm = (Tk_Window) arg;
1180
1181 if ((display != Tk_Display(comm))
1182 || (eventPtr->type != PropertyNotify)
1183 || (eventPtr->xproperty.window != Tk_WindowId(comm))) {
1184 return False;
1185 }
1186 return True;
1187 }
Impressum, Datenschutz