fcxml.c 84.4 KB
Newer Older
Keith Packard's avatar
Keith Packard committed
1
/*
2
 * fontconfig/src/fcxml.c
Keith Packard's avatar
Keith Packard committed
3
 *
4
 * Copyright © 2002 Keith Packard
Apteryks's avatar
Apteryks committed
5
 * Copyright © 2020 Maxim Cournoyer
Keith Packard's avatar
Keith Packard committed
6
7
8
9
10
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
11
 * documentation, and that the name of the author(s) not be used in
Keith Packard's avatar
Keith Packard committed
12
 * advertising or publicity pertaining to distribution of the software without
13
 * specific, written prior permission.  The authors make no
Keith Packard's avatar
Keith Packard committed
14
15
16
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
17
 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
Keith Packard's avatar
Keith Packard committed
18
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
19
 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
Keith Packard's avatar
Keith Packard committed
20
21
22
23
24
25
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

26
#include <string.h>
27
#include "fcint.h"
Patrick Lam's avatar
Patrick Lam committed
28
#include <fcntl.h>
Keith Packard's avatar
Keith Packard committed
29
#include <stdarg.h>
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
30
31

#ifdef HAVE_DIRENT_H
32
#include <dirent.h>
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
33
#endif
34

35
#ifdef ENABLE_LIBXML2
36
37
38
39
40
41
42
43
44
45
46
47

#include <libxml/parser.h>

#define XML_Char			xmlChar
#define XML_Parser			xmlParserCtxtPtr
#define XML_ParserFree			xmlFreeParserCtxt
#define XML_GetCurrentLineNumber	xmlSAX2GetLineNumber
#define XML_GetErrorCode		xmlCtxtGetLastError
#define XML_ErrorString(Error)		(Error)->message

#else /* ENABLE_LIBXML2 */

48
49
50
#ifndef HAVE_XMLPARSE_H
#define HAVE_XMLPARSE_H 0
#endif
51

52
53
54
55
56
#if HAVE_XMLPARSE_H
#include <xmlparse.h>
#else
#include <expat.h>
#endif
Keith Packard's avatar
Keith Packard committed
57

58
59
#endif /* ENABLE_LIBXML2 */

Tor Lillqvist's avatar
Tor Lillqvist committed
60
#ifdef _WIN32
61
#include <mbstring.h>
62
extern FcChar8 fontconfig_instprefix[];
Tor Lillqvist's avatar
Tor Lillqvist committed
63
64
#endif

Akira TAGOH's avatar
Akira TAGOH committed
65
66
67
static FcChar8  *__fc_userdir = NULL;
static FcChar8  *__fc_userconf = NULL;

68
69
static void
FcExprDestroy (FcExpr *e);
70
71
72
73
74
static FcBool
_FcConfigParse (FcConfig	*config,
		const FcChar8	*name,
		FcBool		complain,
		FcBool		load);
Keith Packard's avatar
Keith Packard committed
75
76
77
78
79
80
81
82

void
FcTestDestroy (FcTest *test)
{
    FcExprDestroy (test->expr);
    free (test);
}

83
84
85
void
FcRuleDestroy (FcRule *rule)
{
Akira TAGOH's avatar
Akira TAGOH committed
86
87
    FcRule *n = rule->next;

88
89
90
91
92
93
94
    switch (rule->type) {
    case FcRuleTest:
	FcTestDestroy (rule->u.test);
	break;
    case FcRuleEdit:
	FcEditDestroy (rule->u.edit);
	break;
Behdad Esfahbod's avatar
Behdad Esfahbod committed
95
    case FcRuleUnknown:
96
97
98
99
    default:
	break;
    }
    free (rule);
Akira TAGOH's avatar
Akira TAGOH committed
100
101
    if (n)
	FcRuleDestroy (n);
102
103
}

104
static FcExpr *
105
FcExprCreateInteger (FcConfig *config, int i)
Keith Packard's avatar
Keith Packard committed
106
{
107
    FcExpr *e = FcConfigAllocExpr (config);
Keith Packard's avatar
Keith Packard committed
108
109
110
111
112
113
114
115
    if (e)
    {
	e->op = FcOpInteger;
	e->u.ival = i;
    }
    return e;
}

116
static FcExpr *
117
FcExprCreateDouble (FcConfig *config, double d)
Keith Packard's avatar
Keith Packard committed
118
{
119
    FcExpr *e = FcConfigAllocExpr (config);
Keith Packard's avatar
Keith Packard committed
120
121
122
123
124
125
126
127
    if (e)
    {
	e->op = FcOpDouble;
	e->u.dval = d;
    }
    return e;
}

128
static FcExpr *
129
FcExprCreateString (FcConfig *config, const FcChar8 *s)
Keith Packard's avatar
Keith Packard committed
130
{
131
    FcExpr *e = FcConfigAllocExpr (config);
Keith Packard's avatar
Keith Packard committed
132
133
134
    if (e)
    {
	e->op = FcOpString;
Behdad Esfahbod's avatar
Behdad Esfahbod committed
135
	e->u.sval = FcStrdup (s);
Keith Packard's avatar
Keith Packard committed
136
137
138
139
    }
    return e;
}

140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
static FcExprMatrix *
FcExprMatrixCopyShallow (const FcExprMatrix *matrix)
{
  FcExprMatrix *m = malloc (sizeof (FcExprMatrix));
  if (m)
  {
    *m = *matrix;
  }
  return m;
}

static void
FcExprMatrixFreeShallow (FcExprMatrix *m)
{
  if (!m)
    return;

  free (m);
}

static void
FcExprMatrixFree (FcExprMatrix *m)
{
  if (!m)
    return;

  FcExprDestroy (m->xx);
  FcExprDestroy (m->xy);
  FcExprDestroy (m->yx);
  FcExprDestroy (m->yy);

  free (m);
}

