Coverage Report

Created: 2022-10-31 07:00

/src/cups/cups/ppd-emit.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * PPD code emission routines for CUPS.
3
 *
4
 * Copyright 2007-2015 by Apple Inc.
5
 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
6
 *
7
 * These coded instructions, statements, and computer programs are the
8
 * property of Apple Inc. and are protected by Federal copyright
9
 * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
10
 * which should have been included with this file.  If this file is
11
 * missing or damaged, see the license at "http://www.cups.org/".
12
 *
13
 * PostScript is a trademark of Adobe Systems, Inc.
14
 *
15
 * This file is subject to the Apple OS-Developed Software exception.
16
 */
17
18
/*
19
 * Include necessary headers...
20
 */
21
22
#include "cups-private.h"
23
#include "ppd.h"
24
#if defined(_WIN32) || defined(__EMX__)
25
#  include <io.h>
26
#else
27
#  include <unistd.h>
28
#endif /* _WIN32 || __EMX__ */
29
30
31
/*
32
 * Local functions...
33
 */
34
35
static int  ppd_compare_cparams(ppd_cparam_t *a, ppd_cparam_t *b);
36
static void ppd_handle_media(ppd_file_t *ppd);
37
38
39
/*
40
 * Local globals...
41
 */
42
43
static const char ppd_custom_code[] =
44
    "pop pop pop\n"
45
    "<</PageSize[5 -2 roll]/ImagingBBox null>>setpagedevice\n";
46
47
48
/*
49
 * 'ppdCollect()' - Collect all marked options that reside in the specified
50
 *                  section.
51
 *
52
 * The choices array should be freed using @code free@ when you are
53
 * finished with it.
54
 */
55
56
int         /* O - Number of options marked */
57
ppdCollect(ppd_file_t    *ppd,    /* I - PPD file data */
58
           ppd_section_t section, /* I - Section to collect */
59
           ppd_choice_t  ***choices)  /* O - Pointers to choices */
60
0
{
61
0
  return (ppdCollect2(ppd, section, 0.0, choices));
62
0
}
63
64
65
/*
66
 * 'ppdCollect2()' - Collect all marked options that reside in the
67
 *                   specified section and minimum order.
68
 *
69
 * The choices array should be freed using @code free@ when you are
70
 * finished with it.
71
 *
72
 * @since CUPS 1.2/macOS 10.5@
73
 */
74
75
int         /* O - Number of options marked */
76
ppdCollect2(ppd_file_t    *ppd,   /* I - PPD file data */
77
            ppd_section_t section,  /* I - Section to collect */
78
      float         min_order,  /* I - Minimum OrderDependency value */
79
            ppd_choice_t  ***choices) /* O - Pointers to choices */
80
0
{
81
0
  ppd_choice_t  *c;     /* Current choice */
82
0
  ppd_section_t csection;   /* Current section */
83
0
  float   corder;     /* Current OrderDependency value */
84
0
  int   count;      /* Number of choices collected */
85
0
  ppd_choice_t  **collect;    /* Collected choices */
86
0
  float   *orders;    /* Collected order values */
87
88
89
0
  DEBUG_printf(("ppdCollect2(ppd=%p, section=%d, min_order=%f, choices=%p)",
90
0
                ppd, section, min_order, choices));
91
92
0
  if (!ppd || !choices)
93
0
  {
94
0
    if (choices)
95
0
      *choices = NULL;
96
97
0
    return (0);
98
0
  }
99
100
 /*
101
  * Allocate memory for up to N selected choices...
102
  */
103
104
0
  count = 0;
105
0
  if ((collect = calloc(sizeof(ppd_choice_t *),
106
0
                        (size_t)cupsArrayCount(ppd->marked))) == NULL)
107
0
  {
108
0
    *choices = NULL;
109
0
    return (0);
110
0
  }
111
112
0
  if ((orders = calloc(sizeof(float), (size_t)cupsArrayCount(ppd->marked))) == NULL)
113
0
  {
114
0
    *choices = NULL;
115
0
    free(collect);
116
0
    return (0);
117
0
  }
118
119
 /*
120
  * Loop through all options and add choices as needed...
121
  */
122
123
0
  for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
124
0
       c;
125
0
       c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
126
0
  {
127
0
    csection = c->option->section;
128
0
    corder   = c->option->order;
129
130
0
    if (!strcmp(c->choice, "Custom"))
131
0
    {
132
0
      ppd_attr_t  *attr;    /* NonUIOrderDependency value */
133
0
      float   aorder;   /* Order value */
134
0
      char    asection[17], /* Section name */
135
0
      amain[PPD_MAX_NAME + 1],
136
0
      aoption[PPD_MAX_NAME];
137
          /* *CustomFoo and True */
138
139
140
0
      for (attr = ppdFindAttr(ppd, "NonUIOrderDependency", NULL);
141
0
           attr;
142
0
     attr = ppdFindNextAttr(ppd, "NonUIOrderDependency", NULL))
143
0
        if (attr->value &&
144
0
      sscanf(attr->value, "%f%16s%41s%40s", &aorder, asection, amain,
145
0
             aoption) == 4 &&
146
0
      !strncmp(amain, "*Custom", 7) &&
147
0
      !strcmp(amain + 7, c->option->keyword) && !strcmp(aoption, "True"))
148
0
  {
149
   /*
150
    * Use this NonUIOrderDependency...
151
    */
152
153
0
          corder = aorder;
154
155
0
    if (!strcmp(asection, "DocumentSetup"))
156
0
      csection = PPD_ORDER_DOCUMENT;
157
0
    else if (!strcmp(asection, "ExitServer"))
158
0
      csection = PPD_ORDER_EXIT;
159
0
    else if (!strcmp(asection, "JCLSetup"))
160
0
      csection = PPD_ORDER_JCL;
161
0
    else if (!strcmp(asection, "PageSetup"))
162
0
      csection = PPD_ORDER_PAGE;
163
0
    else if (!strcmp(asection, "Prolog"))
164
0
      csection = PPD_ORDER_PROLOG;
165
0
    else
166
0
      csection = PPD_ORDER_ANY;
167
168
0
    break;
169
0
  }
170
0
    }
171
172
0
    if (csection == section && corder >= min_order)
173
0
    {
174
0
      collect[count] = c;
175
0
      orders[count]  = corder;
176
0
      count ++;
177
0
    }
178
0
  }
179
180
 /*
181
  * If we have more than 1 marked choice, sort them...
182
  */
183
184
0
  if (count > 1)
185
0
  {
186
0
    int i, j;       /* Looping vars */
187
188
0
    for (i = 0; i < (count - 1); i ++)
189
0
      for (j = i + 1; j < count; j ++)
190
0
        if (orders[i] > orders[j])
191
0
  {
192
0
    c          = collect[i];
193
0
    corder     = orders[i];
194
0
    collect[i] = collect[j];
195
0
    orders[i]  = orders[j];
196
0
    collect[j] = c;
197
0
    orders[j]  = corder;
198
0
  }
199
0
  }
200
201
0
  free(orders);
202
203
0
  DEBUG_printf(("2ppdCollect2: %d marked choices...", count));
204
205
 /*
206
  * Return the array and number of choices; if 0, free the array since
207
  * it isn't needed.
208
  */
209
210
0
  if (count > 0)
211
0
  {
212
0
    *choices = collect;
213
0
    return (count);
214
0
  }
215
0
  else
216
0
  {
217
0
    *choices = NULL;
218
0
    free(collect);
219
0
    return (0);
220
0
  }
221
0
}
222
223
224
/*
225
 * 'ppdEmit()' - Emit code for marked options to a file.
226
 */
