1 /* min12xxw.c		driver for minolta pagepro 1[234]xxW printers
2  *
3  * It converts pages in pbmraw format read from stdin to the printer
4  * language used by Minolta PagePro 1[234]00W. The output goes to stdout.
5  *
6  * Copyright (C) 2004-2006 Manuel Tobias Schiller <mala@hinterbergen.de>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22 /* Note: This program was written without any documentation from the
23  * manufacturer. The description of the printer language may be
24  * inaccurate or even wrong because it was obtained by looking at the
25  * output of the windoof driver only. The code dealing with the
26  * compression of raster data is in its spirit heavily based on an
27  * initial driver by Adam Bocim <beetman@seznam.cz> who managed to
28  * find out how things are done (thanks, Adam, you did a great job!).
29  * This new version is considerably faster, implemented more cleanly
30  * and (hopefully) well documented.
31  * It also features querying the printer status and page counter, a
32  * suggestion made by Bruno Schoedlbauer <bruno.schoedlbauer@gmx.de>,
33  * who also pointed me to a nice USB sniffer by Benoit Papillault for
34  * Windoof 98 and up, see http://benoit.papillault.free.fr/usbsnoop.
35  * (This little program may prove very helpful on similar occasions.
36  * Happy USB sniffing...)
37  * */
38 /* see the file CHANGELOG for a list of changes between revisions.
39  */
40 
41 #include "config.h"
42 
43 #define _GNU_SOURCE /* we might need this because we use getline */
44 #include <assert.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <ctype.h>
48 #include <stdio.h>
49 #include <unistd.h>	/* usleep */
50 #include <sys/stat.h>	/* fstat */
51 #include <getopt.h>
52 
53 /* include the inttypes.h for uint8_t and similar types */
54 #ifdef HAVE_INTTYPES_H
55 #include <inttypes.h>
56 #else
57 /* don't know yet what to do in this case. just put some typedefs there
58  * and hope for the best? until someone complains we just make the
59  * compiler throw an error... */
60 #error "You'll need either stdint.h or inttypes.h"
61 #endif
62 
63 /* make sure we get getline even if the system libc does not provide it */
64 #ifndef HAVE_GETLINE
65 #include <getline.h>
66 #endif
67 
68 #ifndef MIN
69 #define MIN(x, y) (((x) < (y))?(x):(y))
70 #endif
71 #ifndef MAX /* not used now - but might come handy in the future */
72 #define MAX(x, y) (((x) > (y))?(x):(y))
73 #endif
74 
75 /* we wish to know what version we are ;) */
76 static char *versionstr = "version " PACKAGE_VERSION " ($Id: min12xxw.c 115 2005-12-31 13:50:48Z mala $)";
77 
78 /* defaults used for the output */
79 static int ptype = 0x00; /* normal paper */
80 static int pformat = 0x04; /* a4 */
81 static int res = 0x0001; /* 600 dpi, horizontal resolution not doubled */
82 static int tray = 0xff; /* auto select paper tray */
83 static int nomargins = 0; /* if flag is set, margins are not enforced */
84 static int ecomode = 0; /* if set, toner-saving mode is enabled */
85 static int model = 0x81; /* 12xxW series printers are the default */
86 static char *device = "/dev/lp0"; /* default device used for queries */
87 
88 /* try to exit gracefully in case of fatal errors */
fatal(char * str)89 static void fatal(char *str)
90 {
91 	perror(str);
92 	exit(1);
93 }
94 
95 /**********************************************************************
96  * minolta esc command utility
97  *
98  * this is used to send printer commands and read printer response
99  * packets
100  * see the file format.txt for an overview of what has been found out
101  * about the format used
102  **********************************************************************/
do_cmd(FILE * out,uint8_t cmd,uint32_t len,uint8_t * data)103 static void do_cmd(FILE *out, uint8_t cmd, uint32_t len, uint8_t *data)
104 {
105 	/* command sequence number, incremented with each command */
106 	static uint8_t sq = 0;
107 	uint8_t cksum = 0; /* every command is transmitted with a checksum */
108 	uint8_t buf[6]; /* buffer to build up command header */
109 	uint8_t *end = &data[len], *p = buf;
110 
111 	assert(len < 0x10000); /* do not transmit way too much data */
112 
113 	/* fill in command header */
114 	*p++ = 0x1b; *p++ = cmd; *p++ = sq++;
115 	*p++ = len & 0xff; *p++ = len >> 8; *p++ = ~cmd;
116 
117 	/* calculate the checksum */
118 	p = data;
119 	while (p < end) cksum += *p++;
120 	p = buf;
121 	end = &buf[6];
122 	while (p < end) cksum += *p++;
123 
124 	/* ok, write the command to the output file */
125 	if (fwrite(buf, sizeof(uint8_t), 6, out) != 6)
126 		fatal("min12xxw: error writing command to output file.");
127 	if (fwrite(data, sizeof(uint8_t), len, out) != len)
128 		fatal("min12xxw: error writing command to output file.");
129 	if (fputc(cksum, out) == EOF)
130 		fatal("min12xxw: error writing command to output file.");
131 }
132 
133 /* this will read data from the printer
134  * note: when reading data from the printer over the USB port, we must
135  * check what we get back because the printer will give back the wrong
136  * register when it is not ready - in this case we sleep a bit and try
137  * again */
do_read(FILE * in,uint8_t reg,int * len)138 static uint8_t *do_read(FILE *in, uint8_t reg, int *len)
139 {
140 	int c, d, i, j = 0;
141 	uint8_t *retVal = NULL;
142 
143 	/* try to read the desired register in a loop */
144 	do {
145 		usleep(100000);
146 		if (retVal != NULL) free(retVal);
147 		if ((d = fgetc(in)) == EOF)
148 			fatal("min12xxw: error reading data from printer");
149 		if ((c = fgetc(in)) == EOF)
150 			fatal("min12xxw: error reading data from printer");
151 
152 		if ((retVal = (uint8_t *) malloc(c * sizeof(uint8_t)))==NULL)
153 			fatal("min12xxw: error allocating buffer");
154 		for (i = 0; i < c; i++) {
155 			int cc;
156 			if ((cc = fgetc(in)) == EOF)
157 				fatal("min12xxw: error reading data from " \
158 					"printer");
159 			retVal[i] = (uint8_t) cc;
160 		}
161 		/* break if we have the desired register or we have waited
162 		 * long enough for the printer to reply and have not seen
163 		 * a decent answer */
164 	} while ((d != reg) && (j++ < 10));
165 
166 	if (d != reg) {
167 		/* we didn't manage to read the desired register */
168 		free(retVal);
169 		*len = 0;
170 		return NULL;
171 	}
172 
173 	/* we have read the desired register */
174 	*len = c;
175 	return retVal;
176 }
177 
178 /* sends the start signal to the printer (make printer pay attention) */
do_start(FILE * out)179 static void do_start(FILE *out)
180 {
181 	uint8_t buf[2];
182 
183 	buf[0] = (uint8_t) model;
184 	buf[1] = 0;
185 	do_cmd(out, 0x40, 2, buf);
186 }
187 
188 /* sends the stop signal to the printer (terminate a command sequence) */
do_stop(FILE * out)189 static void do_stop(FILE *out)
190 {
191 	uint8_t buf = 0;
192 
193 	do_cmd(out, 0x41, 1, &buf);
194 }
195 
196 /* send read register command */
do_readreg(FILE * out,uint8_t reg)197 static void do_readreg(FILE *out, uint8_t reg)
198 {
199 	uint8_t buf[2];
200 
201 	buf[0] = reg;
202 	buf[1] = 0;
203 	do_cmd(out, 0x60, 2, buf);
204 }
205 
206 /* send register enabler command - not sure if this is what this command
207  * does */
do_enreg(FILE * out)208 static void do_enreg(FILE *out)
209 {
210 	uint8_t buf[3];
211 
212 	/* FIXME: until we know better, we'll just treat the 1400W as a
213 	 * better 13xxW here - testers report that this seems to work
214 	 * well */
215 	buf[0] = (((model == 0x83) || (model == 0x86))?(0x1c):(0x78));
216 	buf[1] = 0;
217 	buf[2] = 0x04;
218 	do_cmd(out, 0x6a, 3, buf);
219 }
220 
221 /* produce a quick and dirty register dump from the printer registers
222  * this routine was mainly used for debugging purposes and is therefore
223  * disabled by default - it was never meant to be used by a wider
224  * audience */
225 #if 0
226 static void regdump(FILE *f)
227 {
228 	int i, len, state;
229 	uint8_t *buf = NULL, regs[] = { 0x81, 0x53, 0x05, 0x04, 0x03, 0x02 };
230 
231 	do_start(f);
232 	buf = do_read(f, 0x4a, &len);
233 	printf("\nread start sequence state 0x%02x, %3d bytes:\n", 0x4a, len);
234 	for (state = 0; state < len; state++)
235 		putchar(buf[state]);
236 	free(buf);
237 	do_enreg(f);
238 	buf = do_read(f, 0x45, &len);
239 	printf("\nread reg enabler sequence state 0x%02x, %3d bytes:\n", \
240 		0x45, len);
241 	for (state = 0; state < len; state++)
242 		putchar(buf[state]);
243 	free(buf);
244 	for (i = 0; i < 6; i++) {
245 		do_readreg(f, regs[i]);
246 		buf = do_read(f, regs[i], &len);
247 		printf("\nread register 0x%02x, %3d bytes:\n", regs[i], len);
248 		if (len > 0) {
249 			for (state = 0; state < len; state++)
250 				putchar(buf[state]);
251 		}
252 		free(buf);
253 	}
254 	do_stop(f);
255 }
256 #endif
257 
258 /* this prints whatever information we can read from the hardware */
printhwstate(FILE * f)259 static void printhwstate(FILE *f)
260 {
261 	uint8_t *bufcfw, *bufefw = NULL, *bufpcnt, *bufst;
262 	int len;
263 
264 	do_start(f);
265 	/* the printers appear to have a sort of enabler command - I'm
266 	 * not sure if this is what this does - we send it anyway,
267 	 * because the windoof driver does it as well */
268 	do_enreg(f);
269 	/* get printer state */
270 	do_readreg(f, 0x04);
271 	bufst = do_read(f, 0x04, &len);
272 	if (len != 0) {
273 		memmove(bufst, &bufst[1], len - 1);
274 		bufst[len - 1] = 0;
275 	}
276 	/* get controller firmware version */
277 	do_readreg(f, 0x02);
278 	bufcfw = do_read(f, 0x02, &len);
279 	if (len != 14)
280 		goto readerr;
281 	/* get engine firmware version for models that support it */
282 	do_readreg(f, 0x81);
283 	bufefw = do_read(f, 0x81, &len);
284 	/* if len != 0 the printer appears not to have register 0x81 */
285 	if ((len != 30) && (len != 0))
286 		goto readerr;
287 	/* ok, read page counter */
288 	do_readreg(f, 0x53);
289 	bufpcnt = do_read(f, 0x53, &len);
290 	if (len != 38)
291 		goto readerr;
292 	do_stop(f);
293 
294 	printf("printer status: %s\n", bufst);
295 	free(bufst);
296 	printf("controller firmware version: %c%c%c%c\n", \
297 		bufcfw[3], bufcfw[2], bufcfw[1], bufcfw[0]);
298 	free(bufcfw);
299 	if (bufefw != NULL) {
300 		/* print engine firmware version if the model supports it */
301 		memmove(bufefw, &bufefw[18], 12);
302 		bufefw[12] = 0;
303 		printf("engine firmware version: %s\n", bufefw);
304 		free(bufefw);
305 	}
306 	len = bufpcnt[30] | (bufpcnt[31] << 8) | (bufpcnt[32] << 16) | \
307 	      (bufpcnt[33] << 24);
308 	printf("page counter: %lu pages\n", (unsigned long) len);
309 	free(bufpcnt);
310 	return;
311 
312 readerr:
313 	do_stop(f); /* try not to hang the printer */
314 	fatal("min12xxw: read unexpected data from printer");
315 }
316 
317 /* this procedure sends raster data to the printer */
send_raster_data(FILE * out,uint32_t nlines,uint32_t len,uint8_t * data)318 static void send_raster_data(FILE *out, uint32_t nlines, \
319 				uint32_t len, uint8_t *data)
320 {
321 	uint8_t cmdbuf[6], *p = cmdbuf;
322 
323 	assert(nlines < (1 << 16)); /* just to make sure */
324 	*p++ = len & 0xff; *p++ = (len >> 8) & 0xff;
325 	*p++ = (len >> 16) & 0xff; *p++ = len >> 24;
326 	*p++ = nlines & 0xff;
327 	*p++ = (nlines >> 8) & 0xff;
328 	/* we start with sending the command */
329 	do_cmd(out, 0x52, 6, cmdbuf);
330 	/* the data is appended immediately */
331 	if (fwrite(data, sizeof(uint8_t), len, out) != len)
332 		fatal("min12xxw: couldn't send raster data to output file");
333 }
334 
335 /* this procedure sends a start-of-job/select-resolution-and-papertype
336  * sequence to the printer */
send_start_job(FILE * out)337 static void send_start_job(FILE *out)
338 {
339 	uint8_t cmdbuf[8];
340 
341 	memset(cmdbuf, 0x00, 8 * sizeof(uint8_t)); /* zero our buffer */
342 
343 
344 	do_start(out); /* start-of-printer-commands command */
345 
346 	cmdbuf[0] = (uint8_t) (res & 0xff); /* set resolution and paper type */
347 	/* check if we are to use doubled horizontal resolution */
348 	cmdbuf[1] = (uint8_t) (res >> 8);
349 	cmdbuf[3] = (uint8_t) ptype;
350 	cmdbuf[4] = 0x04;
351 	/* 1[34]xxW series models might expect an 0x04 in cmdbuf[6] as well */
352 	if ((model == 0x83) || (model == 0x86)) cmdbuf[6] = 0x04;
353 	do_cmd(out, 0x50, 8, cmdbuf); /* issue it */
354 }
355 
356 /* this procedure sends an end-of-job sequence to the printer */
send_end_job(FILE * out)357 static void send_end_job(FILE *out)
358 {
359 	uint8_t cmdbuf = 0;
360 
361 	do_cmd(out, 0x55, 1, &cmdbuf);
362 	do_stop(out);
363 }
364 
365 /* the following procedure sends a new page command */
send_new_page(FILE * out,uint32_t x,uint32_t y)366 static void send_new_page(FILE *out, uint32_t x, uint32_t y)
367 {
368 	uint8_t cmdbuf[22], *p = &cmdbuf[1];
369 
370 	memset(cmdbuf, 0x00, 22 * sizeof(uint8_t)); /* zero our buffer */
371 	*p++ = 0x01;
372 	*p++ = (x >> 16) & 0xff; *p++ = x >> 24;
373 	*p++ = x & 0xff; *p++ = (x >> 8) & 0xff;
374 	*p++ = (y >> 16) & 0xff; *p++ = y >> 24;
375 	*p++ = y & 0xff; *p++ = (y >> 8) & 0xff;
376 	*p++ = 0x08; p++; *p++ = 0x08; p++;
377 	*p++ = tray; *p++ = pformat;
378 	if ((res & 0xff) == 0) {
379 		/* apparently, 300 dpi needs special flags set here */
380 		cmdbuf[20] = 0xc0;
381 	}
382 	do_cmd(out, 0x51, 22, cmdbuf); /* issue our command */
383 }
384 
385 /**********************************************************************
386  * raster data compression
387  **********************************************************************/
388 /* the following table keeps sixteen bytes which can be sent to the
389  * printer by referring to their table index (4 bit only)
390  * this table is filled anew for every scanline */
391 static int tbllen = -1; /* all tables uninitialized by default */
392 static uint8_t tbl[16];
393 static uint8_t invtbl[256]; /* this is for inverse lookups */
394 
395 /* return the number of repetitions of a byte at position pointed to by p */
get_len(uint8_t * p,uint8_t * end)396 static uint32_t get_len(uint8_t *p, uint8_t *end)
397 {
398 	uint8_t c = *p++;
399 	uint32_t len = 0;
400 
401 	while ((p < end) && (c == *p++)) len++;
402 
403 	return ++len;
404 }
405 
406 /* add a byte to the table and return the table index to it */
add_tbl(uint8_t b)407 static uint8_t add_tbl(uint8_t b)
408 {
409 	/* is there space in the table or is the byte already there?
410 	 * if the table is full, returning invtbl[b] is still correct
411 	 * doing it this way has the advantage of saving a constant load
412 	 * because we have the value already at hand (from the test) */
413 	if ((invtbl[b] < 16) || (tbllen >= 16)) return invtbl[b];
414 	/* ok, add it to the table */
415 	tbl[tbllen] = b;
416 	invtbl[b] = tbllen;
417 	return tbllen++;
418 }
419 
420 /* check if the next n bytes are in the table, or if we have space for them */
next_n_in_tbl(uint8_t * p,long len,uint8_t * end)421 static int next_n_in_tbl(uint8_t *p, long len, uint8_t *end)
422 {
423 	long i = 0, j = len;
424 
425 	/* make sure we don't read past the end of our data */
426 	if ((p + len) >= end) return 0;
427 
428 	/* count how many bytes are in the table */
429 	while (j--)
430 		if (invtbl[*p++] < 16) i++;
431 
432 	/* check if we have space for those which are not */
433 	if (tbllen < (17 - len + i)) return 1;
434 
435 	return 0; /* sorry - no space in table left... */
436 }
437 
438 /* compress a scanline of data and write the result to an output buffer */
compress_scanline(uint8_t * p,uint8_t * end,uint8_t * obuf)439 static uint32_t compress_scanline(uint8_t *p, uint8_t *end, uint8_t *obuf)
440 {
441 	uint32_t olen = 0;
442 	uint8_t *q; /* temporary pointer */
443 
444 	/* clear the table and inverse lookup table - we try to do
445 	 * partial clearing of the inverse lookup table if possible,
446 	 * because reading and writing 16 or fewer bytes should be faster
447 	 * than writing 256 bytes... Can't do that the first time, though */
448 	if (tbllen == -1) memset(invtbl, 0xff, 256 * sizeof(uint8_t));
449 	else for (q = tbl; q < &tbl[tbllen]; q++) invtbl[*q] = 0xff;
450 	tbllen = 0;
451 
452 	while (p < end) {
453 		uint32_t n = get_len(p, end);
454 		if (n > 2) {
455 			/* RLE compression pays off */
456 			/* make sure n is small enough - we're paranoid */
457 			assert(n < (63 * 64 + 63));
458 			if (n > 63) {
459 				/* how to encode multiples of 64 bytes */
460 				*obuf++ = 0xc0 | (n >> 6);
461 				*obuf++ = *p;
462 				olen += 2;
463 				p += n & (~0x3f);
464 				n &= 0x3f;
465 			}
466 			if (n) {
467 				/* encode the remainder */
468 				*obuf++ = 0x80 | n;
469 				*obuf++ = *p;
470 				olen += 2;
471 				p += n;
472 			}
473 		} else if (next_n_in_tbl(p, 4, end)) {
474 			/* ok - try to do table compression */
475 			/* the next four bytes are in the table or there
476 			 * is enough space to put them into the table
477 			 * we issue a 0x41 code to indicate that we send
478 			 * table indices - the 0x41 means that we send
479 			 * a quartet of table indices - we might even
480 			 * send more, see below */
481 			q = obuf; /* back up current position */
482 			*obuf++= 0x41;
483 			*obuf = add_tbl(*p++) << 4;
484 			*obuf++ |= add_tbl(*p++);
485 			*obuf = add_tbl(*p++) << 4;
486 			*obuf++ |= add_tbl(*p++);
487 			olen += 3;
488 			/* check if we can send even more table indices */
489 			while (next_n_in_tbl(p, 2, end) && (*q < 0x7f)) {
490 				/* break if we can RLE-code the next bytes */
491 				if (get_len(p, MIN(p + 3, end)) >= 3) break;
492 				/* we send one more pair of table indices */
493 				(*q)++;
494 				*obuf = add_tbl(*p++) << 4;
495 				*obuf++ |= add_tbl(*p++);
496 				olen++;
497 			}
498 		} else {
499 			/* can't do table compression - fall back to send
500 			 * up to 10 bytes in uncompressed plaintext */
501 			q = obuf; /* back up current output position */
502 			*obuf++ = 0xff;
503 			olen++;
504 			do {
505 				*obuf++ = *p++;
506 				olen++;
507 				(*q)++; /* update # of plaintext bytes */
508 				/* three repeating bytes ahead? if so,
509 				 * do RLE compression instead */
510 				if (get_len(p, MIN(p + 3, end)) >= 3) break;
511 				/* see if we can do table compression for
512 				 * the bytes ahead - comment the next line
513 				 * of code out if you absolutely need the
514 				 * speed and can live with things compressing
515 				 * a little worse (on the order of a percent
516 				 * or so) */
517 				if (next_n_in_tbl(p, 4, end)) break;
518 			} while ((p < end) && (*q < 9));
519 		}
520 	}
521 
522 	return olen;
523 }
524 
525 /**********************************************************************
526  * process all the pages in the rawpbm input job
527  **********************************************************************/
528 /* printer has non-printable margins of 17/100" on all sides of a sheet
529  * of paper which are present in ghostscript output and need to be
530  * removed - these are in units of 8 pixels; note - this is not exact
531  * to the last decimal place but it has to be good enough
532  * update this if new printers allow for new resolutions codes or equal
533  * codes with different meanings!!! */
534 static int skiptbl[3] = { 6, 13, 25 };
535 
536 /* processes a single page */
dopage(FILE * in,FILE * out,uint32_t x,uint32_t y)537 static void dopage(FILE *in, FILE *out, uint32_t x, uint32_t y)
538 {
539 	uint32_t sclbytes = x / 8; /* # of bytes per scanline */
540 	/* # of scanlines per block (rounded up) */
541 	uint32_t sclperbl = y / 8 + ((y & 7)?1:0);
542 	/* size of buffer for processed data - note: the estimate given
543 	 * below is conservative - the buffer can not become larger... */
544 	uint32_t blblen = sclperbl * (17 + sclbytes + sclbytes / 10 + 1);
545 	uint8_t *scl, *blbuf; /* buffers for scanline and output block */
546 	uint32_t bllen; /* block length so far */
547 	uint32_t sclen; /* length of scanline excluding table */
548 	uint32_t yy, yc; /* counters */
549 	/* this controls how many pixels are skipped on each side of a
550 	 * sheet of paper to keep the margins that the printer expects */
551 	int skip = ((nomargins == 0)?skiptbl[res & 0xff]:0);
552 	int i; /* counter */
553 	int ecofl = 0; /* flag used for toner-saving mode */
554 
555 	/* allocate buffers */
556 	if ((scl = (uint8_t *) malloc(sclbytes * sizeof(uint8_t))) == NULL)
557 		fatal("min12xxw: couldn't allocate memory");
558 	if ((blbuf = (uint8_t *) malloc(blblen * sizeof(uint8_t))) == NULL)
559 		fatal("min12xxw: couldn't allocate memory");
560 
561 	yc = 0;
562 	for (yc = 0; yc < skip * 8; yc++) {
563 		/* skip the first few scanlines in case we enforce margins */
564 		if (fread(scl, sizeof(uint8_t), sclbytes, in) != sclbytes)
565 			fatal("min12xxw: couldn't read scanline");
566 	}
567 	/* we will always build eight blocks per page - the windows driver
568 	 * seems to do the same thing */
569 	for (i = 0; i < 8; i++) {
570 		bllen = 0;
571 		/* process the scanlines in the block */
572 		for (yy = 0; (yy < sclperbl) && (yc < y); yy++, yc++) {
573 			/* read in the next scanline */
574 			if (fread(scl, sizeof(uint8_t), sclbytes, in) != \
575 				sclbytes)
576 				fatal("min12xxw: couldn't read scanline");
577 			/* check if we need to do this line (we don't work
578 			 * for the margins...) */
579 			if ((yc + skip * 8) > y) continue;
580 			/* check if in toner-saving mode */
581 			if (ecomode) {
582 				/* clear every other scanline */
583 				if (ecofl) memset(scl, 0,
584 						sclbytes * sizeof(uint8_t));
585 				ecofl = ~ecofl;
586 			}
587 			/* compress a scanline - leave some space for the
588 			 * table (we'll probably have to remove a hole) */
589 			sclen = compress_scanline(&scl[skip], \
590 						&scl[sclbytes-skip], \
591 						blbuf + 17);
592 			blbuf[0] = 0x80 + tbllen; /* copy table */
593 			if (tbllen) memcpy(blbuf + 1, tbl, tbllen);
594 			if (tbllen < 16) /* remove the hole */
595 				memmove(blbuf + 1 + tbllen, blbuf + 17, sclen);
596 			blbuf += sclen + 1 + tbllen;
597 			bllen += sclen + 1 + tbllen;
598 		}
599 		blbuf -= bllen;
600 		/* account for skipped scanlines in the last block */
601 		if (i == 7) yy -= 8 * skip;
602 		/* send the raster data to the printer */
603 		send_raster_data(out, yy, bllen, blbuf);
604 	}
605 	free(scl); /* avoid memory leaks */
606 	free(blbuf);
607 }
608 
609 /* processes all the pages in a pbmraw job file */
dojob(FILE * in,FILE * out)610 static void dojob(FILE *in, FILE *out)
611 {
612 	uint32_t x, y; /* page dimesions in dots at the current resolution */
613 	char *line = NULL; /* buffer for reading whole lines from the input */
614 	size_t llen = 0; /* length of line buffer */
615 	/* how much to skip because of margins */
616 	int skip = ((nomargins == 0)?(16 * skiptbl[res & 0xff]):0);
617 
618 	/* send the printer a start-of-job/set-resolution-and-papertype
619 	 * sequence */
620 	send_start_job(out);
621 
622 	/* process pages */
623 	while (!feof(in) && !ferror(in)) {
624 		/* read the png header for x and y dimensions */
625 		if (getline(&line, &llen, in) == -1) break;
626 		/* check for valid pbmraw signature */
627 		if ((line[0] != 'P') || (line[1] != '4'))
628 			fatal("min12xxw: input is not valid pbmraw "
629 				"(no valid signature)");
630 		/* read in comment lines and the line containing the
631 		 * resolution */
632 		do {
633 			if (getline(&line, &llen, in) == -1)
634 				fatal("min12xxw: input is not valid pbmraw "
635 					"(premature end of file)");
636 		} while (line[0] == '#');
637 		/* parse resolution */
638 		{
639 			/* this ugly hack is needed to make things work
640 			 * on 64 bit platforms like sparc64 or amd64 */
641 			unsigned long lx, ly;
642 			if (sscanf(line, "%lu %lu", &lx, &ly) != 2)
643 				fatal("min12xxw: input is not valid pbmraw "
644 					"(ill formatted bitmap dimensions)");
645 			x = lx; y = ly;
646 		}
647 		/* scanlines are byte-aligned - adjust x accordingly */
648 		if (x & 0x7) x = 8 + (x & ~0x7);
649 		/* check if the page dimensions are so small that we need
650 		 * switch of the margins
651 		 * many thanks go to Ben Cooper who alerted me to the
652 		 * problem */
653 		if ((((y - skip) / 8) <= skip) || (x <= (2 * skip))) {
654 			if (!nomargins)
655 				fprintf(stderr, "min12xxw: page dimensions"
656 					" are so small that I won't "
657 					"enforce page margins for this and"
658 					"all subsequent pages!\n");
659 			nomargins = 1;
660 			skip = 0;
661 		}
662 
663 		/* tell the printer to start a new page - note the page
664 		 * dimensions are reduced because we need to take the
665 		 * margins into account - see above */
666 		send_new_page(out, x - skip, y - skip);
667 		/* read, process and send the raster data for the next
668 		 * page - this needs to know the real dimensions because
669 		 * it needs to know how much data to read... */
670 		dopage(in, out, x, y);
671 	}
672 
673 	/* tell the printer we got our job finished... */
674 	send_end_job(out);
675 
676 	free(line); /* free memory to avoid leaks... */
677 }
678 
679 /**********************************************************************
680  * option handling
681  **********************************************************************/
682 /* the next few tables contain the mappings from human-readable strings
683  * to id numbers the printer understands (or our routines, for that
684  * matter) */
685 struct map {
686 	char *str;
687 	int id;
688 };
689 static struct map ptypes[] = {
690 	{ "normal", 0x00 }, { "thick", 0x01 }, { "transparency", 0x02 },
691 	{ "postcard", 0x03 }, { "envelope", 0x03 }, { NULL, -1 }
692 };
693 static struct map trays[] = {
694 	{ "auto", 0xff }, { "tray1", 0x00 }, { "tray2", 0x01 },
695 	{ "manual", 0x80 }, { NULL, -1 }
696 };
697 static struct map pformats[] = {
698 	{ "a4", 0x04 }, { "b5", 0x06 }, { "a5", 0x08 }, { "jpost", 0x0c },
699 	{ "corpost", 0x0d }, { "jisy6", 0x10 }, { "jisy0", 0x11 },
700 	{ "chinese16k", 0x13 }, { "chinese32k", 0x15 }, { "legal", 0x19 },
701 	{ "glegal", 0x1a }, { "letter", 0x1b }, { "gletter", 0x1d },
702 	{ "executive", 0x1f }, { "halfletter", 0x21 }, { "envmonarch", 0x24 },
703 	{ "env10", 0x25 }, { "envdl", 0x26 }, { "envc5", 0x27 },
704 	{ "envc6", 0x28 }, { "envb5", 0x29 }, { "choukei3gou", 0x2d },
705 	{ "choukei5gou", 0x2e }, { "custom", 0x31 }, { "envb6", 0x31 },
706 	{ "folio", 0x31 }, { "jisy1", 0x31 }, { "jisy2", 0x31 },
707 	{ "quadpost", 0x31 }, { NULL, -1 }
708 };
709 static struct map models[] = {
710 	{ "1200W", 0x81 }, { "1250W", 0x81 },
711 	{ "1300W", 0x83 }, { "1350W", 0x83 },
712 	{ "1400W", 0x86 }, { NULL, -1 }
713 };
714 static struct map resolutions[] = {
715 	{  "300", 0x0000 }, {   "300x300", 0x0000 },
716 	{  "600", 0x0001 }, {   "600x600", 0x0001 },
717 	{ "1200", 0x0002 }, { "1200x1200", 0x0002 },
718 	{ "1200x600", 0x0101 }, { NULL, -1 }
719 };
720 
721 /* this procedure is used by help() below to print available somethings
722  * in a nice and painless way from the maps above
723  * see help() below and everything will become clearer */
printav(char * msg,struct map * m,int defid)724 static void printav(char *msg, struct map *m, int defid)
725 {
726 	int i, n;
727 
728 	n = printf(msg);
729 	/* print all available somethings from our map */
730 	for (i = 0; m[i].str != NULL; i++) {
731 		/* in case we had to start a new line below, we need to
732 		 * print a tab here */
733 		if (n == 0) {
734 			printf("\t");
735 			n = 8;
736 		}
737 		/* print the next something */
738 		n += printf("%s", m[i].str);
739 		/* is it the default? if so, print "*" */
740 		if (m[i].id == defid)
741 			n += printf("*");
742 		/* print ", " unless it's the last something */
743 		if (m[i + 1].str != NULL)
744 			n += printf(", ");
745 		/* check if we have to start a new line */
746 		if (n > 72) {
747 			printf("\n");
748 			n = 0;
749 		}
750 	}
751 	/* unless the last thing we did was to start a new line, we append
752 	 * a newline here */
753 	if (n != 0) printf("\n");
754 }
755 
756 /* this procedure gives help */
help()757 static void help()
758 {
759 	printf("min12xxw - a pbmraw to Minolta PagePro 1[234]xx W "
760 		"filter\n\noptions: (defaults are marked with an"
761 		" asterisk)\n"
762 		"\t-h\t--help\t\t\tthis help\n"
763 		"\t-v\t--version\t\tshow version number\n"
764 		"\t-d dev\t--device dev\t\tset device to use for "
765 			"queries (%s by default)\n"
766 		"\t-s\t--status\t\tquery printer status\n"
767 		"\t-e\t--ecomode\t\teconomic (toner saving) mode\n"
768 		"\t-n\t--nomargins\t\tdisable enforcement of margins\n"
769 		"\t-m mod\t--model mod\t\tset the printer model to "
770 			"produce output for\n"
771 		"\t-r res\t--res res\t\tset resolution\n"
772 		"\t-t tray\t--tray tray\t\tset paper tray\n"
773 		"\t-p type\t--papertype type\tset paper type"
774 		"\n\t-f fmt\t--paperformat fmt\tset paper format\n"
775 		"\n", device);
776 	/* print the available somethings */
777 	printav("available models: ", models, model);
778 	printav("available resolutions: ", resolutions, res);
779 	printav("available paper trays: ", trays, tray);
780 	printav("available paper types: ", ptypes, ptype);
781 	printav("available paper formats: ", pformats, pformat);
782 	printf("\n");
783 	exit(0);
784 }
785 
786 /* this procedure prints out version information */
version()787 static void version()
788 {
789 	printf("min12xxw: %s\n", versionstr);
790 	printf("\nCopyright (C) 2004-2006 Manuel Tobias Schiller\n"
791 		"This is free software; see the source for copying "
792 		"conditions.  There is NO\nwarranty; not even for "
793 		"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
794 	exit(0);
795 }
796 
797 /* convert string to id number */
getid(struct map * m,char * thing,char * str,int deflt)798 static int getid(struct map *m, char *thing, char *str, int deflt)
799 {
800 	while (m->str != NULL) {
801 		if (strcasecmp(str, m->str) == 0) return m->id;
802 		m++;
803 	}
804 	fprintf(stderr, "min12xxw: unknown %s: %s\n", thing, str);
805 	return deflt; /* return the default */
806 }
807 
808 /* parse the options given to the filter */
parseopts(int argc,char ** argv)809 static void parseopts(int argc, char **argv)
810 {
811 	/* this structure is needed by getopt_long */
812 	static struct option opts[] = {
813 		{ "res",		required_argument, 0, 'r' },
814 		{ "papertype",		required_argument, 0, 'p' },
815 		{ "paperformat",	required_argument, 0, 'f' },
816 		{ "tray",		required_argument, 0, 't' },
817 		{ "nomargins",		no_argument,	   0, 'n' },
818 		{ "ecomode",		no_argument,	   0, 'e' },
819 		{ "version",		no_argument,	   0, 'v' },
820 		{ "device",		required_argument, 0, 'd' },
821 		{ "status",		no_argument,	   0, 's' },
822 		{ "model",		required_argument, 0, 'm' },
823 		{ "help",		no_argument,	   0, 'h' },
824 		{ NULL,			0,		   0,   0 }
825 	};
826 	int optidx = 0, dostate = 0;
827 	int c;
828 	int lres = res, lptype = ptype, lpformat = pformat, lmodel = model;
829 	int ltray = tray, lnomargins = nomargins, lecomode = ecomode;
830 	char *ldevice = device;
831 
832 	opterr = 1; /* let getopt_long do the error reporting */
833 	while (42) { /* this line is for all hitch-hikers out there */
834 		c = getopt_long(argc, argv, "hsevnd:r:p:f:t:m:", opts, &optidx);
835 		if (c == -1) break;
836 		switch (c) {
837 		case 'h':
838 			help();
839 		case 'v':
840 			version();
841 		case 'r':
842 			lres = getid(resolutions, "resolution", optarg, 0x0001);
843 			break;
844 		case 'p':
845 			lptype = getid(ptypes, "paper type", optarg, 0);
846 			break;
847 		case 'f':
848 			lpformat = getid(pformats, "paper format", optarg, 4);
849 			break;
850 		case 'm':
851 			lmodel = getid(models, "printer model", optarg, 0x81);
852 			break;
853 		case 't':
854 			ltray = getid(trays, "tray", optarg, 0xff);
855 			break;
856 		case 'n':
857 			lnomargins = 1;
858 			break;
859 		case 'd':
860 			ldevice = optarg;
861 			break;
862 		case 's':
863 			dostate = 1;
864 			break;
865 		case 'e':
866 			lecomode = 1;
867 			break;
868 		default:
869 			/* getopt_long told the user about the unknown option
870 			 * character, all we have to do now is bail out... */
871 			exit(1);
872 		}
873 	}
874 	/* copy the local copies of options to the global ones
875 	 * we need to do this copying to make sure that help sees the
876 	 * global defaults, not what the users sets with options */
877 	res = lres; ptype = lptype; pformat = lpformat; model = lmodel;
878 	tray = ltray; nomargins = lnomargins; device = ldevice;
879 	ecomode = lecomode;
880 	if (dostate) {
881 		/* process printer queries right away and exit */
882 		FILE *d;
883 		if ((d = fopen(device, "r+b")) == NULL)
884 			fatal("min12xxw: couldn't fdopen printer");
885 		printhwstate(d);
886 		if (fclose(d) != 0) /* close the printer */
887 			fatal("min12xxw: couldn't close printer");
888 		exit(0); /* exit */
889 	}
890 }
891 
892 /* adjust the model defaults to the name of the executable
893  * do some magic for the users to spare them from having to type model
894  * selection options again and again and again */
modeladj(char * str)895 static void modeladj(char *str)
896 {
897 	int len = strlen(str);
898 
899 	if (len < 8) return; /* don't do anything on short strings */
900 	/* we are only interested in the end of the string */
901 	str = &str[len - 8];
902 	/* if it is a model name, set the defaults accordingly */
903 	if (strcmp(str, "min1200w") == 0) model = 0x81;
904 	if (strcmp(str, "min1250w") == 0) model = 0x81;
905 	if (strcmp(str, "min1300w") == 0) model = 0x83;
906 	if (strcmp(str, "min1350w") == 0) model = 0x83;
907 	if (strcmp(str, "min1400w") == 0) model = 0x86;
908 }
909 
910 /**********************************************************************
911  * main program
912  **********************************************************************/
main(int argc,char ** argv)913 int main(int argc, char **argv)
914 {
915 	FILE *tmp;
916 	size_t i;
917 	char *buf;
918 	int fd;
919 	struct stat st;
920 
921 	/* adjust the default model based on the name of the executable */
922 	modeladj(argv[0]);
923 	/* this is really boring now... */
924 	parseopts(argc, argv); /* parse any options given */
925 	/* if we write to a file or a pipe, we won't need a temp file so
926 	 * we'll use fstat to check for this */
927 	fd = fileno(stdout);
928 	if (fstat(fd, &st) != 0)
929 		fatal("min12xxw: couldn't examine stdout via fstat");
930 	if (S_ISREG(st.st_mode) || S_ISFIFO(st.st_mode)) {
931 		tmp = stdout;
932 	} else {
933 		/* create temp file to hold the output until we're done */
934 		if ((tmp = tmpfile()) == NULL)
935 			fatal("min12xxw: couldn't create temporary file");
936 	}
937 
938 	/* filter our input */
939 	dojob(stdin, tmp);
940 
941 	if (stdout != tmp) {
942 		/* if we used a temp file we need to copy output from temp
943 		 * file to stdout */
944 		rewind(tmp);
945 		if ((buf = (char*) malloc(16384 * sizeof(char))) == NULL)
946 			fatal("min12xxw: couldn't allocate memory");
947 		while ((!feof(tmp)) && (!ferror(tmp))) {
948 			i = fread(buf, sizeof(char), 16384, tmp);
949 			if (i <= 0) continue;
950 			if (fwrite(buf, 1, i, stdout) != i)
951 				fatal("min12xxw: couldn't write to stdout");
952 		}
953 		free(buf);
954 	}
955 
956 	/* all went well :) */
957 	return 0;
958 }
959