Coverage Report

Created: 2026-02-14 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/gsparaml.c
Line
Count
Source
1
/* Copyright (C) 2001-2026 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* gsparaml.c -  Handling of reading lists of params from strings */
18
19
#include <stdlib.h>
20
#include "gsparam.h"
21
#include "gserrors.h"
22
#include "string_.h"
23
24
static int
25
add_tokens(gs_param_list *plist, gs_param_name key, char **pp, uint *dict_count);
26
27
static int
28
walk_number(char **p, bool *is_integer)
29
0
{
30
0
    char *p1 = *p;
31
0
    bool integer = true;
32
33
0
    if (*p1 == '+')
34
0
        p1++;
35
0
    while (*p1 == ' ')
36
0
        p1++;
37
0
    while (*p1 == '-')
38
0
        p1++;
39
0
    while (*p1 == ' ')
40
0
        p1++;
41
0
    if (*p1 == 0 || ((*p1 < '0' || *p1 > '9') && (*p1 != '.')))
42
0
        return -1;
43
0
    while ((*p1 >= '0' && *p1 <= '9') || *p1 == '.') {
44
0
        if (*p1 == '.') {
45
0
            if (!integer) /* Can't cope with multiple .'s */
46
0
                return -1;
47
0
            integer = false;
48
0
        }
49
50
0
        p1++;
51
0
    }
52
    /* Allow for exponent form. */
53
0
    if (*p1 == 'e' || *p1 == 'E') {
54
0
        p1++;
55
0
        if (*p1 == '-')
56
0
            p1++;
57
0
        if (*p1 < '0' || *p1 > '9')
58
0
            return -1;
59
0
        while (*p1 >= '0' && *p1 <= '9')
60
0
            p1++;
61
0
    }
62
63
0
    *is_integer = integer;
64
0
    *p = p1;
65
66
0
    return 0;
67
0
}
68
69
/* Delimiter chars, as taken from pdf spec. Any of these characters
70
 * ends a token. */
71
static int
72
ends_token(const char *p)
73
0
{
74
0
    return (*p == 0 ||
75
0
            *p == 9 ||
76
0
            *p == 10 ||
77
0
            *p == 12 ||
78
0
            *p == 13 ||
79
0
            *p == 32 ||
80
0
            *p == '/' ||
81
0
            *p == '%' ||
82
0
            *p == '<' || *p == '>' ||
83
0
            *p == '[' || *p == ']' ||
84
0
            *p == '{' || *p == '}' ||
85
0
            *p == '(' || *p == ')');
86
0
}
87
88
/* Dictionaries are surprisingly easy, we just make a param_dict
89
 * and then call the existing routine to parse the string and
90
 * add tokens to the parameter list contained in the dictionary.
91
 */
92
static int
93
process_dict(gs_memory_t *mem, gs_c_param_list *plist, gs_param_name key, char **p)
94
0
{
95
0
    gs_param_dict dict;
96
0
    int code, code2;
97
98
    /* We are implicitly relying on that fact that we're working to
99
     * C param lists, not ref param lists here, as C param lists don't
100
     * need the size up front, but ref param lists do. This makes the
101
     * recursion MUCH simpler. */
102
0
    code = param_begin_write_dict((gs_param_list *)plist, key, &dict, false);
103
0
    if (code < 0)
104
0
        return code;
105
106
0
    gs_param_list_set_persistent_keys(dict.list, false);
107
108
0
    dict.size = 0;
109
0
    code = add_tokens(dict.list, NULL, p, &dict.size);
110
0
    if (code >= 0) {
111
0
        if ((*p)[0] != '>' || (*p)[1] != '>')
112
0
            code = gs_error_syntaxerror;
113
0
        else
114
0
           (*p) += 2;
115
0
    }
116
0
    code2 = param_end_write_dict((gs_param_list *)plist, key, &dict);
117
0
    return code < 0 ? code : code2;
118
0
}
119
120
static int
121
process_dict_or_hexstring(gs_memory_t *mem, gs_c_param_list *plist, gs_param_name key, char **p)
122
0
{
123
0
    char *p1 = *p, *src, *dest, data;
124
0
    int i;
125
0
    gs_param_string ps;
126
127
0
    if (p1[1] == '<') {
128
0
        *p += 2;
129
0
        return process_dict(mem, plist, key, p);
130
0
    }
131
132
0
    dest = p1;
133
0
    src = p1+1;
134
0
    while (*src && *src != '>') {
135
0
        data = 0;
136
0
        for (i=0;i<2;i++) {
137
0
            if (*src >= '0' && *src <= '9') {
138
0
                data = (data << 4);
139
0
                data += (*src - '0');
140
0
            } else if (*src >= 'A' && *src <= 'F') {
141
0
                data = (data << 4);
142
0
                data += (*src - 'A' + 10);
143
0
            } else if (*src >= 'a' && *src <= 'f') {
144
0
                data = (data << 4);
145
0
                data += (*src - 'a' + 10);
146
0
            } else {
147
0
                return -1;
148
0
            }
149
0
            src++;
150
0
        }
151
0
        *dest++ = data;
152
0
    }
153
154
0
    if (*src == 0)
155
0
        return -1;
156
157
0
    *p = src + 1;
158
159
0
    ps.data = (const byte *)p1;
160
0
    ps.size = dest - p1;
161
0
    ps.persistent = false;
162
0
    return param_write_string((gs_param_list *)plist, key, &ps);
163
0
}
164
165
/* On entry, p points to the '/'. Because we need to null terminate
166
 * to cope with reading the key of key/value pairs, we move all the
167
 * chars back by 1, overwriting the '/' to give us room. This avoids
168
 * us relying on trailing whitespace. */