227
228
int         /* O - 0 on success, -1 on failure */
229
ppdEmit(ppd_file_t    *ppd,   /* I - PPD file record */
230
        FILE          *fp,    /* I - File to write to */
231
        ppd_section_t section)    /* I - Section to write */
232
0
{
233
0
  return (ppdEmitAfterOrder(ppd, fp, section, 0, 0.0));
234
0
}
235
236
237
/*
238
 * 'ppdEmitAfterOrder()' - Emit a subset of the code for marked options to a file.
239
 *
240
 * When "limit" is non-zero, this function only emits options whose
241
 * OrderDependency value is greater than or equal to "min_order".
242
 *
243
 * When "limit" is zero, this function is identical to ppdEmit().
244
 *
245
 * @since CUPS 1.2/macOS 10.5@
246
 */
247
248
int         /* O - 0 on success, -1 on failure */
249
ppdEmitAfterOrder(
250
    ppd_file_t    *ppd,     /* I - PPD file record */
251
    FILE          *fp,      /* I - File to write to */
252
    ppd_section_t section,    /* I - Section to write */
253
    int     limit,    /* I - Non-zero to use min_order */
254
    float         min_order)    /* I - Lowest OrderDependency */
255
0
{
256
0
  char  *buffer;      /* Option code */
257
0
  int status;       /* Return status */
258
259
260
 /*
261
  * Range check input...
262
  */
263
264
0
  if (!ppd || !fp)
265
0
    return (-1);
266
267
 /*
268
  * Get the string...
269
  */
270
271
0
  buffer = ppdEmitString(ppd, section, limit ? min_order : 0.0f);
272
273
 /*
274
  * Write it as needed and return...
275
  */
276
277
0
  if (buffer)
278
0
  {
279
0
    status = fputs(buffer, fp) < 0 ? -1 : 0;
280
281
0
    free(buffer);
282
0
  }
283
0
  else
284
0
    status = 0;
285
286
0
  return (status);
287
0
}
288
289
290
/*
291
 * 'ppdEmitFd()' - Emit code for marked options to a file.
292
 */
293
294
int         /* O - 0 on success, -1 on failure */
295
ppdEmitFd(ppd_file_t    *ppd,   /* I - PPD file record */
296
          int           fd,   /* I - File to write to */
297
          ppd_section_t section)  /* I - Section to write */
