filter.c   [plain text]


/*
 * "$Id: filter.c,v 1.11 2005/01/27 01:06:40 jlovell Exp $"
 *
 *   File type conversion routines for the Common UNIX Printing System (CUPS).
 *
 *   Copyright 1997-2005 by Easy Software Products, all rights reserved.
 *
 *   These coded instructions, statements, and computer programs are the
 *   property of Easy Software Products and are protected by Federal
 *   copyright law.  Distribution and use rights are outlined in the file
 *   "LICENSE.txt" which should have been included with this file.  If this
 *   file is missing or damaged please contact Easy Software Products
 *   at:
 *
 *       Attn: CUPS Licensing Information
 *       Easy Software Products
 *       44141 Airport View Drive, Suite 204
 *       Hollywood, Maryland 20636 USA
 *
 *       Voice: (301) 373-9600
 *       EMail: cups-info@cups.org
 *         WWW: http://www.cups.org
 *
 * Contents:
 *
 *   mimeAddFilter()  - Add a filter to the current MIME database.
 *   mimeFilter()     - Find the fastest way to convert from one type to another.
 *   filter_compare() - Compare two filter types...
 *   lookup()         - Lookup a filter...
 */

/*
 * Include necessary headers...
 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#include <cups/debug.h>
#include <cups/string.h>
#include "mime.h"


/*
 * Local functions...
 */

static int		filter_compare(mime_filter_t *, mime_filter_t *);
static mime_filter_t	*lookup(mime_t *, mime_type_t *, mime_type_t *);


/*
 * '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 */


 /*
  * Range-check the input...
  */

  if (mime == NULL || src == NULL || dst == NULL || filter == NULL)
    return (NULL);

  if (strlen(filter) > (MIME_MAX_FILTER - 1))
    return (NULL);

 /*
  * See if we already have an existing filter for the given source and
  * destination...
  */

  if ((temp = lookup(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)
    {
      temp->cost = cost;
      strlcpy(temp->filter, filter, sizeof(temp->filter));
    }
  }
  else
  {
   /*
    * Nope, add a new one...
    */

    if (mime->num_filters == 0)
      temp = malloc(sizeof(mime_filter_t));
    else
      temp = realloc(mime->filters, sizeof(mime_filter_t) * (mime->num_filters + 1));

    if (temp == NULL)
      return (NULL);

    mime->filters = temp;
    temp += mime->num_filters;
    mime->num_filters ++;

   /*
    * Copy the information over and sort if necessary...
    */

    temp->src  = src;
    temp->dst  = dst;
    temp->cost = cost;
    strlcpy(temp->filter, filter, sizeof(temp->filter));

    if (mime->num_filters > 1)
      mergesort(mime->filters, mime->num_filters, sizeof(mime_filter_t),
            (int (*)(const void *, const void *))filter_compare);
  }

 /*
  * Return the new/updated filter...
  */

  return (temp);
}


/*
 * 'mimeFilter()' - Find the fastest way to convert from one type to another.
 */

