mirror of https://gitee.com/openkylin/cups.git
488 lines
12 KiB
C
488 lines
12 KiB
C
/*
|
|
* 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);
|
|
}
|