| /* |
| * File type conversion routines for CUPS. |
| * |
| * Copyright 2007-2011 by Apple Inc. |
| * Copyright 1997-2007 by Easy Software Products, all rights reserved. |
| * |
| * Licensed under Apache License v2.0. See the file "LICENSE" for more information. |
| */ |
| |
| /* |
| * Include necessary headers... |
| */ |
| |
| #include <cups/string-private.h> |
| #include "mime.h" |
| |
| |
| /* |
| * Debug macros that used to be private API... |
| */ |
| |
| #define DEBUG_puts(x) |
| #define DEBUG_printf(...) |
| |
| |
| /* |
| * Local types... |
| */ |
| |
| typedef struct _mime_typelist_s /**** List of source types ****/ |
| { |
| struct _mime_typelist_s *next; /* Next source type */ |
| mime_type_t *src; /* Source type */ |
| } _mime_typelist_t; |
| |
| |
| /* |
| * Local functions... |
| */ |
| |
| static int mime_compare_filters(mime_filter_t *, mime_filter_t *); |
| static int mime_compare_srcs(mime_filter_t *, mime_filter_t *); |
| static cups_array_t *mime_find_filters(mime_t *mime, mime_type_t *src, |
| size_t srcsize, mime_type_t *dst, |
| int *cost, _mime_typelist_t *visited); |
| |
| |
| /* |
| * 'mimeAddFilter()' - Add a filter to the current MIME database. |
| */ |
| |
| mime_filter_t * /* O - New filter */ |
| mimeAddFilter(mime_t *mime, /* I - MIME database */ |
| mime_type_t *src, /* I - Source type */ |
| mime_type_t *dst, /* I - Destination type */ |
| int cost, /* I - Relative time/resource cost */ |
| const char *filter) /* I - Filter program to run */ |
| { |
| mime_filter_t *temp; /* New filter */ |
| |
| |
| DEBUG_printf(("mimeAddFilter(mime=%p, src=%p(%s/%s), dst=%p(%s/%s), cost=%d, " |
| "filter=\"%s\")", mime, |
| src, src ? src->super : "???", src ? src->type : "???", |
| dst, dst ? dst->super : "???", dst ? dst->type : "???", |
| cost, filter)); |
| |
| /* |
| * Range-check the input... |
| */ |
| |
| if (!mime || !src || !dst || !filter) |
| { |
| DEBUG_puts("1mimeAddFilter: Returning NULL."); |
| return (NULL); |
| } |
| |
| /* |
| * See if we already have an existing filter for the given source and |
| * destination... |
| */ |
| |
| if ((temp = mimeFilterLookup(mime, src, dst)) != NULL) |
| { |
| /* |
| * Yup, does the existing filter have a higher cost? If so, copy the |
| * filter and cost to the existing filter entry and return it... |
| */ |
| |
| if (temp->cost > cost) |
| { |
| DEBUG_printf(("1mimeAddFilter: Replacing filter \"%s\", cost %d.", |
| temp->filter, temp->cost)); |
| temp->cost = cost; |
| strlcpy(temp->filter, filter, sizeof(temp->filter)); |
| } |
| } |
| else |
| { |
| /* |
| * Nope, add a new one... |
| */ |
| |
| if (!mime->filters) |
| mime->filters = cupsArrayNew((cups_array_func_t)mime_compare_filters, NULL); |
| |
| if (!mime->filters) |
| return (NULL); |
| |
| if ((temp = calloc(1, sizeof(mime_filter_t))) == NULL) |
| return (NULL); |
| |
| /* |
| * Copy the information over and sort if necessary... |
| */ |
| |
| temp->src = src; |
| temp->dst = dst; |
| temp->cost = cost; |
| strlcpy(temp->filter, filter, sizeof(temp->filter)); |
| |
| DEBUG_puts("1mimeAddFilter: Adding new filter."); |
| cupsArrayAdd(mime->filters, temp); |
| cupsArrayAdd(mime->srcs, temp); |
| } |
| |
| /* |
| * Return the new/updated filter... |
| */ |
| |
| DEBUG_printf(("1mimeAddFilter: Returning %p.", temp)); |
| |
| return (temp); |
| } |
| |
| |
| /* |
| * 'mimeFilter()' - Find the fastest way to convert from one type to another. |
| */ |
| |
| cups_array_t * /* O - Array of filters to run */ |
| mimeFilter(mime_t *mime, /* I - MIME database */ |
| mime_type_t *src, /* I - Source file type */ |
| mime_type_t *dst, /* I - Destination file type */ |
| int *cost) /* O - Cost of filters */ |
| { |
| DEBUG_printf(("mimeFilter(mime=%p, src=%p(%s/%s), dst=%p(%s/%s), " |
| "cost=%p(%d))", mime, |
| src, src ? src->super : "???", src ? src->type : "???", |
| dst, dst ? dst->super : "???", dst ? dst->type : "???", |
| cost, cost ? *cost : 0)); |
| |
| return (mimeFilter2(mime, src, 0, dst, cost)); |
| } |
| |
| |
| /* |
| * 'mimeFilter2()' - Find the fastest way to convert from one type to another, |
| * including file size. |
| */ |
| |
| cups_array_t * /* O - Array of filters to run */ |
| mimeFilter2(mime_t *mime, /* I - MIME database */ |
| mime_type_t *src, /* I - Source file type */ |
| size_t srcsize, /* I - Size of source file */ |
| mime_type_t *dst, /* I - Destination file type */ |
| int *cost) /* O - Cost of filters */ |
| { |
| cups_array_t *filters; /* Array of filters to run */ |
| |
| |
| /* |
| * Range-check the input... |
| */ |
| |
| DEBUG_printf(("mimeFilter2(mime=%p, src=%p(%s/%s), srcsize=" CUPS_LLFMT |
| ", dst=%p(%s/%s), cost=%p(%d))", mime, |
| src, src ? src->super : "???", src ? src->type : "???", |
| CUPS_LLCAST srcsize, |
| dst, dst ? dst->super : "???", dst ? dst->type : "???", |
| cost, cost ? *cost : 0)); |
| |
| if (cost) |
| *cost = 0; |
| |
| if (!mime || !src || !dst) |
| return (NULL); |
| |
| /* |
| * (Re)build the source lookup array as needed... |
| */ |
| |
| if (!mime->srcs) |
| { |
| mime_filter_t *current; /* Current filter */ |
| |
| mime->srcs = cupsArrayNew((cups_array_func_t)mime_compare_srcs, NULL); |
| |
| for (current = mimeFirstFilter(mime); |
| current; |
| current = mimeNextFilter(mime)) |
| cupsArrayAdd(mime->srcs, current); |
| } |
| |
| /* |
| * Find the filters... |
| */ |
| |
| filters = mime_find_filters(mime, src, srcsize, dst, cost, NULL); |
| |
| DEBUG_printf(("1mimeFilter2: Returning %d filter(s), cost %d:", |
| cupsArrayCount(filters), cost ? *cost : -1)); |
| #ifdef DEBUG |
| { |
| mime_filter_t *filter; /* Current filter */ |
| |
| for (filter = (mime_filter_t *)cupsArrayFirst(filters); |
| filter; |
| filter = (mime_filter_t *)cupsArrayNext(filters)) |
| DEBUG_printf(("1mimeFilter2: %s/%s %s/%s %d %s", filter->src->super, |
| filter->src->type, filter->dst->super, filter->dst->type, |
| filter->cost, filter->filter)); |
| } |
| #endif /* DEBUG */ |
| |
| return (filters); |
| } |
| |
| |
| /* |
| * 'mimeFilterLookup()' - Lookup a filter. |
| */ |
| |
| mime_filter_t * /* O - Filter for src->dst */ |
| mimeFilterLookup(mime_t *mime, /* I - MIME database */ |
| mime_type_t *src, /* I - Source type */ |
| mime_type_t *dst) /* I - Destination type */ |
| { |
| mime_filter_t key, /* Key record for filter search */ |
| *filter; /* Matching filter */ |
| |
| |
| DEBUG_printf(("2mimeFilterLookup(mime=%p, src=%p(%s/%s), dst=%p(%s/%s))", mime, |
| src, src ? src->super : "???", src ? src->type : "???", |
| dst, dst ? dst->super : "???", dst ? dst->type : "???")); |
| |
| key.src = src; |
| key.dst = dst; |
| |
| filter = (mime_filter_t *)cupsArrayFind(mime->filters, &key); |
| DEBUG_printf(("3mimeFilterLookup: Returning %p(%s).", filter, |
| filter ? filter->filter : "???")); |
| return (filter); |
| } |
| |
| |
| /* |
| * 'mime_compare_filters()' - Compare two filters. |
| */ |
| |
| static int /* O - Comparison result */ |
| mime_compare_filters(mime_filter_t *f0, /* I - First filter */ |
| mime_filter_t *f1) /* I - Second filter */ |
| { |
| int i; /* Result of comparison */ |
| |
| |
| if ((i = strcmp(f0->src->super, f1->src->super)) == 0) |
| if ((i = strcmp(f0->src->type, f1->src->type)) == 0) |
| if ((i = strcmp(f0->dst->super, f1->dst->super)) == 0) |
| i = strcmp(f0->dst->type, f1->dst->type); |
| |
| return (i); |
| } |
| |
| |
| /* |
| * 'mime_compare_srcs()' - Compare two filter source types. |
| */ |
| |
| static int /* O - Comparison result */ |
| mime_compare_srcs(mime_filter_t *f0, /* I - First filter */ |
| mime_filter_t *f1) /* I - Second filter */ |
| { |
| int i; /* Result of comparison */ |
| |
| |
| if ((i = strcmp(f0->src->super, f1->src->super)) == 0) |
| i = strcmp(f0->src->type, f1->src->type); |
| |
| return (i); |
| } |
| |
| |
| /* |
| * 'mime_find_filters()' - Find the filters to convert from one type to another. |
| */ |
| |
| static cups_array_t * /* O - Array of filters to run */ |
| mime_find_filters( |
| mime_t *mime, /* I - MIME database */ |
| mime_type_t *src, /* I - Source file type */ |
| size_t srcsize, /* I - Size of source file */ |
| mime_type_t *dst, /* I - Destination file type */ |
| int *cost, /* O - Cost of filters */ |
| _mime_typelist_t *list) /* I - Source types we've used */ |
| { |
| int tempcost, /* Temporary cost */ |
| mincost; /* Current minimum */ |
| cups_array_t *temp, /* Temporary filter */ |
| *mintemp; /* Current minimum */ |
| mime_filter_t *current, /* Current filter */ |
| srckey; /* Source type key */ |
| _mime_typelist_t listnode, /* New list node */ |
| *listptr; /* Pointer in list */ |
| |
| |
| DEBUG_printf(("2mime_find_filters(mime=%p, src=%p(%s/%s), srcsize=" CUPS_LLFMT |
| ", dst=%p(%s/%s), cost=%p, list=%p)", mime, src, src->super, |
| src->type, CUPS_LLCAST srcsize, dst, dst->super, dst->type, |
| cost, list)); |
| |
| /* |
| * See if there is a filter that can convert the files directly... |
| */ |
| |
| if ((current = mimeFilterLookup(mime, src, dst)) != NULL && |
| (current->maxsize == 0 || srcsize <= current->maxsize)) |
| { |
| /* |
| * Got a direct filter! |
| */ |
| |
| DEBUG_puts("3mime_find_filters: Direct filter found."); |
| |
| if ((mintemp = cupsArrayNew(NULL, NULL)) == NULL) |
| { |
| DEBUG_puts("3mime_find_filters: Returning NULL (out of memory)."); |
| return (NULL); |
| } |
| |
| cupsArrayAdd(mintemp, current); |
| |
| mincost = current->cost; |
| |
| if (!cost) |
| { |
| DEBUG_printf(("3mime_find_filters: Returning 1 filter, cost %d:", |
| mincost)); |
| DEBUG_printf(("3mime_find_filters: %s/%s %s/%s %d %s", |
| current->src->super, current->src->type, |
| current->dst->super, current->dst->type, |
| current->cost, current->filter)); |
| return (mintemp); |
| } |
| } |
| else |
| { |
| /* |
| * No direct filter... |
| */ |
| |
| mintemp = NULL; |
| mincost = 9999999; |
| } |
| |
| /* |
| * Initialize this node in the type list... |
| */ |
| |
| listnode.next = list; |
| |
| /* |
| * OK, now look for filters from the source type to any other type... |
| */ |
| |
| srckey.src = src; |
| |
| for (current = (mime_filter_t *)cupsArrayFind(mime->srcs, &srckey); |
| current && current->src == src; |
| current = (mime_filter_t *)cupsArrayNext(mime->srcs)) |
| { |
| /* |
| * See if we have already tried the destination type as a source |
| * type (this avoids extra filter looping...) |
| */ |
| |
| mime_type_t *current_dst; /* Current destination type */ |
| |
| if (current->maxsize > 0 && srcsize > current->maxsize) |
| continue; |
| |
| for (listptr = list, current_dst = current->dst; |
| listptr; |
| listptr = listptr->next) |
| if (current_dst == listptr->src) |
| break; |
| |
| if (listptr) |
| continue; |
| |
| /* |
| * See if we have any filters that can convert from the destination type |
| * of this filter to the final type... |
| */ |
| |
| listnode.src = current->src; |
| |
| cupsArraySave(mime->srcs); |
| temp = mime_find_filters(mime, current->dst, srcsize, dst, &tempcost, |
| &listnode); |
| cupsArrayRestore(mime->srcs); |
| |
| if (!temp) |
| continue; |
| |
| if (!cost) |
| { |
| DEBUG_printf(("3mime_find_filters: Returning %d filter(s), cost %d:", |
| cupsArrayCount(temp), tempcost)); |
| |
| #ifdef DEBUG |
| for (current = (mime_filter_t *)cupsArrayFirst(temp); |
| current; |
| current = (mime_filter_t *)cupsArrayNext(temp)) |
| DEBUG_printf(("3mime_find_filters: %s/%s %s/%s %d %s", |
| current->src->super, current->src->type, |
| current->dst->super, current->dst->type, |
| current->cost, current->filter)); |
| #endif /* DEBUG */ |
| |
| return (temp); |
| } |
| |
| /* |
| * Found a match; see if this one is less costly than the last (if |
| * any...) |
| */ |
| |
| tempcost += current->cost; |
| |
| if (tempcost < mincost) |
| { |
| cupsArrayDelete(mintemp); |
| |
| /* |
| * Hey, we got a match! Add the current filter to the beginning of the |
| * filter list... |
| */ |
| |
| mintemp = temp; |
| mincost = tempcost; |
| cupsArrayInsert(mintemp, current); |
| } |
| else |
| cupsArrayDelete(temp); |
| } |
| |
| if (mintemp) |
| { |
| /* |
| * Hey, we got a match! |
| */ |
| |
| DEBUG_printf(("3mime_find_filters: Returning %d filter(s), cost %d:", |
| cupsArrayCount(mintemp), mincost)); |
| |
| #ifdef DEBUG |
| for (current = (mime_filter_t *)cupsArrayFirst(mintemp); |
| current; |
| current = (mime_filter_t *)cupsArrayNext(mintemp)) |
| DEBUG_printf(("3mime_find_filters: %s/%s %s/%s %d %s", |
| current->src->super, current->src->type, |
| current->dst->super, current->dst->type, |
| current->cost, current->filter)); |
| #endif /* DEBUG */ |
| |
| if (cost) |
| *cost = mincost; |
| |
| return (mintemp); |
| } |
| |
| DEBUG_puts("3mime_find_filters: Returning NULL (no matches)."); |
| |
| return (NULL); |
| } |