/**************************************************************************
 * LPRng IFHP Filter
 * Copyright 1994-1999 Patrick Powell, San Diego, CA <papowell@astart.com>
 **************************************************************************/
/**** HEADER *****/
 static char *const _id = "$Id: checkcode.c,v 1.10 2003/11/17 16:55:36 papowell Exp papowell $";

#include "ifhp.h"

/**** ENDINCLUDE ****/

 /*
 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 Check out the HP PCL 5 reference manual set; it includes the PJL manual
 which contains a complete (as far as I know) listing of status/error codes.
 In case you don't have the manual handy, here's some code I used to handle
 the status codes. The code should be fairly obvious. Free feel to use it.
 
 Eelco van Asperen            /                 Erasmus University Rotterdam
 ----------------------------/   Informatievoorziening en Automatisering FEW
 vanasperen@facb.few.eur.nl / PObox 1738, 3000 DR Rotterdam, The Netherlands
 
 The header file, pjlcode.h:
 
 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 */

#define	PJLC_GROUP(c)	((c)/1000)
#define	PJLC_CODE(c)	((c)%1000)
#define PJLC_X(c)       ((c/100)%10)
#define PJLC_YY(c)      ((c)%100)
#define PJLC_XX(c)      ((c/10)%100)
#define PJLC_Y(c)       ((c)%10)

 typedef struct code_t {
	int	code;
	char *msg;
	int flag;
} code_t;

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

 And the actual code, pjlcode.c:

  -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

 code_t	 groupcode[] = {
	{ 10, "Informational", 0},
#define XTrayCodeYYMediaCode     1
	{ 11, "Background Paper Loading", XTrayCodeYYMediaCode },
#define XTrayCodeYYTrayStatus    2
	{ 12, "Background Paper Tray Status", XTrayCodeYYTrayStatus },
#define XXOutputYBinStatus       3
	{ 15, "Output Bin Status", XXOutputYBinStatus },
	{ 20, "PJL Parser Errors", 0},
	{ 25, "PJL Parser Warning", 0},
	{ 27, "PJL Semantic Errors", 0},
	{ 30, "Auto Continuable Conditions", 0},
	{ 32, "PUL File System Errors", 0},
	{ 35, "Possible Operator Intervention Conditions", 0},
	{ 40, "Operator Intervention Required", 0},
	{ 41, "Paper Mount Required", /* "Foreground Paper Mount" */ XTrayCodeYYMediaCode },
#define XJamCountYYJamLocation   4
	{ 42, "Paper Jam", XJamCountYYJamLocation },
#define XExtDeviceYYCode         5
	{ 43, "External Paper Handling Device Errors", XExtDeviceYYCode },
#define XJamCountYY4000Location  6
	{ 44, "LJ4000 Paper Jam", XJamCountYY4000Location },
	{ 50, "Hardware Errors", 0},
	{ 0, 0, 0},
};
 static	code_t	pjlmsg[] = {
	/* Informational Message */
	{ 10000, "powersave mode", 0},
	{ 10001, "Ready Online", 0},
	{ 10002, "Ready Offline", 0},
	{ 10003, "Warming Up", 0},
	{ 10004, "Self Test", 0},
	{ 10005, "Reset", 0},
	{ 10006, "Toner Low", 0},
	{ 10007, "Cancelling Job", 0},
	{ 10010, "Status Buffer Overflow", 0},
	{ 10011, "auxiliary IO initialisation", 0},
	{ 10013, "self test", 0},
	{ 10014, "printing test", 0},
	{ 10015, "typeface list", 0},
	{ 10016, "engine test", 0},
	{ 10017, "demo page", 0},
	{ 10018, "menu reset", 0},
	{ 10019, "reset active IO", 0},
	{ 10020, "reset all IO", 0},
	{ 10022, "config page", 0},
	{ 10023, "processing job", 0},
	{ 10024, "waiting", 0},
	{ 10027, "remove paper from printer", 0},
	{ 10029, "formfeeding", 0},
	{ 10030, "job message displayed", 0},
	{ 10999, "warming up", 0},

	/* 11xxx (Background Paper Loading Messages */
	{ 11304, "tray 3 empty (tray 3=LC)", 0},

	/* Background paper tray status */
	{ 12201, "tray 2 open", 0},
	{ 12202, "tray 2 lifting", 0},
	{ 12301, "tray 3 open", 0},
	{ 12302, "tray 3 lifting", 0},


	/* PJL Parser Error */
	{ 20001, "generic syntax error", 0},
	{ 20002, "unsupported command", 0},
	{ 20004, "unsupported personality/system", 0},
	{ 20006, "illegal character or line terminated by the UEL command", 0},
	{ 20007, "whitespace or linefeed missing after closing quotes", 0},
	{ 20008, "invalid character in an alphanumeric value", 0},
	{ 20009, "invalid character in a numeric value", 0},
	{ 20010, "invalid character at the start of a string,"
			"alphanumeric value or numeric value", 0},
	{ 20011, "string missing closing double-quote character", 0},
	{ 20012, "numeric value starts with a decimal point", 0},
	{ 20013, "numeric value does not contain any digits", 0},
	{ 20014, "no alphanumeric value after command modifier", 0},
	{ 20015, "option name and equal sign encountered"
			" but value field is missing", 0},
	{ 20016, "more than one command modifier", 0},
	{ 20017, "command modifier encountered after an option", 0},
	{ 20018, "command not an alphanumeric value", 0},
	{ 20019, "numeric value encountered when"
			" an alphanumeric value expected", 0},
	{ 20020, "string encountered when an alphanumeric value expected", 0},
	{ 20021, "unsupported command modifier", 0},
	{ 20022, "command modifier missing", 0},
	{ 20023, "option missing", 0},
	{ 20024, "extra data received after option name", 0},
	{ 20025, "two decimal points in a numeric value", 0},
	{ 20026, "invalid binary value", 0},

	/* PJL Parser Warning */
	{ 25001, "generic warning", 0},
	{ 25002, "PJL prefix missing", 0},
	{ 25003, "alphanumeric value too long", 0},
	{ 25004, "string too long", 0},
	{ 25005, "numeric value too long", 0},
	{ 25006, "unsupported option name", 0},
	{ 25007, "option name requires a value which is missing", 0},
	{ 25008, "option name requires value of a different type", 0},
	{ 25009, "option name received with a value,"
			" but this option does not support values", 0},
	{ 25010, "same option name received more than once", 0},
	{ 25011, "ignored option name due to value underflow or overflow", 0},
	{ 25012, "value truncated or rounded", 0},
	{ 25013, "value out of range; used closest supported limit", 0},
	{ 25014, "value out of range; ignored", 0},
	{ 25016, "option name received with an alphanumeric value,"
			" but this value is not supported", 0},
	{ 25017, "string empty, option ignored", 0},

	/* PJL Semantic Errors; */
	{ 27001, "generic sematic error", 0},
	{ 27002, "EOJ encountered without a previous matching JOB command", 0},
	{ 27004, "value of a read-only variable can not be changed", 0},

	/* Auto-Continuable Condition */
	{ 30010, "status buffer overflow", 0},
	{ 30016, "memory overflow", 0},
	{ 30017, "print overrun", 0},
	{ 30018, "communication error", 0},
	{ 30027, "IO buffer overflow", 0},
	{ 30034, "paper feed error", 0},
	{ 30035, "NVRAM error", 0},
	{ 30036, "NVRAM full", 0},
	{ 30072, "Remove Page", 0},
	{ 30076, "Personity Memory Overflow", 0},
	{ 30094, "General Misprint Error", 0},
	{ 30095, "41.2 Beam Detect Malfunction", 0},
	{ 30096, "41.3 Improper or unexpected paper size", 0},
	{ 30097, "41.4 No video sync", 0},
	{ 30098, "41.4 Noisy video sync", 0},
	{ 30099, "Invalid input source", 0},
	{ 30100, "Invalid output destination", 0},
	{ 30101, "Binding agent out of supplies", 0},
	{ 30102, "Binding agent misalignment", 0},
	{ 30103, "Binding agent media capacity exceeded", 0},
	{ 30104, "External input paper path jam", 0},
	{ 30105, "External output paper path jam", 0},
	{ 30106, "External input device error", 0},
	{ 30107, "External output device error", 0},
	{ 30108, "MIO Error, press GO key", 0},
	{ 30109, "Bad serial data format, press GO key", 0},
	{ 30110, "MIO Error, press GO key", 0},
	{ 30111, "Parallel IO Error, press GO key", 0},
	{ 30112, "Serial IO Error, press GO key", 0},
	{ 30113, "Stopping check last page", 0},
	{ 30114, "Press select to cancel job", 0},
	{ 30119, "Media Feed error", 0},

	/* Possible Operator Intervention Condition */
	{ 35029, "BAD MIO", 0},
	{ 35029, "W1 image adapt", 0},
	{ 35030, "WA job altered", 0},
	{ 35031, "W2 invalid personality", 0},
	{ 35032, "WL warnings lost", 0},
	{ 35037, "W3 job aborted", 0},
	{ 35039, "W9 job 600/LTR", 0},
	{ 35040, "W0 job 600/A4", 0},
	{ 35041, "W8 job 600/OFF", 0},
	{ 35042, "W7 job 300/LGL", 0},
	{ 35043, "W5 job 300/LTR", 0},
	{ 35044, "W6 job 300/A4", 0},
	{ 35045, "W4 job 300/OFF", 0},
	/* turn off from control panel using SERVICE MSG = NO */
	{ 35073, "WM Chk MEM Config", 0},
	{ 35074, "WM MEM Config N/A", 0},
	{ 35075, "User Maintenance Required (200000 copies done)", 0},
	{ 35076, "WM Memory Full, stored data lost", 0},
	{ 35078, "powersave mode", 0},
	{ 35081, "WM Job 300", 0},
	{ 35082, "WD Disk not init", 0},
	{ 35084, "WM job 600/LGL", 0},
	{ 35085, "WM job 600/A3", 0},
	{ 35086, "WM job 600/11x17", 0},
	{ 35087, "WM job 600/A3", 0},
	{ 35088, "WM job 600/11x17", 0},
	{ 35115, "Volume 1 not init", 0},
	{ 35117, "Volume 2 not init", 0},

	/* Operator Intervention Needed */
	{ 40000, "Sleep Mode (STANDBY)", 0},
	{ 40005, "Cartridge Error or Reading Setup Sheet", 0},
	{ 40010, "no EP cartridge", 0},
	{ 40011, "Error Accessing ink cartridge", 0},
	{ 40019, "upper out-bin full", 0},
	{ 40021, "printer open", 0},
	{ 40022, "paper jam", 0},
	{ 40024, "FE cartridge", 0},
	{ 40026, "PC install", 0},
	{ 40038, "toner low", 0},
	{ 40046, "insert cartridge", 0},
	{ 40047, "remove cartridge", 0},
	{ 40048, "[PJL OpMsg]", 0},
	{ 40049, "[PJL StMsg]", 0},
	{ 40050, "service error 50", 0},
	{ 40051, "temporary error 51", 0},
	{ 40052, "temporary error 52", 0},
	{ 40053, "memory error", 0},
	{ 40054, "54 error", 0},
	{ 40055, "temporary error 55", 0},
	{ 40056, "56 Error", 0},
	{ 40057, "service error 57", 0},
	{ 40058, "service error 58", 0},
	{ 40059, "59 error", 0},
	{ 40061, "RAM parity error", 0},
	{ 40062, "error during memory check", 0},
	{ 40063, "internal RAM error", 0},
	{ 40064, "internal service error 64", 0},
	{ 40065, "65 error", 0},
	{ 40067, "67 error", 0},
	{ 40069, "70 error", 0},
	{ 40070, "71 error", 0},
	{ 40071, "72 error", 0},
	{ 40079, "offline", 0},
	{ 40080, "EE incompatible", 0},
	{ 40083, "FS Disk Failure", 0},
	{ 40089, "Incomplete Tray3 Installed", 0},
	{ 40090, "Incomplete Envelope Feeder Installed", 0},
	{ 40092, "81 service", 0},
	{ 40093, "Duplexer Jam", 0},
	{ 40099, "56.1 Error Press Select Key", 0},
	{ 40100, "56.2 Error Press Select Key", 0},
	{ 40102, "Finisher alignment error", 0},
	{ 40103, "Finisher limit reached", 0},
	{ 40104, "Input device feed path open", 0},
	{ 40105, "Output device delivery path open", 0},
	{ 40106, "Input operation error", 0},
	{ 40107, "Ouput operation error", 0},
	{ 40116, "Volume 1 failure", 0},
	{ 40118, "Volume 2 failure", 0},
	{ 40120, "Open face-up output bin", 0},
	{ 40121, "Close face-up output bin", 0},
	{ 40122, "Duplexer not installed", 0},
	{ 40123, "Duplexer error, remove duplexer", 0},
	{ 40124, "Bad Duplexer connection", 0},

	{ 0, 0, 0},
};


 static code_t tray_status[] = {
	{ 1, "Tray Open", 0},
	{ 2, "Tray Lifting", 0},

	{ 0, 0, 0}
};
 static code_t bin_status[] = {
	{ 1, "Output Bin Full", 0},
	{ 2, "Output Bin Open", 0},
	{ 3, "Output Bin Broken", 0},

	{ 0, 0, 0}
};
 static	code_t	tray_tab[] = {
	{ 0, "MP/Tray1/DuplexTray", 0},
	{ 1, "Manual Feed", 0},
	{ 2, "PC/Upper/Tray2", 0},
	{ 3, "LC/Lower/Tray3", 0},
	{ 4, "EE/EnvelopeFeeder", 0},
	{ 5, "HCI", 0},

	{ 0, 0, 0},
};
 static	code_t	media_tab[] = {
	{ 0, "Unknown Paper", 0},
	{ 1, "Unknown envelope", 0},
	{ 2, "Letter Paper", 0},
	{ 3, "Legal Paper", 0},
	{ 4, "A4 Paper", 0},
	{ 5, "Exec Paper", 0},
	{ 6, "Ledger Paper", 0},
	{ 7, "A3 Paper", 0},
	{ 8, "COM10 envelope", 0},
	{ 9, "Monarch envelope", 0},
	{ 10, "C5 envelope", 0},
	{ 11, "DL envelope", 0},
	{ 12, "B4 Paper", 0},
	{ 13, "B5 Paper", 0},
	{ 14, "B5 envelope", 0},
	{ 15, "Custom media", 0},
	{ 16, "J-Postcard", 0},
	{ 17, "J-RoundTrip Postcard", 0},
	{ 18, "A5 Paper", 0},
	{ 19, "Letter-R Paper", 0},
	{ 20, "A4-R Paper", 0},
	{ 21, "A5-R Paper", 0},
	{ 22, "Japan B5-R Paper", 0},
	{ 23, "Exec-R Paper", 0},
	{ 24, "A6 Paper", 0},
	{ 25, "Foolscap Paper", 0},
	{ 26, "Japan B6 Paper", 0},
	{ 0, 0, 0},
};
 static code_t jam_location[] = {
 { 0, "Non-specific jam", 0},
 { 1, "Paper feed 1 jam", 0},
 { 2, "Paper feed 2 jam", 0},
 { 3, "Fuser output jam", 0},
 { 4, "Fuser output jam", 0},
 { 5, "Face down output jam", 0},
 { 6, "Face down output jam", 0},
 { 7, "2 Sided Turn Around jam", 0},
 { 8, "2 Sided Turn Around jam", 0},
 { 9, "2 Sided path jam", 0},
 { 10, "2 Sided path jam", 0},
 { 11, "External input device jam", 0},
 { 12, "External output device jam", 0},
 { 13, "Fuser Accordion jam", 0},
 { 14, "Fuser Accordion jam", 0},
 { 15, "Printer could not eject paper jam", 0},
 { 0, 0, 0 },
};

 static code_t ext_dev[] = {
 { 1, "Device 1", 0},
 { 2, "Device 2", 0},
 { 3, "Device 3", 0},
 { 4, "Device 4", 0},
 { 5, "Device 5", 0},
 { 0, 0, 0},
};

 static code_t ext_code[] = {
 { 1, "Unable to communicate with PHC", 0},
 { 2, "Out of sequence packet", 0},
 { 3, "Bad Transaction", 0},
 { 4, "Invalid status bits", 0},
 { 5, "Invalid status code", 0},
 { 6, "Invalid device specified", 0},
 { 7, "Invalid tray specified", 0},
 { 8, "Invalid bin specified", 0},
 { 9, "Invalid number of special purpose bytes", 0},
 { 10, "Invalid number of NVEE bytes", 0},
 { 11, "General PHC malfunction", 0},
 { 12, "Bad PHC RAM", 0},
 { 13, "PHC ROM Checksum Error", 0},
 { 14, "Faulty PHC FIFO", 0},
 { 15, "PHC cannot communicate with devices", 0},
 { 16, "PHC never ready", 0},
 { 17, "FIFO empty when expecting data", 0},
 { 18, "FIFO full when tray to send data", 0},
 { 19, "Invalid page ACK", 0},
 { 20, "Pack ACK timeout", 0},
 { 21, "Inconsistent page size", 0},
 { 22, "Wrong page ack", 0},
 { 23, "Devices never ready during initialization", 0},
 { 24, "Negative ACK of ready tray", 0},
 { 0, 0, 0 },
};

 static code_t lj4000_jam[] = {
 { 0, "Unknown jam", 0},
 { 1, "input area jam", 0},
 { 2, "output area jam", 0},
 { 3, "underneath top cover jam", 0},
 { 4, "duplexer jam", 0},
 { 5, "duplex front cover area jam", 0},
 { 6, "face down output area jam", 0},
 { 7, "front door area jam", 0},
 { 8, "left door area jam", 0},
 { 9, "right door area jam", 0},
 { 0, 0, 0},
};
 
 static code_t * lookup(code_t *tab, int target)
{
	DEBUG4("lookup_code: code %d", target );
	while( tab->msg && tab->code != target ) ++tab;
	DEBUG4("lookup_code: return %d '%s'", tab->code, tab->msg );
	return( tab );
}

 void pjl_message(char *code_str, char *msg, int msglen)
{
	code_t *code_group, *decoded_msg, *tray, *media, *status;
	int code = atoi(code_str);
	int group = PJLC_GROUP(code);
	int X, XX, Y, YY;

	X = PJLC_X(code);
	YY = PJLC_YY(code);
	XX = PJLC_XX(code);
	Y = PJLC_Y(code);

	DEBUG4("pjl_message: code %d, group %d, X %d, YY %d, XX %d, Y %d",
		code, group, X, YY, XX, Y );

	/* find the code group name */
	msg[0] = 0;
	decoded_msg = lookup(pjlmsg, code );
	if( decoded_msg->msg ){
		SNPRINTF(msg,msglen) "%s",decoded_msg->msg);
		return;
	}

	/* now we use the code group name */
	code_group = lookup( groupcode, group );
	switch( code_group->flag ){
	/* 11 XTrayCodeYYMediaCode tray_tab media_tab */
	case XTrayCodeYYMediaCode:
		tray = lookup( tray_tab, X );
		media = lookup( media_tab, YY );
		SNPRINTF(msg,msglen) "%s - %s", tray->msg, media->msg );
		break;
	/* 12 XTrayCodeYYTrayStatus tray_tab tray_status */
	case XTrayCodeYYTrayStatus:
		tray = lookup( tray_tab, X );
		status = lookup( tray_status, YY );
		SNPRINTF(msg,msglen) "%s - %s", tray->msg, status->msg );
		break;
	/* 15 XXOutputYBinStatus binnumber bin_status  */
	case XXOutputYBinStatus:
		status = lookup( bin_status, Y );
		SNPRINTF(msg,msglen) "Bin %d - %s", XX, status->msg );
		break;
	/* 42 XJamCountYYJamLocation jamcount jam_location */
	case XJamCountYYJamLocation:
		status = lookup( jam_location, YY );
		SNPRINTF(msg,msglen) "%s - %d pages", status->msg, X );
		break;
	/* 43 XExtDeviceYYCode ext_dev ext_code */
	case XExtDeviceYYCode:
		tray = lookup( ext_dev, X );
		status = lookup( ext_code, YY );
		SNPRINTF(msg,msglen) "%s - %s", tray->msg, status->msg );
		break;
	/* 44 XJamCountYY4000Location jamcount lj4000_jam */
	case XJamCountYY4000Location:
		status = lookup( lj4000_jam, YY );
		SNPRINTF(msg,msglen) "%s - %d pages", status->msg, X );
		break;
	}
	if( msg[0] == 0 ){
		if( code_group->msg ){
			SNPRINTF(msg,msglen) "%s - message '%s'", code_group->msg, code_str );
		} else {
			SNPRINTF(msg,msglen) "Unknown message '%s'", code_str );
		}
	}
}

void Check_code( char *code_str, char *msg, int msglen )
{
	char *s;
	DEBUG4( "Check_code: '%s'", code_str );
	msg[0] = 0;
	pjl_message( code_str, msg, msglen );
	if( msg[0] == 0 && (s = GET_HASH_STR_OBJ(Pjl_error_codes, code_str, MEMINFO)) ){ 
		SNPRINTF(msg,msglen) "%s", s );
	}
	DEBUG4("Check_code: returning '%s'", msg );
}
