2
* $XFree86: xc/lib/fontconfig/src/fccfg.c,v 1.24 2002/12/21 02:31:53 dawes Exp $
4
* Copyright � 2000 Keith Packard, member of The XFree86 Project, Inc.
6
* Permission to use, copy, modify, distribute, and sell this software and its
7
* documentation for any purpose is hereby granted without fee, provided that
8
* the above copyright notice appear in all copies and that both that
9
* copyright notice and this permission notice appear in supporting
10
* documentation, and that the name of Keith Packard not be used in
11
* advertising or publicity pertaining to distribution of the software without
12
* specific, written prior permission. Keith Packard makes no
13
* representations about the suitability of this software for any purpose. It
14
* is provided "as is" without express or implied warranty.
16
* KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18
* EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22
* PERFORMANCE OF THIS SOFTWARE.
35
config = malloc (sizeof (FcConfig));
38
FcMemAlloc (FC_MEM_CONFIG, sizeof (FcConfig));
40
config->configDirs = FcStrSetCreate ();
41
if (!config->configDirs)
44
config->configFiles = FcStrSetCreate ();
45
if (!config->configFiles)
48
config->fontDirs = FcStrSetCreate ();
49
if (!config->fontDirs)
53
if (!FcConfigSetCache (config, (FcChar8 *) ("~/" FC_USER_CACHE_FILE)))
58
config->substPattern = 0;
59
config->substFont = 0;
60
config->maxObjects = 0;
61
for (set = FcSetSystem; set <= FcSetApplication; set++)
62
config->fonts[set] = 0;
64
config->rescanTime = time(0);
65
config->rescanInterval = 30;
70
FcStrSetDestroy (config->fontDirs);
72
FcStrSetDestroy (config->configFiles);
74
FcStrSetDestroy (config->configDirs);
77
FcMemFree (FC_MEM_CONFIG, sizeof (FcConfig));
82
typedef struct _FcFileTime {
88
FcConfigNewestFile (FcStrSet *files)
90
FcStrList *list = FcStrListCreate (files);
91
FcFileTime newest = { 0, FcFalse };
97
while ((file = FcStrListNext (list)))
98
if (stat ((char *) file, &statb) == 0)
99
if (!newest.set || statb.st_mtime - newest.time > 0)
100
newest.time = statb.st_mtime;
101
FcStrListDone (list);
107
FcConfigUptoDate (FcConfig *config)
109
FcFileTime config_time, font_time;
110
time_t now = time(0);
113
config = FcConfigGetCurrent ();
117
config_time = FcConfigNewestFile (config->configFiles);
118
font_time = FcConfigNewestFile (config->configDirs);
119
if ((config_time.set && config_time.time - config->rescanTime > 0) ||
120
(font_time.set && font_time.time - config->rescanTime) > 0)
124
config->rescanTime = now;
129
FcSubstDestroy (FcSubst *s)
136
FcTestDestroy (s->test);
137
FcEditDestroy (s->edit);
143
FcConfigDestroy (FcConfig *config)
147
if (config == _fcConfig)
150
FcStrSetDestroy (config->configDirs);
151
FcStrSetDestroy (config->fontDirs);
152
FcStrSetDestroy (config->configFiles);
154
FcStrFree (config->cache);
156
FcSubstDestroy (config->substPattern);
157
FcSubstDestroy (config->substFont);
158
for (set = FcSetSystem; set <= FcSetApplication; set++)
159
if (config->fonts[set])
160
FcFontSetDestroy (config->fonts[set]);
162
FcMemFree (FC_MEM_CONFIG, sizeof (FcConfig));
166
* Scan the current list of directories in the configuration
167
* and build the set of available fonts. Update the
168
* per-user cache file to reflect the new configuration
172
FcConfigBuildFonts (FcConfig *config)
175
FcGlobalCache *cache;
179
fonts = FcFontSetCreate ();
183
cache = FcGlobalCacheCreate ();
187
FcGlobalCacheLoad (cache, config->cache);
189
list = FcConfigGetFontDirs (config);
193
while ((dir = FcStrListNext (list)))
195
if (FcDebug () & FC_DBG_FONTSET)
196
printf ("scan dir %s\n", dir);
197
FcDirScan (fonts, config->fontDirs, cache, config->blanks, dir, FcFalse);
200
FcStrListDone (list);
202
if (FcDebug () & FC_DBG_FONTSET)
203
FcFontSetPrint (fonts);
205
FcGlobalCacheSave (cache, config->cache);
206
FcGlobalCacheDestroy (cache);
208
FcConfigSetFonts (config, fonts, FcSetSystem);
212
FcFontSetDestroy (fonts);
218
FcConfigSetCurrent (FcConfig *config)
221
if (!FcConfigBuildFonts (config))
225
FcConfigDestroy (_fcConfig);
231
FcConfigGetCurrent (void)
240
FcConfigAddConfigDir (FcConfig *config,
243
return FcStrSetAddFilename (config->configDirs, d);
247
FcConfigGetConfigDirs (FcConfig *config)
251
config = FcConfigGetCurrent ();
255
return FcStrListCreate (config->configDirs);
259
FcConfigAddFontDir (FcConfig *config,
262
return FcStrSetAddFilename (config->fontDirs, d);
266
FcConfigAddDir (FcConfig *config,
269
return (FcConfigAddConfigDir (config, d) &&
270
FcConfigAddFontDir (config, d));
274
FcConfigGetFontDirs (FcConfig *config)
278
config = FcConfigGetCurrent ();
282
return FcStrListCreate (config->fontDirs);
286
FcConfigAddConfigFile (FcConfig *config,
290
FcChar8 *file = FcConfigFilename (f);
295
ret = FcStrSetAdd (config->configFiles, file);
301
FcConfigGetConfigFiles (FcConfig *config)
305
config = FcConfigGetCurrent ();
309
return FcStrListCreate (config->configFiles);
313
FcConfigSetCache (FcConfig *config,
316
FcChar8 *new = FcStrCopyFilename (c);
321
FcStrFree (config->cache);
327
FcConfigGetCache (FcConfig *config)
331
config = FcConfigGetCurrent ();
335
return config->cache;
339
FcConfigGetFonts (FcConfig *config,
344
config = FcConfigGetCurrent ();
348
return config->fonts[set];
352
FcConfigSetFonts (FcConfig *config,
356
if (config->fonts[set])
357
FcFontSetDestroy (config->fonts[set]);
358
config->fonts[set] = fonts;
364
FcConfigGetBlanks (FcConfig *config)
368
config = FcConfigGetCurrent ();
372
return config->blanks;
376
FcConfigAddBlank (FcConfig *config,
384
b = FcBlanksCreate ();
388
if (!FcBlanksAdd (b, blank))
395
FcConfigGetRescanInverval (FcConfig *config)
399
config = FcConfigGetCurrent ();
403
return config->rescanInterval;
407
FcConfigSetRescanInverval (FcConfig *config, int rescanInterval)
411
config = FcConfigGetCurrent ();
415
config->rescanInterval = rescanInterval;
420
FcConfigAddEdit (FcConfig *config,
425
FcSubst *subst, **prev;
429
subst = (FcSubst *) malloc (sizeof (FcSubst));
432
FcMemAlloc (FC_MEM_SUBST, sizeof (FcSubst));
433
if (kind == FcMatchPattern)
434
prev = &config->substPattern;
436
prev = &config->substFont;
437
for (; *prev; prev = &(*prev)->next);
443
for (t = test; t; t = t->next)
445
if (t->kind == FcMatchDefault)
449
if (config->maxObjects < num)
450
config->maxObjects = num;
451
if (FcDebug () & FC_DBG_EDIT)
453
printf ("Add Subst ");
454
FcSubstPrint (subst);
459
typedef struct _FcSubState {
465
FcConfigPromote (FcValue v, FcValue u)
467
if (v.type == FcTypeInteger)
469
v.type = FcTypeDouble;
470
v.u.d = (double) v.u.i;
472
else if (v.type == FcTypeVoid && u.type == FcTypeMatrix)
474
v.u.m = &FcIdentityMatrix;
475
v.type = FcTypeMatrix;
477
else if (v.type == FcTypeString && u.type == FcTypeLangSet)
479
v.u.l = FcLangSetPromote (v.u.s);
480
v.type = FcTypeLangSet;
486
FcConfigCompareValue (FcValue m,
490
FcBool ret = FcFalse;
492
m = FcConfigPromote (m, v);
493
v = FcConfigPromote (v, m);
494
if (m.type == v.type)
498
break; /* FcConfigPromote prevents this from happening */
503
ret = m.u.d == v.u.d;
506
case FcOpNotContains:
507
ret = m.u.d != v.u.d;
513
ret = m.u.d <= v.u.d;
519
ret = m.u.d >= v.u.d;
529
ret = m.u.b == v.u.b;
532
case FcOpNotContains:
533
ret = m.u.b != v.u.b;
543
ret = FcStrCmpIgnoreCase (m.u.s, v.u.s) == 0;
546
case FcOpNotContains:
547
ret = FcStrCmpIgnoreCase (m.u.s, v.u.s) != 0;
557
ret = FcMatrixEqual (m.u.m, v.u.m);
560
case FcOpNotContains:
561
ret = !FcMatrixEqual (m.u.m, v.u.m);
570
/* m contains v if v is a subset of m */
571
ret = FcCharSetIsSubset (v.u.c, m.u.c);
573
case FcOpNotContains:
574
/* m contains v if v is a subset of m */
575
ret = !FcCharSetIsSubset (v.u.c, m.u.c);
578
ret = FcCharSetEqual (m.u.c, v.u.c);
581
ret = !FcCharSetEqual (m.u.c, v.u.c);
590
ret = FcLangSetCompare (v.u.l, m.u.l) != FcLangDifferentLang;
592
case FcOpNotContains:
593
ret = FcLangSetCompare (v.u.l, m.u.l) == FcLangDifferentLang;
596
ret = FcLangSetEqual (v.u.l, m.u.l);
599
ret = !FcLangSetEqual (v.u.l, m.u.l);
619
ret = m.u.f == v.u.f;
622
case FcOpNotContains:
623
ret = m.u.f != v.u.f;
633
if (op == FcOpNotEqual || op == FcOpNotContains)
641
FcConfigEvaluate (FcPattern *p, FcExpr *e)
649
v.type = FcTypeInteger;
653
v.type = FcTypeDouble;
657
v.type = FcTypeString;
662
v.type = FcTypeMatrix;
667
v.type = FcTypeCharSet;
676
r = FcPatternGet (p, e->u.field, 0, &v);
677
if (r != FcResultMatch)
681
if (FcNameConstant (e->u.constant, &v.u.i))
682
v.type = FcTypeInteger;
687
vl = FcConfigEvaluate (p, e->u.tree.left);
688
if (vl.type == FcTypeBool)
691
v = FcConfigEvaluate (p, e->u.tree.right->u.tree.left);
693
v = FcConfigEvaluate (p, e->u.tree.right->u.tree.right);
706
case FcOpNotContains:
707
vl = FcConfigEvaluate (p, e->u.tree.left);
708
vr = FcConfigEvaluate (p, e->u.tree.right);
710
v.u.b = FcConfigCompareValue (vl, e->op, vr);
720
vl = FcConfigEvaluate (p, e->u.tree.left);
721
vr = FcConfigEvaluate (p, e->u.tree.right);
722
vl = FcConfigPromote (vl, vr);
723
vr = FcConfigPromote (vr, vl);
724
if (vl.type == vr.type)
730
v.type = FcTypeDouble;
731
v.u.d = vl.u.d + vr.u.d;
734
v.type = FcTypeDouble;
735
v.u.d = vl.u.d - vr.u.d;
738
v.type = FcTypeDouble;
739
v.u.d = vl.u.d * vr.u.d;
742
v.type = FcTypeDouble;
743
v.u.d = vl.u.d / vr.u.d;
749
if (v.type == FcTypeDouble &&
750
v.u.d == (double) (int) v.u.d)
752
v.type = FcTypeInteger;
760
v.u.b = vl.u.b || vr.u.b;
764
v.u.b = vl.u.b && vr.u.b;
774
v.type = FcTypeString;
775
v.u.s = FcStrPlus (vl.u.s, vr.u.s);
787
v.type = FcTypeMatrix;
788
m = malloc (sizeof (FcMatrix));
791
FcMemAlloc (FC_MEM_MATRIX, sizeof (FcMatrix));
792
FcMatrixMultiply (m, vl.u.m, vr.u.m);
816
vl = FcConfigEvaluate (p, e->u.tree.left);
836
FcConfigMatchValueList (FcPattern *p,
840
FcValueList *ret = 0;
847
if (e->op == FcOpComma)
849
value = FcConfigEvaluate (p, e->u.tree.left);
854
value = FcConfigEvaluate (p, e);
858
for (v = values; v; v = v->next)
860
if (FcConfigCompareValue (v->value, t->op, value))
867
if (t->qual == FcQualAll)
874
FcValueDestroy (value);
880
FcConfigValues (FcPattern *p, FcExpr *e, FcValueBinding binding)
886
l = (FcValueList *) malloc (sizeof (FcValueList));
889
FcMemAlloc (FC_MEM_VALLIST, sizeof (FcValueList));
890
if (e->op == FcOpComma)
892
l->value = FcConfigEvaluate (p, e->u.tree.left);
893
l->next = FcConfigValues (p, e->u.tree.right, binding);
897
l->value = FcConfigEvaluate (p, e);
900
l->binding = binding;
901
while (l && l->value.type == FcTypeVoid)
903
FcValueList *next = l->next;
905
FcMemFree (FC_MEM_VALLIST, sizeof (FcValueList));
913
FcConfigAdd (FcValueList **head,
914
FcValueList *position,
918
FcValueList **prev, *last;
923
prev = &position->next;
925
for (prev = head; *prev; prev = &(*prev)->next)
932
for (prev = head; *prev; prev = &(*prev)->next)
934
if (*prev == position)
941
if (FcDebug () & FC_DBG_EDIT)
944
printf ("position not on list\n");
948
if (FcDebug () & FC_DBG_EDIT)
950
printf ("%s list before ", append ? "Append" : "Prepend");
951
FcValueListPrint (*head);
965
if (FcDebug () & FC_DBG_EDIT)
967
printf ("%s list after ", append ? "Append" : "Prepend");
968
FcValueListPrint (*head);
976
FcConfigDel (FcValueList **head,
977
FcValueList *position)
981
for (prev = head; *prev; prev = &(*prev)->next)
983
if (*prev == position)
985
*prev = position->next;
987
FcValueListDestroy (position);
994
FcConfigPatternAdd (FcPattern *p,
1001
FcPatternElt *e = FcPatternInsertElt (p, object);
1005
FcConfigAdd (&e->values, 0, append, list);
1010
* Delete all values associated with a field
1013
FcConfigPatternDel (FcPattern *p,
1016
FcPatternElt *e = FcPatternFindElt (p, object);
1020
FcConfigDel (&e->values, e->values);
1024
FcConfigPatternCanon (FcPattern *p,
1027
FcPatternElt *e = FcPatternFindElt (p, object);
1031
FcPatternDel (p, object);
1035
FcConfigSubstituteWithPat (FcConfig *config,
1050
config = FcConfigGetCurrent ();
1055
st = (FcSubState *) malloc (config->maxObjects * sizeof (FcSubState));
1056
if (!st && config->maxObjects)
1058
FcMemAlloc (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1060
if (FcDebug () & FC_DBG_EDIT)
1062
printf ("FcConfigSubstitute ");
1065
if (kind == FcMatchPattern)
1066
s = config->substPattern;
1068
s = config->substFont;
1069
for (; s; s = s->next)
1072
* Check the tests to see if
1073
* they all match the pattern
1075
for (t = s->test, i = 0; t; t = t->next, i++)
1077
if (FcDebug () & FC_DBG_EDIT)
1079
printf ("FcConfigSubstitute test ");
1083
if (kind == FcMatchFont && t->kind == FcMatchPattern)
1088
st[i].elt = FcPatternFindElt (m, t->field);
1092
* If there's no such field in the font,
1093
* then FcQualAll matches while FcQualAny does not
1097
if (t->qual == FcQualAll)
1106
* Check to see if there is a match, mark the location
1107
* to apply match-relative edits
1109
st[i].value = FcConfigMatchValueList (m, t, st[i].elt->values);
1112
if (t->qual == FcQualFirst && st[i].value != st[i].elt->values)
1114
if (t->qual == FcQualNotFirst && st[i].value == st[i].elt->values)
1119
if (FcDebug () & FC_DBG_EDIT)
1120
printf ("No match\n");
1123
if (FcDebug () & FC_DBG_EDIT)
1125
printf ("Substitute ");
1128
for (e = s->edit; e; e = e->next)
1131
* Evaluate the list of expressions
1133
l = FcConfigValues (p, e->expr, e->binding);
1135
* Locate any test associated with this field, skipping
1136
* tests associated with the pattern when substituting in
1139
for (t = s->test, i = 0; t; t = t->next, i++)
1141
if ((t->kind == FcMatchFont || kind == FcMatchPattern) &&
1142
!FcStrCmpIgnoreCase ((FcChar8 *) t->field,
1143
(FcChar8 *) e->field))
1153
* If there was a test, then replace the matched
1154
* value with the new list of values
1158
FcValueList *thisValue = st[i].value;
1159
FcValueList *nextValue = thisValue ? thisValue->next : 0;
1162
* Append the new list of values after the current value
1164
FcConfigAdd (&st[i].elt->values, thisValue, FcTrue, l);
1166
* Delete the marked value
1168
FcConfigDel (&st[i].elt->values, thisValue);
1170
* Adjust any pointers into the value list to ensure
1171
* future edits occur at the same place
1173
for (t = s->test, i = 0; t; t = t->next, i++)
1175
if (st[i].value == thisValue)
1176
st[i].value = nextValue;
1180
/* fall through ... */
1181
case FcOpAssignReplace:
1183
* Delete all of the values and insert
1186
FcConfigPatternDel (p, e->field);
1187
FcConfigPatternAdd (p, e->field, l, FcTrue);
1189
* Adjust any pointers into the value list as they no
1190
* longer point to anything valid
1194
FcPatternElt *thisElt = st[i].elt;
1195
for (t = s->test, i = 0; t; t = t->next, i++)
1197
if (st[i].elt == thisElt)
1205
FcConfigAdd (&st[i].elt->values, st[i].value, FcFalse, l);
1208
/* fall through ... */
1209
case FcOpPrependFirst:
1210
FcConfigPatternAdd (p, e->field, l, FcFalse);
1215
FcConfigAdd (&st[i].elt->values, st[i].value, FcTrue, l);
1218
/* fall through ... */
1219
case FcOpAppendLast:
1220
FcConfigPatternAdd (p, e->field, l, FcTrue);
1227
* Now go through the pattern and eliminate
1228
* any properties without data
1230
for (e = s->edit; e; e = e->next)
1231
FcConfigPatternCanon (p, e->field);
1233
if (FcDebug () & FC_DBG_EDIT)
1235
printf ("FcConfigSubstitute edit");
1239
FcMemFree (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1241
if (FcDebug () & FC_DBG_EDIT)
1243
printf ("FcConfigSubstitute done");
1250
FcConfigSubstitute (FcConfig *config,
1254
return FcConfigSubstituteWithPat (config, p, 0, kind);
1257
#ifndef FONTCONFIG_PATH
1258
#define FONTCONFIG_PATH "/etc/fonts"
1261
#ifndef FONTCONFIG_FILE
1262
#define FONTCONFIG_FILE "fonts.conf"
1266
FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
1271
dir = (FcChar8 *) "";
1272
path = malloc (strlen ((char *) dir) + 1 + strlen ((char *) file) + 1);
1276
strcpy ((char *) path, (const char *) dir);
1277
/* make sure there's a single separating / */
1278
if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
1279
strcat ((char *) path, "/");
1280
strcat ((char *) path, (char *) file);
1282
FcMemAlloc (FC_MEM_STRING, strlen ((char *) path) + 1);
1283
if (access ((char *) path, R_OK) == 0)
1291
FcConfigGetPath (void)
1294
FcChar8 *env, *e, *colon;
1299
npath = 2; /* default dir + null */
1300
env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
1309
path = calloc (npath, sizeof (FcChar8 *));
1319
colon = (FcChar8 *) strchr ((char *) e, ':');
1321
colon = e + strlen ((char *) e);
1322
path[i] = malloc (colon - e + 1);
1325
strncpy ((char *) path[i], (const char *) e, colon - e);
1326
path[i][colon - e] = '\0';
1335
dir = (FcChar8 *) FONTCONFIG_PATH;
1336
path[i] = malloc (strlen ((char *) dir) + 1);
1339
strcpy ((char *) path[i], (const char *) dir);
1343
for (i = 0; path[i]; i++)
1351
FcConfigFreePath (FcChar8 **path)
1355
for (p = path; *p; p++)
1361
FcConfigFilename (const FcChar8 *url)
1363
FcChar8 *file, *dir, **path, **p;
1367
url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
1369
url = (FcChar8 *) FONTCONFIG_FILE;
1374
dir = (FcChar8 *) getenv ("HOME");
1376
file = FcConfigFileExists (dir, url + 1);
1381
file = FcConfigFileExists (0, url);
1384
path = FcConfigGetPath ();
1387
for (p = path; *p; p++)
1389
file = FcConfigFileExists (*p, url);
1393
FcConfigFreePath (path);
1400
* Manage the application-specific fonts
1404
FcConfigAppFontAddFile (FcConfig *config,
1405
const FcChar8 *file)
1414
config = FcConfigGetCurrent ();
1419
subdirs = FcStrSetCreate ();
1423
set = FcConfigGetFonts (config, FcSetApplication);
1426
set = FcFontSetCreate ();
1429
FcStrSetDestroy (subdirs);
1432
FcConfigSetFonts (config, set, FcSetApplication);
1435
if (!FcFileScan (set, subdirs, 0, config->blanks, file, FcFalse))
1437
FcStrSetDestroy (subdirs);
1440
if ((sublist = FcStrListCreate (subdirs)))
1442
while ((subdir = FcStrListNext (sublist)))
1444
FcConfigAppFontAddDir (config, subdir);
1446
FcStrListDone (sublist);
1452
FcConfigAppFontAddDir (FcConfig *config,
1462
config = FcConfigGetCurrent ();
1466
subdirs = FcStrSetCreate ();
1470
set = FcConfigGetFonts (config, FcSetApplication);
1473
set = FcFontSetCreate ();
1476
FcStrSetDestroy (subdirs);
1479
FcConfigSetFonts (config, set, FcSetApplication);
1482
if (!FcDirScan (set, subdirs, 0, config->blanks, dir, FcFalse))
1484
FcStrSetDestroy (subdirs);
1487
if ((sublist = FcStrListCreate (subdirs)))
1489
while ((subdir = FcStrListNext (sublist)))
1491
FcConfigAppFontAddDir (config, subdir);
1493
FcStrListDone (sublist);
1499
FcConfigAppFontClear (FcConfig *config)
1501
FcConfigSetFonts (config, 0, FcSetApplication);