298
0
{
299
0
  char    *buffer,    /* Option code */
300
0
    *bufptr;    /* Pointer into code */
301
0
  size_t  buflength;    /* Length of option code */
302
0
  ssize_t bytes;      /* Bytes written */
303
0
  int   status;     /* Return status */
304
305
306
 /*
307
  * Range check input...
308
  */
309
310
0
  if (!ppd || fd < 0)
311
0
    return (-1);
312
313
 /*
314
  * Get the string...
315
  */
316
317
0
  buffer = ppdEmitString(ppd, section, 0.0);
318
319
 /*
320
  * Write it as needed and return...
321
  */
322
323
0
  if (buffer)
324
0
  {
325
0
    buflength = strlen(buffer);
326
0
    bufptr    = buffer;
327
0
    bytes     = 0;
328
329
0
    while (buflength > 0)
330
0
    {
331
#ifdef _WIN32
332
      if ((bytes = (ssize_t)write(fd, bufptr, (unsigned)buflength)) < 0)
333
#else
334
0
      if ((bytes = write(fd, bufptr, buflength)) < 0)
335
0
#endif /* _WIN32 */
336
0
      {
337
0
        if (errno == EAGAIN || errno == EINTR)
338
0
    continue;
339
340
0
  break;
341
0
      }
342
343
0
      buflength -= (size_t)bytes;
344
0
      bufptr    += bytes;
345
0
    }
346
347
0
    status = bytes < 0 ? -1 : 0;
348
349
0
    free(buffer);
350
0
  }
351
0
  else
352
0
    status = 0;
353
354
0
  return (status);
355
0
}
356
357
358
/*
359
 * 'ppdEmitJCL()' - Emit code for JCL options to a file.
360
 */
361
362
int         /* O - 0 on success, -1 on failure */
363
ppdEmitJCL(ppd_file_t *ppd,   /* I - PPD file record */
364
           FILE       *fp,    /* I - File to write to */
365
           int        job_id,   /* I - Job ID */
366
     const char *user,    /* I - Username */
367
     const char *title)   /* I - Title */
368
0
{
369
0
  char    *ptr;     /* Pointer into JCL string */
370
0
  char    temp[65],   /* Local title string */
371
0
    displaymsg[33];   /* Local display string */
372
373
374
 /*
375
  * Range check the input...
376
  */
377
378
0
  if (!ppd || !ppd->jcl_begin || !ppd->jcl_ps)
379
0
    return (0);
380
381
 /*
382
  * See if the printer supports HP PJL...
383
  */
384
385
0
  if (!strncmp(ppd->jcl_begin, "\033%-12345X@", 10))
386
0
  {
387
   /*
388
    * This printer uses HP PJL commands for output; filter the output
389
    * so that we only have a single "@PJL JOB" command in the header...
390
    *
391
    * To avoid bugs in the PJL implementation of certain vendors' products
392
    * (Xerox in particular), we add a dummy "@PJL" command at the beginning
393
    * of the PJL commands to initialize PJL processing.
394
    */
395
396
0
    ppd_attr_t  *charset;   /* PJL charset */
397
0
    ppd_attr_t  *display;   /* PJL display command */
398
399
400
0
    if ((charset = ppdFindAttr(ppd, "cupsPJLCharset", NULL)) != NULL)
401
0
    {
402
0
      if (!charset->value || _cups_strcasecmp(charset->value, "UTF-8"))
403
0
        charset = NULL;
404
0
    }
405
406
0
    if ((display = ppdFindAttr(ppd, "cupsPJLDisplay", NULL)) != NULL)
407
0
    {
408
0
      if (!display->value)
409
0
        display = NULL;
410
0
    }
411
412
0
    fputs("\033%-12345X@PJL\n", fp);
413
0
    for (ptr = ppd->jcl_begin + 9; *ptr;)
414
0
      if (!strncmp(ptr, "@PJL JOB", 8))
415
0
      {
416
       /*
417
        * Skip job command...
418
  */
419
420
0
        for (;*ptr; ptr ++)
421
0
    if (*ptr == '\n')
422
0
      break;
423
424
0
  if (*ptr)
425
0
    ptr ++;
426
0
      }
427
0
      else
428
0
      {
429
       /*
430
        * Copy line...
431
  */
432
433
0
        for (;*ptr; ptr ++)
434
0
  {
435
0
    putc(*ptr, fp);
436
0
    if (*ptr == '\n')
437
0
      break;
438
0
  }
439
440
0
  if (*ptr)
441
0
    ptr ++;
442
0
      }
443
444
   /*
445
    * Clean up the job title...
446
    */
447
448
0
    if ((ptr = strrchr(title, '/')) != NULL)
449
0
    {
450
     /*
451
      * Only show basename of file path...
452
      */
453
454
0
      title = ptr + 1;
455
0
    }
456
457
0
    if (!strncmp(title, "smbprn.", 7))
458
0
    {
459
     /*
460
      * Skip leading smbprn.######## from Samba jobs...
461
      */
462
463
0
      for (title += 7; *title && isdigit(*title & 255); title ++);
464
0
      while (_cups_isspace(*title))
465
0
        title ++;
466
467
0
      if ((ptr = strstr(title, " - ")) != NULL)
468
0
      {
469
       /*
470
  * Skip application name in "Some Application - Title of job"...
471
  */
472
473
0
  title = ptr + 3;
474
0
      }
475
0
    }
476
477
   /*
478
    * Replace double quotes with single quotes and UTF-8 characters with
479
    * question marks so that the title does not cause a PJL syntax error.
480
    */
481
482
0
    strlcpy(temp, title, sizeof(temp));
483
484
0
    for (ptr = temp; *ptr; ptr ++)
485
0
      if (*ptr == '\"')
486
0
        *ptr = '\'';
487
0
      else if (!charset && (*ptr & 128))
488
0
        *ptr = '?';
489
490
   /*
491
    * CUPS STR #3125: Long PJL JOB NAME causes problems with some printers
492
    *
493
    * Generate the display message, truncating at 32 characters + nul to avoid
494
    * issues with some printer's PJL implementations...
495
    */
496
497
0
    snprintf(displaymsg, sizeof(displaymsg), "%d %s %s", job_id, user, temp);
498
499
   /*
500
    * Send PJL JOB and PJL RDYMSG commands before we enter PostScript mode...
501
    */
502
503
0
    if (display && strcmp(display->value, "job"))
504
0
      fprintf(fp, "@PJL JOB NAME = \"%s\"\n", temp);
505
0
    else if (display && !strcmp(display->value, "rdymsg"))
506
0
      fprintf(fp, "@PJL RDYMSG DISPLAY = \"%s\"\n", displaymsg);
507
0
    else
508
0
      fprintf(fp, "@PJL JOB NAME = \"%s\" DISPLAY = \"%s\"\n", temp,
509
0
        displaymsg);
510
511
   /*
512
    * Replace double quotes with single quotes and UTF-8 characters with
513
    * question marks so that the user does not cause a PJL syntax error.
514
    */
515
516
0
    strlcpy(temp, user, sizeof(temp));
517
518
0
    for (ptr = temp; *ptr; ptr ++)
519
0
      if (*ptr == '\"')
520
0
        *ptr = '\'';
521
0
      else if (!charset && (*ptr & 128))
522
0
        *ptr = '?';
523
524
0
    fprintf(fp, "@PJL SET USERNAME = \"%s\"\n", temp);
525
0
  }
526
0
  else
527
0
    fputs(ppd->jcl_begin, fp);
528
529
0
  ppdEmit(ppd, fp, PPD_ORDER_JCL);
530
0
  fputs(ppd->jcl_ps, fp);
531
532
0
  return (0);
533
0
}
534
535
536
/*
537
 * 'ppdEmitJCLEnd()' - Emit JCLEnd code to a file.
538
 *
539
 * @since CUPS 1.2/macOS 10.5@
540
 */
