text: /*
   * "lpstat" command for CUPS.
   *
   * Copyright © 2021-2022 by OpenPrinting.
   * Copyright © 2007-2018 by Apple Inc.
   * Copyright © 1997-2006 by Easy Software Products.
   *
   * Licensed under Apache License v2.0.  See the file "LICENSE" for more
   * information.
   */
  
  /*
   * Include necessary headers...
   */
  
  #include <cups/cups-private.h>
  
  
  /*
   * Local functions...
   */
  
  static void	check_dest(const char *command, const char *name,
  		           int *num_dests, cups_dest_t **dests);
  static int	match_list(const char *list, const char *name);
  static int	show_accepting(const char *printers, int num_dests,
  		               cups_dest_t *dests);
  static int	show_classes(const char *dests);
  static void	show_default(cups_dest_t *dest);
  static int	show_devices(const char *printers, int num_dests,
  		             cups_dest_t *dests);
  static int	show_jobs(const char *dests, const char *users, int long_status,
  		          int ranking, const char *which);
  static int	show_printers(const char *printers, int num_dests,
  		              cups_dest_t *dests, int long_status);
  static int	show_scheduler(void);
  static void	usage(void) _CUPS_NORETURN;
  
  
  /*
   * 'main()' - Parse options and show status information.
   */
  
  int
  main(int  argc,				/* I - Number of command-line arguments */
       char *argv[])			/* I - Command-line arguments */
  {
    int		i,			/* Looping var */
  		status;			/* Exit status */
    char		*opt;			/* Option pointer */
    int		num_dests;		/* Number of user destinations */
    cups_dest_t	*dests;			/* User destinations */
    int		long_status;		/* Long status report? */
    int		ranking;		/* Show job ranking? */
    const char	*which;			/* Which jobs to show? */
    char		op;			/* Last operation on command-line */
  
  
    _cupsSetLocale(argv);
  
   /*
    * Parse command-line options...
    */
  
    num_dests   = 0;
    dests       = NULL;
    long_status = 0;
    ranking     = 0;
    status      = 0;
    which       = "not-completed";
    op          = 0;
  
    for (i = 1; i < argc; i ++)
    {
      if (!strcmp(argv[i], "--help"))
        usage();
      else if (argv[i][0] == '-')
      {
        for (opt = argv[i] + 1; *opt; opt ++)
        {
  	switch (*opt)
  	{
  	  case 'D' : /* Show description */
  	      long_status = 1;
  	      break;
  
  	  case 'E' : /* Encrypt */
  #ifdef HAVE_TLS
  	      cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
  #else
  	      _cupsLangPrintf(stderr,
  			      _("%s: Sorry, no encryption support."),
  			      argv[0]);
  #endif /* HAVE_TLS */
  	      break;
  
  	  case 'H' : /* Show server and port */
  	      if (cupsServer()[0] == '/')
  		_cupsLangPuts(stdout, cupsServer());
  	      else
  		_cupsLangPrintf(stdout, "%s:%d", cupsServer(), ippPort());
  	      op = 'H';
  	      break;
  
  	  case 'P' : /* Show paper types */
  	      op = 'P';
  	      break;
  
  	  case 'R' : /* Show ranking */
  	      ranking = 1;
  	      break;
  
  	  case 'S' : /* Show charsets */
  	      op = 'S';
  	      if (!argv[i][2])
  		i ++;
  	      break;
  
  	  case 'U' : /* Username */
  	      if (opt[1] != '\0')
  	      {
  		cupsSetUser(opt + 1);
  		opt += strlen(opt) - 1;
  	      }
  	      else
  	      {
  		i ++;
  		if (i >= argc)
  		{
  		  _cupsLangPrintf(stderr, _("%s: Error - expected username after \"-U\" option."), argv[0]);
  		  usage();
  		}
  
  		cupsSetUser(argv[i]);
  	      }
  	      break;
  
  	  case 'W' : /* Show which jobs? */
  	      if (opt[1] != '\0')
  	      {
  		which = opt + 1;
  		opt += strlen(opt) - 1;
  	      }
  	      else
  	      {
  		i ++;
  
  		if (i >= argc)
  		{
  		  _cupsLangPrintf(stderr, _("%s: Error - need \"completed\", \"not-completed\", or \"all\" after \"-W\" option."), argv[0]);
  		  usage();
  		}
  
  		which = argv[i];
  	      }
  
  	      if (strcmp(which, "completed") && strcmp(which, "not-completed") && strcmp(which, "all"))
  	      {
  		_cupsLangPrintf(stderr, _("%s: Error - need \"completed\", \"not-completed\", or \"all\" after \"-W\" option."), argv[0]);
  		usage();
  	      }
  	      break;
  
  	  case 'a' : /* Show acceptance status */
  	      op = 'a';
  
  	      if (opt[1] != '\0')
  	      {
  		check_dest(argv[0], opt + 1, &num_dests, &dests);
  
  		status |= show_accepting(opt + 1, num_dests, dests);
  	        opt += strlen(opt) - 1;
  	      }
  	      else if ((i + 1) < argc && argv[i + 1][0] != '-')
  	      {
  		i ++;
  
  		check_dest(argv[0], argv[i], &num_dests, &dests);
  
  		status |= show_accepting(argv[i], num_dests, dests);
  	      }
  	      else
  	      {
  		if (num_dests <= 1)
  		{
  		  cupsFreeDests(num_dests, dests);
  		  num_dests = cupsGetDests(&dests);
  
  		  if (num_dests == 0 && (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST || cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
  		  {
  		    _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
  		    return (1);
  		  }
  		}
  
  		status |= show_accepting(NULL, num_dests, dests);
  	      }
  	      break;
  
  	  case 'c' : /* Show classes and members */
  	      op = 'c';
  
  	      if (opt[1] != '\0')
  	      {
  		check_dest(argv[0], opt + 1, &num_dests, &dests);
  
  		status |= show_classes(opt + 1);
  	        opt += strlen(opt) - 1;
  	      }
  	      else if ((i + 1) < argc && argv[i + 1][0] != '-')
  	      {
  		i ++;
  
  		check_dest(argv[0], argv[i], &num_dests, &dests);
  
  		status |= show_classes(argv[i]);
  	      }
  	      else
  		status |= show_classes(NULL);
  	      break;
  
  	  case 'd' : /* Show default destination */
  	      op = 'd';
  
  	      if (num_dests != 1 || !dests[0].is_default)
  	      {
  		cupsFreeDests(num_dests, dests);
  
  		dests     = cupsGetNamedDest(CUPS_HTTP_DEFAULT, NULL, NULL);
  		num_dests = dests ? 1 : 0;
  
  		if (num_dests == 0 &&
  		    (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
  		     cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
  		{
  		  _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
  		  return (1);
  		}
  	      }
  
  	      show_default(dests);
  	      break;
  
  	  case 'e' : /* List destinations */
  	      {
                  cups_dest_t *temp = NULL, *dest;
                  int j, num_temp = cupsGetDests(&temp);
  
                  op = 'e';
  
                  for (j = num_temp, dest = temp; j > 0; j --, dest ++)
                  {
                    if (dest->instance)
                      printf("%s/%s", dest->name, dest->instance);
                    else
                      fputs(dest->name, stdout);
  
                    if (long_status)
                    {
                      const char *printer_uri_supported = cupsGetOption("printer-uri-supported", dest->num_options, dest->options);
                      const char *printer_is_temporary = cupsGetOption("printer-is-temporary", dest->num_options, dest->options);
                      const char *type = "network";
  
                      if (printer_is_temporary && !strcmp(printer_is_temporary, "true"))
                        type = "temporary";
                      else if (printer_uri_supported)
                        type = "permanent";
  
                      printf(" %s %s %s\n", type, printer_uri_supported ? printer_uri_supported : "none", cupsGetOption("device-uri", dest->num_options, dest->options));
                    }
                    else
                      putchar('\n');
                  }
  
                  cupsFreeDests(num_temp, temp);
                }
                break;
  
  	  case 'f' : /* Show forms */
  	      op   = 'f';
  	      if (opt[1] != '\0')
  	      {
  	        opt += strlen(opt) - 1;
  	      }
  	      else
  	      {
  		i ++;
  		if (i >= argc)
  		  return (1);
  	      }
  	      break;
  
  	  case 'h' : /* Connect to host */
  	      if (opt[1] != '\0')
  	      {
  		cupsSetServer(opt + 1);
  	        opt += strlen(opt) - 1;
  	      }
  	      else
  	      {
  		i ++;
  
  		if (i >= argc)
  		{
  		  _cupsLangPrintf(stderr, _("%s: Error - expected hostname after \"-h\" option."), argv[0]);
  		  return (1);
  		}
  
  		cupsSetServer(argv[i]);
  	      }
  	      break;
  
  	  case 'l' : /* Long status or long job status */
  	      long_status = 2;
  	      break;
  
  	  case 'o' : /* Show jobs by destination */
  	      op = 'o';
  
  	      if (opt[1])
  	      {
  		check_dest(argv[0], opt + 1, &num_dests, &dests);
  
  		status |= show_jobs(opt + 1, NULL, long_status, ranking, which);
  	        opt += strlen(opt) - 1;
  	      }
  	      else if ((i + 1) < argc && argv[i + 1][0] != '-')
  	      {
  		i ++;
  
  		check_dest(argv[0], argv[i], &num_dests, &dests);
  
  		status |= show_jobs(argv[i], NULL, long_status, ranking, which);
  	      }
  	      else
  		status |= show_jobs(NULL, NULL, long_status, ranking, which);
  	      break;
  
  	  case 'p' : /* Show printers */
  	      op = 'p';
  
  	      if (opt[1] != '\0')
  	      {
  		check_dest(argv[0], opt + 1, &num_dests, &dests);
  
  		status |= show_printers(opt + 1, num_dests, dests,
  					long_status);
  	        opt += strlen(opt) - 1;
  	      }
  	      else if ((i + 1) < argc && argv[i + 1][0] != '-')
  	      {
  		i ++;
  
  		check_dest(argv[0], argv[i], &num_dests, &dests);
  
  		status |= show_printers(argv[i], num_dests, dests, long_status);
  	      }
  	      else
  	      {
  		if (num_dests <= 1)
  		{
  		  cupsFreeDests(num_dests, dests);
  		  num_dests = cupsGetDests(&dests);
  
  		  if (num_dests == 0 &&
  		      (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
  		       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
  		  {
  		    _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
  		    return (1);
  		  }
  		}
  
  		status |= show_printers(NULL, num_dests, dests, long_status);
  	      }
  	      break;
  
  	  case 'r' : /* Show scheduler status */
  	      op = 'r';
  
  	      if (!show_scheduler())
  		return (0);
  	      break;
  
  	  case 's' : /* Show summary */
  	      op = 's';
  
  	      if (num_dests <= 1)
  	      {
  		cupsFreeDests(num_dests, dests);
  		num_dests = cupsGetDests(&dests);
  
  		if (num_dests == 0 &&
  		    (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
  		     cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
  		{
  		  _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
  		  return (1);
  		}
  	      }
  
  	      show_default(cupsGetDest(NULL, NULL, num_dests, dests));
  	      status |= show_classes(NULL);
  	      status |= show_devices(NULL, num_dests, dests);
  	      break;
  
  	  case 't' : /* Show all info */
  	      op = 't';
  
  	      if (num_dests <= 1)
  	      {
  		cupsFreeDests(num_dests, dests);
  		num_dests = cupsGetDests(&dests);
  
  		if (num_dests == 0 &&
  		    (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
  		     cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
  		{
  		  _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
  		  return (1);
  		}
  	      }
  
  	      if (!show_scheduler())
  		return (0);
  
  	      show_default(cupsGetDest(NULL, NULL, num_dests, dests));
  	      status |= show_classes(NULL);
  	      status |= show_devices(NULL, num_dests, dests);
  	      status |= show_accepting(NULL, num_dests, dests);
  	      status |= show_printers(NULL, num_dests, dests, long_status);
  	      status |= show_jobs(NULL, NULL, long_status, ranking, which);
  	      break;
  
  	  case 'u' : /* Show jobs by user */
  	      op = 'u';
  
  	      if (opt[1] != '\0')
  	      {
  		status |= show_jobs(NULL, opt + 1, long_status, ranking, which);
  	        opt += strlen(opt) - 1;
  	      }
  	      else if ((i + 1) < argc && argv[i + 1][0] != '-')
  	      {
  		i ++;
  		status |= show_jobs(NULL, argv[i], long_status, ranking, which);
  	      }
  	      else
  		status |= show_jobs(NULL, NULL, long_status, ranking, which);
  	      break;
  
  	  case 'v' : /* Show printer devices */
  	      op = 'v';
  
  	      if (opt[1] != '\0')
  	      {
  		check_dest(argv[0], opt + 1, &num_dests, &dests);
  
  		status |= show_devices(opt + 1, num_dests, dests);
  	        opt += strlen(opt) - 1;
  	      }
  	      else if ((i + 1) < argc && argv[i + 1][0] != '-')
  	      {
  		i ++;
  
  		check_dest(argv[0], argv[i], &num_dests, &dests);
  
  		status |= show_devices(argv[i], num_dests, dests);
  	      }
  	      else
  	      {
  		if (num_dests <= 1)
  		{
  		  cupsFreeDests(num_dests, dests);
  		  num_dests = cupsGetDests(&dests);
  
  		  if (num_dests == 0 &&
  		      (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
  		       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
  		  {
  		    _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
  		    return (1);
  		  }
  		}
  
  		status |= show_devices(NULL, num_dests, dests);
  	      }
  	      break;
  
  	  default :
  	      _cupsLangPrintf(stderr, _("%s: Error - unknown option \"%c\"."), argv[0], argv[i][1]);
  	      usage();
  	}
        }
      }
      else
      {
        status |= show_jobs(argv[i], NULL, long_status, ranking, which);
        op = 'o';
      }
    }
  
    if (!op)
      status |= show_jobs(NULL, cupsUser(), long_status, ranking, which);
  
    return (status);
  }
  
  
  /*
   * 'check_dest()' - Verify that the named destination(s) exists.
   */
  
  static void
  check_dest(const char  *command,	/* I  - Command name */
             const char  *name,		/* I  - List of printer/class names */
             int         *num_dests,	/* IO - Number of destinations */
  	   cups_dest_t **dests)		/* IO - Destinations */
  {
    const char	*dptr;			/* Pointer into name */
    char		*pptr,			/* Pointer into printer */
  		printer[1024];		/* Current printer/class name */
  
  
   /*
    * Load the destination list as necessary...
    */
  
    if (*num_dests <= 1)
    {
      if (*num_dests)
        cupsFreeDests(*num_dests, *dests);
  
      if (strchr(name, ','))
        *num_dests = cupsGetDests(dests);
      else
      {
        strlcpy(printer, name, sizeof(printer));
        if ((pptr = strchr(printer, '/')) != NULL)
          *pptr++ = '\0';
  
        if ((*dests = cupsGetNamedDest(CUPS_HTTP_DEFAULT, printer, pptr)) == NULL)
        {
  	if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
  	    cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
  	  _cupsLangPrintf(stderr,
  			  _("%s: Error - add '/version=1.1' to server name."),
  			  command);
  	else
  	  _cupsLangPrintf(stderr,
  			  _("%s: Invalid destination name in list \"%s\"."),
  			  command, name);
  
          exit(1);
        }
        else
        {
          *num_dests = 1;
          return;
        }
      }
    }
  
   /*
    * Scan the name string for printer/class name(s)...
    */
  
    for (dptr = name; *dptr;)
    {
     /*
      * Skip leading whitespace and commas...
      */
  
      while (isspace(*dptr & 255) || *dptr == ',')
        dptr ++;
  
      if (!*dptr)
        break;
  
     /*
      * Extract a single destination name from the name string...
      */
  
      for (pptr = printer; !isspace(*dptr & 255) && *dptr != ',' && *dptr;)
      {
        if ((size_t)(pptr - printer) < (sizeof(printer) - 1))
          *pptr++ = *dptr++;
        else
        {
          _cupsLangPrintf(stderr,
  	                _("%s: Invalid destination name in list \"%s\"."),
  			command, name);
          exit(1);
        }
      }
  
      *pptr = '\0';
  
     /*
      * Check the destination...
      */
  
      if (!cupsGetDest(printer, NULL, *num_dests, *dests))
      {
        if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
            cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
  	_cupsLangPrintf(stderr,
  	                _("%s: Error - add '/version=1.1' to server name."),
  			command);
        else
  	_cupsLangPrintf(stderr,
  			_("%s: Unknown destination \"%s\"."), command, printer);
  
        exit(1);
      }
    }
  }
  
  
  /*
   * 'match_list()' - Match a name from a list of comma or space-separated names.
   */
  
  static int				/* O - 1 on match, 0 on no match */
  match_list(const char *list,		/* I - List of names */
             const char *name)		/* I - Name to find */
  {
    const char	*nameptr;		/* Pointer into name */
  
  
   /*
    * An empty list always matches...
    */
  
    if (!list || !*list)
      return (1);
  
    if (!name)
      return (0);
  
    do
    {
     /*
      * Skip leading whitespace and commas...
      */
  
      while (isspace(*list & 255) || *list == ',')
        list ++;
  
      if (!*list)
        break;
  
     /*
      * Compare names...
      */
  
      for (nameptr = name;
  	 *nameptr && *list && tolower(*nameptr & 255) == tolower(*list & 255);
  	 nameptr ++, list ++);
  
      if (!*nameptr && (!*list || *list == ',' || isspace(*list & 255)))
        return (1);
  
      while (*list && !isspace(*list & 255) && *list != ',')
        list ++;
    }
    while (*list);
  
    return (0);
  }
  
  
  /*
   * 'show_accepting()' - Show acceptance status.
   */
  
  static int				/* O - 0 on success, 1 on fail */
  show_accepting(const char  *printers,	/* I - Destinations */
                 int         num_dests,	/* I - Number of user-defined dests */
  	       cups_dest_t *dests)	/* I - User-defined destinations */
  {
    int		i;			/* Looping var */
    ipp_t		*request,		/* IPP Request */
  		*response;		/* IPP Response */
    ipp_attribute_t *attr;		/* Current attribute */
    const char	*printer,		/* Printer name */
  		*message;		/* Printer device URI */
    int		accepting;		/* Accepting requests? */
    time_t	ptime;			/* Printer state time */
    char		printer_state_time[255];/* Printer state time */
    static const char *pattrs[] =		/* Attributes we need for printers... */
  		{
  		  "printer-name",
  		  "printer-state-change-time",
  		  "printer-state-message",
  		  "printer-is-accepting-jobs"
  		};
  
  
    if (printers != NULL && !strcmp(printers, "all"))
      printers = NULL;
  
   /*
    * Build a CUPS_GET_PRINTERS request, which requires the following
    * attributes:
    *
    *    attributes-charset
    *    attributes-natural-language
    *    requested-attributes
    *    requesting-user-name
    */
  
    request = ippNewRequest(CUPS_GET_PRINTERS);
  
    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
                  "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
  		NULL, pattrs);
  
    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
                 NULL, cupsUser());
  
   /*
    * Do the request and get back a response...
    */
  
    response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
  
    if (cupsLastError() == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE)
    {
      _cupsLangPrintf(stderr, _("%s: Scheduler is not running."), "lpstat");
      ippDelete(response);
      return (1);
    }
    else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST || cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
    {
      _cupsLangPrintf(stderr,
  		    _("%s: Error - add '/version=1.1' to server name."),
  		    "lpstat");
      ippDelete(response);
      return (1);
    }
    else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
    {
      _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
      ippDelete(response);
      return (1);
    }
  
    if (response)
    {
     /*
      * Loop through the printers returned in the list and display
      * their devices...
      */
  
      for (attr = response->attrs; attr != NULL; attr = attr->next)
      {
       /*
        * Skip leading attributes until we hit a printer...
        */
  
        while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
          attr = attr->next;
  
        if (attr == NULL)
          break;
  
       /*
        * Pull the needed attributes from this printer...
        */
  
        printer   = NULL;
        message   = NULL;
        accepting = 1;
        ptime     = 0;
  
        while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
        {
          if (!strcmp(attr->name, "printer-name") &&
  	    attr->value_tag == IPP_TAG_NAME)
  	  printer = attr->values[0].string.text;
          else if (!strcmp(attr->name, "printer-state-change-time") &&
  	         attr->value_tag == IPP_TAG_INTEGER)
  	  ptime = (time_t)attr->values[0].integer;
          else if (!strcmp(attr->name, "printer-state-message") &&
  	         attr->value_tag == IPP_TAG_TEXT)
  	  message = attr->values[0].string.text;
          else if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
  	         attr->value_tag == IPP_TAG_BOOLEAN)
  	  accepting = attr->values[0].boolean;
  
          attr = attr->next;
        }
  
       /*
        * See if we have everything needed...
        */
  
        if (printer == NULL)
        {
          if (attr == NULL)
  	  break;
  	else
            continue;
        }
  
       /*
        * Display the printer entry if needed...
        */
  
        if (match_list(printers, printer))
        {
          _cupsStrDate(printer_state_time, sizeof(printer_state_time), ptime);
  
          if (accepting)
  	  _cupsLangPrintf(stdout, _("%s accepting requests since %s"),
  			  printer, printer_state_time);
  	else
  	{
  	  _cupsLangPrintf(stdout, _("%s not accepting requests since %s -"),
  			  printer, printer_state_time);
  	  _cupsLangPrintf(stdout, _("\t%s"),
  			  (message && *message) ?
  			      message : "reason unknown");
          }
  
          for (i = 0; i < num_dests; i ++)
  	  if (!_cups_strcasecmp(dests[i].name, printer) && dests[i].instance)
  	  {
              if (accepting)
  	      _cupsLangPrintf(stdout, _("%s/%s accepting requests since %s"),
  			      printer, dests[i].instance, printer_state_time);
  	    else
  	    {
  	      _cupsLangPrintf(stdout,
  	                      _("%s/%s not accepting requests since %s -"),
  			      printer, dests[i].instance, printer_state_time);
  	      _cupsLangPrintf(stdout, _("\t%s"),
  	        	      (message && *message) ?
  			          message : "reason unknown");
              }
  	  }
        }
  
        if (attr == NULL)
          break;
      }
  
      ippDelete(response);
    }
  
    return (0);
  }
  
  
  /*
   * 'show_classes()' - Show printer classes.
   */
  
  static int				/* O - 0 on success, 1 on fail */
  show_classes(const char *dests)		/* I - Destinations */
  {
    int		i;			/* Looping var */
    ipp_t		*request,		/* IPP Request */
  		*response,		/* IPP Response */
  		*response2;		/* IPP response from remote server */
    http_t	*http2;			/* Remote server */
    ipp_attribute_t *attr;		/* Current attribute */
    const char	*printer,		/* Printer class name */
  		*printer_uri;		/* Printer class URI */
    ipp_attribute_t *members;		/* Printer members */
    char		method[HTTP_MAX_URI],	/* Request method */
  		username[HTTP_MAX_URI],	/* Username:password */
  		server[HTTP_MAX_URI],	/* Server name */
  		resource[HTTP_MAX_URI];	/* Resource name */
    int		port;			/* Port number */
    static const char *cattrs[] =		/* Attributes we need for classes... */
  		{
  		  "printer-name",
  		  "printer-uri-supported",
  		  "member-names"
  		};
  
  
    if (dests != NULL && !strcmp(dests, "all"))
      dests = NULL;
  
   /*
    * Build a CUPS_GET_CLASSES request, which requires the following
    * attributes:
    *
    *    attributes-charset
    *    attributes-natural-language
    *    requested-attributes
    *    requesting-user-name
    */
  
    request = ippNewRequest(CUPS_GET_CLASSES);
  
    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
                  "requested-attributes", sizeof(cattrs) / sizeof(cattrs[0]),
  		NULL, cattrs);
  
    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
                 NULL, cupsUser());
  
   /*
    * Do the request and get back a response...
    */
  
    response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
  
    if (cupsLastError() == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE)
    {
      _cupsLangPrintf(stderr, _("%s: Scheduler is not running."), "lpstat");
      ippDelete(response);
      return (1);
    }
    if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
        cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
    {
      _cupsLangPrintf(stderr,
  		    _("%s: Error - add '/version=1.1' to server name."),
  		    "lpstat");
      ippDelete(response);
      return (1);
    }
    else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
    {
      _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
      ippDelete(response);
      return (1);
    }
  
    if (response)
    {
      if (response->request.status.status_code > IPP_OK_CONFLICT)
      {
        _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
        ippDelete(response);
        return (1);
      }
  
     /*
      * Loop through the printers returned in the list and display
      * their devices...
      */
  
      for (attr = response->attrs; attr != NULL; attr = attr->next)
      {
       /*
        * Skip leading attributes until we hit a job...
        */
  
        while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
          attr = attr->next;
  
        if (attr == NULL)
          break;
  
       /*
        * Pull the needed attributes from this job...
        */
  
        printer     = NULL;
        printer_uri = NULL;
        members     = NULL;
  
        do
        {
          if (!strcmp(attr->name, "printer-name") &&
  	    attr->value_tag == IPP_TAG_NAME)
  	  printer = attr->values[0].string.text;
  
          if (!strcmp(attr->name, "printer-uri-supported") &&
  	    attr->value_tag == IPP_TAG_URI)
  	  printer_uri = attr->values[0].string.text;
  
          if (!strcmp(attr->name, "member-names") &&
  	    attr->value_tag == IPP_TAG_NAME)
  	  members = attr;
  
          attr = attr->next;
        }
  	  while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER);
  
       /*
        * If this is a remote class, grab the class info from the
        * remote server...
        */
  
        response2 = NULL;
        if (members == NULL && printer_uri != NULL)
        {
          httpSeparateURI(HTTP_URI_CODING_ALL, printer_uri, method, sizeof(method),
  	                username, sizeof(username), server, sizeof(server),
  			&port, resource, sizeof(resource));
  
          if (!_cups_strcasecmp(server, cupsServer()))
  	  http2 = CUPS_HTTP_DEFAULT;
  	else
  	  http2 = httpConnectEncrypt(server, port, cupsEncryption());
  
         /*
  	* Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
  	* following attributes:
  	*
  	*    attributes-charset
  	*    attributes-natural-language
  	*    printer-uri
  	*    requested-attributes
  	*/
  
  	request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
  
  	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
  		     "printer-uri", NULL, printer_uri);
  
  	ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
  		      "requested-attributes",
  		      sizeof(cattrs) / sizeof(cattrs[0]),
  		      NULL, cattrs);
  
  	if ((response2 = cupsDoRequest(http2, request, "/")) != NULL)
  	  members = ippFindAttribute(response2, "member-names", IPP_TAG_NAME);
  
  	if (http2)
  	  httpClose(http2);
        }
  
       /*
        * See if we have everything needed...
        */
  
        if (printer == NULL)
        {
          if (response2)
  	  ippDelete(response2);
  
          if (attr == NULL)
  	  break;
  	else
            continue;
        }
  
       /*
        * Display the printer entry if needed...
        */
  
        if (match_list(dests, printer))
        {
          _cupsLangPrintf(stdout, _("members of class %s:"), printer);
  
  	if (members)
  	{
  	  for (i = 0; i < members->num_values; i ++)
  	    _cupsLangPrintf(stdout, "\t%s", members->values[i].string.text);
          }
  	else
  	  _cupsLangPuts(stdout, "\tunknown");
        }
  
        if (response2)
  	ippDelete(response2);
  
        if (attr == NULL)
          break;
      }
  
      ippDelete(response);
    }
  
    return (0);
  }
  
  
  /*
   * 'show_default()' - Show default destination.
   */
  
  static void
  show_default(cups_dest_t *dest)		/* I - Default destination */
  {
    const char	*printer,		/* Printer name */
  		*val;			/* Environment variable name */
  
  
    if (dest)
    {
      if (dest->instance)
        _cupsLangPrintf(stdout, _("system default destination: %s/%s"),
                        dest->name, dest->instance);
      else
        _cupsLangPrintf(stdout, _("system default destination: %s"),
                        dest->name);
    }
    else
    {
      val = NULL;
  
      if ((printer = getenv("LPDEST")) == NULL)
      {
        if ((printer = getenv("PRINTER")) != NULL)
        {
          if (!strcmp(printer, "lp"))
            printer = NULL;
  	else
  	  val = "PRINTER";
        }
      }
      else
        val = "LPDEST";
  
      if (printer)
        _cupsLangPrintf(stdout,
                        _("lpstat: error - %s environment variable names "
  		        "non-existent destination \"%s\"."),
          	      val, printer);
      else
        _cupsLangPuts(stdout, _("no system default destination"));
    }
  }
  
  
  /*
   * 'show_devices()' - Show printer devices.
   */
  
  static int				/* O - 0 on success, 1 on fail */
  show_devices(const char  *printers,	/* I - Destinations */
               int         num_dests,	/* I - Number of user-defined dests */
  	     cups_dest_t *dests)	/* I - User-defined destinations */
  {
    int		i;			/* Looping var */
    ipp_t		*request,		/* IPP Request */
  		*response;		/* IPP Response */
    ipp_attribute_t *attr;		/* Current attribute */
    const char	*printer,		/* Printer name */
  		*uri,			/* Printer URI */
  		*device;		/* Printer device URI */
    static const char *pattrs[] =		/* Attributes we need for printers... */
  		{
  		  "printer-name",
  		  "printer-uri-supported",
  		  "device-uri"
  		};
  
  
    if (printers != NULL && !strcmp(printers, "all"))
      printers = NULL;
  
   /*
    * Build a CUPS_GET_PRINTERS request, which requires the following
    * attributes:
    *
    *    attributes-charset
    *    attributes-natural-language
    *    requested-attributes
    *    requesting-user-name
    */
  
    request = ippNewRequest(CUPS_GET_PRINTERS);
  
    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
                  "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
  		NULL, pattrs);
  
    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
                 NULL, cupsUser());
  
   /*
    * Do the request and get back a response...
    */
  
    response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
  
    if (cupsLastError() == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE)
    {
      _cupsLangPrintf(stderr, _("%s: Scheduler is not running."), "lpstat");
      ippDelete(response);
      return (1);
    }
    if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
        cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
    {
      _cupsLangPrintf(stderr,
  		    _("%s: Error - add '/version=1.1' to server name."),
  		    "lpstat");
      ippDelete(response);
      return (1);
    }
    else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
    {
      _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
      ippDelete(response);
      return (1);
    }
  
    if (response)
    {
     /*
      * Loop through the printers returned in the list and display
      * their devices...
      */
  
      for (attr = response->attrs; attr != NULL; attr = attr->next)
      {
       /*
        * Skip leading attributes until we hit a job...
        */
  
        while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
          attr = attr->next;
  
        if (attr == NULL)
          break;
  
       /*
        * Pull the needed attributes from this job...
        */
  
        printer = NULL;
        device  = NULL;
        uri     = NULL;
  
        while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
        {
          if (!strcmp(attr->name, "printer-name") &&
  	    attr->value_tag == IPP_TAG_NAME)
  	  printer = attr->values[0].string.text;
  
          if (!strcmp(attr->name, "printer-uri-supported") &&
  	    attr->value_tag == IPP_TAG_URI)
  	  uri = attr->values[0].string.text;
  
          if (!strcmp(attr->name, "device-uri") &&
  	    attr->value_tag == IPP_TAG_URI)
  	  device = attr->values[0].string.text;
  
          attr = attr->next;
        }
  
       /*
        * See if we have everything needed...
        */
  
        if (printer == NULL)
        {
          if (attr == NULL)
  	  break;
  	else
            continue;
        }
  
       /*
        * Display the printer entry if needed...
        */
  
        if (match_list(printers, printer))
        {
          if (device == NULL)
            _cupsLangPrintf(stdout, _("device for %s: %s"),
  	                  printer, uri);
          else if (!strncmp(device, "file:", 5))
            _cupsLangPrintf(stdout, _("device for %s: %s"),
  	                  printer, device + 5);
          else
            _cupsLangPrintf(stdout, _("device for %s: %s"),
  	                  printer, device);
  
          for (i = 0; i < num_dests; i ++)
          {
  	  if (!_cups_strcasecmp(printer, dests[i].name) && dests[i].instance)
  	  {
              if (device == NULL)
                _cupsLangPrintf(stdout, _("device for %s/%s: %s"),
  	                      printer, dests[i].instance, uri);
              else if (!strncmp(device, "file:", 5))
                _cupsLangPrintf(stdout, _("device for %s/%s: %s"),
  	                      printer, dests[i].instance, device + 5);
              else
                _cupsLangPrintf(stdout, _("device for %s/%s: %s"),
  	                      printer, dests[i].instance, device);
  	  }
  	}
        }
  
        if (attr == NULL)
          break;
      }
  
      ippDelete(response);
    }
  
    return (0);
  }
  
  
  /*
   * 'show_jobs()' - Show active print jobs.
   */
  
  static int				/* O - 0 on success, 1 on fail */
  show_jobs(const char *dests,		/* I - Destinations */
            const char *users,		/* I - Users */
            int        long_status,	/* I - Show long status? */
            int        ranking,		/* I - Show job ranking? */
  	  const char *which)		/* I - Show which jobs? */
  {
    int		i;			/* Looping var */
    ipp_t		*request,		/* IPP Request */
  		*response;		/* IPP Response */
    ipp_attribute_t *attr,		/* Current attribute */
  		*reasons;		/* Job state reasons attribute */
    const char	*dest,			/* Pointer into job-printer-uri */
  		*username,		/* Pointer to job-originating-user-name */
  		*message,		/* Pointer to job-printer-state-message */
  		*time_at;		/* time-at-xxx attribute name to use */
    int		rank,			/* Rank in queue */
  		jobid,			/* job-id */
  		size;			/* job-k-octets */
    time_t	jobtime;		/* time-at-creation */
    char		temp[255],		/* Temporary buffer */
  		date[255];		/* Date buffer */
    static const char *jattrs[] =		/* Attributes we need for jobs... */
  		{
  		  "job-id",
  		  "job-k-octets",
  		  "job-name",
  		  "job-originating-user-name",
  		  "job-printer-state-message",
  		  "job-printer-uri",
  		  "job-state-reasons",
  		  "time-at-creation",
  		  "time-at-completed"
  		};
  
  
    if (dests != NULL && !strcmp(dests, "all"))
      dests = NULL;
  
   /*
    * Build a IPP_GET_JOBS request, which requires the following
    * attributes:
    *
    *    attributes-charset
    *    attributes-natural-language
    *    printer-uri
    *    requested-attributes
    *    requesting-user-name
    *    which-jobs
    */
  
    request = ippNewRequest(IPP_GET_JOBS);
  
    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
                 NULL, "ipp://localhost/");
  
    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
                  "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
  		NULL, jattrs);
  
    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
                 NULL, cupsUser());
  
    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "which-jobs",
                 NULL, which);
  
   /*
    * Do the request and get back a response...
    */
  
    response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
  
    if (cupsLastError() == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE)
    {
      _cupsLangPrintf(stderr, _("%s: Scheduler is not running."), "lpstat");
      ippDelete(response);
      return (1);
    }
    if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
        cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
    {
      _cupsLangPrintf(stderr,
  		    _("%s: Error - add '/version=1.1' to server name."),
  		    "lpstat");
      ippDelete(response);
      return (1);
    }
    else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
    {
      _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
      ippDelete(response);
      return (1);
    }
  
    if (response)
    {
     /*
      * Loop through the job list and display them...
      */
  
      if (!strcmp(which, "aborted") ||
          !strcmp(which, "canceled") ||
          !strcmp(which, "completed"))
        time_at = "time-at-completed";
      else
        time_at = "time-at-creation";
  
      rank = -1;
  
      for (attr = response->attrs; attr != NULL; attr = attr->next)
      {
       /*
        * Skip leading attributes until we hit a job...
        */
  
        while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
          attr = attr->next;
  
        if (attr == NULL)
          break;
  
       /*
        * Pull the needed attributes from this job...
        */
  
        jobid    = 0;
        size     = 0;
        username = NULL;
        dest     = NULL;
        jobtime  = 0;
        message  = NULL;
        reasons  = NULL;
  
        while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
        {
          if (!strcmp(attr->name, "job-id") &&
  	    attr->value_tag == IPP_TAG_INTEGER)
  	  jobid = attr->values[0].integer;
          else if (!strcmp(attr->name, "job-k-octets") &&
  		 attr->value_tag == IPP_TAG_INTEGER)
  	  size = attr->values[0].integer;
          else if (!strcmp(attr->name, time_at) && attr->value_tag == IPP_TAG_INTEGER)
  	  jobtime = attr->values[0].integer;
          else if (!strcmp(attr->name, "job-printer-state-message") &&
  	         attr->value_tag == IPP_TAG_TEXT)
  	  message = attr->values[0].string.text;
          else if (!strcmp(attr->name, "job-printer-uri") &&
  	         attr->value_tag == IPP_TAG_URI)
  	{
  	  if ((dest = strrchr(attr->values[0].string.text, '/')) != NULL)
  	    dest ++;
          }
          else if (!strcmp(attr->name, "job-originating-user-name") &&
  	         attr->value_tag == IPP_TAG_NAME)
  	  username = attr->values[0].string.text;
          else if (!strcmp(attr->name, "job-state-reasons") &&
  	         attr->value_tag == IPP_TAG_KEYWORD)
  	  reasons = attr;
  
          attr = attr->next;
        }
  
       /*
        * See if we have everything needed...
        */
  
        if (dest == NULL || jobid == 0)
        {
          if (attr == NULL)
  	  break;
  	else
            continue;
        }
  
       /*
        * Display the job...
        */
  
        rank ++;
  
        if (match_list(dests, dest) && match_list(users, username))
        {
          snprintf(temp, sizeof(temp), "%s-%d", dest, jobid);
  
  	_cupsStrDate(date, sizeof(date), jobtime);
  
  	if (ranking)
  	  _cupsLangPrintf(stdout, "%3d %-21s %-13s %8.0f %s",
  			  rank, temp, username ? username : "unknown",
  			  1024.0 * size, date);
  	else
  	  _cupsLangPrintf(stdout, "%-23s %-13s %8.0f   %s",
  			  temp, username ? username : "unknown",
  			  1024.0 * size, date);
  	if (long_status)
  	{
  	  if (message)
  	    _cupsLangPrintf(stdout, _("\tStatus: %s"), message);
  
  	  if (reasons)
  	  {
  	    char	alerts[1024],	/* Alerts string */
  		      *aptr;		/* Pointer into alerts string */
  
  	    for (i = 0, aptr = alerts; i < reasons->num_values; i ++)
  	    {
  	      if (i)
  		snprintf(aptr, sizeof(alerts) - (size_t)(aptr - alerts), " %s", reasons->values[i].string.text);
  	      else
  		strlcpy(alerts, reasons->values[i].string.text, sizeof(alerts));
  
  	      aptr += strlen(aptr);
  	    }
  
  	    _cupsLangPrintf(stdout, _("\tAlerts: %s"), alerts);
  	  }
  
  	  _cupsLangPrintf(stdout, _("\tqueued for %s"), dest);
  	}
        }
  
        if (attr == NULL)
          break;
      }
  
      ippDelete(response);
    }
  
    return (0);
  }
  
  
  /*
   * 'show_printers()' - Show printers.
   */
  
  static int				/* O - 0 on success, 1 on fail */
  show_printers(const char  *printers,	/* I - Destinations */
                int         num_dests,	/* I - Number of user-defined dests */
  	      cups_dest_t *dests,	/* I - User-defined destinations */
                int         long_status)	/* I - Show long status? */
  {
    int		i, j;			/* Looping vars */
    ipp_t		*request,		/* IPP Request */
  		*response,		/* IPP Response */
  		*jobs;			/* IPP Get Jobs response */
    ipp_attribute_t *attr,		/* Current attribute */
  		*jobattr,		/* Job ID attribute */
  		*reasons;		/* Job state reasons attribute */
    const char	*printer,		/* Printer name */
  		*message,		/* Printer state message */
  		*description,		/* Description of printer */
  		*location,		/* Location of printer */
  		*make_model,		/* Make and model of printer */
  		*uri;			/* URI of printer */
    ipp_attribute_t *allowed,		/* requesting-user-name-allowed */
  		*denied;		/* requestint-user-name-denied */
    ipp_pstate_t	pstate;			/* Printer state */
    cups_ptype_t	ptype;			/* Printer type */
    time_t	ptime;			/* Printer state time */
    int		jobid;			/* Job ID of current job */
    char		printer_uri[HTTP_MAX_URI],
  					/* Printer URI */
  		printer_state_time[255];/* Printer state time */
    _cups_globals_t *cg = _cupsGlobals();	/* Global data */
    static const char *pattrs[] =		/* Attributes we need for printers... */
  		{
  		  "printer-name",
  		  "printer-state",
  		  "printer-state-message",
  		  "printer-state-reasons",
  		  "printer-state-change-time",
  		  "printer-type",
  		  "printer-info",
                    "printer-location",
  		  "printer-make-and-model",
  		  "printer-uri-supported",
  		  "requesting-user-name-allowed",
  		  "requesting-user-name-denied"
  		};
    static const char *jattrs[] =		/* Attributes we need for jobs... */
  		{
  		  "job-id",
  		  "job-state"
  		};
  
  
    if (printers != NULL && !strcmp(printers, "all"))
      printers = NULL;
  
   /*
    * Build a CUPS_GET_PRINTERS request, which requires the following
    * attributes:
    *
    *    attributes-charset
    *    attributes-natural-language
    *    requested-attributes
    *    requesting-user-name
    */
  
    request = ippNewRequest(CUPS_GET_PRINTERS);
  
    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
                  "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
  		NULL, pattrs);
  
    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
                 NULL, cupsUser());
  
   /*
    * Do the request and get back a response...
    */
  
    response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
  
    if (cupsLastError() == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE)
    {
      _cupsLangPrintf(stderr, _("%s: Scheduler is not running."), "lpstat");
      ippDelete(response);
      return (1);
    }
    if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
        cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
    {
      _cupsLangPrintf(stderr,
  		    _("%s: Error - add '/version=1.1' to server name."),
  		    "lpstat");
      ippDelete(response);
      return (1);
    }
    else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
    {
      _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
      ippDelete(response);
      return (1);
    }
  
    if (response)
    {
     /*
      * Loop through the printers returned in the list and display
      * their status...
      */
  
      for (attr = response->attrs; attr != NULL; attr = attr->next)
      {
       /*
        * Skip leading attributes until we hit a job...
        */
  
        while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
          attr = attr->next;
  
        if (attr == NULL)
          break;
  
       /*
        * Pull the needed attributes from this job...
        */
  
        printer     = NULL;
        ptime       = 0;
        ptype       = CUPS_PRINTER_LOCAL;
        pstate      = IPP_PRINTER_IDLE;
        message     = NULL;
        description = NULL;
        location    = NULL;
        make_model  = NULL;
        reasons     = NULL;
        uri         = NULL;
        jobid       = 0;
        allowed     = NULL;
        denied      = NULL;
  
        while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
        {
          if (!strcmp(attr->name, "printer-name") &&
  	    attr->value_tag == IPP_TAG_NAME)
  	  printer = attr->values[0].string.text;
          else if (!strcmp(attr->name, "printer-state") &&
  	         attr->value_tag == IPP_TAG_ENUM)
  	  pstate = (ipp_pstate_t)attr->values[0].integer;
          else if (!strcmp(attr->name, "printer-type") &&
  	         attr->value_tag == IPP_TAG_ENUM)
  	  ptype = (cups_ptype_t)attr->values[0].integer;
          else if (!strcmp(attr->name, "printer-state-message") &&
  	         attr->value_tag == IPP_TAG_TEXT)
  	  message = attr->values[0].string.text;
          else if (!strcmp(attr->name, "printer-state-change-time") &&
  	         attr->value_tag == IPP_TAG_INTEGER)
  	  ptime = (time_t)attr->values[0].integer;
  	else if (!strcmp(attr->name, "printer-info") &&
  	         attr->value_tag == IPP_TAG_TEXT)
  	  description = attr->values[0].string.text;
          else if (!strcmp(attr->name, "printer-location") &&
  	         attr->value_tag == IPP_TAG_TEXT)
  	  location = attr->values[0].string.text;
          else if (!strcmp(attr->name, "printer-make-and-model") &&
  	         attr->value_tag == IPP_TAG_TEXT)
  	  make_model = attr->values[0].string.text;
          else if (!strcmp(attr->name, "printer-uri-supported") &&
  	         attr->value_tag == IPP_TAG_URI)
  	  uri = attr->values[0].string.text;
          else if (!strcmp(attr->name, "printer-state-reasons") &&
  	         attr->value_tag == IPP_TAG_KEYWORD)
  	  reasons = attr;
          else if (!strcmp(attr->name, "requesting-user-name-allowed") &&
  	         attr->value_tag == IPP_TAG_NAME)
  	  allowed = attr;
          else if (!strcmp(attr->name, "requesting-user-name-denied") &&
  	         attr->value_tag == IPP_TAG_NAME)
  	  denied = attr;
  
          attr = attr->next;
        }
  
       /*
        * See if we have everything needed...
        */
  
        if (printer == NULL)
        {
          if (attr == NULL)
  	  break;
  	else
            continue;
        }
  
       /*
        * Display the printer entry if needed...
        */
  
        if (match_list(printers, printer))
        {
         /*
          * If the printer state is "IPP_PRINTER_PROCESSING", then grab the
  	* current job for the printer.
  	*/
  
          if (pstate == IPP_PRINTER_PROCESSING)
  	{
  	 /*
  	  * Build an IPP_GET_JOBS request, which requires the following
  	  * attributes:
  	  *
  	  *    attributes-charset
  	  *    attributes-natural-language
  	  *    printer-uri
  	  *    limit
            *    requested-attributes
  	  */
  
  	  request = ippNewRequest(IPP_GET_JOBS);
  
  	  request->request.op.operation_id = IPP_GET_JOBS;
  	  request->request.op.request_id   = 1;
  
  	  ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
                  	"requested-attributes",
  		        sizeof(jattrs) / sizeof(jattrs[0]), NULL, jattrs);
  
  	  httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
  	                   "ipp", NULL, "localhost", 0, "/printers/%s", printer);
  	  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
  	               "printer-uri", NULL, printer_uri);
  
            if ((jobs = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/")) != NULL)
  	  {
  	   /*
  	    * Get the current active job on this queue...
  	    */
  
              ipp_jstate_t jobstate = IPP_JOB_PENDING;
  	    jobid = 0;
  
  	    for (jobattr = jobs->attrs; jobattr; jobattr = jobattr->next)
  	    {
  	      if (!jobattr->name)
  	      {
  	        if (jobstate == IPP_JOB_PROCESSING)
  		  break;
  	        else
  		  continue;
                }
  
  	      if (!strcmp(jobattr->name, "job-id") &&
  	          jobattr->value_tag == IPP_TAG_INTEGER)
  		jobid = jobattr->values[0].integer;
                else if (!strcmp(jobattr->name, "job-state") &&
  	               jobattr->value_tag == IPP_TAG_ENUM)
  		jobstate = (ipp_jstate_t)jobattr->values[0].integer;
  	    }
  
              if (jobstate != IPP_JOB_PROCESSING)
  	      jobid = 0;
  
              ippDelete(jobs);
  	  }
          }
  
         /*
          * Display it...
  	*/
  
          _cupsStrDate(printer_state_time, sizeof(printer_state_time), ptime);
  
          switch (pstate)
  	{
  	  case IPP_PRINTER_IDLE :
  	      if (ippContainsString(reasons, "hold-new-jobs"))
  		_cupsLangPrintf(stdout, _("printer %s is holding new jobs.  enabled since %s"), printer, printer_state_time);
  	      else
  		_cupsLangPrintf(stdout, _("printer %s is idle.  enabled since %s"), printer, printer_state_time);
  	      break;
  	  case IPP_PRINTER_PROCESSING :
  	      _cupsLangPrintf(stdout, _("printer %s now printing %s-%d.  enabled since %s"), printer, printer, jobid, printer_state_time);
  	      break;
  	  case IPP_PRINTER_STOPPED :
  	      _cupsLangPrintf(stdout, _("printer %s disabled since %s -"), printer, printer_state_time);
  	      break;
  	}
  
          if ((message && *message) || pstate == IPP_PRINTER_STOPPED)
  	{
  	  if (message && *message)
  	  	_cupsLangPrintf(stdout, "\t%s", message);
  	  else
  	    _cupsLangPuts(stdout, _("\treason unknown"));
  	}
  
          if (long_status > 1)
  	{
  	  _cupsLangPuts(stdout, _("\tForm mounted:"));
  	  _cupsLangPuts(stdout, _("\tContent types: any"));
  	  _cupsLangPuts(stdout, _("\tPrinter types: unknown"));
  	}
  
          if (long_status)
  	{
  	  _cupsLangPrintf(stdout, _("\tDescription: %s"),
  	                  description ? description : "");
  
  	  if (reasons)
  	  {
  	    char	alerts[1024],	/* Alerts string */
  			*aptr;		/* Pointer into alerts string */
  
  	    for (i = 0, aptr = alerts; i < reasons->num_values; i ++)
  	    {
  	      if (i)
  		snprintf(aptr, sizeof(alerts) - (size_t)(aptr - alerts), " %s", reasons->values[i].string.text);
  	      else
  		strlcpy(alerts, reasons->values[i].string.text, sizeof(alerts));
  
  	      aptr += strlen(aptr);
  	    }
  
  	    _cupsLangPrintf(stdout, _("\tAlerts: %s"), alerts);
  	  }
  	}
          if (long_status > 1)
  	{
  	  _cupsLangPrintf(stdout, _("\tLocation: %s"),
  	                  location ? location : "");
  
  	  if (ptype & CUPS_PRINTER_REMOTE)
  	  {
  	    _cupsLangPuts(stdout, _("\tConnection: remote"));
  
  	    if (make_model && !strstr(make_model, "System V Printer") &&
  	             !strstr(make_model, "Raw Printer") && uri)
  	      _cupsLangPrintf(stdout, _("\tInterface: %s.ppd"),
  	                      uri);
  	  }
  	  else
  	  {
  	    _cupsLangPuts(stdout, _("\tConnection: direct"));
  
  	    if (make_model && !strstr(make_model, "Raw Printer"))
  	      _cupsLangPrintf(stdout,
  	                      _("\tInterface: %s/ppd/%s.ppd"),
  			      cg->cups_serverroot, printer);
            }
  	  _cupsLangPuts(stdout, _("\tOn fault: no alert"));
  	  _cupsLangPuts(stdout, _("\tAfter fault: continue"));
  	      /* TODO update to use printer-error-policy */
            if (allowed)
  	  {
  	    _cupsLangPuts(stdout, _("\tUsers allowed:"));
  	    for (j = 0; j < allowed->num_values; j ++)
  	      _cupsLangPrintf(stdout, "\t\t%s",
  	                      allowed->values[j].string.text);
  	  }
  	  else if (denied)
  	  {
  	    _cupsLangPuts(stdout, _("\tUsers denied:"));
  	    for (j = 0; j < denied->num_values; j ++)
  	      _cupsLangPrintf(stdout, "\t\t%s",
  	                      denied->values[j].string.text);
  	  }
  	  else
  	  {
  	    _cupsLangPuts(stdout, _("\tUsers allowed:"));
  	    _cupsLangPuts(stdout, _("\t\t(all)"));
  	  }
  	  _cupsLangPuts(stdout, _("\tForms allowed:"));
  	  _cupsLangPuts(stdout, _("\t\t(none)"));
  	  _cupsLangPuts(stdout, _("\tBanner required"));
  	  _cupsLangPuts(stdout, _("\tCharset sets:"));
  	  _cupsLangPuts(stdout, _("\t\t(none)"));
  	  _cupsLangPuts(stdout, _("\tDefault pitch:"));
  	  _cupsLangPuts(stdout, _("\tDefault page size:"));
  	  _cupsLangPuts(stdout, _("\tDefault port settings:"));
  	}
  
          for (i = 0; i < num_dests; i ++)
  	  if (!_cups_strcasecmp(printer, dests[i].name) && dests[i].instance)
  	  {
              switch (pstate)
  	    {
  	      case IPP_PRINTER_IDLE :
  		  _cupsLangPrintf(stdout,
  		                  _("printer %s/%s is idle.  "
  				    "enabled since %s"),
  				  printer, dests[i].instance,
  				  printer_state_time);
  		  break;
  	      case IPP_PRINTER_PROCESSING :
  		  _cupsLangPrintf(stdout,
  		                  _("printer %s/%s now printing %s-%d.  "
  				    "enabled since %s"),
  				  printer, dests[i].instance, printer, jobid,
  				  printer_state_time);
  		  break;
  	      case IPP_PRINTER_STOPPED :
  		  _cupsLangPrintf(stdout,
  		                  _("printer %s/%s disabled since %s -"),
  				  printer, dests[i].instance,
  				  printer_state_time);
  		  break;
  	    }
  
              if ((message && *message) || pstate == IPP_PRINTER_STOPPED)
  	    {
  	      if (message && *message)
  		_cupsLangPrintf(stdout, "\t%s", message);
  	      else
  		_cupsLangPuts(stdout, _("\treason unknown"));
              }
  
              if (long_status > 1)
  	    {
  	      _cupsLangPuts(stdout, _("\tForm mounted:"));
  	      _cupsLangPuts(stdout, _("\tContent types: any"));
  	      _cupsLangPuts(stdout, _("\tPrinter types: unknown"));
  	    }
  
              if (long_status)
  	    {
  	      _cupsLangPrintf(stdout, _("\tDescription: %s"),
  	                      description ? description : "");
  
  	      if (reasons)
  	      {
  		char	alerts[1024],	/* Alerts string */
  			*aptr;		/* Pointer into alerts string */
  
  		for (i = 0, aptr = alerts; i < reasons->num_values; i ++)
  		{
  		  if (i)
  		    snprintf(aptr, sizeof(alerts) - (size_t)(aptr - alerts), " %s", reasons->values[i].string.text);
  		  else
  		    strlcpy(alerts, reasons->values[i].string.text, sizeof(alerts));
  
  		  aptr += strlen(aptr);
  		}
  
  		_cupsLangPrintf(stdout, _("\tAlerts: %s"), alerts);
  	      }
  	    }
              if (long_status > 1)
  	    {
  	      _cupsLangPrintf(stdout, _("\tLocation: %s"),
  	                      location ? location : "");
  
  	      if (ptype & CUPS_PRINTER_REMOTE)
  	      {
  		_cupsLangPuts(stdout, _("\tConnection: remote"));
  
  		if (make_model && !strstr(make_model, "System V Printer") &&
  	        	 !strstr(make_model, "Raw Printer") && uri)
  		  _cupsLangPrintf(stdout, _("\tInterface: %s.ppd"), uri);
  	      }
  	      else
  	      {
  		_cupsLangPuts(stdout, _("\tConnection: direct"));
  
  		if (make_model && !strstr(make_model, "Raw Printer"))
  		  _cupsLangPrintf(stdout,
  	                	  _("\tInterface: %s/ppd/%s.ppd"),
  				  cg->cups_serverroot, printer);
                }
  	      _cupsLangPuts(stdout, _("\tOn fault: no alert"));
  	      _cupsLangPuts(stdout, _("\tAfter fault: continue"));
  		  /* TODO update to use printer-error-policy */
                if (allowed)
  	      {
  		_cupsLangPuts(stdout, _("\tUsers allowed:"));
  		for (j = 0; j < allowed->num_values; j ++)
  		  _cupsLangPrintf(stdout, "\t\t%s",
  	                	  allowed->values[j].string.text);
  	      }
  	      else if (denied)
  	      {
  		_cupsLangPuts(stdout, _("\tUsers denied:"));
  		for (j = 0; j < denied->num_values; j ++)
  		  _cupsLangPrintf(stdout, "\t\t%s",
  	                	  denied->values[j].string.text);
  	      }
  	      else
  	      {
  		_cupsLangPuts(stdout, _("\tUsers allowed:"));
  		_cupsLangPuts(stdout, _("\t\t(all)"));
  	      }
  	      _cupsLangPuts(stdout, _("\tForms allowed:"));
  	      _cupsLangPuts(stdout, _("\t\t(none)"));
  	      _cupsLangPuts(stdout, _("\tBanner required"));
  	      _cupsLangPuts(stdout, _("\tCharset sets:"));
  	      _cupsLangPuts(stdout, _("\t\t(none)"));
  	      _cupsLangPuts(stdout, _("\tDefault pitch:"));
  	      _cupsLangPuts(stdout, _("\tDefault page size:"));
  	      _cupsLangPuts(stdout, _("\tDefault port settings:"));
  	    }
  	  }
        }
  
        if (attr == NULL)
          break;
      }
  
      ippDelete(response);
    }
  
    return (0);
  }
  
  
  /*
   * 'show_scheduler()' - Show scheduler status.
   */
  
  static int				/* 1 on success, 0 on failure */
  show_scheduler(void)
  {
    http_t	*http;			/* Connection to server */
  
  
    if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
                                   cupsEncryption())) != NULL)
    {
      _cupsLangPuts(stdout, _("scheduler is running"));
      httpClose(http);
      return (1);
    }
    else
    {
      _cupsLangPuts(stdout, _("scheduler is not running"));
      return (0);
    }
  }
  
  
  /*
   * 'usage()' - Show program usage and exit.
   */
  
  static void
  usage(void)
  {
    _cupsLangPuts(stdout, _("Usage: lpstat [options]"));
    _cupsLangPuts(stdout, _("Options:"));
    _cupsLangPuts(stdout, _("-E                      Encrypt the connection to the server"));
    _cupsLangPuts(stdout, _("-h server[:port]        Connect to the named server and port"));
    _cupsLangPuts(stdout, _("-l                      Show verbose (long) output"));
    _cupsLangPuts(stdout, _("-U username             Specify the username to use for authentication"));
  
    _cupsLangPuts(stdout, _("-H                      Show the default server and port"));
    _cupsLangPuts(stdout, _("-W completed            Show completed jobs"));
    _cupsLangPuts(stdout, _("-W not-completed        Show pending jobs"));
    _cupsLangPuts(stdout, _("-a [destination(s)]     Show the accepting state of destinations"));
    _cupsLangPuts(stdout, _("-c [class(es)]          Show classes and their member printers"));
    _cupsLangPuts(stdout, _("-d                      Show the default destination"));
    _cupsLangPuts(stdout, _("-e                      Show available destinations on the network"));
    _cupsLangPuts(stdout, _("-o [destination(s)]     Show jobs"));
    _cupsLangPuts(stdout, _("-p [printer(s)]         Show the processing state of destinations"));
    _cupsLangPuts(stdout, _("-r                      Show whether the CUPS server is running"));
    _cupsLangPuts(stdout, _("-R                      Show the ranking of jobs"));
    _cupsLangPuts(stdout, _("-s                      Show a status summary"));
    _cupsLangPuts(stdout, _("-t                      Show all status information"));
    _cupsLangPuts(stdout, _("-u [user(s)]            Show jobs queued by the current or specified users"));
    _cupsLangPuts(stdout, _("-v [printer(s)]         Show the devices for each destination"));
  
    exit(1);
  }
  