174
static FcExpr *
175
FcExprCreateMatrix (FcConfig *config, const FcExprMatrix *matrix)
Keith Packard's avatar
Keith Packard committed
176
{
177
    FcExpr *e = FcConfigAllocExpr (config);
Keith Packard's avatar
Keith Packard committed
178
179
180
    if (e)
    {
	e->op = FcOpMatrix;
181
	e->u.mexpr = FcExprMatrixCopyShallow (matrix);
Keith Packard's avatar
Keith Packard committed
182
183
184
185
    }
    return e;
}

186
187
188
189
190
191
192
193
194
195
196
197
static FcExpr *
FcExprCreateRange (FcConfig *config, FcRange *range)
{
    FcExpr *e = FcConfigAllocExpr (config);
    if (e)
    {
	e->op = FcOpRange;
	e->u.rval = FcRangeCopy (range);
    }
    return e;
}

198
static FcExpr *
199
FcExprCreateBool (FcConfig *config, FcBool b)
Keith Packard's avatar
Keith Packard committed
200
{
201
    FcExpr *e = FcConfigAllocExpr (config);
Keith Packard's avatar
Keith Packard committed
202
203
204
205
206
207
208
209
    if (e)
    {
	e->op = FcOpBool;
	e->u.bval = b;
    }
    return e;
}

Akira TAGOH's avatar
Akira TAGOH committed
210
211
212
213
214
215
216
217
218
219
220
221
static FcExpr *
FcExprCreateCharSet (FcConfig *config, FcCharSet *charset)
{
    FcExpr *e = FcConfigAllocExpr (config);
    if (e)
    {
	e->op = FcOpCharSet;
	e->u.cval = FcCharSetCopy (charset);
    }
    return e;
}

Akira TAGOH's avatar
Akira TAGOH committed
222
223
224
225
226
227
228
229
230
231
232
233
static FcExpr *
FcExprCreateLangSet (FcConfig *config, FcLangSet *langset)
{
    FcExpr *e = FcConfigAllocExpr (config);
    if (e)
    {
	e->op = FcOpLangSet;
	e->u.lval = FcLangSetCopy (langset);
    }
    return e;
}

234
static FcExpr *
235
FcExprCreateName (FcConfig *config, FcExprName name)
Keith Packard's avatar
Keith Packard committed
236
{
237
    FcExpr *e = FcConfigAllocExpr (config);
Keith Packard's avatar
Keith Packard committed
238
239
240
    if (e)
    {
	e->op = FcOpField;
241
	e->u.name = name;
Keith Packard's avatar
Keith Packard committed
242
243
244
245
    }
    return e;
}

246
static FcExpr *
247
FcExprCreateConst (FcConfig *config, const FcChar8 *constant)
Keith Packard's avatar
Keith Packard committed
248
{
249
    FcExpr *e = FcConfigAllocExpr (config);
Keith Packard's avatar
Keith Packard committed
250
251
252
    if (e)
    {
	e->op = FcOpConst;
Behdad Esfahbod's avatar
Behdad Esfahbod committed
253
	e->u.constant = FcStrdup (constant);
Keith Packard's avatar
Keith Packard committed
254
255
256
257
    }
    return e;
}

258
static FcExpr *
259
FcExprCreateOp (FcConfig *config, FcExpr *left, FcOp op, FcExpr *right)
Keith Packard's avatar
Keith Packard committed
260
{
261
    FcExpr *e = FcConfigAllocExpr (config);
Keith Packard's avatar
Keith Packard committed
262
263
264
265
266
267
268
269
270
    if (e)
    {
	e->op = op;
	e->u.tree.left = left;
	e->u.tree.right = right;
    }
    return e;
}

271
static void
Keith Packard's avatar
Keith Packard committed
272
273
FcExprDestroy (FcExpr *e)
{
274
275
    if (!e)
	return;
276
    switch (FC_OP_GET_OP (e->op)) {
Keith Packard's avatar
Keith Packard committed
277
278
279
280
281
    case FcOpInteger:
	break;
    case FcOpDouble:
	break;
    case FcOpString:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
282
	FcFree (e->u.sval);
Keith Packard's avatar
Keith Packard committed
283
284
	break;
    case FcOpMatrix:
285
	FcExprMatrixFree (e->u.mexpr);
Keith Packard's avatar
Keith Packard committed
286
	break;
Akira TAGOH's avatar
Akira TAGOH committed
287
    case FcOpRange:
288
	FcRangeDestroy (e->u.rval);
Akira TAGOH's avatar
Akira TAGOH committed
289
	break;
Keith Packard's avatar
Keith Packard committed
290
291
292
    case FcOpCharSet:
	FcCharSetDestroy (e->u.cval);
	break;
Akira TAGOH's avatar
Akira TAGOH committed
293
294
295
    case FcOpLangSet:
	FcLangSetDestroy (e->u.lval);
	break;
Keith Packard's avatar
Keith Packard committed
296
297
298
299
300
    case FcOpBool:
	break;
    case FcOpField:
	break;
    case FcOpConst:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
301
	FcFree (e->u.constant);
Keith Packard's avatar
Keith Packard committed
302
303
304
305
306
307
308
	break;
    case FcOpAssign:
    case FcOpAssignReplace:
    case FcOpPrepend:
    case FcOpPrependFirst:
    case FcOpAppend:
    case FcOpAppendLast:
309
310
    case FcOpDelete:
    case FcOpDeleteAll:
Keith Packard's avatar
Keith Packard committed
311
312
313
314
315
316
317
318
319
	break;
    case FcOpOr:
    case FcOpAnd:
    case FcOpEqual:
    case FcOpNotEqual:
    case FcOpLess:
    case FcOpLessEqual:
    case FcOpMore:
    case FcOpMoreEqual:
320
    case FcOpContains:
321
    case FcOpListing:
322
    case FcOpNotContains:
Keith Packard's avatar
Keith Packard committed
323
324
325
326
327
328
329
330
331
    case FcOpPlus:
    case FcOpMinus:
    case FcOpTimes:
    case FcOpDivide:
    case FcOpQuest:
    case FcOpComma:
	FcExprDestroy (e->u.tree.right);
	/* fall through */
    case FcOpNot:
332
333
334
335
    case FcOpFloor:
    case FcOpCeil:
    case FcOpRound:
    case FcOpTrunc:
Keith Packard's avatar
Keith Packard committed
336
337
338
339
340
341
	FcExprDestroy (e->u.tree.left);
	break;
    case FcOpNil:
    case FcOpInvalid:
	break;
    }
342
343

    e->op = FcOpNil;
Keith Packard's avatar
Keith Packard committed
344
345
346
347
348
349
350
}

