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