3
* Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com>
4
* Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de>
6
* This file is part of foomatic-rip.
8
* Foomatic-rip 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.
13
* Foomatic-rip is distributed in the hope that it will be useful, but
14
* WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
* General Public License for more details.
18
* You should have received a copy of the GNU Lesser General Public
19
* License along with this library; if not, write to the
20
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21
* Boston, MA 02111-1307, USA.
25
#include "foomaticrip.h"
31
const char *spooler_name(int spooler)
34
case SPOOLER_CUPS: return "cups";
35
case SPOOLER_DIRECT: return "direct";
40
/* This piece of PostScript code (initial idea 2001 by Michael
41
Allerhand (michael.allerhand at ed dot ac dot uk, vastly
42
improved by Till Kamppeter in 2002) lets Ghostscript output
43
the page accounting information which CUPS needs on standard
45
Redesign by Helge Blischke (2004-11-17):
46
- As the PostScript job itself may define BeginPage and/or EndPage
47
procedures, or the alternate pstops filter may have inserted
48
such procedures, we make sure that the accounting routine
49
will safely coexist with those. To achieve this, we force
50
- the accountint stuff to be inserted at the very end of the
51
PostScript job's setup section,
52
- the accounting stuff just using the return value of the
53
existing EndPage procedure, if any (and providing a default one
55
- As PostScript jobs may contain calls to setpagedevice "between"
56
pages, e.g. to change media type, do in-job stapling, etc.,
57
we cannot rely on the "showpage count since last pagedevice
58
activation" but instead count the physical pages by ourselves
59
(in a global dictionary).
61
const char *accounting_prolog_code =
63
"%% Code for writing CUPS accounting tags on standard error\n"
65
"/cupsPSLevel2 % Determine whether we can do PostScript level 2 or newer\n"
66
" systemdict/languagelevel 2 copy\n"
67
" known{get exec}{pop pop 1}ifelse 2 ge\n"
71
"{ % in case of level 2 or higher\n"
72
" currentglobal true setglobal % define a dictioary foomaticDict\n"
73
" globaldict begin % in global VM and establish a\n"
74
" /foomaticDict % pages count key there\n"
82
"/cupsGetNumCopies { % Read the number of Copies requested for the current\n"
86
" % PS Level 2+: Get number of copies from Page Device dictionary\n"
87
" currentpagedevice /NumCopies get\n"
90
" % PS Level 1: Number of copies not in Page Device dictionary\n"
94
" % Check whether the number is defined, if it is \"null\" use #copies \n"
100
" % Check whether the number is defined now, if it is still \"null\" use 1\n"
107
"/cupsWrite { % write a string onto standard error\n"
108
" (%stderr) (w) file\n"
109
" exch writestring\n"
112
"/cupsFlush % flush standard error to make it sort of unbuffered\n"
114
" (%stderr)(w)file flushfile\n"
118
"{ % In language level 2, we try to do something reasonable\n"
121
" [ % start the array that becomes the procedure\n"
122
" currentpagedevice/EndPage 2 copy known\n"
123
" {get} % get the existing EndPage procedure\n"
124
" {pop pop {exch pop 2 ne}bind}ifelse % there is none, define the default\n"
125
" /exec load % make sure it will be executed, whatever it is\n"
126
" /dup load % duplicate the result value\n"
127
" { % true: a sheet gets printed, do accounting\n"
128
" currentglobal true setglobal % switch to global VM ...\n"
129
" foomaticDict begin % ... and access our special dictionary\n"
130
" PhysPages 1 add % count the sheets printed (including this one)\n"
131
" dup /PhysPages exch def % and save the value\n"
132
" end % leave our dict\n"
133
" exch setglobal % return to previous VM\n"
134
" (PAGE: )cupsWrite % assemble and print the accounting string ...\n"
135
" 16 string cvs cupsWrite % ... the sheet count ...\n"
136
" ( )cupsWrite % ... a space ...\n"
137
" cupsGetNumCopies % ... the number of copies ...\n"
138
" 16 string cvs cupsWrite % ...\n"
139
" (\\n)cupsWrite % ... a newline\n"
142
" % false: current page gets discarded; do nothing \n"
143
" ]cvx bind % make the array executable and apply bind\n"
147
" % In language level 1, we do no accounting currently, as there is no global VM\n"
148
" % the contents of which are undesturbed by save and restore. \n"
149
" % If we may be sure that showpage never gets called inside a page related save / restore pair\n"
150
" % we might implement an hack with showpage similar to the one above.\n"
153
"} stopped cleartomark\n";
156
void init_cups(list_t *arglist, dstr_t *filelist, jobparams_t *job)
158
char path [1024] = "";
159
char cups_jobid [128];
160
char cups_user [128];
161
char cups_jobtitle [128];
162
char cups_copies [128];
163
int cups_options_len;
165
char cups_filename [256];
167
if (getenv("CUPS_FONTPATH"))
168
strcpy(path, getenv("CUPS_FONTPATH"));
169
else if (getenv("CUPS_DATADIR")) {
170
strcpy(path, getenv("CUPS_DATADIR"));
171
strcat(path, "/fonts");
173
if (getenv("GS_LIB")) {
175
strcat(path, getenv("GS_LIB"));
177
setenv("GS_LIB", path, 1);
179
/* Get all command line parameters */
180
strncpy_omit(cups_jobid, arglist_get(arglist, 0), 128, omit_shellescapes);
181
strncpy_omit(cups_user, arglist_get(arglist, 1), 128, omit_shellescapes);
182
strncpy_omit(cups_jobtitle, arglist_get(arglist, 2), 128, omit_shellescapes);
183
strncpy_omit(cups_copies, arglist_get(arglist, 3), 128, omit_shellescapes);
185
cups_options_len = strlen(arglist_get(arglist, 4));
186
cups_options = malloc(cups_options_len + 1);
187
strncpy_omit(cups_options, arglist_get(arglist, 4), cups_options_len + 1, omit_shellescapes);
189
/* Common job parameters */
190
strcpy(job->id, cups_jobid);
191
strcpy(job->title, cups_jobtitle);
192
strcpy(job->user, cups_user);
193
strcpy(job->copies, cups_copies);
194
dstrcatf(job->optstr, " %s", cups_options);
196
/* Check for and handle inputfile vs stdin */
197
if (list_item_count(arglist) > 4) {
198
strncpy_omit(cups_filename, arglist_get(arglist, 5), 256, omit_shellescapes);
199
if (cups_filename[0] != '-') {
200
/* We get input from a file */
201
dstrcatf(filelist, "%s ", cups_filename);
202
_log("Getting input from file %s\n", cups_filename);
206
accounting_prolog = accounting_prolog_code;
208
/* On which queue are we printing?
209
CUPS gives the PPD file the same name as the printer queue,
210
so we can get the queue name from the name of the PPD file. */
211
file_basename(job->printer, job->ppdfile, 256);
216
/* used by init_direct to find a ppd file */
217
int find_ppdfile(const char *user_default_path, jobparams_t *job)
219
/* Search also common spooler-specific locations, this way a printer
220
configured under a certain spooler can also be used without spooler */
222
strcpy(job->ppdfile, job->printer);
223
if (access(job->ppdfile, R_OK) == 0)
226
snprintf(job->ppdfile, 256, "%s.ppd", job->printer); /* current dir */
227
if (access(job->ppdfile, R_OK) == 0)
229
snprintf(job->ppdfile, 256, "%s/%s.ppd", user_default_path, job->printer); /* user dir */
230
if (access(job->ppdfile, R_OK) == 0)
232
snprintf(job->ppdfile, 256, "%s/direct/%s.ppd", CONFIG_PATH, job->printer); /* system dir */
233
if (access(job->ppdfile, R_OK) == 0)
235
snprintf(job->ppdfile, 256, "%s/%s.ppd", CONFIG_PATH, job->printer); /* system dir */
236
if (access(job->ppdfile, R_OK) == 0)
238
snprintf(job->ppdfile, 256, "/etc/cups/ppd/%s.ppd", job->printer); /* CUPS config dir */
239
if (access(job->ppdfile, R_OK) == 0)
241
snprintf(job->ppdfile, 256, "/usr/local/etc/cups/ppd/%s.ppd", job->printer); /* CUPS config dir */
242
if (access(job->ppdfile, R_OK) == 0)
246
job->ppdfile[0] = '\0';
250
/* search 'configfile' for 'key', copy value into dest, return success */
251
int configfile_find_option(const char *configfile, const char *key, char *dest, size_t destsize)
259
if (!(fh = fopen(configfile, "r")))
262
while (fgets(line, 1024, fh)) {
263
if (!prefixcmp(line, "default")) {
264
p = strchr(line, ':');
266
strncpy_omit(dest, p + 1, destsize, omit_whitespace_newline);
273
return dest[0] != '\0';
276
/* tries to find a default printer name in various config files and copies the
277
* result into the global var 'printer'. Returns success */
278
int find_default_printer(const char *user_default_path, jobparams_t *job)
280
char configfile [1024];
281
char *key = "default";
283
if (configfile_find_option("./.directconfig", key, job->printer, 256))
285
if (configfile_find_option("./directconfig", key, job->printer, 256))
287
if (configfile_find_option("./.config", key, job->printer, 256))
289
strlcpy(configfile, user_default_path, 1024);
290
strlcat(configfile, "/direct/.config", 1024);
291
if (configfile_find_option(configfile, key, job->printer, 256))
293
strlcpy(configfile, user_default_path, 1024);
294
strlcat(configfile, "/direct.conf", 1024);
295
if (configfile_find_option(configfile, key, job->printer, 256))
297
if (configfile_find_option(CONFIG_PATH "/direct/.config", key, job->printer, 256))
299
if (configfile_find_option(CONFIG_PATH "/direct.conf", key, job->printer, 256))
305
void init_direct(list_t *arglist, dstr_t *filelist, jobparams_t *job)
309
char user_default_path [PATH_MAX];
311
strlcpy(user_default_path, getenv("HOME"), 256);
312
strlcat(user_default_path, "/.foomatic/", 256);
314
/* Which files do we want to print? */
315
for (i = arglist->first; i; i = i->next) {
316
strncpy_omit(tmp, (char*)i->data, 1024, omit_shellescapes);
317
dstrcatf(filelist, "%s ", tmp);
320
if (job->ppdfile[0] == '\0') {
321
if (job->printer[0] == '\0') {
322
/* No printer definition file selected, check whether we have a
323
default printer defined */
324
find_default_printer(user_default_path, job);
327
/* Neither in a config file nor on the command line a printer was selected */
328
if (!job->printer[0]) {
329
_log("No printer definition (option \"-P <name>\") specified!\n");
330
exit(EXIT_PRNERR_NORETRY_BAD_SETTINGS);
333
/* Search for the PPD file */
334
if (!find_ppdfile(user_default_path, job)) {
335
_log("There is no readable PPD file for the printer %s, is it configured?\n", job->printer);
336
exit(EXIT_PRNERR_NORETRY_BAD_SETTINGS);