void
FcEditDestroy (FcEdit *e)
{
    if (e->expr)
	FcExprDestroy (e->expr);
351
    free (e);
Keith Packard's avatar
Keith Packard committed
352
353
}

354
355
356
357
typedef enum _FcElement {
    FcElementNone,
    FcElementFontconfig,
    FcElementDir,
358
    FcElementCacheDir,
359
360
361
362
363
    FcElementCache,
    FcElementInclude,
    FcElementConfig,
    FcElementMatch,
    FcElementAlias,
364
    FcElementDescription,
365
    FcElementRemapDir,
Akira TAGOH's avatar
Akira TAGOH committed
366
    FcElementResetDirs,
367
	
368
    FcElementRescan,
369
370
371
372
373
374

    FcElementPrefer,
    FcElementAccept,
    FcElementDefault,
    FcElementFamily,

375
376
377
378
    FcElementSelectfont,
    FcElementAcceptfont,
    FcElementRejectfont,
    FcElementGlob,
379
380
    FcElementPattern,
    FcElementPatelt,
381

382
383
384
385
386
387
    FcElementTest,
    FcElementEdit,
    FcElementInt,
    FcElementDouble,
    FcElementString,
    FcElementMatrix,
Akira TAGOH's avatar
Akira TAGOH committed
388
    FcElementRange,
389
    FcElementBool,
Akira TAGOH's avatar
Akira TAGOH committed
390
    FcElementCharSet,
Akira TAGOH's avatar
Akira TAGOH committed
391
    FcElementLangSet,
392
393
394
395
396
397
398
399
400
401
    FcElementName,
    FcElementConst,
    FcElementOr,
    FcElementAnd,
    FcElementEq,
    FcElementNotEq,
    FcElementLess,
    FcElementLessEq,
    FcElementMore,
    FcElementMoreEq,
402
403
    FcElementContains,
    FcElementNotContains,
404
405
406
407
408
409
    FcElementPlus,
    FcElementMinus,
    FcElementTimes,
    FcElementDivide,
    FcElementNot,
    FcElementIf,
410
411
412
413
    FcElementFloor,
    FcElementCeil,
    FcElementRound,
    FcElementTrunc,
414
415
416
    FcElementUnknown
} FcElement;

417
static const struct {
Patrick Lam's avatar
Patrick Lam committed
418
419
420
421
422
    const char  name[16];
    FcElement   element;
} fcElementMap[] = {
    { "fontconfig",	FcElementFontconfig },
    { "dir",		FcElementDir },
423
    { "cachedir",	FcElementCacheDir },
Patrick Lam's avatar
Patrick Lam committed
424
425
426
427
428
    { "cache",		FcElementCache },
    { "include",	FcElementInclude },
    { "config",		FcElementConfig },
    { "match",		FcElementMatch },
    { "alias",		FcElementAlias },
429
    { "description",	FcElementDescription },
430
    { "remap-dir",	FcElementRemapDir },
Akira TAGOH's avatar
Akira TAGOH committed
431
    { "reset-dirs",	FcElementResetDirs },
Behdad Esfahbod's avatar
Behdad Esfahbod committed
432

Patrick Lam's avatar
Patrick Lam committed
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
    { "rescan",		FcElementRescan },

    { "prefer",		FcElementPrefer },
    { "accept",		FcElementAccept },
    { "default",	FcElementDefault },
    { "family",		FcElementFamily },

    { "selectfont",	FcElementSelectfont },
    { "acceptfont",	FcElementAcceptfont },
    { "rejectfont",	FcElementRejectfont },
    { "glob",		FcElementGlob },
    { "pattern",	FcElementPattern },
    { "patelt",		FcElementPatelt },

    { "test",		FcElementTest },
    { "edit",		FcElementEdit },
    { "int",		FcElementInt },
    { "double",		FcElementDouble },
    { "string",		FcElementString },
    { "matrix",		FcElementMatrix },
Akira TAGOH's avatar
Akira TAGOH committed
453
    { "range",		FcElementRange },
Patrick Lam's avatar
Patrick Lam committed
454
    { "bool",		FcElementBool },
Akira TAGOH's avatar
Akira TAGOH committed
455
    { "charset",	FcElementCharSet },
Akira TAGOH's avatar
Akira TAGOH committed
456
    { "langset",	FcElementLangSet },
Patrick Lam's avatar
Patrick Lam committed
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
    { "name",		FcElementName },
    { "const",		FcElementConst },
    { "or",		FcElementOr },
    { "and",		FcElementAnd },
    { "eq",		FcElementEq },
    { "not_eq",		FcElementNotEq },
    { "less",		FcElementLess },
    { "less_eq",	FcElementLessEq },
    { "more",		FcElementMore },
    { "more_eq",	FcElementMoreEq },
    { "contains",	FcElementContains },
    { "not_contains",	FcElementNotContains },
    { "plus",		FcElementPlus },
    { "minus",		FcElementMinus },
    { "times",		FcElementTimes },
    { "divide",		FcElementDivide },
    { "not",		FcElementNot },
    { "if",		FcElementIf },
    { "floor",		FcElementFloor },
    { "ceil",		FcElementCeil },
    { "round",		FcElementRound },
    { "trunc",		FcElementTrunc },
};
#define NUM_ELEMENT_MAPS (int) (sizeof fcElementMap / sizeof fcElementMap[0])

