1 /******************************************************************************
2   File:     $Id: pclscan.c,v 1.8 2000-10-22 11:05:34+02 Martin Rel $
3   Contents: PCL scanner
4   Author:   Martin Lottermoser, Greifswaldstrasse 28, 38124 Braunschweig,
5             Germany, e-mail: Martin.Lottermoser@t-online.de
6 
7 *******************************************************************************
8 *									      *
9 *	Copyright (C) 1999, 2000 by Martin Lottermoser			      *
10 *	All rights reserved						      *
11 *									      *
12 ******************************************************************************/
13 
14 /* Configuration management identification */
15 #ifndef lint
16 static const char
17   cm_id[] = "@(#)$Id: pclscan.c,v 1.8 2000-10-22 11:05:34+02 Martin Rel $";
18 #endif
19 
20 /*****************************************************************************/
21 
22 #ifndef _XOPEN_SOURCE
23 #define _XOPEN_SOURCE	500
24 #endif
25 #ifndef _XOPEN_SOURCE_EXTENDED
26 #define _XOPEN_SOURCE_EXTENDED	1
27 #endif
28 
29 /* Standard headers */
30 #include <errno.h>
31 #include <nl_types.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 
36 /* Special headers */
37 #include "pclscan.h"
38 
39 /*****************************************************************************/
40 
41 static nl_catd catd = (nl_catd)-1;	/* NLS catalogue descriptor */
42 
43 
message(int id,const char * s)44 static void message(int id, const char *s)
45 {
46   if (catd != (nl_catd)-1) s = catgets(catd, 1, id, s);
47    /* The check might not be necessary (X/Open requires catgets() to return 's'
48       if the call is unsuccessful for *any* reason and the possible errors
49       include EBADF for an invalid 'catd'), but one never knows. */
50 
51   fputs("? pclscan: ", stderr);
52   fprintf(stderr, s);
53   fputc('\n', stderr);
54 
55   return;
56 }
57 
58 /*****************************************************************************/
59 
60 static const pcl_Octet ESC = '\x1B';
61 
62 /******************************************************************************
63 
64   Function: pcl_is_control_code
65 
66   This function returns a non-zero value iff 'c', taken to be an unsigned char
67   converted to 'int', is a PCL control code.
68 
69 ******************************************************************************/
70 
pcl_is_control_code(int c)71 int pcl_is_control_code(int c)
72 {
73   return
74     c <= ' ' && (
75       c == '\0' ||
76       /* TRG500 does not mention NUL as a control code. */
77       c == '\b' || c == '\t' || c == '\n' || c == '\f' || c == '\r' ||
78       c == '\x0E' /* Shift Out (SO) */ ||
79       c == '\x0F' /* Shift In (SI) */ ||
80       c == '\x11' /* Device Control 1 (DC1) */ ||
81       c == '\x13' /* Device Control 3 (DC3) */ ||
82       c == ESC ||
83       c == ' ');
84 }
85 
86 /*****************************************************************************/
87 
cmp_strings(const void * a,const void * b)88 static int cmp_strings(const void *a, const void *b)
89 {
90   return strncmp((const char *)a, (const char *)b, 3);
91 }
92 
93 
default_interpreter(FILE * in,const pcl_Command * cmd)94 static int default_interpreter(FILE *in, const pcl_Command *cmd)
95 {
96   /* Skip over arguments for those commands which are known to have them */
97   if (cmd->kind >= 3) {
98     if (strncmp((const char *)cmd->chars, "%-X", 3) == 0 && cmd->i == 12345) {
99       /* Universal Exit Language (UEL)/Start of PJL */
100       int c;
101 
102       do {
103 	c = fgetc(in);
104 	if (c != '@') break;
105 	do c = fgetc(in); while (c != EOF && c != '\n');
106       } while (c != EOF);
107       if (c != EOF) ungetc(c, in);
108     }
109     else if (cmd->i > 0) {
110       static const char with_args[][4] = {
111 	/* Must be sorted with respect to cmp_strings() */
112 	"&bW",
113 	"&iW",
114 	"&pX",	/* Transparent Print Mode */
115 	"(sW",	/* Download Character */
116 	")sW",	/* Create Font */
117 	"*bV",	/* Transfer Raster Graphics Data by Plane */
118 	"*bW",	/* Transfer Raster Graphics Data by Row */
119 	"*dW",	/* Palette Configuration */
120 	"*gW",	/* Configure Raster Data */
121 	"*oW"};
122 
123       if (bsearch(cmd->chars, with_args,
124 	  sizeof(with_args)/sizeof(with_args[0]), sizeof(with_args[0]),
125 	  &cmp_strings) != NULL) {
126 	int j;
127 
128 	j = cmd->i;
129 	while (j > 0 && fgetc(in) != EOF) j--;
130 	if (j > 0) {
131 	  message(1, "Premature EOF on input.");
132 	  return -1;
133 	}
134       }
135     }
136   }
137   else if (cmd->kind == 2 && cmd->chars[0] == 'Y') {
138     /* Display Functions Mode ON */
139     int c;
140 
141     /* Read until EOF or Display Functions Mode OFF */
142     do {
143       do c = fgetc(in); while (c != EOF && c != ESC);
144       if (c != EOF) c = fgetc(in);
145     } while (c != EOF && c != 'Z');
146 
147     if (c != 'Z') {
148       message(1, "Premature EOF on input.");
149       return -1;
150     }
151   }
152 
153   return 0;
154 }
155 
156 /*****************************************************************************/
157 
default_handler(FILE * in)158 static int default_handler(FILE *in)
159 {
160   int c;
161 
162   do {
163     c = fgetc(in);
164   } while (c != EOF && !pcl_is_control_code(c));
165   if (c != EOF) ungetc(c, in);
166 
167   return 0;
168 }
169 
170 /******************************************************************************
171 
172   Function: pcl_scan
173 
174   This function scans a PCL file, separating it into printer commands and
175   unknown data, and calling the functions '(*interpreter)()' and '(*handler)()'
176   with these parts, respectively.
177 
178   'in' must have been opened as a binary file and for reading.
179   'interpreter' or 'handler' may be null in which case default routines are
180   used.
181   If a non-NULL function pointer is provided for 'interpreter' or 'handler',
182   the function will be passed the value of 'idata' or 'hdata', respectively.
183 
184   This function returns zero on success and a negative value otherwise.
185   If one of the interpreter or data handler functions returns a negative value,
186   processing will stop and the value will be returned as the return value of
187   pcl_scan().
188 
189 ******************************************************************************/
190 
pcl_scan(FILE * in,pcl_CommandInterpreter interpreter,void * idata,pcl_UnknownDataHandler handler,void * hdata)191 int pcl_scan(FILE *in, pcl_CommandInterpreter interpreter, void *idata,
192   pcl_UnknownDataHandler handler, void *hdata)
193 {
194   int
195     c,
196     rc = 0;
197 
198   /* Open catalogue descriptor */
199   catd = catopen("pcltools", 0);
200   if (catd == (nl_catd)(-1) && errno != ENOENT)
201      /* A system returning ENOENT if no catalogue is available is for example
202 	Sun Solaris 2.5. */
203     fprintf(stderr,
204       "?-W pclscan: Error trying to open message catalogue: %s.\n",
205       strerror(errno));
206 
207   /* Loop over input */
208   while (rc >= 0 && (c = fgetc(in)) != EOF) {
209     if (c == ESC) {
210       pcl_Command command;
211 
212       if ((c = fgetc(in)) == EOF) {
213 	rc = -1;
214 	message(1, "Premature EOF on input.");
215 	break;
216       }
217       command.chars[0] = c;
218       if (48 <= c && c <= 126) {
219 	/* Two-character escape sequence */
220 	command.kind = 2;
221 	if (interpreter == NULL ||
222 	    (rc = (*interpreter)(in, &command, idata)) > 0)
223 	  rc = default_interpreter(in, &command);
224       }
225       else {
226 	int continued;
227 
228 	/* Parameterized escape sequence or garbage. The character we've just
229 	   read is the "parameterized character" and it should be in the range
230 	   33-47. */
231 
232 	/* Now the group character (should be in the range 96-126, but HP uses
233 	   sometimes at least '-', which has the value 45) */
234 	if ((c = fgetc(in)) == EOF) {
235 	  rc = -1;
236 	  message(1, "Premature EOF on input.");
237 	  break;
238 	}
239 	command.chars[1] = c;
240 
241 	continued = 0;
242 	do {
243 	  /* Now for the value */
244 	  if ((c = fgetc(in)) == EOF) {
245 	    rc = -1;
246 	    message(1, "Premature EOF on input.");
247 	    break;
248 	  }
249 	  if (c == '+' || c == '-' || '0' <= c && c <= '9') {
250 	    if (c == '+' || c == '-') command.prefix = c;
251 	    else command.prefix = ' ';
252 	    ungetc(c, in);
253 	    if (fscanf(in, "%d", &command.i) != 1) {
254 	      rc = -1;
255 	      message(2, "Syntax error in value field.");
256 	      break;
257 	    }
258 
259 	    /* Decimal point? */
260 	    if ((c = fgetc(in)) == EOF) {
261 	      rc = -1;
262 	      message(1, "Premature EOF on input.");
263 	      break;
264 	    }
265 	    command.scale = 0;
266 	    command.fraction = 0;
267 	    if (c == '.') {
268 	      command.scale = 1;
269 	      while ((c = fgetc(in)) != EOF && '0' <= c && c <= '9') {
270 		command.fraction = command.fraction*10 + (c - '0');
271 		command.scale *= 10;
272 	      }
273 	      if (c == EOF) {
274 		rc = -1;
275 		message(1, "Premature EOF on input.");
276 		break;
277 	      }
278 	      if (command.prefix == '-') command.fraction = -command.fraction;
279 	    }
280 	  }
281 	  else {
282 	    command.prefix = '\0'; /* no value given */
283 	    command.i = 0;
284 	  }
285 
286 	  /* Final character */
287 	  if (96 <= c && c <= 126) {
288 	    /* Parameter character */
289 	    command.chars[2] = c - ('a' - 'A');
290 	    command.kind = (continued? 6: 4);
291 	    continued = 1;
292 	  }
293 	  else {
294 	    /* Termination character (should be in the range 64-94) */
295 	    command.chars[2] = c;
296 	    command.kind = (continued? 5: 3);
297 	    continued = 0;
298 	  }
299 
300 	  if (interpreter == NULL ||
301 	      (rc = (*interpreter)(in, &command, idata)) > 0)
302 	    rc = default_interpreter(in, &command);
303 	} while (rc == 0 && continued);
304       }
305     }
306     else if (pcl_is_control_code(c)) {
307       pcl_Command command;
308 
309       command.chars[0] = c;
310       command.kind = 1;
311       command.i = 1;	/* number of occurrences */
312 
313       while ((c = fgetc(in)) != EOF && c == command.chars[0]) command.i++;
314       if (c != EOF) ungetc(c, in);
315 
316       if (interpreter == NULL ||
317 	  (rc = (*interpreter)(in, &command, idata)) > 0)
318 	rc = default_interpreter(in, &command);
319     }
320     else {
321       ungetc(c, in);
322       if (handler == NULL || (rc = (*handler)(in, hdata)) > 0)
323 	rc = default_handler(in);
324     }
325   }
326 
327   /* Close catalogue descriptor */
328   if (catd != (nl_catd)-1) catclose(catd);
329 
330   return rc;
331 }
332