541
542
int         /* O - 0 on success, -1 on failure */
543
ppdEmitJCLEnd(ppd_file_t *ppd,    /* I - PPD file record */
544
              FILE       *fp)   /* I - File to write to */
545
0
{
546
 /*
547
  * Range check the input...
548
  */
549
550
0
  if (!ppd)
551
0
    return (0);
552
553
0
  if (!ppd->jcl_end)
554
0
  {
555
0
    if (ppd->num_filters == 0)
556
0
      putc(0x04, fp);
557
558
0
    return (0);
559
0
  }
560
561
 /*
562
  * See if the printer supports HP PJL...
563
  */
564
565
0
  if (!strncmp(ppd->jcl_end, "\033%-12345X@", 10))
566
0
  {
567
   /*
568
    * This printer uses HP PJL commands for output; filter the output
569
    * so that we only have a single "@PJL JOB" command in the header...
570
    *
571
    * To avoid bugs in the PJL implementation of certain vendors' products
572
    * (Xerox in particular), we add a dummy "@PJL" command at the beginning
573
    * of the PJL commands to initialize PJL processing.
574
    */
575
576
0
    fputs("\033%-12345X@PJL\n", fp);
577
0
    fputs("@PJL RDYMSG DISPLAY = \"\"\n", fp);
578
0
    fputs(ppd->jcl_end + 9, fp);
579
0
  }
580
0
  else
581
0
    fputs(ppd->jcl_end, fp);
582
583
0
  return (0);
584
0
}
585
586
587
/*
588
 * 'ppdEmitString()' - Get a string containing the code for marked options.
589
 *
590
 * When "min_order" is greater than zero, this function only includes options
591
 * whose OrderDependency value is greater than or equal to "min_order".
592
 * Otherwise, all options in the specified section are included in the
593
 * returned string.
594
 *
595
 * The return string is allocated on the heap and should be freed using
596
 * @code free@ when you are done with it.
597
 *
598
 * @since CUPS 1.2/macOS 10.5@
599
 */
600
601
char *          /* O - String containing option code or @code NULL@ if there is no option code */
602
ppdEmitString(ppd_file_t    *ppd, /* I - PPD file record */
603
              ppd_section_t section,  /* I - Section to write */
604
        float         min_order)  /* I - Lowest OrderDependency */