482
483
484
485
486
static const char *fcElementIgnoreName[16] = {
    "its:",
    NULL
};

487
488
489
490
491
static FcElement
FcElementMap (const XML_Char *name)
{

    int	    i;
Patrick Lam's avatar
Patrick Lam committed
492
    for (i = 0; i < NUM_ELEMENT_MAPS; i++)
493
494
	if (!strcmp ((char *) name, fcElementMap[i].name))
	    return fcElementMap[i].element;
495
496
497
    for (i = 0; fcElementIgnoreName[i] != NULL; i++)
	if (!strncmp ((char *) name, fcElementIgnoreName[i], strlen (fcElementIgnoreName[i])))
	    return FcElementNone;
498
499
500
    return FcElementUnknown;
}

501
502
503
504
505
506
507
508
509
510
511
512
513
static const char *
FcElementReverseMap (FcElement e)
{
    int i;

    for (i = 0; i < NUM_ELEMENT_MAPS; i++)
	if (fcElementMap[i].element == e)
	    return fcElementMap[i].name;

    return NULL;
}


514
515
516
517
518
typedef struct _FcPStack {
    struct _FcPStack   *prev;
    FcElement		element;
    FcChar8		**attr;
    FcStrBuf		str;
519
    FcChar8            *attr_buf_static[16];
520
} FcPStack;
Behdad Esfahbod's avatar
Behdad Esfahbod committed
521

522
523
524
525
526
527
typedef enum _FcVStackTag {
    FcVStackNone,

    FcVStackString,
    FcVStackFamily,
    FcVStackConstant,
528
    FcVStackGlob,
529
    FcVStackName,
530
    FcVStackPattern,
Behdad Esfahbod's avatar
Behdad Esfahbod committed
531

532
533
534
    FcVStackPrefer,
    FcVStackAccept,
    FcVStackDefault,
Behdad Esfahbod's avatar
Behdad Esfahbod committed
535

536
537
538
    FcVStackInteger,
    FcVStackDouble,
    FcVStackMatrix,
Akira TAGOH's avatar
Akira TAGOH committed
539
    FcVStackRange,
540
    FcVStackBool,
Akira TAGOH's avatar
Akira TAGOH committed
541
    FcVStackCharSet,
Akira TAGOH's avatar
Akira TAGOH committed
542
    FcVStackLangSet,
Behdad Esfahbod's avatar
Behdad Esfahbod committed
543

544
545
546
547
548
549
550
551
552
553
554
555
556
557
    FcVStackTest,
    FcVStackExpr,
    FcVStackEdit
} FcVStackTag;

typedef struct _FcVStack {
    struct _FcVStack	*prev;
    FcPStack		*pstack;	/* related parse element */
    FcVStackTag		tag;
    union {
	FcChar8		*string;

	int		integer;
	double		_double;
558
	FcExprMatrix	*matrix;
559
	FcRange		*range;
560
	FcBool		bool_;
Akira TAGOH's avatar
Akira TAGOH committed
561
	FcCharSet	*charset;
Akira TAGOH's avatar
Akira TAGOH committed
562
	FcLangSet	*langset;
563
	FcExprName	name;
564
565
566
567
568
569

	FcTest		*test;
	FcQual		qual;
	FcOp		op;
	FcExpr		*expr;
	FcEdit		*edit;
570
571

	FcPattern	*pattern;
572
573
574
575
576
577
578
579
580
    } u;
} FcVStack;

typedef struct _FcConfigParse {
    FcPStack	    *pstack;
    FcVStack	    *vstack;
    FcBool	    error;
    const FcChar8   *name;
    FcConfig	    *config;
581
    FcRuleSet	    *ruleset;
582
    XML_Parser	    parser;
Behdad Esfahbod's avatar
Behdad Esfahbod committed
583
    unsigned int    pstack_static_used;
584
    FcPStack        pstack_static[8];
Behdad Esfahbod's avatar
Behdad Esfahbod committed
585
    unsigned int    vstack_static_used;
586
    FcVStack        vstack_static[64];
587
    FcBool          scanOnly;
588
589
} FcConfigParse;

590
591
592
593
typedef enum _FcConfigSeverity {
    FcSevereInfo, FcSevereWarning, FcSevereError
} FcConfigSeverity;

Keith Packard's avatar
Keith Packard committed
594
static void
Patrick Lam's avatar
Patrick Lam committed
595
FcConfigMessage (FcConfigParse *parse, FcConfigSeverity severe, const char *fmt, ...)
Keith Packard's avatar
Keith Packard committed
596
{
Patrick Lam's avatar
Patrick Lam committed
597
    const char	*s = "unknown";
Keith Packard's avatar
Keith Packard committed
598
599
600
    va_list	args;

    va_start (args, fmt);
601
602
603
604
605
606

    switch (severe) {
    case FcSevereInfo: s = "info"; break;
    case FcSevereWarning: s = "warning"; break;
    case FcSevereError: s = "error"; break;
    }
607
608
609
    if (parse)
    {
	if (parse->name)
610
	    fprintf (stderr, "Fontconfig %s: \"%s\", line %d: ", s,
611
		     parse->name, (int)XML_GetCurrentLineNumber (parse->parser));
612
	else
613
	    fprintf (stderr, "Fontconfig %s: line %d: ", s,
614
		     (int)XML_GetCurrentLineNumber (parse->parser));
615
616
	if (severe >= FcSevereError)
	    parse->error = FcTrue;
617
618
    }
    else
619
	fprintf (stderr, "Fontconfig %s: ", s);
Keith Packard's avatar
Keith Packard committed
620
621
622
623
624
    vfprintf (stderr, fmt, args);
    fprintf (stderr, "\n");
    va_end (args);
}

625

626
627
628
629
static FcExpr *
FcPopExpr (FcConfigParse *parse);