169
static int
170
process_name(gs_memory_t *mem, gs_c_param_list *plist, gs_param_name *key, char **p)
171
0
{
172
0
    char *out = *p;
173
0
    char *in = *p + 1;
174
0
    char *start = out;
175
0
    gs_param_string ps;
176
177
0
    while (!ends_token(in)) {
178
0
        if (*in == '#') {
179
0
            int v;
180
0
            if (in[1] >= '0' && in[1] <= '9')
181
0
                v = (in[1] - '0')<<4;
182
0
            else if (in[1] >= 'a' && in[1] <= 'f')
183
0
                v = (in[1] - 'a' + 10)<<4;
184
0
            else if (in[1] >= 'A' && in[1] <= 'F')
185
0
                v = (in[1] - 'a' + 10)<<4;
186
0
            else
187
0
                return -1;
188
0
            if (in[2] >= '0' && in[2] <= '9')
189
0
                v += (in[2] - '0');
190
0
            else if (in[2] >= 'a' && in[2] <= 'f')
191
0
                v += (in[2] - 'a' + 10);
192
0
            else if (in[2] >= 'A' && in[2] <= 'F')
193
0
                v += (in[2] - 'a' + 10);
194
0
            else
195
0
                return -1;
196
0
            if (v == 0)
197
0
                return -1;
198
0
            *out++ = v;
199
0
            in += 3;
200
0
            continue;
201
0
        }
202
0
        *out++ = *in++;
203
0
    }
204
205
    /* Null terminate (in case it's the '*key = NULL' case below) */
206
0
    *out = 0;
207
0
    *p = in;
208
209
0
    if (*key == NULL)
210
0
        *key = (gs_param_name)start;
211
0
    else {
212
0
        ps.data = (const byte *)start;
213
0
        ps.size = out - start;
214
0
        ps.persistent = false;
215
0
        param_write_name((gs_param_list *)plist, *key, &ps);
216
0
        *key = NULL;
217
0
    }
218
0
    return 0;
219
0
}
220
221
static int
222
process_string(gs_memory_t *mem, gs_c_param_list *plist, gs_param_name key, char **p)
223
0
{
224
0
    char *p1 = *p;
225
0
    char *start = p1 + 1;
226
0
    gs_param_string ps;
227
228
0
    while (*p1 && *p1 != ')')
229
0
        p1++;
230
231
0
    if (*p1 == 0)
232
0
        return -1;
233
234
0
    *p = p1 + 1; /* Resume after the ')' */
235
236
0
    ps.data = (const byte *)start;
237
0
    ps.size = p1-start;
238
0
    ps.persistent = false;
239
0
    return param_write_string((gs_param_list *)plist, key, &ps);
240
0
}
241
242
/* Arrays are *way* more complicated than dicts :-(
243
 * We have 4 different kinds of arrays; name, string, int and float.
244
 * It seems that parameter arrays can only contain homogenous data, it
245
 * all has to be of the same type. This complicates matters because we
246
 * can't know in advance what the type is!
247
 *
248
 * So we only handle 3 types of array; int, float and string. Anything
249
 * which isn't one of those either gets converted to a string or (arrays
250
 * and dictionaries) throws an error.
251
 *
252
 * For numbers, we look at the first element, if it's an integer we make
253
 * an int array, otherwise we make a float array. If we start an int array
254
 * and later encounter a float, we make a new float array, copy the existing
255
 * integers into it (converting to floats) and throw away the old int array.
256
 *
257
 * Otherwise if we encounter an object whose type doesn't match the array we
258
 * created we throw an error.
259
 */
