blob: 5596d623ccd0f2c9ebb1ad56fc4d37d39ae39959 [file] [log] [blame]
/*
* 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);
}