Patrick Lam's avatar
Patrick Lam committed
630
static const char *
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
FcTypeName (FcType type)
{
    switch (type) {
    case FcTypeVoid:
	return "void";
    case FcTypeInteger:
    case FcTypeDouble:
	return "number";
    case FcTypeString:
	return "string";
    case FcTypeBool:
	return "bool";
    case FcTypeMatrix:
	return "matrix";
    case FcTypeCharSet:
	return "charset";
    case FcTypeFTFace:
	return "FT_Face";
    case FcTypeLangSet:
	return "langset";
651
652
    case FcTypeRange:
	return "range";
Behdad Esfahbod's avatar
Behdad Esfahbod committed
653
    case FcTypeUnknown:
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
    default:
	return "unknown";
    }
}

static void
FcTypecheckValue (FcConfigParse *parse, FcType value, FcType type)
{
    if (value == FcTypeInteger)
	value = FcTypeDouble;
    if (type == FcTypeInteger)
	type = FcTypeDouble;
    if (value != type)
    {
	if ((value == FcTypeLangSet && type == FcTypeString) ||
669
670
	    (value == FcTypeString && type == FcTypeLangSet) ||
	    (value == FcTypeDouble && type == FcTypeRange))
671
	    return;
Akira TAGOH's avatar
Akira TAGOH committed
672
	if (type ==  FcTypeUnknown)
673
	    return;
674
675
	/* It's perfectly fine to use user-define elements in expressions,
	 * so don't warn in that case. */
Akira TAGOH's avatar
Akira TAGOH committed
676
	if (value == FcTypeUnknown)
677
	    return;
678
679
680
681
682
683
684
685
686
687
	FcConfigMessage (parse, FcSevereWarning, "saw %s, expected %s",
			 FcTypeName (value), FcTypeName (type));
    }
}

static void
FcTypecheckExpr (FcConfigParse *parse, FcExpr *expr, FcType type)
{
    const FcObjectType	*o;
    const FcConstant	*c;
Behdad Esfahbod's avatar
Behdad Esfahbod committed
688

689
690
691
692
    /* If parsing the expression failed, some nodes may be NULL */
    if (!expr)
	return;

693
    switch (FC_OP_GET_OP (expr->op)) {
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
    case FcOpInteger:
    case FcOpDouble:
	FcTypecheckValue (parse, FcTypeDouble, type);
	break;
    case FcOpString:
	FcTypecheckValue (parse, FcTypeString, type);
	break;
    case FcOpMatrix:
	FcTypecheckValue (parse, FcTypeMatrix, type);
	break;
    case FcOpBool:
	FcTypecheckValue (parse, FcTypeBool, type);
	break;
    case FcOpCharSet:
	FcTypecheckValue (parse, FcTypeCharSet, type);
	break;
Akira TAGOH's avatar
Akira TAGOH committed
710
711
712
    case FcOpLangSet:
	FcTypecheckValue (parse, FcTypeLangSet, type);
	break;
713
714
715
    case FcOpRange:
	FcTypecheckValue (parse, FcTypeRange, type);
	break;
716
717
718
    case FcOpNil:
	break;
    case FcOpField:
719
	o = FcNameGetObjectType (FcObjectName (expr->u.name.object));
720
721
722
723
724
725
726
727
728
729
730
	if (o)
	    FcTypecheckValue (parse, o->type, type);
	break;
    case FcOpConst:
	c = FcNameGetConstant (expr->u.constant);
	if (c)
	{
	    o = FcNameGetObjectType (c->object);
	    if (o)
		FcTypecheckValue (parse, o->type, type);
	}
Behdad Esfahbod's avatar
Behdad Esfahbod committed
731
732
        else
            FcConfigMessage (parse, FcSevereWarning,
733
734
                             "invalid constant used : %s",
                             expr->u.constant);
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
	break;
    case FcOpQuest:
	FcTypecheckExpr (parse, expr->u.tree.left, FcTypeBool);
	FcTypecheckExpr (parse, expr->u.tree.right->u.tree.left, type);
	FcTypecheckExpr (parse, expr->u.tree.right->u.tree.right, type);
	break;
    case FcOpAssign:
    case FcOpAssignReplace:
	break;
    case FcOpEqual:
    case FcOpNotEqual:
    case FcOpLess:
    case FcOpLessEqual:
    case FcOpMore:
    case FcOpMoreEqual:
    case FcOpContains:
    case FcOpNotContains:
    case FcOpListing:
	FcTypecheckValue (parse, FcTypeBool, type);
	break;
    case FcOpComma:
    case FcOpOr:
    case FcOpAnd:
    case FcOpPlus:
    case FcOpMinus:
    case FcOpTimes:
    case FcOpDivide:
	FcTypecheckExpr (parse, expr->u.tree.left, type);
	FcTypecheckExpr (parse, expr->u.tree.right, type);
	break;
    case FcOpNot:
	FcTypecheckValue (parse, FcTypeBool, type);
	FcTypecheckExpr (parse, expr->u.tree.left, FcTypeBool);
	break;
    case FcOpFloor:
    case FcOpCeil:
    case FcOpRound:
    case FcOpTrunc:
	FcTypecheckValue (parse, FcTypeDouble, type);
	FcTypecheckExpr (parse, expr->u.tree.left, FcTypeDouble);
	break;
    default:
	break;
    }
}

static FcTest *
FcTestCreate (FcConfigParse *parse,
Behdad Esfahbod's avatar
Behdad Esfahbod committed
783
	      FcMatchKind   kind,
784
785
	      FcQual	    qual,
	      const FcChar8 *field,
786
	      unsigned int  compare,
787
788
789
790
791
792
793
794
795
796
	      FcExpr	    *expr)
{
    FcTest	*test = (FcTest *) malloc (sizeof (FcTest));

    if (test)
    {
	const FcObjectType	*o;
	
	test->kind = kind;
	test->qual = qual;
797
	test->object = FcObjectFromName ((const char *) field);
798
799
	test->op = compare;
	test->expr = expr;
800
	o = FcNameGetObjectType (FcObjectName (test->object));
801
802
803
804
805
806
807
808
	if (o)
	    FcTypecheckExpr (parse, expr, o->type);
    }
    return test;
}