260
static int
261
process_array(gs_memory_t *mem, gs_c_param_list *plist, gs_param_name key, char **p)
262
0
{
263
0
    int code = 0;
264
0
    gs_param_type array_type = gs_param_type_null;
265
0
    int index = 0, array_max = 0;
266
0
    char *start = *p + 1, *p1 = start;
267
0
    gs_param_string *parray = 0L;
268
0
    char *array_data = 0x00;
269
0
    gs_param_string_array string_array;
270
0
    gs_param_int_array int_array;
271
0
    gs_param_float_array float_array;
272
273
0
    p1 = start;
274
275
0
    while (*p1 != ']' && code == 0) {
276
0
        switch (*p1) {
277
0
            case ' ':
278
0
                p1++;
279
0
                break;
280
281
            /* We used to parse 'false' and 'true' here, but they ended
282
             * up as string params, rather that bools, thus making
283
             * [ false ] and [ (false) ] parse to the be the same thing.
284
             * That feels wrong, so we've removed the code until param
285
             * lists actually support arrays of bools. */
286
287
0
            case '<':
288
0
                if (array_type != gs_param_type_null && array_type != gs_param_type_string_array) {
289
0
                    code = gs_error_typecheck;
290
0
                    break;
291
0
                }
292
0
                if (index == array_max) {
293
0
                    int new_max = array_max * 2;
294
0
                    if (new_max == 0)
295
0
                        new_max = 32;
296
0
                    if (array_data == NULL) {
297
0
                        array_data = (char *)gs_alloc_bytes(mem, sizeof(gs_param_string) * (size_t)new_max, "param string array in param parsing");
298
0
                        if (array_data == NULL) {
299
0
                            code = gs_error_VMerror;
300
0
                            break;
301
0
                        }
302
0
                    } else {
303
0
                        char *new_array = (char *)gs_resize_object(mem, array_data, sizeof(gs_param_string) * new_max, "param string array in param parsing");
304
0
                        if (new_array == NULL) {
305
0
                            code = gs_error_VMerror;
306
0
                            break;
307
0
                        }
308
0
                        array_data = new_array;
309
0
                    }
310
0
                    array_max = new_max;
311
0
                    array_type = gs_param_type_string_array;
312
0
                }
313
0
                if (*(p1+1) == '<') {
314
0
                    code = gs_error_typecheck;
315
0
                    break;
316
                    /* dictionary inside an array, not supported */
317
0
                } else {
318
0
                    char *src, *dest;
319
0
                    char data = 0;
320
0
                    int i;
321
322
0
                    parray = (gs_param_string *)array_data;
323
0
                    src = dest = ++p1;
324
0
                    parray[index].data = (const byte *)p1;
325
0
                    while (*src && *src != '>') {
326
0
                        data = 0;
327
0
                        for (i=0;i<2;i++) {
328
0
                            if (*src >= '0' && *src <= '9') {
329
0
                                data = (data << 4);
330
0
                                data += (*src - '0');
331
0
                            } else if (*src >= 'A' && *src <= 'F') {
332
0
                                data = (data << 4);
333
0
                                data += (*src - 'A' + 10);
334
0
                            } else if (*src >= 'a' && *src <= 'f') {
335
0
                                data = (data << 4);
336
0
                                data += (*src - 'a' + 10);
337
0
                            } else {
338
0
                                goto return_minus_one;
339
0
                            }
340
0
                            src++;
341
0
                        }
342
0
                        *dest++ = data;
343
0
                    }
344
0
                    parray[index].size = dest - p1;
345
0
                    parray[index++].persistent = false;
346
0
                    p1 = src;
347
0
                }
348
0
                break;
349
350
0
            case '/':
351
0
                if (array_type != gs_param_type_null && array_type != gs_param_type_name_array) {
352
0
                    code = gs_error_typecheck;
353
0
                    break;
354
0
                }
355
0
                if (index == array_max) {
356
0
                    int new_max = array_max * 2;
357
0
                    if (new_max == 0)
358
0
                        new_max = 32;
359
0
                    if (array_data == NULL) {
360
0
                        array_data = (char *)gs_alloc_bytes(mem, sizeof(gs_param_string) * (size_t)new_max, "param string array in param parsing");
361
0
                        if (array_data == NULL) {
362
0
                            code = gs_error_VMerror;
363
0
                            break;
364
0
                        }
365
0
                    } else {
366
0
                        char *new_array = (char *)gs_resize_object(mem, array_data, sizeof(gs_param_string) * new_max, "param string array in param parsing");
367
0
                        if (new_array == NULL) {
368
0
                            code = gs_error_VMerror;
369
0
                            break;
370
0
                        }
371
0
                        array_data = new_array;
372
0
                    }
373
0
                    array_max = new_max;
374
0
                    array_type = gs_param_type_name_array;
375
0
                }
376
0
                parray = (gs_param_string *)array_data;
377
0
                parray[index].data = (const byte *)++p1;
378
0
                while (!ends_token(p1))
379
0
                    p1++;
380
0
                parray[index].size = p1 - (char *)(parray[index].data);
381
0
                if (parray[index].size == 0)
382
0
                    goto return_minus_one;
383
0
                parray[index++].persistent = false;
384
0
                break;
385
386
0
            case '(':
387
0
                if (array_type != gs_param_type_null && array_type != gs_param_type_string_array) {
388
0
                    code = gs_error_typecheck;
389
0
                    break;
390
0
                }
391
0
                if (index == array_max) {
392
0
                    int new_max = array_max * 2;
393
0
                    if (new_max == 0)
394
0
                        new_max = 32;
395
0
                    if (array_data == NULL) {
396
0
                        array_data = (char *)gs_alloc_bytes(mem, sizeof(gs_param_string) * (size_t)new_max, "param string array in param parsing");
397
0
                        if (array_data == NULL) {
398
0
                            code = gs_error_VMerror;
399
0
                            break;
400
0
                        }
401
0
                    } else {
402
0
                        char *new_array = (char *)gs_resize_object(mem, array_data, sizeof(gs_param_string) * new_max, "param string array in param parsing");
403
0
                        if (new_array == NULL) {
404
0
                            code = gs_error_VMerror;
405
0
                            break;
406
0
                        }
407
0
                        array_data = new_array;
408
0
                    }
409
0
                    array_max = new_max;
410
0
                    array_type = gs_param_type_string_array;
411
0
                }
412
0
                parray = (gs_param_string *)array_data;
413
0
                p1++; // Skip '('
414
0
                parray[index].data = (const byte *)p1;
415
0
                while (*p1 && *p1 != ')')
416
0
                    p1++;
417
0
                if (*p1 == 0)
418
0
                    goto return_minus_one;
419
0
                parray[index].size = p1 - (char *)(parray[index].data);
420
0
                parray[index++].persistent = false;
421
0
                p1++; // Skip ')'
422
0
                break;
423
0
            case '[':
424
                /* Nested arrays, not supported */
425
0
                code = gs_error_typecheck;
426
0
                break;
427
0
            case '0':
428
0
            case '1':
429
0
            case '2':
430
0
            case '3':
431
0
            case '4':
432
0
            case '5':
433
0
            case '6':
434
0
            case '7':
435
0
            case '8':
436
0
            case '9':
437
0
            case '.':
438
0
            case '+':
439
0
            case '-':
440
0
                if (array_type == gs_param_type_string_array || array_type == gs_param_type_name_array ) {
441
0
                    code = gs_error_typecheck;
442
0
                    break;
443
0
                } else {
444
0
                    bool integer;
445
0
                    const char *start = p1;
446
0
                    char c;
447
0
                    float *floats;
448
0
                    int *ints, i;
449
450
0
                    code = walk_number(&p1, &integer);
451
0
                    if (code < 0)
452
0
                        break;
453
454
0
                    if (array_type == gs_param_type_int_array && !integer) {
455
0
                        ints = (int *)array_data;
456
0
                        floats = (float *)gs_alloc_bytes(mem, sizeof(float) * (size_t)array_max, "param string array in param parsing");
457
0
                        if (floats == NULL){
458
0
                            code = gs_error_VMerror;
459
0
                            break;
460
0
                        }
461
0
                        array_type = gs_param_type_float_array;
462
0
                        for (i=0;i<index;i++){
463
0
                            floats[i] = (float)(ints[i]);
464
0
                        }
465
0
                        gs_free_object(mem, ints, "param string array in param parsing");
466
0
                        array_data = (char *)floats;
467
0
                    }
468
0
                    if (index == array_max) {
469
0
                        union { float f; int i; } size_me;
470
0
                        int new_max = array_max * 2;
471
0
                        if (new_max == 0) {
472
0
                            new_max = 32;
473
0
                            array_type = integer ? gs_param_type_int_array : gs_param_type_float_array;
474
0
                        }
475
0
                        if (array_data == NULL) {
476
0
                            array_data = (char *)gs_alloc_bytes(mem, sizeof(size_me) * (size_t)new_max, "param string array in param parsing");
477
0
                            if (array_data == NULL) {
478
0
                                code = gs_error_VMerror;
479
0
                                break;
480
0
                            }
481
0
                        } else {
482
0
                            char *new_array = (char *)gs_resize_object(mem, array_data, sizeof(size_me) * (size_t)new_max, "param string array in param parsing");
483
0
                            if (new_array == NULL) {
484
0
                                code = gs_error_VMerror;
485
0
                                break;
486
0
                            }
487
0
                            array_data = new_array;
488
0
                        }
489
0
                        array_max = new_max;
490
0
                    }
491
0
                    c = *p1;
492
0
                    *p1 = 0;
493
0
                    if (array_type == gs_param_type_int_array) {
494
0
                        ints = (int *)array_data;
495
0
                        ints[index++] = (int)atol(start);
496
0
                    } else {
497
0
                        floats = (float *)array_data;
498
0
                        floats[index++] = (float)atof(start);
499
0
                    }
500
0
                    *p1 = c;
501
0
                }
502
0
                break;
503
0
            default:
504
0
                code = gs_error_typecheck;
505
0
                break;
506
0
        }
507
0
    }
508
0
    if (0) {
509
0
return_minus_one:
510
0
        code = -1;
511
0
    }
512
513
    /* Now we have to deal with adding the array to the parm list, there are
514
     * (of course!) different calls for each array type....
515
     */
516
0
    if (code >= 0)
517
0
    {
518
0
        *p = p1 + 1;
519
0
        switch(array_type) {
520
0
            case gs_param_type_string_array:
521
0
                string_array.data = (const gs_param_string *)array_data;
522
0
                string_array.persistent = 0;
523
0
                string_array.size = index;
524
0
                code = param_write_string_array((gs_param_list *)plist, key, &string_array);
525
0
                break;
526
0
            case gs_param_type_name_array:
527
0
                string_array.data = (const gs_param_string *)array_data;
528
0
                string_array.persistent = 0;
529
0
                string_array.size = index;
530
0
                code = param_write_name_array((gs_param_list *)plist, key, &string_array);
531
0
                break;
532
0
            case gs_param_type_int_array:
533
0
                int_array.data = (const int *)array_data;
534
0
                int_array.persistent = 0;
535
0
                int_array.size = index;
536
0
                code = param_write_int_array((gs_param_list *)plist, key, &int_array);
537
0
                break;
538
0
            case gs_param_type_float_array:
539
0
                float_array.data = (const float *)array_data;
540
0
                float_array.persistent = 0;
541
0
                float_array.size = index;
542
0
                code = param_write_float_array((gs_param_list *)plist, key, &float_array);
543
0
                break;
544
545
0
            default:
546
0
                break;
547
0
        }
548
0
    }
549
550
    /* And now we can throw away the array data, we copied it to the param list. */
551
0
    gs_free_object(mem, array_data, "param string array in param parsing");
552
553
0
    return code;
554
0
}
555
556
/* We rely on the fact that we can overwrite, then restore *end here. */
557
static int
558
process_number(gs_memory_t *mem, gs_c_param_list *plist, gs_param_name key, char **p)
559
0
{
560
0
    bool integer;
561
0
    const char *start = *p;
562
0
    char c;
563
0
    int code = walk_number(p, &integer);
564
565
0
    if (code < 0)
566
0
        return code;
567
568
    /* Hacky. Null terminate so that atof/atol don't overrun. This is
569
     * safe because at worst p points to the null terminator byte, and
570
     * we can safely overwrite end for a moment. Ick. */
571
0
    c = **p;
572
0
    **p = 0;
573
0
    if (!integer) {
574
0
        float f = (float)atof(start);
575
0
        code = param_write_float((gs_param_list *)plist, key, (float *)&f);
576
0
    } else {
577
        /* FIXME: Should probably really be int64_t here rather than int? */
578
0
        long i = atol(start);
579
0
        code = param_write_long((gs_param_list *)plist, key, &i);
580
0
    }
581
0
    **p = c;
582
583
0
    return code;
584
0
}
585
586
static int
587
add_tokens(gs_param_list *plist, gs_param_name key, char **pp, uint *dict_count)
588
0
{
589
0
    char *p = *pp;
590
0
    int code = 0;
591
    /* If single == true, then we are looking for a single value,
592
     * otherwise it's a list of key/value pairs */
593
0
    int single = (key != NULL);
594
    /* If single_done, then we've read our single value. Any non
595
     * whitespace we read is an error. */
596
0
    int single_done = 0;
597
0
    bool f = false, t = true;
598
599
0
    while (*p) {
600
0
        switch (*p) {
601
0
            case ' ':
602
0
                p++;
603
0
                break;
604
0
            case 'f':
605
0
                if (single_done || key == NULL)
606
0
                    return -1;
607
0
                if (strncmp(p, "false", 5) != 0)
608
0
                    return -1;
609
0
                if (!ends_token(p+5))
610
0
                    return -1;
611
0
                code = param_write_bool((gs_param_list *)plist, key, &f);
612
0
                if (code >= 0 && dict_count != NULL)
613
0
                    (*dict_count)++;
614
0
                p += 5;
615
0
                single_done = single;
616
0
                key = NULL;
617
0
                break;
618
0
            case 't':
619
0
                if (single_done || key == NULL)
620
0
                    return -1;
621
0
                if (strncmp(p, "true", 4) != 0)
622
0
                    return -1;
623
0
                if (!ends_token(p+4))
624
0
                    return -1;
625
0
                code = param_write_bool((gs_param_list *)plist, key, &t);
626
0
                if (code >= 0 && dict_count != NULL)
627
0
                    (*dict_count)++;
628
0
                p += 4;
629
0
                single_done = single;
630
0
                key = NULL;
631
0
                break;
632
0
            case '<':
633
0
                if (single_done || key == NULL)
634
0
                    return -1;
635
0
                code = process_dict_or_hexstring(plist->memory, (gs_c_param_list *)plist, key, &p);
636
0
                if (code >= 0 && dict_count != NULL)
637
0
                    (*dict_count)++;
638
0
                single_done = single;
639
0
                key = NULL;
640
0
                break;
641
0
            case '/':
642
0
            {
643
0
                int have_key = (key != NULL);
644
0
                if (single_done)
645
0
                    return -1;
646
0
                code = process_name(plist->memory, (gs_c_param_list *)plist, &key, &p);
647
0
                if (code >= 0 && have_key && dict_count != NULL)
648
0
                    (*dict_count)++;
649
0
                if (have_key) {
650
0
                    single_done = single;
651
0
                    key = NULL;
652
0
                }
653
0
                break;
654
0
            }
655
0
            case '(':
656
0
                if (single_done || key == NULL)
657
0
                    return -1;
658
0
                code = process_string(plist->memory, (gs_c_param_list *)plist, key, &p);
659
0
                if (code >= 0 && dict_count != NULL)
660
0
                    (*dict_count)++;
661
0
                single_done = single;
662
0
                key = NULL;
663
0
                break;
664
0
            case '[':
665
0
                if (single_done || key == NULL)
666
0
                    return -1;
667
0
                code = process_array(plist->memory, (gs_c_param_list *)plist, key, &p);
668
0
                if (code >= 0 && dict_count != NULL)
669
0
                    (*dict_count)++;
670
0
                single_done = single;
671
0
                key = NULL;
672
0
                break;
673
0
            case '0':
674
0
            case '1':
675
0
            case '2':
676
0
            case '3':
677
0
            case '4':
678
0
            case '5':
679
0
            case '6':
680
0
            case '7':
681
0
            case '8':
682
0
            case '9':
683
0
            case '.':
684
0
                if (single_done || key == NULL)
685
0
                    return -1;
686
0
                code = process_number(plist->memory, (gs_c_param_list *)plist, key, &p);
687
0
                if (code >= 0 && dict_count != NULL)
688
0
                    (*dict_count)++;
689
0
                single_done = single;
690
0
                key = NULL;
691
0
                break;
692
0
            case '>':
693
0
                if (dict_count != NULL && p[1] == '>') {
694
0
                    if (key != NULL)
695
0
                        return -1;
696
0
                    *pp = p;
697
0
                    return 0;
698
0
                }
699
0
                return -1;
700
0
            default:
701
0
                return -1;
702
0
                break;
703
0
        }
704
0
        if (code < 0)
705
0
            return code;
706
0
    }
707
708
0
    *pp = p;
709
0
    return 0;
710
0
}
711
712
/* Given a string to parse (a list of key/value pairs), parse it and add
713
 * what we find to the supplied param list.
714
 */
