Coverage Report

Created: 2025-11-11 07:03

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