static FcEdit *
FcEditCreate (FcConfigParse	*parse,
809
	      FcObject		object,
810
811
812
813
814
815
816
817
818
819
	      FcOp		op,
	      FcExpr		*expr,
	      FcValueBinding	binding)
{
    FcEdit *e = (FcEdit *) malloc (sizeof (FcEdit));

    if (e)
    {
	const FcObjectType	*o;

820
	e->object = object;
821
822
823
	e->op = op;
	e->expr = expr;
	e->binding = binding;
824
	o = FcNameGetObjectType (FcObjectName (e->object));
825
826
827
828
829
830
	if (o)
	    FcTypecheckExpr (parse, expr, o->type);
    }
    return e;
}

831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
static FcRule *
FcRuleCreate (FcRuleType type,
	      void       *p)
{
    FcRule *r = (FcRule *) malloc (sizeof (FcRule));

    if (!r)
	return NULL;

    r->next = NULL;
    r->type = type;
    switch (type)
    {
    case FcRuleTest:
	r->u.test = (FcTest *) p;
	break;
    case FcRuleEdit:
	r->u.edit = (FcEdit *) p;
	break;
Behdad Esfahbod's avatar
Behdad Esfahbod committed
850
    case FcRuleUnknown:
851
852
853
854
855
856
857
858
859
    default:
	free (r);
	r = NULL;
	break;
    }

    return r;
}

860
static FcVStack *
861
FcVStackCreateAndPush (FcConfigParse *parse)
Keith Packard's avatar
Keith Packard committed
862
{
863
    FcVStack    *new;
Keith Packard's avatar
Keith Packard committed
864

865
866
867
868
869
870
871
872
    if (parse->vstack_static_used < sizeof (parse->vstack_static) / sizeof (parse->vstack_static[0]))
	new = &parse->vstack_static[parse->vstack_static_used++];
    else
    {
	new = malloc (sizeof (FcVStack));
	if (!new)
	    return 0;
    }
873
874
    new->tag = FcVStackNone;
    new->prev = 0;
Keith Packard's avatar
Keith Packard committed
875

876
877
878
    new->prev = parse->vstack;
    new->pstack = parse->pstack ? parse->pstack->prev : 0;
    parse->vstack = new;
Keith Packard's avatar
Keith Packard committed
879

880
    return new;
Keith Packard's avatar
Keith Packard committed
881
882
883
}

static FcBool
884
FcVStackPushString (FcConfigParse *parse, FcVStackTag tag, FcChar8 *string)
Keith Packard's avatar
Keith Packard committed
885
{
886
    FcVStack    *vstack = FcVStackCreateAndPush (parse);
887
888
889
890
891
    if (!vstack)
	return FcFalse;
    vstack->u.string = string;
    vstack->tag = tag;
    return FcTrue;
Keith Packard's avatar
Keith Packard committed
892
893
894
}

static FcBool
895
FcVStackPushInteger (FcConfigParse *parse, int integer)
Keith Packard's avatar
Keith Packard committed
896
{
897
    FcVStack    *vstack = FcVStackCreateAndPush (parse);
898
    if (!vstack)
Keith Packard's avatar
Keith Packard committed
899
	return FcFalse;
900
901
902
    vstack->u.integer = integer;
    vstack->tag = FcVStackInteger;
    return FcTrue;
Keith Packard's avatar
Keith Packard committed
903
904
905
}

static FcBool
906
FcVStackPushDouble (FcConfigParse *parse, double _double)
Keith Packard's avatar
Keith Packard committed
907
{
908
    FcVStack    *vstack = FcVStackCreateAndPush (parse);
909
    if (!vstack)
Keith Packard's avatar
Keith Packard committed
910
	return FcFalse;
911
912
913
    vstack->u._double = _double;
    vstack->tag = FcVStackDouble;
    return FcTrue;
Keith Packard's avatar
Keith Packard committed
914
915
916
}

static FcBool
917
FcVStackPushMatrix (FcConfigParse *parse, FcExprMatrix *matrix)
Keith Packard's avatar
Keith Packard committed
918
{
919
920
921
922
    FcVStack    *vstack;
    vstack = FcVStackCreateAndPush (parse);
    if (!vstack)
	return FcFalse;
923
    vstack->u.matrix = FcExprMatrixCopyShallow (matrix);
924
925
    vstack->tag = FcVStackMatrix;
    return FcTrue;
Keith Packard's avatar
Keith Packard committed
926
927
}

Akira TAGOH's avatar
Akira TAGOH committed
928
929
930
static FcBool
FcVStackPushRange (FcConfigParse *parse, FcRange *range)
{
931
    FcVStack 	*vstack = FcVStackCreateAndPush (parse);
Akira TAGOH's avatar
Akira TAGOH committed
932
933
    if (!vstack)
	return FcFalse;
934
    vstack->u.range = range;
Akira TAGOH's avatar
Akira TAGOH committed
935
936
937
938
    vstack->tag = FcVStackRange;
    return FcTrue;
}

Keith Packard's avatar
Keith Packard committed
939
static FcBool
940
FcVStackPushBool (FcConfigParse *parse, FcBool bool_)
Keith Packard's avatar
Keith Packard committed
941
{
942
    FcVStack    *vstack = FcVStackCreateAndPush (parse);
943
944
    if (!vstack)
	return FcFalse;
945
    vstack->u.bool_ = bool_;
946
947
948
    vstack->tag = FcVStackBool;
    return FcTrue;
}
Keith Packard's avatar
Keith Packard committed
949

