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