mime_filter_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		    *num_filters, /* O - Number of filters to run */
	   mime_type_list_t *list)	  /* I - List of current solution path */
{
  int		    i, j,		/* Looping vars */
		    num_temp,		/* Number of temporary filters */
		    num_mintemp,	/* Number of filters in the minimum */
		    cost,		/* Current cost */
		    mincost;		/* Current minimum */
  mime_filter_t	    *temp,		/* Temporary filter */
		    *mintemp,		/* Current minimum */
		    *current;		/* Current filter */
  mime_type_list_t  leaf,		/* Current leaf node in solution path */
		    *p;			/* List walking var */

 /*
  * Range-check the input...
  */

  DEBUG_printf(("mimeFilter(mime=%p, src=%p(%s/%s), dst=%p(%s/%s), num_filters=%p(%d))\n",
        	mime, src, src ? src->super : "?", src ? src->type : "?",
		dst, dst ? dst->super : "?", dst ? dst->type : "?",
		num_filters, num_filters ? *num_filters : 0));

  if (mime == NULL || src == NULL || dst == NULL || num_filters == NULL)
    return (NULL);

  *num_filters = 0;

 /*
  * See if there is a filter that can convert the files directly...
  */

  if ((temp = lookup(mime, src, dst)) != NULL)
  {
   /*
    * Got a direct filter!
    */

    if ((mintemp = (mime_filter_t *)malloc(sizeof(mime_filter_t))) == NULL)
      return (NULL);

    memcpy(mintemp, temp, sizeof(mime_filter_t));
    num_mintemp = 1;
    mincost     = mintemp->cost;

    DEBUG_puts("    Found direct filter:");
    DEBUG_printf(("    %s (cost=%d)\n", mintemp->filter, mincost));
  }
  else
  {
   /*
    * No direct filter...
    */

    mincost     = 9999999;
    mintemp     = NULL;
    num_mintemp = 0;
  }

 /*
  * The leaf is the head of the stack based soultion list...
  */

  leaf.next = list;

 /*
  * OK, now look for filters from the source type to any other type...
  */

  for (i = mime->num_filters, current = mime->filters;
       i > 0;
       i --, current ++)
  {
    if (current->src != src)
      continue;

   /*
    * Walk the current solution list to see if this destination type is already
    * used as a source type; if it is then avoid the resulting mutual recursion.
    */

    for (p = list; p != NULL; p = p->next)
      if (current->dst == p->src)
        break;

    if (p)
    {
      DEBUG_printf(("short circuiting the %s/%s mutual recursion loop\n", p->src->super, p->src->type));
      continue;
    }

   /*
    * See if we have any filters that can convert from the destination type
    * of this filter to the final type...
    */

    leaf.src = current->src;

    if ((temp = mimeFilter(mime, current->dst, dst, &num_temp,
                           &leaf)) == NULL)
      continue;

   /*
    * Found a match; see if this one is less costly than the last (if
    * any...)
    */

    for (j = 0, cost = 0; j < num_temp; j ++)
      cost += temp[j].cost;

    if (cost < mincost)
    {
      if (mintemp != NULL)
	free(mintemp);

     /*
      * Hey, we got a match!  Add the current filter to the beginning of the
      * filter list...
      */

      mintemp = (mime_filter_t *)realloc(temp, sizeof(mime_filter_t) *
                                               (num_temp + 1));

      if (mintemp == NULL)
      {
	*num_filters = 0;
	return (NULL);
      }

      memmove(mintemp + 1, mintemp, num_temp * sizeof(mime_filter_t));
      memcpy(mintemp, current, sizeof(mime_filter_t));

      num_mintemp = num_temp + 1;
      mincost     = cost;
    }
    else
      free(temp);
  }

  if (mintemp != NULL)
  {
   /*
    * Hey, we got a match!
    */

    *num_filters = num_mintemp;

#ifdef DEBUG
    printf("    Returning %d filters:\n", *num_filters);
    for (i = 0; i < num_mintemp; i ++)
      printf("    %s\n", mintemp[i].filter);
#endif /* DEBUG */

    return (mintemp);
  }

  DEBUG_puts("    Returning zippo...");

  return (NULL);
}


/*
 * 'filter_compare()' - Compare two filter types...
 */

static int				/* O - Comparison result */
filter_compare(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);
}


/*
 * 'lookup()' - Lookup a filter...
 */

static mime_filter_t *		/* O - Filter for src->dst */
lookup(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 */


  if (mime->num_filters == 0)
    return (NULL);

  key.src = src;
  key.dst = dst;

  return ((mime_filter_t *)bsearch(&key, mime->filters, mime->num_filters,
                                   sizeof(mime_filter_t),
				   (int (*)(const void *, const void *))filter_compare));
}


/*
 * End of "$Id: filter.c,v 1.11 2005/01/27 01:06:40 jlovell Exp $".
 */