• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * "lpc" command for CUPS.
3  *
4  * Copyright © 2020-2024 by OpenPrinting.
5  * Copyright 2007-2014 by Apple Inc.
6  * Copyright 1997-2006 by Easy Software Products.
7  *
8  * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
9  */
10 
11 /*
12  * Include necessary headers...
13  */
14 
15 #include <cups/cups-private.h>
16 
17 
18 /*
19  * Local functions...
20  */
21 
22 static int	compare_strings(const char *, const char *, size_t);
23 static void	do_command(http_t *, const char *, const char *);
24 static void	show_help(const char *);
25 static void	show_prompt(const char *message);
26 static void	show_status(http_t *, const char *);
27 
28 
29 /*
30  * 'main()' - Parse options and commands.
31  */
32 
33 int
main(int argc,char * argv[])34 main(int  argc,				/* I - Number of command-line arguments */
35      char *argv[])			/* I - Command-line arguments */
36 {
37   http_t	*http;			/* Connection to server */
38   char		line[1024],		/* Input line from user */
39 		*params;		/* Pointer to parameters */
40 
41 
42   _cupsSetLocale(argv);
43 
44  /*
45   * Connect to the scheduler...
46   */
47 
48   http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
49 
50   if (argc > 1)
51   {
52    /*
53     * Process a single command on the command-line...
54     */
55 
56     do_command(http, argv[1], argv[2]);
57   }
58   else
59   {
60    /*
61     * Do the command prompt thing...
62     */
63 
64     show_prompt(_("lpc> "));
65     while (fgets(line, sizeof(line), stdin) != NULL)
66     {
67      /*
68       * Strip trailing whitespace...
69       */
70 
71       for (params = line + strlen(line) - 1; params >= line;)
72         if (!isspace(*params & 255))
73 	  break;
74 	else
75 	  *params-- = '\0';
76 
77      /*
78       * Strip leading whitespace...
79       */
80 
81       for (params = line; isspace(*params & 255); params ++);
82 
83       if (params > line)
84         _cups_strcpy(line, params);
85 
86       if (!line[0])
87       {
88        /*
89         * Nothing left, just show a prompt...
90 	*/
91 
92         show_prompt(_("lpc> "));
93 	continue;
94       }
95 
96      /*
97       * Find any options in the string...
98       */
99 
100       for (params = line; *params != '\0'; params ++)
101         if (isspace(*params & 255))
102 	  break;
103 
104      /*
105       * Remove whitespace between the command and parameters...
106       */
107 
108       while (isspace(*params & 255))
109         *params++ = '\0';
110 
111      /*
112       * The "quit" and "exit" commands exit; otherwise, process as needed...
113       */
114 
115       if (!compare_strings(line, "quit", 1) ||
116           !compare_strings(line, "exit", 2))
117         break;
118 
119       if (*params == '\0')
120         do_command(http, line, NULL);
121       else
122         do_command(http, line, params);
123 
124      /*
125       * Put another prompt out to the user...
126       */
127 
128       show_prompt(_("lpc> "));
129     }
130   }
131 
132  /*
133   * Close the connection to the server and return...
134   */
135 
136   httpClose(http);
137 
138   return (0);
139 }
140 
141 
142 /*
143  * 'compare_strings()' - Compare two command-line strings.
144  */
145 
146 static int				/* O - -1 or 1 = no match, 0 = match */
compare_strings(const char * s,const char * t,size_t tmin)147 compare_strings(const char *s,		/* I - Command-line string */
148                 const char *t,		/* I - Option string */
149                 size_t     tmin)	/* I - Minimum number of unique chars in option */
150 {
151   size_t	slen;			/* Length of command-line string */
152 
153 
154   slen = strlen(s);
155   if (slen < tmin)
156     return (-1);
157   else
158     return (strncmp(s, t, slen));
159 }
160 
161 
162 /*
163  * 'do_command()' - Do an lpc command...
164  */
165 
166 static void
do_command(http_t * http,const char * command,const char * params)167 do_command(http_t     *http,		/* I - HTTP connection to server */
168            const char *command,		/* I - Command string */
169 	   const char *params)		/* I - Parameters for command */
170 {
171   if (!compare_strings(command, "status", 4))
172     show_status(http, params);
173   else if (!compare_strings(command, "help", 1) || !strcmp(command, "?"))
174     show_help(params);
175   else
176     _cupsLangPrintf(stdout,
177                     _("%s is not implemented by the CUPS version of lpc."),
178 		    command);
179 }
180 
181 
182 /*
183  * 'show_help()' - Show help messages.
184  */
185 
186 static void
show_help(const char * command)187 show_help(const char *command)		/* I - Command to describe or NULL */
188 {
189   if (!command)
190   {
191     _cupsLangPrintf(stdout,
192                     _("Commands may be abbreviated.  Commands are:\n"
193 		      "\n"
194 		      "exit    help    quit    status  ?"));
195   }
196   else if (!compare_strings(command, "help", 1) || !strcmp(command, "?"))
197     _cupsLangPrintf(stdout, _("help\t\tGet help on commands."));
198   else if (!compare_strings(command, "status", 4))
199     _cupsLangPrintf(stdout, _("status\t\tShow status of daemon and queue."));
200   else
201     _cupsLangPrintf(stdout, _("?Invalid help command unknown."));
202 }
203 
204 
205 /*
206  * 'show_prompt()' - Show a localized prompt message.
207  */
208 
209 static void
show_prompt(const char * message)210 show_prompt(const char *message)	/* I - Message string to use */
211 {
212   ssize_t	bytes;			/* Number of bytes formatted */
213   char		output[8192];		/* Message buffer */
214   cups_lang_t	*lang = cupsLangDefault();
215 					/* Default language */
216 
217  /*
218   * Transcode to the destination charset and write the prompt...
219   */
220 
221   if ((bytes = cupsUTF8ToCharset(output, (cups_utf8_t *)_cupsLangString(lang, message), sizeof(output), lang->encoding)) > 0)
222   {
223     fwrite(output, 1, (size_t)bytes, stdout);
224     fflush(stdout);
225   }
226 }
227 
228 
229 /*
230  * 'show_status()' - Show printers.
231  */
232 
233 static void
show_status(http_t * http,const char * dests)234 show_status(http_t     *http,		/* I - HTTP connection to server */
235             const char *dests)		/* I - Destinations */
236 {
237   ipp_t		*request,		/* IPP Request */
238 		*response;		/* IPP Response */
239   ipp_attribute_t *attr;		/* Current attribute */
240   char		*printer,		/* Printer name */
241 		*device,		/* Device URI */
242                 *delimiter;		/* Char search result */
243   ipp_pstate_t	pstate;			/* Printer state */
244   int		accepting;		/* Is printer accepting jobs? */
245   int		jobcount;		/* Count of current jobs */
246   const char	*dptr,			/* Pointer into destination list */
247 		*ptr;			/* Pointer into printer name */
248   int		match;			/* Non-zero if this job matches */
249   static const char *requested[] =	/* Requested attributes */
250 		{
251 		  "device-uri",
252 		  "printer-is-accepting-jobs",
253 		  "printer-name",
254 		  "printer-state",
255 		  "queued-job-count"
256 		};
257 
258 
259   if (http == NULL)
260     return;
261 
262  /*
263   * Build a CUPS_GET_PRINTERS request, which requires the following
264   * attributes:
265   *
266   *    attributes-charset
267   *    attributes-natural-language
268   */
269 
270   request = ippNewRequest(CUPS_GET_PRINTERS);
271 
272   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
273                 "requested-attributes", sizeof(requested) / sizeof(requested[0]),
274 		NULL, requested);
275 
276  /*
277   * Do the request and get back a response...
278   */
279 
280   if ((response = cupsDoRequest(http, request, "/")) != NULL)
281   {
282    /*
283     * Loop through the printers returned in the list and display
284     * their status...
285     */
286 
287     for (attr = response->attrs; attr != NULL; attr = attr->next)
288     {
289      /*
290       * Skip leading attributes until we hit a job...
291       */
292 
293       while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
294         attr = attr->next;
295 
296       if (attr == NULL)
297         break;
298 
299      /*
300       * Pull the needed attributes from this job...
301       */
302 
303       printer   = NULL;
304       device    = "file:/dev/null";
305       pstate    = IPP_PRINTER_IDLE;
306       jobcount  = 0;
307       accepting = 1;
308 
309       while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
310       {
311         if (!strcmp(attr->name, "device-uri") &&
312 	    attr->value_tag == IPP_TAG_URI)
313 	  device = attr->values[0].string.text;
314         else if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
315 	         attr->value_tag == IPP_TAG_BOOLEAN)
316 	  accepting = attr->values[0].boolean;
317         else if (!strcmp(attr->name, "printer-name") &&
318 	         attr->value_tag == IPP_TAG_NAME)
319 	  printer = attr->values[0].string.text;
320         else if (!strcmp(attr->name, "printer-state") &&
321 	         attr->value_tag == IPP_TAG_ENUM)
322 	  pstate = (ipp_pstate_t)attr->values[0].integer;
323         else if (!strcmp(attr->name, "queued-job-count") &&
324 	         attr->value_tag == IPP_TAG_INTEGER)
325 	  jobcount = attr->values[0].integer;
326 
327         attr = attr->next;
328       }
329 
330      /*
331       * See if we have everything needed...
332       */
333 
334       if (printer == NULL)
335       {
336         if (attr == NULL)
337 	  break;
338 	else
339           continue;
340       }
341 
342      /*
343       * A single 'all' printer name is special, meaning all printers.
344       */
345 
346       if (dests != NULL && !strcmp(dests, "all"))
347         dests = NULL;
348 
349      /*
350       * See if this is a printer we're interested in...
351       */
352 
353       match = dests == NULL;
354 
355       if (dests != NULL)
356       {
357         for (dptr = dests; *dptr != '\0';)
358 	{
359 	 /*
360 	  * Skip leading whitespace and commas...
361 	  */
362 
363 	  while (isspace(*dptr & 255) || *dptr == ',')
364 	    dptr ++;
365 
366 	  if (*dptr == '\0')
367 	    break;
368 
369          /*
370 	  * Compare names...
371 	  */
372 
373 	  for (ptr = printer;
374 	       *ptr != '\0' && *dptr != '\0' && *ptr == *dptr;
375 	       ptr ++, dptr ++)
376 	    /* do nothing */;
377 
378           if (*ptr == '\0' && (*dptr == '\0' || *dptr == ',' ||
379 	                       isspace(*dptr & 255)))
380 	  {
381 	    match = 1;
382 	    break;
383 	  }
384 
385          /*
386 	  * Skip trailing junk...
387 	  */
388 
389           while (!isspace(*dptr & 255) && *dptr != '\0')
390 	    dptr ++;
391 	  while (isspace(*dptr & 255) || *dptr == ',')
392 	    dptr ++;
393 
394 	  if (*dptr == '\0')
395 	    break;
396         }
397       }
398 
399      /*
400       * Display the printer entry if needed...
401       */
402 
403       if (match)
404       {
405        /*
406         * Display it...
407 	*/
408 
409         printf("%s:\n", printer);
410 	if (!strncmp(device, "file:", 5))
411 	  _cupsLangPrintf(stdout,
412 	                  _("\tprinter is on device \'%s\' speed -1"),
413 			  device + 5);
414 	else
415 	{
416 	 /*
417 	  * Just show the scheme...
418 	  */
419 
420 	  if ((delimiter = strchr(device, ':')) != NULL )
421 	  {
422 	    *delimiter = '\0';
423 	    _cupsLangPrintf(stdout,
424 	                    _("\tprinter is on device \'%s\' speed -1"),
425 			    device);
426 	  }
427 	}
428 
429         if (accepting)
430 	  _cupsLangPuts(stdout, _("\tqueuing is enabled"));
431 	else
432 	  _cupsLangPuts(stdout, _("\tqueuing is disabled"));
433 
434         if (pstate != IPP_PRINTER_STOPPED)
435 	  _cupsLangPuts(stdout, _("\tprinting is enabled"));
436 	else
437 	  _cupsLangPuts(stdout, _("\tprinting is disabled"));
438 
439 	if (jobcount == 0)
440 	  _cupsLangPuts(stdout, _("\tno entries"));
441 	else
442 	  _cupsLangPrintf(stdout, _("\t%d entries"), jobcount);
443 
444 	_cupsLangPuts(stdout, _("\tdaemon present"));
445       }
446 
447       if (attr == NULL)
448         break;
449     }
450 
451     ippDelete(response);
452   }
453 }
454