Akira TAGOH's avatar
Akira TAGOH committed
950
951
952
953
954
955
956
957
958
959
960
961
962
963
static FcBool
FcVStackPushCharSet (FcConfigParse *parse, FcCharSet *charset)
{
    FcVStack	*vstack;
    if (!charset)
	return FcFalse;
    vstack = FcVStackCreateAndPush (parse);
    if (!vstack)
	return FcFalse;
    vstack->u.charset = charset;
    vstack->tag = FcVStackCharSet;
    return FcTrue;
}

Akira TAGOH's avatar
Akira TAGOH committed
964
965
966
967
968
969
970
971
972
973
974
975
976
977
static FcBool
FcVStackPushLangSet (FcConfigParse *parse, FcLangSet *langset)
{
    FcVStack	*vstack;
    if (!langset)
	return FcFalse;
    vstack = FcVStackCreateAndPush (parse);
    if (!vstack)
	return FcFalse;
    vstack->u.langset = langset;
    vstack->tag = FcVStackLangSet;
    return FcTrue;
}

978
979
980
981
982
983
984
985
986
987
988
989
static FcBool
FcVStackPushName (FcConfigParse *parse, FcMatchKind kind, FcObject object)
{
    FcVStack    *vstack = FcVStackCreateAndPush (parse);
    if (!vstack)
	return FcFalse;
    vstack->u.name.object = object;
    vstack->u.name.kind = kind;
    vstack->tag = FcVStackName;
    return FcTrue;
}

990
991
992
static FcBool
FcVStackPushTest (FcConfigParse *parse, FcTest *test)
{
993
    FcVStack    *vstack = FcVStackCreateAndPush (parse);
994
    if (!vstack)
Keith Packard's avatar
Keith Packard committed
995
	return FcFalse;
996
997
    vstack->u.test = test;
    vstack->tag = FcVStackTest;
Keith Packard's avatar
Keith Packard committed
998
999
1000
1001
    return FcTrue;
}

static FcBool
1002
FcVStackPushExpr (FcConfigParse *parse, FcVStackTag tag, FcExpr *expr)
Keith Packard's avatar
Keith Packard committed
1003
{
1004
    FcVStack    *vstack = FcVStackCreateAndPush (parse);
1005
1006
1007
1008
1009
1010
    if (!vstack)
	return FcFalse;
    vstack->u.expr = expr;
    vstack->tag = tag;
    return FcTrue;
}
Keith Packard's avatar
Keith Packard committed
1011

1012
1013
1014
static FcBool
FcVStackPushEdit (FcConfigParse *parse, FcEdit *edit)
{
1015
    FcVStack    *vstack = FcVStackCreateAndPush (parse);
1016
    if (!vstack)
Keith Packard's avatar
Keith Packard committed
1017
	return FcFalse;
1018
1019
    vstack->u.edit = edit;
    vstack->tag = FcVStackEdit;
Keith Packard's avatar
Keith Packard committed
1020
1021
1022
    return FcTrue;
}

1023
1024
1025
static FcBool
FcVStackPushPattern (FcConfigParse *parse, FcPattern *pattern)
{
1026
    FcVStack    *vstack = FcVStackCreateAndPush (parse);
1027
1028
1029
1030
1031
1032
1033
    if (!vstack)
	return FcFalse;
    vstack->u.pattern = pattern;
    vstack->tag = FcVStackPattern;
    return FcTrue;
}

1034
1035
static FcVStack *
FcVStackFetch (FcConfigParse *parse, int off)
Keith Packard's avatar
Keith Packard committed
1036
{
1037
    FcVStack    *vstack;
Keith Packard's avatar
Keith Packard committed
1038

1039
1040
1041
1042
    for (vstack = parse->vstack; vstack && off-- > 0; vstack = vstack->prev);
    return vstack;
}

1043
1044
static FcVStack *
FcVStackPeek (FcConfigParse *parse)
1045
{
1046
1047
1048
    FcVStack	*vstack = parse->vstack;

    return vstack && vstack->pstack == parse->pstack ? vstack : 0;
Keith Packard's avatar
Keith Packard committed
1049
1050
}

1051
1052
static void
FcVStackPopAndDestroy (FcConfigParse *parse)
Keith Packard's avatar
Keith Packard committed
1053
{
1054
    FcVStack	*vstack = parse->vstack;
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1055

1056
    if (!vstack || vstack->pstack != parse->pstack)
1057
1058
	return;

1059
    parse->vstack = vstack->prev;
1060
1061
1062
1063

    switch (vstack->tag) {
    case FcVStackNone:
	break;
1064
1065
    case FcVStackName:
	break;
1066
    case FcVStackFamily:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1067
	break;
1068
    case FcVStackString:
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
    case FcVStackConstant:
    case FcVStackGlob:
	FcStrFree (vstack->u.string);
	break;
    case FcVStackPattern:
	FcPatternDestroy (vstack->u.pattern);
	break;
    case FcVStackInteger:
    case FcVStackDouble:
	break;
    case FcVStackMatrix:
1080
	FcExprMatrixFreeShallow (vstack->u.matrix);
1081
1082
1083
	break;
    case FcVStackBool:
	break;
1084
1085
1086
    case FcVStackRange:
	FcRangeDestroy (vstack->u.range);
	break;
Akira TAGOH's avatar
Akira TAGOH committed
1087
1088
1089
    case FcVStackCharSet:
	FcCharSetDestroy (vstack->u.charset);
	break;
Akira TAGOH's avatar
Akira TAGOH committed
1090
1091
1092
    case FcVStackLangSet:
	FcLangSetDestroy (vstack->u.langset);
	break;
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
    case FcVStackTest:
	FcTestDestroy (vstack->u.test);
	break;
    case FcVStackExpr:
    case FcVStackPrefer:
    case FcVStackAccept:
    case FcVStackDefault:
	FcExprDestroy (vstack->u.expr);
	break;
    case FcVStackEdit:
	FcEditDestroy (vstack->u.edit);
	break;
    }

    if (vstack == &parse->vstack_static[parse->vstack_static_used - 1])
	parse->vstack_static_used--;
    else
	free (vstack);
}

