1 /*
2  * tkMacOSXHLEvents.c --
3  *
4  *	Implements high level event support for the Macintosh.
5  *
6  * Copyright © 1995-1997 Sun Microsystems, Inc.
7  * Copyright © 2001-2009 Apple Inc.
8  * Copyright © 2006-2009 Daniel A. Steffen <das@users.sourceforge.net>
9  * Copyright © 2015-2019 Marc Culler
10  * Copyright © 2019 Kevin Walzer/WordTech Communications LLC.
11  *
12  * See the file "license.terms" for information on usage and redistribution
13  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14  */
15 
16 #include "tkMacOSXPrivate.h"
17 #include <sys/param.h>
18 #define URL_MAX_LENGTH (17 + MAXPATHLEN)
19 
20 /*
21  * This is a Tcl_Event structure that the Quit AppleEvent handler uses to
22  * schedule the ReallyKillMe function.
23  */
24 
25 typedef struct KillEvent {
26     Tcl_Event header;		/* Information that is standard for all
27 				 * events. */
28     Tcl_Interp *interp;		/* Interp that was passed to the Quit
29 				 * AppleEvent */
30 } KillEvent;
31 
32 /*
33  * When processing an AppleEvent as an idle task, a pointer to one
34  * of these structs is passed as the clientData.
35  */
36 
37 typedef struct AppleEventInfo {
38     Tcl_Interp *interp;
39     const char *procedure;
40     Tcl_DString command;
41     NSAppleEventDescriptor *replyEvent; /* Only used for DoScriptText. */
42     int retryCount;
43 } AppleEventInfo;
44 
45 /*
46  * Static functions used only in this file.
47  */
48 
49 static int  MissedAnyParameters(const AppleEvent *theEvent);
50 static int  ReallyKillMe(Tcl_Event *eventPtr, int flags);
51 static void ProcessAppleEvent(ClientData clientData);
52 
53 /*
54  * Names of the procedures which can be used to process AppleEvents.
55  */
56 
57 static const char openDocumentProc[] = "::tk::mac::OpenDocument";
58 static const char launchURLProc[] = "::tk::mac::LaunchURL";
59 static const char printDocProc[] = "::tk::mac::PrintDocument";
60 static const char scriptFileProc[] = "::tk::mac::DoScriptFile";
61 static const char scriptTextProc[] = "::tk::mac::DoScriptText";
62 
63 #pragma mark TKApplication(TKHLEvents)
64 
65 @implementation TKApplication(TKHLEvents)
66 - (void) terminate: (id) sender
67 {
68     (void)sender;
69     [self handleQuitApplicationEvent:Nil withReplyEvent:Nil];
70 }
71 
72 - (void) superTerminate: (id) sender
73 {
74     (void) sender;
75     [super terminate:nil];
76 }
77 
78 - (void) preferences: (id) sender
79 {
80     (void)sender;
81     [self handleShowPreferencesEvent:Nil withReplyEvent:Nil];
82 }
83 
84 - (void) handleQuitApplicationEvent: (NSAppleEventDescriptor *)event
85     withReplyEvent: (NSAppleEventDescriptor *)replyEvent
86 {
87     KillEvent *eventPtr;
88     (void)event;
89     (void)replyEvent;
90 
91     if (_eventInterp) {
92 	/*
93 	 * Call the exit command from the event loop, since you are not
94 	 * supposed to call ExitToShell in an Apple Event Handler. We put this
95 	 * at the head of Tcl's event queue because this message usually comes
96 	 * when the Mac is shutting down, and we want to kill the shell as
97 	 * quickly as possible.
98 	 */
99 
100 	eventPtr = (KillEvent *)ckalloc(sizeof(KillEvent));
101 	eventPtr->header.proc = ReallyKillMe;
102 	eventPtr->interp = _eventInterp;
103 
104 	Tcl_QueueEvent((Tcl_Event *) eventPtr, TCL_QUEUE_HEAD);
105     }
106 }
107 
108 - (void) handleOpenApplicationEvent: (NSAppleEventDescriptor *)event
109     withReplyEvent: (NSAppleEventDescriptor *)replyEvent
110 {
111     (void)event;
112     (void)replyEvent;
113 
114     if (_eventInterp &&
115 	Tcl_FindCommand(_eventInterp, "::tk::mac::OpenApplication", NULL, 0)){
116 	int code = Tcl_EvalEx(_eventInterp, "::tk::mac::OpenApplication",
117 			      -1, TCL_EVAL_GLOBAL);
118 	if (code != TCL_OK) {
119 	    Tcl_BackgroundException(_eventInterp, code);
120 	}
121     }
122 }
123 
124 - (void) handleReopenApplicationEvent: (NSAppleEventDescriptor *)event
125     withReplyEvent: (NSAppleEventDescriptor *)replyEvent
126 {
127     (void)event;
128     (void)replyEvent;
129 
130     [NSApp activateIgnoringOtherApps: YES];
131     if (_eventInterp && Tcl_FindCommand(_eventInterp,
132 	    "::tk::mac::ReopenApplication", NULL, 0)) {
133 	int code = Tcl_EvalEx(_eventInterp, "::tk::mac::ReopenApplication",
134 			      -1, TCL_EVAL_GLOBAL);
135 	if (code != TCL_OK){
136 	    Tcl_BackgroundException(_eventInterp, code);
137 	}
138     }
139 }
140 
141 - (void) handleShowPreferencesEvent: (NSAppleEventDescriptor *)event
142     withReplyEvent: (NSAppleEventDescriptor *)replyEvent
143 {
144     (void)event;
145     (void)replyEvent;
146 
147     if (_eventInterp &&
148 	    Tcl_FindCommand(_eventInterp, "::tk::mac::ShowPreferences", NULL, 0)){
149 	int code = Tcl_EvalEx(_eventInterp, "::tk::mac::ShowPreferences",
150 			      -1, TCL_EVAL_GLOBAL);
151 	if (code != TCL_OK) {
152 	    Tcl_BackgroundException(_eventInterp, code);
153 	}
154     }
155 }
156 
157 - (void) handleOpenDocumentsEvent: (NSAppleEventDescriptor *)event
158     withReplyEvent: (NSAppleEventDescriptor *)replyEvent
159 {
160     Tcl_Encoding utf8;
161     const AEDesc *fileSpecDesc = nil;
162     AEDesc contents;
163     char URLString[1 + URL_MAX_LENGTH];
164     NSURL *fileURL;
165     DescType type;
166     Size actual;
167     long count, index;
168     AEKeyword keyword;
169     Tcl_DString pathName;
170     (void)replyEvent;
171 
172     /*
173      * Do nothing if we don't have an interpreter.
174      */
175 
176     if (!_eventInterp) {
177 	return;
178     }
179 
180     fileSpecDesc = [event aeDesc];
181     if (fileSpecDesc == nil ) {
182     	return;
183     }
184 
185     /*
186      * The AppleEvent's descriptor should either contain a value of
187      * typeObjectSpecifier or typeAEList.  In the first case, the descriptor
188      * can be treated as a list of size 1 containing a value which can be
189      * coerced into a fileURL. In the second case we want to work with the list
190      * itself.  Values in the list will be coerced into fileURL's if possible;
191      * otherwise they will be ignored.
192      */
193 
194     /* Get a copy of the AppleEvent's descriptor. */
195     AEGetParamDesc(fileSpecDesc, keyDirectObject, typeWildCard, &contents);
196     if (contents.descriptorType == typeAEList) {
197     	fileSpecDesc = &contents;
198     }
199 
200     if (AECountItems(fileSpecDesc, &count) != noErr) {
201 	AEDisposeDesc(&contents);
202     	return;
203     }
204 
205     /*
206      * Construct a Tcl expression which calls the ::tk::mac::OpenDocument
207      * procedure, passing the paths contained in the AppleEvent as arguments.
208      */
209 
210     AppleEventInfo *AEInfo = (AppleEventInfo *)ckalloc(sizeof(AppleEventInfo));
211     Tcl_DString *openCommand = &AEInfo->command;
212     Tcl_DStringInit(openCommand);
213     Tcl_DStringAppend(openCommand, openDocumentProc, -1);
214     utf8 = Tcl_GetEncoding(NULL, "utf-8");
215 
216     for (index = 1; index <= count; index++) {
217 	if (noErr != AEGetNthPtr(fileSpecDesc, index, typeFileURL, &keyword,
218 				 &type, (Ptr) URLString, URL_MAX_LENGTH, &actual)) {
219 	    continue;
220 	}
221 	if (type != typeFileURL) {
222 	    continue;
223 	}
224 	URLString[actual] = '\0';
225 	fileURL = [NSURL URLWithString:[NSString stringWithUTF8String:(char*)URLString]];
226 	if (fileURL == nil) {
227 	    continue;
228 	}
229 	Tcl_ExternalToUtfDString(utf8, [[fileURL path] UTF8String], -1, &pathName);
230 	Tcl_DStringAppendElement(openCommand, Tcl_DStringValue(&pathName));
231 	Tcl_DStringFree(&pathName);
232     }
233 
234     Tcl_FreeEncoding(utf8);
235     AEDisposeDesc(&contents);
236     AEInfo->interp = _eventInterp;
237     AEInfo->procedure = openDocumentProc;
238     AEInfo->replyEvent = nil;
239     Tcl_DoWhenIdle(ProcessAppleEvent, (ClientData)AEInfo);
240     AEInfo->retryCount = 0;
241 
242     if (Tcl_FindCommand(_eventInterp, "::tk::mac::OpenDocuments", NULL, 0)){
243 	ProcessAppleEvent((ClientData)AEInfo);
244     } else {
245 	Tcl_CreateTimerHandler(500, ProcessAppleEvent, (ClientData)AEInfo);
246     }
247 }
248 
249 - (void) handlePrintDocumentsEvent: (NSAppleEventDescriptor *)event
250 		    withReplyEvent: (NSAppleEventDescriptor *)replyEvent
251 {
252     NSString* file = [[event paramDescriptorForKeyword:keyDirectObject]
253 			 stringValue];
254     const char *printFile = [file UTF8String];
255     AppleEventInfo *AEInfo = (AppleEventInfo *)ckalloc(sizeof(AppleEventInfo));
256     Tcl_DString *printCommand = &AEInfo->command;
257     (void)replyEvent;
258 
259     Tcl_DStringInit(printCommand);
260     Tcl_DStringAppend(printCommand, printDocProc, -1);
261     Tcl_DStringAppendElement(printCommand, printFile);
262     AEInfo->interp = _eventInterp;
263     AEInfo->procedure = printDocProc;
264     AEInfo->replyEvent = nil;
265     Tcl_DoWhenIdle(ProcessAppleEvent, (ClientData)AEInfo);
266     AEInfo->retryCount = 0;
267     ProcessAppleEvent((ClientData)AEInfo);
268 }
269 
270 - (void) handleDoScriptEvent: (NSAppleEventDescriptor *)event
271 	      withReplyEvent: (NSAppleEventDescriptor *)replyEvent
272 {
273     OSStatus err;
274     const AEDesc *theDesc = nil;
275     DescType type = 0, initialType = 0;
276     Size actual;
277     char URLBuffer[1 + URL_MAX_LENGTH];
278     char errString[128];
279 
280     /*
281      * The DoScript event receives one parameter that should be text data or a
282      * fileURL.
283      */
284 
285     theDesc = [event aeDesc];
286     if (theDesc == nil) {
287 	return;
288     }
289 
290     err = AEGetParamPtr(theDesc, keyDirectObject, typeWildCard, &initialType,
291 			NULL, 0, NULL);
292     if (err != noErr) {
293 	sprintf(errString, "AEDoScriptHandler: GetParamDesc error %d", (int)err);
294 	AEPutParamPtr((AppleEvent*)[replyEvent aeDesc], keyErrorString,
295 		      typeChar, errString, strlen(errString));
296 	return;
297     }
298 
299     if (MissedAnyParameters((AppleEvent*)theDesc)) {
300     	sprintf(errString, "AEDoScriptHandler: extra parameters");
301     	AEPutParamPtr((AppleEvent*)[replyEvent aeDesc], keyErrorString,
302 		      typeChar,errString, strlen(errString));
303     	return;
304     }
305 
306     if (initialType == typeFileURL || initialType == typeAlias) {
307 
308 	/*
309 	 * This descriptor can be coerced to a file url.  Construct a Tcl
310 	 * expression which passes the file path as a string argument to
311          * ::tk::mac::DoScriptFile.
312 	 */
313 
314 	if (noErr == AEGetParamPtr(theDesc, keyDirectObject, typeFileURL, &type,
315                                   (Ptr) URLBuffer, URL_MAX_LENGTH, &actual)) {
316             if (actual > 0) {
317                 URLBuffer[actual] = '\0';
318                 NSString *urlString = [NSString stringWithUTF8String:(char*)URLBuffer];
319                 NSURL *fileURL = [NSURL URLWithString:urlString];
320                 AppleEventInfo *AEInfo = (AppleEventInfo *)ckalloc(sizeof(AppleEventInfo));
321                 Tcl_DString *scriptFileCommand = &AEInfo->command;
322                 Tcl_DStringInit(scriptFileCommand);
323                 Tcl_DStringAppend(scriptFileCommand, scriptFileProc, -1);
324                 Tcl_DStringAppendElement(scriptFileCommand, [[fileURL path] UTF8String]);
325                 AEInfo->interp = _eventInterp;
326                 AEInfo->procedure = scriptFileProc;
327                 AEInfo->replyEvent = nil;
328                 Tcl_DoWhenIdle(ProcessAppleEvent, (ClientData)AEInfo);
329 		AEInfo->retryCount = 0;
330                 ProcessAppleEvent((ClientData)AEInfo);
331             }
332         }
333     } else if (noErr == AEGetParamPtr(theDesc, keyDirectObject, typeUTF8Text, &type,
334 			       NULL, 0, &actual)) {
335         /*
336          * The descriptor cannot be coerced to a file URL but can be coerced to
337          * text.  Construct a Tcl expression which passes the text as a string
338          * argument to ::tk::mac::DoScriptText.
339          */
340 
341 	if (actual > 0) {
342 	    char *data = (char *)ckalloc(actual + 1);
343 	    if (noErr == AEGetParamPtr(theDesc, keyDirectObject,
344 				       typeUTF8Text, &type,
345 				       data, actual, NULL)) {
346 		data[actual] = '\0';
347 		AppleEventInfo *AEInfo = (AppleEventInfo *)ckalloc(sizeof(AppleEventInfo));
348 		Tcl_DString *scriptTextCommand = &AEInfo->command;
349 		Tcl_DStringInit(scriptTextCommand);
350 		Tcl_DStringAppend(scriptTextCommand, scriptTextProc, -1);
351 		Tcl_DStringAppendElement(scriptTextCommand, data);
352 		AEInfo->interp = _eventInterp;
353 		AEInfo->procedure = scriptTextProc;
354 		AEInfo->retryCount = 0;
355                 if (Tcl_FindCommand(AEInfo->interp, AEInfo->procedure, NULL, 0)) {
356                     AEInfo->replyEvent = replyEvent;
357                     ProcessAppleEvent(AEInfo);
358                 } else {
359                     AEInfo->replyEvent = nil;
360                     Tcl_DoWhenIdle(ProcessAppleEvent, AEInfo);
361                     ProcessAppleEvent(AEInfo);
362                 }
363 	    }
364 	}
365     }
366 }
367 
368 - (void)handleURLEvent:(NSAppleEventDescriptor*)event
369         withReplyEvent:(NSAppleEventDescriptor*)replyEvent
370 {
371     NSString* url = [[event paramDescriptorForKeyword:keyDirectObject]
372                         stringValue];
373     const char *cURL=[url UTF8String];
374     AppleEventInfo *AEInfo = (AppleEventInfo *)ckalloc(sizeof(AppleEventInfo));
375     Tcl_DString *launchCommand = &AEInfo->command;
376     (void)replyEvent;
377 
378     Tcl_DStringInit(launchCommand);
379     Tcl_DStringAppend(launchCommand, launchURLProc, -1);
380     Tcl_DStringAppendElement(launchCommand, cURL);
381     AEInfo->interp = _eventInterp;
382     AEInfo->procedure = launchURLProc;
383     AEInfo->replyEvent = nil;
384     Tcl_DoWhenIdle(ProcessAppleEvent, (ClientData)AEInfo);
385     AEInfo->retryCount = 0;
386     ProcessAppleEvent((ClientData)AEInfo);
387 }
388 
389 @end
390 
391 #pragma mark -
392 
393 /*
394  *----------------------------------------------------------------------
395  *
396  * ProcessAppleEvent --
397  *
398  *      Usually used as an idle task which evaluates a Tcl expression generated
399  *      from an AppleEvent.  If the AppleEventInfo passed as the client data
400  *      has a non-null replyEvent, the result of evaluating the expression will
401  *      be added to the reply.  This must not be done when this function is
402  *      called as an idle task, but is done when handling DoScriptText events
403  *      when this function is called directly.
404  *
405  * Results:
406  *	None.
407  *
408  * Side effects:
409  *	The expression will be evaluated and the clientData will be freed.
410  *      The replyEvent may be modified to contain the result of evaluating
411  *      a Tcl expression.
412  *
413  *----------------------------------------------------------------------
414  */
415 
ProcessAppleEvent(ClientData clientData)416 static void ProcessAppleEvent(
417     ClientData clientData)
418 {
419     int code;
420     AppleEventInfo *AEInfo = (AppleEventInfo*) clientData;
421 
422     if (!AEInfo->interp) {
423 	return;
424     }
425 
426     /*
427      * Apple events that are delivered during the app startup can arrive
428      * before the Tcl procedure for handling the events has been defined.
429      * If the command is not found we create a timer handler to process
430      * the event later, hopefully after the command has been created.
431      * We retry up to 2 times before giving up.
432      */
433 
434     if (!Tcl_FindCommand(AEInfo->interp, AEInfo->procedure, NULL, 0)) {
435 	if (AEInfo->retryCount < 2) {
436 	    AEInfo->retryCount++;
437 	    Tcl_CreateTimerHandler(200, ProcessAppleEvent, clientData);
438 	} else {
439 	    ckfree(clientData);
440 	}
441 	return;
442     }
443     code = Tcl_EvalEx(AEInfo->interp, Tcl_DStringValue(&AEInfo->command),
444 	    Tcl_DStringLength(&AEInfo->command), TCL_EVAL_GLOBAL);
445 
446     if (AEInfo->replyEvent && code >= 0) {
447         int reslen;
448         const char *result = Tcl_GetStringFromObj(Tcl_GetObjResult(AEInfo->interp),
449                                                   &reslen);
450         if (code == TCL_OK) {
451             AEPutParamPtr((AppleEvent*)[AEInfo->replyEvent aeDesc],
452                           keyDirectObject, typeChar, result, reslen);
453         } else {
454             AEPutParamPtr((AppleEvent*)[AEInfo->replyEvent aeDesc],
455                           keyErrorString, typeChar, result, reslen);
456             AEPutParamPtr((AppleEvent*)[AEInfo->replyEvent aeDesc],
457                           keyErrorNumber, typeSInt32, (Ptr) &code, sizeof(int));
458         }
459     } else if (code != TCL_OK) {
460 	Tcl_BackgroundException(AEInfo->interp, code);
461     }
462 
463     Tcl_DStringFree(&AEInfo->command);
464     ckfree(clientData);
465 }
466 
467 /*
468  *----------------------------------------------------------------------
469  *
470  * TkMacOSXInitAppleEvents --
471  *
472  *	Register AppleEvent handlers with the NSAppleEventManager for
473  *      this NSApplication.
474  *
475  * Results:
476  *	None.
477  *
478  * Side effects:
479  *	None.
480  *
481  *----------------------------------------------------------------------
482  */
483 
484 void
TkMacOSXInitAppleEvents(TCL_UNUSED (Tcl_Interp *))485 TkMacOSXInitAppleEvents(
486     TCL_UNUSED(Tcl_Interp *))
487 {
488     NSAppleEventManager *aeManager = [NSAppleEventManager sharedAppleEventManager];
489     static Boolean initialized = FALSE;
490 
491     if (!initialized) {
492 	initialized = TRUE;
493 
494 	[aeManager setEventHandler:NSApp
495 	    andSelector:@selector(handleQuitApplicationEvent:withReplyEvent:)
496 	    forEventClass:kCoreEventClass andEventID:kAEQuitApplication];
497 
498 	[aeManager setEventHandler:NSApp
499 	    andSelector:@selector(handleOpenApplicationEvent:withReplyEvent:)
500 	    forEventClass:kCoreEventClass andEventID:kAEOpenApplication];
501 
502 	[aeManager setEventHandler:NSApp
503 	    andSelector:@selector(handleReopenApplicationEvent:withReplyEvent:)
504 	    forEventClass:kCoreEventClass andEventID:kAEReopenApplication];
505 
506 	[aeManager setEventHandler:NSApp
507 	    andSelector:@selector(handleShowPreferencesEvent:withReplyEvent:)
508 	    forEventClass:kCoreEventClass andEventID:kAEShowPreferences];
509 
510 	[aeManager setEventHandler:NSApp
511 	    andSelector:@selector(handleOpenDocumentsEvent:withReplyEvent:)
512 	    forEventClass:kCoreEventClass andEventID:kAEOpenDocuments];
513 
514 	[aeManager setEventHandler:NSApp
515 	    andSelector:@selector(handlePrintDocumentsEvent:withReplyEvent:)
516 	    forEventClass:kCoreEventClass andEventID:kAEPrintDocuments];
517 
518 	[aeManager setEventHandler:NSApp
519 	    andSelector:@selector(handleDoScriptEvent:withReplyEvent:)
520 	    forEventClass:kAEMiscStandards andEventID:kAEDoScript];
521 
522 	[aeManager setEventHandler:NSApp
523 	    andSelector:@selector(handleURLEvent:withReplyEvent:)
524 	    forEventClass:kInternetEventClass andEventID:kAEGetURL];
525 
526     }
527 }
528 
529 /*
530  *----------------------------------------------------------------------
531  *
532  * TkMacOSXDoHLEvent --
533  *
534  *	Dispatch an AppleEvent.
535  *
536  * Results:
537  *	None.
538  *
539  * Side effects:
540  *	Depend on the AppleEvent.
541  *
542  *----------------------------------------------------------------------
543  */
544 
545 int
TkMacOSXDoHLEvent(void * theEvent)546 TkMacOSXDoHLEvent(
547     void *theEvent)
548 {
549     /* According to the NSAppleEventManager reference:
550      *   "The theReply parameter always specifies a reply Apple event, never
551      *   nil.  However, the handler should not fill out the reply if the
552      *   descriptor type for the reply event is typeNull, indicating the sender
553      *   does not want a reply."
554      * The specified way to build such a non-nil descriptor is used here.  But
555      * on OSX 10.11, the compiler nonetheless generates a warning.  I am
556      * supressing the warning here -- maybe the warnings will stop in a future
557      * compiler release.
558      */
559 #ifdef __clang__
560 #pragma clang diagnostic push
561 #pragma clang diagnostic ignored "-Wnonnull"
562 #endif
563 
564     NSAppleEventDescriptor* theReply = [NSAppleEventDescriptor nullDescriptor];
565     NSAppleEventManager *aeManager = [NSAppleEventManager sharedAppleEventManager];
566 
567     return [aeManager dispatchRawAppleEvent:(const AppleEvent*)theEvent
568 		      withRawReply: (AppleEvent *)theReply
569 		      handlerRefCon: (SRefCon)0];
570 
571 #ifdef __clang__
572 #pragma clang diagnostic pop
573 #endif
574 }
575 
576 /*
577  *----------------------------------------------------------------------
578  *
579  * ReallyKillMe --
580  *
581  *	This procedure tries to kill the shell by running exit, called from
582  *      an event scheduled by the "Quit" AppleEvent handler.
583  *
584  * Results:
585  *	Runs the "exit" command which might kill the shell.
586  *
587  * Side effects:
588  *	None.
589  *
590  *----------------------------------------------------------------------
591  */
592 
593 static int
ReallyKillMe(Tcl_Event * eventPtr,TCL_UNUSED (int))594 ReallyKillMe(
595     Tcl_Event *eventPtr,
596     TCL_UNUSED(int))
597 {
598     Tcl_Interp *interp = ((KillEvent *) eventPtr)->interp;
599     int quit = Tcl_FindCommand(interp, "::tk::mac::Quit", NULL, 0)!=NULL;
600 
601     if (!quit) {
602 	Tcl_Exit(0);
603     }
604 
605     int code = Tcl_EvalEx(interp, "::tk::mac::Quit", -1, TCL_EVAL_GLOBAL);
606     if (code != TCL_OK) {
607 
608 	/*
609 	 * Should be never reached...
610 	 */
611 
612 	Tcl_BackgroundException(interp, code);
613     }
614     return 1;
615 }
616 
617 /*
618  *----------------------------------------------------------------------
619  *
620  * MissedAnyParameters --
621  *
622  *	Checks to see if parameters are still left in the event.
623  *
624  * Results:
625  *	True or false.
626  *
627  * Side effects:
628  *	None.
629  *
630  *----------------------------------------------------------------------
631  */
632 
633 static int
MissedAnyParameters(const AppleEvent * theEvent)634 MissedAnyParameters(
635     const AppleEvent *theEvent)
636 {
637    DescType returnedType;
638    Size actualSize;
639    OSStatus err;
640 
641    err = AEGetAttributePtr(theEvent, keyMissedKeywordAttr,
642 	    typeWildCard, &returnedType, NULL, 0, &actualSize);
643 
644    return (err != errAEDescNotFound);
645 }
646 
647 /*
648  * Local Variables:
649  * mode: objc
650  * c-basic-offset: 4
651  * fill-column: 79
652  * coding: utf-8
653  * End:
654  */
655