1/* -*- mode: C; coding: macintosh; -*-
2 *
3 *	AppleEvent Printer Function
4 *	by Jens Peter Alfke
5 *
6 *	Copyright �1991-1993 Apple Computer, Inc. All rights reserved.
7 *
8 *	APPLE CONFIDENTIAL
9 */
10
11
12/*
13	CHANGE HISTORY:
14	 ...zillions of changes from 2/91 until...
15	 7/08/93	jpa		Allow for bufStr to be NULL, and return length, to support preflighting.
16	 					Don't truncate hex dumps.
17	 7/09/93	jpa		No, break length-counting into separate function (AEPrintLength).
18	 9/29/93	jpa		Remove calls to vsprintf (cripping floating-point display, oh well...)
19	10/11/93	jpa		Display floats by coercing from 'sing' -> 'TEXT'.
20	 4/14/94	jpa		Fix warnings for Metrowerks.
21	 9/08/94	jpa		Added bufputOSType to fix quoting problems when re-parsing output.
22	10/03/94	jpa		Fixed heinous bug in printDescList that was always changing type to
23						'aevt' on the way out.
24	 3/20/95	jpa		**AEGizmos 1.4
25*/
26
27
28#include <ctype.h>
29#include <stdarg.h>
30#ifndef __CARBON__
31#include <Errors.h>
32#include <Packages.h>
33#include <NumberFormatting.h>
34#include <AppleEvents.h>
35#include <CFString.h>
36#endif
37#include "AEPrintCarbon.h"
38
39typedef struct {
40	char *str;
41	long len;
42} Buf;
43
44
45static OSErr printDesc( Buf *buf, const register AEDesc *desc );
46
47
48#ifdef THINK_C
49#if !__option(macsbug_names)
50	// Always turn on macsbug names for the non-static functions.
51	#define NO_NAMES
52	#pragma options(macsbug_names)
53#endif
54#endif
55
56
57/* AE_PRINT  The big kahuna. Print any AEDesc into bufStr, up to bufLen chars */
58OSErr
59AEPrint( const AEDesc *desc, char *bufStr, long bufSize )
60{
61	Buf buf;
62
63	if( !desc || !bufStr )
64		return paramErr;
65	buf.str = bufStr;
66	buf.len = bufSize -1;
67	bufStr[0] = '\0';
68
69	return printDesc(&buf,desc);
70}
71
72
73/* AE_PRINT_SIZE  Compute the size of string (incl. null byte) AEPrint will build. */
74OSErr
75AEPrintSize( const AEDesc *desc, long *stringLength )
76{
77	Buf buf;
78	OSErr err;
79
80	if( !desc || !stringLength )
81		return paramErr;
82	buf.str = NULL;
83	buf.len = 0x7FFFFFF0;
84
85	err= printDesc(&buf,desc);
86
87	*stringLength = 0x7FFFFFF0 +1 - buf.len;		// Set to size of returned string (length+1)
88	return err;
89}
90
91
92#ifdef NO_NAMES
93	#undef NO_NAMES
94	#pragma options(!macsbug_names)
95#endif
96
97
98/*******************************  THE UTILITY BELT  ************************************/
99
100
101/* BUF_PUT  Copy data to the buffer */
102static void
103bufput( register Buf *buf, void *data, register long len )
104{
105	if( buf->len > 0 ) {
106		if( buf->len < len )
107			len = buf->len;
108		if( buf->str ) {						// Only write data if we have a place for it
109                        if ( len > 0 ) {
110                            memmove( buf->str, data, len );
111                        }
112			buf->str += len;
113			buf->str[0] = '\0';
114		}
115		buf->len -= len;
116	}
117}
118
119
120/* BUF_PUT_C  Copy a single char to the buffer */
121static void
122bufputc( register Buf *buf, char c )
123{
124	if( buf->len > 0 ) {
125		buf->len--;
126		if( buf->str ) {
127			*(buf->str)++ = c;
128			buf->str[0] = '\0';
129		}
130	}
131}
132
133
134/* BUF_PUT_S  Write a C string to the buffer */
135static void
136bufputs( Buf *buf, register char *str )
137{
138	register long len = buf->len;
139	register char *dst= buf->str;
140
141	if( buf->str ) {
142		for(; *str && len>0; len-- )
143			*dst++ = *str++;
144		*dst = '\0';
145		buf->str = dst;
146	} else
147		for(; *str && len>0; len-- )			// No buffer, just count length of str
148			str++;
149	buf->len = len;
150}
151
152
153/* BUF_PUT_OSTYPE  Write a 4-character code to the buffer */
154static void
155bufputOSType( Buf *buf, OSType type )
156{
157	// If type contains any nonalphabetic chars, quote it.
158	// $$$$$ This still won't handle embedded single quotes.
159	char string[5];
160	short i;
161	unsigned char c;
162	Boolean space=false, alpha=false;
163
164	/* Convert OSType to UTF */
165	string[0] = (char) (type >> 24);
166	string[1] = (char) (type >> 16);
167	string[2] = (char) (type >>  8);
168	string[3] = (char) (type);
169	string[4] = '\0';
170
171	for( i=0; i<4; i++ )
172		if( isalpha( c=string[i] ) ) {
173			if( space )
174				break;						// alpha after a space is bad
175			alpha = true;
176		} else if( c==' ' && alpha )
177			space = true;					// space is ok after alpha chars
178		else
179			break;
180
181	if( i<4 )
182		bufputc(buf,'\'');
183	bufput(buf,string,sizeof(type));
184	if( i<4 )
185		bufputc(buf,'\'');
186}
187
188
189/* BUF_PUT_FLOAT  Write a floating-point number to a buffer */
190static OSErr
191bufputfloat( Buf *buf, float f )
192{
193	AEDesc desc, textDesc;
194	OSErr err;
195
196	err= AECreateDesc(typeIEEE32BitFloatingPoint,&f,sizeof(f), &desc);
197	if( err ) return err;
198	err= AECoerceDesc(&desc,'TEXT',&textDesc);
199	AEDisposeDesc(&desc);
200	if( !err ) {
201	#if TARGET_API_MAC_CARBON
202		long len = AEGetDescDataSize(&textDesc);
203		char *theData = ckalloc(len);
204		AEGetDescData(&textDesc, theData, len);
205		bufput(buf, theData, len);
206		ckfree(theData);
207	#else
208		bufput(buf, *textDesc.dataHandle,GetHandleSize(textDesc.dataHandle));
209	#endif
210		AEDisposeDesc(&textDesc);
211	}
212	return err;
213}
214
215
216/***************************  SUPPORT FOR PRINTING EVENTS  ********************************/
217
218
219static OSErr
220getOptionalParams( const AppleEvent *event, AEKeyword** *keys )
221{
222	OSErr err;
223	AEDescList optionals;
224	long n, realSize;
225	AEKeyword key, optionalKey;
226	DescType type;
227
228	*keys = NULL;
229	err= AEGetAttributeDesc( event, keyOptionalKeywordAttr,typeAEList, &optionals );
230	if( err ) {
231		if( err==errAEDescNotFound )
232			return noErr;
233		else
234			return err;
235	}
236
237	err= AECountItems(&optionals, &n);
238	if( err ) goto exit;
239	if( n<=0 )
240		goto exit;
241	*keys = (void*) NewHandle(n*sizeof(AEKeyword));
242	if( (err= MemError()) != noErr ) goto exit;
243
244	for( ; n>0; n-- ) {
245		err= AEGetNthPtr(&optionals,n,typeKeyword, &key,&type,
246						 (Ptr)&optionalKey,sizeof(optionalKey),&realSize);
247		if( err ) goto exit;
248		(**keys)[n-1] = optionalKey;
249	}
250
251exit:
252	AEDisposeDesc(&optionals);
253	if( err && *keys ) {
254		DisposeHandle((Handle)*keys);
255		*keys = NULL;
256	}
257	return err;
258}
259
260/*******************************  THE MAIN ROUTINES  ***********************************/
261
262
263/* PRINT_DESC_LIST  Print a descriptor list -- AEDescList, AERecord or AppleEvent */
264static OSErr
265printDescList( Buf *buf,  AEDescList *desc, DescType originalType )
266{
267	DescType type = desc->descriptorType;
268	long i, size;
269	long itemNo = 0;
270	AEKeyword keyword;
271	AEDesc tempDesc;
272	short meta=false, doMeta=false;
273	AEKeyword **optionals = NULL;
274	OSErr err;
275
276	if( type=='aevt' ) {
277		AEEventClass eventClass;
278		AEEventID eventID;
279		DescType realType;
280		long realSize;
281
282		err= AEGetAttributePtr(	desc, keyEventClassAttr,typeType, &realType,
283								(Ptr)&eventClass, sizeof(eventClass), &realSize );
284		if( err ) goto exit;
285		bufputOSType(buf,eventClass);
286
287		err= AEGetAttributePtr(	desc, keyEventIDAttr,typeType, &realType,
288								(Ptr)&eventID, sizeof(eventID), &realSize );
289		if( err ) goto exit;
290		bufputc(buf, '\\');
291		bufputOSType(buf,eventID);
292
293		err= getOptionalParams(desc, &optionals);
294		if( err ) goto exit;
295
296		doMeta = true;
297
298		bufputc(buf, '{');
299
300	} else if( type=='list' )
301		bufputc(buf, '[');								/* Open-bracket for list */
302
303	else {
304		if( originalType != 'reco' )
305			bufputOSType(buf, originalType);			/* Show type for coerced record */
306		bufputc(buf, '{');								/* Open-brace for record */
307	}
308
309	// Now get all the items and print them.
310	// If this is an Apple event, we will go around twice: one to print the parameters,
311	// and again to print the attributes.
312
313		err= AECountItems(desc, &size);
314		if( err ) goto exit;
315
316		for( i=1; i<=size; i++ ) {							/* Loop through items: */
317			err= AEGetNthDesc(desc,i, typeWildCard, &keyword,&tempDesc);
318			if( err )
319				goto exit;
320
321			if( meta && keyword=='optk' )						// Skip optional-params attribute
322				continue;
323
324			if( itemNo++ >0 )
325				bufputs(buf, ", ");
326			if( type!='list' ) {
327				bufputOSType(buf,keyword);						/* If we're in a record, show key */
328				bufputc(buf,':');
329			}
330			err= printDesc(buf,&tempDesc);						/* Recursively print item */
331			AEDisposeDesc(&tempDesc);
332			if( err ) goto exit;
333		}
334
335	    if (doMeta) {
336		AEKeyword	attribute[] = {keyOptionalKeywordAttr /* JEG - 13 Nov 2004 - Bug 1217 */,
337						keyTransactionIDAttr,  keyReturnIDAttr,
338						keyAddressAttr, keyTimeoutAttr, keyInteractLevelAttr,
339						keyEventSourceAttr, keyMissedKeywordAttr, keyOriginalAddressAttr,
340						keyAcceptTimeoutAttr,
341#if TARGET_API_MAC_CARBON
342						keyUserNameAttr, keyUserPasswordAttr, keyDisableAuthenticationAttr,
343						keyXMLDebuggingAttr, kAEUseHTTPProxyAttr, kAEHTTPProxyPortAttr, kAEHTTPProxyHostAttr,
344						kAEUseSocksAttr, kAESocksProxyAttr, kAESocksHostAttr, kAESocksPortAttr, kAESocksUserAttr, kAESocksPasswordAttr,
345#endif
346						0L};
347
348		for( i=0; attribute[i]; i++ ) {							/* Loop through attributes: */
349			err = AEGetAttributeDesc(desc, attribute[i], typeWildCard, &tempDesc);
350			if (err == errAEDescNotFound) {
351				err = noErr;
352				continue;
353			}
354
355			if(err != noErr)
356				goto exit;
357
358//			if (attribute[i] == keyTransactionIDAttr &&
359
360			if( itemNo++ >0 )
361				bufputs(buf, ", ");
362
363			bufputc(buf,'&');								// Prefix for metaparam (attribute)
364			bufputOSType(buf, attribute[i]);						/* show key */
365			bufputc(buf,':');
366
367			err= printDesc(buf,&tempDesc);						/* Recursively print item */
368			AEDisposeDesc(&tempDesc);
369			if( err ) goto exit;
370		}
371	    }
372
373	if( type=='list' )					/* Close list or record */
374		bufputc(buf, ']');
375	else
376		bufputc(buf, '}');
377
378exit:
379	if( optionals )
380		DisposeHandle((Handle)optionals);
381	return err;
382}
383
384
385/* HEX_DUMP_DESC  Official punting routine. Produce hex dump of descriptor */
386static OSErr
387hexDumpDesc( Buf *buf, const AEDesc *desc )
388{
389	unsigned char *data;
390	long count,n;
391	unsigned char byte;
392	char hex[] = "0123456789ABCDEF";
393	#if TARGET_API_MAC_CARBON
394	char *theData;
395	#endif
396
397	bufputOSType(buf, desc->descriptorType);
398	bufputs(buf, "(�");
399
400	#if TARGET_API_MAC_CARBON
401	count = AEGetDescDataSize(desc);
402	theData = ckalloc(count);
403	data = (void*)theData;
404	AEGetDescData(desc, theData, count);
405	#else
406	data = (void*)*desc->dataHandle;
407	count = GetHandleSize(desc->dataHandle);
408	#endif
409
410
411	n = count;
412#ifdef _MAX_HEX_DUMP_LENGTH_
413	if( n>_MAX_HEX_DUMP_LENGTH_ )
414		n = _MAX_HEX_DUMP_LENGTH_;
415#endif
416	if( n*2>buf->len )
417		n = buf->len>>1;						// No sense going past end of buffer
418
419	while( n-- >0 ) {
420		byte = *data++;
421		bufputc(buf,hex[byte>>4]);
422		bufputc(buf,hex[byte&0x0F]);
423	}
424#ifdef _MAX_HEX_DUMP_LENGTH_
425	if( count>_MAX_HEX_DUMP_LENGTH_ )
426		bufputc(buf,'�');
427#endif
428	bufputs(buf,"�)");
429	#if TARGET_API_MAC_CARBON
430	ckfree(theData);
431	#endif
432	return noErr;
433}
434
435
436/*******************************  THE BIG KAHUNA  ************************************/
437
438
439/* PRINT_DESC  The big kahuna. Print any AEDesc */
440static OSErr
441printDesc( Buf *buf, const register AEDesc *desc )
442{
443	OSErr err = noErr;
444	AEDesc tempDesc;
445
446	#if TARGET_API_MAC_CARBON
447	if( AEGetDescDataSize(desc)==0 ) {
448	#else
449	if( desc->dataHandle==NULL || GetHandleSize(desc->dataHandle)==0 ) {
450	#endif
451		bufputc(buf,'\'');
452		bufputOSType(buf, desc->descriptorType);	/* No data */
453		bufputs(buf,"'()");
454		return noErr;
455	}
456
457	switch( desc->descriptorType ) {
458		case 'bool':								/* Integer types: */
459		case 'shor':
460		case 'long':
461			err= AECoerceDesc(desc,'long',&tempDesc);		/* Coerce to longint */
462			if( !err ) {
463                            CFStringRef theString;
464                            CFIndex 	len;
465                            UInt8	buffer[12];
466
467                            theString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), **(long**)tempDesc.dataHandle);
468                            len = CFStringGetLength(theString);
469                            CFStringGetBytes(theString, CFRangeMake(0, len), kCFStringEncodingMacRoman, 0, false, buffer, sizeof(buffer), NULL);
470                            bufput(buf,buffer,len);
471                            CFRelease(theString);
472			} else if( err==errAECoercionFail )
473				err= hexDumpDesc(buf,desc);
474			AEDisposeDesc(&tempDesc);
475			break;
476
477		case 'sing':								/* Floating-point types: */
478			bufputfloat(buf, **(float**)desc->dataHandle);
479			break;
480		case 'doub':
481			bufputfloat(buf, **(double**)desc->dataHandle);
482			break;
483		case 'exte':
484			bufputfloat(buf, **(long double**)desc->dataHandle);
485			break;
486
487		case 'enum':								/* 4-letter code: */
488			bufputOSType(buf, **(OSType**)desc->dataHandle);
489			break;
490
491		case 'type':								/* 4-letter code as 'type': */
492			bufputs(buf,"type(");
493			bufputOSType(buf, **(OSType**)desc->dataHandle);
494			bufputs(buf,")");
495			break;
496
497		case 'TEXT': {								/* Text string: */
498			long		len;
499			char *		theData;
500			int		i;
501
502		#if TARGET_API_MAC_CARBON
503			len = AEGetDescDataSize(desc);
504			theData = ckalloc(len);
505			AEGetDescData(desc, theData, len);
506		#else
507			len = GetHandleSize(desc->dataHandle);
508			HLock(desc->dataHandle);
509			theData = *desc->dataHandle;
510		#endif
511			for (i = len - 1; i >= 0; i--) {
512			    if (!isprint(theData[i]) || theData[i] == '�' || theData[i] == '�') {
513				break;
514			    }
515			}
516																							if (i < 0) {
517			    bufputc(buf,'�');
518			    bufput(buf, theData, len);
519			    bufputc(buf,'�');
520			} else {
521			    hexDumpDesc(buf, desc);
522			}
523
524		#if TARGET_API_MAC_CARBON
525			ckfree(theData);
526		#else
527			HUnlock(desc->dataHandle);
528		#endif
529			}
530			break;
531
532		case 'aevt':								/* Apple event! */
533			err= AECoerceDesc(desc,'aevt',&tempDesc);
534			if( err==noErr ) {
535			err= printDescList(buf,&tempDesc,'aevt');
536			} else if( err==errAECoercionFail )
537				err= hexDumpDesc(buf,desc);
538			AEDisposeDesc(&tempDesc);
539			break;
540
541		case 'list':								/* AEDescList: */
542			err= AECoerceDesc(desc,'list',&tempDesc);
543			if( err==noErr ) {
544			err= printDescList(buf,&tempDesc,'list');
545			} else if( err==errAECoercionFail )
546				err= hexDumpDesc(buf,desc);
547			AEDisposeDesc(&tempDesc);
548			break;
549
550		default:									/* AERecord, and everything else: */
551			if( desc->descriptorType=='reco' )
552				tempDesc = *desc;
553			else
554				err= AECoerceDesc(desc,'reco',&tempDesc);
555
556			if( err==noErr ) {
557				err= printDescList(buf,&tempDesc,		/* Made it a record, print it */
558									desc->descriptorType);
559				if( desc->descriptorType != 'reco' )
560					AEDisposeDesc(&tempDesc);
561			} else if( err==errAECoercionFail )			/* Couldn't make it a record */
562				err= hexDumpDesc(buf,desc);
563			break;
564	}
565	return err;
566}
567