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