1
0
mirror of https://github.com/koenkooi/foo2zjs.git synced 2026-01-22 03:34:49 +08:00
foo2zjs/command2foo2lava-pjl.c
Koen Kooi 66819e6d05 import 'Tarball last modified: Fri Jan 22 05:14:24 2016 CST'
Signed-off-by: Koen Kooi <koen@dominion.thruhere.net>
2016-01-25 16:22:04 +01:00

535 lines
14 KiB
C

/*
* Konica-Minolta command filter for the Common UNIX Printing System.
*
* Copyright 2010 by Reinhold Kainhofer <reinhold@kainhofer.com>
* Based in part on commandtoepson:
* Copyright 1993-2000 by Easy Software Products.
* Based in part on commandtops:
* Copyright 2008 by Apple Inc.
* Based in part on snmp-supplies.c:
* Copyright 2008-2009 by Apple Inc.
*
* 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.
*
* Contents:
*
* main() - Main entry and command processing.
*/
/*
* Include necessary headers...
*/
#include <cups/sidechannel.h>
#include <cups/cups.h>
#include <cups/ppd.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
inline int
max(int a, int b)
{
return a > b ? a : b;
}
/*
* Macros...
*/
#define pwrite(s,n) fwrite((s), 1, (n), stdout)
void report_levels(int negate);
void auto_configure(void);
/*
* 'main()' - Main entry and processing of driver.
*/
int /* O - Exit status */
main(int argc, /* I - Number of command-line arguments */
char *argv[]) /* I - Command-line arguments */
{
FILE *fp; /* Command file */
char line[1024], /* Line from file */
*lineptr; /* Pointer into line */
ppd_file_t *ppd;
ppd_attr_t *attr;
int negate = 1;
/*
* Check for valid arguments...
*/
if (argc < 6 || argc > 7)
{
/*
* We don't have the correct number of arguments; write an error message
* and return.
*/
fprintf(stderr, "ERROR: %s job-id user title copies options [file]\n",
argv[0]);
return (1);
}
/*
* Get the negate parm from the PPD file
*/
ppd = ppdOpenFile(getenv("PPD"));
if (ppd)
{
attr = ppdFindAttr(ppd, "foo2zjsNegateMarkerLevels", NULL);
if (attr && strcmp(attr->value, "False") == 0)
negate = 0;
ppdClose(ppd);
}
fprintf(stderr, "DEBUG: foo2zjsNegateMarkerLevels=%d\n", negate);
/*
* Open the command file as needed...
*/
if (argc == 7)
{
if ((fp = fopen(argv[6], "r")) == NULL)
{
perror("ERROR: Unable to open command file - ");
return (1);
}
}
else
fp = stdin;
/*
* Read the commands from the file and send the appropriate commands...
*/
while (fgets(line, sizeof(line), fp) != NULL)
{
// Drop trailing newline...
lineptr = line + strlen(line) - 1;
if (*lineptr == '\n')
*lineptr = '\0';
// Skip leading whitespace...
for (lineptr = line; isspace(*lineptr); lineptr++);
// Skip comments and blank lines...
if (*lineptr == '#' || !*lineptr)
continue;
// Parse the command...
if (strncasecmp(lineptr, "AutoConfigure", 13) == 0)
{
// Retrieve the settings from the printer and change the PPD
// according
// to the installed options
// TODO: This is not fully implemented!
// auto_configure ();
}
else if (strncasecmp(lineptr, "ReportStatus", 12) == 0)
{
// Report Status...
// pwrite("\033%-12345X@PJL INFO STATUS\015\012", 27);
// pwrite("\033%-12345X", 9);
// TODO: Read back-channel data
// TODO: Parse back-channel data
// TODO: Feed parsed data to the scheduller
}
else if (strncasecmp(lineptr, "ReportLevels", 12) == 0)
{
// Report ink levels...
report_levels(negate);
}
else
fprintf(stderr, "ERROR: Invalid printer command \"%s\"!\n",
lineptr);
}
/*
* Close the command file and return...
*/
if (fp != stdin)
fclose(fp);
return (0);
}
/****************************************************************************
* Dealing with supplies *
****************************************************************************/
#define CUPS_MAX_SUPPLIES 32 /* Maximum number of supplies for a
* printer */
#define CUPS_MAX_TEXTLEN 155 /* Maximum length of supply names */
typedef struct Supply /**** Printer supply data ****/
{
char id[CUPS_MAX_TEXTLEN], /* ID used in the response */
name[CUPS_MAX_TEXTLEN], /* Name of supply */
color[8], /* Color: "#RRGGBB" or "none" */
type[CUPS_MAX_TEXTLEN]; /* Type of supply, e.g. toner */
int capacity, /* Maximum capacity */
level; /* Current level value */
} Supply;
static const char *const default_supplies[10][4] =
{
{ "B", "Blue", "#0000FF", "toner" },
{ "C", "Cyan", "#00FFFF", "toner" },
{ "G", "Green", "#00FF00", "toner" },
{ "K", "Black", "#000000", "toner" },
{ "M", "Magenta", "#FF00FF", "toner" },
{ "R", "Red", "#FF0000", "toner" },
{ "W", "White", "#FFFFFF", "toner" },
{ "Y", "Yellow", "#FFFF00", "toner" },
{ "TRSBELT", "Transfer unit", "#808080", "transferUnit" },
{ "FUSER", "Fuser", "#808080", "fuser" },
};
/*
* Find the supply information with given ID in the list of supplies. If not
* found, add a new entry with defaults as specified in default_supplies
*/
int
locate_supply_information(Supply * supplies, int num_supplies, int max_supplies,
const char *id)
{
// Check whether we find it in the current list:
int pos = 0;
for (pos = 0; pos < num_supplies; ++pos)
{
if (!strcmp(supplies[pos].id, id))
{
return pos;
}
}
// Not found, create new entry:
if (num_supplies >= max_supplies)
{
// No space left in supplies!
return -1;
}
pos = num_supplies;
strcpy(supplies[pos].id, id);
int deflen =
(int) (sizeof(default_supplies) / sizeof(default_supplies[0]));
int k;
for (k = 0; k < deflen; k++)
{
if (!strcmp(default_supplies[k][0], id))
{ // Found the defaults entry!
// Initialize to defaults from default_supplies:
strcpy(supplies[pos].name, default_supplies[k][1]);
strcpy(supplies[pos].color, default_supplies[k][2]);
strcpy(supplies[pos].type, default_supplies[k][3]);
supplies[pos].capacity = 0;
supplies[pos].level = 0;
break;
}
}
return pos;
};
void
report_levels(int negate)
{
// Buffer for the data
char buffer[8192];
ssize_t bytes;
// Check whether we can get a response from the printer at all:
int datalen = 1;
if (cupsSideChannelDoRequest(CUPS_SC_CMD_GET_BIDI, buffer, &datalen,
30.0) != CUPS_SC_STATUS_OK ||
buffer[0] != CUPS_SC_BIDI_SUPPORTED)
{
fputs("DEBUG: Unable to retrieve supply status from printer - no "
"bidirectional I/O available!\n", stderr);
return;
}
// The actual PJL request
pwrite("\033%-12345X@PJL INFO DSTATUS\015\012", 28);
pwrite("\033%-12345X", 9);
fflush(stdout);
// RER: 07/20/10 - Sleep for a bit!
sleep(5);
// Ask the backend to send all data NOW:
datalen = 0;
cupsSideChannelDoRequest(CUPS_SC_CMD_DRAIN_OUTPUT, buffer, &datalen, 5.0);
// Read back the data from the printer
bytes = cupsBackChannelRead(buffer, sizeof(buffer) - 1, 5.0);
buffer[bytes] = '\0';
if (strncmp(buffer, "@PJL INFO DSTATUS", 17))
{
fprintf(stderr,
"DEBUG: Printer does not return a proper PJL DSTATUS response.\n");
fprintf(stderr, "DEBUG: Got %d bytes: %s\n", (int) bytes, buffer);
return;
}
// fprintf (stderr, "DEBUG: Got %d bytes: %s\n", bytes, buffer);
int num_supplies = 0; /* Number of supplies found */
Supply supplies[CUPS_MAX_SUPPLIES]; /* Supply information */
// Parse the returned data
//
// FORMAT is (with K,C,M,Y as color abbreviations):
//
// @PJL INFO DSTATUS
// CODE=600100
// CONSUMETONERK=16
// [...]
// CONSUMETRSBELT=2
// CONSUMEFUSER=0
// CONSUMETONERTYPEK=1000
// [...]
// CONSUMETONERINSTALLY=YES
// \0x0c
const char *pos = buffer;
char supply[255];
int sindex = 0;
while ((pos = strstr(pos, "CONSUME")))
{
sindex = -1;
pos += 7;
if (!strncmp(pos, "TONERTYPE", 9))
{
pos += 9;
supply[0] = pos[0];
supply[1] = '\0';
pos += 2;
sindex =
locate_supply_information(supplies, num_supplies,
CUPS_MAX_SUPPLIES, supply);
if (sindex >= 0)
{
num_supplies = max(sindex + 1, num_supplies);
supplies[sindex].capacity = atoi(pos);
}
}
else if (!strncmp(pos, "IMGDRUM", 7))
{
pos += 7;
// Don't do anything, this is just dummy information!
}
else if (!strncmp(pos, "TONERCOUNTERFEIT", 16))
{
pos += 16;
// Don't do anything, this is just dummy information!
}
else if (!strncmp(pos, "TONERINSTALL", 12))
{
pos += 12;
// Don't do anything, this is just dummy information!
}
else if (!strncmp(pos, "TONER", 5))
{
pos += 5;
supply[0] = pos[0];
supply[1] = '\0';
pos += 2;
sindex =
locate_supply_information(supplies, num_supplies,
CUPS_MAX_SUPPLIES, supply);
// fprintf (stderr, "DEBUG: sindex %d\n", sindex);
if (sindex >= 0)
{
num_supplies = max(sindex + 1, num_supplies);
supplies[sindex].level = negate ? 100 - atoi(pos) : atoi(pos);
}
}
else if (!strncmp(pos, "FUSER", 5))
{
pos += 6;
sindex =
locate_supply_information(supplies, num_supplies,
CUPS_MAX_SUPPLIES, "FUSER");
if (sindex >= 0)
{
num_supplies = max(sindex + 1, num_supplies);
supplies[sindex].level = negate ? 100 - atoi(pos) : atoi(pos);
}
}
else if (!strncmp(pos, "TRSBELT", 7))
{
pos += 8;
sindex =
locate_supply_information(supplies, num_supplies,
CUPS_MAX_SUPPLIES, "TRSBELT");
if (sindex >= 0)
{
num_supplies = max(sindex + 1, num_supplies);
supplies[sindex].level = negate ? 100 - atoi(pos) : atoi(pos);
}
}
else
{
fprintf(stderr, "DEBUG: Supply return entry did not match any "
"known keyword: %s\n", pos);
}
}
// Create the output:
if (num_supplies)
{
int k;
// Marker types:
strcpy(buffer, supplies[0].type);
for (k = 1; k < num_supplies; ++k)
sprintf(buffer, "%s,%s", buffer, supplies[k].type);
fprintf(stderr, "ATTR: marker-types=%s\n", buffer);
// Marker names
buffer[0] = '\0';
for (k = 0; k < num_supplies; ++k)
{
if (k > 0)
strcat(buffer, ",");
if (supplies[k].capacity > 0)
sprintf(buffer, "%s\"%s (Max %d)\"", buffer, supplies[k].name,
supplies[k].capacity);
else
sprintf(buffer, "%s\"%s\"", buffer, supplies[k].name);
}
fprintf(stderr, "ATTR: marker-names=%s\n", buffer);
// Marker colors
strcpy(buffer, supplies[0].color);
for (k = 1; k < num_supplies; ++k)
sprintf(buffer, "%s,%s", buffer, supplies[k].color);
fprintf(stderr, "ATTR: marker-colors=%s\n", buffer);
// Marker levels
sprintf(buffer, "%d", supplies[0].level);
for (k = 1; k < num_supplies; ++k)
sprintf(buffer, "%s,%d", buffer, supplies[k].level);
fprintf(stderr, "ATTR: marker-levels=%s\n", buffer);
}
else
fprintf(stderr,
"DEBUG: Unable to extract supply information from the "
"printer's response.\n");
// fprintf (stderr, "STATE: \n");
}
/****************************************************************************
* Auto-configuration of printer settings *
****************************************************************************/
void
auto_configure()
{
// Buffer for the data
char buffer[8192];
ssize_t bytes;
int datalen = 1;
// Check whether we can get a response from the printer at all:
if (cupsSideChannelDoRequest(CUPS_SC_CMD_GET_BIDI, buffer, &datalen,
30.0) != CUPS_SC_STATUS_OK ||
buffer[0] != CUPS_SC_BIDI_SUPPORTED)
{
fputs("DEBUG: Unable to auto-configure printer - no "
"bidirectional I/O available!\n", stderr);
return;
}
// The actual PJL request
pwrite("\033%-12345X@PJL INFO CONFIG\015\012", 28);
pwrite("\033%-12345X", 9);
fflush(stdout);
// Ask the backend to send all data NOW:
datalen = 0;
cupsSideChannelDoRequest(CUPS_SC_CMD_DRAIN_OUTPUT, buffer, &datalen, 5.0);
// Read back the data from the printer
bytes = cupsBackChannelRead(buffer, sizeof(buffer) - 1, 5.0);
buffer[bytes] = '\0';
if (strncmp(buffer, "@PJL INFO CONFIG", 17))
{
fprintf(stderr,
"DEBUG: Printer does not return a proper PJL CONFIG response.\n");
fprintf(stderr, "DEBUG: Got %d bytes: %s\n", (int) bytes, buffer);
return;
}
// Parse the returned data
//
// FORMAT is:
//
// @PJL INFO CONFIG
// IN TRAYS [1 ENUMERATED]
// INTRAY1 MP
// LANGUAGES [1 ENUMERATED]
// LAVAFLOW
// USTATUS [6 ENUMERATED]
// DEVICE
// JOB
// PAGE
// TIMED
// DDEVICE
// DTIMED
// TRAY2=NOTINSTALLED [2 ENUMERATED]
// INSTALLED
// NOTINSTALLED
// TRAY3=NOTINSTALLED [2 ENUMERATED]
// INSTALLED
// NOTINSTALLED
// DUPLEX=INSTALLED [2 ENUMERATED]
// INSTALLED
// NOTINSTALLED
// TONER=TONEROK [3 ENUMERATED]
// TONEROK
// TONERDEAD
// TONERNOTGENUINE
// PRINTINGUNIT=PRINTINGUNITOK [2 ENUMERATED]
// PRINTINGUNITOK
// PRINTINGUNITNOTGENUINE
// MEMORY=134217728
// \033
// TODO
}