715
int gs_param_list_add_tokens(gs_param_list *plist, char *p)
716
0
{
717
0
    char *r = p;
718
0
    return add_tokens(plist, NULL, &r, NULL);
719
0
}
720
721
/* Given a key, and a string representing a single (maybe complex) value
722
 * to parse, parse it and add what we find to the supplied param list.
723
 */
724
int gs_param_list_add_parsed_value(gs_param_list *plist, gs_param_name key, const char *p)
725
0
{
726
0
    size_t len;
727
0
    char *q, *r;
728
0
    int code;
729
730
    /* Treat NULL as the empty string. */
731
0
    if (p == NULL)
732
0
        return 0;
733
734
0
    len = strlen(p) + 1;
735
0
    q = (char *)gs_alloc_bytes(plist->memory, len, "gs_param_list_add_parsed_value");
736
0
    if (q == NULL)
737
0
        return_error(gs_error_VMerror);
738
0
    memcpy(q, p, len);
739
740
0
    r = q;
741
0
    code = add_tokens(plist, key, &r, NULL);
742
743
0
    gs_free_object(plist->memory, q, "gs_param_list_add_parsed_value");
744
745
0
    return code;
746
0
}
747
748
typedef struct {
749
    char *value;
750
    int *len;
751
    char last;
752
} outstate;
753
754
static void
755
out_string(outstate *out, const char *str)
756
0
{
757
0
    int slen = str ? (int)strlen(str) : 0;
758
759
0
    if (slen == 0)
760
0
        return;
761
762
0
    if (out->last != 0 && out->last != ')' && out->last != '>' &&
763
0
        out->last != '[' && out->last != ']' && out->last != '}' &&
764
0
        *str != '(' && *str != ')' && *str != '<' && *str != '>' &&
765
0
        *str != '[' && *str != ']' && *str != '{' && *str != '}' &&
766
0
        *str != '/') {
767
        /* We need to insert some whitespace */
768
0
        *out->len += 1;
769
0
        if (out->value != NULL) {
770
0
            *out->value++ = ' ';
771
0
            *out->value = 0;
772
0
        }
773
0
    }
774
775
0
    *out->len += slen;
776
0
    out->last = str[slen-1];
777
0
    if (out->value != NULL) {
778
0
        memcpy(out->value, str, slen);
779
0
        out->value += slen;
780
0
        *out->value = 0;
781
0
    }
782
0
}
783
784
static void
785
string_to_string(const char *data, int len, outstate *out)
786
0
{
787
0
    int i;
788
0
    char text[4];
789
0
    const char *d = data;
790
791
    /* Check to see if we have any awkward chars */
792
0
    for (i = len; i != 0; i--) {
793
0
        if (*d < 32 || *d >= 127 || *d == ')')
794
0
            break;
795
0
        d++;
796
0
    }
797
798
    /* No awkward chars, do it the easy way. */
799
0
    if (i == 0) {
800
0
        d = data;
801
0
        out_string(out, "(");
802
0
        out->last = 0;
803
0
        text[1] = 0;
804
0
        for (i = len; i != 0; i--) {
805
0
            text[0] = *d++;
806
0
            out->last = 0;
807
0
            out_string(out, text);
808
0
        }
809
0
        out->last = 0;
810
0
        out_string(out, ")");
811
0
        return;
812
0
    }
813
814
    /* Output as hexstring */
815
0
    out_string(out, "<");
816
0
    text[2] = 0;
817
0
    for (i = 0; i < len; i++) {
818
0
        text[0] = "0123456789ABCDEF"[(*data >> 4) & 15];
819
0
        text[1] = "0123456789ABCDEF"[(*data++) & 15];
820
0
        out->last = 0;
821
0
        out_string(out, text);
822
0
    }
823
0
    out_string(out, ">");
824
0
}
825
826
static void
827
name_to_string(const char *data, int len, outstate *out)
828
0
{
829
0
    int i;
830
0
    char text[4];
831
832
0
    out_string(out, "/");
833
0
    text[3] = 0;
834
0
    for (i = 0; i < len; i++) {
835
0
        char c = *data++;
836
0
        if (c > 32 && c < 127 && c != '/' && c != '#' &&
837
0
            c != '<' && c != '>' &&
838
0
            c != '[' && c != ']' &&
839
0
            c != '(' && c != ')' &&
840
0
            c != '{' && c != '}') {
841
0
            text[0] = c;
842
0
            text[1] = 0;
843
0
        } else {
844
0
            text[0] = '#';
845
0
            text[1] = "0123456789ABCDEF"[(c >> 4) & 15];
846
0
            text[2] = "0123456789ABCDEF"[c & 15];
847
0
        }
848
0
        out->last = 0;
849
0
        out_string(out, text);
850
0
    }
851
0
}
852
853
static void
854
int_array_to_string(gs_param_int_array ia, outstate *out)
855
0
{
856
0
    int i;
857
0
    char text[32];
858
859
0
    out_string(out, "[");
860
0
    for (i = 0; i < ia.size; i++) {
861
0
        gs_snprintf(text, sizeof(text), "%d", ia.data[i]);
862
0
        out_string(out, text);
863
0
    }
864
0
    out_string(out, "]");
865
0
}
866
867
static void
868
print_float(char text[32], float f)
869
0
{
870
    /* We attempt to tidy up %f's somewhat unpredictable output
871
     * here, so rather than printing 0.10000000 we print 0.1 */
872
0
    char *p = text;
873
0
    int frac = 0;
874
0
    gs_snprintf(text, 32, "%f", f);
875
    /* Find the terminator, or 'e' to spot exponent mode. */
876
0
    while (*p && *p != 'e' && *p != 'E') {
877
0
        if (*p == '.')
878
0
            frac = 1;
879
0
        p++;
880
0
    }
881
    /* If we've hit the terminator, and passed a '.' at some point
882
     * we know we potentially have a tail to tidy up. */
883
0
    if (*p == 0 && frac) {
884
0
        p--;
885
        /* Clear a trail of 0's. */
886
0
        while (*p == '0')
887
0
            *p-- = 0;
888
        /* If we cleared the entire fractional part, remove the . */
889
0
        if (*p == '.') {
890
            /* Allow for -.0000 => -0 rather than - */
891
0
            if (p == text || p[-1] < '0' || p[-1] > '9')
892
0
                *p = '0', p[1] = 0;
893
0
            else
894
0
                p[0] = 0;
895
0
        }
896
0
    }
897
0
}
898
899
static void
900
float_array_to_string(gs_param_float_array fa, outstate *out)
901
0
{
902
0
    int i;
903
0
    char text[32];
904
905
0
    out_string(out, "[");
906
0
    for (i = 0; i < fa.size; i++) {
907
0
        print_float(text, fa.data[i]);
908
0
        out_string(out, text);
909
0
    }
910
0
    out_string(out, "]");
911
0
}
912
913
static void
914
string_array_to_string(gs_param_string_array sa, outstate *out)
915
0
{
916
0
    int i;
917
918
0
    out_string(out, "[");
919
0
    for (i = 0; i < sa.size; i++) {
920
0
        string_to_string((const char *)sa.data[i].data, sa.data[i].size, out);
921
0
    }
922
0
    out_string(out, "]");
923
0
}
924
925
static void
926
name_array_to_string(gs_param_string_array na, outstate *out)
927
0
{
928
0
    int i;
929
930
0
    out_string(out, "[");
931
0
    for (i = 0; i < na.size; i++) {
932
0
        name_to_string((const char *)na.data[i].data, na.data[i].size, out);
933
0
    }
934
0
    out_string(out, "]");
935
0
}
936
937
static int to_string(gs_param_list *plist, gs_param_name key, outstate *out);
938
939
static int
940
out_dict(gs_param_collection *dict, outstate *out)
941
0
{
942
0
    gs_param_list *plist = dict->list;
943
0
    gs_param_enumerator_t enumerator;
944
0
    gs_param_key_t key;
945
0
    int code;
946
947
0
    out_string(out, "<<");
948
949
0
    param_init_enumerator(&enumerator);
950
0
    while ((code = param_get_next_key(plist, &enumerator, &key)) == 0) {
951
0
        char string_key[256]; /* big enough for any reasonable key */
952
953
0
        if (key.size > sizeof(string_key) - 1) {
954
0
            code = gs_note_error(gs_error_rangecheck);
955
0
            break;
956
0
        }
957
0
        memcpy(string_key, key.data, key.size);
958
0
        string_key[key.size] = 0;
959
0
        name_to_string((char *)key.data, key.size, out);
960
0
        code = to_string(plist, string_key, out);
961
0
        if (code < 0)
962
0
            break;
963
0
    }
964
965
0
    out_string(out, ">>");
966
0
    if (code == 1)
967
0
        code = 0;
968
969
0
    return code;
970
0
}
971
972
static int
973
to_string(gs_param_list *plist, gs_param_name key, outstate *out)
974
0
{
975
0
    int code = 0;
976
0
    gs_param_typed_value pvalue;
977
978
0
    pvalue.type = gs_param_type_any;
979
0
    code = param_read_typed(plist, key, &pvalue);
980
0
    if (code < 0)
981
0
        return code;
982
0
    if (code > 0)
983
0
        return_error(gs_error_undefined);
984
0
    switch (pvalue.type) {
985
0
    case gs_param_type_null:
986
0
        out_string(out, "null");
987
0
        break;
988
0
    case gs_param_type_bool:
989
0
        if (pvalue.value.b)
990
0
            out_string(out, "true");
991
0
        else
992
0
            out_string(out, "false");
993
0
        break;
994
0
    case gs_param_type_int:
995
0
    {
996
0
        char text[32];
997
0
        gs_snprintf(text, sizeof(text), "%d", pvalue.value.i);
998
0
        out_string(out, text);
999
0
        break;
1000
0
    }
1001
0
    case gs_param_type_i64:
1002
0
    {
1003
0
        char text[32];
1004
0
        gs_snprintf(text, sizeof(text), "%"PRId64, pvalue.value.i64);
1005
0
        out_string(out, text);
1006
0
        break;
1007
0
    }
1008
0
    case gs_param_type_long:
1009
0
    {
1010
0
        char text[32];
1011
0
        gs_snprintf(text, sizeof(text), "%ld", pvalue.value.l);
1012
0
        out_string(out, text);
1013
0
        break;
1014
0
    }
1015
0
    case gs_param_type_size_t:
1016
0
    {
1017
0
        char text[32];
1018
0
        gs_snprintf(text, sizeof(text), "%"PRIdSIZE, pvalue.value.z);
1019
0
        out_string(out, text);
1020
0
        break;
1021
0
    }
1022
0
    case gs_param_type_float:
1023
0
    {
1024
0
        char text[32];
1025
0
        print_float(text, pvalue.value.f);
1026
0
        out_string(out, text);
1027
0
        break;
1028
0
    }
1029
0
    case gs_param_type_dict:
1030
0
        code = out_dict(&pvalue.value.d, out);
1031
0
        break;
1032
0
    case gs_param_type_dict_int_keys:
1033
0
        return -1;
1034
0
    case gs_param_type_array:
1035
0
        return -1;
1036
0
    case gs_param_type_string:
1037
0
        string_to_string((char *)pvalue.value.s.data, pvalue.value.s.size, out);
1038
0
        break;
1039
0
    case gs_param_type_name:
1040
0
        name_to_string((char *)pvalue.value.n.data, pvalue.value.n.size, out);
1041
0
        break;
1042
0
    case gs_param_type_int_array:
1043
0
        int_array_to_string(pvalue.value.ia, out);
1044
0
        break;
1045
0
    case gs_param_type_float_array:
1046
0
        float_array_to_string(pvalue.value.fa, out);
1047
0
        break;
1048
0
    case gs_param_type_string_array:
1049
0
        string_array_to_string(pvalue.value.sa, out);
1050
0
        break;
1051
0
    case gs_param_type_name_array:
1052
0
        name_array_to_string(pvalue.value.na, out);
1053
0
        break;
1054
0
    default:
1055
0
        return -1;
1056
0
    }
1057
1058
0
    return code;
1059
0
}
1060
1061
int gs_param_list_to_string(gs_param_list *plist, gs_param_name key, char *value, int *len)
1062
0
{
1063
0
    outstate out;
1064
1065
0
    out.value = value;
1066
0
    out.len = len;
1067
0
    out.last = 0;
1068
0
    *len = 1; /* Always space for the terminator. */
1069
0
    if (value)
1070
0
        *value = 0;
1071
0
    return to_string(plist, key, &out);
1072
0
}
1073
1074
int gs_param_list_dump(gs_param_list *plist)
1075
0
{
1076
0
    gs_param_enumerator_t enumerator;
1077
0
    gs_param_key_t key;
1078
0
    int code;
1079
0
    char buffer[4096];
1080
0
    int len;
1081
1082
0
    param_init_enumerator(&enumerator);
1083
0
    while ((code = param_get_next_key(plist, &enumerator, &key)) == 0) {
1084
0
        char string_key[256]; /* big enough for any reasonable key */
1085
1086
0
        if (key.size > sizeof(string_key) - 1) {
1087
0
            code = gs_note_error(gs_error_rangecheck);
1088
0
            break;
1089
0
        }
1090
0
        memcpy(string_key, key.data, key.size);
1091
0
        string_key[key.size] = 0;
1092
0
        dlprintf1("%s ", string_key);
1093
0
        code = gs_param_list_to_string(plist, string_key, buffer, &len);
1094
0
        if (code < 0)
1095
0
            break;
1096
0
        dlprintf1("%s ", buffer);
1097
0
    }
1098
0
    dlprintf("\n");
1099
0
    return code;
1100
0
}