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