605
0
{
606
0
  int   i, j,     /* Looping vars */
607
0
    count;      /* Number of choices */
608
0
  ppd_choice_t  **choices;    /* Choices */
609
0
  ppd_size_t  *size;      /* Custom page size */
610
0
  ppd_coption_t *coption;   /* Custom option */
611
0
  ppd_cparam_t  *cparam;    /* Custom parameter */
612
0
  size_t  bufsize;    /* Size of string buffer needed */
613
0
  char    *buffer,    /* String buffer */
614
0
    *bufptr,    /* Pointer into buffer */
615
0
    *bufend;    /* End of buffer */
616
0
  struct lconv  *loc;     /* Locale data */
617
618
619
0
  DEBUG_printf(("ppdEmitString(ppd=%p, section=%d, min_order=%f)",
620
0
                ppd, section, min_order));
621
622
 /*
623
  * Range check input...
624
  */
625
626
0
  if (!ppd)
627
0
    return (NULL);
628
629
 /*
630
  * Use PageSize or PageRegion as required...
631
  */
632
633
0
  ppd_handle_media(ppd);
634
635
 /*
636
  * Collect the options we need to emit...
637
  */
638
639
0
  if ((count = ppdCollect2(ppd, section, min_order, &choices)) == 0)
640
0
    return (NULL);
641
642
 /*
643
  * Count the number of bytes that are required to hold all of the
644
  * option code...
645
  */
646
647
0
  for (i = 0, bufsize = 1; i < count; i ++)
648
0
  {
649
0
    if (section == PPD_ORDER_JCL)
650
0
    {
651
0
      if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
652
0
    (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
653
0
        != NULL)
654
0
      {
655
       /*
656
        * Add space to account for custom parameter substitution...
657
  */
658
659
0
        for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
660
0
       cparam;
661
0
       cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
662
0
  {
663
0
          switch (cparam->type)
664
0
    {
665
0
      case PPD_CUSTOM_UNKNOWN :
666
0
          break;
667
668
0
      case PPD_CUSTOM_CURVE :
669
0
      case PPD_CUSTOM_INVCURVE :
670
0
      case PPD_CUSTOM_POINTS :
671
0
      case PPD_CUSTOM_REAL :
672
0
      case PPD_CUSTOM_INT :
673
0
          bufsize += 10;
674
0
          break;
675
676
0
      case PPD_CUSTOM_PASSCODE :
677
0
      case PPD_CUSTOM_PASSWORD :
678
0
      case PPD_CUSTOM_STRING :
679
0
          if (cparam->current.custom_string)
680
0
      bufsize += strlen(cparam->current.custom_string);
681
0
          break;
682
0
          }
683
0
  }
684
0
      }
685
0
    }
686
0
    else if (section != PPD_ORDER_EXIT)
687
0
    {
688
0
      bufsize += 3;     /* [{\n */
689
690
0
      if ((!_cups_strcasecmp(choices[i]->option->keyword, "PageSize") ||
691
0
           !_cups_strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
692
0
          !_cups_strcasecmp(choices[i]->choice, "Custom"))
693
0
      {
694
0
        DEBUG_puts("2ppdEmitString: Custom size set!");
695
696
0
        bufsize += 37;      /* %%BeginFeature: *CustomPageSize True\n */
697
0
        bufsize += 50;      /* Five 9-digit numbers + newline */
698
0
      }
699
0
      else if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
700
0
               (coption = ppdFindCustomOption(ppd,
701
0
                                        choices[i]->option->keyword))
702
0
             != NULL)
703
0
      {
704
0
        bufsize += 23 + strlen(choices[i]->option->keyword) + 6;
705
          /* %%BeginFeature: *Customkeyword True\n */
706
707
708
0
        for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
709
0
       cparam;
710
0
       cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
711
0
  {
712
0
          switch (cparam->type)
713
0
    {
714
0
      case PPD_CUSTOM_UNKNOWN :
715
0
          break;
716
717
0
      case PPD_CUSTOM_CURVE :
718
0
      case PPD_CUSTOM_INVCURVE :
719
0
      case PPD_CUSTOM_POINTS :
720
0
      case PPD_CUSTOM_REAL :
721
0
      case PPD_CUSTOM_INT :
722
0
          bufsize += 10;
723
0
          break;
724
725
0
      case PPD_CUSTOM_PASSCODE :
726
0
      case PPD_CUSTOM_PASSWORD :
727
0
      case PPD_CUSTOM_STRING :
728
0
    bufsize += 3;
729
0
          if (cparam->current.custom_string)
730
0
      bufsize += 4 * strlen(cparam->current.custom_string);
731
0
          break;
732
0
          }
733
0
  }
734
0
      }
735
0
      else
736
0
        bufsize += 17 + strlen(choices[i]->option->keyword) + 1 +
737
0
             strlen(choices[i]->choice) + 1;
738
          /* %%BeginFeature: *keyword choice\n */
739
740
0
      bufsize += 13;      /* %%EndFeature\n */
741
0
      bufsize += 22;      /* } stopped cleartomark\n */
742
0
    }
743
744
0
    if (choices[i]->code)
745
0
      bufsize += strlen(choices[i]->code) + 1;
746
0
    else
747
0
      bufsize += strlen(ppd_custom_code);
748
0
  }
749
750
 /*
751
  * Allocate memory...
752
  */
753
754
0
  DEBUG_printf(("2ppdEmitString: Allocating %d bytes for string...",
755
0
                (int)bufsize));
756
757
0
  if ((buffer = calloc(1, bufsize)) == NULL)
758
0
  {
759
0
    free(choices);
760
0
    return (NULL);
761
0
  }
762
763
0
  bufend = buffer + bufsize - 1;
764
0
  loc    = localeconv();
765
766
 /*
767
  * Copy the option code to the buffer...
768
  */
769
770
0
  for (i = 0, bufptr = buffer; i < count; i ++, bufptr += strlen(bufptr))
771
0
    if (section == PPD_ORDER_JCL)
772
0
    {
773
0
      if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
774
0
    choices[i]->code &&
775
0
          (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
776
0
        != NULL)
777
0
      {
778
       /*
779
        * Handle substitutions in custom JCL options...
780
  */
781
782
0
  char  *cptr;      /* Pointer into code */
783
0
  int pnum;     /* Parameter number */
784
785
786
0
        for (cptr = choices[i]->code; *cptr && bufptr < bufend;)
787
0
  {
788
0
    if (*cptr == '\\')
789
0
    {
790
0
      cptr ++;
791
792
0
      if (isdigit(*cptr & 255))
793
0
      {
794
       /*
795
        * Substitute parameter...
796
        */
797
798
0
              pnum = *cptr++ - '0';
799
0
        while (isdigit(*cptr & 255))
800
0
          pnum = pnum * 10 + *cptr++ - '0';
801
802
0
              for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
803
0
             cparam;
804
0
       cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
805
0
    if (cparam->order == pnum)
806
0
      break;
807
808
0
              if (cparam)
809
0
        {
810
0
          switch (cparam->type)
811
0
    {
812
0
      case PPD_CUSTOM_UNKNOWN :
813
0
          break;
814
815
0
      case PPD_CUSTOM_CURVE :
816
0
      case PPD_CUSTOM_INVCURVE :
817
0
      case PPD_CUSTOM_POINTS :
818
0
      case PPD_CUSTOM_REAL :
819
0
          bufptr = _cupsStrFormatd(bufptr, bufend,
820
0
                 cparam->current.custom_real,
821
0
                 loc);
822
0
          break;
823
824
0
      case PPD_CUSTOM_INT :
825
0
          snprintf(bufptr, (size_t)(bufend - bufptr), "%d", cparam->current.custom_int);
826
0
          bufptr += strlen(bufptr);
827
0
          break;
828
829
0
      case PPD_CUSTOM_PASSCODE :
830
0
      case PPD_CUSTOM_PASSWORD :
831
0
      case PPD_CUSTOM_STRING :
832
0
          if (cparam->current.custom_string)
833
0
          {
834
0
      strlcpy(bufptr, cparam->current.custom_string, (size_t)(bufend - bufptr));
835
0
      bufptr += strlen(bufptr);
836
0
          }
837
0
          break;
838
0
    }
839
0
        }
840
0
      }
841
0
      else if (*cptr)
842
0
        *bufptr++ = *cptr++;
843
0
    }
844
0
    else
845
0
      *bufptr++ = *cptr++;
846
0
  }
847
0
      }
848
0
      else
849
0
      {
850
       /*
851
        * Otherwise just copy the option code directly...
852
  */
853
854
0
        strlcpy(bufptr, choices[i]->code, (size_t)(bufend - bufptr + 1));
855
0
        bufptr += strlen(bufptr);
856
0
      }
857
0
    }
858
0
    else if (section != PPD_ORDER_EXIT)
859
0
    {
860
     /*
861
      * Add wrapper commands to prevent printer errors for unsupported
862
      * options...
863
      */
864
865
0
      strlcpy(bufptr, "[{\n", (size_t)(bufend - bufptr + 1));
866
0
      bufptr += 3;
867
868
     /*
869
      * Send DSC comments with option...
870
      */
871
872
0
      DEBUG_printf(("2ppdEmitString: Adding code for %s=%s...",
873
0
        choices[i]->option->keyword, choices[i]->choice));
874
875
0
      if ((!_cups_strcasecmp(choices[i]->option->keyword, "PageSize") ||
876
0
           !_cups_strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
877
0
          !_cups_strcasecmp(choices[i]->choice, "Custom"))
878
0
      {
879
       /*
880
        * Variable size; write out standard size options, using the
881
  * parameter positions defined in the PPD file...
882
  */
883
884
0
        ppd_attr_t  *attr;    /* PPD attribute */
885
0
  int   pos,    /* Position of custom value */
886
0
      orientation;  /* Orientation to use */
887
0
  float   values[5];  /* Values for custom command */
888
889
890
0
        strlcpy(bufptr, "%%BeginFeature: *CustomPageSize True\n", (size_t)(bufend - bufptr + 1));
891
0
        bufptr += 37;
892
893
0
        size = ppdPageSize(ppd, "Custom");
894
895
0
        memset(values, 0, sizeof(values));
896
897
0
  if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Width")) != NULL)
898
0
  {
899
0
    pos = atoi(attr->value) - 1;
900
901
0
          if (pos < 0 || pos > 4)
902
0
      pos = 0;
903
0
  }
904
0
  else
905
0
    pos = 0;
906
907
0
  values[pos] = size->width;
908
909
0
  if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Height")) != NULL)
910
0
  {
911
0
    pos = atoi(attr->value) - 1;
912
913
0
          if (pos < 0 || pos > 4)
914
0
      pos = 1;
915
0
  }
916
0
  else
917
0
    pos = 1;
918
919
0
  values[pos] = size->length;
920
921
       /*
922
        * According to the Adobe PPD specification, an orientation of 1
923
  * will produce a print that comes out upside-down with the X
924
  * axis perpendicular to the direction of feed, which is exactly
925
  * what we want to be consistent with non-PS printers.
926
  *
927
  * We could also use an orientation of 3 to produce output that
928
  * comes out rightside-up (this is the default for many large format
929
  * printer PPDs), however for consistency we will stick with the
930
  * value 1.
931
  *
932
  * If we wanted to get fancy, we could use orientations of 0 or
933
  * 2 and swap the width and length, however we don't want to get
934
  * fancy, we just want it to work consistently.
935
  *
936
  * The orientation value is range limited by the Orientation
937
  * parameter definition, so certain non-PS printer drivers that
938
  * only support an Orientation of 0 will get the value 0 as
939
  * expected.
940
  */
941
942
0
        orientation = 1;
943
944
0
  if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize",
945
0
                          "Orientation")) != NULL)
946
0
  {
947
0
    int min_orient, max_orient; /* Minimum and maximum orientations */
948
949
950
0
          if (sscanf(attr->value, "%d%*s%d%d", &pos, &min_orient,
951
0
               &max_orient) != 3)
952
0
      pos = 4;
953
0
    else
954
0
    {
955
0
      pos --;
956
957
0
            if (pos < 0 || pos > 4)
958
0
        pos = 4;
959
960
0
            if (orientation > max_orient)
961
0
        orientation = max_orient;
962
0
      else if (orientation < min_orient)
963
0
        orientation = min_orient;
964
0
    }
965
0
  }
966
0
  else
967
0
    pos = 4;
968
969
0
  values[pos] = (float)orientation;
970
971
0
        for (pos = 0; pos < 5; pos ++)
972
0
  {
973
0
    bufptr    = _cupsStrFormatd(bufptr, bufend, values[pos], loc);
974
0
    *bufptr++ = '\n';
975
0
        }
976
977
0
  if (!choices[i]->code)
978
0
  {
979
   /*
980
    * This can happen with certain buggy PPD files that don't include
981
    * a CustomPageSize command sequence...  We just use a generic
982
    * Level 2 command sequence...
983
    */
984
985
0
    strlcpy(bufptr, ppd_custom_code, (size_t)(bufend - bufptr + 1));
986
0
          bufptr += strlen(bufptr);
987
0
  }
988
0
      }
