OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

[/] [or1k_old/] [trunk/] [insight/] [tk/] [unix/] [tkUnixSend.c] - Blame information for rev 1782

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.