1/* 2 * "$Id: filter.c 11093 2013-07-03 20:48:42Z msweet $" 3 * 4 * File type conversion routines for CUPS. 5 * 6 * Copyright 2007-2011 by Apple Inc. 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved. 8 * 9 * These coded instructions, statements, and computer programs are the 10 * property of Apple Inc. and are protected by Federal copyright 11 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 12 * which should have been included with this file. If this file is 13 * file is missing or damaged, see the license at "http://www.cups.org/". 14 * 15 * Contents: 16 * 17 * mimeAddFilter() - Add a filter to the current MIME database. 18 * mimeFilter() - Find the fastest way to convert from one type to 19 * another. 20 * mimeFilter2() - Find the fastest way to convert from one type to 21 * another, including the file size. 22 * mimeFilterLookup() - Lookup a filter. 23 * mime_compare_filters() - Compare two filters. 24 * mime_compare_srcs() - Compare two filter source types. 25 * mime_find_filters() - Find the filters to convert from one type to 26 * another. 27 */ 28 29/* 30 * Include necessary headers... 31 */ 32 33#include <cups/string-private.h> 34#include <cups/debug-private.h> 35#include "mime.h" 36 37 38/* 39 * Local types... 40 */ 41 42typedef struct _mime_typelist_s /**** List of source types ****/ 43{ 44 struct _mime_typelist_s *next; /* Next source type */ 45 mime_type_t *src; /* Source type */ 46} _mime_typelist_t; 47 48 49/* 50 * Local functions... 51 */ 52 53static int mime_compare_filters(mime_filter_t *, mime_filter_t *); 54static int mime_compare_srcs(mime_filter_t *, mime_filter_t *); 55static cups_array_t *mime_find_filters(mime_t *mime, mime_type_t *src, 56 size_t srcsize, mime_type_t *dst, 57 int *cost, _mime_typelist_t *visited); 58 59 60/* 61 * 'mimeAddFilter()' - Add a filter to the current MIME database. 62 */ 63 64mime_filter_t * /* O - New filter */ 65mimeAddFilter(mime_t *mime, /* I - MIME database */ 66 mime_type_t *src, /* I - Source type */ 67 mime_type_t *dst, /* I - Destination type */ 68 int cost, /* I - Relative time/resource cost */ 69 const char *filter) /* I - Filter program to run */ 70{ 71 mime_filter_t *temp; /* New filter */ 72 73 74 DEBUG_printf(("mimeAddFilter(mime=%p, src=%p(%s/%s), dst=%p(%s/%s), cost=%d, " 75 "filter=\"%s\")", mime, 76 src, src ? src->super : "???", src ? src->type : "???", 77 dst, dst ? dst->super : "???", dst ? dst->type : "???", 78 cost, filter)); 79 80 /* 81 * Range-check the input... 82 */ 83 84 if (!mime || !src || !dst || !filter) 85 { 86 DEBUG_puts("1mimeAddFilter: Returning NULL."); 87 return (NULL); 88 } 89 90 /* 91 * See if we already have an existing filter for the given source and 92 * destination... 93 */ 94 95 if ((temp = mimeFilterLookup(mime, src, dst)) != NULL) 96 { 97 /* 98 * Yup, does the existing filter have a higher cost? If so, copy the 99 * filter and cost to the existing filter entry and return it... 100 */ 101 102 if (temp->cost > cost) 103 { 104 DEBUG_printf(("1mimeAddFilter: Replacing filter \"%s\", cost %d.", 105 temp->filter, temp->cost)); 106 temp->cost = cost; 107 strlcpy(temp->filter, filter, sizeof(temp->filter)); 108 } 109 } 110 else 111 { 112 /* 113 * Nope, add a new one... 114 */ 115 116 if (!mime->filters) 117 mime->filters = cupsArrayNew((cups_array_func_t)mime_compare_filters, NULL); 118 119 if (!mime->filters) 120 return (NULL); 121 122 if ((temp = calloc(1, sizeof(mime_filter_t))) == NULL) 123 return (NULL); 124 125 /* 126 * Copy the information over and sort if necessary... 127 */ 128 129 temp->src = src; 130 temp->dst = dst; 131 temp->cost = cost; 132 strlcpy(temp->filter, filter, sizeof(temp->filter)); 133 134 DEBUG_puts("1mimeAddFilter: Adding new filter."); 135 cupsArrayAdd(mime->filters, temp); 136 cupsArrayAdd(mime->srcs, temp); 137 } 138 139 /* 140 * Return the new/updated filter... 141 */ 142 143 DEBUG_printf(("1mimeAddFilter: Returning %p.", temp)); 144 145 return (temp); 146} 147 148 149/* 150 * 'mimeFilter()' - Find the fastest way to convert from one type to another. 151 */ 152 153cups_array_t * /* O - Array of filters to run */ 154mimeFilter(mime_t *mime, /* I - MIME database */ 155 mime_type_t *src, /* I - Source file type */ 156 mime_type_t *dst, /* I - Destination file type */ 157 int *cost) /* O - Cost of filters */ 158{ 159 DEBUG_printf(("mimeFilter(mime=%p, src=%p(%s/%s), dst=%p(%s/%s), " 160 "cost=%p(%d))", mime, 161 src, src ? src->super : "???", src ? src->type : "???", 162 dst, dst ? dst->super : "???", dst ? dst->type : "???", 163 cost, cost ? *cost : 0)); 164 165 return (mimeFilter2(mime, src, 0, dst, cost)); 166} 167 168 169/* 170 * 'mimeFilter2()' - Find the fastest way to convert from one type to another, 171 * including file size. 172 */ 173 174cups_array_t * /* O - Array of filters to run */ 175mimeFilter2(mime_t *mime, /* I - MIME database */ 176 mime_type_t *src, /* I - Source file type */ 177 size_t srcsize, /* I - Size of source file */ 178 mime_type_t *dst, /* I - Destination file type */ 179 int *cost) /* O - Cost of filters */ 180{ 181 cups_array_t *filters; /* Array of filters to run */ 182 183 184 /* 185 * Range-check the input... 186 */ 187 188 DEBUG_printf(("mimeFilter2(mime=%p, src=%p(%s/%s), srcsize=" CUPS_LLFMT 189 ", dst=%p(%s/%s), cost=%p(%d))", mime, 190 src, src ? src->super : "???", src ? src->type : "???", 191 CUPS_LLCAST srcsize, 192 dst, dst ? dst->super : "???", dst ? dst->type : "???", 193 cost, cost ? *cost : 0)); 194 195 if (cost) 196 *cost = 0; 197 198 if (!mime || !src || !dst) 199 return (NULL); 200 201 /* 202 * (Re)build the source lookup array as needed... 203 */ 204 205 if (!mime->srcs) 206 { 207 mime_filter_t *current; /* Current filter */ 208 209 mime->srcs = cupsArrayNew((cups_array_func_t)mime_compare_srcs, NULL); 210 211 for (current = mimeFirstFilter(mime); 212 current; 213 current = mimeNextFilter(mime)) 214 cupsArrayAdd(mime->srcs, current); 215 } 216 217 /* 218 * Find the filters... 219 */ 220 221 filters = mime_find_filters(mime, src, srcsize, dst, cost, NULL); 222 223 DEBUG_printf(("1mimeFilter2: Returning %d filter(s), cost %d:", 224 cupsArrayCount(filters), cost ? *cost : -1)); 225#ifdef DEBUG 226 { 227 mime_filter_t *filter; /* Current filter */ 228 229 for (filter = (mime_filter_t *)cupsArrayFirst(filters); 230 filter; 231 filter = (mime_filter_t *)cupsArrayNext(filters)) 232 DEBUG_printf(("1mimeFilter2: %s/%s %s/%s %d %s", filter->src->super, 233 filter->src->type, filter->dst->super, filter->dst->type, 234 filter->cost, filter->filter)); 235 } 236#endif /* DEBUG */ 237 238 return (filters); 239} 240 241 242/* 243 * 'mimeFilterLookup()' - Lookup a filter. 244 */ 245 246mime_filter_t * /* O - Filter for src->dst */ 247mimeFilterLookup(mime_t *mime, /* I - MIME database */ 248 mime_type_t *src, /* I - Source type */ 249 mime_type_t *dst) /* I - Destination type */ 250{ 251 mime_filter_t key, /* Key record for filter search */ 252 *filter; /* Matching filter */ 253 254 255 DEBUG_printf(("2mimeFilterLookup(mime=%p, src=%p(%s/%s), dst=%p(%s/%s))", mime, 256 src, src ? src->super : "???", src ? src->type : "???", 257 dst, dst ? dst->super : "???", dst ? dst->type : "???")); 258 259 key.src = src; 260 key.dst = dst; 261 262 filter = (mime_filter_t *)cupsArrayFind(mime->filters, &key); 263 DEBUG_printf(("3mimeFilterLookup: Returning %p(%s).", filter, 264 filter ? filter->filter : "???")); 265 return (filter); 266} 267 268 269/* 270 * 'mime_compare_filters()' - Compare two filters. 271 */ 272 273static int /* O - Comparison result */ 274mime_compare_filters(mime_filter_t *f0, /* I - First filter */ 275 mime_filter_t *f1) /* I - Second filter */ 276{ 277 int i; /* Result of comparison */ 278 279 280 if ((i = strcmp(f0->src->super, f1->src->super)) == 0) 281 if ((i = strcmp(f0->src->type, f1->src->type)) == 0) 282 if ((i = strcmp(f0->dst->super, f1->dst->super)) == 0) 283 i = strcmp(f0->dst->type, f1->dst->type); 284 285 return (i); 286} 287 288 289/* 290 * 'mime_compare_srcs()' - Compare two filter source types. 291 */ 292 293static int /* O - Comparison result */ 294mime_compare_srcs(mime_filter_t *f0, /* I - First filter */ 295 mime_filter_t *f1) /* I - Second filter */ 296{ 297 int i; /* Result of comparison */ 298 299 300 if ((i = strcmp(f0->src->super, f1->src->super)) == 0) 301 i = strcmp(f0->src->type, f1->src->type); 302 303 return (i); 304} 305 306 307/* 308 * 'mime_find_filters()' - Find the filters to convert from one type to another. 309 */ 310 311static cups_array_t * /* O - Array of filters to run */ 312mime_find_filters( 313 mime_t *mime, /* I - MIME database */ 314 mime_type_t *src, /* I - Source file type */ 315 size_t srcsize, /* I - Size of source file */ 316 mime_type_t *dst, /* I - Destination file type */ 317 int *cost, /* O - Cost of filters */ 318 _mime_typelist_t *list) /* I - Source types we've used */ 319{ 320 int tempcost, /* Temporary cost */ 321 mincost; /* Current minimum */ 322 cups_array_t *temp, /* Temporary filter */ 323 *mintemp; /* Current minimum */ 324 mime_filter_t *current, /* Current filter */ 325 srckey; /* Source type key */ 326 _mime_typelist_t listnode, /* New list node */ 327 *listptr; /* Pointer in list */ 328 329 330 DEBUG_printf(("2mime_find_filters(mime=%p, src=%p(%s/%s), srcsize=" CUPS_LLFMT 331 ", dst=%p(%s/%s), cost=%p, list=%p)", mime, src, src->super, 332 src->type, CUPS_LLCAST srcsize, dst, dst->super, dst->type, 333 cost, list)); 334 335 /* 336 * See if there is a filter that can convert the files directly... 337 */ 338 339 if ((current = mimeFilterLookup(mime, src, dst)) != NULL && 340 (current->maxsize == 0 || srcsize <= current->maxsize)) 341 { 342 /* 343 * Got a direct filter! 344 */ 345 346 DEBUG_puts("3mime_find_filters: Direct filter found."); 347 348 if ((mintemp = cupsArrayNew(NULL, NULL)) == NULL) 349 { 350 DEBUG_puts("3mime_find_filters: Returning NULL (out of memory)."); 351 return (NULL); 352 } 353 354 cupsArrayAdd(mintemp, current); 355 356 mincost = current->cost; 357 358 if (!cost) 359 { 360 DEBUG_printf(("3mime_find_filters: Returning 1 filter, cost %d:", 361 mincost)); 362 DEBUG_printf(("3mime_find_filters: %s/%s %s/%s %d %s", 363 current->src->super, current->src->type, 364 current->dst->super, current->dst->type, 365 current->cost, current->filter)); 366 return (mintemp); 367 } 368 } 369 else 370 { 371 /* 372 * No direct filter... 373 */ 374 375 mintemp = NULL; 376 mincost = 9999999; 377 } 378 379 /* 380 * Initialize this node in the type list... 381 */ 382 383 listnode.next = list; 384 385 /* 386 * OK, now look for filters from the source type to any other type... 387 */ 388 389 srckey.src = src; 390 391 for (current = (mime_filter_t *)cupsArrayFind(mime->srcs, &srckey); 392 current && current->src == src; 393 current = (mime_filter_t *)cupsArrayNext(mime->srcs)) 394 { 395 /* 396 * See if we have already tried the destination type as a source 397 * type (this avoids extra filter looping...) 398 */ 399 400 mime_type_t *current_dst; /* Current destination type */ 401 402 if (current->maxsize > 0 && srcsize > current->maxsize) 403 continue; 404 405 for (listptr = list, current_dst = current->dst; 406 listptr; 407 listptr = listptr->next) 408 if (current_dst == listptr->src) 409 break; 410 411 if (listptr) 412 continue; 413 414 /* 415 * See if we have any filters that can convert from the destination type 416 * of this filter to the final type... 417 */ 418 419 listnode.src = current->src; 420 421 cupsArraySave(mime->srcs); 422 temp = mime_find_filters(mime, current->dst, srcsize, dst, &tempcost, 423 &listnode); 424 cupsArrayRestore(mime->srcs); 425 426 if (!temp) 427 continue; 428 429 if (!cost) 430 { 431 DEBUG_printf(("3mime_find_filters: Returning %d filter(s), cost %d:", 432 cupsArrayCount(temp), tempcost)); 433 434#ifdef DEBUG 435 for (current = (mime_filter_t *)cupsArrayFirst(temp); 436 current; 437 current = (mime_filter_t *)cupsArrayNext(temp)) 438 DEBUG_printf(("3mime_find_filters: %s/%s %s/%s %d %s", 439 current->src->super, current->src->type, 440 current->dst->super, current->dst->type, 441 current->cost, current->filter)); 442#endif /* DEBUG */ 443 444 return (temp); 445 } 446 447 /* 448 * Found a match; see if this one is less costly than the last (if 449 * any...) 450 */ 451 452 tempcost += current->cost; 453 454 if (tempcost < mincost) 455 { 456 cupsArrayDelete(mintemp); 457 458 /* 459 * Hey, we got a match! Add the current filter to the beginning of the 460 * filter list... 461 */ 462 463 mintemp = temp; 464 mincost = tempcost; 465 cupsArrayInsert(mintemp, current); 466 } 467 else 468 cupsArrayDelete(temp); 469 } 470 471 if (mintemp) 472 { 473 /* 474 * Hey, we got a match! 475 */ 476 477 DEBUG_printf(("3mime_find_filters: Returning %d filter(s), cost %d:", 478 cupsArrayCount(mintemp), mincost)); 479 480#ifdef DEBUG 481 for (current = (mime_filter_t *)cupsArrayFirst(mintemp); 482 current; 483 current = (mime_filter_t *)cupsArrayNext(mintemp)) 484 DEBUG_printf(("3mime_find_filters: %s/%s %s/%s %d %s", 485 current->src->super, current->src->type, 486 current->dst->super, current->dst->type, 487 current->cost, current->filter)); 488#endif /* DEBUG */ 489 490 if (cost) 491 *cost = mincost; 492 493 return (mintemp); 494 } 495 496 DEBUG_puts("3mime_find_filters: Returning NULL (no matches)."); 497 498 return (NULL); 499} 500 501 502/* 503 * End of "$Id: filter.c 11093 2013-07-03 20:48:42Z msweet $". 504 */ 505