989
0
      else if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
990
0
               (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
991
0
             != NULL)
992
0
      {
993
       /*
994
        * Custom option...
995
  */
996
997
0
        const char  *s;   /* Pointer into string value */
998
0
        cups_array_t  *params;  /* Parameters in the correct output order */
999
1000
1001
0
        params = cupsArrayNew((cups_array_func_t)ppd_compare_cparams, NULL);
1002
1003
0
        for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
1004
0
       cparam;
1005
0
       cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
1006
0
          cupsArrayAdd(params, cparam);
1007
1008
0
        snprintf(bufptr, (size_t)(bufend - bufptr + 1), "%%%%BeginFeature: *Custom%s True\n", coption->keyword);
1009
0
        bufptr += strlen(bufptr);
1010
1011
0
        for (cparam = (ppd_cparam_t *)cupsArrayFirst(params);
1012
0
       cparam;
1013
0
       cparam = (ppd_cparam_t *)cupsArrayNext(params))
1014
0
  {
1015
0
          switch (cparam->type)
1016
0
    {
1017
0
      case PPD_CUSTOM_UNKNOWN :
1018
0
          break;
1019
1020
0
      case PPD_CUSTOM_CURVE :
1021
0
      case PPD_CUSTOM_INVCURVE :
1022
0
      case PPD_CUSTOM_POINTS :
1023
0
      case PPD_CUSTOM_REAL :
1024
0
          bufptr    = _cupsStrFormatd(bufptr, bufend,
1025
0
                                cparam->current.custom_real, loc);
1026
0
                *bufptr++ = '\n';
1027
0
          break;
1028
1029
0
      case PPD_CUSTOM_INT :
1030
0
          snprintf(bufptr, (size_t)(bufend - bufptr + 1), "%d\n", cparam->current.custom_int);
1031
0
    bufptr += strlen(bufptr);
1032
0
          break;
1033
1034
0
      case PPD_CUSTOM_PASSCODE :
1035
0
      case PPD_CUSTOM_PASSWORD :
1036
0
      case PPD_CUSTOM_STRING :
1037
0
          *bufptr++ = '(';
1038
1039
0
    if (cparam->current.custom_string)
1040
0
    {
1041
0
      for (s = cparam->current.custom_string; *s; s ++)
1042
0
      {
1043
0
        if (*s < ' ' || *s == '(' || *s == ')' || *s >= 127)
1044
0
        {
1045
0
          snprintf(bufptr, (size_t)(bufend - bufptr + 1), "\\%03o", *s & 255);
1046
0
          bufptr += strlen(bufptr);
1047
0
        }
1048
0
        else
1049
0
          *bufptr++ = *s;
1050
0
      }
1051
0
    }
1052
1053
0
          *bufptr++ = ')';
1054
0
    *bufptr++ = '\n';
1055
0
          break;
1056
0
          }
1057
0
  }
1058
1059
0
  cupsArrayDelete(params);
1060
0
      }
