X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=filter%2Fcommandtops.c;h=a75809c2477d68201d0faac665f4db8916d411f5;hb=61515785f7de12d8b2a29090020e684988f89977;hp=ea7caf10dce260c8daca890ee79509ab4e490b14;hpb=7a14d7682bc444c9c97dd5bfd18a3da07efd7eb3;p=thirdparty%2Fcups.git diff --git a/filter/commandtops.c b/filter/commandtops.c index ea7caf10d..a75809c24 100644 --- a/filter/commandtops.c +++ b/filter/commandtops.c @@ -1,9 +1,9 @@ /* - * "$Id$" + * "$Id: commandtops.c 3794 2012-04-23 22:44:16Z msweet $" * * PostScript command filter for CUPS. * - * Copyright 2008 by Apple Inc. + * Copyright 2008-2012 by Apple Inc. * * These coded instructions, statements, and computer programs are the * property of Apple Inc. and are protected by Federal copyright @@ -14,14 +14,21 @@ * * Contents: * + * main() - Process a CUPS command file. + * auto_configure() - Automatically configure the printer using + * PostScript query commands and/or SNMP lookups. + * begin_ps() - Send the standard PostScript prolog. + * end_ps() - Send the standard PostScript trailer. + * print_self_test_page() - Print a self-test page. + * report_levels() - Report supply levels. */ /* * Include necessary headers... */ -#include -#include +#include +#include #include @@ -29,7 +36,9 @@ * Local functions... */ -static void auto_configure(ppd_file_t *ppd, const char *user); +static int auto_configure(ppd_file_t *ppd, const char *user); +static void begin_ps(ppd_file_t *ppd, const char *user); +static void end_ps(ppd_file_t *ppd); static void print_self_test_page(ppd_file_t *ppd, const char *user); static void report_levels(ppd_file_t *ppd, const char *user); @@ -42,6 +51,7 @@ int /* O - Exit status */ main(int argc, /* I - Number of command-line arguments */ char *argv[]) /* I - Command-line arguments */ { + int status = 0; /* Exit status */ cups_file_t *fp; /* Command file */ char line[1024], /* Line from file */ *value; /* Value on line */ @@ -60,7 +70,9 @@ main(int argc, /* I - Number of command-line arguments */ * and return. */ - fputs("ERROR: commandtops job-id user title copies options [file]\n", stderr); + _cupsLangPrintf(stderr, + _("Usage: %s job-id user title copies options [file]"), + argv[0]); return (1); } @@ -101,17 +113,21 @@ main(int argc, /* I - Number of command-line arguments */ * Parse the command... */ - if (!strcasecmp(line, "AutoConfigure")) - auto_configure(ppd, argv[2]); - else if (!strcasecmp(line, "PrintSelfTestPage")) + if (!_cups_strcasecmp(line, "AutoConfigure")) + status |= auto_configure(ppd, argv[2]); + else if (!_cups_strcasecmp(line, "PrintSelfTestPage")) print_self_test_page(ppd, argv[2]); - else if (!strcasecmp(line, "ReportLevels")) + else if (!_cups_strcasecmp(line, "ReportLevels")) report_levels(ppd, argv[2]); else - fprintf(stderr, "ERROR: Invalid printer command \"%s\"!\n", line); + { + _cupsLangPrintFilter(stderr, "ERROR", + _("Invalid printer command \"%s\"."), line); + status = 1; + } } - return (0); + return (status); } @@ -120,12 +136,14 @@ main(int argc, /* I - Number of command-line arguments */ * query commands and/or SNMP lookups. */ -static void +static int /* O - Exit status */ auto_configure(ppd_file_t *ppd, /* I - PPD file */ const char *user) /* I - Printing user */ { + int status = 0; /* Exit status */ ppd_option_t *option; /* Current option in PPD */ ppd_attr_t *attr; /* Query command attribute */ + const char *valptr; /* Pointer into attribute value */ char buffer[1024], /* String buffer */ *bufptr; /* Pointer into buffer */ ssize_t bytes; /* Number of bytes read */ @@ -143,22 +161,47 @@ auto_configure(ppd_file_t *ppd, /* I - PPD file */ { fputs("DEBUG: Unable to auto-configure PostScript Printer - no " "bidirectional I/O available!\n", stderr); - return; + return (1); } /* * Put the printer in PostScript mode... */ - if (ppd->jcl_begin) - { - fputs(ppd->jcl_begin, stdout); - fputs(ppd->jcl_ps, stdout); - } + begin_ps(ppd, user); - puts("%!"); + /* + * (STR #4028) + * + * As a lot of PPDs contain bad PostScript query code, we need to prevent one + * bad query sequence from affecting all auto-configuration. The following + * error handler allows us to log PostScript errors to cupsd. + */ + + puts("/cups_handleerror {\n" + " $error /newerror false put\n" + " (:PostScript error in \") print cups_query_keyword print (\": ) " + "print\n" + " $error /errorname get 128 string cvs print\n" + " (; offending command:) print $error /command get 128 string cvs " + "print (\n) print flush\n" + "} bind def\n" + "errordict /timeout {} put\n" + "/cups_query_keyword (?Unknown) def\n"); fflush(stdout); + /* + * Wait for the printer to become connected... + */ + + do + { + sleep(1); + datalen = 1; + } + while (cupsSideChannelDoRequest(CUPS_SC_CMD_GET_CONNECTED, buffer, &datalen, + 5.0) == CUPS_SC_STATUS_OK && !buffer[0]); + /* * Then loop through every option in the PPD file and ask for the current * value... @@ -185,7 +228,76 @@ auto_configure(ppd_file_t *ppd, /* I - PPD file */ */ fprintf(stderr, "DEBUG: Querying %s...\n", option->keyword); - fputs(attr->value, stdout); + + for (bufptr = buffer, valptr = attr->value; *valptr; valptr ++) + { + /* + * Log the query code, breaking at newlines... + */ + + if (*valptr == '\n') + { + *bufptr = '\0'; + fprintf(stderr, "DEBUG: %s\\n\n", buffer); + bufptr = buffer; + } + else if (*valptr < ' ') + { + if (bufptr >= (buffer + sizeof(buffer) - 4)) + { + *bufptr = '\0'; + fprintf(stderr, "DEBUG: %s\n", buffer); + bufptr = buffer; + } + + if (*valptr == '\r') + { + *bufptr++ = '\\'; + *bufptr++ = 'r'; + } + else if (*valptr == '\t') + { + *bufptr++ = '\\'; + *bufptr++ = 't'; + } + else + { + *bufptr++ = '\\'; + *bufptr++ = '0' + ((*valptr / 64) & 7); + *bufptr++ = '0' + ((*valptr / 8) & 7); + *bufptr++ = '0' + (*valptr & 7); + } + } + else + { + if (bufptr >= (buffer + sizeof(buffer) - 1)) + { + *bufptr = '\0'; + fprintf(stderr, "DEBUG: %s\n", buffer); + bufptr = buffer; + } + + *bufptr++ = *valptr; + } + } + + if (bufptr > buffer) + { + *bufptr = '\0'; + fprintf(stderr, "DEBUG: %s\n", buffer); + } + + printf("/cups_query_keyword (?%s) def\n", option->keyword); + /* Set keyword for error reporting */ + fputs("{ (", stdout); + for (valptr = attr->value; *valptr; valptr ++) + { + if (*valptr == '(' || *valptr == ')' || *valptr == '\\') + putchar('\\'); + putchar(*valptr); + } + fputs(") cvx exec } stopped { cups_handleerror } if clear\n", stdout); + /* Send query code */ fflush(stdout); datalen = 0; @@ -195,45 +307,154 @@ auto_configure(ppd_file_t *ppd, /* I - PPD file */ * Read the response data... */ - while ((bytes = cupsBackChannelRead(buffer, sizeof(buffer) - 1, 5.0)) > 0) + bufptr = buffer; + buffer[0] = '\0'; + while ((bytes = cupsBackChannelRead(bufptr, + sizeof(buffer) - (bufptr - buffer) - 1, + 10.0)) > 0) { /* - * Trim whitespace from both ends... + * No newline at the end? Go on reading ... */ - buffer[bytes] = '\0'; + bufptr += bytes; + *bufptr = '\0'; + + if (bytes == 0 || + (bufptr > buffer && bufptr[-1] != '\r' && bufptr[-1] != '\n')) + continue; - for (bufptr = buffer + bytes - 1; bufptr >= buffer; bufptr --) - if (isspace(*bufptr & 255)) + /* + * Trim whitespace and control characters from both ends... + */ + + bytes = bufptr - buffer; + + for (bufptr --; bufptr >= buffer; bufptr --) + if (isspace(*bufptr & 255) || iscntrl(*bufptr & 255)) *bufptr = '\0'; else break; - for (bufptr = buffer; isspace(*bufptr & 255); bufptr ++); + for (bufptr = buffer; isspace(*bufptr & 255) || iscntrl(*bufptr & 255); + bufptr ++); + + if (bufptr > buffer) + { + _cups_strcpy(buffer, bufptr); + bufptr = buffer; + } + + fprintf(stderr, "DEBUG: Got %d bytes.\n", (int)bytes); /* * Skip blank lines... */ - if (!*bufptr) + if (!buffer[0]) continue; + /* + * Check the response... + */ + + if ((bufptr = strchr(buffer, ':')) != NULL) + { + /* + * PostScript code for this option in the PPD is broken; show the + * interpreter's error message that came back... + */ + + fprintf(stderr, "DEBUG%s\n", bufptr); + break; + } + + /* + * Verify the result is a valid option choice... + */ + + if (!ppdFindChoice(option, buffer)) + { + if (!strcasecmp(buffer, "Unknown")) + break; + + bufptr = buffer; + buffer[0] = '\0'; + continue; + } + /* * Write out the result and move on to the next option... */ - fprintf(stderr, "DEBUG: Default%s=%s\n", option->keyword, bufptr); - fprintf(stderr, "PPD: Default%s=%s\n", option->keyword, bufptr); + fprintf(stderr, "PPD: Default%s=%s\n", option->keyword, buffer); break; } + + /* + * Printer did not answer this option's query + */ + + if (bytes <= 0) + { + fprintf(stderr, + "DEBUG: No answer to query for option %s within 10 seconds.\n", + option->keyword); + status = 1; + } } /* * Finish the job... */ + fflush(stdout); + end_ps(ppd); + + /* + * Return... + */ + + if (status) + _cupsLangPrintFilter(stderr, "WARNING", + _("Unable to configure printer options.")); + + return (0); +} + + +/* + * 'begin_ps()' - Send the standard PostScript prolog. + */ + +static void +begin_ps(ppd_file_t *ppd, /* I - PPD file */ + const char *user) /* I - Username */ +{ + (void)user; + if (ppd->jcl_begin) + { fputs(ppd->jcl_begin, stdout); + fputs(ppd->jcl_ps, stdout); + } + + puts("%!"); + puts("userdict dup(\\004)cvn{}put (\\004\\004)cvn{}put\n"); + + fflush(stdout); +} + + +/* + * 'end_ps()' - Send the standard PostScript trailer. + */ + +static void +end_ps(ppd_file_t *ppd) /* I - PPD file */ +{ + if (ppd->jcl_end) + fputs(ppd->jcl_end, stdout); else putchar(0x04); @@ -253,20 +474,19 @@ print_self_test_page(ppd_file_t *ppd, /* I - PPD file */ * Put the printer in PostScript mode... */ - if (ppd->jcl_begin) - { - fputs(ppd->jcl_begin, stdout); - fputs(ppd->jcl_ps, stdout); - } - - puts("%!"); + begin_ps(ppd, user); /* * Send a simple file the draws a box around the imageable area and shows * the product/interpreter information... */ - puts("% You are using the wrong driver for your printer!\n" + puts("\r%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%" + "%%%%%%%%%%%%%\n" + "\r%%%% If you can read this, you are using the wrong driver for your " + "printer. %%%%\n" + "\r%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%" + "%%%%%%%%%%%%%\n" "0 setgray\n" "2 setlinewidth\n" "initclip newpath clippath gsave stroke grestore pathbbox\n" @@ -282,12 +502,7 @@ print_self_test_page(ppd_file_t *ppd, /* I - PPD file */ * Finish the job... */ - if (ppd->jcl_begin) - fputs(ppd->jcl_begin, stdout); - else - putchar(0x04); - - fflush(stdout); + end_ps(ppd); } @@ -303,27 +518,21 @@ report_levels(ppd_file_t *ppd, /* I - PPD file */ * Put the printer in PostScript mode... */ - if (ppd->jcl_begin) - { - fputs(ppd->jcl_begin, stdout); - fputs(ppd->jcl_ps, stdout); - } + begin_ps(ppd, user); - puts("%!"); + /* + * Don't bother sending any additional PostScript commands, since we just + * want the backend to have enough time to collect the supply info. + */ /* * Finish the job... */ - if (ppd->jcl_begin) - fputs(ppd->jcl_begin, stdout); - else - putchar(0x04); - - fflush(stdout); + end_ps(ppd); } /* - * End of "$Id$". + * End of "$Id: commandtops.c 3794 2012-04-23 22:44:16Z msweet $". */