static void
FcVStackClear (FcConfigParse *parse)
{
    while (FcVStackPeek (parse))
	FcVStackPopAndDestroy (parse);
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
}

static int
FcVStackElements (FcConfigParse *parse)
{
    int		h = 0;
    FcVStack	*vstack = parse->vstack;
    while (vstack && vstack->pstack == parse->pstack)
    {
	h++;
	vstack = vstack->prev;
Keith Packard's avatar
Keith Packard committed
1129
    }
1130
    return h;
Keith Packard's avatar
Keith Packard committed
1131
1132
}

1133
static FcChar8 **
1134
FcConfigSaveAttr (const XML_Char **attr, FcChar8 **buf, int size_bytes)
Keith Packard's avatar
Keith Packard committed
1135
{
1136
1137
1138
1139
    int		slen;
    int		i;
    FcChar8	**new;
    FcChar8	*s;
Keith Packard's avatar
Keith Packard committed
1140

1141
1142
1143
1144
    if (!attr)
	return 0;
    slen = 0;
    for (i = 0; attr[i]; i++)
1145
	slen += strlen ((char *) attr[i]) + 1;
1146
1147
    if (i == 0)
	return 0;
1148
1149
1150
1151
    slen += (i + 1) * sizeof (FcChar8 *);
    if (slen <= size_bytes)
	new = buf;
    else
1152
    {
1153
1154
1155
1156
1157
1158
	new = malloc (slen);
	if (!new)
	{
	    FcConfigMessage (0, FcSevereError, "out of memory");
	    return 0;
	}
1159
    }
1160
1161
    s = (FcChar8 *) (new + (i + 1));
    for (i = 0; attr[i]; i++)
Keith Packard's avatar
Keith Packard committed
1162
    {
1163
1164
1165
	new[i] = s;
	strcpy ((char *) s, (char *) attr[i]);
	s += strlen ((char *) s) + 1;
Keith Packard's avatar
Keith Packard committed
1166
    }
1167
1168
1169
    new[i] = 0;
    return new;
}
Keith Packard's avatar
Keith Packard committed
1170

1171
1172
1173
static FcBool
FcPStackPush (FcConfigParse *parse, FcElement element, const XML_Char **attr)
{
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
    FcPStack   *new;

    if (parse->pstack_static_used < sizeof (parse->pstack_static) / sizeof (parse->pstack_static[0]))
	new = &parse->pstack_static[parse->pstack_static_used++];
    else
    {
	new = malloc (sizeof (FcPStack));
	if (!new)
	    return FcFalse;
    }
1184
1185
1186

    new->prev = parse->pstack;
    new->element = element;
1187
    new->attr = FcConfigSaveAttr (attr, new->attr_buf_static, sizeof (new->attr_buf_static));
1188
1189
1190
1191
    FcStrBufInit (&new->str, 0, 0);
    parse->pstack = new;
    return FcTrue;
}
Keith Packard's avatar
Keith Packard committed
1192

1193
1194
1195
1196
static FcBool
FcPStackPop (FcConfigParse *parse)
{
    FcPStack   *old;
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1197
1198

    if (!parse->pstack)
Keith Packard's avatar
Keith Packard committed
1199
    {
1200
	FcConfigMessage (parse, FcSevereError, "mismatching element");
1201
	return FcFalse;
Keith Packard's avatar
Keith Packard committed
1202
    }
1203

1204
1205
1206
    /* Don't check the attributes for FcElementNone */
    if (parse->pstack->element != FcElementNone &&
	parse->pstack->attr)
1207
1208
1209
1210
1211
1212
1213
    {
	/* Warn about unused attrs. */
	FcChar8 **attrs = parse->pstack->attr;
	while (*attrs)
	{
	    if (attrs[0][0])
	    {
1214
		FcConfigMessage (parse, FcSevereWarning, "invalid attribute '%s'", attrs[0]);
1215
1216
1217
1218
1219
	    }
	    attrs += 2;
	}
    }

1220
1221
1222
1223
    FcVStackClear (parse);
    old = parse->pstack;
    parse->pstack = old->prev;
    FcStrBufDestroy (&old->str);
1224

1225
    if (old->attr && old->attr != old->attr_buf_static)
1226
	free (old->attr);
1227
1228
1229
1230
1231

    if (old == &parse->pstack_static[parse->pstack_static_used - 1])
	parse->pstack_static_used--;
    else
	free (old);
1232
    return FcTrue;
Keith Packard's avatar
Keith Packard committed
1233
1234
}

1235
static FcBool
1236
1237
1238
1239
1240
FcConfigParseInit (FcConfigParse	*parse,
		   const FcChar8	*name,
		   FcConfig		*config,
		   XML_Parser		parser,
		   FcBool		enabled)
Keith Packard's avatar
Keith Packard committed
1241
{
1242
    parse->pstack = 0;
1243
    parse->pstack_static_used = 0;
1244
    parse->vstack = 0;
1245
    parse->vstack_static_used = 0;
1246
1247
1248
    parse->error = FcFalse;
    parse->name = name;
    parse->config = config;
1249
    parse->ruleset = FcRuleSetCreate (name);
1250
    parse->parser = parser;
1251
    parse->scanOnly = !enabled;
1252
1253
    FcRuleSetEnable (parse->ruleset, enabled);

1254
1255
1256
1257
1258
1259
1260
1261
    return FcTrue;
}

static void
FcConfigCleanup (FcConfigParse	*parse)
{
    while (parse->pstack)
	FcPStackPop (parse);
1262
1263
    FcRuleSetDestroy (parse->ruleset);
    parse->ruleset = NULL;
1264
1265
1266
}

static const FcChar8 *
Patrick Lam's avatar
Patrick Lam committed
1267
FcConfigGetAttribute (FcConfigParse *parse, const char *attr)
1268
1269
1270
{
    FcChar8 **attrs;
    if (!parse->pstack)