363 lines
14 KiB
C
363 lines
14 KiB
C
|
/*
|
||
|
*
|
||
|
* lphelp
|
||
|
* ------
|
||
|
#
|
||
|
# A simple tool for getting information about an installed printer or a
|
||
|
# PPD file. Especially the printer-specific options defined in the PPD
|
||
|
# file are listed, so that one can make use of them with the "lp", "lpr",
|
||
|
# and "lpoptions" commands. The programm can also be used by installation/
|
||
|
# configuration scripts to give a "preview" to a PPD file.
|
||
|
#
|
||
|
# ONLY WORKS WITH CUPS DAEMON RUNNING!
|
||
|
# The CUPS library (libcups.so.*) must be installed!
|
||
|
#
|
||
|
# Compile with: gcc -olphelp -lcups lphelp.c
|
||
|
#
|
||
|
* Copyright 2000 by Till Kamppeter
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU General Public License as
|
||
|
* published by the Free Software Foundation; either version 2 of the
|
||
|
* License, or (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||
|
* 02111-1307 USA
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Include necessary headers...
|
||
|
*/
|
||
|
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <unistd.h>
|
||
|
#include <cups/cups.h>
|
||
|
|
||
|
/*
|
||
|
* 'main()' - Main entry for test program.
|
||
|
*/
|
||
|
|
||
|
int /* O - Exit status */
|
||
|
main(int argc, /* I - Number of command-line arguments */
|
||
|
char *argv[]) /* I - Command-line arguments */
|
||
|
{
|
||
|
int i, j, k, m; /* Looping vars */
|
||
|
const char *filename; /* File to load */
|
||
|
FILE *ppdfile;
|
||
|
|
||
|
// Temporary file name (for reading from stdin)
|
||
|
char tmpfile[19] = "/tmp/lphelp.XXXXXX";
|
||
|
const int blocksize = 1024;
|
||
|
char buffer[blocksize];
|
||
|
int bytesread;
|
||
|
|
||
|
// variables for parsing PPD file for usual options (boolean, enumerated)
|
||
|
ppd_file_t *ppd; /* PPD file record */
|
||
|
ppd_size_t *size; /* Size record */
|
||
|
ppd_group_t *group; /* UI group */
|
||
|
ppd_option_t *option; /* Standard UI option */
|
||
|
ppd_choice_t *choice; /* Standard UI option choice */
|
||
|
static char *uis[] = { "BOOLEAN", "PICKONE", "PICKMANY" };
|
||
|
static char *sections[] = { "ANY", "DOCUMENT", "EXIT",
|
||
|
"JCL", "PAGE", "PROLOG" };
|
||
|
|
||
|
// variables for parsing CUPS-O-MATIC info for numerical options (float, int)
|
||
|
char line[1024], /* buffer for reading PPD file line by
|
||
|
line to search numerical options */
|
||
|
item[1024], /* item to be defined (left of "=>") */
|
||
|
value[1024], /* value for item (right of "=>") */
|
||
|
argname[1024], /* name of the current argument */
|
||
|
comment[1024]; /* human-readable argument name */
|
||
|
const char *line_contents; /* contents of line */
|
||
|
const char *scan; /* pointer scanning the line */
|
||
|
char *writepointer;
|
||
|
double min, max, defvalue; /* Range of numerical
|
||
|
CUPS-O-MATIC option */
|
||
|
int opttype; /* 0 = other, 1 = int, 2 = float */
|
||
|
int openbrackets; /* How many curled brackets are open? */
|
||
|
int inquotes; /* are we in quotes now? */
|
||
|
int inargspart; /* are we in the arguments part now? */
|
||
|
|
||
|
/*
|
||
|
* Display PPD files for each file listed on the command-line...
|
||
|
*/
|
||
|
|
||
|
if (argc == 1) {
|
||
|
fputs("Usage: lphelp <filename1>.ppd [<filename2>.ppd ...]\n lphelp <printername1> [<printername2> ...]\n lphelp -\n", stderr);
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
for (i = 1; i < argc; i ++) {
|
||
|
if ((strstr(argv[i], ".ppd")) || (strstr(argv[i], "-")))
|
||
|
filename = argv[i];
|
||
|
else
|
||
|
filename = cupsGetPPD(argv[i]);
|
||
|
if (strcmp(filename,"-") == 0) {
|
||
|
if ((ppdfile = fdopen(mkstemp(tmpfile), "w")) == NULL) {
|
||
|
fprintf(stderr, "Unable to generate temporary file!\n");
|
||
|
}
|
||
|
while ((bytesread = fread(buffer, 1, blocksize, stdin)) > 0) {
|
||
|
fwrite(buffer, 1, bytesread, ppdfile);
|
||
|
}
|
||
|
fclose(ppdfile);
|
||
|
filename = tmpfile;
|
||
|
}
|
||
|
if ((ppd = ppdOpenFile(filename)) == NULL) {
|
||
|
fprintf(stderr, "Unable to open \'%s\' as a PPD file!\n", filename);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
printf("==============================================================================\n\n");
|
||
|
printf("%s\n\n", ppd->modelname);
|
||
|
printf("==============================================================================\n\n");
|
||
|
printf(" %s printer\n\n", ppd->color_device ? "Colour" : "Black & white");
|
||
|
printf(" Printer-specific options\n");
|
||
|
printf(" ------------------------\n\n");
|
||
|
printf(" Besides the options described in the CUPS software users manual\n");
|
||
|
printf(" (http://localhost:631/sum.html) you can use also the following options\n");
|
||
|
printf(" when you print on this printer with the \"lp\" or \"lpr\" command (a choice\n");
|
||
|
printf(" with the \"default\" mark represents the behaviour of the printer when the\n");
|
||
|
printf(" appropriate option is not given on the command line):\n\n");
|
||
|
|
||
|
for (j = 0, group = ppd->groups; j < ppd->num_groups; j ++, group ++) {
|
||
|
for (k = 0, option = group->options; k < group->num_options;
|
||
|
k ++, option ++) {
|
||
|
if (strcmp(option->keyword, "PageRegion") != 0) {
|
||
|
if ((strcmp(uis[option->ui],"BOOLEAN") == 0) ||
|
||
|
(strcmp(uis[option->ui],"PICKONE") == 0)) {
|
||
|
printf(" %s: -o %s=<choice>\n\n",
|
||
|
option->text, option->keyword);
|
||
|
printf(" <choice> can be one of the following:\n\n");
|
||
|
} else {
|
||
|
printf(" %s: -o %s=<choice1>,<choice2>,...\n\n",
|
||
|
option->text, option->keyword);
|
||
|
printf(" <choice1>, <choice2>, and so on can be out of the following:\n\n");
|
||
|
}
|
||
|
if (strcmp(option->keyword, "PageSize") == 0) {
|
||
|
for (m = option->num_choices, choice = option->choices;
|
||
|
m > 0;
|
||
|
m --, choice ++) {
|
||
|
size = ppdPageSize(ppd, choice->choice);
|
||
|
|
||
|
if (size == NULL)
|
||
|
printf(" %s (%s, size unknown", choice->choice, choice->text);
|
||
|
else
|
||
|
printf(" %s (%s, size: %.2fx%.2fin", choice->choice,
|
||
|
choice->text, size->width / 72.0, size->length / 72.0);
|
||
|
if (strcmp(option->defchoice, choice->choice) == 0)
|
||
|
puts(", default)");
|
||
|
else
|
||
|
puts(")");
|
||
|
}
|
||
|
} else {
|
||
|
for (m = option->num_choices, choice = option->choices;
|
||
|
m > 0;
|
||
|
m --, choice ++) {
|
||
|
printf(" %s (%s", choice->choice, choice->text);
|
||
|
if (strcmp(option->defchoice, choice->choice) == 0)
|
||
|
puts(", default)");
|
||
|
else
|
||
|
puts(")");
|
||
|
}
|
||
|
}
|
||
|
printf("\n");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ppdClose(ppd);
|
||
|
|
||
|
// Search for numerical options of CUPS-O-MATIC
|
||
|
if ((ppdfile = fopen(filename,"r")) == NULL) {
|
||
|
fprintf(stderr, "Unable to open \'%s\' as a PPD file!\n", filename);
|
||
|
continue;
|
||
|
}
|
||
|
// Reset all variables
|
||
|
opttype = 0;
|
||
|
min = 0.0; max = 0.0; defvalue = 0.0;
|
||
|
openbrackets = 0;
|
||
|
inquotes = 0;
|
||
|
writepointer = item;
|
||
|
inargspart = 0;
|
||
|
// Read the PPD file again, line by line.
|
||
|
while (fgets(line,sizeof(line),ppdfile)) {
|
||
|
// evaluate only lines with CUPS-O-MATIC info
|
||
|
if (line_contents = strstr(line,"*% COMDATA #")) {
|
||
|
line_contents += 12; // Go to the text after
|
||
|
// "*% COMDATA #"
|
||
|
for (scan = line_contents;
|
||
|
(*scan != '\n') && (*scan != '\0');
|
||
|
scan ++) {
|
||
|
switch(*scan) {
|
||
|
case '[': // open square bracket
|
||
|
case '{': // open curled bracket
|
||
|
if (!inquotes) {
|
||
|
openbrackets ++;
|
||
|
// we are on the left hand side now
|
||
|
*writepointer = '\0';
|
||
|
writepointer = item;
|
||
|
// in which type of block are we now?
|
||
|
if ((openbrackets == 2) &&
|
||
|
(strncasecmp(item,"args",4) == 0)) {
|
||
|
// we are entering the arguments section now
|
||
|
inargspart = 1;
|
||
|
}
|
||
|
if ((openbrackets == 3) &&
|
||
|
(inargspart == 1)) {
|
||
|
// new argument, get its name
|
||
|
strcpy(argname,item);
|
||
|
}
|
||
|
// item already evaluated now
|
||
|
item[0] = '\0';
|
||
|
} else {*writepointer = *scan; writepointer ++;}
|
||
|
break;
|
||
|
case ',': // end of logical line
|
||
|
case ']': // close square bracket
|
||
|
case '}': // close curled bracket
|
||
|
if (!inquotes) {
|
||
|
// right hand side completed, go to left hand side
|
||
|
*writepointer = '\0';
|
||
|
writepointer = item;
|
||
|
// evaluate logical line
|
||
|
if (item[0]) {
|
||
|
// Machine-readable argument name
|
||
|
if ((openbrackets == 3) &&
|
||
|
(inargspart == 1) &&
|
||
|
(strcasecmp(item,"name") == 0)) {
|
||
|
strcpy(argname,value);
|
||
|
}
|
||
|
// Human-readable argument name
|
||
|
if ((openbrackets == 3) &&
|
||
|
(inargspart == 1) &&
|
||
|
(strcasecmp(item,"comment") == 0)) {
|
||
|
strcpy(comment,value);
|
||
|
}
|
||
|
// argument type
|
||
|
if ((openbrackets == 3) &&
|
||
|
(inargspart == 1) &&
|
||
|
(strcasecmp(item,"type") == 0)) {
|
||
|
if (strcasecmp(value,"int") == 0) opttype = 1;
|
||
|
if (strcasecmp(value,"float") == 0) opttype = 2;
|
||
|
}
|
||
|
// minimum value
|
||
|
if ((openbrackets == 3) &&
|
||
|
(inargspart == 1) &&
|
||
|
(strcasecmp(item,"min") == 0)) {
|
||
|
min = atof(value);
|
||
|
}
|
||
|
// maximum value
|
||
|
if ((openbrackets == 3) &&
|
||
|
(inargspart == 1) &&
|
||
|
(strcasecmp(item,"max") == 0)) {
|
||
|
max = atof(value);
|
||
|
}
|
||
|
// default value
|
||
|
if ((openbrackets == 3) &&
|
||
|
(inargspart == 1) &&
|
||
|
(strcasecmp(item,"default") == 0)) {
|
||
|
defvalue = atof(value);
|
||
|
}
|
||
|
// item already evaluated now
|
||
|
item[0] = '\0';
|
||
|
}
|
||
|
// close bracket
|
||
|
if ((*scan == '}') || (*scan == ']')) {
|
||
|
// which block did we complete now?
|
||
|
if ((openbrackets == 2) &&
|
||
|
(inargspart == 1)) {
|
||
|
// We are leaving the arguments part now
|
||
|
inargspart = 0;
|
||
|
}
|
||
|
if ((openbrackets == 3) &&
|
||
|
(inargspart == 1)) {
|
||
|
// The current option is completely parsed
|
||
|
// Is the option a valid numerical option?
|
||
|
if ((opttype > 0) &&
|
||
|
(min != max) &&
|
||
|
(argname[0])) {
|
||
|
// Correct the default value, if necessary
|
||
|
if (min < max) {
|
||
|
if (defvalue < min) defvalue = min;
|
||
|
if (defvalue > max) defvalue = max;
|
||
|
} else {
|
||
|
if (defvalue < max) defvalue = max;
|
||
|
if (defvalue > min) defvalue = min;
|
||
|
}
|
||
|
// Show the found argument
|
||
|
printf(" %s: -o %s=<value>\n\n",
|
||
|
comment, argname);
|
||
|
if (opttype == 1) {
|
||
|
printf(
|
||
|
" <value> must be an integer number in the range %d..%d\n",
|
||
|
(int)(min),(int)(max));
|
||
|
printf(
|
||
|
" The default value is %d\n\n",
|
||
|
(int)(defvalue));
|
||
|
} else {
|
||
|
printf(
|
||
|
" <value> must be a decimal number in the range %.2f..%.2f\n",
|
||
|
min,max);
|
||
|
printf(
|
||
|
" The default value is %.2f\n\n",
|
||
|
defvalue);
|
||
|
}
|
||
|
}
|
||
|
// reset the values
|
||
|
argname[0] = '\0';
|
||
|
opttype = 0;
|
||
|
min = 0.0; max = 0.0; defvalue = 0.0;
|
||
|
}
|
||
|
openbrackets --;
|
||
|
}
|
||
|
} else {*writepointer = *scan; writepointer ++;}
|
||
|
break;
|
||
|
case '\'': // quote
|
||
|
if (!inquotes) { // open quote pair
|
||
|
inquotes = 1;
|
||
|
} else { // close quote pair
|
||
|
inquotes = 0;
|
||
|
}
|
||
|
break;
|
||
|
case '=': // "=>"
|
||
|
if ((!inquotes) && (*(scan + 1) == '>')) {
|
||
|
scan ++;
|
||
|
// left hand side completed, go to right hand side
|
||
|
*writepointer = '\0';
|
||
|
writepointer = value;
|
||
|
} else {*writepointer = *scan; writepointer ++;}
|
||
|
break;
|
||
|
case ' ': // white space
|
||
|
case '\t':
|
||
|
if (!inquotes) {
|
||
|
// ignore white space outside quotes
|
||
|
} else {*writepointer = *scan; writepointer ++;}
|
||
|
break;
|
||
|
default:
|
||
|
// write all other characters
|
||
|
*writepointer = *scan; writepointer ++;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
inquotes = 0; // quote pairs cannot enclose more
|
||
|
// than one line
|
||
|
}
|
||
|
}
|
||
|
fclose(ppdfile);
|
||
|
printf("\n\n\n");
|
||
|
}
|
||
|
|
||
|
if (!(strstr(tmpfile, "XXXXXX"))) {
|
||
|
unlink(tmpfile);
|
||
|
}
|
||
|
|
||
|
return (0);
|
||
|
}
|