/*
* DSC test program for CUPS.
*
* Copyright 2007-2010 by Apple Inc.
* Copyright 2006 by Easy Software Products, all rights reserved.
*
* These coded instructions, statements, and computer programs are the
* property of Apple Inc. and are protected by Federal copyright
* law. Distribution and use rights are outlined in the file "LICENSE.txt"
* which should have been included with this file. If this file is
* missing or damaged, see the license at "http://www.cups.org/".
*
* PostScript is a trademark of Adobe Systems, Inc.
*
* This file is subject to the Apple OS-Developed Software exception.
*/
/*
* Include necessary headers...
*/
#include <cups/cups-private.h>
/*
* Local functions...
*/
static int check_file(const char *filename);
static void usage(void) __attribute__((noreturn));
/*
* 'main()' - Main entry for test program.
*/
int /* O - Exit status */
main(int argc, /* I - Number of command-line args */
char *argv[]) /* I - Command-line arguments */
{
int i; /* Looping var */
int status; /* Status of tests */
int num_files; /* Number of files tested */
_cupsSetLocale(argv);
/*
* Collect command-line arguments...
*/
for (i = 1, num_files = 0, status = 0; i < argc; i ++)
if (argv[i][0] == '-')
{
if (argv[i][1])
{
/*
* Currently the only supported option is "-h" (help)...
*/
usage();
}
else
{
num_files ++;
status += check_file("(stdin)");
}
}
else
{
num_files ++;
status += check_file(argv[i]);
}
if (!num_files)
usage();
return (status);
}
/*
* 'check()' - Check a file for conformance.
*/
static int /* O - 0 on success, 1 on failure */
check_file(const char *filename) /* I - File to read from */
{
int i; /* Looping var */
cups_file_t *fp; /* File */
char line[1024]; /* Line from file */
int ch; /* Current character */
size_t bytes; /* Length of line */
int status; /* Status of test */
int linenum; /* Line number */
int binary; /* File contains binary data? */
float version; /* DSC version */
int lbrt[4]; /* Bounding box */
char page_label[256]; /* Page label string */
int page_number; /* Page number */
int last_page_number; /* Last page number seen */
int level; /* Embedded document level */
int saw_bounding_box, /* %%BoundingBox seen? */
saw_pages, /* %%Pages seen? */
saw_end_comments, /* %%EndComments seen? */
saw_begin_prolog, /* %%BeginProlog seen? */
saw_end_prolog, /* %%EndProlog seen? */
saw_begin_setup, /* %%BeginSetup seen? */
saw_end_setup, /* %%EndSetup seen? */
saw_page, /* %%Page seen? */
saw_trailer, /* %%Trailer seen? */
saw_long_line; /* Saw long lines? */
/*
* Open the file...
*/
if (!strcmp(filename, "(stdin)"))
fp = cupsFileStdin();
else
fp = cupsFileOpen(filename, "r");
if (!fp)
{
perror(filename);
return (1);
}
/*
* Scan the file...
*/
binary = 0;
last_page_number = 0;
level = 0;
linenum = 0;
saw_begin_prolog = 0;
saw_begin_setup = 0;
saw_bounding_box = 0;
saw_end_comments = 0;
saw_end_prolog = 0;
saw_end_setup = 0;
saw_long_line = 0;
saw_page = 0;
saw_pages = 0;
saw_trailer = 0;
status = 0;
version = 0.0f;
/* TODO: Fixme */
printf("%s: ", filename);
fflush(stdout);
while ((bytes = cupsFileGetLine(fp, line, sizeof(line))) > 0)
{
linenum ++;
if (bytes > 255)
{
if (!saw_long_line)
{
if (!status)
_cupsLangPuts(stdout, _("FAIL"));
status ++;
_cupsLangPrintf(stdout,
_(" Line %d is longer than 255 characters (%d).\n"
" REF: Page 25, Line Length"),
linenum, (int)bytes);
}
saw_long_line ++;
}
if (linenum == 1)
{
if (strncmp(line, "%!PS-Adobe-", 11))
{
if (!status)
_cupsLangPuts(stdout, _("FAIL"));
_cupsLangPuts(stdout,
_(" Missing %!PS-Adobe-3.0 on first line.\n"
" REF: Page 17, 3.1 Conforming Documents"));
cupsFileClose(fp);
return (1);
}
else
version = (float)atof(line + 11);
}
else if (level > 0)
{
if (!strncmp(line, "%%BeginDocument:", 16))
level ++;
else if (!strncmp(line, "%%EndDocument", 13))
level --;
}
else if (saw_trailer)
{
if (!strncmp(line, "%%Pages:", 8))
{
if (atoi(line + 8) <= 0)
{
if (!status)
_cupsLangPuts(stdout, _("FAIL"));
status ++;
_cupsLangPrintf(stdout,
_(" Bad %%%%Pages: on line %d.\n"
" REF: Page 43, %%%%Pages:"),
linenum);
}
else
saw_pages = 1;
}
else if (!strncmp(line, "%%BoundingBox:", 14))
{
if (sscanf(line + 14, "%d%d%d%d", lbrt + 0, lbrt + 1, lbrt + 2,
lbrt + 3) != 4)
{
if (!status)
_cupsLangPuts(stdout, _("FAIL"));
status ++;
_cupsLangPrintf(stdout, _(" Bad %%%%BoundingBox: on line %d.\n"
" REF: Page 39, %%%%BoundingBox:"),
linenum);
}
else
saw_bounding_box = 1;
}
}
else if (!saw_end_comments)
{
if (!strncmp(line, "%%EndComments", 13))
saw_end_comments = 1;
else if (line[0] != '%')
saw_end_comments = -1;
else if (!strncmp(line, "%%Pages:", 8))
{
if (strstr(line + 8, "(atend)"))
saw_pages = -1;
else if (atoi(line + 8) <= 0)
{
if (!status)
_cupsLangPuts(stdout, _("FAIL"));
status ++;
_cupsLangPrintf(stdout, _(" Bad %%%%Pages: on line %d.\n"
" REF: Page 43, %%%%Pages:"),
linenum);
}
else
saw_pages = 1;
}
else if (!strncmp(line, "%%BoundingBox:", 14))
{
if (strstr(line, "(atend)"))
saw_bounding_box = -1;
else if (sscanf(line + 14, "%d%d%d%d", lbrt + 0, lbrt + 1, lbrt + 2,
lbrt + 3) != 4)
{
if (!status)
_cupsLangPuts(stdout, _("FAIL"));
status ++;
_cupsLangPrintf(stdout, _(" Bad %%%%BoundingBox: on line %d.\n"
" REF: Page 39, %%%%BoundingBox:"),
linenum);
}
else
saw_bounding_box = 1;
}
}
else if (saw_begin_prolog && !saw_end_prolog)
{
if (!strncmp(line, "%%EndProlog", 11))
saw_end_prolog = 1;
}
else if (saw_begin_setup && !saw_end_setup)
{
if (!strncmp(line, "%%EndSetup", 10))
saw_end_setup = 1;
}
else if (saw_end_comments)
{
if (!strncmp(line, "%%Page:", 7))
{
if (sscanf(line + 7, "%255s%d", page_label, &page_number) != 2 ||
page_number != (last_page_number + 1) || page_number < 1)
{
if (!status)
_cupsLangPuts(stdout, _("FAIL"));
status ++;
_cupsLangPrintf(stdout, _(" Bad %%%%Page: on line %d.\n"
" REF: Page 53, %%%%Page:"),
linenum);
}
else
{
last_page_number = page_number;
saw_page = 1;
}
}
else if (!strncmp(line, "%%BeginProlog", 13))
saw_begin_prolog = 1;
else if (!strncmp(line, "%%BeginSetup", 12))
saw_begin_setup = 1;
else if (!strncmp(line, "%%BeginDocument:", 16))
level ++;
else if (!strncmp(line, "%%EndDocument", 13))
level --;
else if (!strncmp(line, "%%Trailer", 9))
saw_trailer = 1;
}
for (i = 0; !binary && i < (int)bytes; i ++)
{
ch = line[i];
if ((ch < ' ' || (ch & 0x80)) && ch != '\n' && ch != '\r' && ch != '\t')
binary = 1;
}
}
if (saw_bounding_box <= 0)
{
if (!status)
_cupsLangPuts(stdout, _("FAIL"));
status ++;
_cupsLangPuts(stdout, _(" Missing or bad %%BoundingBox: comment.\n"
" REF: Page 39, %%BoundingBox:"));
}
if (saw_pages <= 0)
{
if (!status)
_cupsLangPuts(stdout, _("FAIL"));
status ++;
_cupsLangPuts(stdout, _(" Missing or bad %%Pages: comment.\n"
" REF: Page 43, %%Pages:"));
}
if (!saw_end_comments)
{
if (!status)
_cupsLangPuts(stdout, _("FAIL"));
status ++;
_cupsLangPuts(stdout, _(" Missing %%EndComments comment."
" REF: Page 41, %%EndComments"));
}
if (!saw_page)
{
if (!status)
_cupsLangPuts(stdout, _("FAIL"));
status ++;
_cupsLangPuts(stdout, _(" Missing or bad %%Page: comments.\n"
" REF: Page 53, %%Page:"));
}
if (level < 0)
{
if (!status)
_cupsLangPuts(stdout, _("FAIL"));
status ++;
_cupsLangPuts(stdout, _(" Too many %%EndDocument comments."));
}
else if (level > 0)
{
if (!status)
_cupsLangPuts(stdout, _("FAIL"));
status ++;
_cupsLangPuts(stdout, _(" Too many %%BeginDocument comments."));
}
if (saw_long_line > 1)
_cupsLangPrintf(stderr,
_(" Saw %d lines that exceeded 255 characters."),
saw_long_line);
if (!status)
_cupsLangPuts(stdout, _("PASS"));
if (binary)
_cupsLangPuts(stdout, _(" Warning: file contains binary data."));
if (version < 3.0f)
_cupsLangPrintf(stdout,
_(" Warning: obsolete DSC version %.1f in file."),
version);
if (saw_end_comments < 0)
_cupsLangPuts(stdout, _(" Warning: no %%EndComments comment in file."));
cupsFileClose(fp);
return (status);
}
/*
* 'usage()' - Show program usage.
*/
static void
usage(void)
{
_cupsLangPuts(stdout, _("Usage: cupstestdsc [options] filename.ps [... "
"filename.ps]"));
_cupsLangPuts(stdout, _(" cupstestdsc [options] -"));
_cupsLangPuts(stdout, "");
_cupsLangPuts(stdout, _("Options:"));
_cupsLangPuts(stdout, "");
_cupsLangPuts(stdout, _(" -h Show program usage"));
_cupsLangPuts(stdout, "");
_cupsLangPuts(stdout, _("Note: this program only validates the DSC comments, "
"not the PostScript itself."));
exit(1);
}