1/* 2 * tkFileFilter.c -- 3 * 4 * Process the -filetypes option for the file dialogs on Windows and the 5 * Mac. 6 * 7 * Copyright (c) 1996 Sun Microsystems, Inc. 8 * 9 * See the file "license.terms" for information on usage and redistribution of 10 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * RCS: @(#) $Id$ 13 */ 14 15#include "tkInt.h" 16#include "tkFileFilter.h" 17 18static int AddClause(Tcl_Interp *interp, 19 FileFilter *filterPtr, Tcl_Obj *patternsObj, 20 Tcl_Obj *ostypesObj, int isWindows); 21static void FreeClauses(FileFilter *filterPtr); 22static void FreeGlobPatterns(FileFilterClause *clausePtr); 23static void FreeMacFileTypes(FileFilterClause *clausePtr); 24static FileFilter * GetFilter(FileFilterList *flistPtr, CONST char *name); 25 26/* 27 *---------------------------------------------------------------------- 28 * 29 * TkInitFileFilters -- 30 * 31 * Initializes a FileFilterList data structure. A FileFilterList must be 32 * initialized EXACTLY ONCE before any calls to TkGetFileFilters() is 33 * made. The usual flow of control is: 34 * TkInitFileFilters(&flist); 35 * TkGetFileFilters(&flist, ...); 36 * TkGetFileFilters(&flist, ...); 37 * ... 38 * TkFreeFileFilters(&flist); 39 * 40 * Results: 41 * None. 42 * 43 * Side effects: 44 * The fields in flistPtr are initialized. 45 * 46 *---------------------------------------------------------------------- 47 */ 48 49void 50TkInitFileFilters( 51 FileFilterList *flistPtr) /* The structure to be initialized. */ 52{ 53 flistPtr->filters = NULL; 54 flistPtr->filtersTail = NULL; 55 flistPtr->numFilters = 0; 56} 57 58/* 59 *---------------------------------------------------------------------- 60 * 61 * TkGetFileFilters -- 62 * 63 * This function is called by the Mac and Windows implementation of 64 * tk_getOpenFile and tk_getSaveFile to translate the string value of the 65 * -filetypes option into an easy-to-parse C structure (flistPtr). The 66 * caller of this function will then use flistPtr to perform filetype 67 * matching in a platform specific way. 68 * 69 * flistPtr must be initialized (See comments in TkInitFileFilters). 70 * 71 * Results: 72 * A standard TCL return value. 73 * 74 * Side effects: 75 * The fields in flistPtr are changed according to 'types'. 76 * 77 *---------------------------------------------------------------------- 78 */ 79 80int 81TkGetFileFilters( 82 Tcl_Interp *interp, /* Interpreter to use for error reporting. */ 83 FileFilterList *flistPtr, /* Stores the list of file filters. */ 84 Tcl_Obj *types, /* Value of the -filetypes option. */ 85 int isWindows) /* True if we are running on Windows. */ 86{ 87 int listObjc; 88 Tcl_Obj ** listObjv = NULL; 89 int i; 90 91 if (types == NULL) { 92 return TCL_OK; 93 } 94 95 if (Tcl_ListObjGetElements(interp, types, &listObjc, 96 &listObjv) != TCL_OK) { 97 return TCL_ERROR; 98 } 99 if (listObjc == 0) { 100 return TCL_OK; 101 } 102 103 /* 104 * Free the filter information that have been allocated the previous time; 105 * the -filefilters option may have been used more than once in the 106 * command line. 107 */ 108 TkFreeFileFilters(flistPtr); 109 110 for (i = 0; i<listObjc; i++) { 111 /* 112 * Each file type should have two or three elements: the first one is 113 * the name of the type and the second is the filter of the type. The 114 * third is the Mac OSType ID, but we don't care about them here. 115 */ 116 117 int count; 118 FileFilter *filterPtr; 119 Tcl_Obj **typeInfo; 120 121 if (Tcl_ListObjGetElements(interp, listObjv[i], &count, 122 &typeInfo) != TCL_OK) { 123 return TCL_ERROR; 124 } 125 126 if (count != 2 && count != 3) { 127 Tcl_AppendResult(interp, "bad file type \"", 128 Tcl_GetString(listObjv[i]), "\", ", 129 "should be \"typeName {extension ?extensions ...?} ", 130 "?{macType ?macTypes ...?}?\"", NULL); 131 return TCL_ERROR; 132 } 133 134 filterPtr = GetFilter(flistPtr, Tcl_GetString(typeInfo[0])); 135 136 if (AddClause(interp, filterPtr, typeInfo[1], 137 (count==2 ? NULL : typeInfo[2]), isWindows) != TCL_OK) { 138 return TCL_ERROR; 139 } 140 } 141 142 return TCL_OK; 143} 144 145/* 146 *---------------------------------------------------------------------- 147 * 148 * TkFreeFileFilters -- 149 * 150 * Frees the malloc'ed file filter information. 151 * 152 * Results: 153 * None. 154 * 155 * Side effects: 156 * The fields allocated by TkGetFileFilters() are freed. 157 * 158 *---------------------------------------------------------------------- 159 */ 160 161void 162TkFreeFileFilters( 163 FileFilterList *flistPtr) /* List of file filters to free */ 164{ 165 FileFilter *filterPtr, *toFree; 166 167 filterPtr=flistPtr->filters; 168 while (filterPtr != NULL) { 169 toFree = filterPtr; 170 filterPtr = filterPtr->next; 171 FreeClauses(toFree); 172 ckfree((char*)toFree->name); 173 ckfree((char*)toFree); 174 } 175 flistPtr->filters = NULL; 176} 177 178/* 179 *---------------------------------------------------------------------- 180 * 181 * AddClause -- 182 * 183 * Add one FileFilterClause to filterPtr. 184 * 185 * Results: 186 * A standard TCL result. 187 * 188 * Side effects: 189 * The list of filter clauses are updated in filterPtr. 190 * 191 *---------------------------------------------------------------------- 192 */ 193 194static int 195AddClause( 196 Tcl_Interp *interp, /* Interpreter to use for error reporting. */ 197 FileFilter *filterPtr, /* Stores the new filter clause */ 198 Tcl_Obj *patternsObj, /* A Tcl list of glob patterns. */ 199 Tcl_Obj *ostypesObj, /* A Tcl list of Mac OSType strings. */ 200 int isWindows) /* True if we are running on Windows; False if 201 * we are running on the Mac; Glob patterns 202 * need to be processed differently on these 203 * two platforms */ 204{ 205 Tcl_Obj **globList = NULL, **ostypeList = NULL; 206 int globCount, ostypeCount, i, code = TCL_OK; 207 FileFilterClause *clausePtr; 208 Tcl_Encoding macRoman = NULL; 209 210 if (Tcl_ListObjGetElements(interp, patternsObj, 211 &globCount, &globList) != TCL_OK) { 212 code = TCL_ERROR; 213 goto done; 214 } 215 if (ostypesObj != NULL) { 216 if (Tcl_ListObjGetElements(interp, ostypesObj, 217 &ostypeCount, &ostypeList) != TCL_OK) { 218 code = TCL_ERROR; 219 goto done; 220 } 221 222 /* 223 * We probably need this encoding now... 224 */ 225 226 macRoman = Tcl_GetEncoding(NULL, "macRoman"); 227 228 /* 229 * Might be cleaner to use 'Tcl_GetOSTypeFromObj' but that is actually 230 * static to the MacOS X/Darwin version of Tcl, and would therefore 231 * require further code refactoring. 232 */ 233 234 for (i=0; i<ostypeCount; i++) { 235 int len; 236 CONST char *strType = Tcl_GetStringFromObj(ostypeList[i], &len); 237 238 /* 239 * If len is < 4, it is definitely an error. If equal or longer, 240 * we need to use the macRoman encoding to determine the correct 241 * length (assuming there may be non-ascii characters, e.g., 242 * embedded nulls or accented characters in the string, the 243 * macRoman length will be different). 244 * 245 * If we couldn't load the encoding, then we can't actually check 246 * the correct length. But here we assume we're probably operating 247 * on unix/windows with a minimal set of encodings and so don't 248 * care about MacOS types. So we won't signal an error. 249 */ 250 251 if (len >= 4 && macRoman != NULL) { 252 Tcl_DString osTypeDS; 253 254 /* 255 * Convert utf to macRoman, since MacOS types are defined to 256 * be 4 macRoman characters long 257 */ 258 259 Tcl_UtfToExternalDString(macRoman, strType, len, &osTypeDS); 260 len = Tcl_DStringLength(&osTypeDS); 261 Tcl_DStringFree(&osTypeDS); 262 } 263 if (len != 4) { 264 Tcl_AppendResult(interp, "bad Macintosh file type \"", 265 Tcl_GetString(ostypeList[i]), "\"", NULL); 266 code = TCL_ERROR; 267 goto done; 268 } 269 } 270 } 271 272 /* 273 * Add the clause into the list of clauses 274 */ 275 276 clausePtr = (FileFilterClause*)ckalloc(sizeof(FileFilterClause)); 277 clausePtr->patterns = NULL; 278 clausePtr->patternsTail = NULL; 279 clausePtr->macTypes = NULL; 280 clausePtr->macTypesTail = NULL; 281 282 if (filterPtr->clauses == NULL) { 283 filterPtr->clauses = filterPtr->clausesTail = clausePtr; 284 } else { 285 filterPtr->clausesTail->next = clausePtr; 286 filterPtr->clausesTail = clausePtr; 287 } 288 clausePtr->next = NULL; 289 290 if (globCount > 0 && globList != NULL) { 291 for (i=0; i<globCount; i++) { 292 GlobPattern *globPtr = (GlobPattern*)ckalloc(sizeof(GlobPattern)); 293 int len; 294 295 CONST char *str = Tcl_GetStringFromObj(globList[i], &len); 296 len = (len + 1) * sizeof(char); 297 298 if (str[0] && str[0] != '*') { 299 /* 300 * Prepend a "*" to patterns that do not have a leading "*" 301 */ 302 303 globPtr->pattern = (char*)ckalloc((unsigned int) len+1); 304 globPtr->pattern[0] = '*'; 305 strcpy(globPtr->pattern+1, str); 306 } else if (isWindows) { 307 if (strcmp(str, "*") == 0) { 308 globPtr->pattern = (char*)ckalloc(4 * sizeof(char)); 309 strcpy(globPtr->pattern, "*.*"); 310 } else if (strcmp(str, "") == 0) { 311 /* 312 * An empty string means "match all files with no 313 * extensions" 314 * BUG: "*." actually matches with all files on Win95 315 */ 316 317 globPtr->pattern = (char *) ckalloc(3 * sizeof(char)); 318 strcpy(globPtr->pattern, "*."); 319 } else { 320 globPtr->pattern = (char *) ckalloc((unsigned int) len); 321 strcpy(globPtr->pattern, str); 322 } 323 } else { 324 globPtr->pattern = (char *) ckalloc((unsigned int) len); 325 strcpy(globPtr->pattern, str); 326 } 327 328 /* 329 * Add the glob pattern into the list of patterns. 330 */ 331 332 if (clausePtr->patterns == NULL) { 333 clausePtr->patterns = clausePtr->patternsTail = globPtr; 334 } else { 335 clausePtr->patternsTail->next = globPtr; 336 clausePtr->patternsTail = globPtr; 337 } 338 globPtr->next = NULL; 339 } 340 } 341 if (ostypeList != NULL && ostypeCount > 0) { 342 if (macRoman == NULL) { 343 macRoman = Tcl_GetEncoding(NULL, "macRoman"); 344 } 345 for (i=0; i<ostypeCount; i++) { 346 Tcl_DString osTypeDS; 347 int len; 348 MacFileType *mfPtr = (MacFileType *) ckalloc(sizeof(MacFileType)); 349 CONST char *strType = Tcl_GetStringFromObj(ostypeList[i], &len); 350 char *string; 351 352 /* 353 * Convert utf to macRoman, since MacOS types are defined to be 4 354 * macRoman characters long 355 */ 356 357 Tcl_UtfToExternalDString(macRoman, strType, len, &osTypeDS); 358 string = Tcl_DStringValue(&osTypeDS); 359 mfPtr->type = (OSType) string[0] << 24 | (OSType) string[1] << 16 | 360 (OSType) string[2] << 8 | (OSType) string[3]; 361 Tcl_DStringFree(&osTypeDS); 362 363 /* 364 * Add the Mac type pattern into the list of Mac types 365 */ 366 367 if (clausePtr->macTypes == NULL) { 368 clausePtr->macTypes = clausePtr->macTypesTail = mfPtr; 369 } else { 370 clausePtr->macTypesTail->next = mfPtr; 371 clausePtr->macTypesTail = mfPtr; 372 } 373 mfPtr->next = NULL; 374 } 375 } 376 377 done: 378 if (macRoman != NULL) { 379 Tcl_FreeEncoding(macRoman); 380 } 381 return code; 382} 383 384/* 385 *---------------------------------------------------------------------- 386 * 387 * GetFilter -- 388 * 389 * Add one FileFilter to flistPtr. 390 * 391 * Results: 392 * A standard TCL result. 393 * 394 * Side effects: 395 * The list of filters are updated in flistPtr. 396 * 397 *---------------------------------------------------------------------- 398 */ 399 400static FileFilter * 401GetFilter( 402 FileFilterList *flistPtr, /* The FileFilterList that contains the newly 403 * created filter */ 404 CONST char *name) /* Name of the filter. It is usually displayed 405 * in the "File Types" listbox in the file 406 * dialogs. */ 407{ 408 FileFilter *filterPtr = flistPtr->filters; 409 410 for (; filterPtr; filterPtr=filterPtr->next) { 411 if (strcmp(filterPtr->name, name) == 0) { 412 return filterPtr; 413 } 414 } 415 416 filterPtr = (FileFilter *) ckalloc(sizeof(FileFilter)); 417 filterPtr->clauses = NULL; 418 filterPtr->clausesTail = NULL; 419 filterPtr->name = (char *) ckalloc((strlen(name)+1) * sizeof(char)); 420 strcpy(filterPtr->name, name); 421 422 if (flistPtr->filters == NULL) { 423 flistPtr->filters = flistPtr->filtersTail = filterPtr; 424 } else { 425 flistPtr->filtersTail->next = filterPtr; 426 flistPtr->filtersTail = filterPtr; 427 } 428 filterPtr->next = NULL; 429 430 ++flistPtr->numFilters; 431 return filterPtr; 432} 433 434/* 435 *---------------------------------------------------------------------- 436 * 437 * FreeClauses -- 438 * 439 * Frees the malloc'ed file type clause 440 * 441 * Results: 442 * None. 443 * 444 * Side effects: 445 * The list of clauses in filterPtr->clauses are freed. 446 * 447 *---------------------------------------------------------------------- 448 */ 449 450static void 451FreeClauses( 452 FileFilter *filterPtr) /* FileFilter whose clauses are to be freed */ 453{ 454 FileFilterClause *clausePtr = filterPtr->clauses; 455 456 while (clausePtr != NULL) { 457 FileFilterClause *toFree = clausePtr; 458 clausePtr = clausePtr->next; 459 460 FreeGlobPatterns(toFree); 461 FreeMacFileTypes(toFree); 462 ckfree((char *) toFree); 463 } 464 filterPtr->clauses = NULL; 465 filterPtr->clausesTail = NULL; 466} 467 468/* 469 *---------------------------------------------------------------------- 470 * 471 * FreeGlobPatterns -- 472 * 473 * Frees the malloc'ed glob patterns in a clause 474 * 475 * Results: 476 * None. 477 * 478 * Side effects: 479 * The list of glob patterns in clausePtr->patterns are freed. 480 * 481 *---------------------------------------------------------------------- 482 */ 483 484static void 485FreeGlobPatterns( 486 FileFilterClause *clausePtr)/* The clause whose patterns are to be freed*/ 487{ 488 GlobPattern *globPtr = clausePtr->patterns; 489 490 while (globPtr != NULL) { 491 GlobPattern *toFree = globPtr; 492 globPtr = globPtr->next; 493 494 ckfree((char *) toFree->pattern); 495 ckfree((char *) toFree); 496 } 497 clausePtr->patterns = NULL; 498} 499 500/* 501 *---------------------------------------------------------------------- 502 * 503 * FreeMacFileTypes -- 504 * 505 * Frees the malloc'ed Mac file types in a clause 506 * 507 * Results: 508 * None. 509 * 510 * Side effects: 511 * The list of Mac file types in clausePtr->macTypes are freed. 512 * 513 *---------------------------------------------------------------------- 514 */ 515 516static void 517FreeMacFileTypes( 518 FileFilterClause *clausePtr)/* The clause whose mac types are to be 519 * freed */ 520{ 521 MacFileType *mfPtr = clausePtr->macTypes; 522 523 while (mfPtr != NULL) { 524 MacFileType *toFree = mfPtr; 525 mfPtr = mfPtr->next; 526 ckfree((char *) toFree); 527 } 528 clausePtr->macTypes = NULL; 529} 530 531/* 532 * Local Variables: 533 * mode: c 534 * c-basic-offset: 4 535 * fill-column: 78 536 * End: 537 */ 538