BOBO dHHHHHH H T    Z- cwk).d.cwk8 8cU}alysis.cwkpp ӨMortgage Analysisoto.cwk).d.cwk8 8cU}` wkis.cwkcwk9.cwk8 8`U}AppleCSP.doc.cwkkis.cwkcwk9.cwk8 8cU} C++ Utilitiescwkkis.cwkcwk9.cwk8 8cU}CertJaguarBrainstorm_Oct10.cwkk8 8cU}CertJaguarBrainstor XcU}J@ ӨX_AppleCSP.doc.cwk 8 8 8 8cU}._C++ Utilitiescwk 8 8 8 8cU}._CertJaguarBrainstorm#C0BB.cwk8 8cU}/ ._CrtJaguarBrainstorming7.cwkk8 8cU}._CSP Algorithms 0.6 (#C0B9.cwk8 8cU}._it.cwkgorithms 0.6 (#C0B9.cwk8 8cU}._Jaguar_submissions.cwk0B9.cwk8 8 Perry The CynicjFLOM!`xxHHLYg(HH(d'h  q`l>/jY;V 7 "H/H/VXYjĐ|8   awDSETyyy=Y     lU,+g  -f*U$& t(V/u3 .? pBo /wNd X T.U\1u a8UX0f m9 # x'V|*/P TVl0b HU`0z0Vt1 4-_(LU%(; -S(V ( = R  ` O4qwsn!!"#$&&''(((2() ))9);)F)^)q)s*=+,I,b-.=./0 0011222L334 4<5667778'8i899:=:I;#<="=/=V=`?CAmBnBoBCCDEEOEEF}FFFH_HHHHI IZIJhJK+KJKLLM(MNNO_OPPQ9QzQR$RgRpT-T.TJVVWYBYYZ [[]r]t]]^%^)^\^^^^_` `1`k``abbcceffgh#h%h*hDhFhiiijjYjjjkkAkCkm9mTnoopsntv8w^x'x4y{{|~)~U^ 9Si$3T]hus 61Z\.;=XHi G:01@!9 #@}/JL1!O~0:;H7Hyb#hz/W#=V6ȏIV=                                      "  D L  R ^  5 <  a j         $ ( K ` ~        * [ c s z           % 6 : < M O m          4 A X \ ^ o q                ' s              - r ~    U _             %   $ 0 c o        < F H Y _ i     =  L         !  !) ! ! " # $Z $b $ $ %) %* & &  &- &8  '  '  ' '  '@ 'F  'e 'k  ' '  ((2 (6 (A ( ). )7 )g )o )s *( */ *A *I *V *] +I +T +y + + + + + + + , , ,F ,G ,I,b ,f ,q .A .M . . / / /_ /k / / / 0 0  00 0 0 1 1 1 2 2L 3 4< 5 6 6 6 6 7Y 7d 7 7 8 8 8 9x 9 9 9 : : := :I :\ :g :m :x : : : ;  ;2 ;< < < = = =" =/ =V=` >  > >C >Q ? ? ?E ?V ? ? Am A~ BoB C C C C D D E E/ EN Ex E E E F  Fc Fq F} F F F F F G8 GB G G H_ H H H I  I I I8 IE IZ I_ Ih Ir I I J J! Jh Jp J J J J K+ KJ KL KY Ks K{ K K K K K K K L L# LI LN LT L~ L L L L M M  M M% M( MB ML MR M] M M NB NJ NR NZ N N N N N N O_ O O O O O O O P P P2 PN P P P P P P P Q Q9 QG QM Q Q R$ R( Rg Rp S S S S T-TJ T T T T T T Uc Uk V V V V V V V V V W W( W+ X X  X X XE XM Y Z  Z( Z Z [ [ \  \ \* \2 \d \q \ \ ]! ]( ]n ]o ]r ^ ^ ^ ^ ^ ^ ^ _ _ _ _ _ _ _ _ `  ` ` ` ` ` a8 aD a a bR bZ b_ bh b b b b b b c c) cM cY c c c c d- d4 dt dw d d d d eA eO e e e e e e f f& f f f f f f f f f f g g$ gi gv g h% hD hF h h h h i2 iA ii il i i i jY j kC k l lE lS lq l| l l l l m m5 m9mT mX mb m m n n n n n n o o o o o o p p p p" p: pk p p r r r r s s s s t t" t; tB t t t t u u u u vk vq v v wh ww wy w w w x'x4 y y y y y z z z { { { { | | | | }e }h } } } } ~% ~& ~) ~- ~ * . W d f j        @ D b h         ! ) + 6      $ 3 q u   ] f ~    h u          / B N Z s          / 9 ; E K ] x | ~         6  Z j x = K ^ a r ~  " . 2 ~     , T U b i                     ~     *     G I }     3 5 q u   :       0@ D O  ! % +         A M        U Z     " .       T _        ! # @ F               J L   1 7 i o     2 8         {       ! ~           * 7            ;H  ) 5  7 H  L P     # .           ( 0        B L  _ e    #  , /     ^ _     Z f     d e  h z           ? S        $ )     2 5         F L w {     # = G  g t  b r     × á  ĭ Ĵ  Ļ ļ     | ņ  3 4  6  ȏ    <2$b&%)&' ,F G  N  ]n  i  v  { R| `~% z T  zn^ndĻz3b  / $0 /Z  '   ,"M o #N '> ) ,V /V 3 6 : =/' =VY d?m C F Jq LLd O Sg W ZS ]kz ` di g k) n ,qY Pu* `y& p|  x     D D D E &     ( , 0 4 8 @ĵ Pƶ XC++ Utility Functions (Does it Really Have to be That Hard?) If you write C++ code within the CDSA environment, there is a large number of utility classes that can make your life less miserable and more productive. These utilities are yours to use no matter which part of CDSA you live in - plugin module, CSSM, MDS, application code, layered services - though of course their usefulness varies. In addition to providing often-needed utility functionality, these classes fill two other functions. First, they provide C++ bindings to the C based data types used in the public CDSA interfaces. Second, they provide platform abstractions for things that are implemented differently on varying platforms. That used to mean OS 9 versus OS X, though the OS 9 variants are largely defunct now Note that these utilities are not public to the customer, they are internal helpers. Their existence should be kept from the callers of the official APIs. After all, they cant use them anyway, so why frustrate them with views of greener pastures? If you are wondering how this particular document is organized, stop it now. You are reading a grab-bag of randomly ordered items. If you can think of a nice ordering, let me know. Use of Exceptions All utility functions signal error conditions by throwing exceptions. There are no return values that indicate errors. In particular, functions returning objects generally return references or pointers that you do not have to check for NULL-ness. The above does not apply to results that are not errors/. In particular, find and lookup functions typically come in two varieties: ones that consider not found an error and throw, and those that consider it okay and return NULL or false. Check it before you call it; anything returning references definitely considers not found an error. Pointer-returning functions can swing either way. You may catch specific exceptions if you wish to handle them in a particular context. You should not catch all exceptions and turn them into some other error reporting scheme unless you are at the transition layer out of C++. Even there, the API wrapper macros described below will generally take care of this better and more flexibly than you could. Improvements These utility functions have been built to satisfy an immediate need. They are thus quite incomplete - anything we havent needed yet isnt there. If you find that there is some utility functionality that sure would make your life easier, please ask Perry - the worst he can say is sorry. It is important that common utility functions become part of the utility libraries; we dont want multiple different ways of doing the same thing in different insight components. Thank you. POD Wrappers Many CSSM struct objects are created in one spot, then passed around as pointers between users, CSSM, and plugins. These are C objects whose data layout follows C language rules. C++ folks often call them PODs, for Plain Old Data, since they have no runtime overhead, no methods, and no access restrictions. It is possible and useful to subclass POD types as C++ classes. This does not rZestrict their former use as C objects, but it can add methods for easier construction, use, and management of their contents. The utility libraries provide definitions of POD Wrappers for most CSSM defined structure types. Many are little more than restatements of the underlying layout with C++ style access functions, but some (like e.g. Context) add major new functionality that thus becomes intrinsically available to their CSSM ancestor. Lets call the C structure the base type (e.g. CSSM_DATA) and the C++ wrapper class (e.g. CssmData) the wrapper type. You can create objects of wrapper type normally, both on the stack or heap. These objects have identical memory layout and footprint and can be implicitly used as their base types whenever desired. Alternately, a pointer to a base type can be turned into a reference to its wrapper type using the required method: WrapperType::required(pointer_to_base_type) This will throw an exception if the pointer value is NULL. If the value is optional, and thus NULL is a valid value, you can use the WrapperType::optional function instead. Of course, optional returns a pointer, not a reference. All these functions handle const intelligently. CSSM Contexts The Context class is an extended POD Wrapper for the CSSM_CONTEXT type. In addition to the usual support (such as required methods etc.), Context provides support for reading and building Context objects. The class Context::Attr is a POD Wrapper for a single CSSM_CONTEXT_ATTRIBUTE element within a Context. You can get an attribute slot from a context by simple indexing, and get and set values of various types safely. Retrieving Context Attributes Contexts have a set of template access methods that retrieve context attribute values by type. contextObject.get(CSSM_ATTRIBUTE_type, errorIfNotPresent) locates the attribute of type CSSM_ATTRIBUTE_type in contextObject and retrieves its value as a DataType reference. If that attribute is not present, error code errorIfNotPresent is thrown. If you specify the wrong data type, an internal error (assertion) will be generated. If you omit the error code argument, the method will return a pointer to DataType instead and return NULL if not found, rather than throw an exception. For integer (uint32) valued attributes, the corresponding contextObject.getInt(CSSM_ATTRIBUTE_type, errorIfNotPresent) is used, with zero acting as a not-found indicator as needed. Building Contexts The Context::Builder class provides a simple way to construct dynamically allocated Context objects efficiently. Rather than allocate numerous memory blocks and construct CSSM data structures from them, Context::Builder uses a two-pass scheme to place everything into a single memory block, based on Walkers (described below). This makes destruction of Contexts simple, and also allows for atomic modifications without excessive hassles. Please note that CSSM proper uses a subclass of Context, CssmContext, that has additional semantics specific to CSSM, as well as a specialized builder class (CssmContext::Maker). Use of these classes is probably not appropriate outside of CSSM itself. HandleObjects CSSM uses handles as opaque references to objects created on behalf of the user. These handles are all defined as type CSSM_HANDLE. The HandledObject mix-in class gives any class a handle; the handle may be specified on construction or set (slightly) later with the setHande call. Once set, the handle cannot be modified. It can however be removed with a call to clearHandle. You can check the validity status of a HandledObject (it is considered valid if its handle was set and not cleared). The HandleObject class goes one important step farther. It automatically generates a unique handle upon construction, based on the objects memory address . The resulting handle value is unique within an address space. There is a findHandle function that, given a HandleObject handle, will locate its object and return a reference to it. An implementation of HandleObject can also override virtual lock and tryLock methods. The defaults do nothing, but an object with a suitable object lock may implement lock/tryLock to apply to it. A variant of findHandle, findHandleAndLock, atomically locates the object and acquires its lock before returning the object. Finally a third variant, killHanMdle, locates the object for a handle, acquires its lock, destroys its handle, and returns the locked object (whose handle is now invalid), all in one atomic swoop. Please note that HandleObject does not guarantee persistent uniqueness of its handles. Once a HandleObject is destroyed, it is possible that a newly created HandleObject will reuse the same handle value . The only guarantee is that no two HandleObjects with identical handles can exist in the same address space (process) at the same time. Together, these three functions (findHandle, findHandleAndLock, and killHandle) provide a thread-safe means to manage object handles. Exception Classes There is a set of exception classes based on CssmCommonError, which in turn is derived from the standard C++ library exception class. All CssmCommonError objects have virtual cssmError and osStatus methods, returning CSSM error codes and MacOS OSStatus codes, respectively , for the errors they represent. CssmCommonError is not meant to be used directly. Throw a CssmError if you want to report an error that is directly originated within the CSSM framework. The argument is a CSSM error code. You may either use a convertible error number, or a specific error code. Throw a UnixError if you want to report that a UNIX-style system call or library function failed, and you cannot easily map the failure to a CSSM error code. The argument is a UNIX errno value; it defaults to the current value of the errno global variable. Throw a MacOSError if you want to report that a MacOS library or system function returned an error, and you cannot easily map the failure to a CSSM error code. The argument is a MacOS OSStatus value. You should understand that the API and SPI transition layNer code catches all exceptions, and transforms all instances of CssmCommonError into a CSSM error code or OSStatus value as best as it can. Throw the most appropriate error class and let the machinery sort it out. It works pretty well by now . It is legal to throw another type of exception object from your code . Do understand that such exceptions are generally mapped to internal error at the transition layers, so dont do this unless you have no clue as to what is happening - which of course does mean internal error. Error Message Strings For convenience, the cssmerrno.h header defines two functions that can translate CSSM error numbers and codes into readable strings. This is here for simple diagnostic output only. Dont show it to the user; he wont understand what it all means . The cssmPerror function acts like the standard perror function and writes a line to stderr. It is available to both C and C++ code. The cssmErrorString function returns the error string for a CSSM error as a C++ string object. You cannot call this function from C. API/SPI Barrier Macros The utilities.h header contains a set of C++ macros that can be used to perform the transition from C++ code to public C interfaces. Essentially, you can bracket your C++ code like this: #include ... CSSM_RETURN myPublicFunction(arguments) { BEGIN_API // your C++ code here END_API(facility) } These macros will automatically catch all exception in your C++ code and translate them into CSSM error codes that are returned from your function. If your code completes normally, CSSM_OK is returned. The facility argument to END_API is the abbreviated code of the CSSM subsystem being modeled: AC, CL, CSP, DL, or TP. This is used to translate generic (convertible) error codes to subsystem specific versions on exit. If your function returns something other than a CSSM_RETURN error indicator, you should replace END_API with END_API0 (for void returns) or END_API1(error_return_value) for other types. Note that END_API1 will not generate a normal return for you; you must ensure that a return statement is executed. Dynamic Loader Interface The modloader.h header provides an abstract interface for the loading of plugin modules, independent of OS 9/OS X features. This is currently using CFBundles on X and CFM calls on 9. Please note that this is not a generalized interface to the platform loader facilities. It implements a specific capability to load and manage plugin modules. It will be extended as needed to provide other interfaces to the platform loader, but it will never be general. That is not its job. The ModuleLoader class represents the system facilities that load and manage loadable modules. There is generally only one instance per CSSM instance. The ModuleLoader::loadModule method can procure a LoadedModule object that represents a loaded plugin module. Note that you cant load a LoadedModule object - the fact that it was successfully constructed indicates that the corresponding plugin is loaded. To unload a module, delete its LoadedModule object. There is also support for built-in plugin modules. At the operational level, builtin plugins work just like the ones that really got loaded; theres just no actual loading going on. Globalizer The globalizer.h header provides a facility for creating global data on a selectable scale. As a general rule, you define some object type containing the data you wish to globalize, and then declare a nexus object for that type to make a global instance of the object: class MyGlobal { ... } SomeNexus myGlobal; ... myGlobal().method(); ... somevar = myGlobal().someField; Note the use of function-call notation on the nexus object. Calling the nexus object yields a (writable) reference to the underlying object of your chosen type. The nexus object takes care of creating the object lazily when first needed, of accessing it thread-safely, and of disposing of it as appropriate. If you make extensive use of your global object in some section of code, you should use a reference object: MyGlobal &global = myGlobal(); global.method(); ... somevar = global.someField; It is legal to reference another nexus from within the constructor of a nexuss base object. This will lead to lazy creation of a web of nexi. You may however not refer to a nexus from within the constructor of its own base object class, since this will lead to deadlock. In particular, you cannot invoke the nexus from within the objects constructor, even indirectly. Warning: t is your responsibility to resolve any multi-threaded accesses to your global object. The nexus facility guarantees that your object is created and retrieved safely, but makes no attempt to apply locking to your objects fields or methods. ModuleNexus A ModuleNexus implements globality as defined by the linkers view of the world. It is the most efficiently implemented form, and the most frequently used one. Each ModuleNexus object defines a scope of globality, according to these rules: Location of ModuleNexus Scope of globality A main program The entire process A framework One process linking against the framework A plugin Each process that loads the plugin, until it is unloaded An object module or static library (.a) Each linker unit (as above) that links the object module By default, a ModuleNexus does not destroy its underlying object when it is destroyed. This reflects its prevailing use for objects that have process lifetime. If you want a ModuleNexus to destroy its underlying object, you may construct it with a boolean argument ModuleNexus myNexus(true); or call its destroyMeOnDeath() method early on during initialization. ThreadNexus You can think of a ThreadNexus as a ModuleNexus that maintains separate objects for each participating thread. In a single-threaded program, a ThreadNexus operates identically to a ModuleNexus (but is less efficient). Do not overuse ThreadNexi. Their proper use is for libraries that have no control over their clients creation and maintainance of threads, but who need to keep per-thread data for reasons of API compliance or caching efficiency. If your code makes and controls its own threads, you are much better off definiting and maintaining your own per-thread data structures explicitly. If a thread is destroyed, all ThreadNexus based objects for that thread are destroyed if the nexus was created with a true argument. ProcessNexus 'ProcessNexus is defunct. Dont use it. YCallbacks CSSM uses application callback functions in several places, paired with context values that are being saved and passed back to the callback functions when invoked. The ModuleCallback class encapsulates that feature. Create a ModuleCallback object from a function pointer and context value. ModuleCallbacks can be compared for equality (of both function and context). You may invoke it at any time to initiate a callback. Calling a ModuleCallback object is a synchronous operation. A ModuleCallbackSet implements a set of ModuleCallbacks. You can add and remove callbacks from it at any timme, and inquire about the contents. You can invoke a ModuleCallbackSet just like a ModuleCallback, but the behavior is different: all callbacks currently contained in the set are invoked in some unspecified order, and the callbacks are performed in a separate thread if threading is available. That means that (unless no threading is supported on the platform) the invocation returns immediately, and the actual callback invocations happen later. ModuleCallbackSet performs appropriate locking to ensure that you cannot remove a callback that is scheduled to be executed but has not yet completed. Any attempt to remove such a callback throws an exception. There are no restrictions on adding callbacks. Allocator Support Within our code, it is often necessary to procure memory from specific places. For example, CSSM and its plugins need to allocate memory from application-specified allocators; security-sensitive modules need to allocate unswappable memory, and so on. To support this flexibility, the cssmalloc.h header contains some help. CssmAllocator Everything that can allocate memory is a subclass of CssmAllocator. CssmAllocator itself is an abstract class that implements standard malloc, free, and realloc methods with the obvious meanings. It also adds various nicer forms that are implemented based on these three: alloc() // enough memory for one DataType alloc(count)// array of count DataType objects alloc(oldPointer, newCount) // realloc for vectors of DataTypes There are also variants of malloc and realloc that can directly specify their result types. CssmAllocator::malloc will never return NULL. If malloc or realloc fail to obtain memory, they will throw the std::bad_alloc exception. Standard Allocators The easiest way to obtain a CssmAllocator for use is to call CssmAllocator::standard(properties) This static method returns a reference to an allocator with the given properties. If you omit properties, you get the default allocator, which generally provides cheap memory with no special features . The standard mechanism guarantees that when called with (exactly) the same properties, it will return a reference to a compatible allocator, so that the sequence Type *data = CssmAllocator::standard(properties).alloc(); ... CssmAllocator::standard(properties).free(data); is guaranteed to work. C++ Objects and CssmAllocators The C++ new operator is overloaded to accept a CssmAllocator as a memory source: new (allocator) TheType(constructor-arguments) obtains memory from the allocator and creates a C++ dynamic object in it. Unfortunately, the intuitively obvious syntax delete(allocator) object-pointer does not work, so instead you have to say the (somewhat less obvious) destroy(pointer-to-object, allocator) to properly terminate a C++ object and release its memory to the proper allocqator. Obviously, the allocators given to new and destroy need to be compatible. An auto_ptr for CssmAllocators A CssmAllocator-aware version of the STL auto_ptr is available, called CssmAutoPtr. It is intended to work just like it: CssmAutoPtr thePtr(myAllocator, thePointerValue) will release thePointerValue using destroy(thePointerValue, myAllocator) whedn thePtr goes out of scope, unless you called its release method first. If you want to create a MyDataType object right there, you can instead use CssmNewAutoPtr thePtr(myAllocator [, constructor arguments]) which will allocate a new MyDataType with myAllocator and bind it to thePtr, and acts otherwise just like a CssmAutoPtr. If tracking the allocator used during new is too onerous (perhaps you use different ones dynamically), you can make your C++ object inherit from the CssmHeap class. CssmHeap contains special magic that remembers the allocator used during new, so that delete pointer-to-object works properly. The price you pay is about one extra pointer per object storage overhead, which ought to be fine unless your objects are small and numerous. Special CssmAllocator Implementations Several implementations of CssmAllocator exist for special purposes. CssmMemoryFunctionsAllocator is a CssmAllocator based on a CSSM_MEMORY_FUNCTIONS structure. Conversely, CssmAllocatorMemoryFunctions produces a CSSM memory functions object based on a CssmAllocator. Writing Your Own CssmAllocator You can implement your own CssmAllocator as needed, and all code taking CssmAllocator objects will use it happily. The rules are simple: The return of malloc must be aligned properly for any data type. Malloc must return a unique pointer even if its length argument is zero (do not return NULL). If malloc runs out of memory, it should throw the std::bad_alloc exception. Free must gracefully accept a NULL pointer (and do nothing to it). Pitfalls If you use allocators, it is your responsibility to provide compatible allocators to allocation and destruction operations for the same object. If incompatible allocators are used, your program will disintegrate into a gory mess of flaky bits. This usually happens because you forgot to specify an allocator during objecgt destruction - beware of the delete operator. When in doubt, using CssmHeap will will save you from yourself (for a price). Keeping Track of Data Blobs In CDSA, a more-or-less opaque blob of data is represented by a CSSM_DATA structure. The corresponding PodWrapper, CssmData, represents a C++ interface to this. CssmData knows nothing about allocators, and contains no methods for allocating or freeing its data content. If you use CssmData objects, you are responsible for managing the allocation of their blobs in a consistent fashion. Pay particular attention to temporary data blobs: if an exception terminates your code, it is your responsibility to release their data. Happily, utility classes stand ready to assist you. Dataoid Classes The Cssmdata PodWrapper class has methods data and length to return the pointer to data and length of data, respectively. Note that these are not virtual methods, and it is not possible to modify their meaning in subclasses of CssmData. There are a number of places in the code where a class or method is templated based on a type that is only assumed to have such data and length methods. We call such template type arguments dataoids, to express that they behave somewhat like CssmData in their ability to yield a contiguous blob of untyped data on demand. This turns out to be surprisingly powerful. In particular, the memory-managed data classes described below are dataoids. If you create a class that manages, owns, or can reasonably return a contiguous block of bytes, you should give it two methods with precisely this profile: void *data() const; size_t length() const; This makes yourS class a dataoid, and allows all templates expecting dataoid arguments to automatically work with your class. Do note that you are expected to maintain your data at the location returned by data - if your data can be shuffled around or deleted at will, dont do this. If there are well-defined events that may move the data, be sure to document them clearly. CssmAutoData To promote more consistent allocation management, a hierarchy of objects stands ready to assist you. In the simplest form, a CssmAutoData object represents a CssmData that owns its data blob (if any) and knows which CssmAllocator it was allocated with. In other words, when a CssmAutoData object is destroyed, no matter how, it destroys its data blob by freeing it with its allocator, unless you call its release method first to indicate that you want to take possession of the bzlob : { CssmAutoData data1(myAllocator); // make empty data memcpy(myData.malloc(theSize), ...); // allocate and fill CssmAutoData data2(myAllocator, someCssmData); // make from source ... theBlob = data1.release(); // give up data1s blob } // data1s blob survives in theBlob // data2s blob was destroyed when data2 vanished A CssmAutoData is obviously a dataoid. Note that the length method with an argument, which in CssmData only allows you to reduce the recorded blob size, can actually shrink or grow a CssmAutoDatas blob, resulting in actual memory allocation actions. CssmAutoData has methods copy and set to implement copying and taking over of memory blobs: CssmAutoData &destination, &source; destination.copy(source); // make a copy of sources blob destination.set(source); // take over sources blob if possible destination = source; // best guess, Mr. Sulu If set can take over the blob from source, source will let go of it and lose ownership. For CssmAutoData, this means that it is cleared, again just like auto_ptr would behave. Assignment is available for CssmAutoData, and acts conservatively: if the destination can take over blob ownership from the source, it will; otherwise it will make a copy. In particular, since pure CssmData (or CSSM_DATA) sources have no notion of ownership, they are always copied. CssmRemoteData If you want to have a CssmAutoData that manages some existing CSSM_DATA structure, you want to use a CssmRemoteData instead. It behaves identically to CssmAutoData, except that its CssmData lives outside itself and must be specified in the constructor: CssmRemoteData myData(myAllocator, theCssmData); A CssmRemoteData that gives up ownership of its blob (because you called release on it, or another data managing object is taking it over) will not clear its data, it will simply remember that it doesnt own it anymore. This contrasts with a CssmAutoData that will clear itself in that case. There is one minor point that might prove important: the CssmRemoteData constructor behaves differently depending on whether its second argument is a reference to a CSSM_DATA or a CssmData. If presented with a CSSM_DATA, it will clear it upon construction. If presented with a CssmData, it will assume that any data blob inside was allocated with its allocator and take ownership. The Fine Print Both CssmAutoData and CssmRemoteData are subclasses of CssmOwnedData. CssmOwnedData represents any class that owns the blob of a given CssmData object. Since it is an abstract class, you cant actually declare a CssmOwnedData variable. You can however meaningfully pass references to them around, and this is indeed usually better than passing its subclasses: void getSomeData(CssmOwnedData &data) { ... data = someDataSource(); } which will work no matter what kind of CssmOwnedData is being passed in. Stepping back even further, CssmOwnedData is a subclass of CssmManagedData, which represents anything that can act as a source of ownership-controlled data blobs. CssmManagedData objects cant be assigned to, but they can be asked to yield a data blob: CssmManagedData &source; result = source.get(); // get some shared copy we dont own result = source.release(); // get some copy we now own source.reset(); // tell source to throw any owned data away Of course, CssmManagedData is also abstract, but you can use it to good effect as a potential source of data blobs: void storeSomeData(CssmManagedData &source) { MyStorageContainer *store = ...; store->theData = source.release(); } which will avoid an unnecessary copy of the source blob, provided you dont need) to keep a copy for other uses after the function returns. Finally, compare this suite of objects to CssmAutoPtr, which manages other (C or C++) objects on the heap allocated by CssmAllocators. The two are quite distinct: CssmAutoPtr manages the storage of the object itself, while CssmAutoData and friends manage data blobs identified by CssmData-like objects. In principle, you could have a CssmAutoPtr... Low-Level Memory Utilities The memutils.h header contains some useful utilities for dealing with raw memory. All these classes are in namespace LowLevelMemoryUtilities, and you are strictly discouraged from using this namespace as a whole. Fully qualify its members on every use, or at least only import individual classes or functions. The increment, difference, and alignUp functions deal with raw (void) pointers and integers. They should only be used where the type system must necessarily be violated - usually in other low-level memory uses built on them. Note that they deal in byte units. Writers and Readers The Writer and Reader classes provide for stuffing and unstuffing of contiguous memory areas with binary data. Writer and Reader are very low-level and know (and care) nothing about the C++ type system. They will not work as expected for C++ objects with constructors or internal structure. A Writer is initialized with a memory (void) pointer. You can then invoke it with any object reference, and it will stuff its binary data at increasing memory addresses from that point on, observing alignment rules in a system-dependent (but consistent) fashion, and rYeturning a pointer to where the data was copied to. Variants exist for arbitrary data areas (identified by start pointer and byte length) and for C strings, as well as counted-length opaque data areas (countedData). Note that Writer does no overflow checks, and in fact has no notion of end of area. It is your responsibility to provide a memory area large enough to contain the data you stuff into the Writer. In case you dont know a priori how big an area you need, you can use the Writer::Counter class. Make a Counter and invoke it with the same arguments in the same sequence as Writer. When you are done, the Counter object is the number of bytes required to store the sequence of objects you handed it. Reader is the obvious converse of Writer. Given a pointer to the start of a (constant) memory area, you can invoke it on a sequence of object variables and it will retrieve them and store them for you, effectively undoing the work of Writer. Again, Reader has no notion of end of stream and cannot tell you when you have exhausted your input; continuing to retrieve data that was not Written will yield garbage. It is important to understand that some methods of Reader copy data out of the source blob, while others return pointers into the blob . In the latter case, these pointers are constant, and its up to you to either copy the underlying data or retain the pointer together with the source blob. Note that Writer::Counter, Writer, and Reader agree on the needed alignment adjustments, so while you dont have to know what alignment rules they apply, you can be confident that they are consistent.Data Walking In many cases, code needs to traverse a (possibly recursive) web of data structures connected by pointers. The Walker interface provides a flexible framework for doing this. At its core is the idea of separating the enumeration of a data structures contents from the actual operations you wish to perform on it. We call the function that generically enumerates a data structure its Walker, and the actions to be performed on it are called Operations. The walkers.h header contains the generic machinery for data walking, including a set of generally useful operations and a bunch of functions that implement common functionality in terms of them. The cssmwalkers.h header contains walkers for many common CSSM data types . In order to use the data walking facility, the data type(s) you work with need to have a Walker function. If one is not provided, you need to write it (see below). Simple Copying At the simplest level, there are top-level functions and classes in walkers.h that provide common operations in completely encapsulated form. To use these, you need to know nothing about how walkers work - you just need to make sure t hat the data structures involved have a walker function. The copy function takes an object and copies it and all objects it points to into a single memory blob. This is a deep copy. It is not alias-safe - if copying encounters multiple pointers to the same sub-object, separate copies will be made. In other words, copy is designed for trees of objects, not DAGs or arbitrary webs . Copy takes a pointer to the input to be copied. In addition, it needs one of the following: A memory pointer (void *). The copy is made starting at that address, and no overflow checks are performed. You can determine how much memory is needed by using the size function on the input object pointer. A CssmAllocator. Copy will allocate memory and then make the copy into that area. The size is calculated automatically using the size function . The size function, applied to an object pointer, calculates how much space is needed for copy to make a complete copy. The Copier class provides a slightly enriched interface to copy. Creating a Copier object performs the copy as indicated above (only the CssmAllocator version is provided): Copier copier(myObject, myAllocator); MyType *theCopy = copier; size_t theLengthOfTheCopy = copier.length(); The copy is automatically freed when the Copier object is destroyed, unless you first call its keep method - this indicates that you have taken possession of the memory block and are now responsible for disposing of it when desired. Copier has a variant for copying an entire array of a data type. All elements of the array, plus all their pointed-to substructures, will be packed up into a single contiguous data area. IPC Relocation To transmit a data tree through an IPC link, the data needs to be packaged up, transmitted as a single data blob, and then fixed up at the receiving end to appear again as a tree of data objects. Walkers support this process by copying on the sender side and relocation at the receiver. On the sending side, use the copy function or Copier object to pack up the data. The copy will be contiguous. Transmit this copy through whatever IPC mechanism you use, and also transmit the sender-side address of this blob. On the receiving end, call the function relocate(pointer-to-received-blob, sender-side-address-of-blob) This function will traverse the received blob and correct all pointers so they become valid client-side pointers. After that call, the data blob will be a faithful, self-consistent copy of the original. Step by Step If the self-contained functions described above are insufficient, you can step down into a more detailed view. In this view, size, copy, Copier, and relocate are implemented in terms of operational classes applied to data walkers. The general pattern is OperationalClass operation(some-arguments); walk(operation, first-object); ... walk(operation, last-object); ... retrieve results from operation object The pre-defined operational classes SizeWalker, CopyWalker, and ReconstituteWalker are used to implement the size, copy, and relocate functions respectively. For a good (and fairly complex) example of use, take a look at the Context::Builder class in context.h. Writing a Walker for a New Data Type There are two kinds of walker functions: a pointer walker takes a pointer-to-object, while a reference walker takes a reference-to-object. The difference is that a pointer walker copies the object itself, while a reference walker only copies its sub-components. Most object classes have only pointer walkers. Reference walkers are needed to support arrays of objects, as well as objects embedded (by value) within other objects. The general pattern for an ordinary pointer walker is template Type *walk(Action &operate, Type * &obj) { operate(obj, size-of-object); // (see note below) walk(operate, obj->firstField); ... walk(operate, obj->lastField);] if (operate.needsRelinking) /* fix up any fields that depend on the fields above */; return obj; } Size-of-object is the length of *obj; it is taken as sizeof(Type) if omitted and thus needs to be specified only for dynamically sized objects. If you must dynamically calculate this size based on the objects contents (i.e. *obj), then you must skip this calculation (and substitute zero or omit the length argument) if operate.needsSize is false - this indicates that the operation is fixing up an object representation that is not (yet) correct, so you cannot use *obj. FirstField...lastField are fields of the object that need to be walked in turn. If you have no subfields requiring walking at all, your data structure is flat and is probably covered by a default walk function. Note how the argument is not const; it will work on const input anyway due to some fairly intricate magic . Note that operate may modify its obj argument if it performs actual copying. It is thus important that you maintain the ordering of calls: call operate first, then walk your substructures, and finally fix up any dependent fields if needed. If your object contains dynamically sized sub-blobs, you may need to call operate on them explicitly. Take a look at the CssmData walker for an example. Creating an Operational Class Warning: This is slightly out of date. Additional methods must be defined in operational classes. Take a look at ReconstituteWalker for a sample. Even better, ask Perry. Since all participating object classes already have walk functions, its tempting to use them for other operations that require traversing an object tree. Sure, go ahead - here are the rules. Your operational class must implement a template function-invocation operator of the form template void operator () (T * &obj, size_t size); This operator will be invoked, in some order you should take to be unspecified but reproducible, for each contiguous memory node (heap object) being walked. For each invocation you will be passed a pointer and a length that together denote a T object in memory. The length argument tells you how big the heap node is. This is interesting if you plan to calculate sizes or copy things; otherwise you can (and should) ignore it. If you never need the length, you should set your needsSize member constant to false (see below). The obj argument is more interesting. It is a pointer variable to the start of the heap node being walked. It is always a non-const pointer. You can do all kinds of interesting things with it: You can ignore it (of course) You can read the object itself and make use of its contents. Note that you can, in principle, define a different function-invocation operator for each argument type, so you can take specific actions for types that are somehow interesting or special. You can modify the object if desired. If you declare the pointer argument to be a reference-to-pointer, you can actually modify the variable containing the pointer to the heap object. Your operator is guaranteed to be called with something that is an lvalue (variable) of type pointer-to-object. This is how CopyWalker replaces pointers-to-originals with pointers-to-copies during its walk, for example. Of course there are also some fairly important rules that you must respect: If you are given a pointer-to-void (void *), the memory area must be taken to be opaque; you must not try to interpret it. If you allow walking with pointer-to-const arguments, then you must make sure to copy the data before making any changes to it. This is a side contract your operational class has with callers of walk(YourClass, ...). On the other hand, if you require your walks to have nonconst-pointer arguments, then you are free to modify the object in-place. Note that in that case, the type system will be too permissive, and callers of walk(YourClass, ..) must take care to not pass pointer-to-const-objects. In addition to the operator () method, you must also define two boolean member constants: needsRelinking indicates that your operation actually changes its obj argument, and thus walker functions must be prepared to update any fields that may depend on your changes. Set this to false only if you never modify your obj argument. needsSize indicates that your operation actually makes use of the length argument of your operator (). Set this to false if you dont need it. You must set this to false if your operation works on temporary invalid objects to fix them up, since any length calculation performed by walkers would be using such invalid data . You control whats happening. The walk functions have a very restrictive protocol; they have to play well with radically different operations. Your important contracts are with the callers of walk(YourClass, ...). For example, ReconstituteWalkers contract is that pointers to objects are off by a particular offset and should be fixed by offsetting them. Thread Support The threading.h header is meant to provide a complete abstraction for threading and synchronization. If you use the C++ classes defined there, you should be independent of the platform specific ideosyncracies. Thread Objects The Thread class implements a separate thread of execution. It is fairly basic and simple. Subclass from Thread and implement the action method to create a runnable thread. Create the object on the heap and call its run method to actually start it executing. Alternately, you can use the ThreadRunner class and simply pass it a pointer to a function you wish to run in a separate thread. Either way, note that the thread will not start executing until its run method is invoked. Any subclass of the Thread object must be allocated as a heap object. Do not place a Thread object on the stack, or inside another object. Locks A Mutex object is a primitive synchronization object. A Mutex can be locked and unlocked, and the system guarantees that at most one thread has any one Mutex locked at once. Mutexes can be used to guard any group of memory objects against simultaneous thread access. Note that the association of a Mutex to some memory object is not magical, but by arrangement, and all code must agree to the arrangement for it to be safe. It is an error to destroy a Mutex object if any thread holds its lock. It is possible to call the tryLock method of a Mutex to attempt a lock without blocking. TryLock will return false immediately if the Mutex was already locked. A CountedMutex is an ordinary Mutex that also contains a count. The counter starts at zero at initialization. In addition to the lock/unlock methods of a Mutex, a CountedMutex also has enter/exit methods that atomically increment and decrement its counter. This is intended for in-use counts. The underlying lock/unlock functions are still available and can interoperate with enter/leave. In particular, there is a finishEnter method that upgrades an ordinary lock to a counted lock (as if enter had been called). Safe Locking Support: StLock StLock is a versatile class that can help you safely maintain locking hierarchies in the face of unpredictable control flow, particularly exceptions. An StLock object is usually placed on the stack as a local variable . It takes a locking primitive object as an argument, and (by default) locks it during its creation and unlocks it when it is destroyed: { StLock locker(myMutex); callSomethingDangerousThatMayThrowAnException(); } This will lock myMutex on entry, and will guarantee that when the scope of braces is exited in any way, the lock will be released. Often, just placing an StLock object declaration at the beginning of a function is sufficient to solve many locking issues: int MyClass::someAccess() { StLock _(myLock); return someCalculation(); } assuming, of course, that you maintain proper discipline in applying this coding style to all your accessor methods. Note the ritual use of the name _ for the locker object, since the locker itself is rarely accessed directly. StLock is actually much more general. If you declare an StLock object with a boolean argument, you are telling it the state the lock is in and should remain in. Specifically, StLock _(myLock, true); indicates that myLock has already been locked, and StLock should merely ensure that it is being unlocked when it dies. Conversely, StLock locker(myLock, false); tells StLock that it should not lock myLock right away. That is useful because StLock has lock and unlock methods of its own, that keep track of the lock status and ensure clean unlocks on destruction. Note that StLocks lock method will do nothing if it knows the lock to be already active. For example, you could do something like this: void myFunction(bool alreadyLocked) { StLock locker(myLock, alreadyLocked); // maybe locked, maybe not - but locker knows locker.lock(); // now its locked, whether it was before or not } You have noticed that StLock is a template based on the underlying locking primitive. In fact, there are two more template arguments that denote the primitives lock and unlock methods. Thus, StLock can be used with other lock objects or levels: StLock _(myLock); creates an StLock that controls a CountingMutex and calls its exit method when control leaves its scope. Note the notation form for the methods, which must be followed precisely (ampersand, type name, double colon, method name) . If you find yourself doing this frequently, just make a typedef. In fact, if the need arises, you may create locking objects with arbitrarily interesting lock/unlock methods and apply StLock to it. As a simple example, CSSM Module objects have a conditional lock facility (safeLock and safeUnlock) that engages the module lock only if the underlying plugin is not thread-safe. So the corresponding code uses a StLock to perform its locking magic, ensuring that the unlock happens appropriately whenever an exception disrupts program flow. Mach Support There are a few classes that provide support for the Mach 3 environment. This is fairly ad-hoc right now - just what I needed. Note that Mach support is not available in the OS 9 environment. All Mach support is in namespace MachPlusPlus. Basic Facilities The Port class, unsurprisingly, models a Mach port object. You can manipulate it, copy it, etc. to your hearts desire. Port is passive in that it does not allocate or deallocate its underlying Mach port. Its subclass, ReceivePort, creates and owns a port object; this means that the port will be destroyed when the ReceivePort object goes out of scope. The ReceiveSet class models port names that are port sets. Finally, TaskPort provides some functions specific to a task port. TaskPort will self-initialize to the task port of the current task, but you can assign another tasks task port to it should you somehow get ahold of it. MachServer The MachServer class provides an encapsulation of a Mach IPC server loop for MIG based servers. Create a subclass of MachServer and implement the handle method to handle incoming messages. (You will typically just call the MIG-generated handler function there.) In your main program, declare an instance of your server object and call its run method. Run will not return until the server fails for some reason. Voil: a server. The MIG server functions you write to implement server functionality are C code. To access the server object from within, use Server::active(), which returns a reference to the server object while the server is servicing requests . If your handler needs to allocate temporary memory to return values, call the releaseWhenDone method of the server to arrange for the memory to be released as soon as it is not needed anymore. To be notified of port deaths, register ports with the notifyIfDead method of the server object, and override its notifyDeadName method. The server will call this method when any of the registered ports become dead. The most obvious use here is to register the client port, so that you find out if your client has aborted . MachServer Timers If you need to perform periodic tasks, you may use Timer objects. Create an object derived from MachServer::Timer and override its action method to perform some timed task. Then pass the object to MachServer::setTimer, giving an absolute or relative time in seconds. MachServer will arrange to call your objects action method shortly after its allotted time. You may have any number of Timer objects for one server, but each Timer object can only be scheduled once. If you reschedule a Timer, its old schedule will be canceled in favor of the new. If you schedule a Timer into the past, it will be run as soon as practical. You may explicitly un-schedule a Timer with MachServer::clearTimer. Times are expressed in floating-point seconds. It is possible to schedule at fractional times, though there is no guarantee as to the resolution supported. Current implementations may provide millisecond resolution at best; do not expect anything less than 1/100 second to be practically useful. When creating absolute times, use MachServer::now to retrieve the current time and use offsets: MachServer::active().setTimer(myTimer, MachServer::now() + 3600); Within a Timers action method, its last scheduled time (the one that caused action to run) can (still) be retrieved with the when method. Thus, code like MachServer::active().setTimer(this, when() + 300) at the start of an action method can create a periodic timer that is free of systemic drift. Multi-Threaded MachServer MachServer can turn into a multi-threaded server bundle if you wish. If your service code determines that it is engaged in an activity that may take a long time (e.g. it intends to block on a client call of its own, or it will do something computationally intensive), it can call the servers longTermActivity method. This signals to the server that more threads should be created to share the load as needed; the server will do this only if it is needed to ensure that at least one thread is available to service new incoming requests. The original server thread plus any auxiliary threads created by MachServer share the servers load equally; all of them may handle incoming requests, dead port notifications, or timers. If the load on the server lightens, auxiliary threads will automatically be terminated after a short delay. You can set or get this delay with the servers timeout method. The main server thread (the one that called MachaServer::run) will never be terminated for you. You can set an absolute maximum number of threads (including the main server thread) with the maxThreads method; the default is finite but high and only intended to reign in a run-away bug. Note that this is an optional facility; if you never call longTermActivity, you will get a single-threaded server where the main thread services all requests, timers, and notifications. Once you call longTermActivity even once, MachServer is permanently switched into multi-threaded mode, and it is not possible to return to single-thread mode. Warning: If you use multi-threaded MachServers, you are responsible for making your code thread safe. All your code must expect that request handers, notifiers, and timers can run in parallel in any order or sequence. MachServer makes sure its own operations are thread-safe, but it leaves the thread-safety of your code to you. Runloop Servers If you want to add a Mach/MIG style server to a program that has a CF- or NS-style runloop, you can use a MachRunLoopServer object. MachRunLoopServer is a subclass of MachServer that, instead of running its own server loop, registers its service port with the default runloop and receives messages from there. Most MachServer facilities still work, though they may be differently implemented. Warning: Timers and multi-threade operation are not currently implemented for MachRunLoopServers.ZNDSETT`  p  8$     This does not mean that the abstraction layers are obsolete and can be leaked through. Future portability is always a good idea. For example, we may well want to switch threading systems in the future.DSETTII`UUU U$   U No, the handle is not exactly the address. Theres a bit of bit mucking going on. Just assume theres a magic map in there. There is.DSETT`U0U4UDT$   U$ The code makes a minor effort to avoid reusing a handle immediately. But in long-running programs, you may see the same handle re-appear, theoretically.DSETT&/`U   2 L26    V V There actually are two forms of the cssmError method. The form without arguments returns the error code as is, while the form with one argument applies the module code given to the error code if it is convertible and not already tagged with a module code. If this means nothing to you, check the CDSA documentation appendix on error codes.DSETT` UUU&U&$    U8  Before Panther, UNIX errors did not map well to CSSM/OSStatus errors, and tended to get mangled into internal errors on the way out. Starting in Panther, the UNIX errno range is now embedded in the OSStatus range (at 100000+) and errno values will make it out alive.DSETT`   & P&  B N ] e      Sometimes you cannot avoid this. For example, an application of operator new may throw the badalloc error. Dont worry about this on principle; just do the best job you can. (The transition layer maps badalloc to a memory error diagnostic.)DSETTHH`HV8V<V@U$   IV4I CssmPerror translates all known CSSM errors, plus the OSStatus values DSETTCG` Q   UH  * 0  R R For safety, place all return statements inside the BEGIN_API/END_API1 brackets.DSETT'0` h $   H$   i (i In particular, default memory may be swappable or otherwise observable from outside the local process.DSETTfn`  < 0 4 X   7 > J P f n   L For completeness, lets note that you can still call destroy instead of delete if you wish, even on CssmHeap derived objects.DSETTF` F X P T \ $   G \G This, again, is consciously modeled after the STL auto_ptr paradigm.DSETTHK`  p d l D H  H K   t There is no guarantee as to the lifetime of the reference returned by get, so its only good for a quick peek at the data without incurring reference assignment overhead.DSETT`  x |V $     As a rule of thumb, methods taking non-const pointers make copies, while methods returning const pointers yield pointers into the source blob.DSETT~`   V H  t     Some more specialized CSSM types, such as ACL related ones, have their walkers included in their header. Look for namespace DataWalkers.DSETTs`s   V$   t t ... and that you include whatever header contains them, since most of this magic is based on template expansions.DSETT`   V$     If that turns out to be a problem, it would be fairly simple to invent a SharedCopyWalker class that merges duplicate pointers. This has just not been needed so far.DSETTqp`u   V$   v v If you happen to know the size required, you can pass it as a third argument to avoid needlessly re-calculating it.DSETTad` D 8 @V$    T Actually, the magic consists of a template walk function that casts away the const-ness of the obj argument. The intricate magic lies in why this is safe and legal to do...DSETTTT`T l \ `V$   U pU Yes, that means that such fix-up operations currently cannot get size information.DSETT`  t xV$     A CountedMutex is not intended to provide semaphore semantics, though it might be implemented through semaphores on some platforms.DSETTgg`gTWHWLU$   hTh In some situations, it might be a class member or even a base class. Think different (but carefully)!DSETT `V   VH      W W Likewise, unlock will do nothing if the underlying object is not known to be locked.DSETT`   V$     This is the notation for C++ pointer-to-member constants. C++ syntax only allows pointer-to-member constants in this place, and you have to explicitly restate the class name. Stop groaning already...DSETTK`K   W$   L L If you call it at any other time, the method will fail with an assertion.DSETT`   W$     Strictly speaking, when its reply port has been destroyed. For the default MIG reply port, this means the thread that made the call(s) has terminated.DSETTu`   W $     Because of the way threads are reaped, it may take up to twice the timeout period before all auxiliary threads are terminated after request load has vanished.DSETT,`,   $   - - At least, we dont support this right now.DSET2H $Z26*ZDSET<2H6 (Z   00@@P P ` `   #p #p%%((++--0026*ZDSUM(Perry The CynicHDNISTYLRJJSTYL   P T ` x |         g  37:  A  1    $ 5       >    %000000 0 0 0 0 0 0!!0%""##0""$$0""%%0""&&0"$''0"$((0"$ ))0"$ **0"$ ++0"$ ,,0"#--0"#..0"%!//0"%%00110002200033000440025500266002 77002 88002 99002 ::001 ;;001 <<003==003%>>??0>>!@@0>> AA0>>!BB0>@"" CC0>@#DD0>@$ EE0>@% FF0>@& GG0>@' HH0>?#( II0>?#) JJ0>A* KK0>A+%LL,MM0LL$-NN0LL.OO0LL/ PP0LN0QQ0LN1RR0LN2SS0LN3TT0LN4UU0LN5VV0LM%: WW0LM%; XX0LO8 YY0LO9 ZZ,[[0ZZ- N\\0ZZ.]]0ZZ<^^0Z\0__0Z\=``0Z\>aa0Z\?bb0Z\@cc0Z[&: dd0Z]Aee Bff0ee'Cgg0ee Dhh0eg $ii0eg %jj0eg &kk0eg 'll0ef(Gmm0ef(Hnn oo8nn pp8nn qq8nn rr8nn ss8nn tt8nn uu8nn vv8nn ww8nn xx8nn yy8nn zz8nn {{)||8{{*}}8{{+~~8{{,8{{-8{{- 8{{-!8{{-"8{{-#8{{-$8{{-%  &8 '8 (8 )8 *8 +O .  /,/,'- .M. \   #    0404141 3# 41 5252046 0404140  9,9,?2/!> <6<6B 0404140   7 7 1737.1  : ;8;. " =4=41411 ?? 5252HASH $Ý ô׌Lô،Z"0Ҵ"<"%8e>n:7QQA,Qv,oQpvqrst u/vTwyxyz 7+ 7* 7*5*gjkhi*lm,,,0f ,/,T,v*~y, ,Q=},*|,- AV BWy:6;$ ' ( ) *+/& EHFI9K 1#9c<M +& &6 '7 (8 )92=.z<>= %84@?44[3)5#D$E%F&G<JVB%R&S'T(U&YC$K @ A \ ]@X>d%Q/a0b'^-_.`:PO -% & 2 26 26(2 6(2%L:Qfeg{hmhiCodjak*k*k(l)l(m + m +n*n * n *o(o.+p *p *p *qit r.*r9*rl r*Xtuzoz}Bod}n@ CHAR       7"     77"   " ( ' %         .     / * 2 !/  $  '& &  & )&     2  7  3"          <2HASH (66 (- : 1       % 8 > &,  & ) 1# :"   1 ; .     =  3 0  #7 9+ :4  ?    < # 42 5&*: 9: /:I CELL (N'          6x $ x         7E       F  ( s @HASH     ( ) :;,xx13   -  D"# $ % & '*+ 2345 89B !.<*GHC 10A=>?@ / F GRPH EG   HASH      f<8l RULR  \&` ` $B  @  ` ` $ $$Hl D h  $Hl D h$Hl >++ff ~  ` `f `$9`.`.`.`.l:HASH- .@  k-&,'0Q(v)*+0 6,Qh/v /Ty,Qv ! " /# T$ y% 0  h5  B" ,"h0( 2(1`4`3b b(c" d" d&7d1;dT:$ KSEN HASHLKUP  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~#$NAMEDefault Default SSHeaderBodyFooterFootnoteFootnote Index Bullet Title Section 1 Q & AQQA DefinitionCode ChecklistNumberClassic" Blue Gray 10 Blue Gray 2>ColorfulL 3D Table 1Z 3D Table 2e AccountingnHarvard{LegalDiamondEmphasisFilename CodeLinesWarning Doc ReferenceSubtitle Section 2 Default TBDFNTM HelveticaGeneva Lucida GrandeTimesPalatinoCourierMCROMCROoBlNMARKMRKS MOBJWMBTSNAP-1-,-, HH HHUQ@+^o{o{wo{cwkZ^{s{EBg9cJRR^kZJR9F15-k9NsF1w=kZJR91NsR=F1NsBF1JR-kg9CRV{o{g9kZJRZVF1VNsVBZsBF1VNsZRZVRF1s g9a,kZNs=cZBVg9ZNsV-kNs^R5Zg9NsJRF1^ZJRo{RNsw9VcR=JRVF19JRRR=ZBs]kZVZR^RVo{^g9JRVNsV={g9VBZVg9c^Zg9VRwkZcc=Zo{cRRZRo{kZ {޽-{{{ws{{{{5VsJRVo{JRRNsZco{^^sZRJRsBJRZVo{NsRw^cRg9JRsRVVRVZNsNsR{NsBZg9Zo{Vg9JRZJRsVZNso{Rg9VNsVVkZ^NsRJRRo{o{{sww{ss{{wso{s{wo{o{{skZwswswskZso{s{s^kZ{swo{kZZkZ{wo{sww{wso{o{{{o{wwo{Kg9JRNsZRNso{Ns=NsJRg9NsVNscJRRVJRJR{NsF1F1VRBNscNsBg9NsJRRkZRRNsJRNsVNsF1cJRNsF1kZNsNsJR=JRo{JRBsJRRJRNskZNskZJRNskZNsg9RJRR{w{ww{w{{g9kZo{wwwwg9wo{kZkZo{{wsso{kZw w{{wwwKg9Bg9JRJR9Nsg9JRBF1cBsc^JRZNsg9BRkZJR5kZ=kZ{ZR=BF1cNsF1=JR9w^VVNsNsg9^JR^Ns^cF1JRNsF1F15NsRVF1F1JRkZF1Ns9BF1s=RNs{Assw{wo{{{swo{so{sM"g9NsNso{{NsNsV=V^NskZRRNsVRg9BRcVNsF1^NsBNsJRsBZ=VG{{{o{{{{{{{{{{w ZsF1NsRNsRVkZNso{VR6ckZNsF1ZkZVBVF1Vo{VRRJR{NsVRNsRVVZJRsNsRNsRo{ZNsRF1ccVkZZF1RkZVF1NsRVRZJRVZ6wwo{{g9so{wswskZsg9so{o{g9^{kZw{{sswc{{co{kZo{wkZo{sswo{so{{{sws{wo{kZ{kZo{{cg9g9kZkZskZwo{sww9cVNsJRF19Ns{JRBJR==Zco{^ZsBF1BF1BBwBsJRBo{Z{JR9=JRkZ==BF1=JRNsg9=JR=g9JRkZF1Bo{JRF1 {ZZRZF1w=BF1Ns9BNsms{wg9s{{{wskZ{{wso{so{w o{swsw{wcJRF1FB{Ns9RsJRBJR==kZF1JRF1BF1F1g9JRJRNsJR5R=JRBw=JR{F1B=F1g9NsF1Ns^F1JRcF1BF1BJRBJR=BsJRF15RBBNs^VB{9BR=BkZ^{cs{ww{w ww{w{{s{{ws{wwsKkZVNsF1^VVc{g9JRJRo{RF1VcRkZRNsRo{ZNssNscNsVVNsg9cNsZNsRNsVVg9VVcVcZo{F1VNsVJRkZRF1wJRRRVZc=^NsVVkZVNsRo{kZsws,wsswo{wsswwss{sw{wswssw{s{so{wVkZsw{w{{{s{sssg9^sswswo{s{ws{HNscVsZ^^g9R^ZZVZ^^{Z^wcZo{g9Z^^{VwRZwg9g9Zg9^Vo{o{Z^cg9g9kZo{V^^g9^kZZcZcg9ZZ^ckZccVcZcwskZo{{o{kZswg9o{kZkZg9kZg9kZg9kZwkZskZg9kZkZkZo{g9skZo{g9o{{kZskZwo{o{kZo{skZo{ZkZo{wkZwo{co{o{g9kZ{o{ZVccVg9R^>Z^ccVZkZ^RZVwccNsNswcF1V^Nso{^F1Vso{^BVsZRkZVV^o{Z^o{kZ^Vo{JRR^o{^cVVJR^{Zo{ZR^kZ:so{g9o{g9sso{wc^wskZkZo{{kZo{skZg9kZkZ{o{wo{g9kZo{g9o{g9sswkZswo{skZ{kZc{kZo{kZso{kZ{kZkZ{kZ wo{kZg9o{kZkZskZwwkZg9{M"g9cVRZNsZVV^^g9JRNso{^F1VVkZVkZcNsZZVo{RRV^NsVg9Iwwswsw{wswsw{sw{swVs{o{w{g9s{ssw VsccwcZ{^Zg9Vc8o{cg9cZ^kZscg9Vg9Zg9o{kZZkZg9c^ckZkZsg9ckZccZckZo{Zcscsg9cg9V^kZwc^sZ^VkZg9ws/kZ{^kZsg9g9g9g9kZg9kZg9g9Nswg9kZo{o{g9kZo{g9g9kZo{cg9so{co{kZg9g9kZso{skZ^Rg9g9ckZo{^so{sg9o{kZskZkZwg9o{skZcg9g9JRww;kZ^ZVcVVkZVsVVRZ^^Vo{^VZRZ^RZ^g9kZZo{Z^kZcZsJR^^Z^^Zs^ZkZcZNs^ZZo{^Ro{^^ZVsg9^kZswo{VkZkZ{kZkZg9g9o{kZg9skZo{ kZo{o{wso{kZsskZo{wkZg9g9o{kZso{skZkZso{kZg9kZkZZco{sso{kZg9ww{w{s+Vo{RRg9JRcF1^RRJR^BRJRNskZ) wkZ^kZo{ZsccVg9JRcZ^kZw{{wwRVo{RZNsRkZVV^NsVNsg9Z6RZZg9RVo{ZVVRF1^NskZRVwNsR^BRVsVNsNsVNsVVJRg9cBZcZNso{VZo{NsNsZRF1VJRRo{o{o{wso{ {wswskZs{wcZskZwkZs(o{{wo{sso{wo{ckZwswg9wg9o{s{wsw{ssss{wo{wwo{ss$cVNsZVNsVNsJRo{RVNs^g9o{VJRRJR^VJRkZg9cJR^NsRRcZF1ZRVo{RNsVg9NsRVVo{NsNsZRo{NsRNsF1ZVJRcc^cRVJRNsVo{{g9wwso{{{o{kZw{{o{{kZkZ{o{wkZo{wkZco{wws{w{ws^w{o{{{wo{g9w skZw{ss{ww{w{w{kZwsK!g9F1F1^=JR^JRF1^BR^1NsRJR^VB=R^BF1g9JRVZo{F1BF1g9 {ޠ{ZJRkZZBNskZVZZccZg9Z^ZVo{Zo{^Z^VJRkZZVkZZNsR^o{Rg9Z^skZZsZ^RcRZkZo{VZg9ZcRkZZ^R^Zg9Z^^NsZ^s$sskZ{ws{wo{wsws{g9ccwws{o{kZo{wssw{w{sg9{wwcg9sswo{ss{sso{wkZo{{swo{s^s{o{kZs{g9RGVVRkZZVJRg9Vo{RBckZ=VVNsF1g9g9RJRVg9RRkZNs^NsVNsNso{wcRRcVVJR^wo{Ro{VVNsg9ZVNsg9ZRVF1wJRVg9RVNsF1kZRRc+o{g9o{sg9o{wswkZ{o{{wswssww{{sswsswssckZwwwkZwwo{s o{{skZsswso{{kZs{o{swswkZsJcVNsNsJR=VkZg9ZJRJRRkZJRNskZF1NsRJRkZ=RVVkZRVZZVcVNsNsVJRkZg9RRJRNsNssNsRkZZBcg9cg9JRRRBNsJR{BRJRVF1NsccBJR^s{ww{sw{{{{s{ws{ {ww{{skZ{swsVNs=F1^BF1>cNsF1=F1=F1VZF1JR^BNsJR=w=g9BBJRZkZZNs9BF1o{9NsBR=F1^Z9F1R=JR9g9Ns=g9NsB9BcJRJR1ZZ=JRo{Eww{{{{wso{{s{w{HV^Zg9NsRkZRJR^VZZBZ^V^RRVRB^Nso{Ro{NsVo{JRNsVo{NskZZRJR^F1g9VNsNso{Vo{kZNsJRZVVNsZg9RVJRRV^VZZVZVRkZwwsg9wswg9{swwo{wkZo{o{s{kZ{o{w{{ss{wso{sswswo{wwg9so{swo{swsswwsskZo{wKkZRNsg9RJRZkZJRo{RF1VRNsRVJRkZVRo{JRZVcVF1Vo{VRkZZRVBkZNsRVkZRRVg9g9NsJRRZRNso{R^NsRJRwZZ9Z^VJRVkZVRcZNsRo{"sw{{o{w{{kZw{wwkZsswkZo{kZsw{co{so{o{wswwswkZwsw{sw{{wkZw{sZwkZss{o{kZww{ssg9{w{{o{kZ;g9B==Ns=Bg9ZB=JRg9JRF1o{Nsc^Zs^VNsBBkZF1B9sc9ZZJRVJRJRBJR9BwB5RBBsJR9=NsNs9NskZB JRg9JRF1JRc9Ns1JRF1Nsg9N{{{o{o{{{o{wwo{wcwwsw]*kZNs=g9RBJRkZJRo{NsNswBNsF1ZcNsBg9RJRRo{JRNsJRNsVVZNsJRkZJRZVZNsZJRg9{{޿kZ{ީ%VJR5=%)NsB99551JR5Z֪ kZ(wco{wwsg9kZo{skZswg9ww{o{ww{o{{w{o{wskZkZ{wwkZo{kZ{sswskZkZ{wo{cg9w cwwsskZswsw{o{NsVR{ZVo{kZZVg9kZJR^ZVckZcVg9^kZc^RVcsRo{ZR^RZZkZJRVco{^V^ZZo{cZZkZcZRo{ZZc^Z wg9kZc{kZo{^cg9g9{kZo{kZ7g9skZo{^g9g9o{g9kZkZg9wo{Vg9o{swZsg9o{w^kZckZg9co{^o{g9{c{kZg9o{so{^ccwNso{kZcg9kZo{c^wKkZZ^V{o{VZkZZZF1{RZsF1g9Zcso{R^VRg9g9^sR^s^V^g9Zs^^o{^ZVwVVs^ZVwRs^Z^c^^Vcs^V^kZVcNsVco{.cco{o{g9o{g9sg9kZo{Nsg9o{g9o{sco{so{^kZo{skZ^swg9o{kZkZ{kZg9{Vo{ckZVkZwkZg9kZkZwo{kZ{kZkZwg9o{kZckZkZkZo{wkZg9Jwc^sc^cwZkZ^Z{cR^cg9g9^o{s^g9c^cZkZkZ^^ZZsZ^wg9cc^{kZR{^c^o{sZsg9co{^ZsZg9g9^^so{g9wcg9o{c!^^wNsg9kZsg9^g9^kZcs^ZZo{c^g9^ccg9Rcg9g9co{kZ^kZ"cZcsg9ZVc{VVccg9cwwkZg9sZco{o{g9kZcco{cg9RcZo{ckZkZg9cc{c^o{o{kZkZg9kZco{sc kZc^g9kZckZg9ckZ(g9wV^sscsg9cc^c^{cg9o{ckZo{s{kZo{o{co{wsg9cg9kZo{g9c^RcwRNsVkZ^Zg9kZ^kZw^kZVc,g9Vso{cZg9kZV^ZwZo{RRZc^Zo{Zg9^ZkZg9g9^cZ^g9Z^ZZ^cVs^g9w!wc^o{swo{ssg9o{wg9wkZo{kZg9o{o{{o{g9so{wo{o{Vo{sg9Vs kZwo{o{Zo{sso{sVsso{ wsg9kZg9o{kZsw'VRNsJRF1wF1=Z^ZV^VZg9'kZg9g9^o{sg9^=F1^g9ckZGNsNs^ZkZZJRZRRsV^Z^o{VRVZg9ZVsg9RRNsZkZZw^Rg9cZZkZg9JRZsZRZNsR{VV^ZZg9RRZZR^ccNsZF1RZwZ^sg9cZ{kZww{kZo{wo{{wwcc{wwswkZo{wswswg9wwsg9sswsskZo{wso{skZ{g9ssw{wso{wwJ^^ZkZo{RNsZVRNs^kZVR^RJRg9cJRVRcVZcskZRNsJRVc^BVVF1NsNsF1kZRRVRkZNsZRVBkZcscNsRZJRNsRVNsNso{g9o{Z^wscwskZww{wg9o{cso{Rccs!o{w{o{g9w{{kZo{kZcwwwo{swg9ws{o{o{wssso{{s{ws^csZo{wss{ww{{JcJRNsJRwNsF1NsRZRRg9NsJRF1RVRV^^Rc^VBNsg9VRccJRF1Zo{NsNsJRo{F1F1V{F1JRBBZcNsNsF1F1NskZRB^BJRJRg9NsJRkZRNsF1NsR=]{{s{w{{ww{ kZwwo{g9o{w{sw{kZsK^F1Bg99V^JRF1BBs9BJRF1RBF1=g9g9F1wJRF1BF1JR9o{9F1ZVJRBF1F1kZF1kZNsF1F1JR=JRkZZcVg9^Ns9F1s9c^kZ^cg9R9F19Rg9F1F1kZ:{o{wwwo{{s{kZso{o{ss{kZws{w{wwkZw{sswsswo{{wwo{{sww{{kZo{wwg9wo{ww^o{w{w {wwso{wo{sw{wwAkZNsJRVkZRZo{NsZVVkZkZF1RkZkZRVVNso{^JRcV^g9wcNsRR^ckZVZo{o{VF1g9ZNsNso{VRVJRNsVo{^^VRVF1kZ^RZZVRRco{NsJR{:{swswskZ{o{kZkZwo{wswkZskZw{޼;g9VNs^RNsRF1F1RVF1Zg9RsRRNskZZVNsRZZֵg{kZwkZ{{w{{{{{o{o{sso{w{ wo{ssww{{{c9Zg9JRF1F1RkZF1 =^ZF1Ns9=NskZ9F1,B^^F1{VZNs^RJRBNsJRBBw=F1{JR==cg9^VZF1c^5RB1F1sF1R9RJR=kZq{sw wwsws{{{{{wo{{kZs{w{{kZ{{{BkZRNsF1kZg9JRVNsNso{NsRg9RBo{RNsNsRg9NsBg9VJRRF1NsJRF1RRkZVcRNskZZNsJRJRRNsJRF1cNsRNsRg9F1NsRo{o{^g9RJRJRo{RNsVcVNsZ{/o{cg9wo{wo{{kZswwkZo{w{{s{swkZo{{s{o{swsswkZsw^wcg9cwo{wwo{wwskZwskZo{wsso{kZ{KsNsRJRg9Z^g9VVF1o{kZNsJRc^g9ZkZVRZRZ^wRNsVo{RNsVZF1VVkZg9RRVRRF1VZZ=o{o{VJRg9g9NsNs^RJRZo{RZRRVRNsVo{s{ww;wsso{sso{sso{sso{so{sso{o{so{sso{swHkZwZRo{NsckZcg9o{VcZg9^Ro{^kZRcZ^^Vcg9^ZZo{ZsZg9cVRw^^cZg9ZVcRNso{V^cZg9sVcVZ{VVg9VVRsZw^8wo{{ww{swo{ws{o{so{o{w{w{sswo{{wws{ws{{o{w{sww{sws{o{{g9sw{ o{{wso{w{s{w}7wVVkZg9g9^Zc=VsV^o{VRNsVRwg9Zg9RZkZZRcZNsVo{g9kZVRNscNsVZF1Nsg9g9VZNs^kZRo{F1cZkZ){ww{{w{TNAMACB71FEE5D207786E09EE7BBFE3736CPRT-.s com.apple.print.PrintSettings.PMColorMode com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PrintSettings.PMColorMode 3 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2003-06-17T17:46:54Z com.apple.print.ticket.stateFlag 0 com.apple.print.PrintSettings.PMCopies com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PrintSettings.PMCopies 1 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2003-06-17T17:46:54Z com.apple.print.ticket.stateFlag 0 com.apple.print.PrintSettings.PMDestinationType com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PrintSettings.PMDestinationType 1 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2003-06-17T17:46:54Z com.apple.print.ticket.stateFlag 0 com.apple.print.PrintSettings.PMFirstPage com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PrintSettings.PMFirstPage 1 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2003-06-17T17:46:54Z com.apple.print.ticket.stateFlag 0 com.apple.print.PrintSettings.PMLastPage com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PrintSettings.PMLastPage 9999 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2003-06-17T17:46:54Z com.apple.print.ticket.stateFlag 0 com.apple.print.PrintSettings.PMPageRange com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PrintSettings.PMPageRange 1 32000 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2003-06-17T17:46:54Z com.apple.print.ticket.stateFlag 0 com.apple.print.ticket.APIVersion 00.20 com.apple.print.ticket.privateLock com.apple.print.ticket.type com.apple.print.PrintSettingsTicket  com.apple.print.PageFormat.PMHorizontalRes com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMHorizontalRes 72 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2003-06-17T17:46:54Z com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMOrientation com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMOrientation 1 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2003-06-17T17:46:54Z com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMScaling com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMScaling 1 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2003-06-17T17:46:54Z com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMVerticalRes com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMVerticalRes 72 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2003-06-17T17:46:54Z com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMVerticalScaling com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMVerticalScaling 1 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2003-06-17T17:46:54Z com.apple.print.ticket.stateFlag 0 com.apple.print.subTicket.paper_info_ticket com.apple.print.PageFormat.PMAdjustedPageRect com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMAdjustedPageRect 0.0 0.0 767.8798828125 587.51995849609375 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2003-06-17T17:46:54Z com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMAdjustedPaperRect com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMAdjustedPaperRect -9.96002197265625 -11.520000457763672 782.03997802734375 600.47998046875 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2003-06-17T17:46:54Z com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMPaperName com.apple.print.ticket.creator CUPS_CPL com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMPaperName na-letter com.apple.print.ticket.client CUPS_CPL com.apple.print.ticket.modDate 2003-06-17T17:46:54Z com.apple.print.ticket.stateFlag 1 com.apple.print.PaperInfo.PMUnadjustedPageRect com.apple.print.ticket.creator CUPS_CPL com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMUnadjustedPageRect 0.0 0.0 767.8798828125 587.51995849609375 com.apple.print.ticket.client CUPS_CPL com.apple.print.ticket.modDate 2003-06-17T17:46:54Z com.apple.print.ticket.stateFlag 1 com.apple.print.PaperInfo.PMUnadjustedPaperRect com.apple.print.ticket.creator CUPS_CPL com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMUnadjustedPaperRect -9.96002197265625 -11.520000457763672 782.03997802734375 600.47998046875 com.apple.print.ticket.client CUPS_CPL com.apple.print.ticket.modDate 2003-06-17T17:46:54Z com.apple.print.ticket.stateFlag 1 com.apple.print.PaperInfo.ppd.PMPaperName com.apple.print.ticket.creator CUPS_CPL com.apple.print.ticket.itemArray com.apple.print.PaperInfo.ppd.PMPaperName Letter com.apple.print.ticket.client CUPS_CPL com.apple.print.ticket.modDate 2003-06-17T17:46:54Z com.apple.print.ticket.stateFlag 1 com.apple.print.ticket.APIVersion 00.20 com.apple.print.ticket.privateLock com.apple.print.ticket.type com.apple.print.PaperInfoTicket com.apple.print.ticket.APIVersion 00.20 com.apple.print.ticket.privateLock com.apple.print.ticket.type com.apple.print.PageFormatTicket ETBLhDSUMYMHDNIY}STYLYMCROoBlNMARKWMBTSNAP'TNAM`CPRTهETBL?\B