Example #1
0
static mime_type_t *			/* O - Printer type or NULL on error */
add_printer_filters(
    const char  *command,		/* I - Command name */
    mime_t      *mime,			/* I - MIME database */
    const char  *printer,		/* I - Printer name */
    const char  *ppdfile,		/* I - PPD file */
    mime_type_t **prefilter_type)	/* O - Prefilter type */
{
  ppd_file_t	*ppd;			/* PPD file data */
  _ppd_cache_t	*pc;			/* Cache data for PPD */
  const char	*value;			/* Filter definition value */
  mime_type_t	*printer_type;		/* Printer filter type */


  if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_NONE)) == NULL)
  {
    ppd_status_t	status;		/* PPD load status */
    int			linenum;	/* Line number */

    status = ppdLastError(&linenum);
    _cupsLangPrintf(stderr, _("%s: Unable to open PPD file: %s on line %d."),
                    command, ppdErrorString(status), linenum);
    return (NULL);
  }

  pc = _ppdCacheCreateWithPPD(ppd);
  if (!pc)
    return (NULL);

  printer_type    = mimeAddType(mime, "printer", printer);
  *prefilter_type = NULL;

  if (pc->filters)
  {
    for (value = (const char *)cupsArrayFirst(pc->filters);
         value;
         value = (const char *)cupsArrayNext(pc->filters))
      add_printer_filter(command, mime, printer_type, value);
  }
  else
  {
    add_printer_filter(command, mime, printer_type,
                       "application/vnd.cups-raw 0 -");
    add_printer_filter(command, mime, printer_type,
                       "application/vnd.cups-postscript 0 -");
  }

  if (pc->prefilters)
  {
    *prefilter_type = mimeAddType(mime, "prefilter", printer);

    for (value = (const char *)cupsArrayFirst(pc->prefilters);
         value;
         value = (const char *)cupsArrayNext(pc->prefilters))
      add_printer_filter(command, mime, *prefilter_type, value);
  }

  return (printer_type);
}
Example #2
0
void
_cupsStrFlush(void)
{
  _cups_sp_item_t	*item;		/* Current item */


  // DEBUG_printf(("_cupsStrFlush(cg=%p)\n", cg));
  DEBUG_printf(("    %d strings in array\n", cupsArrayCount(stringpool)));

#ifdef HAVE_PTHREAD_H
  pthread_mutex_lock(&sp_mutex);
#endif /* HAVE_PTHREAD_H */

  for (item = (_cups_sp_item_t *)cupsArrayFirst(stringpool);
       item;
       item = (_cups_sp_item_t *)cupsArrayNext(stringpool))
  {
    free(item->str);
    free(item);
  }

  cupsArrayDelete(stringpool);
  stringpool = NULL;

#ifdef HAVE_PTHREAD_H
  pthread_mutex_unlock(&sp_mutex);
#endif /* HAVE_PTHREAD_H */
}
Example #3
0
void
cupsdCleanDirty(void)
{
  if (DirtyFiles & CUPSD_DIRTY_PRINTERS)
    cupsdSaveAllPrinters();

  if (DirtyFiles & CUPSD_DIRTY_CLASSES)
    cupsdSaveAllClasses();

  if (DirtyFiles & CUPSD_DIRTY_PRINTCAP)
    cupsdWritePrintcap();

  if (DirtyFiles & CUPSD_DIRTY_JOBS)
  {
    cupsd_job_t	*job;			/* Current job */

    cupsdSaveAllJobs();

    for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
         job;
	 job = (cupsd_job_t *)cupsArrayNext(Jobs))
      if (job->dirty)
        cupsdSaveJob(job);
  }

  if (DirtyFiles & CUPSD_DIRTY_SUBSCRIPTIONS)
    cupsdSaveAllSubscriptions();

  DirtyFiles     = CUPSD_DIRTY_NONE;
  DirtyCleanTime = 0;

  cupsdSetBusyState();
}
Example #4
0
void
cupsdPauseListening(void)
{
  cupsd_listener_t	*lis;		/* Current listening socket */


  if (cupsArrayCount(Listeners) < 1)
    return;

  if (cupsArrayCount(Clients) == MaxClients)
    cupsdLogMessage(CUPSD_LOG_WARN,
                    "Max clients reached, holding new connections...");
  else if (errno == ENFILE || errno == EMFILE)
    cupsdLogMessage(CUPSD_LOG_WARN,
                    "Too many open files, holding new connections for "
		    "30 seconds...");

  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdPauseListening: Clearing input bits...");

  for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
       lis;
       lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
    cupsdRemoveSelect(lis->fd);

  ListeningPaused = time(NULL) + 30;
}
Example #5
0
void
cupsdExpireSubscriptions(
    cupsd_printer_t *dest,		/* I - Printer, if any */
    cupsd_job_t     *job)		/* I - Job, if any */
{
  cupsd_subscription_t	*sub;		/* Current subscription */
  int			update;		/* Update subscriptions.conf? */
  time_t		curtime;	/* Current time */


  curtime = time(NULL);
  update  = 0;

  for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
       sub;
       sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
    if ((!sub->job && !dest && sub->expire && sub->expire <= curtime) ||
        (dest && sub->dest == dest) ||
	(job && sub->job == job))
    {
      cupsdLogMessage(CUPSD_LOG_INFO, "Subscription %d has expired...",
                      sub->id);

      cupsdDeleteSubscription(sub, 0);

      update = 1;
    }

  if (update)
    cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
}
void
cupsdDeleteAllPolicies(void)
{
  cupsd_printer_t	*printer;	/* Current printer */


  if (!Policies)
    return;

 /*
  * First clear the policy pointers for all printers...
  */

  for (printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
       printer;
       printer = (cupsd_printer_t *)cupsArrayNext(Printers))
    printer->op_policy_ptr = NULL;

  DefaultPolicyPtr = NULL;

 /*
  * Then free all of the policies...
  */

  cupsArrayDelete(Policies);

  Policies = NULL;
}
Example #7
0
void
ppdMarkDefaults(ppd_file_t *ppd)	/* I - PPD file record */
{
  int		i;			/* Looping variables */
  ppd_group_t	*g;			/* Current group */
  ppd_choice_t	*c;			/* Current choice */


  if (!ppd)
    return;

 /*
  * Clean out the marked array...
  */

  for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
       c;
       c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
  {
    cupsArrayRemove(ppd->marked, c);
    c->marked = 0;
  }

 /*
  * Then repopulate it with the defaults...
  */

  for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
    ppd_defaults(ppd, g);
}
Example #8
0
static void
free_cache(void)
{
  snmp_cache_t	*cache;			/* Cached device */


  for (cache = (snmp_cache_t *)cupsArrayFirst(Devices);
       cache;
       cache = (snmp_cache_t *)cupsArrayNext(Devices))
  {
    free(cache->addrname);

    if (cache->uri)
      free(cache->uri);

    if (cache->id)
      free(cache->id);

    if (cache->make_and_model)
      free(cache->make_and_model);

    free(cache);
  }

  cupsArrayDelete(Devices);
  Devices = NULL;
}
Example #9
0
File: listen.c Project: jelmer/cups
void
cupsdStopListening(void)
{
  cupsd_listener_t	*lis;		/* Current listening socket */


  cupsdLogMessage(CUPSD_LOG_DEBUG2,
                  "cupsdStopListening: closing all listen sockets.");

  cupsdPauseListening();

  for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
       lis;
       lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
  {
    if (lis->fd != -1)
    {
#ifdef HAVE_LAUNCH_H
      httpAddrClose(NULL, lis->fd);
#else
      httpAddrClose(&(lis->address), lis->fd);
#endif /* HAVE_LAUNCH */

      lis->fd = -1;
    }
  }
}
Example #10
0
static void
help_delete_node(help_node_t *n)	/* I - Node */
{
  help_word_t	*w;			/* Current word */


  DEBUG_printf(("2help_delete_node(n=%p)", n));

  if (!n)
    return;

  if (n->filename)
    free(n->filename);

  if (n->anchor)
    free(n->anchor);

  if (n->section)
    free(n->section);

  if (n->text)
    free(n->text);

  for (w = (help_word_t *)cupsArrayFirst(n->words);
       w;
       w = (help_word_t *)cupsArrayNext(n->words))
    help_delete_word(w);

  cupsArrayDelete(n->words);

  free(n);
}
Example #11
0
void
serverCleanJobs(server_printer_t *printer)	/* I - Printer */
{
  server_job_t	*job;			/* Current job */
  time_t	cleantime;		/* Clean time */


  if (cupsArrayCount(printer->jobs) == 0)
    return;

  cleantime = time(NULL) - 60;

  _cupsRWLockWrite(&(printer->rwlock));
  for (job = (server_job_t *)cupsArrayFirst(printer->jobs);
       job;
       job = (server_job_t *)cupsArrayNext(printer->jobs))
    if (job->completed && job->completed < cleantime)
    {
      cupsArrayRemove(printer->jobs, job);
      serverDeleteJob(job);
    }
    else
      break;
  _cupsRWUnlock(&(printer->rwlock));
}
Example #12
0
void
serverCheckJobs(server_printer_t *printer)	/* I - Printer */
{
  server_job_t	*job;			/* Current job */


  if (printer->processing_job)
    return;

  _cupsRWLockWrite(&(printer->rwlock));
  for (job = (server_job_t *)cupsArrayFirst(printer->active_jobs);
       job;
       job = (server_job_t *)cupsArrayNext(printer->active_jobs))
  {
    if (job->state == IPP_JSTATE_PENDING)
    {
      if (!_cupsThreadCreate((_cups_thread_func_t)serverProcessJob, job))
      {
        job->state     = IPP_JSTATE_ABORTED;
	job->completed = time(NULL);

        serverAddEvent(printer, job, SERVER_EVENT_JOB_COMPLETED, "Job aborted because creation of processing thread failed.");
      }
      break;
    }
  }
  _cupsRWUnlock(&(printer->rwlock));
}
Example #13
0
static void
list_nodes(help_index_t *hi,		/* I - Help index */
           const char   *title,		/* I - Title string */
	   cups_array_t *nodes)		/* I - Nodes */
{
  help_node_t	*node,			/* Current node */
		*file;			/* File node */


  printf("%d\n", cupsArrayCount(nodes));
  for (node = (help_node_t *)cupsArrayFirst(nodes);
       node;
       node = (help_node_t *)cupsArrayNext(nodes))
  {
    if (node->anchor)
    {
      file = helpFindNode(hi, node->filename, NULL);
      printf("%d|%s#%s|%s|%s\n", node->score, node->filename, node->anchor,
             node->text, file ? file->text : node->filename);
    }
    else
      printf("%d|%s|%s|%s\n", node->score, node->filename, node->text,
             node->text);
  }
}
Example #14
0
int					/* O - Number of conflicting options */
cupsGetConflicts(
    ppd_file_t    *ppd,			/* I - PPD file */
    const char    *option,		/* I - Option to test */
    const char    *choice,		/* I - Choice to test */
    cups_option_t **options)		/* O - Conflicting options */
{
  int			i,		/* Looping var */
			num_options;	/* Number of conflicting options */
  cups_array_t		*active;	/* Active conflicts */
  _ppd_cups_uiconsts_t	*c;		/* Current constraints */
  _ppd_cups_uiconst_t	*cptr;		/* Current constraint */
  ppd_choice_t		*marked;	/* Marked choice */


 /*
  * Range check input...
  */

  if (options)
    *options = NULL;

  if (!ppd || !option || !choice || !options)
    return (0);

 /*
  * Test for conflicts...
  */

  active = ppd_test_constraints(ppd, option, choice, 0, NULL,
                                _PPD_ALL_CONSTRAINTS);

 /*
  * Loop through all of the UI constraints and add any options that conflict...
  */

  for (num_options = 0, c = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active);
       c;
       c = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
  {
    for (i = c->num_constraints, cptr = c->constraints;
         i > 0;
	 i --, cptr ++)
      if (_cups_strcasecmp(cptr->option->keyword, option))
      {
        if (cptr->choice)
	  num_options = cupsAddOption(cptr->option->keyword,
	                              cptr->choice->choice, num_options,
				      options);
        else if ((marked = ppdFindMarkedChoice(ppd,
	                                       cptr->option->keyword)) != NULL)
	  num_options = cupsAddOption(cptr->option->keyword, marked->choice,
				      num_options, options);
      }
  }

  cupsArrayDelete(active);

  return (num_options);
}
Example #15
0
void
cupsdStopListening(void)
{
  cupsd_listener_t	*lis;		/* Current listening socket */


  cupsdLogMessage(CUPSD_LOG_DEBUG2,
                  "cupsdStopListening: closing all listen sockets.");

  cupsdPauseListening();

  for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
       lis;
       lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
  {
#if defined(HAVE_LAUNCHD) || defined(HAVE_SYSTEMD)
    if (!lis->on_demand && lis->fd != -1)
    {
      httpAddrClose(&(lis->address), lis->fd);
      lis->fd = -1;
    }

#else
    if (lis->fd != -1)
    {
      httpAddrClose(&(lis->address), lis->fd);
      lis->fd = -1;
    }
#endif /* HAVE_LAUNCHD || HAVE_SYSTEMD */
  }
}
Example #16
0
mime_filter_t *				/* O - Filter or NULL */
mimeNextFilter(mime_t *mime)		/* I - MIME database */
{
  if (!mime)
    return (NULL);
  else
    return ((mime_filter_t *)cupsArrayNext(mime->filters));
}
Example #17
0
ppd_cparam_t *				/* O - Custom parameter or NULL */
ppdNextCustomParam(ppd_coption_t *opt)	/* I - Custom option */
{
  if (!opt)
    return (NULL);

  return ((ppd_cparam_t *)cupsArrayNext(opt->params));
}
Example #18
0
mime_type_t *				/* O - Type or NULL */
mimeNextType(mime_t *mime)		/* I - MIME database */
{
  if (!mime)
    return (NULL);
  else
    return ((mime_type_t *)cupsArrayNext(mime->types));
}
Example #19
0
ppd_option_t *				/* O - Next option or @code NULL@ */
ppdNextOption(ppd_file_t *ppd)		/* I - PPD file */
{
  if (!ppd)
    return (NULL);
  else
    return ((ppd_option_t *)cupsArrayNext(ppd->options));
}
Example #20
0
int					/* O - Exit code */
main(int  argc,				/* I - Number of command-line args */
     char *argv[])			/* I - Command-line arguments */
{
  FILE			*strings;	/* .strings file */
  cups_array_t		*po;		/* .po file */
  char			iconv[1024];	/* iconv command */
  _cups_message_t	*msg;		/* Current message */


  if (argc != 3)
  {
    puts("Usage: po2strings filename.po filename.strings");
    return (1);
  }

 /*
  * Use the CUPS .po loader to get the message strings...
  */

  if ((po = _cupsMessageLoad(argv[1])) == NULL)
  {
    perror(argv[1]);
    return (1);
  }

 /*
  * Cheat by using iconv to write the .strings file with a UTF-16 encoding.
  * The .po file uses UTF-8...
  */

  snprintf(iconv, sizeof(iconv), "iconv -f utf-8 -t utf-16 >'%s'", argv[2]);
  if ((strings = popen(iconv, "w")) == NULL)
  {
    perror(argv[2]);
    _cupsMessageFree(po);
    return (1);
  }

  for (msg = (_cups_message_t *)cupsArrayFirst(po);
       msg;
       msg = (_cups_message_t *)cupsArrayNext(po))
  {
    write_string(strings, msg->id);
    fputs(" = ", strings);
    write_string(strings, msg->str);
    fputs(";\n", strings);
  }

  printf("%s: %d messages.\n", argv[2], cupsArrayCount(po));

  pclose(strings);
  _cupsMessageFree(po);

  return (0);
}
Example #21
0
int					/* O - Number of conflicts found */
ppdConflicts(ppd_file_t *ppd)		/* I - PPD to check */
{
  int			i,		/* Looping variable */
			conflicts;	/* Number of conflicts */
  cups_array_t		*active;	/* Active conflicts */
  _ppd_cups_uiconsts_t	*c;		/* Current constraints */
  _ppd_cups_uiconst_t	*cptr;		/* Current constraint */
  ppd_option_t	*o;			/* Current option */


  if (!ppd)
    return (0);

 /*
  * Clear all conflicts...
  */

  cupsArraySave(ppd->options);

  for (o = ppdFirstOption(ppd); o; o = ppdNextOption(ppd))
    o->conflicted = 0;

  cupsArrayRestore(ppd->options);

 /*
  * Test for conflicts...
  */

  active    = ppd_test_constraints(ppd, NULL, NULL, 0, NULL,
                                   _PPD_ALL_CONSTRAINTS);
  conflicts = cupsArrayCount(active);

 /*
  * Loop through all of the UI constraints and flag any options
  * that conflict...
  */

  for (c = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active);
       c;
       c = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
  {
    for (i = c->num_constraints, cptr = c->constraints;
         i > 0;
	 i --, cptr ++)
      cptr->option->conflicted = 1;
  }

  cupsArrayDelete(active);

 /*
  * Return the number of conflicts found...
  */

  return (conflicts);
}
Example #22
0
void
mimeDelete(mime_t *mime)		/* I - MIME database */
{
  mime_type_t	*type;			/* Current type */
  mime_filter_t	*filter;		/* Current filter */


  printf("[mime.c::mimeDelete()]called %p\n", mime);

  if (!mime)
    return;

 /*
  * Loop through filters and free them...
  */

  for (filter = (mime_filter_t *)cupsArrayFirst(mime->filters);
       filter;
       filter = (mime_filter_t *)cupsArrayNext(mime->filters))
    {
      mimeDeleteFilter(mime, filter);
      printf("[mime.c::mimeDelete()] deleting filter %p\n", filter);
    }
 /*
  * Loop through the file types and delete any rules...
  */

  for (type = (mime_type_t *)cupsArrayFirst(mime->types);
       type;
       type = (mime_type_t *)cupsArrayNext(mime->types))
    {
      printf("[mime.c::mimeDelete()] deleting type rule %p\n", type);
      mimeDeleteType(mime, type);
    }
 /*
  * Free the types and filters arrays, and then the MIME database structure.
  */

  cupsArrayDelete(mime->types);
  cupsArrayDelete(mime->filters);
  cupsArrayDelete(mime->srcs);
  free(mime);
}
Example #23
0
static void
free_array(cups_array_t *a)		/* I - Array */
{
  char	*s;				/* Current string */


  for (s = (char *)cupsArrayFirst(a); s; s = (char *)cupsArrayNext(a))
    free(s);

  cupsArrayDelete(a);
}
static void
free_formats(cups_array_t *fmts)	/* I - Array of format strings */
{
  char	*s;				/* Current string */


  for (s = (char *)cupsArrayFirst(fmts); s; s = (char *)cupsArrayNext(fmts))
    free(s);

  cupsArrayDelete(fmts);
}
Example #25
0
void
mimeDelete(mime_t *mime)		/* I - MIME database */
{
  mime_type_t	*type;			/* Current type */
  mime_filter_t	*filter;		/* Current filter */


  DEBUG_printf(("mimeDelete(mime=%p)", mime));

  if (!mime)
    return;

 /*
  * Loop through filters and free them...
  */

  for (filter = (mime_filter_t *)cupsArrayFirst(mime->filters);
       filter;
       filter = (mime_filter_t *)cupsArrayNext(mime->filters))
    mimeDeleteFilter(mime, filter);

 /*
  * Loop through the file types and delete any rules...
  */

  for (type = (mime_type_t *)cupsArrayFirst(mime->types);
       type;
       type = (mime_type_t *)cupsArrayNext(mime->types))
    mimeDeleteType(mime, type);

 /*
  * Free the types and filters arrays, and then the MIME database structure.
  */

  cupsArrayDelete(mime->types);
  cupsArrayDelete(mime->filters);
  cupsArrayDelete(mime->srcs);
  free(mime);
}
Example #26
0
static void
add_ppd_filters(mime_t     *mime,	/* I - MIME database */
                ppd_file_t *ppd)	/* I - PPD file */
{
  _ppd_cache_t	*pc;			/* Cache data for PPD */
  const char	*value;			/* Filter definition value */
  mime_type_t	*filter,		/* Filter type */
		*prefilter;		/* Pre-filter type */


  pc = _ppdCacheCreateWithPPD(ppd);
  if (!pc)
    return;

  filter = mimeAddType(mime, "printer", "test");

  if (pc->filters)
  {
    for (value = (const char *)cupsArrayFirst(pc->filters);
         value;
         value = (const char *)cupsArrayNext(pc->filters))
      add_ppd_filter(mime, filter, value);
  }
  else
  {
    add_ppd_filter(mime, filter, "application/vnd.cups-raw 0 -");
    add_ppd_filter(mime, filter, "application/vnd.cups-postscript 0 -");
  }

  if (pc->prefilters)
  {
    prefilter = mimeAddType(mime, "prefilter", "test");

    for (value = (const char *)cupsArrayFirst(pc->prefilters);
         value;
         value = (const char *)cupsArrayNext(pc->prefilters))
      add_ppd_filter(mime, prefilter, value);
  }
}
Example #27
0
File: listen.c Project: jelmer/cups
void
cupsdDeleteAllListeners(void)
{
  cupsd_listener_t	*lis;		/* Current listening socket */


  for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
       lis;
       lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
    free(lis);

  cupsArrayDelete(Listeners);
  Listeners = NULL;
}
Example #28
0
size_t					/* O - Number of strings */
_cupsStrStatistics(size_t *alloc_bytes,	/* O - Allocated bytes */
                   size_t *total_bytes)	/* O - Total string bytes */
{
  size_t		count,		/* Number of strings */
			abytes,		/* Allocated string bytes */
			tbytes,		/* Total string bytes */
			len;		/* Length of string */
  _cups_sp_item_t	*item;		/* Current item */


 /*
  * Loop through strings in pool, counting everything up...
  */

#ifdef HAVE_PTHREAD_H
  pthread_mutex_lock(&sp_mutex);
#endif /* HAVE_PTHREAD_H */

  for (count = 0, abytes = 0, tbytes = 0,
           item = (_cups_sp_item_t *)cupsArrayFirst(stringpool);
       item;
       item = (_cups_sp_item_t *)cupsArrayNext(stringpool))
  {
   /*
    * Count allocated memory, using a 64-bit aligned buffer as a basis.
    */

    count  += item->ref_count;
    len    = (strlen(item->str) + 8) & ~7;
    abytes += sizeof(_cups_sp_item_t) + len;
    tbytes += item->ref_count * len;
  }

#ifdef HAVE_PTHREAD_H
  pthread_mutex_unlock(&sp_mutex);
#endif /* HAVE_PTHREAD_H */

 /*
  * Return values...
  */

  if (alloc_bytes)
    *alloc_bytes = abytes;

  if (total_bytes)
    *total_bytes = tbytes;

  return (count);
}
Example #29
0
static void
ppd_debug_marked(ppd_file_t *ppd,		/* I - PPD file data */
             const char *title)		/* I - Title for list */
{
  ppd_choice_t	*c;			/* Current choice */


  DEBUG_printf(("2cupsMarkOptions: %s", title));

  for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
       c;
       c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
    DEBUG_printf(("2cupsMarkOptions: %s=%s", c->option->keyword, c->choice));
}
Example #30
0
void
_ppdFreeLanguages(
    cups_array_t *languages)		/* I - Languages array */
{
  char	*language;			/* Current language */


  for (language = (char *)cupsArrayFirst(languages);
       language;
       language = (char *)cupsArrayNext(languages))
    free(language);

  cupsArrayDelete(languages);
}