1/* 2 * UserData.c -- 3 * 4 * Implements the 'userdata' command. 5 * It is part of the QuickTimeTcl package which provides Tcl/Tk bindings for QuickTime. 6 * 7 * Copyright (c) 2003 Mats Bengtsson 8 * 9 * $Id: UserData.c,v 1.2 2003/11/28 13:38:42 matben Exp $ 10 */ 11 12#include "MoviePlayer.h" 13 14const UInt8 kUserDataIsText = 0xA9; // the copyright symbol 15 16/* 17 * Use hash table to map between human readable option names and long types 18 * for user data. 19 */ 20 21static Tcl_HashTable *gOptionToUserDataHashPtr = NULL; 22static Tcl_HashTable *gUserDataToOptionHashPtr = NULL; 23 24typedef struct UserDataLookupEntry { 25 char *option; 26 OSType udType; /* long */ 27} UserDataLookupEntry; 28 29UserDataLookupEntry gLookupTable[] = { 30 {"-album", kUserDataTextAlbum}, 31 {"-artist", kUserDataTextArtist}, 32 {"-author", kUserDataTextAuthor}, 33 {"-chapter", kUserDataTextChapter}, 34 {"-comment", kUserDataTextComment}, 35 {"-composer", kUserDataTextComposer}, 36 {"-copyright", kUserDataTextCopyright}, 37 {"-creationdate", kUserDataTextCreationDate}, 38 {"-description", kUserDataTextDescription}, 39 {"-director", kUserDataTextDirector}, 40 {"-disclaimer", kUserDataTextDisclaimer}, 41 {"-encodedby", kUserDataTextEncodedBy}, 42 {"-fullname", kUserDataTextFullName}, 43 {"-genre", kUserDataTextGenre}, 44 {"-hostcomputer", kUserDataTextHostComputer}, 45 {"-information", kUserDataTextInformation}, 46 {"-keywords", kUserDataTextKeywords}, 47 {"-make", kUserDataTextMake}, 48 {"-model", kUserDataTextModel}, 49 {"-name", kUserDataName}, 50 {"-originalartist", kUserDataTextOriginalArtist}, 51 {"-originalformat", kUserDataTextOriginalFormat}, 52 {"-originalsource", kUserDataTextOriginalSource}, 53 {"-performers", kUserDataTextPerformers}, 54 {"-producer", kUserDataTextProducer}, 55 {"-product", kUserDataTextProduct}, 56 {"-software", kUserDataTextSoftware}, 57 {"-specialplaybackrequirements", kUserDataTextSpecialPlaybackRequirements}, 58 {"-track", kUserDataTextTrack}, 59 {"-warning", kUserDataTextWarning}, 60 {"-writer", kUserDataTextWriter}, 61 {"-urllink", kUserDataTextURLLink}, 62 {"-editdate1", kUserDataTextEditDate1}, 63 {NULL, 0} 64}; 65 66static int SetupUserDataOptionHashTables( Tcl_Interp *interp ); 67 68 69/* 70 *---------------------------------------------------------------------- 71 * 72 * GetUserDataListCmd -- 73 * 74 * Process the "userdata" subcommand. 75 * 76 * Results: 77 * Normal TCL results 78 * 79 * Side effects: 80 * None. 81 * 82 *---------------------------------------------------------------------- 83 */ 84 85int 86GetUserDataListCmd( UserData userData, 87 Tcl_Interp *interp ) 88{ 89 Tcl_Obj *listObjPtr; 90 OSType udType; 91 short count, i; 92 char nul = 0; 93 char str8[8]; 94 char *charPtr; 95 unsigned long lType; 96 Handle hData = NULL; 97 Tcl_HashEntry *hashPtr = NULL; 98 Ptr p; 99 Tcl_DString ds; 100 OSErr err = noErr; 101 int result = TCL_OK; 102 103 if (gOptionToUserDataHashPtr == NULL) { 104 if (TCL_OK != SetupUserDataOptionHashTables( interp )) { 105 result = TCL_ERROR; 106 goto bail; 107 } 108 } 109 listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL ); 110 hData = NewHandle(0); 111 udType = GetNextUserDataType( userData, 0 ); 112 113 if(0 != udType) { 114 do { 115 count = CountUserDataType( userData, udType ); 116 for(i = 1; i <= count; i++) { 117 if(((udType>>24) == kUserDataIsText) || (udType == kUserDataName)) { 118 119 /* 120 * If the first letter of udType is 0xA9 121 * (the copyright symbol), then use GetUserDataText instead 122 * of GetUserData. See Movies.h for a list of interesting 123 * user data types. 124 */ 125 126 err = GetUserDataText( userData, hData, udType, i, langEnglish ); 127 if (err != noErr) { 128 CheckAndSetErrorResult( interp, err ); 129 goto bail; 130 } 131 132 /* null-terminate the string in the handle */ 133 PtrAndHand( &nul, hData, 1 ); 134 135 /* turn any CRs into spaces */ 136 p = *hData; 137 while (*p) { 138 if (*p == kReturnCharCode) { 139 *p = ' '; 140 } 141 p++; 142 }; 143 144 /* 145 * Find any option string in hash table for this ud type. 146 */ 147 148 hashPtr = Tcl_FindHashEntry( gUserDataToOptionHashPtr, 149 (char *) udType ); 150 if (hashPtr != NULL) { 151 charPtr = (char *) Tcl_GetHashValue( hashPtr ); 152 Tcl_ListObjAppendElement( interp, listObjPtr, 153 Tcl_NewStringObj(charPtr, -1) ); 154 } else { 155 lType = EndianU32_BtoN( udType ); 156 memset( str8, 0, 8 ); 157 strcat( str8, "-" ); 158 memcpy( str8 + 1, &lType, 4 ); 159 Tcl_ListObjAppendElement( interp, listObjPtr, 160 Tcl_NewStringObj(str8, -1) ); 161 } 162 163 HLock( hData ); 164 charPtr = Tcl_ExternalToUtfDString( gQTTclTranslationEncoding, *hData, -1, &ds ); 165 Tcl_ListObjAppendElement( interp, listObjPtr, 166 Tcl_NewStringObj(charPtr, Tcl_DStringLength(&ds)) ); 167 HUnlock( hData ); 168 } else { 169 170 /* Non text user data. */ 171 err = GetUserData( userData, hData, udType, i ); 172 if (err != noErr) { 173 CheckAndSetErrorResult( interp, err ); 174 result = TCL_ERROR; 175 goto bail; 176 } 177 178 lType = EndianU32_BtoN( udType ); 179 memset( str8, 0, 8 ); 180 strcat( str8, "-" ); 181 memcpy( str8 + 1, &lType, 4 ); 182 Tcl_ListObjAppendElement( interp, listObjPtr, 183 Tcl_NewStringObj(str8, -1) ); 184 HLock( hData ); 185 Tcl_ListObjAppendElement( interp, listObjPtr, 186 Tcl_NewStringObj( *hData, GetHandleSize(hData) ) ); 187 HUnlock( hData ); 188 } 189 } 190 udType = GetNextUserDataType( userData, udType ); 191 192 } while (0 != udType); 193 } 194 195 Tcl_SetObjResult( interp, listObjPtr ); 196 197bail: 198 if (hData != NULL) { 199 DisposeHandle( hData ); 200 } 201 return result; 202} 203 204/* 205 *---------------------------------------------------------------------- 206 * 207 * SetUserDataListCmd -- 208 * 209 * Process the "userdata" subcommand. 210 * 211 * Results: 212 * Normal TCL results 213 * 214 * Side effects: 215 * Depends on the command. 216 * 217 *---------------------------------------------------------------------- 218 */ 219 220int 221SetUserDataListCmd( UserData userData, 222 Tcl_Interp *interp, 223 int objc, 224 Tcl_Obj *CONST objv[] ) 225{ 226 int iarg; 227 OSType udType; 228 Handle hData = NULL; 229 char *charPtr; 230 int isTextUserData; 231 int len; 232 unsigned long lType; 233 Tcl_DString ds; 234 Tcl_HashEntry *hashPtr = NULL; 235 OSErr err = noErr; 236 int result = TCL_OK; 237 238 if (gOptionToUserDataHashPtr == NULL) { 239 if (SetupUserDataOptionHashTables( interp ) != TCL_OK) { 240 result = TCL_ERROR; 241 goto bail; 242 } 243 } 244 245 for (iarg = 0; iarg < objc; iarg += 2) { 246 hashPtr = Tcl_FindHashEntry( gOptionToUserDataHashPtr, 247 Tcl_GetString( objv[iarg] ) ); 248 if (hashPtr != NULL) { 249 udType = (OSType) Tcl_GetHashValue( hashPtr ); 250 isTextUserData = true; 251 } else { 252 253 /* 254 * The argument did not match any of the predefined options. 255 * UTF translation first! 256 */ 257 258 charPtr = Tcl_UtfToExternalDString( gQTTclTranslationEncoding, 259 Tcl_GetString( objv[iarg] ), -1, &ds ); 260 if ((charPtr[0] != '-') || (Tcl_DStringLength(&ds) != 5)) { 261 Tcl_SetObjResult( interp, Tcl_NewStringObj( 262 "userdata type option must be -FOUR if not predefined types", -1 ) ); 263 result = TCL_ERROR; 264 goto bail; 265 } 266 memcpy( &lType, charPtr+1, 4 ); 267 udType = EndianU32_NtoB( lType ); 268 if((udType>>24) == kUserDataIsText) { 269 isTextUserData = true; 270 } else { 271 isTextUserData = false; 272 } 273 } 274 275 /* Get rid of any old before setting the new one. */ 276 if (isTextUserData) { 277 while (RemoveUserDataText( userData, udType, 1, langEnglish ) == noErr) 278 ; 279 charPtr = Tcl_UtfToExternalDString( gQTTclTranslationEncoding, 280 Tcl_GetString( objv[iarg+1] ), -1, &ds ); 281 len = Tcl_DStringLength(&ds); 282 PtrToHand( charPtr, &hData, len + 1 ); 283 HLock( hData ); 284 err = AddUserDataText( userData, hData, udType, 1, langEnglish ); 285 HUnlock( hData ); 286 Tcl_DStringFree( &ds ); 287 } else { 288 while (RemoveUserData( userData, udType, 1 ) == noErr) 289 ; 290 charPtr = Tcl_GetStringFromObj( objv[iarg+1], &len ); 291 err = SetUserDataItem( userData, charPtr, len, udType, 1 ); 292 } 293 if (err != noErr) { 294 CheckAndSetErrorResult( interp, err ); 295 result = TCL_ERROR; 296 goto bail; 297 } 298 } 299 300bail: 301 302 return result; 303} 304 305/* 306 *---------------------------------------------------------------------- 307 * 308 * SetupUserDataOptionHashTables -- 309 * 310 * Sets up two hash tables from User Data types to option strings. 311 * 312 * Results: 313 * Normal TCL results 314 * 315 * Side effects: 316 * hash tables made. 317 * 318 *---------------------------------------------------------------------- 319 */ 320 321static int 322SetupUserDataOptionHashTables( Tcl_Interp *interp ) 323{ 324 Tcl_HashEntry *hPtr; 325 UserDataLookupEntry *entryPtr; 326 int isNew; 327 int result = TCL_OK; 328 329 /* 330 * Map from option string to User Data Type (long). 331 */ 332 333 gOptionToUserDataHashPtr = (Tcl_HashTable *) ckalloc( sizeof(Tcl_HashTable) ); 334 Tcl_InitHashTable( gOptionToUserDataHashPtr, TCL_STRING_KEYS ); 335 for (entryPtr = &gLookupTable[0]; entryPtr->option != NULL; entryPtr++) { 336 hPtr = Tcl_CreateHashEntry( gOptionToUserDataHashPtr, 337 entryPtr->option, &isNew ); 338 Tcl_SetHashValue( hPtr, entryPtr->udType ); 339 } 340 341 /* 342 * Map from User Data Type (long) to option string. 343 */ 344 345 gUserDataToOptionHashPtr = (Tcl_HashTable *) ckalloc( sizeof(Tcl_HashTable) ); 346 Tcl_InitHashTable( gUserDataToOptionHashPtr, TCL_ONE_WORD_KEYS ); 347 for (entryPtr = &gLookupTable[0]; entryPtr->option != NULL; entryPtr++) { 348 hPtr = Tcl_CreateHashEntry( gUserDataToOptionHashPtr, 349 (char *) entryPtr->udType, &isNew ); 350 Tcl_SetHashValue( hPtr, entryPtr->option ); 351 } 352 return result; 353} 354 355void 356UserDataHashTablesFree( void ) 357{ 358 if (gOptionToUserDataHashPtr != NULL) { 359 Tcl_DeleteHashTable( gOptionToUserDataHashPtr ); 360 ckfree( (char *) gOptionToUserDataHashPtr ); 361 } 362 if (gUserDataToOptionHashPtr != NULL) { 363 Tcl_DeleteHashTable( gUserDataToOptionHashPtr ); 364 ckfree( (char *) gUserDataToOptionHashPtr ); 365 } 366} 367 368/*---------------------------------------------------------------------------*/