1061
0
      else
1062
0
      {
1063
0
        snprintf(bufptr, (size_t)(bufend - bufptr + 1), "%%%%BeginFeature: *%s %s\n", choices[i]->option->keyword, choices[i]->choice);
1064
0
  bufptr += strlen(bufptr);
1065
0
      }
1066
1067
0
      if (choices[i]->code && choices[i]->code[0])
1068
0
      {
1069
0
        j = (int)strlen(choices[i]->code);
1070
0
  memcpy(bufptr, choices[i]->code, (size_t)j);
1071
0
  bufptr += j;
1072
1073
0
  if (choices[i]->code[j - 1] != '\n')
1074
0
    *bufptr++ = '\n';
1075
0
      }
1076
1077
0
      strlcpy(bufptr, "%%EndFeature\n"
1078
0
          "} stopped cleartomark\n", (size_t)(bufend - bufptr + 1));
1079
0
      bufptr += strlen(bufptr);
1080
1081
0
      DEBUG_printf(("2ppdEmitString: Offset in string is %d...",
1082
0
                    (int)(bufptr - buffer)));
1083
0
    }
1084
0
    else
1085
0
    {
1086
0
      strlcpy(bufptr, choices[i]->code, (size_t)(bufend - bufptr + 1));
1087
0
      bufptr += strlen(bufptr);
1088
0
    }
