2 * "$Id: cupstestppd.c 7807 2008-07-28 21:54:24Z mike $"
4 * PPD test program for the Common UNIX Printing System (CUPS).
6 * Copyright 2007-2009 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * PostScript is a trademark of Adobe Systems, Inc.
17 * This file is subject to the Apple OS-Developed Software exception.
21 * main() - Main entry for test program.
22 * check_basics() - Check for CR LF, mixed line endings, and blank
24 * check_constraints() - Check UIConstraints in the PPD file.
25 * check_case() - Check that there are no duplicate groups, options,
26 * or choices that differ only by case.
27 * check_defaults() - Check default option keywords in the PPD file.
28 * check_duplex() - Check duplex keywords in the PPD file.
29 * check_filters() - Check filters in the PPD file.
30 * check_profiles() - Check ICC color profiles in the PPD file.
31 * check_sizes() - Check media sizes in the PPD file.
32 * check_translations() - Check translations in the PPD file.
33 * show_conflicts() - Show option conflicts in a PPD file.
34 * test_raster() - Test PostScript commands for raster printers.
35 * usage() - Show program usage...
36 * valid_path() - Check whether a path has the correct capitalization.
37 * valid_utf8() - Check whether a string contains valid UTF-8 text.
41 * Include necessary headers...
44 #include <cups/string.h>
45 #include <cups/cups.h>
47 #include <cups/ppd-private.h>
48 #include <cups/i18n.h>
49 #include <cups/raster.h>
59 * Error warning overrides...
69 WARN_TRANSLATIONS
= 16,
104 * File permissions...
107 #define MODE_WRITE 0022 /* Group/other write */
108 #define MODE_MASK 0555 /* Owner/group/other read+exec/search */
109 #define MODE_DATAFILE 0444 /* Owner/group/other read */
110 #define MODE_DIRECTORY 0555 /* Owner/group/other read+search */
111 #define MODE_PROGRAM 0555 /* Owner/group/other read+exec */
115 * Standard Adobe media keywords (must remain sorted)...
118 static const char adobe_size_names
[][PPD_MAX_NAME
] =
179 "DoublePostcardRotated",
234 "FanFoldGermanLegal",
280 static void check_basics(const char *filename
);
281 static int check_constraints(ppd_file_t
*ppd
, int errors
, int verbose
,
283 static int check_case(ppd_file_t
*ppd
, int errors
, int verbose
);
284 static int check_defaults(ppd_file_t
*ppd
, int errors
, int verbose
,
286 static int check_duplex(ppd_file_t
*ppd
, int errors
, int verbose
,
288 static int check_filters(ppd_file_t
*ppd
, const char *root
, int errors
,
289 int verbose
, int warn
);
290 static int check_profiles(ppd_file_t
*ppd
, const char *root
, int errors
,
291 int verbose
, int warn
);
292 static int check_sizes(ppd_file_t
*ppd
, int errors
, int verbose
, int warn
);
293 static int check_translations(ppd_file_t
*ppd
, int errors
, int verbose
,
295 static void show_conflicts(ppd_file_t
*ppd
);
296 static int test_raster(ppd_file_t
*ppd
, int verbose
);
297 static void usage(void);
298 static int valid_path(const char *keyword
, const char *path
, int errors
,
299 int verbose
, int warn
);
300 static int valid_utf8(const char *s
);
304 * 'main()' - Main entry for test program.
307 int /* O - Exit status */
308 main(int argc
, /* I - Number of command-line args */
309 char *argv
[]) /* I - Command-line arguments */
311 int i
, j
, k
, m
, n
; /* Looping vars */
312 int len
; /* Length of option name */
313 char *opt
; /* Option character */
314 const char *ptr
; /* Pointer into string */
315 int files
; /* Number of files */
316 int verbose
; /* Want verbose output? */
317 int warn
; /* Which errors to just warn about */
318 int ignore
; /* Which errors to ignore */
319 int status
; /* Exit status */
320 int errors
; /* Number of conformance errors */
321 int ppdversion
; /* PPD spec version in PPD file */
322 ppd_status_t error
; /* Status of ppdOpen*() */
323 int line
; /* Line number for error */
324 char *root
; /* Root directory */
325 int xdpi
, /* X resolution */
326 ydpi
; /* Y resolution */
327 ppd_file_t
*ppd
; /* PPD file record */
328 ppd_attr_t
*attr
; /* PPD attribute */
329 ppd_size_t
*size
; /* Size record */
330 ppd_group_t
*group
; /* UI group */
331 ppd_option_t
*option
; /* Standard UI option */
332 ppd_group_t
*group2
; /* UI group */
333 ppd_option_t
*option2
; /* Standard UI option */
334 ppd_choice_t
*choice
; /* Standard UI option choice */
335 struct lconv
*loc
; /* Locale data */
336 static char *uis
[] = { "BOOLEAN", "PICKONE", "PICKMANY" };
337 static char *sections
[] = { "ANY", "DOCUMENT", "EXIT",
338 "JCL", "PAGE", "PROLOG" };
341 _cupsSetLocale(argv
);
345 * Display PPD files for each file listed on the command-line...
348 ppdSetConformance(PPD_CONFORM_STRICT
);
358 for (i
= 1; i
< argc
; i
++)
359 if (argv
[i
][0] == '-' && argv
[i
][1])
361 for (opt
= argv
[i
] + 1; *opt
; opt
++)
364 case 'I' : /* Ignore errors */
370 if (!strcmp(argv
[i
], "none"))
372 else if (!strcmp(argv
[i
], "filters"))
373 ignore
|= WARN_FILTERS
;
374 else if (!strcmp(argv
[i
], "profiles"))
375 ignore
|= WARN_PROFILES
;
376 else if (!strcmp(argv
[i
], "all"))
377 ignore
= WARN_FILTERS
| WARN_PROFILES
;
382 case 'R' : /* Alternate root directory */
391 case 'W' : /* Turn errors into warnings */
397 if (!strcmp(argv
[i
], "none"))
399 else if (!strcmp(argv
[i
], "constraints"))
400 warn
|= WARN_CONSTRAINTS
;
401 else if (!strcmp(argv
[i
], "defaults"))
402 warn
|= WARN_DEFAULTS
;
403 else if (!strcmp(argv
[i
], "duplex"))
405 else if (!strcmp(argv
[i
], "filters"))
406 warn
|= WARN_FILTERS
;
407 else if (!strcmp(argv
[i
], "profiles"))
408 warn
|= WARN_PROFILES
;
409 else if (!strcmp(argv
[i
], "sizes"))
411 else if (!strcmp(argv
[i
], "translations"))
412 warn
|= WARN_TRANSLATIONS
;
413 else if (!strcmp(argv
[i
], "all"))
419 case 'q' : /* Quiet mode */
422 _cupsLangPuts(stderr
,
423 _("cupstestppd: The -q option is incompatible "
424 "with the -v option.\n"));
431 case 'r' : /* Relaxed mode */
432 ppdSetConformance(PPD_CONFORM_RELAXED
);
435 case 'v' : /* Verbose mode */
438 _cupsLangPuts(stderr
,
439 _("cupstestppd: The -v option is incompatible "
440 "with the -q option.\n"));
455 * Open the PPD file...
458 if (files
&& verbose
>= 0)
459 _cupsLangPuts(stdout
, "\n");
463 if (argv
[i
][0] == '-')
469 ppd
= ppdOpen(stdin
);
472 printf("%s:", (ppd
&& ppd
->pcfilename
) ? ppd
->pcfilename
: "(stdin)");
477 * Read from a file...
481 printf("%s:", argv
[i
]);
483 ppd
= ppdOpenFile(argv
[i
]);
488 error
= ppdLastError(&line
);
490 if (error
<= PPD_ALLOC_ERROR
)
492 status
= ERROR_FILE_OPEN
;
495 _cupsLangPrintf(stdout
,
497 " **FAIL** Unable to open PPD file - %s\n"),
502 status
= ERROR_PPD_FORMAT
;
506 _cupsLangPrintf(stdout
,
508 " **FAIL** Unable to open PPD file - "
510 ppdErrorString(error
), line
);
514 case PPD_MISSING_PPDADOBE4
:
515 _cupsLangPuts(stdout
,
516 _(" REF: Page 42, section 5.2.\n"));
518 case PPD_MISSING_VALUE
:
519 _cupsLangPuts(stdout
,
520 _(" REF: Page 20, section 3.4.\n"));
522 case PPD_BAD_OPEN_GROUP
:
523 case PPD_NESTED_OPEN_GROUP
:
524 _cupsLangPuts(stdout
,
525 _(" REF: Pages 45-46, section 5.2.\n"));
527 case PPD_BAD_OPEN_UI
:
528 case PPD_NESTED_OPEN_UI
:
529 _cupsLangPuts(stdout
,
530 _(" REF: Pages 42-45, section 5.2.\n"));
532 case PPD_BAD_ORDER_DEPENDENCY
:
533 _cupsLangPuts(stdout
,
534 _(" REF: Pages 48-49, section 5.2.\n"));
536 case PPD_BAD_UI_CONSTRAINTS
:
537 _cupsLangPuts(stdout
,
538 _(" REF: Pages 52-54, section 5.2.\n"));
540 case PPD_MISSING_ASTERISK
:
541 _cupsLangPuts(stdout
,
542 _(" REF: Page 15, section 3.2.\n"));
544 case PPD_LINE_TOO_LONG
:
545 _cupsLangPuts(stdout
,
546 _(" REF: Page 15, section 3.1.\n"));
548 case PPD_ILLEGAL_CHARACTER
:
549 _cupsLangPuts(stdout
,
550 _(" REF: Page 15, section 3.1.\n"));
552 case PPD_ILLEGAL_MAIN_KEYWORD
:
553 _cupsLangPuts(stdout
,
554 _(" REF: Pages 16-17, section 3.2.\n"));
556 case PPD_ILLEGAL_OPTION_KEYWORD
:
557 _cupsLangPuts(stdout
,
558 _(" REF: Page 19, section 3.3.\n"));
560 case PPD_ILLEGAL_TRANSLATION
:
561 _cupsLangPuts(stdout
,
562 _(" REF: Page 27, section 3.5.\n"));
568 check_basics(argv
[i
]);
576 * Show the header and then perform basic conformance tests (limited
577 * only by what the CUPS PPD functions actually load...)
584 _cupsLangPuts(stdout
,
585 _("\n DETAILED CONFORMANCE TEST RESULTS\n"));
587 if ((attr
= ppdFindAttr(ppd
, "FormatVersion", NULL
)) != NULL
&&
589 ppdversion
= (int)(10 * _cupsStrScand(attr
->value
, NULL
, loc
) + 0.5);
591 for (j
= 0; j
< ppd
->num_filters
; j
++)
592 if (strstr(ppd
->filters
[j
], "application/vnd.cups-raster"))
594 if (!test_raster(ppd
, verbose
))
600 * Look for default keywords with no matching option...
603 if (!(warn
& WARN_DEFAULTS
))
604 errors
= check_defaults(ppd
, errors
, verbose
, 0);
606 if ((attr
= ppdFindAttr(ppd
, "DefaultImageableArea", NULL
)) == NULL
)
610 if (!errors
&& !verbose
)
611 _cupsLangPuts(stdout
, _(" FAIL\n"));
613 _cupsLangPuts(stdout
,
614 _(" **FAIL** REQUIRED DefaultImageableArea\n"
615 " REF: Page 102, section 5.15.\n"));
620 else if (ppdPageSize(ppd
, attr
->value
) == NULL
&&
621 strcmp(attr
->value
, "Unknown"))
625 if (!errors
&& !verbose
)
626 _cupsLangPuts(stdout
, _(" FAIL\n"));
628 _cupsLangPrintf(stdout
,
629 _(" **FAIL** BAD DefaultImageableArea %s!\n"
630 " REF: Page 102, section 5.15.\n"),
639 _cupsLangPuts(stdout
, _(" PASS DefaultImageableArea\n"));
642 if ((attr
= ppdFindAttr(ppd
, "DefaultPaperDimension", NULL
)) == NULL
)
646 if (!errors
&& !verbose
)
647 _cupsLangPuts(stdout
, _(" FAIL\n"));
649 _cupsLangPuts(stdout
,
650 _(" **FAIL** REQUIRED DefaultPaperDimension\n"
651 " REF: Page 103, section 5.15.\n"));
656 else if (ppdPageSize(ppd
, attr
->value
) == NULL
&&
657 strcmp(attr
->value
, "Unknown"))
661 if (!errors
&& !verbose
)
662 _cupsLangPuts(stdout
, _(" FAIL\n"));
664 _cupsLangPrintf(stdout
,
665 _(" **FAIL** BAD DefaultPaperDimension %s!\n"
666 " REF: Page 103, section 5.15.\n"),
672 else if (verbose
> 0)
673 _cupsLangPuts(stdout
, _(" PASS DefaultPaperDimension\n"));
675 for (j
= 0, group
= ppd
->groups
; j
< ppd
->num_groups
; j
++, group
++)
676 for (k
= 0, option
= group
->options
; k
< group
->num_options
; k
++, option
++)
679 * Verify that we have a default choice...
682 if (option
->defchoice
[0])
684 if (ppdFindChoice(option
, option
->defchoice
) == NULL
&&
685 strcmp(option
->defchoice
, "Unknown"))
689 if (!errors
&& !verbose
)
690 _cupsLangPuts(stdout
, _(" FAIL\n"));
692 _cupsLangPrintf(stdout
,
693 _(" **FAIL** BAD Default%s %s\n"
694 " REF: Page 40, section 4.5.\n"),
695 option
->keyword
, option
->defchoice
);
700 else if (verbose
> 0)
701 _cupsLangPrintf(stdout
,
702 _(" PASS Default%s\n"),
709 if (!errors
&& !verbose
)
710 _cupsLangPuts(stdout
, _(" FAIL\n"));
712 _cupsLangPrintf(stdout
,
713 _(" **FAIL** REQUIRED Default%s\n"
714 " REF: Page 40, section 4.5.\n"),
722 if ((attr
= ppdFindAttr(ppd
, "FileVersion", NULL
)) != NULL
)
724 for (ptr
= attr
->value
; *ptr
; ptr
++)
725 if (!isdigit(*ptr
& 255) && *ptr
!= '.')
732 if (!errors
&& !verbose
)
733 _cupsLangPuts(stdout
, _(" FAIL\n"));
735 _cupsLangPrintf(stdout
,
736 _(" **FAIL** Bad FileVersion \"%s\"\n"
737 " REF: Page 56, section 5.3.\n"),
743 else if (verbose
> 0)
744 _cupsLangPuts(stdout
, _(" PASS FileVersion\n"));
750 if (!errors
&& !verbose
)
751 _cupsLangPuts(stdout
, _(" FAIL\n"));
753 _cupsLangPuts(stdout
,
754 _(" **FAIL** REQUIRED FileVersion\n"
755 " REF: Page 56, section 5.3.\n"));
761 if ((attr
= ppdFindAttr(ppd
, "FormatVersion", NULL
)) != NULL
)
764 if (*ptr
== '4' && ptr
[1] == '.')
767 for (ptr
+= 2; *ptr
; ptr
++)
768 if (!isdigit(*ptr
& 255))
776 if (!errors
&& !verbose
)
777 _cupsLangPuts(stdout
, _(" FAIL\n"));
779 _cupsLangPrintf(stdout
,
780 _(" **FAIL** Bad FormatVersion \"%s\"\n"
781 " REF: Page 56, section 5.3.\n"),
787 else if (verbose
> 0)
788 _cupsLangPuts(stdout
, _(" PASS FormatVersion\n"));
794 if (!errors
&& !verbose
)
795 _cupsLangPuts(stdout
, _(" FAIL\n"));
797 _cupsLangPuts(stdout
,
798 _(" **FAIL** REQUIRED FormatVersion\n"
799 " REF: Page 56, section 5.3.\n"));
805 if (ppd
->lang_encoding
!= NULL
)
808 _cupsLangPuts(stdout
, _(" PASS LanguageEncoding\n"));
810 else if (ppdversion
> 40)
814 if (!errors
&& !verbose
)
815 _cupsLangPuts(stdout
, _(" FAIL\n"));
817 _cupsLangPuts(stdout
,
818 _(" **FAIL** REQUIRED LanguageEncoding\n"
819 " REF: Pages 56-57, section 5.3.\n"));
825 if (ppd
->lang_version
!= NULL
)
828 _cupsLangPuts(stdout
, _(" PASS LanguageVersion\n"));
834 if (!errors
&& !verbose
)
835 _cupsLangPuts(stdout
, _(" FAIL\n"));
837 _cupsLangPuts(stdout
,
838 _(" **FAIL** REQUIRED LanguageVersion\n"
839 " REF: Pages 57-58, section 5.3.\n"));
845 if (ppd
->manufacturer
!= NULL
)
847 if (!strncasecmp(ppd
->manufacturer
, "Hewlett-Packard", 15) ||
848 !strncasecmp(ppd
->manufacturer
, "Hewlett Packard", 15))
852 if (!errors
&& !verbose
)
853 _cupsLangPuts(stdout
, _(" FAIL\n"));
855 _cupsLangPuts(stdout
,
856 _(" **FAIL** BAD Manufacturer (should be "
858 " REF: Page 211, table D.1.\n"));
863 else if (!strncasecmp(ppd
->manufacturer
, "OkiData", 7) ||
864 !strncasecmp(ppd
->manufacturer
, "Oki Data", 8))
868 if (!errors
&& !verbose
)
869 _cupsLangPuts(stdout
, _(" FAIL\n"));
871 _cupsLangPuts(stdout
,
872 _(" **FAIL** BAD Manufacturer (should be "
874 " REF: Page 211, table D.1.\n"));
879 else if (verbose
> 0)
880 _cupsLangPuts(stdout
, _(" PASS Manufacturer\n"));
882 else if (ppdversion
>= 43)
886 if (!errors
&& !verbose
)
887 _cupsLangPuts(stdout
, _(" FAIL\n"));
889 _cupsLangPuts(stdout
,
890 _(" **FAIL** REQUIRED Manufacturer\n"
891 " REF: Pages 58-59, section 5.3.\n"));
897 if (ppd
->modelname
!= NULL
)
899 for (ptr
= ppd
->modelname
; *ptr
; ptr
++)
900 if (!isalnum(*ptr
& 255) && !strchr(" ./-+", *ptr
))
907 if (!errors
&& !verbose
)
908 _cupsLangPuts(stdout
, _(" FAIL\n"));
910 _cupsLangPrintf(stdout
,
911 _(" **FAIL** BAD ModelName - \"%c\" not "
912 "allowed in string.\n"
913 " REF: Pages 59-60, section 5.3.\n"),
919 else if (verbose
> 0)
920 _cupsLangPuts(stdout
, _(" PASS ModelName\n"));
926 if (!errors
&& !verbose
)
927 _cupsLangPuts(stdout
, _(" FAIL\n"));
929 _cupsLangPuts(stdout
,
930 _(" **FAIL** REQUIRED ModelName\n"
931 " REF: Pages 59-60, section 5.3.\n"));
937 if (ppd
->nickname
!= NULL
)
940 _cupsLangPuts(stdout
, _(" PASS NickName\n"));
946 if (!errors
&& !verbose
)
947 _cupsLangPuts(stdout
, _(" FAIL\n"));
949 _cupsLangPuts(stdout
,
950 _(" **FAIL** REQUIRED NickName\n"
951 " REF: Page 60, section 5.3.\n"));
957 if (ppdFindOption(ppd
, "PageSize") != NULL
)
960 _cupsLangPuts(stdout
, _(" PASS PageSize\n"));
966 if (!errors
&& !verbose
)
967 _cupsLangPuts(stdout
, _(" FAIL\n"));
969 _cupsLangPuts(stdout
,
970 _(" **FAIL** REQUIRED PageSize\n"
971 " REF: Pages 99-100, section 5.14.\n"));
977 if (ppdFindOption(ppd
, "PageRegion") != NULL
)
980 _cupsLangPuts(stdout
, _(" PASS PageRegion\n"));
986 if (!errors
&& !verbose
)
987 _cupsLangPuts(stdout
, _(" FAIL\n"));
989 _cupsLangPuts(stdout
,
990 _(" **FAIL** REQUIRED PageRegion\n"
991 " REF: Page 100, section 5.14.\n"));
997 if (ppd
->pcfilename
!= NULL
)
1000 _cupsLangPuts(stdout
, _(" PASS PCFileName\n"));
1006 if (!errors
&& !verbose
)
1007 _cupsLangPuts(stdout
, _(" FAIL\n"));
1009 _cupsLangPuts(stdout
,
1010 _(" **FAIL** REQUIRED PCFileName\n"
1011 " REF: Pages 61-62, section 5.3.\n"));
1017 if (ppd
->product
!= NULL
)
1019 if (ppd
->product
[0] != '(' ||
1020 ppd
->product
[strlen(ppd
->product
) - 1] != ')')
1024 if (!errors
&& !verbose
)
1025 _cupsLangPuts(stdout
, _(" FAIL\n"));
1027 _cupsLangPuts(stdout
,
1028 _(" **FAIL** BAD Product - not \"(string)\".\n"
1029 " REF: Page 62, section 5.3.\n"));
1034 else if (verbose
> 0)
1035 _cupsLangPuts(stdout
, _(" PASS Product\n"));
1041 if (!errors
&& !verbose
)
1042 _cupsLangPuts(stdout
, _(" FAIL\n"));
1044 _cupsLangPuts(stdout
,
1045 _(" **FAIL** REQUIRED Product\n"
1046 " REF: Page 62, section 5.3.\n"));
1052 if ((attr
= ppdFindAttr(ppd
, "PSVersion", NULL
)) != NULL
&&
1053 attr
->value
!= NULL
)
1055 char junkstr
[255]; /* Temp string */
1056 int junkint
; /* Temp integer */
1059 if (sscanf(attr
->value
, "(%[^)])%d", junkstr
, &junkint
) != 2)
1063 if (!errors
&& !verbose
)
1064 _cupsLangPuts(stdout
, _(" FAIL\n"));
1066 _cupsLangPuts(stdout
,
1067 _(" **FAIL** BAD PSVersion - not \"(string) "
1069 " REF: Pages 62-64, section 5.3.\n"));
1074 else if (verbose
> 0)
1075 _cupsLangPuts(stdout
, _(" PASS PSVersion\n"));
1081 if (!errors
&& !verbose
)
1082 _cupsLangPuts(stdout
, _(" FAIL\n"));
1084 _cupsLangPuts(stdout
,
1085 _(" **FAIL** REQUIRED PSVersion\n"
1086 " REF: Pages 62-64, section 5.3.\n"));
1092 if (ppd
->shortnickname
!= NULL
)
1094 if (strlen(ppd
->shortnickname
) > 31)
1098 if (!errors
&& !verbose
)
1099 _cupsLangPuts(stdout
, _(" FAIL\n"));
1101 _cupsLangPuts(stdout
,
1102 _(" **FAIL** BAD ShortNickName - longer "
1104 " REF: Pages 64-65, section 5.3.\n"));
1109 else if (verbose
> 0)
1110 _cupsLangPuts(stdout
, _(" PASS ShortNickName\n"));
1112 else if (ppdversion
>= 43)
1116 if (!errors
&& !verbose
)
1117 _cupsLangPuts(stdout
, _(" FAIL\n"));
1119 _cupsLangPuts(stdout
,
1120 _(" **FAIL** REQUIRED ShortNickName\n"
1121 " REF: Page 64-65, section 5.3.\n"));
1127 if (ppd
->patches
!= NULL
&& strchr(ppd
->patches
, '\"') &&
1128 strstr(ppd
->patches
, "*End"))
1132 if (!errors
&& !verbose
)
1133 _cupsLangPuts(stdout
, _(" FAIL\n"));
1135 _cupsLangPuts(stdout
,
1136 _(" **FAIL** BAD JobPatchFile attribute in file\n"
1137 " REF: Page 24, section 3.4.\n"));
1144 * Check for page sizes without the corresponding ImageableArea or
1145 * PaperDimension values...
1148 if (ppd
->num_sizes
== 0)
1152 if (!errors
&& !verbose
)
1153 _cupsLangPuts(stdout
, _(" FAIL\n"));
1155 _cupsLangPuts(stdout
,
1156 _(" **FAIL** REQUIRED PageSize\n"
1157 " REF: Page 41, section 5.\n"
1158 " REF: Page 99, section 5.14.\n"));
1165 for (j
= 0, size
= ppd
->sizes
; j
< ppd
->num_sizes
; j
++, size
++)
1168 * Don't check custom size...
1171 if (!strcmp(size
->name
, "Custom"))
1175 * Check for ImageableArea...
1178 if (size
->left
== 0.0 && size
->bottom
== 0.0 &&
1179 size
->right
== 0.0 && size
->top
== 0.0)
1183 if (!errors
&& !verbose
)
1184 _cupsLangPuts(stdout
, _(" FAIL\n"));
1186 _cupsLangPrintf(stdout
,
1187 _(" **FAIL** REQUIRED ImageableArea for "
1189 " REF: Page 41, section 5.\n"
1190 " REF: Page 102, section 5.15.\n"),
1198 * Check for PaperDimension...
1201 if (size
->width
== 0.0 && size
->length
== 0.0)
1205 if (!errors
&& !verbose
)
1206 _cupsLangPuts(stdout
, _(" FAIL\n"));
1208 _cupsLangPrintf(stdout
,
1209 _(" **FAIL** REQUIRED PaperDimension "
1211 " REF: Page 41, section 5.\n"
1212 " REF: Page 103, section 5.15.\n"),
1222 * Check for valid Resolution, JCLResolution, or SetResolution values...
1225 if ((option
= ppdFindOption(ppd
, "Resolution")) == NULL
)
1226 if ((option
= ppdFindOption(ppd
, "JCLResolution")) == NULL
)
1227 option
= ppdFindOption(ppd
, "SetResolution");
1231 for (j
= option
->num_choices
, choice
= option
->choices
; j
> 0; j
--, choice
++)
1234 * Verify that all resolution options are of the form NNNdpi
1238 xdpi
= strtol(choice
->choice
, (char **)&ptr
, 10);
1239 if (ptr
> choice
->choice
&& xdpi
> 0)
1242 ydpi
= strtol(ptr
+ 1, (char **)&ptr
, 10);
1249 if (xdpi
<= 0 || xdpi
> 99999 || ydpi
<= 0 || ydpi
> 99999 ||
1254 if (!errors
&& !verbose
)
1255 _cupsLangPuts(stdout
, _(" FAIL\n"));
1257 _cupsLangPrintf(stdout
,
1258 _(" **FAIL** Bad %s choice %s!\n"
1259 " REF: Page 84, section 5.9\n"),
1260 option
->keyword
, choice
->choice
);
1268 if ((attr
= ppdFindAttr(ppd
, "1284DeviceID", NULL
)) &&
1269 strcmp(attr
->name
, "1284DeviceID"))
1273 if (!errors
&& !verbose
)
1274 _cupsLangPuts(stdout
, _(" FAIL\n"));
1276 _cupsLangPrintf(stdout
,
1277 _(" **FAIL** %s must be 1284DeviceID!\n"
1278 " REF: Page 72, section 5.5\n"),
1285 errors
= check_case(ppd
, errors
, verbose
);
1287 if (!(warn
& WARN_CONSTRAINTS
))
1288 errors
= check_constraints(ppd
, errors
, verbose
, 0);
1290 if (!(warn
& WARN_FILTERS
) && !(ignore
& WARN_FILTERS
))
1291 errors
= check_filters(ppd
, root
, errors
, verbose
, 0);
1293 if (!(warn
& WARN_PROFILES
) && !(ignore
& WARN_PROFILES
))
1294 errors
= check_profiles(ppd
, root
, errors
, verbose
, 0);
1296 if (!(warn
& WARN_SIZES
))
1297 errors
= check_sizes(ppd
, errors
, verbose
, 0);
1299 if (!(warn
& WARN_TRANSLATIONS
))
1300 errors
= check_translations(ppd
, errors
, verbose
, 0);
1302 if (!(warn
& WARN_DUPLEX
))
1303 errors
= check_duplex(ppd
, errors
, verbose
, 0);
1305 if ((attr
= ppdFindAttr(ppd
, "cupsLanguages", NULL
)) != NULL
&&
1309 * This file contains localizations, check for conformance of the
1310 * base translation...
1313 if ((attr
= ppdFindAttr(ppd
, "LanguageEncoding", NULL
)) != NULL
)
1315 if (!attr
->value
|| strcmp(attr
->value
, "ISOLatin1"))
1317 if (!errors
&& !verbose
)
1318 _cupsLangPuts(stdout
, _(" FAIL\n"));
1321 _cupsLangPrintf(stdout
,
1322 _(" **FAIL** Bad LanguageEncoding %s - "
1323 "must be ISOLatin1!\n"),
1324 attr
->value
? attr
->value
: "(null)");
1329 if (!ppd
->lang_version
|| strcmp(ppd
->lang_version
, "English"))
1331 if (!errors
&& !verbose
)
1332 _cupsLangPuts(stdout
, _(" FAIL\n"));
1335 _cupsLangPrintf(stdout
,
1336 _(" **FAIL** Bad LanguageVersion %s - "
1337 "must be English!\n"),
1338 ppd
->lang_version
? ppd
->lang_version
: "(null)");
1344 * Loop through all options and choices...
1347 for (option
= ppdFirstOption(ppd
);
1349 option
= ppdNextOption(ppd
))
1352 * Check for special characters outside A0 to BF, F7, or F8
1353 * that are used for languages other than English.
1356 for (ptr
= option
->text
; *ptr
; ptr
++)
1357 if ((*ptr
& 0x80) && (*ptr
& 0xe0) != 0xa0 &&
1358 (*ptr
& 0xff) != 0xf7 && (*ptr
& 0xff) != 0xf8)
1363 if (!errors
&& !verbose
)
1364 _cupsLangPuts(stdout
, _(" FAIL\n"));
1367 _cupsLangPrintf(stdout
,
1368 _(" **FAIL** Default translation "
1369 "string for option %s contains 8-bit "
1376 for (j
= 0; j
< option
->num_choices
; j
++)
1379 * Check for special characters outside A0 to BF, F7, or F8
1380 * that are used for languages other than English.
1383 for (ptr
= option
->choices
[j
].text
; *ptr
; ptr
++)
1384 if ((*ptr
& 0x80) && (*ptr
& 0xe0) != 0xa0 &&
1385 (*ptr
& 0xff) != 0xf7 && (*ptr
& 0xff) != 0xf8)
1390 if (!errors
&& !verbose
)
1391 _cupsLangPuts(stdout
, _(" FAIL\n"));
1394 _cupsLangPrintf(stdout
,
1395 _(" **FAIL** Default translation "
1396 "string for option %s choice %s contains "
1397 "8-bit characters!\n"),
1399 option
->choices
[j
].choice
);
1409 * Final pass/fail notification...
1413 status
= ERROR_CONFORMANCE
;
1415 _cupsLangPuts(stdout
, _(" PASS\n"));
1419 check_basics(argv
[i
]);
1421 if (warn
& WARN_DEFAULTS
)
1422 errors
= check_defaults(ppd
, errors
, verbose
, 1);
1424 if (warn
& WARN_CONSTRAINTS
)
1425 errors
= check_constraints(ppd
, errors
, verbose
, 1);
1427 if ((warn
& WARN_FILTERS
) && !(ignore
& WARN_FILTERS
))
1428 errors
= check_filters(ppd
, root
, errors
, verbose
, 1);
1430 if ((warn
& WARN_PROFILES
) && !(ignore
& WARN_PROFILES
))
1431 errors
= check_profiles(ppd
, root
, errors
, verbose
, 1);
1433 if (warn
& WARN_SIZES
)
1434 errors
= check_sizes(ppd
, errors
, verbose
, 1);
1436 errors
= check_sizes(ppd
, errors
, verbose
, 2);
1438 if (warn
& WARN_TRANSLATIONS
)
1439 errors
= check_translations(ppd
, errors
, verbose
, 1);
1441 if (warn
& WARN_DUPLEX
)
1442 errors
= check_duplex(ppd
, errors
, verbose
, 1);
1445 * Look for legacy duplex keywords...
1448 if ((option
= ppdFindOption(ppd
, "JCLDuplex")) == NULL
)
1449 if ((option
= ppdFindOption(ppd
, "EFDuplex")) == NULL
)
1450 option
= ppdFindOption(ppd
, "KD03Duplex");
1453 _cupsLangPrintf(stdout
,
1454 _(" WARN Duplex option keyword %s may not "
1455 "work as expected and should be named Duplex!\n"
1456 " REF: Page 122, section 5.17\n"),
1460 * Look for default keywords with no corresponding option...
1463 for (j
= 0; j
< ppd
->num_attrs
; j
++)
1465 attr
= ppd
->attrs
[j
];
1467 if (!strcmp(attr
->name
, "DefaultColorSpace") ||
1468 !strcmp(attr
->name
, "DefaultColorSep") ||
1469 !strcmp(attr
->name
, "DefaultFont") ||
1470 !strcmp(attr
->name
, "DefaultHalftoneType") ||
1471 !strcmp(attr
->name
, "DefaultImageableArea") ||
1472 !strcmp(attr
->name
, "DefaultLeadingEdge") ||
1473 !strcmp(attr
->name
, "DefaultOutputOrder") ||
1474 !strcmp(attr
->name
, "DefaultPaperDimension") ||
1475 !strcmp(attr
->name
, "DefaultResolution") ||
1476 !strcmp(attr
->name
, "DefaultScreenProc") ||
1477 !strcmp(attr
->name
, "DefaultTransfer"))
1480 if (!strncmp(attr
->name
, "Default", 7) &&
1481 !ppdFindOption(ppd
, attr
->name
+ 7))
1482 _cupsLangPrintf(stdout
,
1483 _(" WARN %s has no corresponding "
1488 ppdMarkDefaults(ppd
);
1489 if (ppdConflicts(ppd
))
1491 _cupsLangPuts(stdout
,
1492 _(" WARN Default choices conflicting!\n"));
1494 show_conflicts(ppd
);
1497 if (ppdversion
< 43)
1499 _cupsLangPrintf(stdout
,
1500 _(" WARN Obsolete PPD version %.1f!\n"
1501 " REF: Page 42, section 5.2.\n"),
1505 if (!ppd
->lang_encoding
&& ppdversion
< 41)
1507 _cupsLangPuts(stdout
,
1508 _(" WARN LanguageEncoding required by PPD "
1510 " REF: Pages 56-57, section 5.3.\n"));
1513 if (!ppd
->manufacturer
&& ppdversion
< 43)
1515 _cupsLangPuts(stdout
,
1516 _(" WARN Manufacturer required by PPD "
1518 " REF: Pages 58-59, section 5.3.\n"));
1522 * Treat a PCFileName attribute longer than 12 characters as
1523 * a warning and not a hard error...
1526 if (ppd
->pcfilename
&& strlen(ppd
->pcfilename
) > 12)
1528 _cupsLangPuts(stdout
,
1529 _(" WARN PCFileName longer than 8.3 in "
1530 "violation of PPD spec.\n"
1531 " REF: Pages 61-62, section 5.3.\n"));
1534 if (!ppd
->shortnickname
&& ppdversion
< 43)
1536 _cupsLangPuts(stdout
,
1537 _(" WARN ShortNickName required by PPD "
1539 " REF: Pages 64-65, section 5.3.\n"));
1543 * Check the Protocols line and flag PJL + BCP since TBCP is
1544 * usually used with PJL...
1549 if (strstr(ppd
->protocols
, "PJL") &&
1550 strstr(ppd
->protocols
, "BCP") &&
1551 !strstr(ppd
->protocols
, "TBCP"))
1553 _cupsLangPuts(stdout
,
1554 _(" WARN Protocols contains both PJL "
1555 "and BCP; expected TBCP.\n"
1556 " REF: Pages 78-79, section 5.7.\n"));
1559 if (strstr(ppd
->protocols
, "PJL") &&
1560 (!ppd
->jcl_begin
|| !ppd
->jcl_end
|| !ppd
->jcl_ps
))
1562 _cupsLangPuts(stdout
,
1563 _(" WARN Protocols contains PJL but JCL "
1564 "attributes are not set.\n"
1565 " REF: Pages 78-79, section 5.7.\n"));
1570 * Check for options with a common prefix, e.g. Duplex and Duplexer,
1571 * which are errors according to the spec but won't cause problems
1572 * with CUPS specifically...
1575 for (j
= 0, group
= ppd
->groups
; j
< ppd
->num_groups
; j
++, group
++)
1576 for (k
= 0, option
= group
->options
; k
< group
->num_options
; k
++, option
++)
1578 len
= (int)strlen(option
->keyword
);
1580 for (m
= 0, group2
= ppd
->groups
;
1581 m
< ppd
->num_groups
;
1583 for (n
= 0, option2
= group2
->options
;
1584 n
< group2
->num_options
;
1586 if (option
!= option2
&&
1587 len
< (int)strlen(option2
->keyword
) &&
1588 !strncmp(option
->keyword
, option2
->keyword
, len
))
1590 _cupsLangPrintf(stdout
,
1591 _(" WARN %s shares a common "
1593 " REF: Page 15, section "
1595 option
->keyword
, option2
->keyword
);
1603 _cupsLangPrintf(stdout
, _(" %d ERRORS FOUND\n"), errors
);
1605 _cupsLangPuts(stdout
, _(" NO ERRORS FOUND\n"));
1609 * Then list the options, if "-v" was provided...
1614 _cupsLangPrintf(stdout
,
1616 " language_level = %d\n"
1617 " color_device = %s\n"
1618 " variable_sizes = %s\n"
1619 " landscape = %d\n",
1620 ppd
->language_level
,
1621 ppd
->color_device
? "TRUE" : "FALSE",
1622 ppd
->variable_sizes
? "TRUE" : "FALSE",
1625 switch (ppd
->colorspace
)
1628 _cupsLangPuts(stdout
, " colorspace = PPD_CS_CMYK\n");
1631 _cupsLangPuts(stdout
, " colorspace = PPD_CS_CMY\n");
1634 _cupsLangPuts(stdout
, " colorspace = PPD_CS_GRAY\n");
1637 _cupsLangPuts(stdout
, " colorspace = PPD_CS_RGB\n");
1640 _cupsLangPuts(stdout
, " colorspace = <unknown>\n");
1644 _cupsLangPrintf(stdout
, " num_emulations = %d\n",
1645 ppd
->num_emulations
);
1646 for (j
= 0; j
< ppd
->num_emulations
; j
++)
1647 _cupsLangPrintf(stdout
, " emulations[%d] = %s\n",
1648 j
, ppd
->emulations
[j
].name
);
1650 _cupsLangPrintf(stdout
, " lang_encoding = %s\n",
1651 ppd
->lang_encoding
);
1652 _cupsLangPrintf(stdout
, " lang_version = %s\n",
1654 _cupsLangPrintf(stdout
, " modelname = %s\n", ppd
->modelname
);
1655 _cupsLangPrintf(stdout
, " ttrasterizer = %s\n",
1656 ppd
->ttrasterizer
== NULL
? "None" : ppd
->ttrasterizer
);
1657 _cupsLangPrintf(stdout
, " manufacturer = %s\n",
1659 _cupsLangPrintf(stdout
, " product = %s\n", ppd
->product
);
1660 _cupsLangPrintf(stdout
, " nickname = %s\n", ppd
->nickname
);
1661 _cupsLangPrintf(stdout
, " shortnickname = %s\n",
1662 ppd
->shortnickname
);
1663 _cupsLangPrintf(stdout
, " patches = %d bytes\n",
1664 ppd
->patches
== NULL
? 0 : (int)strlen(ppd
->patches
));
1666 _cupsLangPrintf(stdout
, " num_groups = %d\n", ppd
->num_groups
);
1667 for (j
= 0, group
= ppd
->groups
; j
< ppd
->num_groups
; j
++, group
++)
1669 _cupsLangPrintf(stdout
, " group[%d] = %s\n",
1672 for (k
= 0, option
= group
->options
; k
< group
->num_options
; k
++, option
++)
1674 _cupsLangPrintf(stdout
,
1675 " options[%d] = %s (%s) %s %s %.0f "
1677 k
, option
->keyword
, option
->text
, uis
[option
->ui
],
1678 sections
[option
->section
], option
->order
,
1679 option
->num_choices
);
1681 if (!strcmp(option
->keyword
, "PageSize") ||
1682 !strcmp(option
->keyword
, "PageRegion"))
1684 for (m
= option
->num_choices
, choice
= option
->choices
;
1688 size
= ppdPageSize(ppd
, choice
->choice
);
1691 _cupsLangPrintf(stdout
,
1693 choice
->choice
, choice
->text
);
1695 _cupsLangPrintf(stdout
,
1696 " %s (%s) = %.2fx%.2fin "
1697 "(%.1f,%.1f,%.1f,%.1f)",
1698 choice
->choice
, choice
->text
,
1699 size
->width
/ 72.0, size
->length
/ 72.0,
1700 size
->left
/ 72.0, size
->bottom
/ 72.0,
1701 size
->right
/ 72.0, size
->top
/ 72.0);
1703 if (!strcmp(option
->defchoice
, choice
->choice
))
1704 _cupsLangPuts(stdout
, " *\n");
1706 _cupsLangPuts(stdout
, "\n");
1711 for (m
= option
->num_choices
, choice
= option
->choices
;
1715 _cupsLangPrintf(stdout
, " %s (%s)",
1716 choice
->choice
, choice
->text
);
1718 if (!strcmp(option
->defchoice
, choice
->choice
))
1719 _cupsLangPuts(stdout
, " *\n");
1721 _cupsLangPuts(stdout
, "\n");
1727 _cupsLangPrintf(stdout
, " num_consts = %d\n",
1729 for (j
= 0; j
< ppd
->num_consts
; j
++)
1730 _cupsLangPrintf(stdout
,
1731 " consts[%d] = *%s %s *%s %s\n",
1732 j
, ppd
->consts
[j
].option1
, ppd
->consts
[j
].choice1
,
1733 ppd
->consts
[j
].option2
, ppd
->consts
[j
].choice2
);
1735 _cupsLangPrintf(stdout
, " num_profiles = %d\n",
1737 for (j
= 0; j
< ppd
->num_profiles
; j
++)
1738 _cupsLangPrintf(stdout
,
1739 " profiles[%d] = %s/%s %.3f %.3f "
1740 "[ %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f ]\n",
1741 j
, ppd
->profiles
[j
].resolution
,
1742 ppd
->profiles
[j
].media_type
,
1743 ppd
->profiles
[j
].gamma
, ppd
->profiles
[j
].density
,
1744 ppd
->profiles
[j
].matrix
[0][0],
1745 ppd
->profiles
[j
].matrix
[0][1],
1746 ppd
->profiles
[j
].matrix
[0][2],
1747 ppd
->profiles
[j
].matrix
[1][0],
1748 ppd
->profiles
[j
].matrix
[1][1],
1749 ppd
->profiles
[j
].matrix
[1][2],
1750 ppd
->profiles
[j
].matrix
[2][0],
1751 ppd
->profiles
[j
].matrix
[2][1],
1752 ppd
->profiles
[j
].matrix
[2][2]);
1754 _cupsLangPrintf(stdout
, " num_fonts = %d\n", ppd
->num_fonts
);
1755 for (j
= 0; j
< ppd
->num_fonts
; j
++)
1756 _cupsLangPrintf(stdout
, " fonts[%d] = %s\n",
1759 _cupsLangPrintf(stdout
, " num_attrs = %d\n", ppd
->num_attrs
);
1760 for (j
= 0; j
< ppd
->num_attrs
; j
++)
1761 _cupsLangPrintf(stdout
,
1762 " attrs[%d] = %s %s%s%s: \"%s\"\n", j
,
1763 ppd
->attrs
[j
]->name
, ppd
->attrs
[j
]->spec
,
1764 ppd
->attrs
[j
]->text
[0] ? "/" : "",
1765 ppd
->attrs
[j
]->text
,
1766 ppd
->attrs
[j
]->value
?
1767 ppd
->attrs
[j
]->value
: "(null)");
1781 * 'check_basics()' - Check for CR LF, mixed line endings, and blank lines.
1785 check_basics(const char *filename
) /* I - PPD file to check */
1787 cups_file_t
*fp
; /* File pointer */
1788 int ch
; /* Current character */
1789 int col
, /* Current column */
1790 whitespace
; /* Only seen whitespace? */
1791 int eol
; /* Line endings */
1792 int linenum
; /* Line number */
1793 int mixed
; /* Mixed line endings? */
1796 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
1805 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
1807 if (ch
== '\r' || ch
== '\n')
1811 if (eol
== EOL_NONE
)
1813 else if (eol
!= EOL_LF
)
1816 else if (ch
== '\r')
1818 if (cupsFilePeekChar(fp
) == '\n')
1820 cupsFileGetChar(fp
);
1822 if (eol
== EOL_NONE
)
1824 else if (eol
!= EOL_CRLF
)
1827 else if (eol
== EOL_NONE
)
1829 else if (eol
!= EOL_CR
)
1833 if (col
> 0 && whitespace
)
1834 _cupsLangPrintf(stdout
,
1835 _(" WARN Line %d only contains whitespace!\n"),
1844 if (ch
!= ' ' && ch
!= '\t')
1852 _cupsLangPuts(stdout
,
1853 _(" WARN File contains a mix of CR, LF, and "
1854 "CR LF line endings!\n"));
1856 if (eol
== EOL_CRLF
)
1857 _cupsLangPuts(stdout
,
1858 _(" WARN Non-Windows PPD files should use lines "
1859 "ending with only LF, not CR LF!\n"));
1866 * 'check_constraints()' - Check UIConstraints in the PPD file.
1869 static int /* O - Errors found */
1870 check_constraints(ppd_file_t
*ppd
, /* I - PPD file */
1871 int errors
, /* I - Errors found */
1872 int verbose
, /* I - Verbosity level */
1873 int warn
) /* I - Warnings only? */
1875 int i
; /* Looping var */
1876 const char *prefix
; /* WARN/FAIL prefix */
1877 ppd_const_t
*c
; /* Current UIConstraints data */
1878 ppd_attr_t
*constattr
; /* Current cupsUIConstraints attribute */
1879 const char *vptr
; /* Pointer into constraint value */
1880 char option
[PPD_MAX_NAME
],
1881 /* Option name/MainKeyword */
1882 choice
[PPD_MAX_NAME
],
1883 /* Choice/OptionKeyword */
1884 *ptr
; /* Pointer into option or choice */
1885 int num_options
; /* Number of options */
1886 cups_option_t
*options
; /* Options */
1887 ppd_option_t
*o
; /* PPD option */
1890 prefix
= warn
? " WARN " : "**FAIL**";
1894 * See what kind of constraint data we have in the PPD...
1897 if ((constattr
= ppdFindAttr(ppd
, "cupsUIConstraints", NULL
)) != NULL
)
1900 * Check new-style cupsUIConstraints data...
1904 constattr
= ppdFindNextAttr(ppd
, "cupsUIConstraints", NULL
))
1906 if (!constattr
->value
)
1908 if (!warn
&& !errors
&& !verbose
)
1909 _cupsLangPuts(stdout
, _(" FAIL\n"));
1911 _cupsLangPrintf(stdout
,
1912 _(" %s Empty cupsUIConstraints %s!\n"),
1913 prefix
, constattr
->spec
);
1921 for (i
= 0, vptr
= strchr(constattr
->value
, '*');
1923 i
++, vptr
= strchr(vptr
+ 1, '*'));
1927 if (!warn
&& !errors
&& !verbose
)
1928 _cupsLangPuts(stdout
, _(" FAIL\n"));
1930 _cupsLangPrintf(stdout
,
1931 _(" %s Bad cupsUIConstraints %s: \"%s\"!\n"),
1932 prefix
, constattr
->spec
, constattr
->value
);
1940 cupsArraySave(ppd
->sorted_attrs
);
1942 if (constattr
->spec
[0] &&
1943 !ppdFindAttr(ppd
, "cupsUIResolver", constattr
->spec
))
1945 if (!warn
&& !errors
&& !verbose
)
1946 _cupsLangPuts(stdout
, _(" FAIL\n"));
1948 _cupsLangPrintf(stdout
,
1949 _(" %s Missing cupsUIResolver %s!\n"),
1950 prefix
, constattr
->spec
);
1956 cupsArrayRestore(ppd
->sorted_attrs
);
1961 for (vptr
= strchr(constattr
->value
, '*');
1963 vptr
= strchr(vptr
, '*'))
1966 * Extract "*Option Choice" or just "*Option"...
1969 for (vptr
++, ptr
= option
; *vptr
&& !isspace(*vptr
& 255); vptr
++)
1970 if (ptr
< (option
+ sizeof(option
) - 1))
1975 while (isspace(*vptr
& 255))
1982 for (ptr
= choice
; *vptr
&& !isspace(*vptr
& 255); vptr
++)
1983 if (ptr
< (choice
+ sizeof(choice
) - 1))
1989 if (!strncasecmp(option
, "Custom", 6) && !strcasecmp(choice
, "True"))
1991 _cups_strcpy(option
, option
+ 6);
1992 strcpy(choice
, "Custom");
1995 if ((o
= ppdFindOption(ppd
, option
)) == NULL
)
1997 if (!warn
&& !errors
&& !verbose
)
1998 _cupsLangPuts(stdout
, _(" FAIL\n"));
2000 _cupsLangPrintf(stdout
,
2001 _(" %s Missing option %s in "
2002 "cupsUIConstraints %s: \"%s\"!\n"),
2003 prefix
, option
, constattr
->spec
, constattr
->value
);
2011 if (choice
[0] && !ppdFindChoice(o
, choice
))
2013 if (!warn
&& !errors
&& !verbose
)
2014 _cupsLangPuts(stdout
, _(" FAIL\n"));
2016 _cupsLangPrintf(stdout
,
2017 _(" %s Missing choice *%s %s in "
2018 "cupsUIConstraints %s: \"%s\"!\n"),
2019 prefix
, option
, choice
, constattr
->spec
,
2029 num_options
= cupsAddOption(option
, choice
, num_options
, &options
);
2032 for (i
= 0; i
< o
->num_choices
; i
++)
2033 if (strcasecmp(o
->choices
[i
].choice
, "None") &&
2034 strcasecmp(o
->choices
[i
].choice
, "Off") &&
2035 strcasecmp(o
->choices
[i
].choice
, "False"))
2037 num_options
= cupsAddOption(option
, o
->choices
[i
].choice
,
2038 num_options
, &options
);
2045 * Resolvers must list at least two options...
2048 if (num_options
< 2)
2050 if (!warn
&& !errors
&& !verbose
)
2051 _cupsLangPuts(stdout
, _(" FAIL\n"));
2053 _cupsLangPrintf(stdout
,
2054 _(" %s cupsUIResolver %s does not list at least "
2055 "two different options!\n"),
2056 prefix
, constattr
->spec
);
2063 * Test the resolver...
2066 if (!cupsResolveConflicts(ppd
, NULL
, NULL
, &num_options
, &options
))
2068 if (!warn
&& !errors
&& !verbose
)
2069 _cupsLangPuts(stdout
, _(" FAIL\n"));
2071 _cupsLangPrintf(stdout
,
2072 _(" %s cupsUIResolver %s causes a loop!\n"),
2073 prefix
, constattr
->spec
);
2079 cupsFreeOptions(num_options
, options
);
2085 * Check old-style [Non]UIConstraints data...
2088 for (i
= ppd
->num_consts
, c
= ppd
->consts
; i
> 0; i
--, c
++)
2090 if (!strncasecmp(c
->option1
, "Custom", 6) &&
2091 !strcasecmp(c
->choice1
, "True"))
2093 strcpy(option
, c
->option1
+ 6);
2094 strcpy(choice
, "Custom");
2098 strcpy(option
, c
->option1
);
2099 strcpy(choice
, c
->choice1
);
2102 if ((o
= ppdFindOption(ppd
, option
)) == NULL
)
2104 if (!warn
&& !errors
&& !verbose
)
2105 _cupsLangPuts(stdout
, _(" FAIL\n"));
2107 _cupsLangPrintf(stdout
,
2108 _(" %s Missing option %s in "
2109 "UIConstraints \"*%s %s *%s %s\"!\n"),
2111 c
->option1
, c
->choice1
, c
->option2
, c
->choice2
);
2116 else if (choice
[0] && !ppdFindChoice(o
, choice
))
2118 if (!warn
&& !errors
&& !verbose
)
2119 _cupsLangPuts(stdout
, _(" FAIL\n"));
2121 _cupsLangPrintf(stdout
,
2122 _(" %s Missing choice *%s %s in "
2123 "UIConstraints \"*%s %s *%s %s\"!\n"),
2124 prefix
, c
->option1
, c
->choice1
,
2125 c
->option1
, c
->choice1
, c
->option2
, c
->choice2
);
2131 if (!strncasecmp(c
->option2
, "Custom", 6) &&
2132 !strcasecmp(c
->choice2
, "True"))
2134 strcpy(option
, c
->option2
+ 6);
2135 strcpy(choice
, "Custom");
2139 strcpy(option
, c
->option2
);
2140 strcpy(choice
, c
->choice2
);
2143 if ((o
= ppdFindOption(ppd
, option
)) == NULL
)
2145 if (!warn
&& !errors
&& !verbose
)
2146 _cupsLangPuts(stdout
, _(" FAIL\n"));
2148 _cupsLangPrintf(stdout
,
2149 _(" %s Missing option %s in "
2150 "UIConstraints \"*%s %s *%s %s\"!\n"),
2152 c
->option1
, c
->choice1
, c
->option2
, c
->choice2
);
2157 else if (choice
[0] && !ppdFindChoice(o
, choice
))
2159 if (!warn
&& !errors
&& !verbose
)
2160 _cupsLangPuts(stdout
, _(" FAIL\n"));
2162 _cupsLangPrintf(stdout
,
2163 _(" %s Missing choice *%s %s in "
2164 "UIConstraints \"*%s %s *%s %s\"!\n"),
2165 prefix
, c
->option2
, c
->choice2
,
2166 c
->option1
, c
->choice1
, c
->option2
, c
->choice2
);
2179 * 'check_case()' - Check that there are no duplicate groups, options,
2180 * or choices that differ only by case.
2183 static int /* O - Errors found */
2184 check_case(ppd_file_t
*ppd
, /* I - PPD file */
2185 int errors
, /* I - Errors found */
2186 int verbose
) /* I - Verbosity level */
2188 int i
, j
; /* Looping vars */
2189 ppd_group_t
*groupa
, /* First group */
2190 *groupb
; /* Second group */
2191 ppd_option_t
*optiona
, /* First option */
2192 *optionb
; /* Second option */
2193 ppd_choice_t
*choicea
, /* First choice */
2194 *choiceb
; /* Second choice */
2198 * Check that the groups do not have any duplicate names...
2201 for (i
= ppd
->num_groups
, groupa
= ppd
->groups
; i
> 1; i
--, groupa
++)
2202 for (j
= i
- 1, groupb
= groupa
+ 1; j
> 0; j
--, groupb
++)
2203 if (!strcasecmp(groupa
->name
, groupb
->name
))
2205 if (!errors
&& !verbose
)
2206 _cupsLangPuts(stdout
, _(" FAIL\n"));
2209 _cupsLangPrintf(stdout
,
2210 _(" **FAIL** Group names %s and %s differ only "
2212 groupa
->name
, groupb
->name
);
2218 * Check that the options do not have any duplicate names...
2221 for (optiona
= ppdFirstOption(ppd
); optiona
; optiona
= ppdNextOption(ppd
))
2223 cupsArraySave(ppd
->options
);
2224 for (optionb
= ppdNextOption(ppd
); optionb
; optionb
= ppdNextOption(ppd
))
2225 if (!strcasecmp(optiona
->keyword
, optionb
->keyword
))
2227 if (!errors
&& !verbose
)
2228 _cupsLangPuts(stdout
, _(" FAIL\n"));
2231 _cupsLangPrintf(stdout
,
2232 _(" **FAIL** Option names %s and %s differ only "
2234 optiona
->keyword
, optionb
->keyword
);
2238 cupsArrayRestore(ppd
->options
);
2241 * Then the choices...
2244 for (i
= optiona
->num_choices
, choicea
= optiona
->choices
;
2247 for (j
= i
- 1, choiceb
= choicea
+ 1; j
> 0; j
--, choiceb
++)
2248 if (!strcmp(choicea
->choice
, choiceb
->choice
))
2250 if (!errors
&& !verbose
)
2251 _cupsLangPuts(stdout
, _(" FAIL\n"));
2254 _cupsLangPrintf(stdout
,
2255 _(" **FAIL** Multiple occurrences of %s "
2256 "choice name %s!\n"),
2257 optiona
->keyword
, choicea
->choice
);
2265 else if (!strcasecmp(choicea
->choice
, choiceb
->choice
))
2267 if (!errors
&& !verbose
)
2268 _cupsLangPuts(stdout
, _(" FAIL\n"));
2271 _cupsLangPrintf(stdout
,
2272 _(" **FAIL** %s choice names %s and %s "
2273 "differ only by case!\n"),
2274 optiona
->keyword
, choicea
->choice
, choiceb
->choice
);
2281 * Return the number of errors found...
2289 * 'check_defaults()' - Check default option keywords in the PPD file.
2292 static int /* O - Errors found */
2293 check_defaults(ppd_file_t
*ppd
, /* I - PPD file */
2294 int errors
, /* I - Errors found */
2295 int verbose
, /* I - Verbosity level */
2296 int warn
) /* I - Warnings only? */
2298 int j
, k
; /* Looping vars */
2299 ppd_attr_t
*attr
; /* PPD attribute */
2300 ppd_option_t
*option
; /* Standard UI option */
2301 const char *prefix
; /* WARN/FAIL prefix */
2304 prefix
= warn
? " WARN " : "**FAIL**";
2306 for (j
= 0; j
< ppd
->num_attrs
; j
++)
2308 attr
= ppd
->attrs
[j
];
2310 if (!strcmp(attr
->name
, "DefaultColorSpace") ||
2311 !strcmp(attr
->name
, "DefaultFont") ||
2312 !strcmp(attr
->name
, "DefaultHalftoneType") ||
2313 !strcmp(attr
->name
, "DefaultImageableArea") ||
2314 !strcmp(attr
->name
, "DefaultLeadingEdge") ||
2315 !strcmp(attr
->name
, "DefaultOutputOrder") ||
2316 !strcmp(attr
->name
, "DefaultPaperDimension") ||
2317 !strcmp(attr
->name
, "DefaultResolution") ||
2318 !strcmp(attr
->name
, "DefaultTransfer"))
2321 if (!strncmp(attr
->name
, "Default", 7))
2323 if ((option
= ppdFindOption(ppd
, attr
->name
+ 7)) != NULL
&&
2324 strcmp(attr
->value
, "Unknown"))
2327 * Check that the default option value matches a choice...
2330 for (k
= 0; k
< option
->num_choices
; k
++)
2331 if (!strcmp(option
->choices
[k
].choice
, attr
->value
))
2334 if (k
>= option
->num_choices
)
2336 if (!warn
&& !errors
&& !verbose
)
2337 _cupsLangPuts(stdout
, _(" FAIL\n"));
2340 _cupsLangPrintf(stdout
,
2341 _(" %s %s %s does not exist!\n"),
2342 prefix
, attr
->name
, attr
->value
);
2356 * 'check_duplex()' - Check duplex keywords in the PPD file.
2359 static int /* O - Errors found */
2360 check_duplex(ppd_file_t
*ppd
, /* I - PPD file */
2361 int errors
, /* I - Error found */
2362 int verbose
, /* I - Verbosity level */
2363 int warn
) /* I - Warnings only? */
2365 int i
; /* Looping var */
2366 ppd_option_t
*option
; /* PPD option */
2367 ppd_choice_t
*choice
; /* Current choice */
2368 const char *prefix
; /* Message prefix */
2371 prefix
= warn
? " WARN " : "**FAIL**";
2374 * Check for a duplex option, and for standard values...
2377 if ((option
= ppdFindOption(ppd
, "Duplex")) != NULL
)
2379 if (!ppdFindChoice(option
, "None"))
2383 if (!warn
&& !errors
&& !verbose
)
2384 _cupsLangPuts(stdout
, _(" FAIL\n"));
2386 _cupsLangPrintf(stdout
,
2387 _(" %s REQUIRED %s does not define "
2389 " REF: Page 122, section 5.17\n"),
2390 prefix
, option
->keyword
);
2397 for (i
= option
->num_choices
, choice
= option
->choices
;
2400 if (strcmp(choice
->choice
, "None") &&
2401 strcmp(choice
->choice
, "DuplexNoTumble") &&
2402 strcmp(choice
->choice
, "DuplexTumble") &&
2403 strcmp(choice
->choice
, "SimplexTumble"))
2407 if (!warn
&& !errors
&& !verbose
)
2408 _cupsLangPuts(stdout
, _(" FAIL\n"));
2410 _cupsLangPrintf(stdout
,
2411 _(" %s Bad %s choice %s!\n"
2412 " REF: Page 122, section 5.17\n"),
2413 prefix
, option
->keyword
, choice
->choice
);
2426 * 'check_filters()' - Check filters in the PPD file.
2429 static int /* O - Errors found */
2430 check_filters(ppd_file_t
*ppd
, /* I - PPD file */
2431 const char *root
, /* I - Root directory */
2432 int errors
, /* I - Errors found */
2433 int verbose
, /* I - Verbosity level */
2434 int warn
) /* I - Warnings only? */
2436 int i
; /* Looping var */
2437 ppd_attr_t
*attr
; /* PPD attribute */
2438 const char *ptr
; /* Pointer into string */
2439 char super
[16], /* Super-type for filter */
2440 type
[256], /* Type for filter */
2441 program
[1024], /* Program/filter name */
2442 pathprog
[1024]; /* Complete path to program/filter */
2443 int cost
; /* Cost of filter */
2444 const char *prefix
; /* WARN/FAIL prefix */
2445 struct stat fileinfo
; /* File information */
2448 prefix
= warn
? " WARN " : "**FAIL**";
2454 for (i
= 0; i
< ppd
->num_filters
; i
++)
2456 if (sscanf(ppd
->filters
[i
], "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super
, type
,
2457 &cost
, program
) != 4)
2459 if (!warn
&& !errors
&& !verbose
)
2460 _cupsLangPuts(stdout
, _(" FAIL\n"));
2463 _cupsLangPrintf(stdout
,
2464 _(" %s Bad cupsFilter value \"%s\"!\n"),
2465 prefix
, ppd
->filters
[i
]);
2470 else if (strcmp(program
, "-"))
2472 if (program
[0] == '/')
2473 snprintf(pathprog
, sizeof(pathprog
), "%s%s", root
, program
);
2476 if ((ptr
= getenv("CUPS_SERVERBIN")) == NULL
)
2477 ptr
= CUPS_SERVERBIN
;
2479 if (*ptr
== '/' || !*root
)
2480 snprintf(pathprog
, sizeof(pathprog
), "%s%s/filter/%s", root
, ptr
,
2483 snprintf(pathprog
, sizeof(pathprog
), "%s/%s/filter/%s", root
, ptr
,
2487 if (stat(pathprog
, &fileinfo
))
2489 if (!warn
&& !errors
&& !verbose
)
2490 _cupsLangPuts(stdout
, _(" FAIL\n"));
2493 _cupsLangPrintf(stdout
, _(" %s Missing cupsFilter "
2494 "file \"%s\"\n"), prefix
, pathprog
);
2499 else if (fileinfo
.st_uid
!= 0 ||
2500 (fileinfo
.st_mode
& MODE_WRITE
) ||
2501 (fileinfo
.st_mode
& MODE_MASK
) != MODE_PROGRAM
)
2503 if (!warn
&& !errors
&& !verbose
)
2504 _cupsLangPuts(stdout
, _(" FAIL\n"));
2507 _cupsLangPrintf(stdout
, _(" %s Bad permissions on cupsFilter "
2508 "file \"%s\"\n"), prefix
, pathprog
);
2514 errors
= valid_path("cupsFilter", pathprog
, errors
, verbose
, warn
);
2522 for (attr
= ppdFindAttr(ppd
, "cupsPreFilter", NULL
);
2524 attr
= ppdFindNextAttr(ppd
, "cupsPreFilter", NULL
))
2526 if (strcmp(attr
->name
, "cupsPreFilter"))
2528 if (!warn
&& !errors
&& !verbose
)
2529 _cupsLangPuts(stdout
, _(" FAIL\n"));
2532 _cupsLangPrintf(stdout
,
2533 _(" %s Bad spelling of %s - should be %s!\n"),
2534 prefix
, attr
->name
, "cupsPreFilter");
2541 sscanf(attr
->value
, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super
, type
,
2542 &cost
, program
) != 4)
2544 if (!warn
&& !errors
&& !verbose
)
2545 _cupsLangPuts(stdout
, _(" FAIL\n"));
2548 _cupsLangPrintf(stdout
,
2549 _(" %s Bad cupsPreFilter value \"%s\"!\n"),
2550 prefix
, attr
->value
? attr
->value
: "");
2555 else if (strcmp(program
, "-"))
2557 if (program
[0] == '/')
2558 snprintf(pathprog
, sizeof(pathprog
), "%s%s", root
, program
);
2561 if ((ptr
= getenv("CUPS_SERVERBIN")) == NULL
)
2562 ptr
= CUPS_SERVERBIN
;
2564 if (*ptr
== '/' || !*root
)
2565 snprintf(pathprog
, sizeof(pathprog
), "%s%s/filter/%s", root
, ptr
,
2568 snprintf(pathprog
, sizeof(pathprog
), "%s/%s/filter/%s", root
, ptr
,
2572 if (stat(pathprog
, &fileinfo
))
2574 if (!warn
&& !errors
&& !verbose
)
2575 _cupsLangPuts(stdout
, _(" FAIL\n"));
2578 _cupsLangPrintf(stdout
, _(" %s Missing cupsPreFilter "
2579 "file \"%s\"\n"), prefix
, pathprog
);
2584 else if (fileinfo
.st_uid
!= 0 ||
2585 (fileinfo
.st_mode
& MODE_WRITE
) ||
2586 (fileinfo
.st_mode
& MODE_MASK
) != MODE_PROGRAM
)
2588 if (!warn
&& !errors
&& !verbose
)
2589 _cupsLangPuts(stdout
, _(" FAIL\n"));
2592 _cupsLangPrintf(stdout
, _(" %s Bad permissions on "
2593 "cupsPreFilter file \"%s\"\n"), prefix
,
2600 errors
= valid_path("cupsPreFilter", pathprog
, errors
, verbose
, warn
);
2609 for (attr
= ppdFindAttr(ppd
, "APDialogExtension", NULL
);
2611 attr
= ppdFindNextAttr(ppd
, "APDialogExtension", NULL
))
2613 if (strcmp(attr
->name
, "APDialogExtension"))
2615 if (!warn
&& !errors
&& !verbose
)
2616 _cupsLangPuts(stdout
, _(" FAIL\n"));
2619 _cupsLangPrintf(stdout
,
2620 _(" %s Bad spelling of %s - should be %s!\n"),
2621 prefix
, attr
->name
, "APDialogExtension");
2627 snprintf(pathprog
, sizeof(pathprog
), "%s%s", root
,
2628 attr
->value
? attr
->value
: "(null)");
2630 if (!attr
->value
|| stat(pathprog
, &fileinfo
))
2632 if (!warn
&& !errors
&& !verbose
)
2633 _cupsLangPuts(stdout
, _(" FAIL\n"));
2636 _cupsLangPrintf(stdout
, _(" %s Missing "
2637 "APDialogExtension file \"%s\"\n"),
2643 else if (fileinfo
.st_uid
!= 0 ||
2644 (fileinfo
.st_mode
& MODE_WRITE
) ||
2645 (fileinfo
.st_mode
& MODE_MASK
) != MODE_DIRECTORY
)
2647 if (!warn
&& !errors
&& !verbose
)
2648 _cupsLangPuts(stdout
, _(" FAIL\n"));
2651 _cupsLangPrintf(stdout
, _(" %s Bad permissions on "
2652 "APDialogExtension file \"%s\"\n"), prefix
,
2659 errors
= valid_path("APDialogExtension", pathprog
, errors
, verbose
,
2667 if ((attr
= ppdFindAttr(ppd
, "APPrinterIconPath", NULL
)) != NULL
)
2669 if (strcmp(attr
->name
, "APPrinterIconPath"))
2671 if (!warn
&& !errors
&& !verbose
)
2672 _cupsLangPuts(stdout
, _(" FAIL\n"));
2675 _cupsLangPrintf(stdout
,
2676 _(" %s Bad spelling of %s - should be %s!\n"),
2677 prefix
, attr
->name
, "APPrinterIconPath");
2683 snprintf(pathprog
, sizeof(pathprog
), "%s%s", root
,
2684 attr
->value
? attr
->value
: "(null)");
2686 if (!attr
->value
|| stat(pathprog
, &fileinfo
))
2688 if (!warn
&& !errors
&& !verbose
)
2689 _cupsLangPuts(stdout
, _(" FAIL\n"));
2692 _cupsLangPrintf(stdout
, _(" %s Missing "
2693 "APPrinterIconPath file \"%s\"\n"),
2699 else if (fileinfo
.st_uid
!= 0 ||
2700 (fileinfo
.st_mode
& MODE_WRITE
) ||
2701 (fileinfo
.st_mode
& MODE_MASK
) != MODE_DATAFILE
)
2703 if (!warn
&& !errors
&& !verbose
)
2704 _cupsLangPuts(stdout
, _(" FAIL\n"));
2707 _cupsLangPrintf(stdout
, _(" %s Bad permissions on "
2708 "APPrinterIconPath file \"%s\"\n"), prefix
,
2715 errors
= valid_path("APPrinterIconPath", pathprog
, errors
, verbose
,
2720 * APPrinterLowInkTool
2723 if ((attr
= ppdFindAttr(ppd
, "APPrinterLowInkTool", NULL
)) != NULL
)
2725 if (strcmp(attr
->name
, "APPrinterLowInkTool"))
2727 if (!warn
&& !errors
&& !verbose
)
2728 _cupsLangPuts(stdout
, _(" FAIL\n"));
2731 _cupsLangPrintf(stdout
,
2732 _(" %s Bad spelling of %s - should be %s!\n"),
2733 prefix
, attr
->name
, "APPrinterLowInkTool");
2739 snprintf(pathprog
, sizeof(pathprog
), "%s%s", root
,
2740 attr
->value
? attr
->value
: "(null)");
2742 if (!attr
->value
|| stat(pathprog
, &fileinfo
))
2744 if (!warn
&& !errors
&& !verbose
)
2745 _cupsLangPuts(stdout
, _(" FAIL\n"));
2748 _cupsLangPrintf(stdout
, _(" %s Missing "
2749 "APPrinterLowInkTool file \"%s\"\n"),
2755 else if (fileinfo
.st_uid
!= 0 ||
2756 (fileinfo
.st_mode
& MODE_WRITE
) ||
2757 (fileinfo
.st_mode
& MODE_MASK
) != MODE_DIRECTORY
)
2759 if (!warn
&& !errors
&& !verbose
)
2760 _cupsLangPuts(stdout
, _(" FAIL\n"));
2763 _cupsLangPrintf(stdout
, _(" %s Bad permissions on "
2764 "APPrinterLowInkTool file \"%s\"\n"), prefix
,
2771 errors
= valid_path("APPrinterLowInkTool", pathprog
, errors
, verbose
,
2776 * APPrinterUtilityPath
2779 if ((attr
= ppdFindAttr(ppd
, "APPrinterUtilityPath", NULL
)) != NULL
)
2781 if (strcmp(attr
->name
, "APPrinterUtilityPath"))
2783 if (!warn
&& !errors
&& !verbose
)
2784 _cupsLangPuts(stdout
, _(" FAIL\n"));
2787 _cupsLangPrintf(stdout
,
2788 _(" %s Bad spelling of %s - should be %s!\n"),
2789 prefix
, attr
->name
, "APPrinterUtilityPath");
2795 snprintf(pathprog
, sizeof(pathprog
), "%s%s", root
,
2796 attr
->value
? attr
->value
: "(null)");
2798 if (!attr
->value
|| stat(pathprog
, &fileinfo
))
2800 if (!warn
&& !errors
&& !verbose
)
2801 _cupsLangPuts(stdout
, _(" FAIL\n"));
2804 _cupsLangPrintf(stdout
, _(" %s Missing "
2805 "APPrinterUtilityPath file \"%s\"\n"),
2811 else if (fileinfo
.st_uid
!= 0 ||
2812 (fileinfo
.st_mode
& MODE_WRITE
) ||
2813 (fileinfo
.st_mode
& MODE_MASK
) != MODE_DIRECTORY
)
2815 if (!warn
&& !errors
&& !verbose
)
2816 _cupsLangPuts(stdout
, _(" FAIL\n"));
2819 _cupsLangPrintf(stdout
, _(" %s Bad permissions on "
2820 "APPrinterUtilityPath file \"%s\"\n"), prefix
,
2827 errors
= valid_path("APPrinterUtilityPath", pathprog
, errors
, verbose
,
2832 * APScanAppBundleID and APScanAppPath
2835 if ((attr
= ppdFindAttr(ppd
, "APScanAppPath", NULL
)) != NULL
)
2837 if (strcmp(attr
->name
, "APScanAppPath"))
2839 if (!warn
&& !errors
&& !verbose
)
2840 _cupsLangPuts(stdout
, _(" FAIL\n"));
2843 _cupsLangPrintf(stdout
,
2844 _(" %s Bad spelling of %s - should be %s!\n"),
2845 prefix
, attr
->name
, "APScanAppPath");
2851 if (!attr
->value
|| stat(attr
->value
, &fileinfo
))
2853 if (!warn
&& !errors
&& !verbose
)
2854 _cupsLangPuts(stdout
, _(" FAIL\n"));
2857 _cupsLangPrintf(stdout
, _(" %s Missing "
2858 "APScanAppPath file \"%s\"\n"),
2859 prefix
, attr
->value
? attr
->value
: "<NULL>");
2864 else if (fileinfo
.st_uid
!= 0 ||
2865 (fileinfo
.st_mode
& MODE_WRITE
) ||
2866 (fileinfo
.st_mode
& MODE_MASK
) != MODE_DIRECTORY
)
2868 if (!warn
&& !errors
&& !verbose
)
2869 _cupsLangPuts(stdout
, _(" FAIL\n"));
2872 _cupsLangPrintf(stdout
, _(" %s Bad permissions on "
2873 "APScanAppPath file \"%s\"\n"), prefix
,
2880 errors
= valid_path("APScanAppPath", attr
->value
, errors
, verbose
,
2883 if (ppdFindAttr(ppd
, "APScanAppBundleID", NULL
))
2885 if (!warn
&& !errors
&& !verbose
)
2886 _cupsLangPuts(stdout
, _(" FAIL\n"));
2889 _cupsLangPrintf(stdout
, _(" %s Cannot provide both "
2890 "APScanAppPath and APScanAppBundleID!\n"),
2897 #endif /* __APPLE__ */
2904 * 'check_profiles()' - Check ICC color profiles in the PPD file.
2907 static int /* O - Errors found */
2908 check_profiles(ppd_file_t
*ppd
, /* I - PPD file */
2909 const char *root
, /* I - Root directory */
2910 int errors
, /* I - Errors found */
2911 int verbose
, /* I - Verbosity level */
2912 int warn
) /* I - Warnings only? */
2914 int i
; /* Looping var */
2915 ppd_attr_t
*attr
; /* PPD attribute */
2916 const char *ptr
; /* Pointer into string */
2917 const char *prefix
; /* WARN/FAIL prefix */
2918 char filename
[1024]; /* Profile filename */
2919 struct stat fileinfo
; /* File information */
2920 int num_profiles
= 0; /* Number of profiles */
2921 unsigned hash
, /* Current hash value */
2922 hashes
[1000]; /* Hash values of profile names */
2923 const char *specs
[1000]; /* Specifiers for profiles */
2926 prefix
= warn
? " WARN " : "**FAIL**";
2928 for (attr
= ppdFindAttr(ppd
, "cupsICCProfile", NULL
);
2930 attr
= ppdFindNextAttr(ppd
, "cupsICCProfile", NULL
))
2933 * Check for valid selector...
2936 for (i
= 0, ptr
= strchr(attr
->spec
, '.'); ptr
; ptr
= strchr(ptr
+ 1, '.'))
2939 if (!attr
->value
|| i
< 2)
2941 if (!warn
&& !errors
&& !verbose
)
2942 _cupsLangPuts(stdout
, _(" FAIL\n"));
2945 _cupsLangPrintf(stdout
,
2946 _(" %s Bad cupsICCProfile %s!\n"),
2947 prefix
, attr
->spec
);
2956 * Check for valid profile filename...
2959 if (attr
->value
[0] == '/')
2960 snprintf(filename
, sizeof(filename
), "%s%s", root
, attr
->value
);
2963 if ((ptr
= getenv("CUPS_DATADIR")) == NULL
)
2966 if (*ptr
== '/' || !*root
)
2967 snprintf(filename
, sizeof(filename
), "%s%s/profiles/%s", root
, ptr
,
2970 snprintf(filename
, sizeof(filename
), "%s/%s/profiles/%s", root
, ptr
,
2974 if (stat(filename
, &fileinfo
))
2976 if (!warn
&& !errors
&& !verbose
)
2977 _cupsLangPuts(stdout
, _(" FAIL\n"));
2980 _cupsLangPrintf(stdout
, _(" %s Missing cupsICCProfile "
2981 "file \"%s\"!\n"), prefix
, filename
);
2986 else if (fileinfo
.st_uid
!= 0 ||
2987 (fileinfo
.st_mode
& MODE_WRITE
) ||
2988 (fileinfo
.st_mode
& MODE_MASK
) != MODE_DATAFILE
)
2990 if (!warn
&& !errors
&& !verbose
)
2991 _cupsLangPuts(stdout
, _(" FAIL\n"));
2994 _cupsLangPrintf(stdout
, _(" %s Bad permissions on "
2995 "cupsICCProfile file \"%s\"\n"), prefix
,
3002 errors
= valid_path("cupsICCProfile", filename
, errors
, verbose
, warn
);
3005 * Check for hash collisions...
3008 hash
= _ppdHashName(attr
->spec
);
3010 if (num_profiles
> 0)
3012 for (i
= 0; i
< num_profiles
; i
++)
3013 if (hashes
[i
] == hash
)
3016 if (i
< num_profiles
)
3018 if (!warn
&& !errors
&& !verbose
)
3019 _cupsLangPuts(stdout
, _(" FAIL\n"));
3022 _cupsLangPrintf(stdout
,
3023 _(" %s cupsICCProfile %s hash value "
3024 "collides with %s!\n"), prefix
, attr
->spec
,
3033 * Remember up to 1000 profiles...
3036 if (num_profiles
< 1000)
3038 hashes
[num_profiles
] = hash
;
3039 specs
[num_profiles
] = attr
->spec
;
3049 * 'check_sizes()' - Check media sizes in the PPD file.
3052 static int /* O - Errors found */
3053 check_sizes(ppd_file_t
*ppd
, /* I - PPD file */
3054 int errors
, /* I - Errors found */
3055 int verbose
, /* I - Verbosity level */
3056 int warn
) /* I - Warnings only? */
3058 int i
; /* Looping vars */
3059 ppd_size_t
*size
; /* Current size */
3060 int width
, /* Custom width */
3061 length
; /* Custom length */
3062 char name
[PPD_MAX_NAME
], /* Size name without dot suffix */
3063 *nameptr
; /* Pointer into name */
3064 const char *prefix
; /* WARN/FAIL prefix */
3065 ppd_option_t
*page_size
, /* PageSize option */
3066 *page_region
; /* PageRegion option */
3069 prefix
= warn
? " WARN " : "**FAIL**";
3071 if ((page_size
= ppdFindOption(ppd
, "PageSize")) == NULL
&& warn
!= 2)
3073 if (!warn
&& !errors
&& !verbose
)
3074 _cupsLangPuts(stdout
, _(" FAIL\n"));
3077 _cupsLangPrintf(stdout
,
3078 _(" %s Missing REQUIRED PageSize option!\n"
3079 " REF: Page 99, section 5.14.\n"),
3086 if ((page_region
= ppdFindOption(ppd
, "PageRegion")) == NULL
&& warn
!= 2)
3088 if (!warn
&& !errors
&& !verbose
)
3089 _cupsLangPuts(stdout
, _(" FAIL\n"));
3092 _cupsLangPrintf(stdout
,
3093 _(" %s Missing REQUIRED PageRegion option!\n"
3094 " REF: Page 100, section 5.14.\n"),
3101 for (i
= ppd
->num_sizes
, size
= ppd
->sizes
; i
> 0; i
--, size
++)
3104 * Check that the size name is standard...
3107 if (!strcmp(size
->name
, "Custom"))
3110 * Skip custom page size...
3115 else if (warn
!= 2 && size
->name
[0] == 'w' &&
3116 sscanf(size
->name
, "w%dh%d", &width
, &length
) == 2)
3119 * Validate device-specific size wNNNhNNN should have proper width and
3123 if (fabs(width
- size
->width
) >= 1.0 ||
3124 fabs(length
- size
->length
) >= 1.0)
3126 if (!warn
&& !errors
&& !verbose
)
3127 _cupsLangPuts(stdout
, _(" FAIL\n"));
3130 _cupsLangPrintf(stdout
,
3131 _(" %s Size \"%s\" has unexpected dimensions "
3133 prefix
, size
->name
, size
->width
, size
->length
);
3139 else if (warn
&& verbose
>= 0)
3142 * Lookup the size name in the standard size table...
3145 strlcpy(name
, size
->name
, sizeof(name
));
3146 if ((nameptr
= strchr(name
, '.')) != NULL
)
3149 if (!bsearch(name
, adobe_size_names
,
3150 sizeof(adobe_size_names
) /
3151 sizeof(adobe_size_names
[0]),
3152 sizeof(adobe_size_names
[0]),
3153 (int (*)(const void *, const void *))strcmp
))
3155 _cupsLangPrintf(stdout
,
3156 _(" %s Non-standard size name \"%s\"!\n"
3157 " REF: Page 187, section B.2.\n"),
3158 prefix
, size
->name
);
3163 * Verify that the size is defined for both PageSize and PageRegion...
3166 if (warn
!= 2 && !ppdFindChoice(page_size
, size
->name
))
3168 if (!warn
&& !errors
&& !verbose
)
3169 _cupsLangPuts(stdout
, _(" FAIL\n"));
3172 _cupsLangPrintf(stdout
,
3173 _(" %s Size \"%s\" defined for %s but not for "
3175 prefix
, size
->name
, "PageRegion", "PageSize");
3180 else if (warn
!= 2 && !ppdFindChoice(page_region
, size
->name
))
3182 if (!warn
&& !errors
&& !verbose
)
3183 _cupsLangPuts(stdout
, _(" FAIL\n"));
3186 _cupsLangPrintf(stdout
,
3187 _(" %s Size \"%s\" defined for %s but not for "
3189 prefix
, size
->name
, "PageSize", "PageRegion");
3201 * 'check_translations()' - Check translations in the PPD file.
3204 static int /* O - Errors found */
3205 check_translations(ppd_file_t
*ppd
, /* I - PPD file */
3206 int errors
, /* I - Errors found */
3207 int verbose
, /* I - Verbosity level */
3208 int warn
) /* I - Warnings only? */
3210 int j
; /* Looping var */
3211 ppd_attr_t
*attr
; /* PPD attribute */
3212 cups_array_t
*languages
; /* Array of languages */
3213 int langlen
; /* Length of language */
3214 char *language
, /* Current language */
3215 keyword
[PPD_MAX_NAME
], /* Localization keyword (full) */
3216 llkeyword
[PPD_MAX_NAME
],/* Localization keyword (base) */
3217 ckeyword
[PPD_MAX_NAME
], /* Custom option keyword (full) */
3218 cllkeyword
[PPD_MAX_NAME
];
3219 /* Custom option keyword (base) */
3220 ppd_option_t
*option
; /* Standard UI option */
3221 ppd_coption_t
*coption
; /* Custom option */
3222 ppd_cparam_t
*cparam
; /* Custom parameter */
3223 char ll
[3]; /* Base language */
3224 const char *prefix
; /* WARN/FAIL prefix */
3225 const char *text
; /* Pointer into UI text */
3228 prefix
= warn
? " WARN " : "**FAIL**";
3230 if ((languages
= _ppdGetLanguages(ppd
)) != NULL
)
3233 * This file contains localizations, check them...
3236 for (language
= (char *)cupsArrayFirst(languages
);
3238 language
= (char *)cupsArrayNext(languages
))
3240 langlen
= (int)strlen(language
);
3241 if (langlen
!= 2 && langlen
!= 5)
3243 if (!warn
&& !errors
&& !verbose
)
3244 _cupsLangPuts(stdout
, _(" FAIL\n"));
3247 _cupsLangPrintf(stdout
,
3248 _(" %s Bad language \"%s\"!\n"),
3257 if (!strcmp(language
, "en"))
3260 strlcpy(ll
, language
, sizeof(ll
));
3263 * Loop through all options and choices...
3266 for (option
= ppdFirstOption(ppd
);
3268 option
= ppdNextOption(ppd
))
3270 if (!strcmp(option
->keyword
, "PageRegion"))
3273 snprintf(keyword
, sizeof(keyword
), "%s.Translation", language
);
3274 snprintf(llkeyword
, sizeof(llkeyword
), "%s.Translation", ll
);
3276 if ((attr
= ppdFindAttr(ppd
, keyword
, option
->keyword
)) == NULL
&&
3277 (attr
= ppdFindAttr(ppd
, llkeyword
, option
->keyword
)) == NULL
)
3279 if (!warn
&& !errors
&& !verbose
)
3280 _cupsLangPuts(stdout
, _(" FAIL\n"));
3283 _cupsLangPrintf(stdout
,
3284 _(" %s Missing \"%s\" translation "
3285 "string for option %s!\n"),
3286 prefix
, language
, option
->keyword
);
3291 else if (!valid_utf8(attr
->text
))
3293 if (!warn
&& !errors
&& !verbose
)
3294 _cupsLangPuts(stdout
, _(" FAIL\n"));
3297 _cupsLangPrintf(stdout
,
3298 _(" %s Bad UTF-8 \"%s\" translation "
3299 "string for option %s!\n"),
3300 prefix
, language
, option
->keyword
);
3306 snprintf(keyword
, sizeof(keyword
), "%s.%s", language
,
3308 snprintf(llkeyword
, sizeof(llkeyword
), "%s.%s", ll
,
3311 for (j
= 0; j
< option
->num_choices
; j
++)
3314 * First see if this choice is a number; if so, don't require
3318 for (text
= option
->choices
[j
].text
; *text
; text
++)
3319 if (!strchr("0123456789-+.", *text
))
3326 * Check custom choices differently...
3329 if (!strcasecmp(option
->choices
[j
].choice
, "Custom") &&
3330 (coption
= ppdFindCustomOption(ppd
,
3331 option
->keyword
)) != NULL
)
3333 snprintf(ckeyword
, sizeof(ckeyword
), "%s.Custom%s",
3334 language
, option
->keyword
);
3336 if ((attr
= ppdFindAttr(ppd
, ckeyword
, "True")) != NULL
&&
3337 !valid_utf8(attr
->text
))
3339 if (!warn
&& !errors
&& !verbose
)
3340 _cupsLangPuts(stdout
, _(" FAIL\n"));
3343 _cupsLangPrintf(stdout
,
3344 _(" %s Bad UTF-8 \"%s\" "
3345 "translation string for option %s, "
3348 ckeyword
+ 1 + strlen(language
),
3355 if (strcasecmp(option
->keyword
, "PageSize"))
3357 for (cparam
= (ppd_cparam_t
*)cupsArrayFirst(coption
->params
);
3359 cparam
= (ppd_cparam_t
*)cupsArrayNext(coption
->params
))
3361 snprintf(ckeyword
, sizeof(ckeyword
), "%s.ParamCustom%s",
3362 language
, option
->keyword
);
3363 snprintf(cllkeyword
, sizeof(cllkeyword
), "%s.ParamCustom%s",
3364 ll
, option
->keyword
);
3366 if ((attr
= ppdFindAttr(ppd
, ckeyword
,
3367 cparam
->name
)) == NULL
&&
3368 (attr
= ppdFindAttr(ppd
, cllkeyword
,
3369 cparam
->name
)) == NULL
)
3371 if (!warn
&& !errors
&& !verbose
)
3372 _cupsLangPuts(stdout
, _(" FAIL\n"));
3375 _cupsLangPrintf(stdout
,
3376 _(" %s Missing \"%s\" "
3377 "translation string for option %s, "
3380 ckeyword
+ 1 + strlen(language
),
3386 else if (!valid_utf8(attr
->text
))
3388 if (!warn
&& !errors
&& !verbose
)
3389 _cupsLangPuts(stdout
, _(" FAIL\n"));
3392 _cupsLangPrintf(stdout
,
3393 _(" %s Bad UTF-8 \"%s\" "
3394 "translation string for option %s, "
3397 ckeyword
+ 1 + strlen(language
),
3406 else if ((attr
= ppdFindAttr(ppd
, keyword
,
3407 option
->choices
[j
].choice
)) == NULL
&&
3408 (attr
= ppdFindAttr(ppd
, llkeyword
,
3409 option
->choices
[j
].choice
)) == NULL
)
3411 if (!warn
&& !errors
&& !verbose
)
3412 _cupsLangPuts(stdout
, _(" FAIL\n"));
3415 _cupsLangPrintf(stdout
,
3416 _(" %s Missing \"%s\" "
3417 "translation string for option %s, "
3419 prefix
, language
, option
->keyword
,
3420 option
->choices
[j
].choice
);
3425 else if (!valid_utf8(attr
->text
))
3427 if (!warn
&& !errors
&& !verbose
)
3428 _cupsLangPuts(stdout
, _(" FAIL\n"));
3431 _cupsLangPrintf(stdout
,
3432 _(" %s Bad UTF-8 \"%s\" "
3433 "translation string for option %s, "
3435 prefix
, language
, option
->keyword
,
3436 option
->choices
[j
].choice
);
3446 * Verify that we have the base language for each localized one...
3449 for (language
= (char *)cupsArrayFirst(languages
);
3451 language
= (char *)cupsArrayNext(languages
))
3455 * Lookup the base language...
3458 cupsArraySave(languages
);
3460 strlcpy(ll
, language
, sizeof(ll
));
3462 if (!cupsArrayFind(languages
, ll
) &&
3463 strcmp(ll
, "zh") && strcmp(ll
, "en"))
3465 if (!warn
&& !errors
&& !verbose
)
3466 _cupsLangPuts(stdout
, _(" FAIL\n"));
3469 _cupsLangPrintf(stdout
,
3470 _(" %s No base translation \"%s\" "
3471 "is included in file!\n"), prefix
, ll
);
3477 cupsArrayRestore(languages
);
3481 * Free memory used for the languages...
3484 _ppdFreeLanguages(languages
);
3492 * 'show_conflicts()' - Show option conflicts in a PPD file.
3496 show_conflicts(ppd_file_t
*ppd
) /* I - PPD to check */
3498 int i
, j
; /* Looping variables */
3499 ppd_const_t
*c
; /* Current constraint */
3500 ppd_option_t
*o1
, *o2
; /* Options */
3501 ppd_choice_t
*c1
, *c2
; /* Choices */
3505 * Loop through all of the UI constraints and report any options
3509 for (i
= ppd
->num_consts
, c
= ppd
->consts
; i
> 0; i
--, c
++)
3512 * Grab pointers to the first option...
3515 o1
= ppdFindOption(ppd
, c
->option1
);
3519 else if (c
->choice1
[0] != '\0')
3522 * This constraint maps to a specific choice.
3525 c1
= ppdFindChoice(o1
, c
->choice1
);
3530 * This constraint applies to any choice for this option.
3533 for (j
= o1
->num_choices
, c1
= o1
->choices
; j
> 0; j
--, c1
++)
3538 !strcasecmp(c1
->choice
, "None") ||
3539 !strcasecmp(c1
->choice
, "Off") ||
3540 !strcasecmp(c1
->choice
, "False"))
3545 * Grab pointers to the second option...
3548 o2
= ppdFindOption(ppd
, c
->option2
);
3552 else if (c
->choice2
[0] != '\0')
3555 * This constraint maps to a specific choice.
3558 c2
= ppdFindChoice(o2
, c
->choice2
);
3563 * This constraint applies to any choice for this option.
3566 for (j
= o2
->num_choices
, c2
= o2
->choices
; j
> 0; j
--, c2
++)
3571 !strcasecmp(c2
->choice
, "None") ||
3572 !strcasecmp(c2
->choice
, "Off") ||
3573 !strcasecmp(c2
->choice
, "False"))
3578 * If both options are marked then there is a conflict...
3581 if (c1
!= NULL
&& c1
->marked
&& c2
!= NULL
&& c2
->marked
)
3582 _cupsLangPrintf(stdout
,
3583 _(" WARN \"%s %s\" conflicts with \"%s %s\"\n"
3584 " (constraint=\"%s %s %s %s\")\n"),
3585 o1
->keyword
, c1
->choice
, o2
->keyword
, c2
->choice
,
3586 c
->option1
, c
->choice1
, c
->option2
, c
->choice2
);
3592 * 'test_raster()' - Test PostScript commands for raster printers.
3595 static int /* O - 1 on success, 0 on failure */
3596 test_raster(ppd_file_t
*ppd
, /* I - PPD file */
3597 int verbose
) /* I - Verbosity */
3599 cups_page_header2_t header
; /* Page header */
3602 ppdMarkDefaults(ppd
);
3603 if (cupsRasterInterpretPPD(&header
, ppd
, 0, NULL
, 0))
3606 _cupsLangPuts(stdout
, _(" FAIL\n"));
3609 _cupsLangPrintf(stdout
,
3610 _(" **FAIL** Default option code cannot be "
3611 "interpreted: %s\n"), cupsRasterErrorString());
3617 * Try a test of custom page size code, if available...
3620 if (!ppdPageSize(ppd
, "Custom.612x792"))
3623 ppdMarkOption(ppd
, "PageSize", "Custom.612x792");
3625 if (cupsRasterInterpretPPD(&header
, ppd
, 0, NULL
, 0))
3628 _cupsLangPuts(stdout
, _(" FAIL\n"));
3631 _cupsLangPrintf(stdout
,
3632 _(" **FAIL** Default option code cannot be "
3633 "interpreted: %s\n"), cupsRasterErrorString());
3643 * 'usage()' - Show program usage...
3649 _cupsLangPuts(stdout
,
3650 _("Usage: cupstestppd [options] filename1.ppd[.gz] "
3651 "[... filenameN.ppd[.gz]]\n"
3652 " program | cupstestppd [options] -\n"
3656 " -I {filters,profiles}\n"
3657 " Ignore missing files\n"
3658 " -R root-directory Set alternate root\n"
3659 " -W {all,none,constraints,defaults,duplex,filters,"
3660 "profiles,sizes,translations}\n"
3661 " Issue warnings instead of errors\n"
3662 " -q Run silently\n"
3663 " -r Use 'relaxed' open mode\n"
3664 " -v Be slightly verbose\n"
3665 " -vv Be very verbose\n"));
3672 * 'valid_path()' - Check whether a path has the correct capitalization.
3675 static int /* O - Errors found */
3676 valid_path(const char *keyword
, /* I - Keyword using path */
3677 const char *path
, /* I - Path to check */
3678 int errors
, /* I - Errors found */
3679 int verbose
, /* I - Verbosity level */
3680 int warn
) /* I - Warnings only? */
3682 cups_dir_t
*dir
; /* Current directory */
3683 cups_dentry_t
*dentry
; /* Current directory entry */
3684 char temp
[1024], /* Temporary path */
3685 *ptr
; /* Pointer into temporary path */
3686 const char *prefix
; /* WARN/FAIL prefix */
3689 prefix
= warn
? " WARN " : "**FAIL**";
3692 * Loop over the components of the path, checking that the entry exists with
3693 * the same capitalization...
3696 strlcpy(temp
, path
, sizeof(temp
));
3698 while ((ptr
= strrchr(temp
, '/')) != NULL
)
3701 * Chop off the trailing component so temp == dirname and ptr == basename.
3707 * Try opening the directory containing the base name...
3711 dir
= cupsDirOpen(temp
);
3713 dir
= cupsDirOpen("/");
3719 while ((dentry
= cupsDirRead(dir
)) != NULL
)
3721 if (!strcmp(dentry
->filename
, ptr
))
3729 * Display an error if the filename doesn't exist with the same
3735 if (!warn
&& !errors
&& !verbose
)
3736 _cupsLangPuts(stdout
, _(" FAIL\n"));
3739 _cupsLangPrintf(stdout
,
3740 _(" %s %s file \"%s\" has the wrong "
3741 "capitalization!\n"), prefix
, keyword
, path
);
3755 * 'valid_utf8()' - Check whether a string contains valid UTF-8 text.
3758 static int /* O - 1 if valid, 0 if not */
3759 valid_utf8(const char *s
) /* I - String to check */
3766 * Check for valid UTF-8 sequence...
3769 if ((*s
& 0xc0) == 0x80)
3770 return (0); /* Illegal suffix byte */
3771 else if ((*s
& 0xe0) == 0xc0)
3774 * 2-byte sequence...
3779 if ((*s
& 0xc0) != 0x80)
3780 return (0); /* Missing suffix byte */
3782 else if ((*s
& 0xf0) == 0xe0)
3785 * 3-byte sequence...
3790 if ((*s
& 0xc0) != 0x80)
3791 return (0); /* Missing suffix byte */
3795 if ((*s
& 0xc0) != 0x80)
3796 return (0); /* Missing suffix byte */
3798 else if ((*s
& 0xf8) == 0xf0)
3801 * 4-byte sequence...
3806 if ((*s
& 0xc0) != 0x80)
3807 return (0); /* Missing suffix byte */
3811 if ((*s
& 0xc0) != 0x80)
3812 return (0); /* Missing suffix byte */
3816 if ((*s
& 0xc0) != 0x80)
3817 return (0); /* Missing suffix byte */
3820 return (0); /* Bad sequence */
3831 * End of "$Id: cupstestppd.c 7807 2008-07-28 21:54:24Z mike $".