Coverage Report

Created: 2026-08-31 07:17

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