1089
1090
 /*
1091
  * Nul-terminate, free, and return...
1092
  */
1093
1094
0
  *bufptr = '\0';
1095
1096
0
  free(choices);
1097
1098
0
  return (buffer);
1099
0
}
1100
1101
1102
/*
1103
 * 'ppd_compare_cparams()' - Compare the order of two custom parameters.
1104
 */
1105
1106
static int        /* O - Result of comparison */
1107
ppd_compare_cparams(ppd_cparam_t *a,  /* I - First parameter */
1108
                    ppd_cparam_t *b)  /* I - Second parameter */
1109
0
{
1110
0
  return (a->order - b->order);
1111
0
}
1112
1113
1114
/*
1115
 * 'ppd_handle_media()' - Handle media selection...
1116
 */
1117
1118
static void
1119
ppd_handle_media(ppd_file_t *ppd) /* I - PPD file */
1120
0
{
1121
0
  ppd_choice_t  *manual_feed,   /* ManualFeed choice, if any */
1122
0
    *input_slot;    /* InputSlot choice, if any */
1123
0
  ppd_size_t  *size;      /* Current media size */
1124
0
  ppd_attr_t  *rpr;     /* RequiresPageRegion value */
1125
1126
1127
 /*
1128
  * This function determines what page size code to use, if any, for the
1129
  * current media size, InputSlot, and ManualFeed selections.
1130
  *
1131
  * We use the PageSize code if:
1132
  *
1133
  * 1. A custom media size is selected.
1134
  * 2. ManualFeed and InputSlot are not selected (or do not exist).
1135
  * 3. ManualFeed is selected but is False and InputSlot is not selected or
1136
  *    the selection has no code - the latter check done to support "auto" or
1137
  *    "printer default" InputSlot options.
1138
  *
1139
  * We use the PageRegion code if:
1140
  *
1141
  * 4. RequiresPageRegion does not exist and the PPD contains cupsFilter
1142
  *    keywords, indicating this is a CUPS-based driver.
1143
  * 5. RequiresPageRegion exists for the selected InputSlot (or "All" for any
1144
  *    InputSlot or ManualFeed selection) and is True.
1145
  *
1146
  * If none of the 5 conditions are true, no page size code is used and we
1147
  * unmark any existing PageSize or PageRegion choices.
1148
  */
1149
1150
0
  if ((size = ppdPageSize(ppd, NULL)) == NULL)
1151
0
    return;
1152
1153
0
  manual_feed = ppdFindMarkedChoice(ppd, "ManualFeed");
1154
0
  input_slot  = ppdFindMarkedChoice(ppd, "InputSlot");
1155
1156
0
  if (input_slot != NULL)
1157
0
    rpr = ppdFindAttr(ppd, "RequiresPageRegion", input_slot->choice);
1158
0
  else
1159
0
    rpr = NULL;
1160
1161
0
  if (!rpr)
1162
0
    rpr = ppdFindAttr(ppd, "RequiresPageRegion", "All");
1163
1164
0
  if (!_cups_strcasecmp(size->name, "Custom") ||
1165
0
      (!manual_feed && !input_slot) ||
1166
0
      (manual_feed && !_cups_strcasecmp(manual_feed->choice, "False") &&
1167
0
       (!input_slot || (input_slot->code && !input_slot->code[0]))) ||
1168
0
      (!rpr && ppd->num_filters > 0))
1169
0
  {
1170
   /*
1171
    * Use PageSize code...
1172
    */
1173
1174
0
    ppdMarkOption(ppd, "PageSize", size->name);
1175
0
  }
1176
0
  else if (rpr && rpr->value && !_cups_strcasecmp(rpr->value, "True"))
1177
0
  {
1178
   /*
1179
    * Use PageRegion code...
1180
    */
1181
1182
0
    ppdMarkOption(ppd, "PageRegion", size->name);
1183
0
  }
1184
0
  else
1185
0
  {
1186
   /*
1187
    * Do not use PageSize or PageRegion code...
1188
    */
1189
1190
0
    ppd_choice_t  *page;    /* PageSize/Region choice, if any */
1191
1192
0
    if ((page = ppdFindMarkedChoice(ppd, "PageSize")) != NULL)
1193
0
    {
1194
     /*
1195
      * Unmark PageSize...
1196
      */
1197
1198
0
      page->marked = 0;
1199
0
      cupsArrayRemove(ppd->marked, page);
1200
0
    }
1201
1202
0
    if ((page = ppdFindMarkedChoice(ppd, "PageRegion")) != NULL)
1203
0
    {
1204
     /*
1205
      * Unmark PageRegion...
1206
      */
1207
1208
0
      page->marked = 0;
1209
0
      cupsArrayRemove(ppd->marked, page);
1210
0
    }
1211
0
  }
1212
0
}