1 /*
2  * psstat.c
3  *
4  * Recording information about the PostScript process
5  * Copyright (c) 1988, 89, 90, 91, 92, 93 Miguel Santana
6  * Copyright (c) 1995, 96, 97, 98 Akim Demaille, Miguel Santana
7  */
8 
9 /*
10  * This file is part of a2ps.
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2, or (at your option)
15  * any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; see the file COPYING.  If not, write to
24  * the Free Software Foundation, 59 Temple Place - Suite 330,
25  * Boston, MA 02111-1307, USA.
26  */
27 
28 /*
29  * $Id: psstat.c,v 1.37 1998/02/24 15:23:39 demaille Exp $
30  */
31 #include "a2ps.h"
32 #include "psstat.h"
33 #include "jobs.h"
34 #include "routines.h"
35 #include "dsc.h"
36 
37 /************************************************************************
38  * Monovalued hash tables						*
39  ************************************************************************/
40 /* Definition of the hash'd structure used for page device definitions */
41 typedef struct dict_entry
42 {
43   char * key;
44   char * value;
45   int def;
46 } dict_entry;
47 
48 /*
49  * Used with the page device(-D), and status dict (-S) definitions
50  */
51 static unsigned long
key_hash_1(void const * key)52 key_hash_1 (void const *key)
53 {
54   return_STRING_HASH_1 (((const dict_entry *)key)->key);
55 }
56 
57 static unsigned long
key_hash_2(void const * key)58 key_hash_2 (void const *key)
59 {
60   return_STRING_HASH_2 (((const dict_entry *)key)->key);
61 }
62 
63 static int
key_hash_cmp(void const * x,void const * y)64 key_hash_cmp (void const *x, void const *y)
65 {
66   return_STRING_COMPARE (((const dict_entry *)x)->key,
67 			 ((const dict_entry *)y)->key);
68 }
69 
70 static struct hash_table_s *
new_dict_entry_table(void)71 new_dict_entry_table (void)
72 {
73   static struct hash_table_s * res;
74 
75   res = XMALLOC (hash_table, 1);
76   hash_init (res, 8,
77 	     key_hash_1, key_hash_2, key_hash_cmp);
78   return res;
79 }
80 
81 static void
dict_entry_add(struct hash_table_s * table,const char * key,const char * value,int def)82 dict_entry_add (struct hash_table_s * table,
83 		const char * key, const char * value, int def)
84 {
85   NEW (dict_entry, item);
86   item->key = xstrdup (key);
87   item->value = xstrdup (value);
88   item->def = def;
89   hash_insert (table, item);
90 }
91 
92 static dict_entry *
dict_entry_get(struct hash_table_s * table,const char * key)93 dict_entry_get (struct hash_table_s * table, const char * key)
94 {
95   struct dict_entry token;
96   token.key = (char *) key;
97   return (struct dict_entry *) hash_find_item (table, &token);
98 }
99 
100 static void
free_dict_entry(struct dict_entry * entry)101 free_dict_entry (struct dict_entry * entry)
102 {
103   free (entry->key);
104   free (entry->value);
105   free (entry);
106 }
107 
108 static void
dict_entry_remove(struct hash_table_s * table,const char * key)109 dict_entry_remove (struct hash_table_s * table, const char * key)
110 {
111   struct dict_entry * item;
112   item = dict_entry_get (table, key);
113   if (item) {
114     hash_delete (table, item);
115     free_dict_entry (item);
116   }
117 }
118 
119 static void
free_dict_entry_table(struct hash_table_s * table)120 free_dict_entry_table (struct hash_table_s * table)
121 {
122   hash_free (table, (hash_map_func_t) free_dict_entry);
123   free (table);
124 }
125 
126 
127 /************************************************************************/
128 /*	Dealing with the structure (creation etc.) that will be kept	*/
129 /*	in JOB								*/
130 /************************************************************************/
131 /*
132  * Private information for the PS generation engine
133  */
134 struct ps_status *
new_ps_status(void)135 new_ps_status (void)
136 {
137   struct ps_status * res = XMALLOC (struct ps_status, 1);
138 
139   /* The very first line of a PS file */
140   res->magic_number = xustrdup ("%!PS-Adobe-3.0");
141 
142   /* By default, the list of page number intervals */
143   res->page_label_format = xustrdup ("#!s|$p-|, |");
144 
145   /* At the beginning, no encoding dict is opened */
146   res->opened_encoding = NULL;
147 
148   /* After delegating the order is no longer respected */
149   res->page_are_ordered = true;
150 
151   /* for fonts etc. */
152   res->needed_resources = multivalued_table_new ();
153 
154   /* Things to put in the preamble */
155   res->supplied_resources = multivalued_table_new ();
156 
157   /* for setpagedevice */
158   res->pagedevice = new_dict_entry_table ();
159 
160   /* PS statusdict definitions */
161   res->statusdict = new_dict_entry_table ();
162 
163   /* The setups read in the files */
164   res->setup = output_new ("setup");
165 
166   return res;
167 }
168 
169 void
ps_status_free(struct ps_status * status)170 ps_status_free (struct ps_status * status)
171 {
172   free (status->magic_number);
173   free (status->page_label_format);
174 
175   multivalued_table_free (status->needed_resources);
176   multivalued_table_free (status->supplied_resources);
177   free_dict_entry_table (status->pagedevice);
178   free_dict_entry_table (status->statusdict);
179   output_free (status->setup);
180 
181   free (status);
182 }
183 
184 /*
185  * Called for each new input session.
186  */
187 void
initialize_ps_status(struct ps_status * status)188 initialize_ps_status (struct ps_status * status)
189 {
190   /* This one will be malloc'd for each (physical) page.
191    * Hence, do not risk to touch it, unless you want to SEGV */
192 /*  status->page_label = NULL;*/
193 
194   /* Reinitialize the ps status */
195   status->start_page = true;
196   status->start_line = true;
197   status->line_continued = false;
198   status->is_in_cut = false;
199   status->face = Plain;
200   status->face_declared = false;	/* One could think in using
201 					 * an extra value in ->face
202 					 * (eg No_face), but it gets
203 					 * painful with switches of
204 					 * encodings... */
205   status->nonprinting_chars = 0;
206   status->chars = 0;
207   status->line = 0;
208   status->column = 0;
209   status->wx = 0;
210 }
211 
212 /************************************************************************/
213 /*	Dealing with the various components of this structure		*/
214 /************************************************************************/
215 /*
216  * Used with the page device definitions (-D)
217  */
218 static void
dict_entry_print(void const * item,FILE * stream)219 dict_entry_print (void const * item, FILE * stream)
220 {
221   const dict_entry * tok = (const dict_entry *) item;
222   if (tok->def)
223     fprintf (stream, "%s::%s ", tok->key, tok->value);
224   else
225     fprintf (stream, "%s:%s ", tok->key, tok->value);
226 }
227 
228 /*
229  * Listing the content of a dict entry hash table
230  */
231 static void
dict_entry_table_dump(struct hash_table_s * table,FILE * stream)232 dict_entry_table_dump (struct hash_table_s * table, FILE * stream)
233 {
234   int i;
235   struct dict_entry ** items;
236 
237   items = (struct dict_entry **) hash_dump (table, NULL, NULL);
238 
239   for (i = 0 ; items [i] ; i++)
240     dict_entry_print (items [i], stream);
241   putc ('\n', stream);
242 
243   free (items);
244 }
245 
246 /* Page device definitions */
247 void
output_pagedevice(a2ps_job * job)248 output_pagedevice (a2ps_job * job)
249 {
250   dict_entry ** entries = NULL;
251   dict_entry ** entry;
252   entries = (dict_entry **) hash_dump (job->status->pagedevice, NULL, NULL);
253 
254   if (!*entries)
255     return;
256 
257   /* Protect calls to setpagedevice through a stopped environment.
258    * (Cf. PDF spec, by Adobe) */
259   output (job->divertion, "\
260 %%%% Pagedevice definitions:\n\
261 countdictstack\n\
262 %% Push our own mark, since there can be several PS marks pushed depending\n\
263 %% where the failure really occured.\n\
264 /a2ps_mark\n\
265 {\n");
266 
267   /* Each Pagedevice */
268   for (entry = entries ; *entry ; entry++)
269     output (job->divertion, "\
270 %%%%BeginFeature: *%s %c%s\n\
271   (<<) cvx exec /%s (%s) cvx exec (>>) cvx exec setpagedevice\n\
272 %%%%EndFeature\n",
273       (*entry)->key, toupper ((*entry)->value[0]),
274       (*entry)->value + 1,
275       (*entry)->key, (*entry)->value);
276 
277   /* Close the stopped env, and clear the stack */
278   output (job->divertion, "\
279 } stopped\n\
280 %% My cleartomark\n\
281 { /a2ps_mark eq { exit } if } loop\n\
282 countdictstack exch sub dup 0 gt\n\
283 {\n\
284   { end } repeat\n\
285 }{\n\
286   pop\n\
287 } ifelse\n");
288 
289   free (entries);
290 }
291 
292 /* FIXME: Find some better scheme.  But I don't want to do that before
293    4.11.  This is the same routine as above, but which fputs instead of
294    output.
295 
296    This routine will only be call when only a single delegated job
297    is output, therefore a2ps' prologue will not be output, therefore
298    there is a very high chance (contrary to the previous item) that
299    the psutils have neutralized the setpagedevice operator.  Here
300    we really want to use it, hence the `systemdict /setpagedevice get exec'
301    sequence instead of just `setpagdevice'.
302 */
303 
304 void
pagedevice_dump(FILE * stream,a2ps_job * job)305 pagedevice_dump (FILE *stream, a2ps_job * job)
306 {
307   dict_entry ** entries = NULL;
308   dict_entry ** entry;
309   entries = (dict_entry **) hash_dump (job->status->pagedevice, NULL, NULL);
310 
311   if (!*entries)
312     return;
313 
314   /* Protect calls to setpagedevice through a stopped environment.
315    * (Cf. PDF spec, by Adobe) */
316   fputs ("\
317 %% Pagedevice definitions:\n\
318 countdictstack\n\
319 % Push our own mark, since there can be several PS marks pushed depending\n\
320 % where the failure really occured.\n\
321 /a2ps_mark\n\
322 {\n", stream);
323 
324   /* Each Pagedevice */
325   for (entry = entries ; *entry ; entry++)
326     fprintf (stream, "\
327 %%%%BeginFeature: *%s %c%s\n\
328   (<<) cvx exec /%s (%s) cvx exec (>>) cvx exec\n\
329   systemdict /setpagedevice get exec\n\
330 %%%%EndFeature\n",
331       (*entry)->key, toupper ((*entry)->value[0]),
332       (*entry)->value + 1,
333       (*entry)->key, (*entry)->value);
334 
335   /* Close the stopped env, and clear the stack */
336   fputs ("\
337 } stopped\n\
338 % My cleartomark\n\
339 { /a2ps_mark eq { exit } if } loop\n\
340 countdictstack exch sub dup 0 gt\n\
341 {\n\
342   { end } repeat\n\
343 }{\n\
344   pop\n\
345 } ifelse\n", stream);
346 
347   free (entries);
348 }
349 
350 void
dump_requirements(FILE * stream,struct a2ps_job * job)351 dump_requirements (FILE * stream, struct a2ps_job * job)
352 {
353   dict_entry ** entries = NULL;
354   dict_entry ** entry;
355   entries = (dict_entry **) hash_dump (job->status->pagedevice, NULL, NULL);
356   /* Dump only if there is something to say */
357   if (*entries)
358     {
359       fputs ("%%Requirements: ", stream);
360       for (entry = entries ; *entry ; entry++)
361 	fprintf (stream, "%s ", (*entry)->key);
362       putc ('\n', stream);
363     }
364   /* We don't want this one which breaks some collating systems
365      output (job->divertion, "numcopies(%d)", job->copies);
366      */
367   free (entries);
368 }
369 
370 void
setpagedevice(a2ps_job * job,const char * key,const char * value)371 setpagedevice (a2ps_job * job, const char * key, const char * value)
372 {
373   dict_entry_add (job->status->pagedevice, key, value, false);
374 }
375 
376 void
delpagedevice(a2ps_job * job,const char * key)377 delpagedevice (a2ps_job * job, const char * key)
378 {
379   dict_entry_remove (job->status->pagedevice, key);
380 }
381 
382 /*
383  * For --list-options
384  */
385 void
list_pagedevice(a2ps_job * job,FILE * stream)386 list_pagedevice (a2ps_job * job, FILE * stream)
387 {
388   dict_entry_table_dump (job->status->pagedevice, stream);
389 }
390 
391 /*
392  * Used with the status dict definitions (-S)
393  */
394 void
setstatusdict(a2ps_job * job,const char * key,const char * value,int def)395 setstatusdict (a2ps_job * job, const char * key, const char * value, int def)
396 {
397   dict_entry_add (job->status->statusdict, key, value, def);
398 }
399 
400 void
delstatusdict(a2ps_job * job,const char * key)401 delstatusdict (a2ps_job * job, const char * key)
402 {
403   dict_entry_remove (job->status->statusdict, key);
404 }
405 
406 void
output_statusdict(a2ps_job * job)407 output_statusdict (a2ps_job * job)
408 {
409   dict_entry ** entries = NULL;
410   dict_entry ** entry;
411 
412   entries = (dict_entry **) hash_dump (job->status->statusdict, NULL, NULL);
413   if (*entries) {
414     output (job->divertion, "%% Statustdict definitions:\n");
415     output (job->divertion, "statusdict begin\n");
416     for ( entry = entries ; *entry ; entry++)
417       if ((*entry)->def)
418 	output (job->divertion, "  /%s %s def\n",
419 		(*entry)->key, (*entry)->value);
420       else
421 	output (job->divertion, "  %s %s\n",
422 		(*entry)->value, (*entry)->key);
423     output (job->divertion, "end\n");
424   }
425   free (entries);
426 }
427 
428 /*
429  * For --list-options
430  */
431 void
list_statusdict(a2ps_job * job,FILE * stream)432 list_statusdict (a2ps_job * job, FILE * stream)
433 {
434   dict_entry_table_dump (job->status->statusdict, stream);
435 }
436