#include "cups-private.h"
#if defined(WIN32) || defined(__EMX__)
# include <io.h>
#else
# include <unistd.h>
#endif
static int ppd_compare_cparams(ppd_cparam_t *a, ppd_cparam_t *b);
static void ppd_handle_media(ppd_file_t *ppd);
static const char ppd_custom_code[] =
"pop pop pop\n"
"<</PageSize[5 -2 roll]/ImagingBBox null>>setpagedevice\n";
int
ppdCollect(ppd_file_t *ppd,
ppd_section_t section,
ppd_choice_t ***choices)
{
return (ppdCollect2(ppd, section, 0.0, choices));
}
int
ppdCollect2(ppd_file_t *ppd,
ppd_section_t section,
float min_order,
ppd_choice_t ***choices)
{
ppd_choice_t *c;
ppd_section_t csection;
float corder;
int count;
ppd_choice_t **collect;
float *orders;
DEBUG_printf(("ppdCollect2(ppd=%p, section=%d, min_order=%f, choices=%p)",
ppd, section, min_order, choices));
if (!ppd || !choices)
{
if (choices)
*choices = NULL;
return (0);
}
count = 0;
if ((collect = calloc(sizeof(ppd_choice_t *),
cupsArrayCount(ppd->marked))) == NULL)
{
*choices = NULL;
return (0);
}
if ((orders = calloc(sizeof(float), cupsArrayCount(ppd->marked))) == NULL)
{
*choices = NULL;
free(collect);
return (0);
}
for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
c;
c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
{
csection = c->option->section;
corder = c->option->order;
if (!strcmp(c->choice, "Custom"))
{
ppd_attr_t *attr;
float aorder;
char asection[17],
amain[PPD_MAX_NAME + 1],
aoption[PPD_MAX_NAME];
for (attr = ppdFindAttr(ppd, "NonUIOrderDependency", NULL);
attr;
attr = ppdFindNextAttr(ppd, "NonUIOrderDependency", NULL))
if (attr->value &&
sscanf(attr->value, "%f%16s%41s%40s", &aorder, asection, amain,
aoption) == 4 &&
!strncmp(amain, "*Custom", 7) &&
!strcmp(amain + 7, c->option->keyword) && !strcmp(aoption, "True"))
{
corder = aorder;
if (!strcmp(asection, "DocumentSetup"))
csection = PPD_ORDER_DOCUMENT;
else if (!strcmp(asection, "ExitServer"))
csection = PPD_ORDER_EXIT;
else if (!strcmp(asection, "JCLSetup"))
csection = PPD_ORDER_JCL;
else if (!strcmp(asection, "PageSetup"))
csection = PPD_ORDER_PAGE;
else if (!strcmp(asection, "Prolog"))
csection = PPD_ORDER_PROLOG;
else
csection = PPD_ORDER_ANY;
break;
}
}
if (csection == section && corder >= min_order)
{
collect[count] = c;
orders[count] = corder;
count ++;
}
}
if (count > 1)
{
int i, j;
for (i = 0; i < (count - 1); i ++)
for (j = i + 1; j < count; j ++)
if (orders[i] > orders[j])
{
c = collect[i];
corder = orders[i];
collect[i] = collect[j];
orders[i] = orders[j];
collect[j] = c;
orders[j] = corder;
}
}
free(orders);
DEBUG_printf(("2ppdCollect2: %d marked choices...", count));
if (count > 0)
{
*choices = collect;
return (count);
}
else
{
*choices = NULL;
free(collect);
return (0);
}
}
int
ppdEmit(ppd_file_t *ppd,
FILE *fp,
ppd_section_t section)
{
return (ppdEmitAfterOrder(ppd, fp, section, 0, 0.0));
}
int
ppdEmitAfterOrder(
ppd_file_t *ppd,
FILE *fp,
ppd_section_t section,
int limit,
float min_order)
{
char *buffer;
int status;
if (!ppd || !fp)
return (-1);
buffer = ppdEmitString(ppd, section, limit ? min_order : 0.0f);
if (buffer)
{
status = fputs(buffer, fp) < 0 ? -1 : 0;
free(buffer);
}
else
status = 0;
return (status);
}
int
ppdEmitFd(ppd_file_t *ppd,
int fd,
ppd_section_t section)
{
char *buffer,
*bufptr;
size_t buflength;
ssize_t bytes;
int status;
if (!ppd || fd < 0)
return (-1);
buffer = ppdEmitString(ppd, section, 0.0);
if (buffer)
{
buflength = strlen(buffer);
bufptr = buffer;
bytes = 0;
while (buflength > 0)
{
#ifdef WIN32
if ((bytes = (ssize_t)write(fd, bufptr, (unsigned)buflength)) < 0)
#else
if ((bytes = write(fd, bufptr, buflength)) < 0)
#endif
{
if (errno == EAGAIN || errno == EINTR)
continue;
break;
}
buflength -= bytes;
bufptr += bytes;
}
status = bytes < 0 ? -1 : 0;
free(buffer);
}
else
status = 0;
return (status);
}
int
ppdEmitJCL(ppd_file_t *ppd,
FILE *fp,
int job_id,
const char *user,
const char *title)
{
char *ptr;
char temp[65],
displaymsg[33];
if (!ppd || !ppd->jcl_begin || !ppd->jcl_ps)
return (0);
if (!strncmp(ppd->jcl_begin, "\033%-12345X@", 10))
{
ppd_attr_t *charset;
ppd_attr_t *display;
if ((charset = ppdFindAttr(ppd, "cupsPJLCharset", NULL)) != NULL)
{
if (!charset->value || _cups_strcasecmp(charset->value, "UTF-8"))
charset = NULL;
}
if ((display = ppdFindAttr(ppd, "cupsPJLDisplay", NULL)) != NULL)
{
if (!display->value)
display = NULL;
}
fputs("\033%-12345X@PJL\n", fp);
for (ptr = ppd->jcl_begin + 9; *ptr;)
if (!strncmp(ptr, "@PJL JOB", 8))
{
for (;*ptr; ptr ++)
if (*ptr == '\n')
break;
if (*ptr)
ptr ++;
}
else
{
for (;*ptr; ptr ++)
{
putc(*ptr, fp);
if (*ptr == '\n')
break;
}
if (*ptr)
ptr ++;
}
if ((ptr = strrchr(title, '/')) != NULL)
{
title = ptr + 1;
}
if (!strncmp(title, "smbprn.", 7))
{
for (title += 7; *title && isdigit(*title & 255); title ++);
while (_cups_isspace(*title))
title ++;
if ((ptr = strstr(title, " - ")) != NULL)
{
title = ptr + 3;
}
}
strlcpy(temp, title, sizeof(temp));
for (ptr = temp; *ptr; ptr ++)
if (*ptr == '\"')
*ptr = '\'';
else if (!charset && (*ptr & 128))
*ptr = '?';
snprintf(displaymsg, sizeof(displaymsg), "%d %s %s", job_id, user, temp);
if (display && strcmp(display->value, "job"))
fprintf(fp, "@PJL JOB NAME = \"%s\"\n", temp);
else if (display && !strcmp(display->value, "rdymsg"))
fprintf(fp, "@PJL RDYMSG DISPLAY = \"%s\"\n", displaymsg);
else
fprintf(fp, "@PJL JOB NAME = \"%s\" DISPLAY = \"%s\"\n", temp,
displaymsg);
strlcpy(temp, user, sizeof(temp));
for (ptr = temp; *ptr; ptr ++)
if (*ptr == '\"')
*ptr = '\'';
else if (!charset && (*ptr & 128))
*ptr = '?';
fprintf(fp, "@PJL SET USERNAME = \"%s\"\n", temp);
}
else
fputs(ppd->jcl_begin, fp);
ppdEmit(ppd, fp, PPD_ORDER_JCL);
fputs(ppd->jcl_ps, fp);
return (0);
}
int
ppdEmitJCLEnd(ppd_file_t *ppd,
FILE *fp)
{
if (!ppd)
return (0);
if (!ppd->jcl_end)
{
if (ppd->num_filters == 0)
putc(0x04, fp);
return (0);
}
if (!strncmp(ppd->jcl_end, "\033%-12345X@", 10))
{
fputs("\033%-12345X@PJL\n", fp);
fputs("@PJL RDYMSG DISPLAY = \"\"\n", fp);
fputs(ppd->jcl_end + 9, fp);
}
else
fputs(ppd->jcl_end, fp);
return (0);
}
char *
ppdEmitString(ppd_file_t *ppd,
ppd_section_t section,
float min_order)
{
int i, j,
count;
ppd_choice_t **choices;
ppd_size_t *size;
ppd_coption_t *coption;
ppd_cparam_t *cparam;
size_t bufsize;
char *buffer,
*bufptr,
*bufend;
struct lconv *loc;
DEBUG_printf(("ppdEmitString(ppd=%p, section=%d, min_order=%f)",
ppd, section, min_order));
if (!ppd)
return (NULL);
ppd_handle_media(ppd);
if ((count = ppdCollect2(ppd, section, min_order, &choices)) == 0)
return (NULL);
for (i = 0, bufsize = 1; i < count; i ++)
{
if (section == PPD_ORDER_JCL)
{
if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
(coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
!= NULL)
{
for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
cparam;
cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
{
switch (cparam->type)
{
case PPD_CUSTOM_CURVE :
case PPD_CUSTOM_INVCURVE :
case PPD_CUSTOM_POINTS :
case PPD_CUSTOM_REAL :
case PPD_CUSTOM_INT :
bufsize += 10;
break;
case PPD_CUSTOM_PASSCODE :
case PPD_CUSTOM_PASSWORD :
case PPD_CUSTOM_STRING :
if (cparam->current.custom_string)
bufsize += strlen(cparam->current.custom_string);
break;
}
}
}
}
else if (section != PPD_ORDER_EXIT)
{
bufsize += 3;
if ((!_cups_strcasecmp(choices[i]->option->keyword, "PageSize") ||
!_cups_strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
!_cups_strcasecmp(choices[i]->choice, "Custom"))
{
DEBUG_puts("2ppdEmitString: Custom size set!");
bufsize += 37;
bufsize += 50;
}
else if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
(coption = ppdFindCustomOption(ppd,
choices[i]->option->keyword))
!= NULL)
{
bufsize += 23 + strlen(choices[i]->option->keyword) + 6;
for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
cparam;
cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
{
switch (cparam->type)
{
case PPD_CUSTOM_CURVE :
case PPD_CUSTOM_INVCURVE :
case PPD_CUSTOM_POINTS :
case PPD_CUSTOM_REAL :
case PPD_CUSTOM_INT :
bufsize += 10;
break;
case PPD_CUSTOM_PASSCODE :
case PPD_CUSTOM_PASSWORD :
case PPD_CUSTOM_STRING :
bufsize += 3;
if (cparam->current.custom_string)
bufsize += 4 * strlen(cparam->current.custom_string);
break;
}
}
}
else
bufsize += 17 + strlen(choices[i]->option->keyword) + 1 +
strlen(choices[i]->choice) + 1;
bufsize += 13;
bufsize += 22;
}
if (choices[i]->code)
bufsize += strlen(choices[i]->code) + 1;
else
bufsize += strlen(ppd_custom_code);
}
DEBUG_printf(("2ppdEmitString: Allocating %d bytes for string...",
(int)bufsize));
if ((buffer = calloc(1, bufsize)) == NULL)
{
free(choices);
return (NULL);
}
bufend = buffer + bufsize - 1;
loc = localeconv();
for (i = 0, bufptr = buffer; i < count; i ++, bufptr += strlen(bufptr))
if (section == PPD_ORDER_JCL)
{
if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
choices[i]->code &&
(coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
!= NULL)
{
char *cptr;
int pnum;
for (cptr = choices[i]->code; *cptr && bufptr < bufend;)
{
if (*cptr == '\\')
{
cptr ++;
if (isdigit(*cptr & 255))
{
pnum = *cptr++ - '0';
while (isdigit(*cptr & 255))
pnum = pnum * 10 + *cptr++ - '0';
for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
cparam;
cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
if (cparam->order == pnum)
break;
if (cparam)
{
switch (cparam->type)
{
case PPD_CUSTOM_CURVE :
case PPD_CUSTOM_INVCURVE :
case PPD_CUSTOM_POINTS :
case PPD_CUSTOM_REAL :
bufptr = _cupsStrFormatd(bufptr, bufend,
cparam->current.custom_real,
loc);
break;
case PPD_CUSTOM_INT :
snprintf(bufptr, bufend - bufptr, "%d",
cparam->current.custom_int);
bufptr += strlen(bufptr);
break;
case PPD_CUSTOM_PASSCODE :
case PPD_CUSTOM_PASSWORD :
case PPD_CUSTOM_STRING :
if (cparam->current.custom_string)
{
strlcpy(bufptr, cparam->current.custom_string,
bufend - bufptr);
bufptr += strlen(bufptr);
}
break;
}
}
}
else if (*cptr)
*bufptr++ = *cptr++;
}
else
*bufptr++ = *cptr++;
}
}
else
{
strlcpy(bufptr, choices[i]->code, bufend - bufptr + 1);
bufptr += strlen(bufptr);
}
}
else if (section != PPD_ORDER_EXIT)
{
strlcpy(bufptr, "[{\n", bufend - bufptr + 1);
bufptr += 3;
DEBUG_printf(("2ppdEmitString: Adding code for %s=%s...",
choices[i]->option->keyword, choices[i]->choice));
if ((!_cups_strcasecmp(choices[i]->option->keyword, "PageSize") ||
!_cups_strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
!_cups_strcasecmp(choices[i]->choice, "Custom"))
{
ppd_attr_t *attr;
int pos,
orientation;
float values[5];
strlcpy(bufptr, "%%BeginFeature: *CustomPageSize True\n",
bufend - bufptr + 1);
bufptr += 37;
size = ppdPageSize(ppd, "Custom");
memset(values, 0, sizeof(values));
if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Width")) != NULL)
{
pos = atoi(attr->value) - 1;
if (pos < 0 || pos > 4)
pos = 0;
}
else
pos = 0;
values[pos] = size->width;
if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Height")) != NULL)
{
pos = atoi(attr->value) - 1;
if (pos < 0 || pos > 4)
pos = 1;
}
else
pos = 1;
values[pos] = size->length;
orientation = 1;
if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize",
"Orientation")) != NULL)
{
int min_orient, max_orient;
if (sscanf(attr->value, "%d%*s%d%d", &pos, &min_orient,
&max_orient) != 3)
pos = 4;
else
{
pos --;
if (pos < 0 || pos > 4)
pos = 4;
if (orientation > max_orient)
orientation = max_orient;
else if (orientation < min_orient)
orientation = min_orient;
}
}
else
pos = 4;
values[pos] = (float)orientation;
for (pos = 0; pos < 5; pos ++)
{
bufptr = _cupsStrFormatd(bufptr, bufend, values[pos], loc);
*bufptr++ = '\n';
}
if (!choices[i]->code)
{
strlcpy(bufptr, ppd_custom_code, bufend - bufptr + 1);
bufptr += strlen(bufptr);
}
}
else if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
(coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
!= NULL)
{
const char *s;
cups_array_t *params;
params = cupsArrayNew((cups_array_func_t)ppd_compare_cparams, NULL);
for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
cparam;
cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
cupsArrayAdd(params, cparam);
snprintf(bufptr, bufend - bufptr + 1,
"%%%%BeginFeature: *Custom%s True\n", coption->keyword);
bufptr += strlen(bufptr);
for (cparam = (ppd_cparam_t *)cupsArrayFirst(params);
cparam;
cparam = (ppd_cparam_t *)cupsArrayNext(params))
{
switch (cparam->type)
{
case PPD_CUSTOM_CURVE :
case PPD_CUSTOM_INVCURVE :
case PPD_CUSTOM_POINTS :
case PPD_CUSTOM_REAL :
bufptr = _cupsStrFormatd(bufptr, bufend,
cparam->current.custom_real, loc);
*bufptr++ = '\n';
break;
case PPD_CUSTOM_INT :
snprintf(bufptr, bufend - bufptr + 1, "%d\n",
cparam->current.custom_int);
bufptr += strlen(bufptr);
break;
case PPD_CUSTOM_PASSCODE :
case PPD_CUSTOM_PASSWORD :
case PPD_CUSTOM_STRING :
*bufptr++ = '(';
if (cparam->current.custom_string)
{
for (s = cparam->current.custom_string; *s; s ++)
{
if (*s < ' ' || *s == '(' || *s == ')' || *s >= 127)
{
snprintf(bufptr, bufend - bufptr + 1, "\\%03o", *s & 255);
bufptr += strlen(bufptr);
}
else
*bufptr++ = *s;
}
}
*bufptr++ = ')';
*bufptr++ = '\n';
break;
}
}
cupsArrayDelete(params);
}
else
{
snprintf(bufptr, bufend - bufptr + 1, "%%%%BeginFeature: *%s %s\n",
choices[i]->option->keyword, choices[i]->choice);
bufptr += strlen(bufptr);
}
if (choices[i]->code && choices[i]->code[0])
{
j = (int)strlen(choices[i]->code);
memcpy(bufptr, choices[i]->code, j);
bufptr += j;
if (choices[i]->code[j - 1] != '\n')
*bufptr++ = '\n';
}
strlcpy(bufptr, "%%EndFeature\n"
"} stopped cleartomark\n", bufend - bufptr + 1);
bufptr += strlen(bufptr);
DEBUG_printf(("2ppdEmitString: Offset in string is %d...",
(int)(bufptr - buffer)));
}
else
{
strlcpy(bufptr, choices[i]->code, bufend - bufptr + 1);
bufptr += strlen(bufptr);
}
*bufptr = '\0';
free(choices);
return (buffer);
}
static int
ppd_compare_cparams(ppd_cparam_t *a,
ppd_cparam_t *b)
{
return (a->order - b->order);
}
static void
ppd_handle_media(ppd_file_t *ppd)
{
ppd_choice_t *manual_feed,
*input_slot;
ppd_size_t *size;
ppd_attr_t *rpr;
if ((size = ppdPageSize(ppd, NULL)) == NULL)
return;
manual_feed = ppdFindMarkedChoice(ppd, "ManualFeed");
input_slot = ppdFindMarkedChoice(ppd, "InputSlot");
if (input_slot != NULL)
rpr = ppdFindAttr(ppd, "RequiresPageRegion", input_slot->choice);
else
rpr = NULL;
if (!rpr)
rpr = ppdFindAttr(ppd, "RequiresPageRegion", "All");
if (!_cups_strcasecmp(size->name, "Custom") ||
(!manual_feed && !input_slot) ||
(manual_feed && !_cups_strcasecmp(manual_feed->choice, "False") &&
(!input_slot || (input_slot->code && !input_slot->code[0]))) ||
(!rpr && ppd->num_filters > 0))
{
ppdMarkOption(ppd, "PageSize", size->name);
}
else if (rpr && rpr->value && !_cups_strcasecmp(rpr->value, "True"))
{
ppdMarkOption(ppd, "PageRegion", size->name);
}
else
{
ppd_choice_t *page;
if ((page = ppdFindMarkedChoice(ppd, "PageSize")) != NULL)
{
page->marked = 0;
cupsArrayRemove(ppd->marked, page);
}
if ((page = ppdFindMarkedChoice(ppd, "PageRegion")) != NULL)
{
page->marked = 0;
cupsArrayRemove(ppd->marked, page);
}
}
}